diff --git a/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md b/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md index 52228f33b0..4273af66f6 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md @@ -1,3 +1,4 @@ ### Fixed * Allow `let!` and `use!` type annotations without requiring parentheses ([PR #18508](https://github.com/dotnet/fsharp/pull/18508)) +* Shorthand lambda: fix completion for chained calls and analysis for unfinished expression ([PR #18560](https://github.com/dotnet/fsharp/pull/18560)) diff --git a/src/Compiler/Service/ServiceParsedInputOps.fs b/src/Compiler/Service/ServiceParsedInputOps.fs index f5b1593690..b8ffe0d030 100644 --- a/src/Compiler/Service/ServiceParsedInputOps.fs +++ b/src/Compiler/Service/ServiceParsedInputOps.fs @@ -320,6 +320,13 @@ module ParsedInput = let _, r = CheckLongIdent longIdent Some r + | SynExpr.DotLambda(SynExpr.LongIdent _, range, _) -> Some range + | SynExpr.DotLambda(synExpr, range, _) -> + let result = traverseSynExpr synExpr + + result + |> Option.map (fun r -> if posEq r.Start synExpr.Range.Start then range else r) + | SynExpr.DotGet(synExpr, _dotm, lid, _) -> let (SynLongIdent(longIdent, _, _)) = lid diff --git a/src/Compiler/SyntaxTree/SyntaxTreeOps.fs b/src/Compiler/SyntaxTree/SyntaxTreeOps.fs index 6633dab240..616191c9b5 100644 --- a/src/Compiler/SyntaxTree/SyntaxTreeOps.fs +++ b/src/Compiler/SyntaxTree/SyntaxTreeOps.fs @@ -103,6 +103,8 @@ let rec pushUnaryArg expr arg = SynExpr.TypeApp(innerExpr, mLess, tyargs, mCommas, mGreater, mTypars, m) | SynExpr.ArbitraryAfterError(_, m) when m.Start = m.End -> SynExpr.DiscardAfterMissingQualificationAfterDot(SynExpr.Ident arg, m.StartRange, unionRanges arg.idRange m) + | SynExpr.DiscardAfterMissingQualificationAfterDot(synExpr, dotRange, m) -> + SynExpr.DiscardAfterMissingQualificationAfterDot(pushUnaryArg synExpr arg, dotRange, unionRanges arg.idRange m) | _ -> errorR (Error(FSComp.SR.tcDotLambdaAtNotSupportedExpression (), expr.Range)) expr diff --git a/tests/FSharp.Compiler.ComponentTests/Language/DotLambdaTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/DotLambdaTests.fs index ab135a31be..c8a34da06d 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/DotLambdaTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/DotLambdaTests.fs @@ -105,6 +105,17 @@ let myFunction (x:MyRecord) = x |> _.DoStuff 1 2 3""" Error 72, Line 4, Col 36, Line 4, Col 45, "Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved." ] +[] +let ``Underscore Dot Lambda - Missing qualification after dot`` () = + Fsx """ +"" |> _.Length. """ + |> withLangVersion80 + |> typecheck + |> shouldFail + |> withDiagnostics [ + Error 599, Line 2, Col 15, Line 2, Col 16, "Missing qualification after '.'" + ] + [] let ``Underscore Dot Length on string`` () = Fsx """ diff --git a/tests/FSharp.Compiler.Service.Tests/CompletionTests.fs b/tests/FSharp.Compiler.Service.Tests/CompletionTests.fs index 2d70f5c58f..cfe43453d7 100644 --- a/tests/FSharp.Compiler.Service.Tests/CompletionTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/CompletionTests.fs @@ -78,29 +78,140 @@ let record = { Field = 1 } assertHasItemWithNames ["Field"; "record"] info [] -let ``Underscore dot lambda - completion`` () = +let ``Underscore dot lambda - completion 01`` () = let info = getCompletionInfo """ -let myFancyFunc (x:string) = - x - |> _.Len{caret}""" +"" |> _.Len{caret}""" + + assertHasItemWithNames ["Length"] info + +[] +let ``Underscore dot lambda - completion 02`` () = + let info = getCompletionInfo """ +System.DateTime.Now |> _.TimeOfDay.Mill{caret}""" + + assertHasItemWithNames ["Milliseconds"] info + +[] +let ``Underscore dot lambda - completion 03`` () = + let info = getCompletionInfo """ +"" |> _.ToString().Len{caret}""" + + assertHasItemWithNames ["Length"] info + +[] +let ``Underscore dot lambda - completion 04`` () = + let info = getCompletionInfo """ +"" |> _.Len{caret}gth.ToString()""" + + assertHasItemWithNames ["Length"] info + +[] +let ``Underscore dot lambda - completion 05`` () = + let info = getCompletionInfo """ +"" |> _.Length.ToString().Chars("".Len{caret})""" + assertHasItemWithNames ["Length"] info [] -let ``Underscore dot lambda - method completion`` () = +let ``Underscore dot lambda - completion 06`` () = + let info = getCompletionInfo """ +"" |> _.Chars(System.DateTime.UtcNow.Tic{caret}).ToString()""" + + assertHasItemWithNames ["Ticks"] info + +[] +let ``Underscore dot lambda - completion 07`` () = + let info = getCompletionInfo """ +"" |> _.Length.ToString().Len{caret}""" + + assertHasItemWithNames ["Length"] info + +[] +let ``Underscore dot lambda - completion 08`` () = + let info = getCompletionInfo """ +System.DateTime.Now |> _.TimeOfDay + .Mill{caret}""" + + assertHasItemWithNames ["Milliseconds"] info + +[] +let ``Underscore dot lambda - completion 09`` () = + let info = getCompletionInfo """ +"" |> _.Length.ToSt{caret}.Length""" + + assertHasItemWithNames ["ToString"] info + +[] +let ``Underscore dot lambda - completion 10`` () = + let info = getCompletionInfo """ +"" |> _.Chars(0).ToStr{caret}.Length""" + + assertHasItemWithNames ["ToString"] info + +[] +let ``Underscore dot lambda - completion 11`` () = + let info = getCompletionInfo """ +open System.Linq + +[[""]] |> _.Select(_.Head.ToL{caret})""" + + assertHasItemWithNames ["ToLower"] info + +[] +let ``Underscore dot lambda - completion 12`` () = + let info = getCompletionInfo """ +open System.Linq + +[[[""]]] |> _.Head.Select(_.Head.ToL{caret})""" + + assertHasItemWithNames ["ToLower"] info + +[] +let ``Underscore dot lambda - completion 13`` () = let info = getCompletionInfo """ -let myFancyFunc (x:string) = - x +let myFancyFunc (x:string) = + x |> _.ToL{caret}""" assertHasItemWithNames ["ToLower"] info [] -let ``Underscore dot lambda - No prefix`` () = +let ``Underscore dot lambda - completion 14`` () = + let info = getCompletionInfo """ +let myFancyFunc (x:System.DateTime) = + x + |> _.TimeOfDay.Mill{caret} + |> id""" + assertHasItemWithNames ["Milliseconds"] info + +[] +let ``Underscore dot lambda - completion 15`` () = + let info = getCompletionInfo """ +let _a = 5 +"" |> _{caret}.Length.ToString() """ + assertHasItemWithNames ["_a"] info + +[] +let ``Underscore dot lambda - No prefix 01`` () = let info = getCompletionInfo """ let s = "" -[s] |> List.map _.{caret} +[s] |> List.map _.{caret} """ assertHasItemWithNames ["Length"] info +[] +let ``Underscore dot lambda - No prefix 02`` () = + let info = getCompletionInfo """ +System.DateTime.Now |> _.TimeOfDay.{caret}""" + + assertHasItemWithNames ["Milliseconds"] info + +[] +let ``Underscore dot lambda - No prefix 03`` () = + let info = getCompletionInfo """ +"" |> _.Length.ToString().{caret}""" + + assertHasItemWithNames ["Length"] info + [] let ``Type decl - Record - Field type 01`` () = let info = getCompletionInfo """ diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl index e81c636f40..4f5daf0954 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl @@ -34,7 +34,7 @@ [IL]: Error [StackUnexpected]: : .$FSharpCheckerResults+dataTipOfReferences@2225::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000084][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.AssemblyContent+traverseMemberFunctionAndValues@176::Invoke([FSharp.Compiler.Service]FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue)][offset 0x00000059][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.AssemblyContent+traverseEntity@218::GenerateNext([S.P.CoreLib]System.Collections.Generic.IEnumerable`1&)][offset 0x000000DA][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.ParsedInput+visitor@1424-6::VisitExpr([FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>, [FSharp.Compiler.Service]FSharp.Compiler.Syntax.SynExpr)][offset 0x00000605][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.ParsedInput+visitor@1431-6::VisitExpr([FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>, [FSharp.Compiler.Service]FSharp.Compiler.Syntax.SynExpr)][offset 0x00000605][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@922-509::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@922-509::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@922-509::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000082][found Char] Unexpected type on the stack. diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl index b53d7c7ecc..e1bc01e19b 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl @@ -33,7 +33,7 @@ [IL]: Error [StackUnexpected]: : .$FSharpCheckerResults+GetReferenceResolutionStructuredToolTipText@2225::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000076][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.AssemblyContent+traverseMemberFunctionAndValues@176::Invoke([FSharp.Compiler.Service]FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue)][offset 0x0000002B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.AssemblyContent+traverseEntity@218::GenerateNext([S.P.CoreLib]System.Collections.Generic.IEnumerable`1&)][offset 0x000000BB][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.ParsedInput+visitor@1424-11::VisitExpr([FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>, [FSharp.Compiler.Service]FSharp.Compiler.Syntax.SynExpr)][offset 0x00000620][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.ParsedInput+visitor@1431-11::VisitExpr([FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>, [FSharp.Compiler.Service]FSharp.Compiler.Syntax.SynExpr)][offset 0x00000620][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@922-530::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@922-530::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@922-530::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000064][found Char] Unexpected type on the stack.