Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
19 changes: 19 additions & 0 deletions DEVGUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,25 @@ You can find all test options as separate flags. For example `build -testAll`:

Running any of the above will build the latest changes and run tests against them.

### Windows editor spot checks with the SDK compiler

In a separate PowerShell session, use the repository-local SDK and keep desktop targets enabled.
`BUILDING_USING_DOTNET=true` excludes `net472` from the editor's test utilities.
Build the compiler solution first: the editor test fixture also requires the desktop `fsc` and `fsi` tools.

```powershell
$env:DOTNET_ROOT = (Resolve-Path .\.dotnet).Path
$env:BUILDING_USING_DOTNET = "false"
$compilerArgs = @("-p:DisableCompilerRedirection=true", "-p:DisableAutoSetFscCompilerPath=false")
.\.dotnet\dotnet.exe build .\FSharp.slnx -c Release @compilerArgs
.\.dotnet\dotnet.exe build .\vsintegration\tests\FSharp.Editor.Tests\FSharp.Editor.Tests.fsproj -c Release @compilerArgs -p:EmbedUntrackedSources=false
.\.dotnet\dotnet.exe test --project .\vsintegration\tests\FSharp.Editor.Tests\FSharp.Editor.Tests.fsproj -c Release --no-build @compilerArgs --filter-class "*InlineRenameTests*" "*FindReferencesTests*"
```

Pass the compiler arguments to both build and test, even with `--no-build`, so neither imports a
stale or incomplete `artifacts\Bootstrap` toolset. Disabling untracked-source embedding avoids
generated XAML source-path errors in the SDK-hosted WPF build; all project references still build.

## Using your custom compiler to build this repository

By removing all the subfolders called `Bootstrap` or `Proto` under `artifacts` and running the `build` script again, the proto compiler will include your changes.
Expand Down
1 change: 1 addition & 0 deletions docs/release-notes/.VisualStudio/18.vNext.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
* Go To Definition no longer blocks the UI thread with a bare `Task.Wait`: the synchronous `IFSharpGoToDefinitionService` call now waits through the cancellable threaded-wait dialog, and the editor's `TaskCompletionSource` bridges run their continuations on the thread pool instead of inline on whichever thread finished the check, so repeated F12 on a large solution no longer starves semantic classification and other main-thread work. ([PR #20482](https://github.com/dotnet/fsharp/pull/20482))
* Peek Definition on an F# symbol whose definition lives in metadata no longer deadlocks Visual Studio. Peek holds the main thread in `JoinableTaskFactory.Run` without pumping messages while it asks the language service for the definition, and generating the metadata document needs that same thread; Peek now stops at definitions that already have a document, and Go To Definition, which owns the wait it makes, still opens the generated one. ([PR #20503](https://github.com/dotnet/fsharp/pull/20503))
* Improve Find All References performance by throttling parallel typechecks. ([PR #20128](https://github.com/dotnet/fsharp/pull/20128), [PR #20695](https://github.com/dotnet/fsharp/pull/20695))
* Fix record-type Rename corrupting copy-and-update expressions, including from abstract signatures, while retaining Find All References results. ([Issue #20791](https://github.com/dotnet/fsharp/issues/20791), [PR #20797](https://github.com/dotnet/fsharp/pull/20797))
* Fixed Rename incorrectly renaming `get` and `set` keywords for properties with explicit accessors. ([Issue #18270](https://github.com/dotnet/fsharp/issues/18270), [PR #19252](https://github.com/dotnet/fsharp/pull/19252))
* Fixed Find All References crash when F# project contains non-F# files like `.cshtml`. ([Issue #16394](https://github.com/dotnet/fsharp/issues/16394), [PR #19252](https://github.com/dotnet/fsharp/pull/19252))
* Avoid using `cancellableTask` in `DocumentCache`; the editor cache now uses direct `CancellationToken`-aware `task` wrappers, avoiding the background `Task.Run` offload and a larger wrapper closure from the `cancellableTask` builder. ([Issue #20268](https://github.com/dotnet/fsharp/issues/20268))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -900,6 +900,73 @@ module EventHandlerSyntheticSymbols =
/// https://github.com/dotnet/fsharp/issues/15290
module RecordCopyAndUpdate =

[<Theory>]
[<InlineData(false, false)>]
[<InlineData(true, false)>]
[<InlineData(false, true)>]
[<InlineData(true, true)>]
let ``Issue 20791 - record references distinguish type tokens and semantic expressions`` (fast, transparent) : Task =
task {
let source = """module Main
type State = { Count: int }
type Outer = { Inner: State }
let state = { Count = 0 }
let update (state: State) = { state with Count = state.Count + 1 }
let outer = { Inner = state }
let nested = { outer with Inner.Count = 2 }
let explicitNested = { outer with Inner = { state with Count = 3 } }
"""
let project =
{ SyntheticProject.Create({ sourceFile "Main" [] with Source = source }) with AutoAddModules = false }
let checker =
FSharpChecker.Create(
useTransparentCompiler = transparent,
enableBackgroundItemKeyStoreAndSemanticClassification = true,
captureIdentifiersWhenParsing = true)
let! _ = SaveAndCheckProject project checker false
let! results = checkFile "Main" project checker
let check = getTypeCheckResult results
let symbol = check.GetSymbolUseAtLocation(2, 10, "type State = { Count: int }", ["State"]).Value.Symbol
let text = SourceText.ofString source
let describe (range: range) =
range.StartLine, range.StartColumn, range.EndLine, range.EndColumn, text.GetSubTextFromRange range
let ordinary = check.GetUsesOfSymbolInFile(symbol, relatedSymbolKinds = RelatedSymbolUseKind.None)
let related = check.GetUsesOfSymbolInFile(symbol, relatedSymbolKinds = RelatedSymbolUseKind.CopyAndUpdateRecord)
let expectedOrdinary =
[|2, 5, 2, 10, "State"; 3, 22, 3, 27, "State"; 5, 19, 5, 24, "State"|]
let expectedRelated =
[|yield! expectedOrdinary
5, 28, 5, 66, "{ state with Count = state.Count + 1 }"
8, 42, 8, 66, "{ state with Count = 3 }"|]
for use' in related do
Assert.True(symbol.IsEffectivelySameAs use'.Symbol)
let outer = check.GetSymbolUseAtLocation(3, 10, "type Outer = { Inner: State }", ["Outer"]).Value.Symbol
Assert.Equal<(int * int * int * int * string) array>(
[|3, 5, 3, 10, "Outer"
7, 13, 7, 43, "{ outer with Inner.Count = 2 }"
8, 21, 8, 68, "{ outer with Inner = { state with Count = 3 } }"|],
check.GetUsesOfSymbolInFile(outer, relatedSymbolKinds = RelatedSymbolUseKind.All)
|> Array.map (fun use' -> describe use'.Range)
|> Array.sort)
Assert.Equal<(int * int * int * int * string) array>(
expectedOrdinary,
ordinary |> Array.map (fun use' -> describe use'.Range) |> Array.sort)
Assert.Equal<(int * int * int * int * string) array>(
expectedRelated, related |> Array.map (fun use' -> describe use'.Range) |> Array.sort)
let options = project.GetProjectOptions checker
let! background =
if transparent then
async {
let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot project)
return! checker.FindBackgroundReferencesInFile(project.GetFilePath "Main", snapshot, symbol)
}
else
checker.FindBackgroundReferencesInFile(project.GetFilePath "Main", options, symbol, fastCheck = fast)
Assert.Equal<(int * int * int * int * string) array>(
related |> Array.map (fun use' -> describe use'.Range) |> Array.sort,
background |> Seq.map describe |> Seq.sort |> Seq.toArray)
}

[<Fact>]
let ``Find references of record type includes copy-and-update`` () =
let source = """
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ type internal InlineRenameInfo

let symbolDisplayName = symbolUse.Symbol.DisplayName

let recordTypeName =
match symbolUse.Symbol with
| :? FSharpEntity as entity when entity.IsFSharpRecord || entity.IsOpaque ->
ValueSome(FSharpKeywords.NormalizeIdentifierBackticks symbolDisplayName)
| _ -> ValueNone

override _.CanRename = true
override _.LocalizedErrorMessage = null
override _.TriggerSpan = triggerSpan
Expand Down Expand Up @@ -160,12 +166,51 @@ type internal InlineRenameInfo
let document = document.Project.Solution.GetDocument(documentId)
let! sourceText = document.GetTextAsync(cancellationToken)

let! recordOptions =
match recordTypeName with
| ValueSome name ->
cancellableTask {
let! defines, langVersion = document.GetFsharpParsingOptionsAsync(nameof (InlineRenameInfo))
return ValueSome(defines, langVersion, name)
}
| ValueNone -> CancellableTask.singleton ValueNone

let tryGetEditSpan (textSpan: TextSpan) =
match recordOptions with
| ValueSome(defines, langVersion, name) when not textSpan.IsEmpty ->
// Record construction/update references can describe expressions, not type-name tokens.
match
Tokenizer.getSymbolAtPosition (
document.Id,
sourceText,
textSpan.End - 1,
document.FilePath,
defines,
SymbolLookupKind.Greedy,
false,
false,
Some langVersion,
cancellationToken
)
with
| Some token when FSharpKeywords.NormalizeIdentifierBackticks token.Ident.idText = name ->
match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, token.Range) with
| ValueSome tokenSpan when tokenSpan.End = textSpan.End && tokenSpan.Start >= textSpan.Start ->
ValueSome tokenSpan
| _ -> ValueNone
| _ -> ValueNone
| ValueSome _ -> ValueNone
| ValueNone -> ValueSome textSpan

return
[|
for symbolUse in symbolUses do
match Tokenizer.TryFSharpRangeToTextSpanForEditor(sourceText, symbolUse, symbolDisplayName) with
match
Tokenizer.TryFSharpRangeToTextSpanForEditor(sourceText, symbolUse, symbolDisplayName)
|> ValueOption.bind tryGetEditSpan
with
| ValueSome textSpan -> yield FSharpInlineRenameLocation(document, textSpan)
| ValueNone -> ()
| _ -> ()
|]
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
<Compile Include="IndentationServiceTests.fs" />
<Compile Include="CompletionProviderTests.fs" />
<Compile Include="FindReferencesTests.fs" />
<Compile Include="InlineRenameTests.fs" />
<Compile Include="GoToDefinitionServiceTests.fs" />
<Compile Include="PathMapNavigationTests.fs" />
<Compile Include="HelpContextServiceTests.fs" />
Expand Down Expand Up @@ -95,6 +96,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
<PackageReference Include="Microsoft.CodeAnalysis.EditorFeatures" />

<PackageReference Include="Microsoft.VisualStudio.LanguageServices.ExternalAccess" />
<PackageReference Include="Microsoft.VisualStudio.Platform.VSEditor" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ type RoslynTestHelpers private () =
let document = project.Documents |> Seq.last
document

static member CreateSolution(syntheticProject: SyntheticProject) =
static member CreateSolution(syntheticProject: SyntheticProject, ?configureWorkspace: Solution -> unit) =

let checker = syntheticProject.SaveAndCheck()

Expand Down Expand Up @@ -371,14 +371,15 @@ type RoslynTestHelpers private () =
projInfo.WithMetadataReferences(RoslynTestHelpers.MetadataReferencesOf(options, []))

let solution = RoslynTestHelpers.CreateSolution [ projInfo ]
(defaultArg configureWorkspace ignore) solution

options |> RoslynTestHelpers.SetProjectOptions projId solution

solution, checker

/// One Roslyn project per synthetic project, wired with project references the way VS wires
/// project-to-project references, so the options manager builds in-memory F# references.
static member CreateMultiProjectSolution(syntheticProject: SyntheticProject) =
static member CreateMultiProjectSolution(syntheticProject: SyntheticProject, ?configureWorkspace: Solution -> unit) =
let checker = syntheticProject.SaveAndCheck()

let projects =
Expand Down Expand Up @@ -410,6 +411,7 @@ type RoslynTestHelpers private () =
]

let solution = RoslynTestHelpers.CreateSolution projectInfos
(defaultArg configureWorkspace ignore) solution

for project, id in projects do
project.GetProjectOptions checker
Expand Down
Loading
Loading