diff --git a/docs/release-notes/.FSharp.Compiler.Service/11.0.0.md b/docs/release-notes/.FSharp.Compiler.Service/11.0.0.md index 655ad02ad2a..0ea4c87d9bf 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.0.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.0.md @@ -9,6 +9,8 @@ ### Added +* Type checker: recover function type unification in method checking ([#19036](https://github.com/dotnet/fsharp/pull/19036#pullrequestreview-3392846258)) + ### Changed * Parallel compilation stabilised and enabled by default ([PR #18998](https://github.com/dotnet/fsharp/pull/18998)) diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index 5d9197599be..e4313fa393f 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -689,6 +689,20 @@ let UnifyFunctionType extraInfo (cenv: cenv) denv mFunExpr ty = | Some argm -> error (NotAFunction(denv, ty, mFunExpr, argm)) | None -> error (FunctionExpected(denv, ty, mFunExpr)) +let UnifyFunctionTypeAndRecover extraInfo (cenv: cenv) denv mFunExpr ty = + match UnifyFunctionTypeUndoIfFailed cenv denv mFunExpr ty with + | ValueSome res -> res + | ValueNone -> + match extraInfo with + | Some argm -> errorR (NotAFunction(denv, ty, mFunExpr, argm)) + | None -> errorR (FunctionExpected(denv, ty, mFunExpr)) + + let g = cenv.g + let domainTy = NewInferenceType g + let resultTy = NewInferenceType g + domainTy, resultTy + + let ReportImplicitlyIgnoredBoolExpression denv m ty expr = let checkExpr m expr = match stripDebugPoints expr with @@ -9988,7 +10002,7 @@ and TcMethodApplication_UniqueOverloadInference // type we assume the number of arguments is just "1". | None, _ -> - let domainTy, returnTy = UnifyFunctionType None cenv denv mMethExpr exprTy.Commit + let domainTy, returnTy = UnifyFunctionTypeAndRecover None cenv denv mMethExpr exprTy.Commit let argTys = if isUnitTy g domainTy then [] else tryDestRefTupleTy g domainTy // Only apply this rule if a candidate method exists with this number of arguments let argTys = @@ -10068,7 +10082,7 @@ and TcMethodApplication_CheckArguments let curriedArgTys, returnTy = UnifyMatchingSimpleArgumentTypes cenv env exprTy.Commit calledMeth mMethExpr mItem curriedArgTys, paramNamesIfFeatureEnabled g calledMeth, MustEqual returnTy | _ -> - let domainTy, returnTy = UnifyFunctionType None cenv denv mMethExpr exprTy.Commit + let domainTy, returnTy = UnifyFunctionTypeAndRecover None cenv denv mMethExpr exprTy.Commit let argTys = if isUnitTy g domainTy then [] else tryDestRefTupleTy g domainTy // Only apply this rule if a candidate method exists with this number of arguments let argTys, argNames = diff --git a/tests/FSharp.Compiler.Service.Tests/TypeChecker/TypeCheckerRecoveryTests.fs b/tests/FSharp.Compiler.Service.Tests/TypeChecker/TypeCheckerRecoveryTests.fs index 07827b7d399..56221f808e6 100644 --- a/tests/FSharp.Compiler.Service.Tests/TypeChecker/TypeCheckerRecoveryTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/TypeChecker/TypeCheckerRecoveryTests.fs @@ -1,9 +1,20 @@ module FSharp.Compiler.Service.Tests.TypeChecker.TypeCheckerRecoveryTests open FSharp.Compiler.Service.Tests +open FSharp.Compiler.Text open FSharp.Test.Assert open Xunit +let assertHasSymbolUsageAtCaret name source = + let context, checkResults = Checker.getCheckedResolveContext source + + getSymbolUses checkResults + |> Seq.exists (fun symbolUse -> + Range.rangeContainsPos symbolUse.Range context.Pos && + symbolUse.Symbol.DisplayNameCore = name + ) + |> shouldEqual true + [] let ``Let 01`` () = let _, checkResults = getParseAndCheckResults """ @@ -49,4 +60,42 @@ Math.Max(a,b,) "(4,0--4,14)", 503 ] - assertHasSymbolUsages ["Max"] checkResults \ No newline at end of file + assertHasSymbolUsages ["Max"] checkResults + +module Expressions = + [] + let ``Method type 01`` () = + assertHasSymbolUsageAtCaret "ToString" """ +if true then + "".ToString{caret} +""" + + + [] + let ``Method type 02`` () = + assertHasSymbolUsageAtCaret "M" """ +type T = + static member M() = "" + +if true then + T.M{caret} +""" + + [] + let ``Method type 03`` () = + assertHasSymbolUsageAtCaret "M" """ +type T = + static member M(i: int) = "" + static member M(s: string) = "" + +if true then + T.M{caret} +""" + + [] + let ``Method type 04`` () = + assertHasSymbolUsageAtCaret "GetHashCode" """ +let o: obj = null +if true then + o.GetHashCode{caret} +""" \ No newline at end of file