From e4dd4c87e4ce4063918c275e14b83e9f73a7c9ea Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Mon, 20 May 2024 13:24:47 -0400 Subject: [PATCH 1/6] Improve a few more active pattern error msgs * Don't imply that a pattern argument is needed when it may (and should) be omitted. * A typar with (most kinds of) constraints cannot be unit. * Add more comprehensive tests for active pattern arg count error messages. --- src/Compiler/Checking/CheckExpressions.fs | 54 +- .../ActivePatternArgCountMismatchTest.fs | 1315 ++++++++++++++++- 2 files changed, 1324 insertions(+), 45 deletions(-) diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs index 4aa56135baf..15ea6a791ea 100644 --- a/src/Compiler/Checking/CheckExpressions.fs +++ b/src/Compiler/Checking/CheckExpressions.fs @@ -5119,17 +5119,33 @@ and TcPatLongIdentActivePatternCase warnOnUpper (cenv: cenv) (env: TcEnv) vFlags let vExprTy = vExpr.Type let activePatArgsAsSynPats, patArg = - let rec IsNotSolved ty = - match ty with - | TType_var(v, _) when v.IsSolved -> - match v.Solution with - | Some t -> IsNotSolved t - | None -> false - | TType_var _ -> true - | _ -> false + let isSolved ty = + let couldResolveToUnit constraints = + constraints + |> List.exists (fun c -> + match c with + // These apply to unit. + | TyparConstraint.IsReferenceType _ + | TyparConstraint.SupportsComparison _ + | TyparConstraint.SupportsEquality _ -> false + // This could apply to unit if this RFC is implemented: + // https://github.com/fsharp/fslang-design/blob/main/RFCs/FS-1043-extension-members-for-operators-and-srtp-constraints.md + | TyparConstraint.MayResolveMember _ -> false + // Any other kind of constraint cannot apply to unit. + | _ -> true) + + tryDestTyparTy g ty + |> ValueOption.forall (fun typar -> typar.IsSolved || couldResolveToUnit typar.Constraints) // only cases which return unit or unresolved type (in AP definition) can omit output arg - let canOmit retTy = isUnitTy g retTy || IsNotSolved retTy + let canOmit retTy = + let caseRetTy = + if isOptionTy g retTy then destOptionTy g retTy + elif isValueOptionTy g retTy then destValueOptionTy g retTy + elif isChoiceTy g retTy then destChoiceTy g retTy idx + else retTy + + isUnitTy g caseRetTy || not (isSolved caseRetTy) // This bit of type-directed analysis ensures that parameterized partial active patterns returning unit do not need to take an argument let dtys, retTy = stripFunTy g vExprTy @@ -5169,25 +5185,27 @@ and TcPatLongIdentActivePatternCase warnOnUpper (cenv: cenv) (env: TcEnv) vFlags // active pattern cases returning unit or unknown things (in AP definition) can omit output arg elif paramCount = args.Length then - let caseRetTy = - if isOptionTy g retTy then destOptionTy g retTy - elif isValueOptionTy g retTy then destValueOptionTy g retTy - elif isChoiceTy g retTy then destChoiceTy g retTy idx - else retTy - // only cases which return unit or unresolved type (in AP definition) can omit output arg - if canOmit caseRetTy then + if canOmit retTy then args, SynPat.Const(SynConst.Unit, m) else showErrMsg 1 // active pattern in function param (e.g. let f (|P|_|) = ...) - elif IsNotSolved vExprTy then + elif not (isSolved vExprTy) then List.frontAndBack args // args count should equal to AP function params count elif dtys.Length <> args.Length then - showErrMsg 1 + let returnCount = + match dtys with + // val (|P|) : expr1:_ -> unit + // val (|P|_|) : expr1:_ -> unit option + // val (|P|_|) : expr1:_ -> unit voption + | [_] when canOmit retTy -> 0 + | _ -> 1 + + showErrMsg returnCount else List.frontAndBack args diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ActivePatternArgCountMismatchTest.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ActivePatternArgCountMismatchTest.fs index 77fd98449b0..f05ced77489 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ActivePatternArgCountMismatchTest.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ActivePatternArgCountMismatchTest.fs @@ -1,40 +1,1301 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace ErrorMessages +module ErrorMessages.``Active pattern arg counts`` open Xunit open FSharp.Test.Compiler -module ``Active Pattern argument count mismatch test`` = +/// Warning FS0025: Incomplete pattern matches on this expression. +/// We suppress this warning in the assertions below so that we +/// don't need to add a wildcard case to every match. +let [] IncompletePatternMatches = 25 - [] - let ``test``() = - FSharp """ -let (|IsEven|_|) x = x % 2 = 0 -match 1 with -| IsEven xxx -> () -| _ -> printfn "Odd!" +module TooFew = + module ``int → int → unit`` = + [] + let ``match expr1 with P -> …`` () = + FSharp """ +let (|P|) (expr2 : int) (expr1 : int) = P +match 1 with P -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + //|> withSingleDiagnostic (Error 3868, Line 3, Col 14, Line 3, Col 15, "This active pattern expects 1 expression argument(s) and a pattern argument, e.g., 'P e1 ()'.") + |> withSingleDiagnostic (Error 3868, Line 3, Col 14, Line 3, Col 15, "This active pattern expects 1 expression argument(s) and a pattern argument, e.g., 'P e1 pat'.") + + module ``int → int → unit option`` = + [] + let ``match expr1 with P -> …`` () = + FSharp """ +let (|P|_|) (expr2 : int) (expr1 : int) = if expr1 = expr2 then Some P else None +match 1 with P -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 3, Col 14, Line 3, Col 15, "This active pattern expects 1 expression argument(s) and a pattern argument, e.g., 'P e1 pat'.") + + module ``int → int → unit voption`` = + [] + let ``match expr1 with P -> …`` () = + FSharp """ +let (|P|_|) (expr2 : int) (expr1 : int) = if expr1 = expr2 then ValueSome P else ValueNone +match 1 with P -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 3, Col 14, Line 3, Col 15, "This active pattern expects 1 expression argument(s) and a pattern argument, e.g., 'P e1 pat'.") + + module ``int → int → Choice`` = + [] + let ``match expr1 with P -> …`` () = + FSharp """ +let (|P|Q|) (expr2 : int) (expr1 : int) = if expr1 = expr2 then P else Q +match 1 with P -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 3, Col 14, Line 3, Col 15, "This active pattern expects 1 expression argument(s) and a pattern argument, e.g., 'P e1 pat'.") + + module ``int → int → bool`` = + [] + let ``match expr1 with P -> …`` () = + FSharp """ +let (|P|_|) (expr2 : int) (expr1 : int) = expr1 = expr2 +match 1 with P -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 3, Col 14, Line 3, Col 15, "This active pattern expects 1 expression argument(s), e.g., 'P e1'.") + + module ``int → int → int`` = + [] + let ``match expr1 with P -> …`` () = + FSharp """ +let (|P|) expr2 expr1 = P (expr1 + expr2) +match 1 with P -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 3, Col 14, Line 3, Col 15, "This active pattern expects 1 expression argument(s) and a pattern argument, e.g., 'P e1 pat'.") + + module ``int → int → int option`` = + [] + let ``match expr1 with P -> …`` () = + FSharp """ +let (|P|_|) expr2 expr1 = if expr1 = expr2 then Some (P (expr1 + expr2)) else None +match 1 with P -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 3, Col 14, Line 3, Col 15, "This active pattern expects 1 expression argument(s) and a pattern argument, e.g., 'P e1 pat'.") + + [] + let ``match expr1 with P pat -> …`` () = + FSharp """ +let (|P|_|) expr2 expr1 = if expr1 = expr2 then Some (P (expr1 + expr2)) else None +match 1 with P pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 3, Col 14, Line 3, Col 19, "This active pattern expects 1 expression argument(s) and a pattern argument, e.g., 'P e1 pat'.") + + [] + let ``match expr1 with P expr2 -> …`` () = + FSharp """ +let (|P|_|) expr2 expr1 = if expr1 = expr2 then Some (P (expr1 + expr2)) else None +let expr2 = 2 +match 1 with P expr2 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 21, "This active pattern expects 1 expression argument(s) and a pattern argument, e.g., 'P e1 pat'.") -let (|Ignore|) x = () -match 1 with -| Ignore () () -> printfn "Ignored!" -| _ -> () + module ``int → int → int voption`` = + [] + let ``match expr1 with P -> …`` () = + FSharp """ +let (|P|_|) expr2 expr1 = if expr1 = expr2 then ValueSome (P (expr1 + expr2)) else ValueNone +match 1 with P -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 3, Col 14, Line 3, Col 15, "This active pattern expects 1 expression argument(s) and a pattern argument, e.g., 'P e1 pat'.") -let (|Equals|_|) z y x = x = y -match 1 with -| Equals "" 2 xxx -> () -| _ -> printfn "Not Equal" + [] + let ``match expr1 with P pat -> …`` () = + FSharp """ +let (|P|_|) expr2 expr1 = if expr1 = expr2 then ValueSome (P (expr1 + expr2)) else ValueNone +match 1 with P pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 3, Col 14, Line 3, Col 19, "This active pattern expects 1 expression argument(s) and a pattern argument, e.g., 'P e1 pat'.") -let (|A|) a b c d = d -match 1 with -| A -> () -| _ -> () - """ |> withLangVersionPreview + [] + let ``match expr1 with P expr2 -> …`` () = + FSharp """ +let (|P|_|) expr2 expr1 = if expr1 = expr2 then ValueSome (P (expr1 + expr2)) else ValueNone +let expr2 = 2 +match 1 with P expr2 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 21, "This active pattern expects 1 expression argument(s) and a pattern argument, e.g., 'P e1 pat'.") + + module ``int → int → Choice`` = + [] + let ``match expr1 with P -> …`` () = + FSharp """ +let (|P|Q|) expr2 expr1 = if expr1 = expr2 then P (expr1 + expr2) else Q +match 1 with P -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 3, Col 14, Line 3, Col 15, "This active pattern expects 1 expression argument(s) and a pattern argument, e.g., 'P e1 pat'.") + + [] + let ``match expr1 with P pat -> …`` () = + FSharp """ +let (|P|Q|) expr2 expr1 = if expr1 = expr2 then P (expr1 + expr2) else Q +match 1 with P pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 3, Col 14, Line 3, Col 19, "This active pattern expects 1 expression argument(s) and a pattern argument, e.g., 'P e1 pat'.") + + [] + let ``match expr1 with P expr2 -> …`` () = + FSharp """ +let (|P|Q|) expr2 expr1 = if expr1 = expr2 then P (expr1 + expr2) else Q +let expr2 = 2 +match 1 with P expr2 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 21, "This active pattern expects 1 expression argument(s) and a pattern argument, e.g., 'P e1 pat'.") + +module Enough = + module ``int → unit`` = + [] + let ``match expr1 with P -> …`` () = + FSharp """ +let (|P|) expr1 = P +match 1 with P -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + // pat is unit. + [] + let ``match expr1 with P pat -> …`` () = + FSharp """ +let (|P|) expr1 = P +match 1 with P pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + [] + let ``match expr1 with P () -> …`` () = + FSharp """ +let (|P|) expr1 = P +match 1 with P () -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + module ``int → unit option`` = + [] + let ``match expr1 with P -> …`` () = + FSharp """ +let (|P|_|) expr1 = if expr1 = 1 then Some P else None +match 1 with P -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + // pat is unit. + [] + let ``match expr1 with P pat -> …`` () = + FSharp """ +let (|P|_|) expr1 = if expr1 = 1 then Some P else None +match 1 with P pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + [] + let ``match expr1 with P () -> …`` () = + FSharp """ +let (|P|_|) expr1 = if expr1 = 1 then Some P else None +match 1 with P () -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + module ``int → unit voption`` = + [] + let ``match expr1 with P -> …`` () = + FSharp """ +let (|P|_|) expr1 = if expr1 = 1 then ValueSome P else ValueNone +match 1 with P -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + // pat is unit. + [] + let ``match expr1 with P pat -> …`` () = + FSharp """ +let (|P|_|) expr1 = if expr1 = 1 then ValueSome P else ValueNone +match 1 with P pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + [] + let ``match expr1 with P () -> …`` () = + FSharp """ +let (|P|_|) expr1 = if expr1 = 1 then ValueSome P else ValueNone +match 1 with P () -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + module ``int → Choice`` = + [] + let ``match expr1 with P -> …`` () = + FSharp """ +let (|P|Q|) expr1 = if expr1 = 1 then P else Q +match 1 with P -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + // pat is unit. + [] + let ``match expr1 with P pat -> …`` () = + FSharp """ +let (|P|Q|) expr1 = if expr1 = 1 then P else Q +match 1 with P pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + [] + let ``match expr1 with P () -> …`` () = + FSharp """ +let (|P|Q|) expr1 = if expr1 = 1 then P else Q +match 1 with P () -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + module ``int → bool`` = + [] + let ``match expr1 with P -> …`` () = + FSharp """ +let (|P|_|) expr1 = expr1 = 1 +match 1 with P -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + module ``int → int`` = + [] + let ``match expr1 with P pat -> …`` () = + FSharp """ +let (|P|) (expr1 : int) = P expr1 +match 1 with P pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + module ``int → int option`` = + [] + let ``match expr1 with P pat -> …`` () = + FSharp """ +let (|P|_|) (expr1 : int) = if expr1 = 1 then Some (P expr1) else None +match 1 with P pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + module ``int → int voption`` = + [] + let ``match expr1 with P pat -> …`` () = + FSharp """ +let (|P|_|) (expr1 : int) = if expr1 = 1 then ValueSome (P expr1) else ValueNone +match 1 with P pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + module ``int → Choice`` = + [] + let ``match expr1 with P pat -> …`` () = + FSharp """ +let (|P|Q|) (expr1 : int) = if expr1 = 1 then P expr1 else Q +match 1 with P pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + module ``int → int → unit`` = + [] + let ``match expr1 with P expr2 () -> …`` () = + FSharp """ +let (|P|) (expr2 : int) (expr1 : int) = P +let expr2 = 2 +match 1 with P expr2 () -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + // pat is unit. + [] + let ``match expr1 with P expr2 pat -> …`` () = + FSharp """ +let (|P|) (expr2 : int) (expr1 : int) = P +let expr2 = 2 +match 1 with P expr2 pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + // expr3 is actually a pattern bound to unit. + [] + let ``match expr1 with P expr2 expr3 -> …`` () = + FSharp """ +let (|P|) (expr2 : int) (expr1 : int) = P +let expr2 = 2 +let expr3 = 3 +match 1 with P expr2 expr3 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + // pat is bound to a value of type (int -> unit). + [] + let ``match expr1 with P pat -> …`` () = + FSharp """ +let (|P|) (expr2 : int) (expr1 : int) = P +match 1 with P pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + // expr2 is actually a pattern bound to a value of type (int -> unit). + [] + let ``match expr1 with P expr2 -> …`` () = + FSharp """ +let (|P|) (expr2 : int) (expr1 : int) = P +let expr2 = 2 +match 1 with P expr2 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + module ``int → int → unit option`` = + [] + let ``match expr1 with P expr2 () -> …`` () = + FSharp """ +let (|P|_|) (expr2 : int) (expr1 : int) = if expr1 = expr2 then Some P else None +let expr2 = 2 +match 1 with P expr2 () -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + // pat is unit. + [] + let ``match expr1 with P expr2 pat -> …`` () = + FSharp """ +let (|P|_|) (expr2 : int) (expr1 : int) = if expr1 = expr2 then Some P else None +let expr2 = 2 +match 1 with P expr2 pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + // expr3 is actually a pattern bound to unit. + [] + let ``match expr1 with P expr2 expr3 -> …`` () = + FSharp """ +let (|P|_|) (expr2 : int) (expr1 : int) = if expr1 = expr2 then Some P else None +let expr2 = 2 +let expr3 = 3 +match 1 with P expr2 expr3 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + [] + let ``match expr1 with P pat -> …`` () = + FSharp """ +let (|P|_|) (expr2 : int) (expr1 : int) = if expr1 = expr2 then Some P else None +match 1 with P pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 3, Col 16, Line 3, Col 19, "The value or constructor 'pat' is not defined.") + + [] + let ``match expr1 with P expr2 -> …`` () = + FSharp """ +let (|P|_|) (expr2 : int) (expr1 : int) = if expr1 = expr2 then Some P else None +let expr2 = 2 +match 1 with P expr2 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + module ``int → int → unit voption`` = + [] + let ``match expr1 with P expr2 () -> …`` () = + FSharp """ +let (|P|_|) (expr2 : int) (expr1 : int) = if expr1 = expr2 then ValueSome P else ValueNone +let expr2 = 2 +match 1 with P expr2 () -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + // pat is unit. + [] + let ``match expr1 with P expr2 pat -> …`` () = + FSharp """ +let (|P|_|) (expr2 : int) (expr1 : int) = if expr1 = expr2 then ValueSome P else ValueNone +let expr2 = 2 +match 1 with P expr2 pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + // expr3 is actually a pattern bound to unit. + [] + let ``match expr1 with P expr2 expr3 -> …`` () = + FSharp """ +let (|P|_|) (expr2 : int) (expr1 : int) = if expr1 = expr2 then ValueSome P else ValueNone +let expr2 = 2 +let expr3 = 3 +match 1 with P expr2 expr3 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + [] + let ``match expr1 with P pat -> …`` () = + FSharp """ +let (|P|_|) (expr2 : int) (expr1 : int) = if expr1 = expr2 then ValueSome P else ValueNone +match 1 with P pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 3, Col 16, Line 3, Col 19, "The value or constructor 'pat' is not defined.") + + [] + let ``match expr1 with P expr2 -> …`` () = + FSharp """ +let (|P|_|) (expr2 : int) (expr1 : int) = if expr1 = expr2 then ValueSome P else ValueNone +let expr2 = 2 +match 1 with P expr2 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + module ``int → int → Choice`` = + [] + let ``match expr1 with P expr2 () -> …`` () = + FSharp """ +let (|P|Q|) (expr2 : int) (expr1 : int) = if expr1 = expr2 then P else Q +let expr2 = 2 +match 1 with P expr2 () -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 722, Line 4, Col 14, Line 4, Col 24, "Only active patterns returning exactly one result may accept arguments") + + [] + let ``match expr1 with P expr2 pat -> …`` () = + FSharp """ +let (|P|Q|) (expr2 : int) (expr1 : int) = if expr1 = expr2 then P else Q +let expr2 = 2 +match 1 with P expr2 pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 722, Line 4, Col 14, Line 4, Col 25, "Only active patterns returning exactly one result may accept arguments") + + [] + let ``match expr1 with P expr2 expr3 -> …`` () = + FSharp """ +let (|P|Q|) (expr2 : int) (expr1 : int) = if expr1 = expr2 then P else Q +let expr2 = 2 +let expr3 = 3 +match 1 with P expr2 expr3 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 722, Line 5, Col 14, Line 5, Col 27, "Only active patterns returning exactly one result may accept arguments") + + [] + let ``match expr1 with P pat -> …`` () = + FSharp """ +let (|P|Q|) (expr2 : int) (expr1 : int) = if expr1 = expr2 then P else Q +match 1 with P pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches |> typecheck |> shouldFail |> withDiagnostics [ - (Error 3868, Line 4, Col 3, Line 4, Col 13, "This active pattern does not expect any arguments, i.e., it should be used like 'IsEven' instead of 'IsEven x'.") - (Error 3868, Line 9, Col 3, Line 9, Col 15, "This active pattern expects exactly one pattern argument, e.g., 'Ignore pat'.") - (Error 3868, Line 14, Col 3, Line 14, Col 18, "This active pattern expects 2 expression argument(s), e.g., 'Equals e1 e2'.") - (Error 3868, Line 19, Col 3, Line 19, Col 4, "This active pattern expects 3 expression argument(s) and a pattern argument, e.g., 'A e1 e2 e3 pat'.") - ] + Error 722, Line 3, Col 14, Line 3, Col 19, "Only active patterns returning exactly one result may accept arguments" + Error 39, Line 3, Col 16, Line 3, Col 19, "The value or constructor 'pat' is not defined." + ] + + [] + let ``match expr1 with P expr2 -> …`` () = + FSharp """ +let (|P|Q|) (expr2 : int) (expr1 : int) = if expr1 = expr2 then P else Q +let expr2 = 2 +match 1 with P expr2 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 722, Line 4, Col 14, Line 4, Col 21, "Only active patterns returning exactly one result may accept arguments") + + module ``int → int → bool`` = + [] + let ``match expr1 with P expr2 () -> …`` () = + FSharp """ +let (|P|_|) (expr2 : int) (expr1 : int) = expr1 = expr2 +let expr2 = 2 +match 1 with P expr2 () -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 24, "This active pattern expects 1 expression argument(s), e.g., 'P e1'.") + + [] + let ``match expr1 with P expr2 pat -> …`` () = + FSharp """ +let (|P|_|) (expr2 : int) (expr1 : int) = expr1 = expr2 +let expr2 = 2 +match 1 with P expr2 pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 25, "This active pattern expects 1 expression argument(s), e.g., 'P e1'.") + + [] + let ``match expr1 with P expr2 expr3 -> …`` () = + FSharp """ +let (|P|_|) (expr2 : int) (expr1 : int) = expr1 = expr2 +let expr2 = 2 +let expr3 = 3 +match 1 with P expr2 expr3 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 5, Col 14, Line 5, Col 27, "This active pattern expects 1 expression argument(s), e.g., 'P e1'.") + + [] + let ``match expr1 with P pat -> …`` () = + FSharp """ +let (|P|_|) (expr2 : int) (expr1 : int) = expr1 = expr2 +match 1 with P pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 3, Col 16, Line 3, Col 19, "The value or constructor 'pat' is not defined.") + + [] + let ``match expr1 with P expr2 -> …`` () = + FSharp """ +let (|P|_|) (expr2 : int) (expr1 : int) = expr1 = expr2 +let expr2 = 2 +match 1 with P expr2 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + module ``int → int → int`` = + // Normal usage; pat is int. + [] + let ``match expr1 with P expr2 pat -> …`` () = + FSharp """ +let (|P|) expr2 expr1 = P (expr1 + expr2) +let expr2 = 2 +match 1 with P expr2 pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + // expr3 is actually a pattern bound to a value of type int. + [] + let ``match expr1 with P expr2 expr3 -> …`` () = + FSharp """ +let (|P|) expr2 expr1 = P (expr1 + expr2) +let expr2 = 2 +let expr3 = 2 +match 1 with P expr2 expr3 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + // pat is bound to a value of type (int -> int). + [] + let ``match expr1 with P pat -> …`` () = + FSharp """ +let (|P|) expr2 expr1 = P (expr1 + expr2) +match 1 with P pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + // expr2 is actually a pattern bound to a value of type (int -> int). + [] + let ``match expr1 with P expr2 -> …`` () = + FSharp """ +let (|P|) expr2 expr1 = P (expr1 + expr2) +let expr2 = 2 +match 1 with P expr2 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + module ``int → int → int option`` = + // Normal usage; pat is int. + [] + let ``match expr1 with P expr2 pat -> …`` () = + FSharp """ +let (|P|_|) expr2 expr1 = if expr1 = expr2 then Some (P (expr1 + expr2)) else None +let expr2 = 2 +match 1 with P expr2 pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + // expr3 is actually a pattern bound to a value of type int. + [] + let ``match expr1 with P expr2 expr3 -> …`` () = + FSharp """ +let (|P|_|) expr2 expr1 = if expr1 = expr2 then Some (P (expr1 + expr2)) else None +let expr2 = 2 +let expr3 = 2 +match 1 with P expr2 expr3 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + [] + let ``match expr1 with P expr2 -> …`` () = + FSharp """ +let (|P|_|) expr2 expr1 = if expr1 = expr2 then Some (P (expr1 + expr2)) else None +let expr2 = 2 +match 1 with P expr2 pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + module ``int → int → int voption`` = + // Normal usage; pat is int. + [] + let ``match expr1 with P expr2 pat -> …`` () = + FSharp """ +let (|P|_|) expr2 expr1 = if expr1 = expr2 then ValueSome (P (expr1 + expr2)) else ValueNone +let expr2 = 2 +match 1 with P expr2 pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + // expr3 is actually a pattern bound to a value of type int. + [] + let ``match expr1 with P expr2 expr3 -> …`` () = + FSharp """ +let (|P|_|) expr2 expr1 = if expr1 = expr2 then ValueSome (P (expr1 + expr2)) else ValueNone +let expr2 = 2 +let expr3 = 2 +match 1 with P expr2 expr3 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + [] + let ``match expr1 with P expr2 -> …`` () = + FSharp """ +let (|P|_|) expr2 expr1 = if expr1 = expr2 then ValueSome (P (expr1 + expr2)) else ValueNone +let expr2 = 2 +match 1 with P expr2 pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldSucceed + + module ``int → int → Choice`` = + [] + let ``match expr1 with P expr2 pat -> …`` () = + FSharp """ +let (|P|Q|) expr2 expr1 = if expr1 = expr2 then P (expr1 + expr2) else Q +let expr2 = 2 +match 1 with P expr2 pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 722, Line 4, Col 14, Line 4, Col 25, "Only active patterns returning exactly one result may accept arguments") + + [] + let ``match expr1 with P expr2 expr3 -> …`` () = + FSharp """ +let (|P|Q|) expr2 expr1 = if expr1 = expr2 then P (expr1 + expr2) else Q +let expr2 = 2 +let expr3 = 2 +match 1 with P expr2 expr3 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 722, Line 5, Col 14, Line 5, Col 27, "Only active patterns returning exactly one result may accept arguments") + + [] + let ``match expr1 with P expr2 -> …`` () = + FSharp """ +let (|P|Q|) expr2 expr1 = if expr1 = expr2 then P (expr1 + expr2) else Q +let expr2 = 2 +match 1 with P expr2 pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 722, Line 4, Col 14, Line 4, Col 25, "Only active patterns returning exactly one result may accept arguments") + +module TooMany = + module ``int → unit`` = + [] + let ``match expr1 with P expr2 pat -> …`` () = + FSharp """ +let (|P|) (expr1 : int) = P +let expr2 = 2 +match 1 with P expr2 pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 25, "This active pattern does not expect any arguments, i.e., it should be used like 'P' instead of 'P x'.") + + [] + let ``match expr1 with P expr2 () -> …`` () = + FSharp """ +let (|P|) (expr1 : int) = P +let expr2 = 2 +match 1 with P expr2 () -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 24, "This active pattern does not expect any arguments, i.e., it should be used like 'P' instead of 'P x'.") + + [] + let ``match expr1 with P () expr2 -> …`` () = + FSharp """ +let (|P|) (expr1 : int) = P +let expr2 = 2 +match 1 with P () expr2 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 24, "This active pattern does not expect any arguments, i.e., it should be used like 'P' instead of 'P x'.") + + module ``int → unit option`` = + [] + let ``match expr1 with P expr2 pat -> …`` () = + FSharp """ +let (|P|_|) (expr1 : int) = if expr1 = 1 then Some P else None +let expr2 = 2 +match 1 with P expr2 pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 25, "This active pattern does not expect any arguments, i.e., it should be used like 'P' instead of 'P x'.") + + [] + let ``match expr1 with P expr2 () -> …`` () = + FSharp """ +let (|P|_|) (expr1 : int) = if expr1 = 1 then Some P else None +let expr2 = 2 +match 1 with P expr2 () -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 24, "This active pattern does not expect any arguments, i.e., it should be used like 'P' instead of 'P x'.") + + [] + let ``match expr1 with P () expr2 -> …`` () = + FSharp """ +let (|P|_|) (expr1 : int) = if expr1 = 1 then Some P else None +let expr2 = 2 +match 1 with P () expr2 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 24, "This active pattern does not expect any arguments, i.e., it should be used like 'P' instead of 'P x'.") + + module ``int → unit voption`` = + [] + let ``match expr1 with P expr2 pat -> …`` () = + FSharp """ +let (|P|_|) (expr1 : int) = if expr1 = 1 then ValueSome P else ValueNone +let expr2 = 2 +match 1 with P expr2 pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 25, "This active pattern does not expect any arguments, i.e., it should be used like 'P' instead of 'P x'.") + + [] + let ``match expr1 with P expr2 () -> …`` () = + FSharp """ +let (|P|_|) (expr1 : int) = if expr1 = 1 then ValueSome P else ValueNone +let expr2 = 2 +match 1 with P expr2 () -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 24, "This active pattern does not expect any arguments, i.e., it should be used like 'P' instead of 'P x'.") + + [] + let ``match expr1 with P () expr2 -> …`` () = + FSharp """ +let (|P|_|) (expr1 : int) = if expr1 = 1 then ValueSome P else ValueNone +let expr2 = 2 +match 1 with P () expr2 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 24, "This active pattern does not expect any arguments, i.e., it should be used like 'P' instead of 'P x'.") + + module ``int → Choice`` = + [] + let ``match expr1 with P expr2 pat -> …`` () = + FSharp """ +let (|P|Q|) (expr1 : int) = if expr1 = 1 then P else Q +let expr2 = 2 +match 1 with P expr2 pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 25, "This active pattern does not expect any arguments, i.e., it should be used like 'P' instead of 'P x'.") + + [] + let ``match expr1 with P expr2 () -> …`` () = + FSharp """ +let (|P|Q|) (expr1 : int) = if expr1 = 1 then P else Q +let expr2 = 2 +match 1 with P expr2 () -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 24, "This active pattern does not expect any arguments, i.e., it should be used like 'P' instead of 'P x'.") + + [] + let ``match expr1 with P () expr2 -> …`` () = + FSharp """ +let (|P|Q|) (expr1 : int) = if expr1 = 1 then P else Q +let expr2 = 2 +match 1 with P () expr2 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 24, "This active pattern does not expect any arguments, i.e., it should be used like 'P' instead of 'P x'.") + + module ``int → bool`` = + [] + let ``match expr1 with P pat -> …`` () = + FSharp """ +let (|P|_|) expr1 = expr1 = 1 +match 1 with P pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 3, Col 14, Line 3, Col 19, "This active pattern does not expect any arguments, i.e., it should be used like 'P' instead of 'P x'.") + + module ``int → int`` = + [] + let ``match expr1 with P expr2 pat -> …`` () = + FSharp """ +let (|P|) (expr1 : int) = P expr1 +let expr2 = 2 +match 1 with P expr2 pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 25, "This active pattern expects exactly one pattern argument, e.g., 'P pat'.") + + [] + let ``match expr1 with P expr2 () -> …`` () = + FSharp """ +let (|P|) (expr1 : int) = P expr1 +let expr2 = 2 +match 1 with P expr2 () -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 24, "This active pattern expects exactly one pattern argument, e.g., 'P pat'.") + + module ``int → int option`` = + [] + let ``match expr1 with P expr2 pat -> …`` () = + FSharp """ +let (|P|_|) (expr1 : int) = if expr1 = 1 then Some (P expr1) else None +let expr2 = 2 +match 1 with P expr2 pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 25, "This active pattern expects exactly one pattern argument, e.g., 'P pat'.") + + [] + let ``match expr1 with P expr2 () -> …`` () = + FSharp """ +let (|P|_|) (expr1 : int) = if expr1 = 1 then Some (P expr1) else None +let expr2 = 2 +match 1 with P expr2 () -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 24, "This active pattern expects exactly one pattern argument, e.g., 'P pat'.") + + module ``int → int voption`` = + [] + let ``match expr1 with P expr2 pat -> …`` () = + FSharp """ +let (|P|_|) (expr1 : int) = if expr1 = 1 then ValueSome (P expr1) else ValueNone +let expr2 = 2 +match 1 with P expr2 pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 25, "This active pattern expects exactly one pattern argument, e.g., 'P pat'.") + + [] + let ``match expr1 with P expr2 () -> …`` () = + FSharp """ +let (|P|_|) (expr1 : int) = if expr1 = 1 then ValueSome (P expr1) else ValueNone +let expr2 = 2 +match 1 with P expr2 () -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 24, "This active pattern expects exactly one pattern argument, e.g., 'P pat'.") + + module ``int → Choice`` = + [] + let ``match expr1 with P expr2 pat -> …`` () = + FSharp """ +let (|P|Q|) (expr1 : int) = if expr1 = 1 then P expr1 else Q +let expr2 = 2 +match 1 with P expr2 pat -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 25, "This active pattern expects exactly one pattern argument, e.g., 'P pat'.") + + [] + let ``match expr1 with P expr2 () -> …`` () = + FSharp """ +let (|P|Q|) (expr1 : int) = if expr1 = 1 then P expr1 else Q +let expr2 = 2 +match 1 with P expr2 () -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 24, "This active pattern expects exactly one pattern argument, e.g., 'P pat'.") + + module ``int → int → unit`` = + [] + let ``match expr1 with P expr2 pat1 pat2 -> …`` () = + FSharp """ +let (|P|) (expr2 : int) (expr1 : int) = P +let expr2 = 2 +match 1 with P expr2 pat1 pat2 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 31, "This active pattern expects 1 expression argument(s) and a pattern argument, e.g., 'P e1 pat'.") + + module ``int → int → unit option`` = + [] + let ``match expr1 with P expr2 pat1 pat2 -> …`` () = + FSharp """ +let (|P|_|) (expr2 : int) (expr1 : int) = if expr1 = expr2 then Some P else None +let expr2 = 2 +match 1 with P expr2 pat1 pat2 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 31, "This active pattern expects 1 expression argument(s) and a pattern argument, e.g., 'P e1 pat'.") + + module ``int → int → unit voption`` = + [] + let ``match expr1 with P expr2 pat1 pat2 -> …`` () = + FSharp """ +let (|P|_|) (expr2 : int) (expr1 : int) = if expr1 = expr2 then ValueSome P else ValueNone +let expr2 = 2 +match 1 with P expr2 pat1 pat2 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 31, "This active pattern expects 1 expression argument(s) and a pattern argument, e.g., 'P e1 pat'.") + + module ``int → int → Choice`` = + [] + let ``match expr1 with P expr2 pat1 pat2 -> …`` () = + FSharp """ +let (|P|Q|) (expr2 : int) (expr1 : int) = if expr1 = expr2 then P else Q +let expr2 = 2 +match 1 with P expr2 pat1 pat2 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 31, "This active pattern expects 1 expression argument(s) and a pattern argument, e.g., 'P e1 pat'.") + + module ``int → int → int`` = + [] + let ``match expr1 with P expr2 pat1 pat2 -> …`` () = + FSharp """ +let (|P|) expr2 expr1 = P (expr1 + expr2) +let expr2 = 2 +match 1 with P expr2 pat1 pat2 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 31, "This active pattern expects 1 expression argument(s) and a pattern argument, e.g., 'P e1 pat'.") + + module ``int → int → int option`` = + [] + let ``match expr1 with P expr2 pat1 pat2 -> …`` () = + FSharp """ +let (|P|_|) expr2 expr1 = if expr1 = expr2 then Some (P (expr1 + expr2)) else None +let expr2 = 2 +match 1 with P expr2 pat1 pat2 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 31, "This active pattern expects 1 expression argument(s) and a pattern argument, e.g., 'P e1 pat'.") + + module ``int → int → int voption`` = + [] + let ``match expr1 with P expr2 pat1 pat2 -> …`` () = + FSharp """ +let (|P|_|) expr2 expr1 = if expr1 = expr2 then ValueSome (P (expr1 + expr2)) else ValueNone +let expr2 = 2 +match 1 with P expr2 pat1 pat2 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 31, "This active pattern expects 1 expression argument(s) and a pattern argument, e.g., 'P e1 pat'.") + + module ``int → int → Choice`` = + [] + let ``match expr1 with P expr2 pat1 pat2 -> …`` () = + FSharp """ +let (|P|Q|) expr2 expr1 = if expr1 = expr2 then P (expr1 + expr2) else Q +let expr2 = 2 +match 1 with P expr2 pat1 pat2 -> () + """ + |> withLangVersionPreview + |> withNoWarn IncompletePatternMatches + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3868, Line 4, Col 14, Line 4, Col 31, "This active pattern expects 1 expression argument(s) and a pattern argument, e.g., 'P e1 pat'.") From 6a075f089ca59f0c754401e20f073f1a34318778 Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Mon, 20 May 2024 13:36:44 -0400 Subject: [PATCH 2/6] Remove commented code --- .../ErrorMessages/ActivePatternArgCountMismatchTest.fs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ActivePatternArgCountMismatchTest.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ActivePatternArgCountMismatchTest.fs index f05ced77489..e2a988ec0c3 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ActivePatternArgCountMismatchTest.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ActivePatternArgCountMismatchTest.fs @@ -22,7 +22,6 @@ match 1 with P -> () |> withNoWarn IncompletePatternMatches |> typecheck |> shouldFail - //|> withSingleDiagnostic (Error 3868, Line 3, Col 14, Line 3, Col 15, "This active pattern expects 1 expression argument(s) and a pattern argument, e.g., 'P e1 ()'.") |> withSingleDiagnostic (Error 3868, Line 3, Col 14, Line 3, Col 15, "This active pattern expects 1 expression argument(s) and a pattern argument, e.g., 'P e1 pat'.") module ``int → int → unit option`` = From d44dc449689693681d75515ef161b3f28c920625 Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Mon, 20 May 2024 13:38:08 -0400 Subject: [PATCH 3/6] Update release notes --- docs/release-notes/.FSharp.Core/8.0.400.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/.FSharp.Core/8.0.400.md b/docs/release-notes/.FSharp.Core/8.0.400.md index 48a05231dc9..9ba04f0b47d 100644 --- a/docs/release-notes/.FSharp.Core/8.0.400.md +++ b/docs/release-notes/.FSharp.Core/8.0.400.md @@ -4,4 +4,5 @@ ### Changed +* Some more active pattern error message improvements. ([PR #17186](https://github.com/dotnet/fsharp/pull/17186)) * Cache delegate in query extensions. ([PR #17130](https://github.com/dotnet/fsharp/pull/17130)) From 5da515b343b9cb8179b2f14859357497f9795171 Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Mon, 20 May 2024 13:43:19 -0400 Subject: [PATCH 4/6] Right release notes --- docs/release-notes/.FSharp.Compiler.Service/8.0.400.md | 4 ++-- docs/release-notes/.FSharp.Core/8.0.400.md | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md b/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md index 7765f188e0e..6bd7fce5327 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md +++ b/docs/release-notes/.FSharp.Compiler.Service/8.0.400.md @@ -22,6 +22,6 @@ ### Changed * Minor compiler perf improvements. ([PR #17130](https://github.com/dotnet/fsharp/pull/17130)) -* Improve error of Active Pattern case Argument Count Not Match ([PR #16846](https://github.com/dotnet/fsharp/pull/16846)) +* Improve error messages for active pattern argument count mismatch ([PR #16846](https://github.com/dotnet/fsharp/pull/16846), [PR #17186](https://github.com/dotnet/fsharp/pull/17186)) * AsyncLocal diagnostics context. ([PR #16779](https://github.com/dotnet/fsharp/pull/16779)) -* Reduce allocations in compiler checking via `ValueOption` usage ([PR #16822](https://github.com/dotnet/fsharp/pull/16822)) \ No newline at end of file +* Reduce allocations in compiler checking via `ValueOption` usage ([PR #16822](https://github.com/dotnet/fsharp/pull/16822)) diff --git a/docs/release-notes/.FSharp.Core/8.0.400.md b/docs/release-notes/.FSharp.Core/8.0.400.md index 9ba04f0b47d..48a05231dc9 100644 --- a/docs/release-notes/.FSharp.Core/8.0.400.md +++ b/docs/release-notes/.FSharp.Core/8.0.400.md @@ -4,5 +4,4 @@ ### Changed -* Some more active pattern error message improvements. ([PR #17186](https://github.com/dotnet/fsharp/pull/17186)) * Cache delegate in query extensions. ([PR #17130](https://github.com/dotnet/fsharp/pull/17130)) From 94f05e845b0e3b8470d9d74f3d34c7899413dbdc Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Mon, 20 May 2024 13:54:46 -0400 Subject: [PATCH 5/6] Make comment slightly more correct --- src/Compiler/Checking/CheckExpressions.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs index 15ea6a791ea..840a41f56a8 100644 --- a/src/Compiler/Checking/CheckExpressions.fs +++ b/src/Compiler/Checking/CheckExpressions.fs @@ -5128,7 +5128,7 @@ and TcPatLongIdentActivePatternCase warnOnUpper (cenv: cenv) (env: TcEnv) vFlags | TyparConstraint.IsReferenceType _ | TyparConstraint.SupportsComparison _ | TyparConstraint.SupportsEquality _ -> false - // This could apply to unit if this RFC is implemented: + // This could apply to unit if the member is a method on obj, or if this RFC is implemented: // https://github.com/fsharp/fslang-design/blob/main/RFCs/FS-1043-extension-members-for-operators-and-srtp-constraints.md | TyparConstraint.MayResolveMember _ -> false // Any other kind of constraint cannot apply to unit. From 63ce52740cf12cd06a2fe8429baf5d565143f630 Mon Sep 17 00:00:00 2001 From: Brian Rourke Boll Date: Mon, 20 May 2024 15:17:57 -0400 Subject: [PATCH 6/6] More thorough & correct unit compat --- src/Compiler/Checking/CheckExpressions.fs | 50 +++++++++++++---------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs index 840a41f56a8..89253bd6783 100644 --- a/src/Compiler/Checking/CheckExpressions.fs +++ b/src/Compiler/Checking/CheckExpressions.fs @@ -5119,33 +5119,41 @@ and TcPatLongIdentActivePatternCase warnOnUpper (cenv: cenv) (env: TcEnv) vFlags let vExprTy = vExpr.Type let activePatArgsAsSynPats, patArg = - let isSolved ty = - let couldResolveToUnit constraints = - constraints - |> List.exists (fun c -> - match c with - // These apply to unit. - | TyparConstraint.IsReferenceType _ - | TyparConstraint.SupportsComparison _ - | TyparConstraint.SupportsEquality _ -> false - // This could apply to unit if the member is a method on obj, or if this RFC is implemented: - // https://github.com/fsharp/fslang-design/blob/main/RFCs/FS-1043-extension-members-for-operators-and-srtp-constraints.md - | TyparConstraint.MayResolveMember _ -> false - // Any other kind of constraint cannot apply to unit. - | _ -> true) - - tryDestTyparTy g ty - |> ValueOption.forall (fun typar -> typar.IsSolved || couldResolveToUnit typar.Constraints) - - // only cases which return unit or unresolved type (in AP definition) can omit output arg + // only cases which return unit or unresolved type (in AP definition) compatible with unit can omit output arg let canOmit retTy = + let couldResolveToUnit ty = + tryDestTyparTy g ty + |> ValueOption.exists (fun typar -> + not typar.IsSolved + && typar.Constraints |> List.forall (fun c -> + let (|Unit|_|) ty = if isUnitTy g ty then Some Unit else None + + match c with + // These apply or could apply to unit. + | TyparConstraint.IsReferenceType _ + | TyparConstraint.SupportsComparison _ + | TyparConstraint.SupportsEquality _ + | TyparConstraint.DefaultsTo (ty = Unit) + | TyparConstraint.MayResolveMember _ -> true + + // Any other kind of constraint is incompatible with unit. + | TyparConstraint.CoercesTo _ + | TyparConstraint.DefaultsTo _ + | TyparConstraint.IsDelegate _ + | TyparConstraint.IsEnum _ + | TyparConstraint.IsNonNullableStruct _ + | TyparConstraint.IsUnmanaged _ + | TyparConstraint.RequiresDefaultConstructor _ + | TyparConstraint.SimpleChoice _ + | TyparConstraint.SupportsNull _ -> false)) + let caseRetTy = if isOptionTy g retTy then destOptionTy g retTy elif isValueOptionTy g retTy then destValueOptionTy g retTy elif isChoiceTy g retTy then destChoiceTy g retTy idx else retTy - isUnitTy g caseRetTy || not (isSolved caseRetTy) + isUnitTy g caseRetTy || couldResolveToUnit caseRetTy // This bit of type-directed analysis ensures that parameterized partial active patterns returning unit do not need to take an argument let dtys, retTy = stripFunTy g vExprTy @@ -5192,7 +5200,7 @@ and TcPatLongIdentActivePatternCase warnOnUpper (cenv: cenv) (env: TcEnv) vFlags showErrMsg 1 // active pattern in function param (e.g. let f (|P|_|) = ...) - elif not (isSolved vExprTy) then + elif tryDestTyparTy g vExprTy |> ValueOption.exists (fun typar -> not typar.IsSolved) then List.frontAndBack args // args count should equal to AP function params count