diff --git a/src/Compiler/Checking/CheckComputationExpressions.fs b/src/Compiler/Checking/CheckComputationExpressions.fs index e27dcffc7bf..8ac9066557b 100644 --- a/src/Compiler/Checking/CheckComputationExpressions.fs +++ b/src/Compiler/Checking/CheckComputationExpressions.fs @@ -229,7 +229,10 @@ let TcComputationExpression (cenv: cenv) env (overallTy: OverallTy) tpenv (mWhol // Give bespoke error messages for the FSharp.Core "query" builder let isQuery = match stripDebugPoints interpExpr with - | Expr.Val (vref, _, m) -> + // An unparameterized custom builder, e.g., `query`, `async`. + | Expr.Val (vref, _, m) + // A parameterized custom builder, e.g., `builder<…>`, `builder ()`. + | Expr.App (funcExpr = Expr.Val (vref, _, m)) -> let item = Item.CustomBuilder (vref.DisplayName, vref) CallNameResolutionSink cenv.tcSink (m, env.NameEnv, item, emptyTyparInst, ItemOccurence.Use, env.eAccessRights) valRefEq cenv.g vref cenv.g.query_value_vref diff --git a/tests/service/Symbols.fs b/tests/service/Symbols.fs index 59ac5b9b84f..bd8f138ef48 100644 --- a/tests/service/Symbols.fs +++ b/tests/service/Symbols.fs @@ -951,4 +951,127 @@ let nestedFunc (a: RecordA) = { a with Zoo.Foo = 1; Zoo.Zoo.Bar = 2; Zoo.Ba Assert.AreEqual ("RecordA`1", field.DeclaringEntity.Value.CompiledName) assertRange (4, 87) (4, 90) fieldSymbolUse.Range - | _ -> Assert.Fail "Symbol was not FSharpField" \ No newline at end of file + | _ -> Assert.Fail "Symbol was not FSharpField" + +module ComputationExpressions = + [] + let ``IsFromComputationExpression only returns true for 'builder' in 'builder { … }'`` () = + let _, checkResults = getParseAndCheckResults """ +type Builder () = + member _.Return x = x + member _.Run x = x + +let builder = Builder () + +let x = builder { return 3 } +let y = builder +let z = Builder () { return 3 } +""" + + shouldEqual + [ + // type Builder () = + (2, 5), false + + // … = Builder () + (6, 14), false + + // let builder = … + (6, 4), false + + // let x = builder { return 3 } + (8, 8), false // Item.Value _ + (8, 8), true // Item.CustomBuilder _ + + // let y = builder + (9, 8), false + + // let z = Builder () { return 3 } + (10, 8), false + ] + [ + for symbolUse in checkResults.GetAllUsesOfAllSymbolsInFile() do + match symbolUse.Symbol.DisplayName with + | "Builder" | "builder" -> (symbolUse.Range.StartLine, symbolUse.Range.StartColumn), symbolUse.IsFromComputationExpression + | _ -> () + ] + + [] + let ``IsFromComputationExpression only returns true for 'builder' in 'builder<…> { … }'`` () = + let _, checkResults = getParseAndCheckResults """ +type Builder<'T> () = + member _.Return x = x + member _.Run x = x + +let builder<'T> = Builder<'T> () + +let x = builder { return 3 } +let y = builder { return 3 } +let z = builder<_> { return 3 } +let p = builder +let q<'T> = builder<'T> +""" + + shouldEqual + [ + // let builder<'T> = Builder<'T> () + (6, 4), false + + // let x = builder { return 3 } + (8, 8), false // Item.Value _ + (8, 8), true // Item.CustomBuilder _ + + // let y = builder { return 3 } + (9, 8), false // Item.Value _ + (9, 8), true // Item.CustomBuilder _ + + // let z = builder<_> { return 3 } + (10, 8), false // Item.Value _ + (10, 8), true // Item.CustomBuilder _ + + // let p = builder + (11, 8), false + + // let q<'T> = builder<'T> + (12, 12), false + ] + [ + for symbolUse in checkResults.GetAllUsesOfAllSymbolsInFile() do + if symbolUse.Symbol.DisplayName = "builder" then + (symbolUse.Range.StartLine, symbolUse.Range.StartColumn), symbolUse.IsFromComputationExpression + ] + + [] + let ``IsFromComputationExpression only returns true for 'builder' in 'builder () { … }'`` () = + let _, checkResults = getParseAndCheckResults """ +type Builder () = + member _.Return x = x + member _.Run x = x + +let builder () = Builder () + +let x = builder () { return 3 } +let y = builder () +let z = builder +""" + + shouldEqual + [ + // let builder () = Builder () + (6, 4), false + + // let x = builder () { return 3 } + (8, 8), false // Item.Value _ + (8, 8), true // Item.CustomBuilder _ + + // let y = builder () + (9, 8), false + + // let z = builder + (10, 8), false + ] + [ + for symbolUse in checkResults.GetAllUsesOfAllSymbolsInFile() do + if symbolUse.Symbol.DisplayName = "builder" then + (symbolUse.Range.StartLine, symbolUse.Range.StartColumn), symbolUse.IsFromComputationExpression + ]