Last active
June 12, 2026 08:47
-
-
Save T-Gro/5e2df1271a16db22b714aaad403c31f0 to your computer and use it in GitHub Desktop.
Spread IDE service tests - RED on dotnet/fsharp#18927 (apply with git apply)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| diff --git a/tests/FSharp.Compiler.Service.Tests/CompletionTests.fs b/tests/FSharp.Compiler.Service.Tests/CompletionTests.fs | |
| index cdcbf7a0b8..048759d779 100644 | |
| --- a/tests/FSharp.Compiler.Service.Tests/CompletionTests.fs | |
| +++ b/tests/FSharp.Compiler.Service.Tests/CompletionTests.fs | |
| @@ -860,4 +860,31 @@ let normalize' x = x + 1 | |
| normaliz{caret} | |
| """ | |
| - assertHasItemWithNames [ "normalize'" ] info | |
| \ No newline at end of file | |
| + assertHasItemWithNames [ "normalize'" ] info | |
| +module RecordSpreads = | |
| + let private getPreviewCompletionInfo markedSource = | |
| + Checker.getCompletionInfoWithCompilerAndCompletionOptions | |
| + [| "--langversion:preview" |] | |
| + FSharpCodeCompletionOptions.Default | |
| + markedSource | |
| + | |
| + [<Fact>] | |
| + let ``spread - completion fires inside nominal record spread`` () = | |
| + let info = getPreviewCompletionInfo """ | |
| +type R = { A: int; B: int } | |
| +let r1 = { A = 1; B = 2 } | |
| +let r2 = { ...{caret} } | |
| +""" | |
| + let names = info.Items |> Array.map _.NameInCode | |
| + if not (Array.contains "r1" names) then | |
| + failwith $"Expected completion at '{{ ...|caret| }}' to offer in-scope record value 'r1', but got %d{names.Length} item(s)." | |
| + | |
| + [<Fact>] | |
| + let ``spread - completion fires inside anonymous record spread`` () = | |
| + let info = getPreviewCompletionInfo """ | |
| +let r1 = {| A = 1; B = 2 |} | |
| +let r2 = {| ...{caret} ; X = 1 |} | |
| +""" | |
| + let names = info.Items |> Array.map _.NameInCode | |
| + if not (Array.contains "r1" names) then | |
| + failwith $"Expected completion at '{{| ...|caret| ; X = 1 |}}' to offer in-scope record value 'r1', but got %d{names.Length} item(s)." | |
| \ No newline at end of file | |
| diff --git a/tests/FSharp.Compiler.Service.Tests/Symbols.fs b/tests/FSharp.Compiler.Service.Tests/Symbols.fs | |
| index 25cc0cd4b6..91dc13dfae 100644 | |
| --- a/tests/FSharp.Compiler.Service.Tests/Symbols.fs | |
| +++ b/tests/FSharp.Compiler.Service.Tests/Symbols.fs | |
| @@ -1545,3 +1545,55 @@ let result = 1 -.- 2 | |
| let rangeLength = range.EndColumn - range.StartColumn | |
| Assert.Equal(3, rangeLength) | |
| + | |
| +module RecordSpreads = | |
| + open FSharp.Compiler.EditorServices | |
| + | |
| + [<Fact>] | |
| + let ``spread - spread operator is not classified as a record field`` () = | |
| + let _, checkResults = | |
| + getParseAndCheckResultsPreview """ | |
| +type R1 = { A : int; B : int } | |
| +type R2 = { ...R1; C : int } | |
| +""" | |
| + let items = checkResults.GetSemanticClassification(None, RelatedSymbolUseKind.All) | |
| + let badItems = | |
| + items | |
| + |> Array.filter (fun i -> | |
| + i.Type = SemanticClassificationType.RecordField | |
| + && i.Range.StartLine = 3 | |
| + && i.Range.StartColumn < 15 | |
| + && i.Range.EndColumn > 12) | |
| + if badItems.Length > 0 then | |
| + failwith $"Expected the '...' spread operator to NOT be classified as RecordField, but found: %A{badItems |> Array.map (fun i -> getRangeCoords i.Range)}" | |
| + | |
| + [<Fact>] | |
| + let ``spread - GetSymbolUseAtLocation range excludes leading spread operator`` () = | |
| + let _, checkResults = | |
| + getParseAndCheckResultsPreview """ | |
| +type R1 = { A : int; B : int } | |
| +let r1 = { A = 1; B = 2 } | |
| +let r2 = { ...r1; C = 3 } | |
| +""" | |
| + let line4 = "let r2 = { ...r1; C = 3 }" | |
| + match checkResults.GetSymbolUseAtLocation(4, 16, line4, [ "r1" ]) with | |
| + | None -> failwith "Expected to resolve symbol 'r1' inside the spread '...r1'." | |
| + | Some su -> | |
| + let spreadUse = | |
| + checkResults.GetUsesOfSymbolInFile(su.Symbol) | |
| + |> Array.find (fun u -> not u.IsFromDefinition) | |
| + if getRangeCoords su.Range <> getRangeCoords spreadUse.Range then | |
| + failwith $"GetSymbolUseAtLocation range %A{getRangeCoords su.Range} should match GetUsesOfSymbolInFile range %A{getRangeCoords spreadUse.Range} (no leading '...')." | |
| + | |
| + [<Fact>] | |
| + let ``spread - synthesized fields have distinct DeclarationLocations`` () = | |
| + let _, checkResults = | |
| + getParseAndCheckResultsPreview """ | |
| +type R1 = { A : int; B : int } | |
| +type R2 = { ...R1; C : int } | |
| +""" | |
| + let r2 = findSymbolByName "R2" checkResults :?> FSharpEntity | |
| + let r2A = r2.FSharpFields |> Seq.find (fun f -> f.Name = "A") | |
| + let r2B = r2.FSharpFields |> Seq.find (fun f -> f.Name = "B") | |
| + if getRangeCoords r2A.DeclarationLocation = getRangeCoords r2B.DeclarationLocation then | |
| + failwith $"Expected spread-synthesized fields A and B to have distinct DeclarationLocations, but both are %A{getRangeCoords r2A.DeclarationLocation}." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment