Skip to content

Commit 6640edd

Browse files
ijklampsfinaki
andauthored
Add error tcActivePatternArgumentCountNotMatch (#16846)
* Add error FS3866 tcActivePatternArgumentCountNotMatch * fix: single case active pattern returning unit can omit return value * fix: Total AP case returning unit can omit unit * fix: allow omit last argument when return type of the case not solve * try fix test * try fix test 2 * Change way to get parameter count * try fix 3 * single case: allow all dtys.Length >= args.Length * update release note * divide the msg by whether with params * fix test * typo * update xlf * split the error to four * fix test * improve msg * fix test * 更新 ActivePatternArgCountMismatchTest.fs * update to errorR * revert errorR * fix --------- Co-authored-by: Petr <psfinaki@users.noreply.github.com>
1 parent 339a139 commit 6640edd

21 files changed

+416
-45
lines changed

docs/release-notes/.FSharp.Compiler.Service/8.0.400.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,7 @@
1414
### Added
1515

1616
* Generate new `Equals` overload to avoid boxing for structural comparison ([PR #16857](https://github.com/dotnet/fsharp/pull/16857))
17+
18+
### Changed
19+
20+
* Improve error of Active Pattern case Argument Count Not Match ([PR #16846](https://github.com/dotnet/fsharp/pull/16846))

src/Compiler/Checking/CheckExpressions.fs

Lines changed: 69 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5119,22 +5119,77 @@ and TcPatLongIdentActivePatternCase warnOnUpper (cenv: cenv) (env: TcEnv) vFlags
51195119
let vExprTy = vExpr.Type
51205120

51215121
let activePatArgsAsSynPats, patArg =
5122-
match args with
5123-
| [] -> [], SynPat.Const(SynConst.Unit, m)
5124-
| _ ->
5125-
// This bit of type-directed analysis ensures that parameterized partial active patterns returning unit do not need to take an argument
5126-
let dtys, retTy = stripFunTy g vExprTy
5127-
5128-
if dtys.Length = args.Length + 1 &&
5129-
((isOptionTy g retTy && isUnitTy g (destOptionTy g retTy)) ||
5130-
(isValueOptionTy g retTy && isUnitTy g (destValueOptionTy g retTy))) ||
5131-
// `bool` partial AP always be treated as `unit option`
5132-
// For `val (|P|_|) : _ -> bool`, only allow `match x with | P -> ...`
5133-
// For `val (|P|_|) : _ -> _ -> bool`, only allow `match x with | P parameter -> ...`
5134-
(not apinfo.IsTotal && isBoolTy g retTy) then
5122+
let rec IsNotSolved ty =
5123+
match ty with
5124+
| TType_var(v, _) when v.IsSolved ->
5125+
match v.Solution with
5126+
| Some t -> IsNotSolved t
5127+
| None -> false
5128+
| TType_var _ -> true
5129+
| _ -> false
5130+
5131+
// only cases which return unit or unresolved type (in AP definition) can omit output arg
5132+
let canOmit retTy = isUnitTy g retTy || IsNotSolved retTy
5133+
5134+
// This bit of type-directed analysis ensures that parameterized partial active patterns returning unit do not need to take an argument
5135+
let dtys, retTy = stripFunTy g vExprTy
5136+
let paramCount = if dtys.Length = 0 then 0 else dtys.Length - 1
5137+
5138+
let showErrMsg returnCount =
5139+
let fmtExprArgs paramCount =
5140+
let rec loop i (sb: Text.StringBuilder) =
5141+
let cutoff = 10
5142+
if i > paramCount then sb.ToString()
5143+
elif i > cutoff then sb.Append("...").ToString()
5144+
else loop (i + 1) (sb.Append(" e").Append i)
5145+
5146+
loop 1 (Text.StringBuilder())
5147+
5148+
let caseName = apinfo.ActiveTags[idx]
5149+
let msg =
5150+
match paramCount, returnCount with
5151+
| 0, 0 -> FSComp.SR.tcActivePatternArgsCountNotMatchNoArgsNoPat(caseName, caseName)
5152+
| 0, _ -> FSComp.SR.tcActivePatternArgsCountNotMatchOnlyPat(caseName)
5153+
| _, 0 -> FSComp.SR.tcActivePatternArgsCountNotMatchArgs(paramCount, caseName, fmtExprArgs paramCount)
5154+
| _, _ -> FSComp.SR.tcActivePatternArgsCountNotMatchArgsAndPat(paramCount, caseName, fmtExprArgs paramCount)
5155+
error(Error(msg, m))
5156+
5157+
// partial active pattern (returning bool) doesn't have output arg
5158+
if (not apinfo.IsTotal && isBoolTy g retTy) then
5159+
checkLanguageFeatureError g.langVersion LanguageFeature.BooleanReturningAndReturnTypeDirectedPartialActivePattern m
5160+
if paramCount = List.length args then
51355161
args, SynPat.Const(SynConst.Unit, m)
51365162
else
5137-
List.frontAndBack args
5163+
showErrMsg 0
5164+
5165+
// for single case active pattern, if not all parameter provided, output will be a function
5166+
// that takes the remaining parameter as input
5167+
elif apinfo.IsTotal && apinfo.ActiveTags.Length = 1 && dtys.Length >= args.Length && not args.IsEmpty then
5168+
List.frontAndBack args
5169+
5170+
// active pattern cases returning unit or unknown things (in AP definition) can omit output arg
5171+
elif paramCount = args.Length then
5172+
let caseRetTy =
5173+
if isOptionTy g retTy then destOptionTy g retTy
5174+
elif isValueOptionTy g retTy then destValueOptionTy g retTy
5175+
elif isChoiceTy g retTy then destChoiceTy g retTy idx
5176+
else retTy
5177+
5178+
// only cases which return unit or unresolved type (in AP definition) can omit output arg
5179+
if canOmit caseRetTy then
5180+
args, SynPat.Const(SynConst.Unit, m)
5181+
else
5182+
showErrMsg 1
5183+
5184+
// active pattern in function param (e.g. let f (|P|_|) = ...)
5185+
elif IsNotSolved vExprTy then
5186+
List.frontAndBack args
5187+
5188+
// args count should equal to AP function params count
5189+
elif dtys.Length <> args.Length then
5190+
showErrMsg 1
5191+
else
5192+
List.frontAndBack args
51385193

51395194
if not (isNil activePatArgsAsSynPats) && apinfo.ActiveTags.Length <> 1 then
51405195
errorR (Error (FSComp.SR.tcRequireActivePatternWithOneResult (), m))

src/Compiler/FSComp.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1747,4 +1747,8 @@ featureReuseSameFieldsInStructUnions,"Share underlying fields in a [<Struct>] di
17471747
3864,tooManyMethodsInDotNetTypeWritingAssembly,"The type '%s' has too many methods. Found: '%d', maximum: '%d'"
17481748
3865,parsOnlySimplePatternsAreAllowedInConstructors,"Only simple patterns are allowed in primary constructors"
17491749
3866,chkStaticAbstractInterfaceMembers,"A static abstract non-virtual interface member should only be called via type parameter (for example: 'T.%s)."
1750-
3867,chkStaticAbstractMembersOnClasses,"Classes cannot contain static abstract members."
1750+
3867,chkStaticAbstractMembersOnClasses,"Classes cannot contain static abstract members."
1751+
3868,tcActivePatternArgsCountNotMatchNoArgsNoPat,"This active pattern does not expect any arguments, i.e., it should be used like '%s' instead of '%s x'."
1752+
3868,tcActivePatternArgsCountNotMatchOnlyPat,"This active pattern expects exactly one pattern argument, e.g., '%s pat'."
1753+
3868,tcActivePatternArgsCountNotMatchArgs,"This active pattern expects %d expression argument(s), e.g., '%s%s'."
1754+
3868,tcActivePatternArgsCountNotMatchArgsAndPat,"This active pattern expects %d expression argument(s) and a pattern argument, e.g., '%s%s pat'."

src/Compiler/TypedTree/TypedTreeOps.fs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3689,6 +3689,17 @@ let isOptionTy (g: TcGlobals) ty =
36893689
| ValueNone -> false
36903690
| ValueSome tcref -> tyconRefEq g g.option_tcr_canon tcref
36913691

3692+
let isChoiceTy (g: TcGlobals) ty =
3693+
match tryTcrefOfAppTy g ty with
3694+
| ValueNone -> false
3695+
| ValueSome tcref ->
3696+
tyconRefEq g g.choice2_tcr tcref ||
3697+
tyconRefEq g g.choice3_tcr tcref ||
3698+
tyconRefEq g g.choice4_tcr tcref ||
3699+
tyconRefEq g g.choice5_tcr tcref ||
3700+
tyconRefEq g g.choice6_tcr tcref ||
3701+
tyconRefEq g g.choice7_tcr tcref
3702+
36923703
let tryDestOptionTy g ty =
36933704
match argsOfAppTy g ty with
36943705
| [ty1] when isOptionTy g ty -> ValueSome ty1
@@ -3699,6 +3710,11 @@ let tryDestValueOptionTy g ty =
36993710
| [ty1] when isValueOptionTy g ty -> ValueSome ty1
37003711
| _ -> ValueNone
37013712

3713+
let tryDestChoiceTy g ty idx =
3714+
match argsOfAppTy g ty with
3715+
| ls when isChoiceTy g ty && ls.Length > idx -> ValueSome ls[idx]
3716+
| _ -> ValueNone
3717+
37023718
let destOptionTy g ty =
37033719
match tryDestOptionTy g ty with
37043720
| ValueSome ty -> ty
@@ -3709,6 +3725,11 @@ let destValueOptionTy g ty =
37093725
| ValueSome ty -> ty
37103726
| ValueNone -> failwith "destValueOptionTy: not a value option type"
37113727

3728+
let destChoiceTy g ty idx =
3729+
match tryDestChoiceTy g ty idx with
3730+
| ValueSome ty -> ty
3731+
| ValueNone -> failwith "destChoiceTy: not a Choice type"
3732+
37123733
let isNullableTy (g: TcGlobals) ty =
37133734
match tryTcrefOfAppTy g ty with
37143735
| ValueNone -> false

src/Compiler/TypedTree/TypedTreeOps.fsi

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1547,6 +1547,9 @@ val isValueOptionTy: TcGlobals -> TType -> bool
15471547
/// Determine if a type is an option type
15481548
val isOptionTy: TcGlobals -> TType -> bool
15491549

1550+
/// Determine if a type is an Choice type
1551+
val isChoiceTy: TcGlobals -> TType -> bool
1552+
15501553
/// Take apart an option type
15511554
val destOptionTy: TcGlobals -> TType -> TType
15521555

@@ -1556,6 +1559,12 @@ val tryDestOptionTy: TcGlobals -> TType -> TType voption
15561559
/// Try to take apart an option type
15571560
val destValueOptionTy: TcGlobals -> TType -> TType
15581561

1562+
/// Take apart an Choice type
1563+
val tryDestChoiceTy: TcGlobals -> TType -> int -> TType voption
1564+
1565+
/// Try to take apart an Choice type
1566+
val destChoiceTy: TcGlobals -> TType -> int -> TType
1567+
15591568
/// Determine is a type is a System.Nullable type
15601569
val isNullableTy: TcGlobals -> TType -> bool
15611570

src/Compiler/xlf/FSComp.txt.cs.xlf

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.de.xlf

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.es.xlf

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.fr.xlf

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.it.xlf

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)