Skip to content

Shorthand lambda: fix completion for chained calls #18560

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
May 19, 2025
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/10.0.100.md
Original file line number Diff line number Diff line change
@@ -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))
7 changes: 7 additions & 0 deletions src/Compiler/Service/ServiceParsedInputOps.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions src/Compiler/SyntaxTree/SyntaxTreeOps.fs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ 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, _, _) -> pushUnaryArg synExpr arg
| _ ->
errorR (Error(FSComp.SR.tcDotLambdaAtNotSupportedExpression (), expr.Range))
expr
Expand Down
11 changes: 11 additions & 0 deletions tests/FSharp.Compiler.ComponentTests/Language/DotLambdaTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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."
]

[<Fact>]
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 '.'"
]

[<Fact>]
let ``Underscore Dot Length on string`` () =
Fsx """
Expand Down
129 changes: 120 additions & 9 deletions tests/FSharp.Compiler.Service.Tests/CompletionTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -78,29 +78,140 @@ let record = { Field = 1 }
assertHasItemWithNames ["Field"; "record"] info

[<Fact>]
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

[<Fact>]
let ``Underscore dot lambda - completion 02`` () =
let info = getCompletionInfo """
System.DateTime.Now |> _.TimeOfDay.Mill{caret}"""

assertHasItemWithNames ["Milliseconds"] info

[<Fact>]
let ``Underscore dot lambda - completion 03`` () =
let info = getCompletionInfo """
"" |> _.ToString().Len{caret}"""

assertHasItemWithNames ["Length"] info

[<Fact>]
let ``Underscore dot lambda - completion 04`` () =
let info = getCompletionInfo """
"" |> _.Len{caret}gth.ToString()"""

assertHasItemWithNames ["Length"] info

[<Fact>]
let ``Underscore dot lambda - completion 05`` () =
let info = getCompletionInfo """
"" |> _.Length.ToString().Chars("".Len{caret})"""

assertHasItemWithNames ["Length"] info

[<Fact>]
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

[<Fact>]
let ``Underscore dot lambda - completion 07`` () =
let info = getCompletionInfo """
"" |> _.Length.ToString().Len{caret}"""

assertHasItemWithNames ["Length"] info

[<Fact>]
let ``Underscore dot lambda - completion 08`` () =
let info = getCompletionInfo """
System.DateTime.Now |> _.TimeOfDay
.Mill{caret}"""

assertHasItemWithNames ["Milliseconds"] info

[<Fact>]
let ``Underscore dot lambda - completion 09`` () =
let info = getCompletionInfo """
"" |> _.Length.ToSt{caret}.Length"""

assertHasItemWithNames ["ToString"] info

[<Fact>]
let ``Underscore dot lambda - completion 10`` () =
let info = getCompletionInfo """
"" |> _.Chars(0).ToStr{caret}.Length"""

assertHasItemWithNames ["ToString"] info

[<Fact>]
let ``Underscore dot lambda - completion 11`` () =
let info = getCompletionInfo """
open System.Linq

[[""]] |> _.Select(_.Head.ToL{caret})"""

assertHasItemWithNames ["ToLower"] info

[<Fact>]
let ``Underscore dot lambda - completion 12`` () =
let info = getCompletionInfo """
open System.Linq

[[[""]]] |> _.Head.Select(_.Head.ToL{caret})"""

assertHasItemWithNames ["ToLower"] info

[<Fact>]
let ``Underscore dot lambda - completion 13`` () =
let info = getCompletionInfo """
let myFancyFunc (x:string) =
x
let myFancyFunc (x:string) =
x
|> _.ToL{caret}"""
assertHasItemWithNames ["ToLower"] info

[<Fact>]
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

[<Fact>]
let ``Underscore dot lambda - completion 15`` () =
let info = getCompletionInfo """
let _a = 5
"" |> _{caret}.Length.ToString() """
assertHasItemWithNames ["_a"] info

[<Fact>]
let ``Underscore dot lambda - No prefix 01`` () =
let info = getCompletionInfo """
let s = ""
[s] |> List.map _.{caret}
[s] |> List.map _.{caret}
"""
assertHasItemWithNames ["Length"] info

[<Fact>]
let ``Underscore dot lambda - No prefix 02`` () =
let info = getCompletionInfo """
System.DateTime.Now |> _.TimeOfDay.{caret}"""

assertHasItemWithNames ["Milliseconds"] info

[<Fact>]
let ``Underscore dot lambda - No prefix 03`` () =
let info = getCompletionInfo """
"" |> _.Length.ToString().{caret}"""

assertHasItemWithNames ["Length"] info

[<Fact>]
let ``Type decl - Record - Field type 01`` () =
let info = getCompletionInfo """
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
[IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$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<FSharp.Compiler.EditorServices.AssemblySymbol>&)][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.Compiler.Syntax.SyntaxNode>, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<FSharp.Compiler.Syntax.SynExpr,Microsoft.FSharp.Core.FSharpOption`1<FSharp.Compiler.EditorServices.CompletionContext>>, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<FSharp.Compiler.Syntax.SynExpr,Microsoft.FSharp.Core.FSharpOption`1<FSharp.Compiler.EditorServices.CompletionContext>>, [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.Compiler.Syntax.SyntaxNode>, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<FSharp.Compiler.Syntax.SynExpr,Microsoft.FSharp.Core.FSharpOption`1<FSharp.Compiler.EditorServices.CompletionContext>>, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<FSharp.Compiler.Syntax.SynExpr,Microsoft.FSharp.Core.FSharpOption`1<FSharp.Compiler.EditorServices.CompletionContext>>, [FSharp.Compiler.Service]FSharp.Compiler.Syntax.SynExpr)][offset 0x00000605][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$ServiceLexing+clo@922-509::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<System.Tuple`3<FSharp.Compiler.Parser+token,int32,int32>,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$ServiceLexing+clo@922-509::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<System.Tuple`3<FSharp.Compiler.Parser+token,int32,int32>,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$ServiceLexing+clo@922-509::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<System.Tuple`3<FSharp.Compiler.Parser+token,int32,int32>,Microsoft.FSharp.Core.Unit>)][offset 0x00000082][found Char] Unexpected type on the stack.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
[IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$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<FSharp.Compiler.EditorServices.AssemblySymbol>&)][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.Compiler.Syntax.SyntaxNode>, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<FSharp.Compiler.Syntax.SynExpr,Microsoft.FSharp.Core.FSharpOption`1<FSharp.Compiler.EditorServices.CompletionContext>>, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<FSharp.Compiler.Syntax.SynExpr,Microsoft.FSharp.Core.FSharpOption`1<FSharp.Compiler.EditorServices.CompletionContext>>, [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.Compiler.Syntax.SyntaxNode>, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<FSharp.Compiler.Syntax.SynExpr,Microsoft.FSharp.Core.FSharpOption`1<FSharp.Compiler.EditorServices.CompletionContext>>, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<FSharp.Compiler.Syntax.SynExpr,Microsoft.FSharp.Core.FSharpOption`1<FSharp.Compiler.EditorServices.CompletionContext>>, [FSharp.Compiler.Service]FSharp.Compiler.Syntax.SynExpr)][offset 0x00000620][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$ServiceLexing+clo@922-530::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<System.Tuple`3<FSharp.Compiler.Parser+token,int32,int32>,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$ServiceLexing+clo@922-530::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<System.Tuple`3<FSharp.Compiler.Parser+token,int32,int32>,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack.
[IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$ServiceLexing+clo@922-530::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<System.Tuple`3<FSharp.Compiler.Parser+token,int32,int32>,Microsoft.FSharp.Core.Unit>)][offset 0x00000064][found Char] Unexpected type on the stack.
Expand Down
Loading