Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
7192705
Avoid leaking a MeterListener per Cache in DEBUG builds (#19995)
NatElkins Jul 8, 2026
3ebcc74
Bump FCSMinorVersion to 13 (keep main above 10.0.4xx servicing 43.12.…
T-Gro Jul 9, 2026
87ad51b
Update dependencies from https://github.com/dotnet/msbuild build 2026…
dotnet-maestro[bot] Jul 9, 2026
d11945a
Update dependencies from https://github.com/dotnet/roslyn build 20260…
dotnet-maestro[bot] Jul 9, 2026
60d315a
Add ResetCompilerGeneratedNameState to compiler-generated name genera…
NatElkins Jul 9, 2026
5928e91
Update dependencies from https://github.com/dotnet/msbuild build 2026…
dotnet-maestro[bot] Jul 10, 2026
4eefd05
[main] Update dependencies from dotnet/msbuild (#20055)
dotnet-maestro[bot] Jul 21, 2026
2cd254e
[main] Update dependencies from dotnet/roslyn (#20052)
dotnet-maestro[bot] Jul 21, 2026
9fc230a
Localized file check-in by OneLocBuild Task: Build definition ID 499:…
dotnet-bot Jul 21, 2026
e729d97
[main] Update dependencies from dotnet/arcade (#20054)
dotnet-maestro[bot] Jul 22, 2026
69fca7f
Tests/source context: support multiple carets (#20077)
auduchinok Jul 22, 2026
2f07589
Move VS language-service logic tests to FSharp.Compiler.Service.Tests…
T-Gro Jul 23, 2026
24d731a
Add Roslyn-format EnC CustomDebugInformation codec and portable PDB m…
NatElkins Jul 24, 2026
2e838bb
Add stable synthesized-name replay infrastructure for hot reload (#20…
NatElkins Jul 24, 2026
4a2749e
Fix #19457: lift CE constructs from plain let RHS in computation expr…
T-Gro Jul 24, 2026
1d8dc39
Fix attribute resolution in recursive module/namespace scopes (#19744)
T-Gro Jul 24, 2026
1dc395a
Correct StructLayout size emission for data-less struct unions (#19759)
Copilot Jul 24, 2026
8c0e444
Report FS3888 for generic attribute type abbreviations instead of FS0…
T-Gro Jul 26, 2026
fd6ed49
Move to .NET 11 (SDK, Arcade, product TargetFramework) (#20080)
T-Gro Jul 28, 2026
0c94e4e
Support NotNullIfNotNullAttribute (#19977)
kerams Jul 28, 2026
5dfbf7f
Move SDL/TSA validation to 1ES templates after Arcade 11 upgrade (#20…
abonie Jul 29, 2026
17cb503
Compiled ToStrings under -reflectionfree for DUs and Records (#19976)
charlesroddie Jul 30, 2026
c00299f
Run ilverify via the tool manifest instead of a hard-coded cache path…
abonie Jul 30, 2026
f4b785f
Record spreads (#18927)
brianrourkeboll Aug 1, 2026
d3403ca
Implement interpolated strings via String.Concat (#19971)
charlesroddie Aug 1, 2026
f5c88eb
[main] Source code updates from dotnet/dotnet (#20058)
dotnet-maestro[bot] Aug 3, 2026
647548b
Secure release-note checks for fork pull requests (#20081)
NatElkins Aug 3, 2026
536800c
Update test project to net11 (#20104)
abonie Aug 3, 2026
a6b69ce
Update dependencies from https://github.com/dotnet/arcade build 20260…
dotnet-maestro[bot] Aug 4, 2026
3cc7755
Move to Roslyn's unified ExternalAccess library (#20099)
JoeRobich Aug 4, 2026
ea778bb
LexFilter: drop non-strict mode (#20106)
auduchinok Aug 4, 2026
36ce348
Enable Central Package Management with transitive pinning (#20084)
T-Gro Aug 4, 2026
d89529c
Implement direct delegates (#19993)
kerams Aug 4, 2026
7e2af62
Merge remote-tracking branch 'origin/main' into darc-release/10.0.4xx…
invalid-email-address Aug 4, 2026
92a3700
Fix check_release_notes 403 by restoring pull-requests: write and mak…
T-Gro Aug 4, 2026
8ed0ce1
Restore NuGetRepack UsingTask workaround for Arcade 10 in Microsoft.F…
invalid-email-address Aug 4, 2026
4d65a64
Merge remote-tracking branch 'origin/main' into darc-release/10.0.4xx…
invalid-email-address Aug 4, 2026
3bc2155
Update dependencies from https://github.com/dotnet/arcade build 20260…
dotnet-maestro[bot] Aug 5, 2026
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
Prev Previous commit
Next Next commit
Move VS language-service logic tests to FSharp.Compiler.Service.Tests (…
…#20033)

* Move VS language-service logic tests to FSharp.Compiler.Service.Tests

Port completion, quick info, parameter info, go-to-definition, and
diagnostics coverage from the Windows-only VS Salsa suite to the
cross-platform FSharp.Compiler.Service.Tests. The legacy suite keeps only
the tests that genuinely exercise Visual Studio integration.
  • Loading branch information
T-Gro authored Jul 23, 2026
commit 2f07589d742861372eecce48d06ab70e841f8408
50 changes: 49 additions & 1 deletion tests/FSharp.Compiler.Service.Tests/Checker.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ namespace FSharp.Compiler.Service.Tests
open System
open System.Text.RegularExpressions
open FSharp.Compiler.CodeAnalysis
open FSharp.Compiler.Diagnostics
open FSharp.Compiler.EditorServices
open FSharp.Compiler.Text
open FSharp.Compiler.Text.Range
open FSharp.Compiler.Tokenization
open FSharp.Test.Assert
open FSharp.Test.Compiler.Assertions.TextBasedDiagnosticAsserts
open Xunit

type SourceContext =
{ Source: string
Expand Down Expand Up @@ -193,6 +196,9 @@ module CheckResultsExtensions =
member this.GetTooltip(context: ResolveContext, width) =
this.GetToolTip(context.Pos.Line, context.Pos.Column, context.LineText, context.Names, FSharpTokenTag.Identifier, width)

member this.GetDeclarationLocation(context: ResolveContext) =
this.GetDeclarationLocation(context.Pos.Line, context.Pos.Column, context.LineText, context.Names)

member this.GetCodeCompletionSuggestions(context: CodeCompletionContext, parseResults: FSharpParseFileResults, options: FSharpCodeCompletionOptions) =
this.GetDeclarationListInfo(Some parseResults, context.Pos.Line, context.LineText, context.PartialIdentifier, options = options)

Expand Down Expand Up @@ -241,6 +247,11 @@ module Checker =
let getCompletionInfo markedSource =
getCompletionInfoWithOptions FSharpCodeCompletionOptions.Default markedSource

let getCompletionInfoOfSignatureFile markedSource =
let context = getCompletionContext markedSource
let parseResults, checkResults = getParseAndCheckResultsOfSignatureFile context.Source
checkResults.GetCodeCompletionSuggestions(context, parseResults, FSharpCodeCompletionOptions.Default)

let getSymbolUses (markedSource: string) =
let context, checkResults = getCheckedResolveContext markedSource
checkResults.GetSymbolUses(context)
Expand All @@ -249,14 +260,51 @@ module Checker =
let symbolUses = getSymbolUses markedSource
symbolUses |> List.exactlyOne

let getDeclarationLocation (markedSource: string) =
let context, checkResults = getCheckedResolveContext markedSource
checkResults.GetDeclarationLocation(context)

let getTooltipWithOptions (options: string array) (markedSource: string) =
let context = getResolveContext markedSource
let _, checkResults = getParseAndCheckResultsWithOptions options context.Source
checkResults.GetToolTip(context.Pos.Line, context.Pos.Column, context.LineText, context.Names, FSharpTokenTag.Identifier)
checkResults.GetTooltip(context)

let getTooltip (markedSource: string) =
getTooltipWithOptions [||] markedSource

let getMethodOverloads names (markedSource: string) =
let context, checkResults = getCheckedResolveContext markedSource
checkResults.GetMethodOverloads(context, names)

/// Shared assertion helpers reused by the completion, editor, tooltip and type-checker-recovery
/// test files. Defined here (an early-compiled file) so every consuming file sees a single
/// definition instead of redefining its own copy.
[<AutoOpen>]
module AssertHelpers =
let assertItemsWithNames contains names (completionInfo: DeclarationListInfo) =
let itemNames =
completionInfo.Items
|> Array.map _.NameInCode
|> Array.map normalizeNewLines
|> set

for name in names do
let name = normalizeNewLines name
Set.contains name itemNames |> shouldEqual contains

let assertHasItemWithNames names (completionInfo: DeclarationListInfo) =
assertItemsWithNames true names completionInfo

let assertHasNoItemsWithNames names (completionInfo: DeclarationListInfo) =
assertItemsWithNames false names completionInfo

let assertAndExtractTooltip (ToolTipText(items)) =
Assert.Equal(1, items.Length)
match items[0] with
| ToolTipElement.Group [ singleElement ] ->
let toolTipText =
singleElement.MainDescription
|> taggedTextToString
toolTipText, singleElement.XmlDoc, singleElement.Remarks |> Option.map taggedTextToString
| _ -> failwith $"Expected group, got {items[0]}"

30 changes: 22 additions & 8 deletions tests/FSharp.Compiler.Service.Tests/Common.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ open System.IO
open System.Collections.Generic
open System.Threading.Tasks
open FSharp.Compiler.CodeAnalysis
open FSharp.Compiler.Diagnostics
open FSharp.Compiler.IO
open FSharp.Compiler.Symbols
open FSharp.Compiler.Syntax
Expand Down Expand Up @@ -363,6 +364,12 @@ let getParseResultsOfSignatureFile (source: string) =
let getParseAndCheckResults (source: string) =
parseAndCheckScript("Test.fsx", source)

/// Reference/#load script tests must not share the checker's filename-keyed script-closure
/// cache: a shared "Test.fsx" lets one test's closure (its resolved/failed #r references and
/// their diagnostics) leak into the next. Give each such test a unique script identity.
let getParseAndCheckResultsUniqueName (source: string) =
parseAndCheckScript(Guid.NewGuid().ToString("N") + ".fsx", source)

let getParseAndCheckResultsWithOptions options source =
parseAndCheckScriptWithOptions ("Test.fsx", source, options)

Expand All @@ -376,15 +383,22 @@ let getParseAndCheckResults80 (source: string) =
parseAndCheckScript80("Test.fsx", source)


let inline dumpDiagnostics (results: FSharpCheckFileResults) =
let normalizeDiagnosticMessage (d: FSharpDiagnostic) =
d.Message.Split('\n')
|> Array.map _.Trim()
|> Array.filter (fun s -> s.Length > 0)
|> String.concat " "

let formatDiagnostic (d: FSharpDiagnostic) =
sprintf "%s: %s" (d.Range.ToString()) (normalizeDiagnosticMessage d)

let dumpDiagnostics (results: FSharpCheckFileResults) =
results.Diagnostics |> Array.map formatDiagnostic |> List.ofArray

let dumpDiagnosticsOfSeverity (severity: FSharpDiagnosticSeverity) (results: FSharpCheckFileResults) =
results.Diagnostics
|> Array.map (fun e ->
let message =
e.Message.Split('\n')
|> Array.map _.Trim()
|> Array.filter (fun s -> s.Length > 0)
|> String.concat " "
sprintf "%s: %s" (e.Range.ToString()) message)
|> Array.filter (fun d -> d.Severity = severity)
|> Array.map formatDiagnostic
|> List.ofArray

let inline dumpDiagnosticNumbers (results: FSharpCheckFileResults) =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
module FSharp.Compiler.Service.Tests.CompletionAccessibilityTests

open Xunit

[<Fact>]
let ``PrivateVisible`` () =
let info =
Checker.getCompletionInfo
"""
module CodeAccessibility
module Module1 =
let private fieldPrivate = 1
let private MethodPrivate x =
x+1
type private TypePrivate() =
member this.mem = 1
let a = (*Marker1*) {caret}"""

assertHasItemWithNames [ "fieldPrivate"; "MethodPrivate"; "TypePrivate" ] info

[<Fact>]
let ``InternalVisible`` () =
let info =
Checker.getCompletionInfo
"""
module CodeAccessibility
module Module1 =
let internal fieldInternal = 1
let internal MethodInternal x =
x+1
type internal TypeInternal() =
member this.mem = 1
let a = (*Marker1*) {caret}"""

assertHasItemWithNames [ "fieldInternal"; "MethodInternal"; "TypeInternal" ] info

let private widgetInheritanceSource =
"""
open System
//define the base class
type Widget() =
let mutable state = 0
member internal x.MethodInternal() = state
member public x.MethodPublic(n) = state <- state + n
member private x.MethodPrivate() = (state <> 0)
[<DefaultValue>]
val mutable internal fieldInternal:int
[<DefaultValue>]
val mutable public fieldPublic:int
[<DefaultValue>]
val mutable private fieldPrivate:int
//define the divided class which inherent "Widget"
type Divided() =
inherit Widget()
member x.myPrint() =
base.{caret}
Console.ReadKey(true)"""

[<Fact>]
let ``InheritedClass.BaseClassPrivateMethod.Negative`` () =
let info = Checker.getCompletionInfo widgetInheritanceSource
assertHasNoItemsWithNames [ "MethodPrivate"; "fieldPrivate" ] info

[<Fact>]
let ``InheritedClass.BaseClassPublicMethodAndProperty`` () =
let info = Checker.getCompletionInfo widgetInheritanceSource
assertHasItemWithNames [ "MethodPublic"; "fieldPublic" ] info

[<Fact>]
let ``Visibility.InternalNestedClass.Negative`` () =
let info = Checker.getCompletionInfo "System.Console.{caret}"

assertHasNoItemsWithNames [ "ControlCDelegateData" ] info

[<Fact>]
let ``Visibility.PrivateIdentifierInDiffModule.Negative`` () =
let info =
Checker.getCompletionInfo
"""
module Module1 =
let private fieldPrivate = 1
let private MethodPrivate x =
x+1
type private TypePrivate()=
member this.mem = 1
module Module2 =
Module1.{caret}"""

Assert.Equal(0, info.Items.Length)

[<Fact>]
let ``Visibility.PrivateIdentifierInDiffClass.Negative`` () =
let info =
Checker.getCompletionInfo
"""
open System
module Module1 =
type Type1()=
[<DefaultValue>]
val mutable private fieldPrivate:int
member private x.MethodPrivate() = 1
type Type2()=
let M1=
let type1 = new Type1()
type1.{caret}"""

assertHasNoItemsWithNames [ "fieldPrivate"; "MethodPrivate" ] info

[<Theory>]
[<InlineData("""
open System
module Module1 =
type Type1()=
[<DefaultValue>]
val mutable private PrivateField:int
static member private PrivateMethod() = 1
member this.Field1 with get () = this.{caret}
member x.MethodTest() = Type1(*MarkerMethodInType*)
let type1 = new Type1() """,
"PrivateField")>]
[<InlineData("""
open System
module Module1 =
type Type1()=
[<DefaultValue>]
val mutable private PrivateField:int
static member private PrivateMethod() = 1
member this.Field1 with get () = this(*MarkerFieldInType*)
member x.MethodTest() = Type1.{caret}
let type1 = new Type1() """,
"PrivateMethod")>]
let ``Visibility.PrivateMemberInSameClass`` (markedSource: string) (expected: string) =
let info = Checker.getCompletionInfo markedSource

assertHasItemWithNames [ expected ] info

[<Fact>]
let ``Visibility.InternalMethods.DefInSameAssembly`` () =
let info =
Checker.getCompletionInfo
"""
module CodeAccessibility
open System
module Module1 =
type Type1()=
[<DefaultValue>]
val mutable internal fieldInternal:int
member internal x.MethodInternal (x:int) = x+2
let type1 = new Type1()
type1.{caret}"""

assertHasItemWithNames [ "fieldInternal"; "MethodInternal" ] info

[<Theory>]
[<InlineData("""type Base =
val mutable baseField : int
val mutable private baseFieldPrivate : int
new () = { baseField = 0; baseFieldPrivate=1 }
type Derived =
val mutable derivedField : int
val mutable private derivedFieldPrivate : int
inherit Base
new () = { derivedField = 0;derivedFieldPrivate = 0 }
let derived = Derived()
derived.{caret}derivedField""")>]
[<InlineData("""type Base =
val mutable baseField : int
val mutable private baseFieldPrivate : int
new () = { baseField = 0; baseFieldPrivate=1 }
type Derived =
val mutable baseField : int
val mutable derivedField : int
val mutable private derivedFieldPrivate : int
inherit Base
new () = { baseField = 0; derivedField = 0; derivedFieldPrivate = 0 }
let derived = Derived()
derived.{caret}derivedField""")>]
let ``ObjInstance.InheritedClass.MethodsWithDiffAccessibility`` (markedSource: string) =
let info = Checker.getCompletionInfo markedSource
assertHasItemWithNames [ "baseField"; "derivedField" ] info
assertHasNoItemsWithNames [ "baseFieldPrivate"; "derivedFieldPrivate" ] info

[<Theory>]
[<InlineData("""type Base =
val mutable baseField : int
val mutable private baseFieldPrivate : int
new () = { baseField = 0; baseFieldPrivate=1 }
type Derived =
val mutable derivedField : int
val mutable private derivedFieldPrivate : int
inherit Base
new () = { derivedField = 0;derivedFieldPrivate = 0 }
member this.Method() =
(*marker*)this.{caret}baseField""")>]
[<InlineData("""type Base =
val mutable baseField : int
val mutable private baseFieldPrivate : int
new () = { baseField = 0; baseFieldPrivate=1 }
type Derived =
val mutable baseField : int
val mutable derivedField : int
val mutable private derivedFieldPrivate : int
inherit Base
new () = { baseField = 0; derivedField = 0; derivedFieldPrivate = 0 }
member this.Method() =
(*marker*)this.{caret}baseField""")>]
let ``Visibility.InheritedClass.MethodsWithDiffAccessibility`` (markedSource: string) =
let info = Checker.getCompletionInfo markedSource
assertHasItemWithNames [ "baseField"; "derivedField"; "derivedFieldPrivate" ] info
assertHasNoItemsWithNames [ "baseFieldPrivate" ] info

[<Fact>]
let ``Visibility.InheritedClass.MethodsWithSameNameMethod`` () =
let info =
Checker.getCompletionInfo
"""type MyClass =
val foo : int
new (foo) = { foo = foo }
type MyClass2 =
inherit MyClass
val foo : int
new (foo) = {
inherit MyClass(foo)
foo = foo
}
let x = new MyClass2(0)
(*marker*)x.{caret}foo"""

assertHasItemWithNames [ "foo" ] info
Loading
Loading