From d7b8c2451d2f13b92054c8587d5509b060597515 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Tue, 30 Jul 2024 14:30:01 +1000 Subject: [PATCH 001/305] Allow more of Razor to use Roslyn Lsp types --- ...ft.CodeAnalysis.LanguageServer.Protocol.csproj | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj b/src/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj index 73945f4ec700e..1b359cf6c239c 100644 --- a/src/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj +++ b/src/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj @@ -37,10 +37,22 @@ + + + + - + + + + + + + + + @@ -89,6 +101,7 @@ + From 0f0ae058d600a44f754c5ce3192bd387ba3c77d2 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Tue, 30 Jul 2024 14:30:20 +1000 Subject: [PATCH 002/305] Add missing Lsp types/properties/defaults --- .../Diagnostics/VSInternalDiagnosticKind.cs | 6 +++--- .../Protocol/Internal/VSInternalMapCodeParams.cs | 13 +++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticKind.cs b/src/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticKind.cs index 3ac8099a356ec..7325280f38ba3 100644 --- a/src/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticKind.cs +++ b/src/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticKind.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -20,8 +20,8 @@ internal readonly record struct VSInternalDiagnosticKind(string Value) : IString public static readonly VSInternalDiagnosticKind Task = new("task"); /// - /// Edit and Continue diagnostic kind. + /// Syntax diagnostic kind. /// - public static readonly VSInternalDiagnosticKind EditAndContiue = new("enc"); + public static readonly VSInternalDiagnosticKind Syntax = new("syntax"); } } diff --git a/src/LanguageServer/Protocol/Protocol/Internal/VSInternalMapCodeParams.cs b/src/LanguageServer/Protocol/Protocol/Internal/VSInternalMapCodeParams.cs index e1460443c6612..4cf49e5466f12 100644 --- a/src/LanguageServer/Protocol/Protocol/Internal/VSInternalMapCodeParams.cs +++ b/src/LanguageServer/Protocol/Protocol/Internal/VSInternalMapCodeParams.cs @@ -4,6 +4,7 @@ namespace Roslyn.LanguageServer.Protocol { + using System; using System.Text.Json.Serialization; /// @@ -11,6 +12,18 @@ namespace Roslyn.LanguageServer.Protocol /// internal class VSInternalMapCodeParams { + /// + /// Internal correlation GUID, used to correlate map code messages from Copilot + /// with LSP Client actions. Used for telemetry. + /// + [JsonPropertyName("_vs_map_code_correlation_id")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Guid? MapCodeCorrelationId + { + get; + set; + } + /// /// Set of code blocks, associated with documents and regions, to map. /// From 485288668346cc8a0887936ed10011bbb9036f20 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Tue, 30 Jul 2024 14:31:19 +1000 Subject: [PATCH 003/305] Fix some typos --- .../Handler/Diagnostics/PullDiagnosticCategories.cs | 2 +- .../Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj | 2 +- .../Internal/Diagnostics/VSInternalDiagnosticKind.cs | 7 ++++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/LanguageServer/Protocol/Handler/Diagnostics/PullDiagnosticCategories.cs b/src/LanguageServer/Protocol/Handler/Diagnostics/PullDiagnosticCategories.cs index ca79789913f81..39ec24da85143 100644 --- a/src/LanguageServer/Protocol/Handler/Diagnostics/PullDiagnosticCategories.cs +++ b/src/LanguageServer/Protocol/Handler/Diagnostics/PullDiagnosticCategories.cs @@ -16,7 +16,7 @@ internal static class PullDiagnosticCategories /// /// Edit and Continue diagnostics. Can be for Document or Workspace pull requests. /// - public static readonly string EditAndContinue = VSInternalDiagnosticKind.EditAndContiue.Value; + public static readonly string EditAndContinue = VSInternalDiagnosticKind.EditAndContinue.Value; // Workspace categories diff --git a/src/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj b/src/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj index 1b359cf6c239c..b08de10b4c3d1 100644 --- a/src/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj +++ b/src/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj @@ -45,7 +45,7 @@ - + diff --git a/src/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticKind.cs b/src/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticKind.cs index 7325280f38ba3..1956ef22d949c 100644 --- a/src/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticKind.cs +++ b/src/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticKind.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -19,6 +19,11 @@ internal readonly record struct VSInternalDiagnosticKind(string Value) : IString /// public static readonly VSInternalDiagnosticKind Task = new("task"); + /// + /// Edit and Continue diagnostic kind. + /// + public static readonly VSInternalDiagnosticKind EditAndContinue = new("enc"); + /// /// Syntax diagnostic kind. /// From 1930426ff7f3c956a3502ccea5880eea0ea072f7 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 13 Dec 2024 17:05:44 -0600 Subject: [PATCH 004/305] Avoid focus changes during argument provider tests --- .../CSharp/CSharpArgumentProvider.cs | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpArgumentProvider.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpArgumentProvider.cs index bbb2bc03834a2..7dd63fd55d512 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpArgumentProvider.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpArgumentProvider.cs @@ -50,14 +50,14 @@ public void Method() await TestServices.Input.SendAsync(VirtualKeyCode.RETURN, HangMitigatingCancellationToken); await TestServices.Input.SendAsync("f.ToSt", HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" f.ToString$$", assertCaretPosition: true, HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync([FeatureAttribute.Workspace, FeatureAttribute.SignatureHelp], HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" f.ToString($$)", assertCaretPosition: true, HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" f.ToString()$$", assertCaretPosition: true, HangMitigatingCancellationToken); } @@ -76,17 +76,17 @@ public void Method() await TestServices.Input.SendAsync("object.Equ", HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" object.Equals$$", assertCaretPosition: true, HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync([FeatureAttribute.Workspace, FeatureAttribute.SignatureHelp], HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" object.Equals(null$$, null)", assertCaretPosition: true, HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" object.Equals(null, null$$)", assertCaretPosition: true, HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" object.Equals(null, null)$$", assertCaretPosition: true, HangMitigatingCancellationToken); } @@ -105,14 +105,14 @@ public void Method() await TestServices.Input.SendAsync("new obje", HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" var value = new object$$", assertCaretPosition: true, HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync([FeatureAttribute.Workspace, FeatureAttribute.SignatureHelp], HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" var value = new object($$)", assertCaretPosition: true, HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" var value = new object()$$", assertCaretPosition: true, HangMitigatingCancellationToken); } @@ -133,14 +133,14 @@ public void Method() await TestServices.Input.SendAsync("f.ToSt", HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" f.ToString$$;", assertCaretPosition: true, HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync([FeatureAttribute.Workspace, FeatureAttribute.SignatureHelp], HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" f.ToString($$);", assertCaretPosition: true, HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" f.ToString()$$;", assertCaretPosition: true, HangMitigatingCancellationToken); } @@ -162,10 +162,10 @@ public void Method(IFormatProvider provider) await TestServices.Input.SendAsync(VirtualKeyCode.RETURN, HangMitigatingCancellationToken); await TestServices.Input.SendAsync("f.ToSt", HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" f.ToString$$", assertCaretPosition: true, HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync([FeatureAttribute.Workspace, FeatureAttribute.SignatureHelp], HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" f.ToString($$)", assertCaretPosition: true, HangMitigatingCancellationToken); @@ -181,7 +181,7 @@ public void Method(IFormatProvider provider) await TestServices.Input.SendAsync("\"format\"", HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" f.ToString(\"format\"$$, provider)", assertCaretPosition: true, HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" f.ToString(\"format\", provider$$)", assertCaretPosition: true, HangMitigatingCancellationToken); await TestServices.Input.SendAsync(VirtualKeyCode.UP, HangMitigatingCancellationToken); @@ -214,10 +214,10 @@ void Test(int x, int y) { } await TestServices.Input.SendAsync(VirtualKeyCode.RETURN, HangMitigatingCancellationToken); await TestServices.Input.SendAsync("Test", HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" Test$$", assertCaretPosition: true, HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync([FeatureAttribute.Workspace, FeatureAttribute.SignatureHelp], HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" Test($$)", assertCaretPosition: true, HangMitigatingCancellationToken); @@ -291,10 +291,10 @@ public void Method() await TestServices.Input.SendAsync(VirtualKeyCode.RETURN, HangMitigatingCancellationToken); await TestServices.Input.SendAsync("f.ToSt", HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" f.ToString$$", assertCaretPosition: true, HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync([FeatureAttribute.Workspace, FeatureAttribute.SignatureHelp], HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" f.ToString($$)", assertCaretPosition: true, HangMitigatingCancellationToken); @@ -322,14 +322,14 @@ public void Method() await TestServices.Input.SendAsync(VirtualKeyCode.RETURN, HangMitigatingCancellationToken); await TestServices.Input.SendAsync("object.Equ", HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" object.Equals$$", assertCaretPosition: true, HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync([FeatureAttribute.Workspace, FeatureAttribute.SignatureHelp], HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" object.Equals(null$$, null)", assertCaretPosition: true, HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" object.Equals(null, null$$)", assertCaretPosition: true, HangMitigatingCancellationToken); await TestServices.Input.SendAsync(';', HangMitigatingCancellationToken); @@ -360,10 +360,10 @@ public void Method2(int value) await TestServices.Input.SendAsync(VirtualKeyCode.RETURN, HangMitigatingCancellationToken); await TestServices.Input.SendAsync("this.M2", HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" this.Method2$$", assertCaretPosition: true, HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync([FeatureAttribute.Workspace, FeatureAttribute.SignatureHelp], HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" this.Method2(value$$)", assertCaretPosition: true, HangMitigatingCancellationToken); @@ -388,10 +388,10 @@ public void Method() await TestServices.Input.SendAsync(VirtualKeyCode.RETURN, HangMitigatingCancellationToken); await TestServices.Input.SendAsync("f.ToSt", HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" f.ToString$$", assertCaretPosition: true, HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync([FeatureAttribute.Workspace, FeatureAttribute.SignatureHelp], HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" f.ToString($$)", assertCaretPosition: true, HangMitigatingCancellationToken); @@ -427,14 +427,14 @@ public void Method() await TestServices.Input.SendAsync(VirtualKeyCode.RETURN, HangMitigatingCancellationToken); await TestServices.Input.SendAsync("object.Equ", HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" object.Equals$$", assertCaretPosition: true, HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync([FeatureAttribute.Workspace, FeatureAttribute.SignatureHelp], HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" object.Equals(null$$, null)", assertCaretPosition: true, HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" object.Equals(null, null$$)", assertCaretPosition: true, HangMitigatingCancellationToken); await TestServices.Input.SendAsync((VirtualKeyCode.RETURN, VirtualKeyCode.SHIFT), HangMitigatingCancellationToken); @@ -481,10 +481,10 @@ public void M(string s, int i, int i2) await TestServices.Input.SendAsync(VirtualKeyCode.RETURN, HangMitigatingCancellationToken); await TestServices.Input.SendAsync("M", HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" M$$", assertCaretPosition: true, HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync([FeatureAttribute.Workspace, FeatureAttribute.SignatureHelp], HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync(" M(null, 0)", cancellationToken: HangMitigatingCancellationToken); @@ -509,10 +509,10 @@ public class TestClass await TestServices.Input.SendAsync("#i", HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync("#if$$", assertCaretPosition: true, HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); + await TestServices.Input.SendWithoutActivateAsync(VirtualKeyCode.TAB, HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentLineTextAsync("#if true$$", assertCaretPosition: true, HangMitigatingCancellationToken); var expected = @" From a4dbcc30bfab42beca6dac11e29ae6e10833ef96 Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Fri, 17 Jan 2025 15:18:33 -0800 Subject: [PATCH 005/305] Delete the SERVICEHUB_ASSETS comment We now deploy ServiceHub assets in different VSIXes, so this comment doesn't matter anymore. --- src/VisualStudio/Setup/source.extension.vsixmanifest | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/VisualStudio/Setup/source.extension.vsixmanifest b/src/VisualStudio/Setup/source.extension.vsixmanifest index 438a34bfc9481..3dabae35b9b09 100644 --- a/src/VisualStudio/Setup/source.extension.vsixmanifest +++ b/src/VisualStudio/Setup/source.extension.vsixmanifest @@ -70,8 +70,6 @@ - - From 46c91c82dc0968f870ebc00064d978f9fff055af Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Fri, 17 Jan 2025 15:22:38 -0800 Subject: [PATCH 006/305] Delete the DebuggerEngineExtension asset The |ServicesVisualStudio;VsdConfigOutputGroup| is supposed to be replaced at build time with the output group files, but it seems that's not happening here. Since the value we're shipping is clearly invalid, this must not be used anymore. --- src/VisualStudio/Setup/source.extension.vsixmanifest | 1 - 1 file changed, 1 deletion(-) diff --git a/src/VisualStudio/Setup/source.extension.vsixmanifest b/src/VisualStudio/Setup/source.extension.vsixmanifest index 3dabae35b9b09..545f5f5674c87 100644 --- a/src/VisualStudio/Setup/source.extension.vsixmanifest +++ b/src/VisualStudio/Setup/source.extension.vsixmanifest @@ -47,7 +47,6 @@ - From 04970e7d6acef3a871b6a72d2868912b0787eac3 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Mon, 27 Jan 2025 10:29:22 -0800 Subject: [PATCH 007/305] Update Roslyn LSP server to .net9 --- eng/targets/TargetFrameworks.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/targets/TargetFrameworks.props b/eng/targets/TargetFrameworks.props index 58f90114f4d1e..8f037a4120685 100644 --- a/eng/targets/TargetFrameworks.props +++ b/eng/targets/TargetFrameworks.props @@ -15,7 +15,7 @@ net9.0 net8.0;net9.0 net8.0 - net8.0 + net9.0 net8.0 net6.0 net9.0 From ae6e9cd93b23dbc610c7e985763bef5fdc513860 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 31 Jan 2025 20:15:11 -0800 Subject: [PATCH 008/305] Keep initializer when converting properties --- ...tyToFullPropertyCodeRefactoringProvider.cs | 6 ++-- .../ConvertAutoPropertyToFullPropertyTests.cs | 30 +++++++++++++++++++ ...tyToFullPropertyCodeRefactoringProvider.cs | 8 ++--- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/Features/CSharp/Portable/ConvertAutoPropertyToFullProperty/CSharpConvertAutoPropertyToFullPropertyCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertAutoPropertyToFullProperty/CSharpConvertAutoPropertyToFullPropertyCodeRefactoringProvider.cs index c238e4b2dd533..6472d3dac8a02 100644 --- a/src/Features/CSharp/Portable/ConvertAutoPropertyToFullProperty/CSharpConvertAutoPropertyToFullPropertyCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertAutoPropertyToFullProperty/CSharpConvertAutoPropertyToFullPropertyCodeRefactoringProvider.cs @@ -168,8 +168,10 @@ protected override async Task ExpandToFieldPropertyAsync( // Update the getter/setter to reference the 'field' expression instead. var (newGetAccessor, newSetAccessor) = GetNewAccessors(info, property, FieldExpression(), cancellationToken); - var finalProperty = CreateFinalProperty(document, property, info, newGetAccessor, newSetAccessor); - var finalRoot = root.ReplaceNode(property, finalProperty); + // The normal helper will strip off the semicolon (as we're normally moving the initializer to a field). + // Don't do that here as we will keep the current initializer on the property if it is there. + var finalProperty = (PropertyDeclarationSyntax)CreateFinalProperty(document, property, info, newGetAccessor, newSetAccessor); + var finalRoot = root.ReplaceNode(property, finalProperty.WithSemicolonToken(property.SemicolonToken)); return document.WithSyntaxRoot(finalRoot); } diff --git a/src/Features/CSharpTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests.cs b/src/Features/CSharpTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests.cs index 111cea0267bfa..1884ab2379f76 100644 --- a/src/Features/CSharpTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests.cs +++ b/src/Features/CSharpTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests.cs @@ -1444,4 +1444,34 @@ public int Goo """; await TestInRegularAndScriptAsync(text, expected, options: DoNotPreferExpressionBodiedAccessors, index: 1, parseOptions: CSharp14); } + + [Theory] + [InlineData("set"), InlineData("init")] + [WorkItem("https://github.com/dotnet/roslyn/issues/76992")] + public async Task ProduceFieldBackedProperty2(string setter) + { + var text = $$""" + class TestClass + { + public int G[||]oo { get; {{setter}}; } = 0; + } + """; + var expected = $$""" + class TestClass + { + public int Goo + { + get + { + return field; + } + {{setter}} + { + field = value; + } + } = 0; + } + """; + await TestInRegularAndScriptAsync(text, expected, options: DoNotPreferExpressionBodiedAccessors, index: 1, parseOptions: CSharp14); + } } diff --git a/src/Features/Core/Portable/ConvertAutoPropertyToFullProperty/AbstractConvertAutoPropertyToFullPropertyCodeRefactoringProvider.cs b/src/Features/Core/Portable/ConvertAutoPropertyToFullProperty/AbstractConvertAutoPropertyToFullPropertyCodeRefactoringProvider.cs index 9fdb11ae24361..1275577e9282f 100644 --- a/src/Features/Core/Portable/ConvertAutoPropertyToFullProperty/AbstractConvertAutoPropertyToFullPropertyCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/ConvertAutoPropertyToFullProperty/AbstractConvertAutoPropertyToFullPropertyCodeRefactoringProvider.cs @@ -99,9 +99,9 @@ private async Task ExpandToFullPropertyAsync( var fieldName = await GetFieldNameAsync(document, propertySymbol, cancellationToken).ConfigureAwait(false); var (newGetAccessor, newSetAccessor) = GetNewAccessors(info, property, fieldName, cancellationToken); - editor.ReplaceNode( - property, - CreateFinalProperty(document, property, info, newGetAccessor, newSetAccessor)); + var finalProperty = CreateFinalProperty( + document, GetPropertyWithoutInitializer(property), info, newGetAccessor, newSetAccessor); + editor.ReplaceNode(property, finalProperty); // add backing field, plus initializer if it exists var newField = CodeGenerationSymbolFactory.CreateFieldSymbol( @@ -137,7 +137,7 @@ protected SyntaxNode CreateFinalProperty( var fullProperty = generator .WithAccessorDeclarations( - GetPropertyWithoutInitializer(property), + property, newSetAccessor == null ? [newGetAccessor] : [newGetAccessor, newSetAccessor]) From ea802900d3a7f091f23103d33b2e0b4b6c7ac341 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 31 Jan 2025 20:19:39 -0800 Subject: [PATCH 009/305] Simplify syntactic manipulatinos --- ...pertyToFullPropertyCodeRefactoringProvider.cs | 12 ++++-------- .../CodeGeneration/CSharpSyntaxGenerator.cs | 16 +++++++++------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/Features/CSharp/Portable/ConvertAutoPropertyToFullProperty/CSharpConvertAutoPropertyToFullPropertyCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertAutoPropertyToFullProperty/CSharpConvertAutoPropertyToFullPropertyCodeRefactoringProvider.cs index 6472d3dac8a02..5cb11e73a8cf6 100644 --- a/src/Features/CSharp/Portable/ConvertAutoPropertyToFullProperty/CSharpConvertAutoPropertyToFullPropertyCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertAutoPropertyToFullProperty/CSharpConvertAutoPropertyToFullPropertyCodeRefactoringProvider.cs @@ -129,9 +129,7 @@ protected override SyntaxNode ConvertPropertyToExpressionBodyIfDesired( var preference = info.Options.PreferExpressionBodiedProperties.Value; if (preference == ExpressionBodyPreference.Never) - { - return propertyDeclaration.WithSemicolonToken(default); - } + return propertyDeclaration; // if there is a get accessors only, we can move the expression body to the property if (propertyDeclaration.AccessorList?.Accessors.Count == 1 && @@ -146,7 +144,7 @@ protected override SyntaxNode ConvertPropertyToExpressionBodyIfDesired( } } - return propertyDeclaration.WithSemicolonToken(default); + return propertyDeclaration; } protected override SyntaxNode GetTypeBlock(SyntaxNode syntaxNode) @@ -168,10 +166,8 @@ protected override async Task ExpandToFieldPropertyAsync( // Update the getter/setter to reference the 'field' expression instead. var (newGetAccessor, newSetAccessor) = GetNewAccessors(info, property, FieldExpression(), cancellationToken); - // The normal helper will strip off the semicolon (as we're normally moving the initializer to a field). - // Don't do that here as we will keep the current initializer on the property if it is there. - var finalProperty = (PropertyDeclarationSyntax)CreateFinalProperty(document, property, info, newGetAccessor, newSetAccessor); - var finalRoot = root.ReplaceNode(property, finalProperty.WithSemicolonToken(property.SemicolonToken)); + var finalProperty = CreateFinalProperty(document, property, info, newGetAccessor, newSetAccessor); + var finalRoot = root.ReplaceNode(property, finalProperty); return document.WithSyntaxRoot(finalRoot); } diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs index cf2aa2ed0d0e9..2c005f7ad2bd6 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs @@ -440,13 +440,15 @@ private static SyntaxNode AccessorDeclaration( public override SyntaxNode WithAccessorDeclarations(SyntaxNode declaration, IEnumerable accessorDeclarations) => declaration switch { - PropertyDeclarationSyntax property => property.WithAccessorList(CreateAccessorList(property.AccessorList, accessorDeclarations)) - .WithExpressionBody(null) - .WithSemicolonToken(default), - - IndexerDeclarationSyntax indexer => indexer.WithAccessorList(CreateAccessorList(indexer.AccessorList, accessorDeclarations)) - .WithExpressionBody(null) - .WithSemicolonToken(default), + PropertyDeclarationSyntax property => + property.WithAccessorList(CreateAccessorList(property.AccessorList, accessorDeclarations)) + .WithExpressionBody(null) + .WithSemicolonToken(property.Initializer is null ? default : property.SemicolonToken), + + IndexerDeclarationSyntax indexer => + indexer.WithAccessorList(CreateAccessorList(indexer.AccessorList, accessorDeclarations)) + .WithExpressionBody(null) + .WithSemicolonToken(default), _ => declaration, }; From 0b1978d0487f0eef63e282de0088cdff4bd5e557 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Mon, 3 Feb 2025 11:14:38 -0600 Subject: [PATCH 010/305] Remove unnecessary helper TakeAsArray --- .../AbstractGenerateConstructorService.State.cs | 4 ++-- .../Compiler/Core/Extensions/ImmutableArrayExtensions.cs | 9 --------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/Analyzers/Core/CodeFixes/GenerateConstructor/AbstractGenerateConstructorService.State.cs b/src/Analyzers/Core/CodeFixes/GenerateConstructor/AbstractGenerateConstructorService.State.cs index 062d0ab2acdd5..1c57b5cd4ce5e 100644 --- a/src/Analyzers/Core/CodeFixes/GenerateConstructor/AbstractGenerateConstructorService.State.cs +++ b/src/Analyzers/Core/CodeFixes/GenerateConstructor/AbstractGenerateConstructorService.State.cs @@ -195,8 +195,8 @@ await GetParametersAsync( for (var i = allParameters.Length; i > 0; i--) { - var parameters = allParameters.TakeAsArray(i); - var expressions = allExpressions.TakeAsArray(i); + var parameters = allParameters[0..i]; + var expressions = allExpressions[0..i]; var result = FindConstructorToDelegateTo(parameters, expressions, TypeToGenerateIn.InstanceConstructors, cancellationToken) ?? FindConstructorToDelegateTo(parameters, expressions, TypeToGenerateIn.BaseType.InstanceConstructors, cancellationToken); if (result != null) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ImmutableArrayExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ImmutableArrayExtensions.cs index d48e7dc3b86af..a3353548a19f5 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ImmutableArrayExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ImmutableArrayExtensions.cs @@ -31,15 +31,6 @@ public static ImmutableArray ToImmutableArrayOrEmpty(this T[]? items) return ImmutableArray.Create(items); } - public static ImmutableArray TakeAsArray(this ImmutableArray array, int count) - { - var result = new FixedSizeArrayBuilder(count); - for (var i = 0; i < count; i++) - result.Add(array[i]); - - return result.MoveToImmutable(); - } - public static ImmutableArray ToImmutableAndClear(this ImmutableArray.Builder builder) { if (builder.Count == 0) From efeaa2ae6e3ce91a2463091153f73b27386f299b Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Tue, 5 Nov 2024 09:01:37 -0600 Subject: [PATCH 011/305] Remove duplicate enum type --- .../EditAndContinueWorkspaceServiceTests.cs | 47 ++++++++----------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs b/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs index f61982ada6dcc..22e23e97cd922 100644 --- a/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs +++ b/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs @@ -1594,15 +1594,8 @@ public async Task HasChanges() EndDebuggingSession(debuggingSession); } - public enum DocumentKind - { - Source, - Additional, - AnalyzerConfig, - } - [Theory, CombinatorialData] - public async Task HasChanges_Documents(DocumentKind documentKind) + public async Task HasChanges_Documents(TextDocumentKind documentKind) { using var _ = CreateWorkspace(out var solution, out var service); var log = new TraceLog("Test"); @@ -1617,15 +1610,15 @@ public async Task HasChanges_Documents(DocumentKind documentKind) { switch (documentKind) { - case DocumentKind.Source: + case TextDocumentKind.Document: context.AddSource("Generated.cs", context.Compilation.SyntaxTrees.SingleOrDefault(t => t.FilePath.EndsWith("X.cs"))?.ToString() ?? "none"); break; - case DocumentKind.Additional: + case TextDocumentKind.AdditionalDocument: context.AddSource("Generated.cs", context.AdditionalFiles.FirstOrDefault()?.GetText().ToString() ?? "none"); break; - case DocumentKind.AnalyzerConfig: + case TextDocumentKind.AnalyzerConfigDocument: var syntaxTree = context.Compilation.SyntaxTrees.Single(t => t.FilePath.EndsWith("A.cs")); var content = context.AnalyzerConfigOptions.GetOptions(syntaxTree).TryGetValue("x", out var optionValue) ? optionValue.ToString() : "none"; @@ -1663,9 +1656,9 @@ public async Task HasChanges_Documents(DocumentKind documentKind) var documentId = DocumentId.CreateNewId(projectId); solution = documentKind switch { - DocumentKind.Source => solution.AddDocument(documentId, "X", CreateText("xxx"), filePath: pathX), - DocumentKind.Additional => solution.AddAdditionalDocument(documentId, "X", CreateText("xxx"), filePath: pathX), - DocumentKind.AnalyzerConfig => solution.AddAnalyzerConfigDocument(documentId, "X", GetAnalyzerConfigText([("x", "1")]), filePath: pathX), + TextDocumentKind.Document => solution.AddDocument(documentId, "X", CreateText("xxx"), filePath: pathX), + TextDocumentKind.AdditionalDocument => solution.AddAdditionalDocument(documentId, "X", CreateText("xxx"), filePath: pathX), + TextDocumentKind.AnalyzerConfigDocument => solution.AddAnalyzerConfigDocument(documentId, "X", GetAnalyzerConfigText([("x", "1")]), filePath: pathX), _ => throw ExceptionUtilities.Unreachable(), }; Assert.True(await EditSession.HasChangesAsync(oldSolution, solution, CancellationToken.None)); @@ -1683,7 +1676,7 @@ public async Task HasChanges_Documents(DocumentKind documentKind) var diagnostics = new ArrayBuilder(); await EditSession.PopulateChangedAndAddedDocumentsAsync(log, oldSolution.GetProject(projectId), solution.GetProject(projectId), changedOrAddedDocuments, diagnostics, CancellationToken.None); Assert.Empty(diagnostics); - AssertEx.Equal(documentKind == DocumentKind.Source ? [documentId, generatedDocumentId] : [generatedDocumentId], changedOrAddedDocuments.Select(d => d.Id)); + AssertEx.Equal(documentKind == TextDocumentKind.Document ? [documentId, generatedDocumentId] : [generatedDocumentId], changedOrAddedDocuments.Select(d => d.Id)); Assert.Equal(1, generatorExecutionCount); @@ -1696,9 +1689,9 @@ public async Task HasChanges_Documents(DocumentKind documentKind) solution = documentKind switch { - DocumentKind.Source => solution.WithDocumentText(documentId, CreateText("xxx")), - DocumentKind.Additional => solution.WithAdditionalDocumentText(documentId, CreateText("xxx")), - DocumentKind.AnalyzerConfig => solution.WithAnalyzerConfigDocumentText(documentId, GetAnalyzerConfigText([("x", "1")])), + TextDocumentKind.Document => solution.WithDocumentText(documentId, CreateText("xxx")), + TextDocumentKind.AdditionalDocument => solution.WithAdditionalDocumentText(documentId, CreateText("xxx")), + TextDocumentKind.AnalyzerConfigDocument => solution.WithAnalyzerConfigDocumentText(documentId, GetAnalyzerConfigText([("x", "1")])), _ => throw ExceptionUtilities.Unreachable(), }; Assert.False(await EditSession.HasChangesAsync(oldSolution, solution, CancellationToken.None)); @@ -1707,7 +1700,7 @@ public async Task HasChanges_Documents(DocumentKind documentKind) Assert.Equal(0, generatorExecutionCount); // source generator infrastructure compares content and reuses state if it matches (SourceGeneratedDocumentState.WithUpdatedGeneratedContent): - AssertEx.Equal(documentKind == DocumentKind.Source ? new[] { documentId } : [], + AssertEx.Equal(documentKind == TextDocumentKind.Document ? new[] { documentId } : [], await EditSession.GetChangedDocumentsAsync(log, oldSolution.GetProject(projectId), solution.GetProject(projectId), CancellationToken.None).ToImmutableArrayAsync(CancellationToken.None)); await EditSession.PopulateChangedAndAddedDocumentsAsync(log, oldSolution.GetProject(projectId), solution.GetProject(projectId), changedOrAddedDocuments, diagnostics, CancellationToken.None); @@ -1724,20 +1717,20 @@ public async Task HasChanges_Documents(DocumentKind documentKind) oldSolution = solution; solution = documentKind switch { - DocumentKind.Source => solution.WithDocumentText(documentId, CreateText("xxx-changed")), - DocumentKind.Additional => solution.WithAdditionalDocumentText(documentId, CreateText("xxx-changed")), - DocumentKind.AnalyzerConfig => solution.WithAnalyzerConfigDocumentText(documentId, GetAnalyzerConfigText([("x", "2")])), + TextDocumentKind.Document => solution.WithDocumentText(documentId, CreateText("xxx-changed")), + TextDocumentKind.AdditionalDocument => solution.WithAdditionalDocumentText(documentId, CreateText("xxx-changed")), + TextDocumentKind.AnalyzerConfigDocument => solution.WithAnalyzerConfigDocumentText(documentId, GetAnalyzerConfigText([("x", "2")])), _ => throw ExceptionUtilities.Unreachable(), }; Assert.True(await EditSession.HasChangesAsync(oldSolution, solution, CancellationToken.None)); Assert.True(await EditSession.HasChangesAsync(oldSolution, solution, pathX, CancellationToken.None)); - AssertEx.Equal(documentKind == DocumentKind.Source ? [documentId, generatedDocumentId] : [generatedDocumentId], + AssertEx.Equal(documentKind == TextDocumentKind.Document ? [documentId, generatedDocumentId] : [generatedDocumentId], await EditSession.GetChangedDocumentsAsync(log, oldSolution.GetProject(projectId), solution.GetProject(projectId), CancellationToken.None).ToImmutableArrayAsync(CancellationToken.None)); await EditSession.PopulateChangedAndAddedDocumentsAsync(log, oldSolution.GetProject(projectId), solution.GetProject(projectId), changedOrAddedDocuments, diagnostics, CancellationToken.None); Assert.Empty(diagnostics); - AssertEx.Equal(documentKind == DocumentKind.Source ? [documentId, generatedDocumentId] : [generatedDocumentId], changedOrAddedDocuments.Select(d => d.Id)); + AssertEx.Equal(documentKind == TextDocumentKind.Document ? [documentId, generatedDocumentId] : [generatedDocumentId], changedOrAddedDocuments.Select(d => d.Id)); Assert.Equal(1, generatorExecutionCount); @@ -1749,9 +1742,9 @@ public async Task HasChanges_Documents(DocumentKind documentKind) oldSolution = solution; solution = documentKind switch { - DocumentKind.Source => solution.RemoveDocument(documentId), - DocumentKind.Additional => solution.RemoveAdditionalDocument(documentId), - DocumentKind.AnalyzerConfig => solution.RemoveAnalyzerConfigDocument(documentId), + TextDocumentKind.Document => solution.RemoveDocument(documentId), + TextDocumentKind.AdditionalDocument => solution.RemoveAdditionalDocument(documentId), + TextDocumentKind.AnalyzerConfigDocument => solution.RemoveAnalyzerConfigDocument(documentId), _ => throw ExceptionUtilities.Unreachable(), }; Assert.True(await EditSession.HasChangesAsync(oldSolution, solution, CancellationToken.None)); From 56744ab84f00defc16f6b561d22ede02e02e0780 Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Tue, 28 Jan 2025 09:22:52 -0800 Subject: [PATCH 012/305] Remove Services.props from Roslyn.VisualStudio.Setup.csproj --- src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj b/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj index f02ebb53bedc9..2b78af2b9d9d9 100644 --- a/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj +++ b/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj @@ -1,7 +1,6 @@ - Library Roslyn.VisualStudio.Setup From c46ba8ba9a26b9f031b2addcbd24eb543d0770d6 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Tue, 4 Feb 2025 16:47:40 +0100 Subject: [PATCH 013/305] Emit entrypoint to ref assemblies (#76783) * Emit entrypoint to ref assemblies * Improve code * Fix async Main * Document the breaking change * Improve * Check there are no entry point errors * Test debug entrypoint --- .../Compiler Breaking Changes - DotNet 10.md | 23 ++ .../Portable/Compilation/CSharpCompilation.cs | 23 ++ .../Portable/Compiler/MethodCompiler.cs | 3 +- .../Test/Emit/Emit/CompilationEmitTests.cs | 4 +- .../Test/Emit/Emit/EmitMetadataTests.cs | 219 ++++++++++++++++++ .../Test/Emit3/Attributes/AttributeTests.cs | 2 + .../Test/Semantic/Semantics/LambdaTests.cs | 6 +- .../Core/Portable/PEWriter/Members.cs | 5 + .../Core/Portable/PEWriter/MetadataWriter.cs | 2 +- .../Compilation/VisualBasicCompilation.vb | 10 + .../Test/Emit/Attributes/AttributeTests.vb | 2 + .../Test/Emit/Emit/EmitMetadata.vb | 189 ++++++++++++++- .../WorkspaceTests_EditorFeatures.cs | 18 +- .../SymbolFinder/DependentTypeFinderTests.cs | 7 +- .../CoreTest/SolutionTests/SolutionTests.cs | 49 +++- 15 files changed, 544 insertions(+), 18 deletions(-) diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md index 6075637771e03..da3ecdd5166a7 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md @@ -291,3 +291,26 @@ unsafe record struct R( public bool Equals(R other) => true; } ``` + +## Emitting metadata-only executables requires an entrypoint + +***Introduced in Visual Studio 2022 version 17.14*** + +Previously, the entrypoint was [unintentionally unset](https://github.com/dotnet/roslyn/issues/76707) +when emitting executables in metadata-only mode (also known as ref assemblies). +That is now corrected but it also means that a missing entrypoint is a compilation error: + +```cs +// previously successful, now fails: +CSharpCompilation.Create("test").Emit(new MemoryStream(), + options: EmitOptions.Default.WithEmitMetadataOnly(true)) + +CSharpCompilation.Create("test", + // workaround - mark as DLL instead of EXE (the default): + options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)) + .Emit(new MemoryStream(), + options: EmitOptions.Default.WithEmitMetadataOnly(true)) +``` + +Similarly this can be observed when using the command-line argument `/refonly` +or the `ProduceOnlyReferenceAssembly` MSBuild property. diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index d5fee9089cef0..e23c3b07ded64 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -3503,6 +3503,29 @@ internal override bool CompileMethods( } SynthesizedMetadataCompiler.ProcessSynthesizedMembers(this, moduleBeingBuilt, cancellationToken); + + if (moduleBeingBuilt.OutputKind.IsApplication()) + { + var entryPointDiagnostics = BindingDiagnosticBag.GetInstance(withDiagnostics: true, withDependencies: false); + var entryPoint = MethodCompiler.GetEntryPoint( + this, + moduleBeingBuilt, + hasDeclarationErrors: false, + emitMethodBodies: false, + entryPointDiagnostics, + cancellationToken); + diagnostics.AddRange(entryPointDiagnostics.DiagnosticBag!); + bool shouldSetEntryPoint = entryPoint != null && !entryPointDiagnostics.HasAnyErrors(); + entryPointDiagnostics.Free(); + if (shouldSetEntryPoint) + { + moduleBeingBuilt.SetPEEntryPoint(entryPoint, diagnostics); + } + else + { + return false; + } + } } else { diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs index 632a09ab48501..aa022352a842e 100644 --- a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs +++ b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs @@ -218,7 +218,7 @@ public static void CompileMethodBodies( // Returns the MethodSymbol for the assembly entrypoint. If the user has a Task returning main, // this function returns the synthesized Main MethodSymbol. - private static MethodSymbol GetEntryPoint(CSharpCompilation compilation, PEModuleBuilder moduleBeingBuilt, bool hasDeclarationErrors, bool emitMethodBodies, BindingDiagnosticBag diagnostics, CancellationToken cancellationToken) + internal static MethodSymbol GetEntryPoint(CSharpCompilation compilation, PEModuleBuilder moduleBeingBuilt, bool hasDeclarationErrors, bool emitMethodBodies, BindingDiagnosticBag diagnostics, CancellationToken cancellationToken) { Debug.Assert(diagnostics.DiagnosticBag != null); @@ -252,6 +252,7 @@ private static MethodSymbol GetEntryPoint(CSharpCompilation compilation, PEModul if (((object)synthesizedEntryPoint != null) && (moduleBeingBuilt != null) && !hasDeclarationErrors && + !moduleBeingBuilt.EmitOptions.EmitMetadataOnly && !diagnostics.HasAnyErrors()) { BoundStatement body = synthesizedEntryPoint.CreateBody(diagnostics); diff --git a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs index 5058dc3912928..c4dca4e0d2899 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs @@ -574,8 +574,8 @@ internal static void Main() VerifyMethods(output, "C", new[] { "void C.Main()", "C..ctor()" }); VerifyMvid(output, hasMvidSection: false); - verifyEntryPoint(metadataOutput, expectZero: true); - VerifyMethods(metadataOutput, "C", new[] { "C..ctor()" }); + verifyEntryPoint(metadataOutput, expectZero: false); + VerifyMethods(metadataOutput, "C", new[] { "void C.Main()", "C..ctor()" }); VerifyMvid(metadataOutput, hasMvidSection: true); } diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EmitMetadataTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EmitMetadataTests.cs index 234d9f222229f..126f4d6ed3bbd 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EmitMetadataTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EmitMetadataTests.cs @@ -3507,5 +3507,224 @@ public class InvalidOperationException(); // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1)); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76707")] + public void EmitMetadataOnly_Exe() + { + CompileAndVerify(""" + System.Console.WriteLine("a"); + """, + options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All), + emitOptions: EmitOptions.Default.WithEmitMetadataOnly(true), + symbolValidator: static (ModuleSymbol module) => + { + Assert.NotEqual(0, module.GetMetadata().Module.PEReaderOpt.PEHeaders.CorHeader.EntryPointTokenOrRelativeVirtualAddress); + var main = module.GlobalNamespace.GetMember("Program.
$"); + Assert.Equal(Accessibility.Private, main.DeclaredAccessibility); + }) + .VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76707")] + public void EmitMetadataOnly_Exe_AsyncMain() + { + CompileAndVerify(""" + using System.Threading.Tasks; + static class Program + { + static async Task Main() + { + await Task.Yield(); + System.Console.WriteLine("a"); + } + } + """, + options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All), + emitOptions: EmitOptions.Default.WithEmitMetadataOnly(true), + symbolValidator: static (ModuleSymbol module) => + { + Assert.NotEqual(0, module.GetMetadata().Module.PEReaderOpt.PEHeaders.CorHeader.EntryPointTokenOrRelativeVirtualAddress); + var main = module.GlobalNamespace.GetMember("Program.
"); + Assert.Equal(Accessibility.Private, main.DeclaredAccessibility); + }) + .VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76707")] + public void EmitMetadataOnly_Exe_NoMain() + { + var emitResult = CreateCompilation(""" + class Program; + """, + options: TestOptions.ReleaseExe) + .Emit(new MemoryStream(), options: EmitOptions.Default.WithEmitMetadataOnly(true)); + Assert.False(emitResult.Success); + emitResult.Diagnostics.Verify( + // error CS5001: Program does not contain a static 'Main' method suitable for an entry point + Diagnostic(ErrorCode.ERR_NoEntryPoint).WithLocation(1, 1)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76707")] + public void EmitMetadataOnly_Exe_PrivateMain_ExcludePrivateMembers() + { + CompileAndVerify(""" + static class Program + { + private static void Main() { } + } + """, + options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All), + emitOptions: EmitOptions.Default + .WithEmitMetadataOnly(true) + .WithIncludePrivateMembers(false), + symbolValidator: static (ModuleSymbol module) => + { + Assert.NotEqual(0, module.GetMetadata().Module.PEReaderOpt.PEHeaders.CorHeader.EntryPointTokenOrRelativeVirtualAddress); + var main = module.GlobalNamespace.GetMember("Program.Main"); + Assert.Equal(Accessibility.Private, main.DeclaredAccessibility); + }) + .VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76707")] + public void EmitMetadataOnly_Exe_PrivateMain_ExcludePrivateMembers_AsyncMain() + { + CompileAndVerify(""" + using System.Threading.Tasks; + static class Program + { + private static async Task Main() + { + await Task.Yield(); + } + } + """, + options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All), + emitOptions: EmitOptions.Default + .WithEmitMetadataOnly(true) + .WithIncludePrivateMembers(false), + symbolValidator: static (ModuleSymbol module) => + { + Assert.NotEqual(0, module.GetMetadata().Module.PEReaderOpt.PEHeaders.CorHeader.EntryPointTokenOrRelativeVirtualAddress); + var main = module.GlobalNamespace.GetMember("Program.
"); + Assert.Equal(Accessibility.Private, main.DeclaredAccessibility); + }) + .VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76707")] + public void ExcludePrivateMembers_PrivateMain() + { + using var peStream = new MemoryStream(); + using var metadataStream = new MemoryStream(); + var comp = CreateCompilation(""" + static class Program + { + private static void Main() { } + } + """, + options: TestOptions.ReleaseExe); + var emitResult = comp.Emit( + peStream: peStream, + metadataPEStream: metadataStream, + options: EmitOptions.Default.WithIncludePrivateMembers(false)); + Assert.True(emitResult.Success); + emitResult.Diagnostics.Verify(); + + verify(peStream); + verify(metadataStream); + + CompileAndVerify(comp).VerifyDiagnostics(); + + static void verify(Stream stream) + { + stream.Position = 0; + Assert.NotEqual(0, new PEHeaders(stream).CorHeader.EntryPointTokenOrRelativeVirtualAddress); + + stream.Position = 0; + var reference = AssemblyMetadata.CreateFromStream(stream).GetReference(); + var comp = CreateCompilation("", references: [reference], + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); + var main = comp.GetMember("Program.Main"); + Assert.Equal(Accessibility.Private, main.DeclaredAccessibility); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76707")] + public void ExcludePrivateMembers_PrivateMain_AsyncMain() + { + using var peStream = new MemoryStream(); + using var metadataStream = new MemoryStream(); + var comp = CreateCompilation(""" + using System.Threading.Tasks; + static class Program + { + private static async Task Main() + { + await Task.Yield(); + } + } + """, + options: TestOptions.ReleaseExe); + var emitResult = comp.Emit( + peStream: peStream, + metadataPEStream: metadataStream, + options: EmitOptions.Default.WithIncludePrivateMembers(false)); + Assert.True(emitResult.Success); + emitResult.Diagnostics.Verify(); + + verify(peStream); + verify(metadataStream); + + CompileAndVerify(comp).VerifyDiagnostics(); + + static void verify(Stream stream) + { + stream.Position = 0; + Assert.NotEqual(0, new PEHeaders(stream).CorHeader.EntryPointTokenOrRelativeVirtualAddress); + + stream.Position = 0; + var reference = AssemblyMetadata.CreateFromStream(stream).GetReference(); + var comp = CreateCompilation("", references: [reference], + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); + var main = comp.GetMember("Program.
"); + Assert.Equal(Accessibility.Private, main.DeclaredAccessibility); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76707")] + public void ExcludePrivateMembers_DebugEntryPoint() + { + using var peStream = new MemoryStream(); + using var metadataStream = new MemoryStream(); + + { + var comp = CreateCompilation(""" + static class Program + { + static void M1() { } + static void M2() { } + } + """).VerifyDiagnostics(); + var emitResult = comp.Emit( + peStream: peStream, + metadataPEStream: metadataStream, + debugEntryPoint: comp.GetMember("Program.M1").GetPublicSymbol(), + options: EmitOptions.Default.WithIncludePrivateMembers(false)); + Assert.True(emitResult.Success); + emitResult.Diagnostics.Verify(); + } + + { + // M1 should be emitted (it's the debug entry-point), M2 shouldn't (private members are excluded). + metadataStream.Position = 0; + var reference = AssemblyMetadata.CreateFromStream(metadataStream).GetReference(); + var comp = CreateCompilation("", references: [reference], + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); + var m1 = comp.GetMember("Program.M1"); + Assert.Equal(Accessibility.Private, m1.DeclaredAccessibility); + Assert.Null(comp.GetMember("Program.M2")); + } + } } } diff --git a/src/Compilers/CSharp/Test/Emit3/Attributes/AttributeTests.cs b/src/Compilers/CSharp/Test/Emit3/Attributes/AttributeTests.cs index 620867a4028d8..80b439bf2dfdd 100644 --- a/src/Compilers/CSharp/Test/Emit3/Attributes/AttributeTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Attributes/AttributeTests.cs @@ -4879,12 +4879,14 @@ class C { } public void AttributeArgumentAsEnumFromMetadata() { var metadataStream1 = CSharpCompilation.Create("bar.dll", + options: TestOptions.DebugDll, references: new[] { MscorlibRef }, syntaxTrees: new[] { Parse("public enum Bar { Baz }") }).EmitToStream(options: new EmitOptions(metadataOnly: true)); var ref1 = MetadataReference.CreateFromStream(metadataStream1); var metadataStream2 = CSharpCompilation.Create("goo.dll", references: new[] { MscorlibRef, ref1 }, + options: TestOptions.DebugDll, syntaxTrees: new[] { SyntaxFactory.ParseSyntaxTree( "public class Ca : System.Attribute { public Ca(object o) { } } " + diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index 9226f1e71e1e3..54d7002e47a40 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -507,7 +507,8 @@ End Module var vbProject = VisualBasic.VisualBasicCompilation.Create( "VBProject", references: new[] { MscorlibRef }, - syntaxTrees: new[] { VisualBasic.VisualBasicSyntaxTree.ParseText(vbSource) }); + syntaxTrees: new[] { VisualBasic.VisualBasicSyntaxTree.ParseText(vbSource) }, + options: new VisualBasic.VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); var csSource = @" class Program @@ -557,7 +558,8 @@ End Module var vbProject = VisualBasic.VisualBasicCompilation.Create( "VBProject", references: new[] { MscorlibRef }, - syntaxTrees: new[] { VisualBasic.VisualBasicSyntaxTree.ParseText(vbSource) }); + syntaxTrees: new[] { VisualBasic.VisualBasicSyntaxTree.ParseText(vbSource) }, + options: new VisualBasic.VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); var csSource = @" class Program diff --git a/src/Compilers/Core/Portable/PEWriter/Members.cs b/src/Compilers/Core/Portable/PEWriter/Members.cs index c1c2cd6e5ecc6..c15988d673d5b 100644 --- a/src/Compilers/Core/Portable/PEWriter/Members.cs +++ b/src/Compilers/Core/Portable/PEWriter/Members.cs @@ -1047,6 +1047,11 @@ public static bool ShouldInclude(this ITypeDefinitionMember member, EmitContext } } + if (method != null && (context.Module.PEEntryPoint == method || context.Module.DebugEntryPoint == method)) + { + return true; + } + return false; } } diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs index da741a04c3d43..73db3eb903cdd 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs @@ -1869,7 +1869,7 @@ public PortablePdbBuilder GetPortablePdbBuilder(ImmutableArray typeSystemRo internal void GetEntryPoints(out MethodDefinitionHandle entryPointHandle, out MethodDefinitionHandle debugEntryPointHandle) { - if (IsFullMetadata && !MetadataOnly) + if (IsFullMetadata) { // PE entry point is set for executable programs IMethodReference entryPoint = module.PEEntryPoint; diff --git a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb index cb0257ff9b975..549049a430208 100644 --- a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb +++ b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb @@ -2487,6 +2487,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End If SynthesizedMetadataCompiler.ProcessSynthesizedMembers(Me, moduleBeingBuilt, cancellationToken) + + If moduleBeingBuilt.OutputKind.IsApplication() Then + Dim entryPoint = GetEntryPointAndDiagnostics(cancellationToken) + diagnostics.AddRange(entryPoint.Diagnostics) + If entryPoint.MethodSymbol IsNot Nothing AndAlso Not entryPoint.Diagnostics.HasAnyErrors() Then + moduleBeingBuilt.SetPEEntryPoint(entryPoint.MethodSymbol, diagnostics) + Else + Return False + End If + End If Else ' start generating PDB checksums if we need to emit PDBs If (emittingPdb OrElse moduleBuilder.EmitOptions.InstrumentationKinds.Contains(InstrumentationKind.TestCoverage)) AndAlso diff --git a/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests.vb b/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests.vb index 489f5ef45ae5c..0b6f776f7b276 100644 --- a/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests.vb @@ -1875,6 +1875,7 @@ End Class Public Sub AttributeArgumentAsEnumFromMetadata() Dim metadata1 = VisualBasicCompilation.Create("bar.dll", + options:=TestOptions.DebugDll, references:={MscorlibRef}, syntaxTrees:={Parse("Public Enum Bar : Baz : End Enum")}).EmitToArray(New EmitOptions(metadataOnly:=True)) @@ -1882,6 +1883,7 @@ End Class Dim metadata2 = VisualBasicCompilation.Create( "goo.dll", + options:=TestOptions.DebugDll, references:={MscorlibRef, ref1}, syntaxTrees:={ VisualBasicSyntaxTree.ParseText( + Public Sub EmitMetadataOnly_Exe() + CompileAndVerify( + + +Module Program + Sub Main() + System.Console.WriteLine("a") + End Sub +End Module + + , + options:=TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All), + emitOptions:=EmitOptions.Default.WithEmitMetadataOnly(True), + symbolValidator:=Sub(m) + Assert.NotEqual(0, m.GetMetadata().Module.PEReaderOpt.PEHeaders.CorHeader.EntryPointTokenOrRelativeVirtualAddress) + Dim main = m.GlobalNamespace.GetMember(Of MethodSymbol)("Program.Main") + Assert.Equal(Accessibility.Public, main.DeclaredAccessibility) + End Sub + ).VerifyDiagnostics() + End Sub + + + Public Sub EmitMetadataOnly_Exe_AsyncMain() + Dim emitResult = CreateCompilation( + + +Imports System.Threading.Tasks +Module Program + Public Async Function Main() As Task + Await Task.Yield() + System.Console.WriteLine("a") + End Function +End Module + + , + options:=TestOptions.ReleaseExe, + assemblyName:="MyLib" + ).Emit(New MemoryStream(), options:=EmitOptions.Default.WithEmitMetadataOnly(True)) + Assert.False(emitResult.Success) + emitResult.Diagnostics.AssertTheseDiagnostics( +BC30737: No accessible 'Main' method with an appropriate signature was found in 'MyLib'. + ) + End Sub + + + Public Sub EmitMetadataOnly_Exe_AsyncMain_Void() + Dim emitResult = CreateCompilation( + + +Imports System.Threading.Tasks +Module Program + Public Async Sub Main() + Await Task.Yield() + System.Console.WriteLine("a") + End Sub +End Module + + , + options:=TestOptions.ReleaseExe, + assemblyName:="MyLib" + ).Emit(New MemoryStream(), options:=EmitOptions.Default.WithEmitMetadataOnly(True)) + Assert.False(emitResult.Success) + emitResult.Diagnostics.AssertTheseDiagnostics( +BC36934: The 'Main' method cannot be marked 'Async'. + Public Async Sub Main() + ~~~~ + ) + End Sub + + + Public Sub EmitMetadataOnly_Exe_NoMain() + Dim emitResult = CreateCompilation( + + +Module Program +End Module + + , + options:=TestOptions.ReleaseExe, + assemblyName:="MyLib" + ).Emit(New MemoryStream(), options:=EmitOptions.Default.WithEmitMetadataOnly(True)) + Assert.False(emitResult.Success) + emitResult.Diagnostics.AssertTheseDiagnostics( +BC30420: 'Sub Main' was not found in 'MyLib'. + ) + End Sub + + + Public Sub EmitMetadataOnly_Exe_FriendMain_ExcludePrivateMembers() + CompileAndVerify( + + +Module Program + Friend Sub Main() + End Sub +End Module + + , + options:=TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All), + emitOptions:=EmitOptions.Default.WithEmitMetadataOnly(True).WithIncludePrivateMembers(False), + symbolValidator:=Sub(m) + Assert.NotEqual(0, m.GetMetadata().Module.PEReaderOpt.PEHeaders.CorHeader.EntryPointTokenOrRelativeVirtualAddress) + Dim main = m.GlobalNamespace.GetMember(Of MethodSymbol)("Program.Main") + Assert.Equal(Accessibility.Internal, main.DeclaredAccessibility) + End Sub + ).VerifyDiagnostics() + End Sub + + + Public Sub ExcludePrivateMembers_FriendMain() + Using peStream As New MemoryStream() + Using metadataStream As New MemoryStream() + Dim comp = CreateCompilation( + + +Module Program + Friend Sub Main() + End Sub +End Module + + , + options:=TestOptions.ReleaseExe + ) + Dim emitResult = comp.Emit( + peStream:=peStream, + metadataPEStream:=metadataStream, + options:=EmitOptions.Default.WithIncludePrivateMembers(False)) + Assert.True(emitResult.Success) + emitResult.Diagnostics.Verify() + + Verify(peStream) + Verify(metadataStream) + + CompileAndVerify(comp).VerifyDiagnostics() + End Using + End Using + End Sub + + Private Shared Sub Verify(stream As Stream) + stream.Position = 0 + Assert.NotEqual(0, New PEHeaders(stream).CorHeader.EntryPointTokenOrRelativeVirtualAddress) + + stream.Position = 0 + Dim reference = AssemblyMetadata.CreateFromStream(stream).GetReference() + Dim comp = CreateCompilation("", references:={reference}, options:=TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)) + Dim main = comp.GetMember(Of MethodSymbol)("Program.Main") + Assert.Equal(Accessibility.Internal, main.DeclaredAccessibility) + End Sub + + + Public Sub ExcludePrivateMembers_DebugEntryPoint() + Using peStream As New MemoryStream() + Using metadataStream As New MemoryStream() + Dim comp = CreateCompilation( + + +Module Program + Private Sub M1() + End Sub + Private Sub M2() + End Sub +End Module + + ).VerifyDiagnostics() + Dim emitResult = comp.Emit( + peStream:=peStream, + metadataPEStream:=metadataStream, + debugEntryPoint:=comp.GetMember(Of MethodSymbol)("Program.M1"), + options:=EmitOptions.Default.WithIncludePrivateMembers(False)) + Assert.True(emitResult.Success) + emitResult.Diagnostics.Verify() + + ' M1 should be emitted (it's the debug entry-point), M2 shouldn't (private members are excluded). + metadataStream.Position = 0 + Dim reference = AssemblyMetadata.CreateFromStream(metadataStream).GetReference() + Dim comp2 = CreateCompilation("", references:={reference}, + options:=TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)) + Dim m1 = comp2.GetMember(Of MethodSymbol)("Program.M1") + Assert.Equal(Accessibility.Private, m1.DeclaredAccessibility) + Assert.Null(comp2.GetMember(Of MethodSymbol)("Program.M2")) + End Using + End Using + End Sub End Class End Namespace diff --git a/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests_EditorFeatures.cs b/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests_EditorFeatures.cs index 27f3d213052c5..e4a42458c6bfa 100644 --- a/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests_EditorFeatures.cs +++ b/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests_EditorFeatures.cs @@ -479,7 +479,11 @@ internal async Task TestGetCompilationOnCrossLanguageDependentProjectChanged( var solutionX = workspace.CurrentSolution; var document1 = new EditorTestHostDocument(@"public class C { }"); - var project1 = new EditorTestHostProject(workspace, document1, name: "project1"); + var project1 = new EditorTestHostProject(workspace, document1, name: "project1", + compilationOptions: solutionX.Services + .GetRequiredLanguageService(LanguageNames.CSharp) + .GetDefaultCompilationOptions() + .WithOutputKind(OutputKind.DynamicallyLinkedLibrary)); var document2 = new EditorTestHostDocument(""" Public Class D @@ -533,7 +537,11 @@ public async Task TestDependentSemanticVersionChangesWhenNotOriginallyAccessed() var solutionX = workspace.CurrentSolution; var document1 = new EditorTestHostDocument(@"public class C { }"); - var project1 = new EditorTestHostProject(workspace, document1, name: "project1"); + var project1 = new EditorTestHostProject(workspace, document1, name: "project1", + compilationOptions: solutionX.Services + .GetRequiredLanguageService(LanguageNames.CSharp) + .GetDefaultCompilationOptions() + .WithOutputKind(OutputKind.DynamicallyLinkedLibrary)); var document2 = new EditorTestHostDocument(""" Public Class D @@ -601,7 +609,11 @@ internal async Task TestGetCompilationOnCrossLanguageDependentProjectChangedInPr var solutionX = workspace.CurrentSolution; var document1 = new EditorTestHostDocument(@"public class C { }"); - var project1 = new EditorTestHostProject(workspace, document1, name: "project1"); + var project1 = new EditorTestHostProject(workspace, document1, name: "project1", + compilationOptions: solutionX.Services + .GetRequiredLanguageService(LanguageNames.CSharp) + .GetDefaultCompilationOptions() + .WithOutputKind(OutputKind.DynamicallyLinkedLibrary)); var document2 = new EditorTestHostDocument(""" Public Class D diff --git a/src/EditorFeatures/Test/SymbolFinder/DependentTypeFinderTests.cs b/src/EditorFeatures/Test/SymbolFinder/DependentTypeFinderTests.cs index 7bc0ab4d97aeb..87f8dc0ae04d1 100644 --- a/src/EditorFeatures/Test/SymbolFinder/DependentTypeFinderTests.cs +++ b/src/EditorFeatures/Test/SymbolFinder/DependentTypeFinderTests.cs @@ -12,6 +12,7 @@ using Basic.Reference.Assemblies; using Microsoft.CodeAnalysis.Editor.UnitTests; using Microsoft.CodeAnalysis.FindSymbols; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Remote.Testing; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; @@ -36,7 +37,11 @@ private static Solution AddProjectWithMetadataReferences(Solution solution, stri projectName, languageName, metadataReferences: [metadataReference], - projectReferences: projectReferences.Select(p => new ProjectReference(p))); + projectReferences: projectReferences.Select(p => new ProjectReference(p)), + compilationOptions: solution.Services + .GetRequiredLanguageService(languageName) + .GetDefaultCompilationOptions() + .WithOutputKind(OutputKind.DynamicallyLinkedLibrary)); return solution.AddProject(pi).AddDocument(did, $"{projectName}.{suffix}", SourceText.From(code)); } diff --git a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs index 73f7d29d9fe0c..fdb847ccc5e62 100644 --- a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs @@ -2820,7 +2820,10 @@ public async Task TestCrossLanguageProjectsAsync() using var workspace = CreateWorkspace(); var solution = workspace.CurrentSolution - .AddProject(pm1, "goo", "goo.dll", LanguageNames.CSharp) + .AddProject(ProjectInfo.Create(pm1, VersionStamp.Create(), "goo", "goo.dll", LanguageNames.CSharp, compilationOptions: workspace.Services + .GetLanguageService(LanguageNames.CSharp) + .GetDefaultCompilationOptions() + .WithOutputKind(OutputKind.DynamicallyLinkedLibrary))) .AddMetadataReference(pm1, s_mscorlib) .AddProject(pm2, "bar", "bar.dll", LanguageNames.VisualBasic) .AddMetadataReference(pm2, s_mscorlib) @@ -3968,6 +3971,13 @@ public class C : A { .AddProjectReference(pid3, new ProjectReference(pid1)) .AddProjectReference(pid3, new ProjectReference(pid2)); + var options = solution.Workspace.Services + .GetLanguageService(LanguageNames.VisualBasic) + .GetDefaultCompilationOptions() + .WithOutputKind(OutputKind.DynamicallyLinkedLibrary); + solution = solution.WithProjectCompilationOptions(pid1, options) + .WithProjectCompilationOptions(pid2, options); + var project3 = solution.GetProject(pid3); var comp3 = project3.GetCompilationAsync().Result; var classC = comp3.GetTypeByMetadataName("C"); @@ -4020,7 +4030,11 @@ public async Task TestProjectWithNoBrokenReferencesHasNoIncompleteReferences() "CSharpProject", "CSharpProject", LanguageNames.CSharp, - metadataReferences: [MscorlibRef])); + metadataReferences: [MscorlibRef], + compilationOptions: workspace.Services + .GetLanguageService(LanguageNames.CSharp) + .GetDefaultCompilationOptions() + .WithOutputKind(OutputKind.DynamicallyLinkedLibrary))); var project2 = workspace.AddProject( ProjectInfo.Create( ProjectId.CreateNewId(), @@ -4221,7 +4235,11 @@ public async Task TestFrozenPartialProjectAlwaysIsIncomplete() "CSharpProject", "CSharpProject", LanguageNames.CSharp, - metadataReferences: [MscorlibRef])); + metadataReferences: [MscorlibRef], + compilationOptions: workspace.Services + .GetLanguageService(LanguageNames.CSharp) + .GetDefaultCompilationOptions() + .WithOutputKind(OutputKind.DynamicallyLinkedLibrary))); var project2 = workspace.AddProject( ProjectInfo.Create( @@ -5326,7 +5344,12 @@ private static void GetMultipleProjects( "CSharpProject", "CSharpProject", LanguageNames.CSharp, - metadataReferences: [MscorlibRef]).WithHasAllInformation(hasAllInformation: false)); + metadataReferences: [MscorlibRef], + compilationOptions: workspace.Services + .GetLanguageService(LanguageNames.CSharp) + .GetDefaultCompilationOptions() + .WithOutputKind(OutputKind.DynamicallyLinkedLibrary)) + .WithHasAllInformation(hasAllInformation: false)); vbNormalProject = workspace.AddProject( ProjectInfo.Create( @@ -5335,7 +5358,11 @@ private static void GetMultipleProjects( "VisualBasicProject", "VisualBasicProject", LanguageNames.VisualBasic, - metadataReferences: [MscorlibRef])); + metadataReferences: [MscorlibRef], + compilationOptions: workspace.Services + .GetLanguageService(LanguageNames.VisualBasic) + .GetDefaultCompilationOptions() + .WithOutputKind(OutputKind.DynamicallyLinkedLibrary))); dependsOnBrokenProject = workspace.AddProject( ProjectInfo.Create( @@ -5345,7 +5372,11 @@ private static void GetMultipleProjects( "VisualBasicProject", LanguageNames.VisualBasic, metadataReferences: [MscorlibRef], - projectReferences: [new ProjectReference(csBrokenProject.Id), new ProjectReference(vbNormalProject.Id)])); + projectReferences: [new ProjectReference(csBrokenProject.Id), new ProjectReference(vbNormalProject.Id)], + compilationOptions: workspace.Services + .GetLanguageService(LanguageNames.VisualBasic) + .GetDefaultCompilationOptions() + .WithOutputKind(OutputKind.DynamicallyLinkedLibrary))); dependsOnVbNormalProject = workspace.AddProject( ProjectInfo.Create( @@ -5355,7 +5386,11 @@ private static void GetMultipleProjects( "CSharpProject", LanguageNames.CSharp, metadataReferences: [MscorlibRef], - projectReferences: [new ProjectReference(vbNormalProject.Id)])); + projectReferences: [new ProjectReference(vbNormalProject.Id)], + compilationOptions: workspace.Services + .GetLanguageService(LanguageNames.CSharp) + .GetDefaultCompilationOptions() + .WithOutputKind(OutputKind.DynamicallyLinkedLibrary))); transitivelyDependsOnBrokenProjects = workspace.AddProject( ProjectInfo.Create( From 75426e84cb70f5be0eca903c8c506d279dfe1d45 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 4 Feb 2025 10:01:01 -0800 Subject: [PATCH 014/305] Do not try to make a P2P metadata reference when a 'NetModule' is involed --- .../Workspace/Solution/SolutionCompilationState.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs index 83eb6fae6fa13..cf2b521c16588 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs @@ -1216,6 +1216,14 @@ public ValueTask> GetSourceGeneratorDiagnosticsAsync( { try { + // Getting a metadata reference from a 'module' is not supported from the compilation layer. Nor is + // emitting a 'metadata-only' stream for it (a 'skeleton' reference). So we just bail here. Note: this is + // not a common user scenario (they have to be explicitly creating 'modules' which are a feature practically + // never used anymore). So it's not worthwhile trying to merge the referenced module into the current + // compilation in any fashion. + if (tracker.ProjectState.CompilationOptions?.OutputKind == OutputKind.NetModule) + return null; + // If same language then we can wrap the other project's compilation into a compilation reference if (tracker.ProjectState.LanguageServices == fromProject.LanguageServices) { From 770b6fc00a58684f9111a6aa74b7e7d3f0283314 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 4 Feb 2025 10:34:07 -0800 Subject: [PATCH 015/305] Remove IGlobalOptionService from diagnostic service --- .../Analyzer/AnalyzerSettingsProvider.cs | 10 +- .../AnalyzerSettingsProviderFactory.cs | 13 +- ...AnalyzerSettingsWorkspaceServiceFactory.cs | 9 +- .../MockDiagnosticAnalyzerService.cs | 75 +++++------ .../Diagnostics/IDiagnosticAnalyzerService.cs | 2 - .../Diagnostics/DiagnosticAnalyzerService.cs | 2 +- ...sticAnalyzerService_IncrementalAnalyzer.cs | 2 +- .../EngineV2/DiagnosticIncrementalAnalyzer.cs | 123 +++++++++--------- .../Core/Def/Venus/ContainedLanguage.cs | 2 - .../ExternalDiagnosticUpdateSourceTests.vb | 17 +-- 10 files changed, 124 insertions(+), 131 deletions(-) diff --git a/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Analyzer/AnalyzerSettingsProvider.cs b/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Analyzer/AnalyzerSettingsProvider.cs index f5604f7827400..5939394e80816 100644 --- a/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Analyzer/AnalyzerSettingsProvider.cs +++ b/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Analyzer/AnalyzerSettingsProvider.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Data; using Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Updater; using Microsoft.CodeAnalysis.EditorConfig; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using RoslynEnumerableExtensions = Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Extensions.EnumerableExtensions; @@ -19,8 +20,13 @@ internal sealed class AnalyzerSettingsProvider : SettingsProviderBase +internal sealed class AnalyzerSettingsProviderFactory( + Workspace workspace, + IDiagnosticAnalyzerService analyzerService, + IGlobalOptionService globalOptionService) : IWorkspaceSettingsProviderFactory { - private readonly Workspace _workspace = workspace; - private readonly IDiagnosticAnalyzerService _analyzerService = analyzerService; - public ISettingsProvider GetForFile(string filePath) { - var updater = new AnalyzerSettingsUpdater(_workspace, filePath); - return new AnalyzerSettingsProvider(filePath, updater, _workspace, _analyzerService); + var updater = new AnalyzerSettingsUpdater(workspace, filePath); + return new AnalyzerSettingsProvider(filePath, updater, workspace, analyzerService, globalOptionService); } } diff --git a/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Analyzer/AnalyzerSettingsWorkspaceServiceFactory.cs b/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Analyzer/AnalyzerSettingsWorkspaceServiceFactory.cs index b098b95760e16..7945e987215b1 100644 --- a/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Analyzer/AnalyzerSettingsWorkspaceServiceFactory.cs +++ b/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Analyzer/AnalyzerSettingsWorkspaceServiceFactory.cs @@ -8,16 +8,17 @@ using Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Data; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.Editor.EditorConfigSettings.DataProvider.Analyzer; [ExportWorkspaceServiceFactory(typeof(IWorkspaceSettingsProviderFactory)), Shared] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal class AnalyzerSettingsWorkspaceServiceFactory(IDiagnosticAnalyzerService analyzerService) : IWorkspaceServiceFactory +internal sealed class AnalyzerSettingsWorkspaceServiceFactory( + IDiagnosticAnalyzerService analyzerService, + IGlobalOptionService globalOptionService) : IWorkspaceServiceFactory { - private readonly IDiagnosticAnalyzerService _analyzerService = analyzerService; - public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) - => new AnalyzerSettingsProviderFactory(workspaceServices.Workspace, _analyzerService); + => new AnalyzerSettingsProviderFactory(workspaceServices.Workspace, analyzerService, globalOptionService); } diff --git a/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs b/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs index 7c4baeeb75e67..7f1337b390ffe 100644 --- a/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs +++ b/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs @@ -16,58 +16,49 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; -namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics -{ - [Export(typeof(IDiagnosticAnalyzerService)), Shared, PartNotDiscoverable] - internal class MockDiagnosticAnalyzerService : IDiagnosticAnalyzerService - { - private readonly ArrayBuilder<(DiagnosticData Diagnostic, DiagnosticKind KindFilter)> _diagnosticsWithKindFilter; - public bool RequestedRefresh; +namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics; - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public MockDiagnosticAnalyzerService(IGlobalOptionService globalOptions) - { - GlobalOptions = globalOptions; - _diagnosticsWithKindFilter = ArrayBuilder<(DiagnosticData Diagnostic, DiagnosticKind KindFilter)>.GetInstance(); - } - - public void AddDiagnostic(DiagnosticData diagnostic, DiagnosticKind diagnosticKind) - => _diagnosticsWithKindFilter.Add((diagnostic, diagnosticKind)); +[Export(typeof(IDiagnosticAnalyzerService)), Shared, PartNotDiscoverable] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal class MockDiagnosticAnalyzerService() : IDiagnosticAnalyzerService +{ + private readonly ArrayBuilder<(DiagnosticData Diagnostic, DiagnosticKind KindFilter)> _diagnosticsWithKindFilter = ArrayBuilder<(DiagnosticData Diagnostic, DiagnosticKind KindFilter)>.GetInstance(); + public bool RequestedRefresh; - public void AddDiagnostics(ImmutableArray diagnostics, DiagnosticKind diagnosticKind) - { - foreach (var diagnostic in diagnostics) - AddDiagnostic(diagnostic, diagnosticKind); - } + public void AddDiagnostic(DiagnosticData diagnostic, DiagnosticKind diagnosticKind) + => _diagnosticsWithKindFilter.Add((diagnostic, diagnosticKind)); - public void RequestDiagnosticRefresh() - => RequestedRefresh = true; + public void AddDiagnostics(ImmutableArray diagnostics, DiagnosticKind diagnosticKind) + { + foreach (var diagnostic in diagnostics) + AddDiagnostic(diagnostic, diagnosticKind); + } - public DiagnosticAnalyzerInfoCache AnalyzerInfoCache - => throw new NotImplementedException(); + public void RequestDiagnosticRefresh() + => RequestedRefresh = true; - public IGlobalOptionService GlobalOptions { get; } + public DiagnosticAnalyzerInfoCache AnalyzerInfoCache + => throw new NotImplementedException(); - public bool ContainsDiagnostics(Workspace workspace, ProjectId projectId) - => throw new NotImplementedException(); + public bool ContainsDiagnostics(Workspace workspace, ProjectId projectId) + => throw new NotImplementedException(); - public Task ForceAnalyzeProjectAsync(Project project, CancellationToken cancellationToken) - => throw new NotImplementedException(); + public Task ForceAnalyzeProjectAsync(Project project, CancellationToken cancellationToken) + => throw new NotImplementedException(); - public Task> GetCachedDiagnosticsAsync(Workspace workspace, ProjectId? projectId, DocumentId? documentId, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) - => throw new NotImplementedException(); + public Task> GetCachedDiagnosticsAsync(Workspace workspace, ProjectId? projectId, DocumentId? documentId, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) + => throw new NotImplementedException(); - public Task> GetDiagnosticsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) - => throw new NotImplementedException(); + public Task> GetDiagnosticsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) + => throw new NotImplementedException(); - public Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, Func>? getDocuments, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) - => throw new NotImplementedException(); + public Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, Func>? getDocuments, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) + => throw new NotImplementedException(); - public Task> GetDiagnosticsForSpanAsync(TextDocument document, TextSpan? range, Func? shouldIncludeDiagnostic, bool includeCompilerDiagnostics, ICodeActionRequestPriorityProvider priorityProvider, DiagnosticKind diagnosticKind, bool isExplicit, CancellationToken cancellationToken) - => Task.FromResult(_diagnosticsWithKindFilter.Where(d => diagnosticKind == d.KindFilter).Select(d => d.Diagnostic).ToImmutableArray()); + public Task> GetDiagnosticsForSpanAsync(TextDocument document, TextSpan? range, Func? shouldIncludeDiagnostic, bool includeCompilerDiagnostics, ICodeActionRequestPriorityProvider priorityProvider, DiagnosticKind diagnosticKind, bool isExplicit, CancellationToken cancellationToken) + => Task.FromResult(_diagnosticsWithKindFilter.Where(d => diagnosticKind == d.KindFilter).Select(d => d.Diagnostic).ToImmutableArray()); - public Task> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) - => throw new NotImplementedException(); - } + public Task> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) + => throw new NotImplementedException(); } diff --git a/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs index 406c168c5324e..acb8bddd6269e 100644 --- a/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs @@ -15,8 +15,6 @@ namespace Microsoft.CodeAnalysis.Diagnostics; internal interface IDiagnosticAnalyzerService { - public IGlobalOptionService GlobalOptions { get; } - /// /// Provides and caches analyzer information. /// diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs index fbef8d15b4f76..d1c23c1a5bd3d 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs @@ -30,7 +30,7 @@ internal partial class DiagnosticAnalyzerService : IDiagnosticAnalyzerService public DiagnosticAnalyzerInfoCache AnalyzerInfoCache { get; private set; } public IAsynchronousOperationListener Listener { get; } - public IGlobalOptionService GlobalOptions { get; } + private IGlobalOptionService GlobalOptions { get; } private readonly ConditionalWeakTable _map = new(); private readonly ConditionalWeakTable.CreateValueCallback _createIncrementalAnalyzer; diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs index 99d9760bc40ca..078135d1990a2 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs @@ -21,7 +21,7 @@ private DiagnosticIncrementalAnalyzer CreateIncrementalAnalyzerCallback(Workspac // subscribe to active context changed event for new workspace workspace.DocumentActiveContextChanged += OnDocumentActiveContextChanged; - return new DiagnosticIncrementalAnalyzer(this, workspace, AnalyzerInfoCache); + return new DiagnosticIncrementalAnalyzer(this, workspace, AnalyzerInfoCache, this.GlobalOptions); } private void OnDocumentActiveContextChanged(object? sender, DocumentActiveContextChangedEventArgs e) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index 50c31315f85ca..92657188a6b37 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -14,82 +14,83 @@ using Microsoft.CodeAnalysis.Workspaces.Diagnostics; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 +namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2; + +/// +/// Diagnostic Analyzer Engine V2 +/// +/// This one follows pattern compiler has set for diagnostic analyzer. +/// +internal partial class DiagnosticIncrementalAnalyzer { - /// - /// Diagnostic Analyzer Engine V2 - /// - /// This one follows pattern compiler has set for diagnostic analyzer. - /// - internal partial class DiagnosticIncrementalAnalyzer + private readonly DiagnosticAnalyzerTelemetry _telemetry = new(); + private readonly StateManager _stateManager; + private readonly InProcOrRemoteHostAnalyzerRunner _diagnosticAnalyzerRunner; + private readonly IncrementalMemberEditAnalyzer _incrementalMemberEditAnalyzer = new(); + + internal DiagnosticAnalyzerService AnalyzerService { get; } + internal Workspace Workspace { get; } + + [Obsolete(MefConstruction.FactoryMethodMessage, error: true)] + public DiagnosticIncrementalAnalyzer( + DiagnosticAnalyzerService analyzerService, + Workspace workspace, + DiagnosticAnalyzerInfoCache analyzerInfoCache, + IGlobalOptionService globalOptionService) { - private readonly DiagnosticAnalyzerTelemetry _telemetry = new(); - private readonly StateManager _stateManager; - private readonly InProcOrRemoteHostAnalyzerRunner _diagnosticAnalyzerRunner; - private readonly IncrementalMemberEditAnalyzer _incrementalMemberEditAnalyzer = new(); - - internal DiagnosticAnalyzerService AnalyzerService { get; } - internal Workspace Workspace { get; } - - [Obsolete(MefConstruction.FactoryMethodMessage, error: true)] - public DiagnosticIncrementalAnalyzer( - DiagnosticAnalyzerService analyzerService, - Workspace workspace, - DiagnosticAnalyzerInfoCache analyzerInfoCache) - { - Contract.ThrowIfNull(analyzerService); + Contract.ThrowIfNull(analyzerService); - AnalyzerService = analyzerService; - Workspace = workspace; + AnalyzerService = analyzerService; + Workspace = workspace; + GlobalOptions = globalOptionService; - _stateManager = new StateManager(workspace, analyzerInfoCache); - _stateManager.ProjectAnalyzerReferenceChanged += OnProjectAnalyzerReferenceChanged; + _stateManager = new StateManager(workspace, analyzerInfoCache); + _stateManager.ProjectAnalyzerReferenceChanged += OnProjectAnalyzerReferenceChanged; - var enabled = this.AnalyzerService.GlobalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler); - _diagnosticAnalyzerRunner = new InProcOrRemoteHostAnalyzerRunner( - enabled, analyzerInfoCache, analyzerService.Listener); - } + var enabled = globalOptionService.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler); + _diagnosticAnalyzerRunner = new InProcOrRemoteHostAnalyzerRunner( + enabled, analyzerInfoCache, analyzerService.Listener); + } - internal IGlobalOptionService GlobalOptions => AnalyzerService.GlobalOptions; - internal DiagnosticAnalyzerInfoCache DiagnosticAnalyzerInfoCache => _diagnosticAnalyzerRunner.AnalyzerInfoCache; + internal IGlobalOptionService GlobalOptions { get; } + internal DiagnosticAnalyzerInfoCache DiagnosticAnalyzerInfoCache => _diagnosticAnalyzerRunner.AnalyzerInfoCache; - private void OnProjectAnalyzerReferenceChanged(object? sender, ProjectAnalyzerReferenceChangedEventArgs e) + private void OnProjectAnalyzerReferenceChanged(object? sender, ProjectAnalyzerReferenceChangedEventArgs e) + { + if (e.Removed.Length == 0) { - if (e.Removed.Length == 0) - { - // nothing to refresh - return; - } - - // make sure we drop cache related to the analyzers - foreach (var stateSet in e.Removed) - stateSet.OnRemoved(); + // nothing to refresh + return; } - public static Task GetDiagnosticVersionAsync(Project project, CancellationToken cancellationToken) - => project.GetDependentVersionAsync(cancellationToken); - - private static DiagnosticAnalysisResult GetResultOrEmpty(ImmutableDictionary map, DiagnosticAnalyzer analyzer, ProjectId projectId, VersionStamp version) - { - if (map.TryGetValue(analyzer, out var result)) - { - return result; - } + // make sure we drop cache related to the analyzers + foreach (var stateSet in e.Removed) + stateSet.OnRemoved(); + } - return DiagnosticAnalysisResult.CreateEmpty(projectId, version); - } + public static Task GetDiagnosticVersionAsync(Project project, CancellationToken cancellationToken) + => project.GetDependentVersionAsync(cancellationToken); - internal async Task> GetAnalyzersTestOnlyAsync(Project project, CancellationToken cancellationToken) + private static DiagnosticAnalysisResult GetResultOrEmpty(ImmutableDictionary map, DiagnosticAnalyzer analyzer, ProjectId projectId, VersionStamp version) + { + if (map.TryGetValue(analyzer, out var result)) { - var analyzers = await _stateManager.GetOrCreateStateSetsAsync(project, cancellationToken).ConfigureAwait(false); - - return analyzers.Select(s => s.Analyzer); + return result; } - private static string GetProjectLogMessage(Project project, ImmutableArray stateSets) - => $"project: ({project.Id}), ({string.Join(Environment.NewLine, stateSets.Select(s => s.Analyzer.ToString()))})"; + return DiagnosticAnalysisResult.CreateEmpty(projectId, version); + } - private static string GetOpenLogMessage(TextDocument document) - => $"document open: ({document.FilePath ?? document.Name})"; + internal async Task> GetAnalyzersTestOnlyAsync(Project project, CancellationToken cancellationToken) + { + var analyzers = await _stateManager.GetOrCreateStateSetsAsync(project, cancellationToken).ConfigureAwait(false); + + return analyzers.Select(s => s.Analyzer); } + + private static string GetProjectLogMessage(Project project, ImmutableArray stateSets) + => $"project: ({project.Id}), ({string.Join(Environment.NewLine, stateSets.Select(s => s.Analyzer.ToString()))})"; + + private static string GetOpenLogMessage(TextDocument document) + => $"document open: ({document.FilePath ?? document.Name})"; } diff --git a/src/VisualStudio/Core/Def/Venus/ContainedLanguage.cs b/src/VisualStudio/Core/Def/Venus/ContainedLanguage.cs index d4757ab3baa21..0b511973a49b5 100644 --- a/src/VisualStudio/Core/Def/Venus/ContainedLanguage.cs +++ b/src/VisualStudio/Core/Def/Venus/ContainedLanguage.cs @@ -139,8 +139,6 @@ internal ContainedLanguage( this.DataBuffer.Changed += OnDataBufferChanged; } - public IGlobalOptionService GlobalOptions => _diagnosticAnalyzerService.GlobalOptions; - private void OnDisconnect() { this.DataBuffer.Changed -= OnDataBufferChanged; diff --git a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb index 71428bfca2712..057443deff25b 100644 --- a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb +++ b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb @@ -52,7 +52,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim threadingContext = workspace.ExportProvider.GetExport(Of IThreadingContext).Value - Dim service = New TestDiagnosticAnalyzerService(workspace.GlobalOptions) + Dim service = New TestDiagnosticAnalyzerService() Dim vsWorkspace = workspace.ExportProvider.GetExportedValue(Of MockVisualStudioWorkspace)() vsWorkspace.SetWorkspace(workspace) Using source = workspace.ExportProvider.GetExportedValue(Of ExternalErrorDiagnosticUpdateSource)() @@ -71,7 +71,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Async Function TestExternalDiagnostics_SupportedDiagnosticId_Concurrent() As Task Using workspace = EditorTestWorkspace.CreateCSharp(String.Empty, composition:=s_composition) Dim waiter = workspace.GetService(Of AsynchronousOperationListenerProvider)().GetWaiter(FeatureAttribute.ErrorList) - Dim service = New TestDiagnosticAnalyzerService(workspace.GlobalOptions) + Dim service = New TestDiagnosticAnalyzerService() Dim vsWorkspace = workspace.ExportProvider.GetExportedValue(Of MockVisualStudioWorkspace)() vsWorkspace.SetWorkspace(workspace) Using source = workspace.ExportProvider.GetExportedValue(Of ExternalErrorDiagnosticUpdateSource)() @@ -98,7 +98,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim threadingContext = workspace.ExportProvider.GetExport(Of IThreadingContext).Value - Dim service = New TestDiagnosticAnalyzerService(workspace.GlobalOptions) + Dim service = New TestDiagnosticAnalyzerService() Dim vsWorkspace = workspace.ExportProvider.GetExportedValue(Of MockVisualStudioWorkspace)() vsWorkspace.SetWorkspace(workspace) Using source = workspace.ExportProvider.GetExportedValue(Of ExternalErrorDiagnosticUpdateSource)() @@ -119,7 +119,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim project = workspace.CurrentSolution.Projects.First() Dim diagnostic = GetDiagnosticData(project.Id) - Dim service = New TestDiagnosticAnalyzerService(workspace.GlobalOptions) + Dim service = New TestDiagnosticAnalyzerService() Dim threadingContext = workspace.ExportProvider.GetExportedValue(Of IThreadingContext) Dim testServiceBroker = workspace.ExportProvider.GetExportedValue(Of TestServiceBroker) Dim vsWorkspace = workspace.ExportProvider.GetExportedValue(Of MockVisualStudioWorkspace)() @@ -154,7 +154,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim project = workspace.CurrentSolution.Projects.First() - Dim service = New TestDiagnosticAnalyzerService(workspace.GlobalOptions) + Dim service = New TestDiagnosticAnalyzerService() Dim threadingContext = workspace.ExportProvider.GetExportedValue(Of IThreadingContext) Dim testServiceBroker = workspace.ExportProvider.GetExportedValue(Of TestServiceBroker) Dim vsWorkspace = workspace.ExportProvider.GetExportedValue(Of MockVisualStudioWorkspace)() @@ -195,7 +195,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim project = workspace.CurrentSolution.Projects.First() Dim diagnostic = GetDiagnosticData(project.Id) - Dim service = New TestDiagnosticAnalyzerService(globalOptions) + Dim service = New TestDiagnosticAnalyzerService() Dim threadingContext = workspace.ExportProvider.GetExportedValue(Of IThreadingContext) Dim testServiceBroker = workspace.ExportProvider.GetExportedValue(Of TestServiceBroker) Dim vsWorkspace = workspace.ExportProvider.GetExportedValue(Of MockVisualStudioWorkspace)() @@ -300,11 +300,8 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Private ReadOnly _analyzerInfoCache As DiagnosticAnalyzerInfoCache - Public ReadOnly Property GlobalOptions As IGlobalOptionService Implements IDiagnosticAnalyzerService.GlobalOptions - - Public Sub New(globalOptions As IGlobalOptionService, Optional data As ImmutableArray(Of DiagnosticData) = Nothing) + Public Sub New(Optional data As ImmutableArray(Of DiagnosticData) = Nothing) _analyzerInfoCache = New DiagnosticAnalyzerInfoCache() - Me.GlobalOptions = globalOptions End Sub Public ReadOnly Property AnalyzerInfoCache As DiagnosticAnalyzerInfoCache Implements IDiagnosticAnalyzerService.AnalyzerInfoCache From 0a46d6e88db901ea04ad050ced3c2c40ebfebbfb Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 4 Feb 2025 10:58:44 -0800 Subject: [PATCH 016/305] Remove unnecessary parameters from diagnostics api --- .../CodeAnalysisDiagnosticAnalyzerService.cs | 6 ++---- .../Diagnostics/IDiagnosticAnalyzerService.cs | 20 +++++++++---------- .../Diagnostics/DiagnosticAnalyzerService.cs | 5 +++-- ...osticIncrementalAnalyzer_GetDiagnostics.cs | 13 ++++++------ 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/Features/Core/Portable/Diagnostics/CodeAnalysisDiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/CodeAnalysisDiagnosticAnalyzerService.cs index cc52cd179ee96..543242d1f71ed 100644 --- a/src/Features/Core/Portable/Diagnostics/CodeAnalysisDiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/Diagnostics/CodeAnalysisDiagnosticAnalyzerService.cs @@ -147,8 +147,7 @@ public async Task> GetLastComputedDocumentDiagnos return []; var diagnostics = await _diagnosticAnalyzerService.GetCachedDiagnosticsAsync( - _workspace, documentId.ProjectId, documentId, includeLocalDocumentDiagnostics: true, - includeNonLocalDocumentDiagnostics: true, cancellationToken).ConfigureAwait(false); + _workspace, documentId.ProjectId, documentId, cancellationToken).ConfigureAwait(false); return diagnostics.WhereAsArray(d => !d.IsSuppressed); } @@ -166,8 +165,7 @@ public async Task> GetLastComputedProjectDiagnost return []; var diagnostics = await _diagnosticAnalyzerService.GetCachedDiagnosticsAsync( - _workspace, projectId, documentId: null, includeLocalDocumentDiagnostics: false, - includeNonLocalDocumentDiagnostics: false, cancellationToken).ConfigureAwait(false); + _workspace, projectId, documentId: null, cancellationToken).ConfigureAwait(false); return diagnostics.WhereAsArray(d => !d.IsSuppressed); } } diff --git a/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs index 406c168c5324e..3158be28ee07e 100644 --- a/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs @@ -31,21 +31,21 @@ internal interface IDiagnosticAnalyzerService /// Get diagnostics currently stored in the source. returned diagnostic might be out-of-date if solution has changed but analyzer hasn't run for the new solution. /// /// Workspace for the document/project/solution to compute diagnostics for. - /// Optional project to scope the returned diagnostics. - /// Optional document to scope the returned diagnostics. - /// - /// Indicates if local document diagnostics must be returned. + /// Required project to scope the returned diagnostics. + /// Optional document to scope the returned diagnostics. When provided, only local + /// diagnostics to that document are returned and non-local diagnostics are not returned. When absent, only + /// non-local diagnostics are included and local diagnostics are not returned. + /// Cancellation token. + /// /// Local diagnostics are the ones that are reported by analyzers on the same file for which the callback was received /// and hence can be computed by analyzing a single file in isolation. - /// - /// - /// Indicates if non-local document diagnostics must be returned. + /// /// Non-local diagnostics are the ones reported by analyzers either at compilation end callback OR /// in a different file from which the callback was made. Entire project must be analyzed to get the /// complete set of non-local document diagnostics. - /// - /// Cancellation token. - Task> GetCachedDiagnosticsAsync(Workspace workspace, ProjectId? projectId, DocumentId? documentId, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken); + /// + Task> GetCachedDiagnosticsAsync( + Workspace workspace, ProjectId projectId, DocumentId? documentId, CancellationToken cancellationToken); /// /// Force analyzes the given project by running all applicable analyzers on the project and caching the reported analyzer diagnostics. diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs index fbef8d15b4f76..5e0d300915380 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs @@ -94,10 +94,11 @@ public async Task> GetDiagnosticsForSpanAsync( priorityProvider, diagnosticKinds, isExplicit, cancellationToken).ConfigureAwait(false); } - public Task> GetCachedDiagnosticsAsync(Workspace workspace, ProjectId? projectId, DocumentId? documentId, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) + public Task> GetCachedDiagnosticsAsync( + Workspace workspace, ProjectId? projectId, DocumentId? documentId, CancellationToken cancellationToken) { var analyzer = CreateIncrementalAnalyzer(workspace); - return analyzer.GetCachedDiagnosticsAsync(workspace.CurrentSolution, projectId, documentId, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics, cancellationToken); + return analyzer.GetCachedDiagnosticsAsync(workspace.CurrentSolution, projectId, documentId, cancellationToken); } public async Task ForceAnalyzeProjectAsync(Project project, CancellationToken cancellationToken) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index 0aaa78c089dd6..3a74cba4d06ee 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -15,8 +15,8 @@ namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 { internal partial class DiagnosticIncrementalAnalyzer { - public Task> GetCachedDiagnosticsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) - => new IdeCachedDiagnosticGetter(this, solution, projectId, documentId, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics).GetDiagnosticsAsync(cancellationToken); + public Task> GetCachedDiagnosticsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, CancellationToken cancellationToken) + => new IdeCachedDiagnosticGetter(this, solution, projectId, documentId).GetDiagnosticsAsync(cancellationToken); public Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, Func>? getDocuments, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) => new IdeLatestDiagnosticGetter(this, solution, projectId, documentId, diagnosticIds, shouldIncludeAnalyzer, getDocuments, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics).GetDiagnosticsAsync(cancellationToken); @@ -98,10 +98,11 @@ private sealed class IdeCachedDiagnosticGetter( DiagnosticIncrementalAnalyzer owner, Solution solution, ProjectId? projectId, - DocumentId? documentId, - bool includeLocalDocumentDiagnostics, - bool includeNonLocalDocumentDiagnostics) : DiagnosticGetter( - owner, solution, projectId, documentId, getDocuments: null, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics) + DocumentId? documentId) + : DiagnosticGetter( + owner, solution, projectId, documentId, getDocuments: null, + includeLocalDocumentDiagnostics: documentId != null, + includeNonLocalDocumentDiagnostics: documentId != null) { protected override async Task ProduceDiagnosticsAsync( Project project, IReadOnlyList documentIds, bool includeProjectNonLocalResult, From 3150d6d93db5dd46d4d0b9dbe88046cc4baec83b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 4 Feb 2025 10:59:09 -0800 Subject: [PATCH 017/305] Simplify --- .../Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs index 5e0d300915380..c68dca52fea1c 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs @@ -21,8 +21,7 @@ namespace Microsoft.CodeAnalysis.Diagnostics { - [Export(typeof(IDiagnosticAnalyzerService))] - [Shared] + [Export(typeof(IDiagnosticAnalyzerService)), Shared] internal partial class DiagnosticAnalyzerService : IDiagnosticAnalyzerService { private static readonly Option2 s_crashOnAnalyzerException = new("dotnet_crash_on_analyzer_exception", defaultValue: false); From 516b39195aba9ec8adb17fbbf79bba376dce2514 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 4 Feb 2025 11:03:52 -0800 Subject: [PATCH 018/305] Fix tests --- .../TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs b/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs index 7c4baeeb75e67..8abd199b22097 100644 --- a/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs +++ b/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs @@ -55,7 +55,7 @@ public bool ContainsDiagnostics(Workspace workspace, ProjectId projectId) public Task ForceAnalyzeProjectAsync(Project project, CancellationToken cancellationToken) => throw new NotImplementedException(); - public Task> GetCachedDiagnosticsAsync(Workspace workspace, ProjectId? projectId, DocumentId? documentId, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) + public Task> GetCachedDiagnosticsAsync(Workspace workspace, ProjectId? projectId, DocumentId? documentId, CancellationToken cancellationToken) => throw new NotImplementedException(); public Task> GetDiagnosticsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) From cf1e524ae827afed58e05600fa194fe35f334b0e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 4 Feb 2025 11:13:17 -0800 Subject: [PATCH 019/305] Add check --- .../ProjectSystemProjectFactory.cs | 17 ++++++++++++----- .../Solution/SolutionCompilationState.cs | 3 +++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectFactory.cs b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectFactory.cs index 6b47e6cfdfd01..e31420944a198 100644 --- a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectFactory.cs +++ b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectFactory.cs @@ -603,11 +603,12 @@ private static bool CanConvertMetadataReferenceToProjectReference(Solution solut Contract.ThrowIfNull(projectWithMetadataReference); Contract.ThrowIfNull(referencedProject); - // We don't want to convert a metadata reference to a project reference if the project being referenced isn't something - // we can create a Compilation for. For example, if we have a C# project, and it's referencing a F# project via a metadata reference - // everything would be fine if we left it a metadata reference. Converting it to a project reference means we couldn't create a Compilation - // anymore in the IDE, since the C# compilation would need to reference an F# compilation. F# projects referencing other F# projects though - // do expect this to work, and so we'll always allow references through of the same language. + // We don't want to convert a metadata reference to a project reference if the project being referenced isn't + // something we can create a Compilation for. For example, if we have a C# project, and it's referencing a F# + // project via a metadata reference everything would be fine if we left it a metadata reference. Converting it + // to a project reference means we couldn't create a Compilation anymore in the IDE, since the C# compilation + // would need to reference an F# compilation. F# projects referencing other F# projects though do expect this to + // work, and so we'll always allow references through of the same language. if (projectWithMetadataReference.Language != referencedProject.Language) { if (projectWithMetadataReference.LanguageServices.GetService() != null && @@ -618,6 +619,12 @@ private static bool CanConvertMetadataReferenceToProjectReference(Solution solut } } + // Getting a metadata reference from a 'module' is not supported from the compilation layer. Nor is emitting a + // 'metadata-only' stream for it (a 'skeleton' reference). So converting a NetModule reference to a project + // reference won't actually help us out. Best to keep this as a plain metadata reference. + if (referencedProject.CompilationOptions?.OutputKind == OutputKind.NetModule) + return false; + // If this is going to cause a circular reference, also disallow it if (solution.GetProjectDependencyGraph().GetProjectsThatThisProjectTransitivelyDependsOn(referencedProjectId).Contains(projectIdWithMetadataReference)) { diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs index cf2b521c16588..0c403bf2167b6 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs @@ -1221,6 +1221,9 @@ public ValueTask> GetSourceGeneratorDiagnosticsAsync( // not a common user scenario (they have to be explicitly creating 'modules' which are a feature practically // never used anymore). So it's not worthwhile trying to merge the referenced module into the current // compilation in any fashion. + // + // Note: ProjectSystemProjectFactory.CanConvertMetadataReferenceToProjectReference attempts to prevent this + // scenario from arising. However, this code just acts as a final failsafe to ensure we don't crash. if (tracker.ProjectState.CompilationOptions?.OutputKind == OutputKind.NetModule) return null; From d81080a50087dff93e6a12a8d0b09423b2619bfb Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 4 Feb 2025 11:24:57 -0800 Subject: [PATCH 020/305] Make project id required with diagnostic requestS --- .../Diagnostics/IDiagnosticAnalyzerService.cs | 6 +- ...CodeFixService.FixAllDiagnosticProvider.cs | 113 +++++++++--------- .../Diagnostics/DiagnosticAnalyzerService.cs | 6 +- ...osticIncrementalAnalyzer_GetDiagnostics.cs | 17 +-- 4 files changed, 71 insertions(+), 71 deletions(-) diff --git a/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs index 3158be28ee07e..c7aade9b1f72f 100644 --- a/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs @@ -75,7 +75,7 @@ Task> GetCachedDiagnosticsAsync( /// project must be analyzed to get the complete set of non-local document diagnostics. /// /// Cancellation token. - Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, Func>? getDocumentIds, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken); + Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, Func>? getDocumentIds, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken); /// /// Get project diagnostics (diagnostics with no source location) of the given diagnostic ids and/or analyzers from @@ -93,7 +93,7 @@ Task> GetCachedDiagnosticsAsync( /// Entire project must be analyzed to get the complete set of non-local diagnostics. /// /// Cancellation token. - Task> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken); + Task> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId projectId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken); /// /// Return up to date diagnostics for the given span for the document @@ -151,7 +151,7 @@ public static Task> GetDiagnosticsForSpanAsync(th } public static Task> GetDiagnosticsForIdsAsync( - this IDiagnosticAnalyzerService service, Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) + this IDiagnosticAnalyzerService service, Solution solution, ProjectId projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) { return service.GetDiagnosticsForIdsAsync( solution, projectId, documentId, diagnosticIds, shouldIncludeAnalyzer, getDocumentIds: null, diff --git a/src/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs b/src/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs index 55bde7157b61e..16b67fd2bdb45 100644 --- a/src/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs +++ b/src/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs @@ -12,74 +12,73 @@ using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.CodeFixes +namespace Microsoft.CodeAnalysis.CodeFixes; + +internal partial class CodeFixService { - internal partial class CodeFixService + private sealed class FixAllDiagnosticProvider : FixAllContext.SpanBasedDiagnosticProvider { - private sealed class FixAllDiagnosticProvider : FixAllContext.SpanBasedDiagnosticProvider + private readonly IDiagnosticAnalyzerService _diagnosticService; + private readonly ImmutableHashSet? _diagnosticIds; + private readonly bool _includeSuppressedDiagnostics; + + public FixAllDiagnosticProvider(IDiagnosticAnalyzerService diagnosticService, ImmutableHashSet diagnosticIds) { - private readonly IDiagnosticAnalyzerService _diagnosticService; - private readonly ImmutableHashSet? _diagnosticIds; - private readonly bool _includeSuppressedDiagnostics; + _diagnosticService = diagnosticService; - public FixAllDiagnosticProvider(IDiagnosticAnalyzerService diagnosticService, ImmutableHashSet diagnosticIds) + // When computing FixAll for unnecessary pragma suppression diagnostic, + // we need to include suppressed diagnostics, as well as reported compiler and analyzer diagnostics. + // A null value for '_diagnosticIds' ensures the latter. + if (diagnosticIds.Contains(IDEDiagnosticIds.RemoveUnnecessarySuppressionDiagnosticId)) { - _diagnosticService = diagnosticService; - - // When computing FixAll for unnecessary pragma suppression diagnostic, - // we need to include suppressed diagnostics, as well as reported compiler and analyzer diagnostics. - // A null value for '_diagnosticIds' ensures the latter. - if (diagnosticIds.Contains(IDEDiagnosticIds.RemoveUnnecessarySuppressionDiagnosticId)) - { - _diagnosticIds = null; - _includeSuppressedDiagnostics = true; - } - else - { - _diagnosticIds = diagnosticIds; - _includeSuppressedDiagnostics = false; - } + _diagnosticIds = null; + _includeSuppressedDiagnostics = true; } - - private ImmutableArray Filter(ImmutableArray diagnostics) - => diagnostics.WhereAsArray(d => _includeSuppressedDiagnostics || !d.IsSuppressed); - - public override async Task> GetDocumentDiagnosticsAsync(Document document, CancellationToken cancellationToken) + else { - var solution = document.Project.Solution; - var diagnostics = Filter(await _diagnosticService.GetDiagnosticsForIdsAsync( - solution, projectId: null, document.Id, _diagnosticIds, shouldIncludeAnalyzer: null, includeLocalDocumentDiagnostics: true, includeNonLocalDocumentDiagnostics: false, cancellationToken).ConfigureAwait(false)); - Contract.ThrowIfFalse(diagnostics.All(d => d.DocumentId != null)); - return await diagnostics.ToDiagnosticsAsync(document.Project, cancellationToken).ConfigureAwait(false); + _diagnosticIds = diagnosticIds; + _includeSuppressedDiagnostics = false; } + } - public override async Task> GetDocumentSpanDiagnosticsAsync(Document document, TextSpan fixAllSpan, CancellationToken cancellationToken) - { - bool shouldIncludeDiagnostic(string id) => _diagnosticIds == null || _diagnosticIds.Contains(id); - var diagnostics = Filter(await _diagnosticService.GetDiagnosticsForSpanAsync( - document, fixAllSpan, shouldIncludeDiagnostic, includeCompilerDiagnostics: true, - priorityProvider: new DefaultCodeActionRequestPriorityProvider(), - DiagnosticKind.All, isExplicit: false, cancellationToken).ConfigureAwait(false)); - Contract.ThrowIfFalse(diagnostics.All(d => d.DocumentId != null)); - return await diagnostics.ToDiagnosticsAsync(document.Project, cancellationToken).ConfigureAwait(false); - } + private ImmutableArray Filter(ImmutableArray diagnostics) + => diagnostics.WhereAsArray(d => _includeSuppressedDiagnostics || !d.IsSuppressed); - public override async Task> GetAllDiagnosticsAsync(Project project, CancellationToken cancellationToken) - { - // Get all diagnostics for the entire project, including document diagnostics. - var diagnostics = Filter(await _diagnosticService.GetDiagnosticsForIdsAsync( - project.Solution, project.Id, documentId: null, _diagnosticIds, shouldIncludeAnalyzer: null, includeLocalDocumentDiagnostics: true, includeNonLocalDocumentDiagnostics: false, cancellationToken).ConfigureAwait(false)); - return await diagnostics.ToDiagnosticsAsync(project, cancellationToken).ConfigureAwait(false); - } + public override async Task> GetDocumentDiagnosticsAsync(Document document, CancellationToken cancellationToken) + { + var solution = document.Project.Solution; + var diagnostics = Filter(await _diagnosticService.GetDiagnosticsForIdsAsync( + solution, projectId: document.Project.Id, document.Id, _diagnosticIds, shouldIncludeAnalyzer: null, includeLocalDocumentDiagnostics: true, includeNonLocalDocumentDiagnostics: false, cancellationToken).ConfigureAwait(false)); + Contract.ThrowIfFalse(diagnostics.All(d => d.DocumentId != null)); + return await diagnostics.ToDiagnosticsAsync(document.Project, cancellationToken).ConfigureAwait(false); + } - public override async Task> GetProjectDiagnosticsAsync(Project project, CancellationToken cancellationToken) - { - // Get all no-location diagnostics for the project, doesn't include document diagnostics. - var diagnostics = Filter(await _diagnosticService.GetProjectDiagnosticsForIdsAsync( - project.Solution, project.Id, _diagnosticIds, shouldIncludeAnalyzer: null, includeNonLocalDocumentDiagnostics: false, cancellationToken).ConfigureAwait(false)); - Contract.ThrowIfFalse(diagnostics.All(d => d.DocumentId == null)); - return await diagnostics.ToDiagnosticsAsync(project, cancellationToken).ConfigureAwait(false); - } + public override async Task> GetDocumentSpanDiagnosticsAsync(Document document, TextSpan fixAllSpan, CancellationToken cancellationToken) + { + bool shouldIncludeDiagnostic(string id) => _diagnosticIds == null || _diagnosticIds.Contains(id); + var diagnostics = Filter(await _diagnosticService.GetDiagnosticsForSpanAsync( + document, fixAllSpan, shouldIncludeDiagnostic, includeCompilerDiagnostics: true, + priorityProvider: new DefaultCodeActionRequestPriorityProvider(), + DiagnosticKind.All, isExplicit: false, cancellationToken).ConfigureAwait(false)); + Contract.ThrowIfFalse(diagnostics.All(d => d.DocumentId != null)); + return await diagnostics.ToDiagnosticsAsync(document.Project, cancellationToken).ConfigureAwait(false); + } + + public override async Task> GetAllDiagnosticsAsync(Project project, CancellationToken cancellationToken) + { + // Get all diagnostics for the entire project, including document diagnostics. + var diagnostics = Filter(await _diagnosticService.GetDiagnosticsForIdsAsync( + project.Solution, project.Id, documentId: null, _diagnosticIds, shouldIncludeAnalyzer: null, includeLocalDocumentDiagnostics: true, includeNonLocalDocumentDiagnostics: false, cancellationToken).ConfigureAwait(false)); + return await diagnostics.ToDiagnosticsAsync(project, cancellationToken).ConfigureAwait(false); + } + + public override async Task> GetProjectDiagnosticsAsync(Project project, CancellationToken cancellationToken) + { + // Get all no-location diagnostics for the project, doesn't include document diagnostics. + var diagnostics = Filter(await _diagnosticService.GetProjectDiagnosticsForIdsAsync( + project.Solution, project.Id, _diagnosticIds, shouldIncludeAnalyzer: null, includeNonLocalDocumentDiagnostics: false, cancellationToken).ConfigureAwait(false)); + Contract.ThrowIfFalse(diagnostics.All(d => d.DocumentId == null)); + return await diagnostics.ToDiagnosticsAsync(project, cancellationToken).ConfigureAwait(false); } } } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs index c68dca52fea1c..a8ce130fd64d8 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs @@ -94,7 +94,7 @@ public async Task> GetDiagnosticsForSpanAsync( } public Task> GetCachedDiagnosticsAsync( - Workspace workspace, ProjectId? projectId, DocumentId? documentId, CancellationToken cancellationToken) + Workspace workspace, ProjectId projectId, DocumentId? documentId, CancellationToken cancellationToken) { var analyzer = CreateIncrementalAnalyzer(workspace); return analyzer.GetCachedDiagnosticsAsync(workspace.CurrentSolution, projectId, documentId, cancellationToken); @@ -107,14 +107,14 @@ public async Task ForceAnalyzeProjectAsync(Project project, CancellationToken ca } public Task> GetDiagnosticsForIdsAsync( - Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, Func>? getDocuments, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) + Solution solution, ProjectId projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, Func>? getDocuments, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) { var analyzer = CreateIncrementalAnalyzer(solution.Workspace); return analyzer.GetDiagnosticsForIdsAsync(solution, projectId, documentId, diagnosticIds, shouldIncludeAnalyzer, getDocuments, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics, cancellationToken); } public Task> GetProjectDiagnosticsForIdsAsync( - Solution solution, ProjectId? projectId, ImmutableHashSet? diagnosticIds, + Solution solution, ProjectId projectId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) { diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index 3a74cba4d06ee..ee5c05c28dda5 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -15,19 +15,19 @@ namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 { internal partial class DiagnosticIncrementalAnalyzer { - public Task> GetCachedDiagnosticsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, CancellationToken cancellationToken) + public Task> GetCachedDiagnosticsAsync(Solution solution, ProjectId projectId, DocumentId? documentId, CancellationToken cancellationToken) => new IdeCachedDiagnosticGetter(this, solution, projectId, documentId).GetDiagnosticsAsync(cancellationToken); - public Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, Func>? getDocuments, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) + public Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, Func>? getDocuments, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) => new IdeLatestDiagnosticGetter(this, solution, projectId, documentId, diagnosticIds, shouldIncludeAnalyzer, getDocuments, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics).GetDiagnosticsAsync(cancellationToken); - public Task> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) + public Task> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId projectId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) => new IdeLatestDiagnosticGetter(this, solution, projectId, documentId: null, diagnosticIds, shouldIncludeAnalyzer, getDocuments: null, includeLocalDocumentDiagnostics: false, includeNonLocalDocumentDiagnostics).GetProjectDiagnosticsAsync(cancellationToken); private abstract class DiagnosticGetter( DiagnosticIncrementalAnalyzer owner, Solution solution, - ProjectId? projectId, + ProjectId projectId, DocumentId? documentId, Func>? getDocuments, bool includeLocalDocumentDiagnostics, @@ -36,7 +36,7 @@ private abstract class DiagnosticGetter( protected readonly DiagnosticIncrementalAnalyzer Owner = owner; protected readonly Solution Solution = solution; - protected readonly ProjectId? ProjectId = projectId ?? documentId?.ProjectId; + protected readonly ProjectId ProjectId = projectId; protected readonly DocumentId? DocumentId = documentId; protected readonly bool IncludeLocalDocumentDiagnostics = includeLocalDocumentDiagnostics; protected readonly bool IncludeNonLocalDocumentDiagnostics = includeNonLocalDocumentDiagnostics; @@ -97,7 +97,7 @@ protected void InvokeCallback(Action callback, ImmutableArray> GetProjectStateDiagnos private sealed class IdeLatestDiagnosticGetter( DiagnosticIncrementalAnalyzer owner, Solution solution, - ProjectId? projectId, + ProjectId projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, Func>? getDocuments, bool includeLocalDocumentDiagnostics, - bool includeNonLocalDocumentDiagnostics) : DiagnosticGetter( + bool includeNonLocalDocumentDiagnostics) + : DiagnosticGetter( owner, solution, projectId, documentId, getDocuments, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics) { private readonly ImmutableHashSet? _diagnosticIds = diagnosticIds; From e259bc99d1cac6abfc8a741dc490257975ce413d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 4 Feb 2025 11:30:31 -0800 Subject: [PATCH 021/305] Update tests --- src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs | 4 ++-- .../Test2/Diagnostics/DiagnosticServiceTests.vb | 5 +---- .../Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs index 1b777a72a585c..dec0dbc263e02 100644 --- a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs +++ b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs @@ -1141,8 +1141,8 @@ static bool GetExpectDeprioritization( static async Task VerifyCachedDiagnosticsAsync(Document sourceDocument, bool expectedCachedDiagnostic, TextSpan testSpan, DiagnosticIncrementalAnalyzer diagnosticIncrementalAnalyzer) { - var cachedDiagnostics = await diagnosticIncrementalAnalyzer.GetCachedDiagnosticsAsync(sourceDocument.Project.Solution, sourceDocument.Project.Id, sourceDocument.Id, - includeLocalDocumentDiagnostics: true, includeNonLocalDocumentDiagnostics: true, CancellationToken.None); + var cachedDiagnostics = await diagnosticIncrementalAnalyzer.GetCachedDiagnosticsAsync( + sourceDocument.Project.Solution, sourceDocument.Project.Id, sourceDocument.Id, CancellationToken.None); cachedDiagnostics = cachedDiagnostics.WhereAsArray(d => !d.IsSuppressed); if (!expectedCachedDiagnostic) diff --git a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb index aa4ecdbe70240..21492feaa1183 100644 --- a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb @@ -2132,10 +2132,7 @@ class MyClass ' Get cached project diagnostics. Dim diagnostics = Await diagnosticService.GetCachedDiagnosticsAsync( - workspace, project.Id, documentId:=Nothing, - includeLocalDocumentDiagnostics:=True, - includeNonLocalDocumentDiagnostics:=True, - CancellationToken.None) + workspace, project.Id, documentId:=Nothing, CancellationToken.None) ' in v2, solution crawler never creates non-local hidden diagnostics. ' v2 still creates those for LB and explicit queries such as FixAll. diff --git a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb index 71428bfca2712..1fe1dcb81fca5 100644 --- a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb +++ b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb @@ -320,7 +320,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Return SpecializedTasks.EmptyImmutableArray(Of DiagnosticData) End Function - Public Function GetCachedDiagnosticsAsync(workspace As Workspace, projectId As ProjectId, documentId As DocumentId, includeLocalDocumentDiagnostics As Boolean, includeNonLocalDocumentDiagnostics As Boolean, cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of DiagnosticData)) Implements IDiagnosticAnalyzerService.GetCachedDiagnosticsAsync + Public Function GetCachedDiagnosticsAsync(workspace As Workspace, projectId As ProjectId, documentId As DocumentId, cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of DiagnosticData)) Implements IDiagnosticAnalyzerService.GetCachedDiagnosticsAsync Return SpecializedTasks.EmptyImmutableArray(Of DiagnosticData)() End Function From c215ccefb66c8e093bb305805e6ebbb61508c265 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 4 Feb 2025 11:55:51 -0800 Subject: [PATCH 022/305] Remove 'FromBuild' flags inside diagnostic subsystem --- ...gnosticIncrementalAnalyzer.ProjectState.cs | 7 +- .../DiagnosticIncrementalAnalyzer.StateSet.cs | 3 - ...IncrementalAnalyzer_IncrementalAnalyzer.cs | 24 ------- .../Diagnostics/DiagnosticAnalysisResult.cs | 71 ++++--------------- 4 files changed, 15 insertions(+), 90 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index d738aa7ec02de..27b0c8e35dacf 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -40,8 +40,6 @@ public ProjectState(StateSet owner, ProjectId projectId) _lastResult = DiagnosticAnalysisResult.CreateInitialResult(projectId); } - public bool FromBuild => _lastResult.FromBuild; - public ImmutableHashSet GetDocumentsWithDiagnostics() => _lastResult.DocumentIdsOrEmpty; @@ -263,9 +261,6 @@ public async ValueTask MergeAsync(ActiveFileState state, TextDocument document, var fullAnalysis = _owner.Analyzer.IsFullSolutionAnalysisEnabled(globalOptions, project.Language) && await project.HasSuccessfullyLoadedAsync(CancellationToken.None).ConfigureAwait(false); - // keep from build flag if full analysis is off - var fromBuild = fullAnalysis ? false : lastResult.FromBuild; - // if it is allowed to keep project state, check versions and if they are same, bail out. // if full solution analysis is off or we are asked to reset document state, we always merge. if (fullAnalysis && @@ -288,7 +283,7 @@ public async ValueTask MergeAsync(ActiveFileState state, TextDocument document, AddToInMemoryStorage(serializerVersion, project, document, document.Id, SemanticStateName, semantic.Items); // save last aggregated form of analysis result - _lastResult = _lastResult.UpdateAggregatedResult(version, state.DocumentId, fromBuild); + _lastResult = _lastResult.UpdateAggregatedResult(version, state.DocumentId); } public bool OnDocumentRemoved(DocumentId id) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs index 3a6afd3e5633a..b353064dc253f 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs @@ -86,9 +86,6 @@ public void CollectDocumentsWithDiagnostics(ProjectId projectId, HashSet _activeFileStates.ContainsKey(documentId); - public bool FromBuild(ProjectId projectId) - => _projectStates.TryGetValue(projectId, out var projectState) && projectState.FromBuild; - public bool TryGetActiveFileState(DocumentId documentId, [NotNullWhen(true)] out ActiveFileState? state) => _activeFileStates.TryGetValue(documentId, out state); diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 93ba079476f40..eec0871924a2a 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -75,30 +75,6 @@ private async Task TextDocumentOpenAsync(TextDocument document, CancellationToke /// private ImmutableArray GetStateSetsForFullSolutionAnalysis(ImmutableArray stateSets, Project project) { - // If full analysis is off, remove state that is created from build. - // this will make sure diagnostics from build (converted from build to live) will never be cleared - // until next build. - _ = GlobalOptions.IsFullSolutionAnalysisEnabled(project.Language, out var compilerFullSolutionAnalysisEnabled, out var analyzersFullSolutionAnalysisEnabled); - if (!compilerFullSolutionAnalysisEnabled) - { - // Full solution analysis is not enabled for compiler diagnostics, - // so we remove the compiler analyzer state sets that are from build. - // We do so by retaining only those state sets that are - // either not for compiler analyzer or those which are for compiler - // analyzer, but not from build. - stateSets = stateSets.WhereAsArray(s => !s.Analyzer.IsCompilerAnalyzer() || !s.FromBuild(project.Id)); - } - - if (!analyzersFullSolutionAnalysisEnabled) - { - // Full solution analysis is not enabled for analyzer diagnostics, - // so we remove the analyzer state sets that are from build. - // We do so by retaining only those state sets that are - // either for the special compiler/workspace analyzers or those which are for - // other analyzers, but not from build. - stateSets = stateSets.WhereAsArray(s => s.Analyzer.IsCompilerAnalyzer() || s.Analyzer.IsWorkspaceDiagnosticAnalyzer() || !s.FromBuild(project.Id)); - } - // Include only analyzers we want to run for full solution analysis. // Analyzers not included here will never be saved because result is unknown. return stateSets.WhereAsArray(static (s, arg) => arg.self.IsCandidateForFullSolutionAnalysis(s.Analyzer, s.IsHostAnalyzer, arg.project), (self: this, project)); diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs index 9cd424ee9c284..056bc50303d40 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs @@ -20,7 +20,6 @@ namespace Microsoft.CodeAnalysis.Workspaces.Diagnostics; /// internal readonly struct DiagnosticAnalysisResult { - public readonly bool FromBuild; public readonly ProjectId ProjectId; public readonly VersionStamp Version; @@ -50,13 +49,16 @@ internal readonly struct DiagnosticAnalysisResult /// private readonly ImmutableArray _others; - private DiagnosticAnalysisResult(ProjectId projectId, VersionStamp version, ImmutableHashSet? documentIds, bool isEmpty, bool fromBuild) + private DiagnosticAnalysisResult( + ProjectId projectId, + VersionStamp version, + ImmutableHashSet? documentIds, + bool isEmpty) { ProjectId = projectId; Version = version; DocumentIds = documentIds; IsEmpty = isEmpty; - FromBuild = fromBuild; _syntaxLocals = null; _semanticLocals = null; @@ -71,8 +73,7 @@ private DiagnosticAnalysisResult( ImmutableDictionary> semanticLocals, ImmutableDictionary> nonLocals, ImmutableArray others, - ImmutableHashSet? documentIds, - bool fromBuild) + ImmutableHashSet? documentIds) { Debug.Assert(!others.IsDefault); Debug.Assert(!syntaxLocals.Values.Any(item => item.IsDefault)); @@ -81,7 +82,6 @@ private DiagnosticAnalysisResult( ProjectId = projectId; Version = version; - FromBuild = fromBuild; _syntaxLocals = syntaxLocals; _semanticLocals = semanticLocals; @@ -101,8 +101,7 @@ public static DiagnosticAnalysisResult CreateEmpty(ProjectId projectId, VersionS syntaxLocals: ImmutableDictionary>.Empty, semanticLocals: ImmutableDictionary>.Empty, nonLocals: ImmutableDictionary>.Empty, - others: [], - fromBuild: false); + others: []); } public static DiagnosticAnalysisResult CreateInitialResult(ProjectId projectId) @@ -111,47 +110,7 @@ public static DiagnosticAnalysisResult CreateInitialResult(ProjectId projectId) projectId, version: VersionStamp.Default, documentIds: null, - isEmpty: true, - fromBuild: false); - } - - public static DiagnosticAnalysisResult CreateFromBuild(Project project, ImmutableArray diagnostics, IEnumerable initialDocuments) - { - // we can't distinguish locals and non locals from build diagnostics nor determine right snapshot version for the build. - // so we put everything in as semantic local with default version. this lets us to replace those to live diagnostics when needed easily. - var version = VersionStamp.Default; - - var documentIds = ImmutableHashSet.CreateBuilder(); - documentIds.AddRange(initialDocuments); - - var diagnosticsWithDocumentId = PooledDictionary>.GetInstance(); - var diagnosticsWithoutDocumentId = ArrayBuilder.GetInstance(); - - foreach (var data in diagnostics) - { - var documentId = data.DocumentId; - if (documentId != null) - { - documentIds.Add(documentId); - diagnosticsWithDocumentId.MultiAdd(documentId, data); - } - else - { - diagnosticsWithoutDocumentId.Add(data); - } - } - - var result = new DiagnosticAnalysisResult( - project.Id, - version, - documentIds: documentIds.ToImmutable(), - syntaxLocals: ImmutableDictionary>.Empty, - semanticLocals: diagnosticsWithDocumentId.ToImmutableMultiDictionaryAndFree(), - nonLocals: ImmutableDictionary>.Empty, - others: diagnosticsWithoutDocumentId.ToImmutableAndFree(), - fromBuild: true); - - return result; + isEmpty: true); } public static DiagnosticAnalysisResult Create( @@ -174,8 +133,7 @@ public static DiagnosticAnalysisResult Create( semanticLocalMap, nonLocalMap, others, - documentIds, - fromBuild: false); + documentIds); } public static DiagnosticAnalysisResult CreateFromBuilder(DiagnosticAnalysisResultBuilder builder) @@ -261,13 +219,13 @@ public ImmutableArray GetOtherDiagnostics() => (IsAggregatedForm || IsEmpty) ? [] : _others; public DiagnosticAnalysisResult ToAggregatedForm() - => new(ProjectId, Version, DocumentIds, IsEmpty, FromBuild); + => new(ProjectId, Version, DocumentIds, IsEmpty); - public DiagnosticAnalysisResult UpdateAggregatedResult(VersionStamp version, DocumentId documentId, bool fromBuild) - => new(ProjectId, version, DocumentIdsOrEmpty.Add(documentId), isEmpty: false, fromBuild: fromBuild); + public DiagnosticAnalysisResult UpdateAggregatedResult(VersionStamp version, DocumentId documentId) + => new(ProjectId, version, DocumentIdsOrEmpty.Add(documentId), isEmpty: false); public DiagnosticAnalysisResult Reset() - => new(ProjectId, VersionStamp.Default, DocumentIds, IsEmpty, FromBuild); + => new(ProjectId, VersionStamp.Default, DocumentIds, IsEmpty); public DiagnosticAnalysisResult DropExceptSyntax() { @@ -285,8 +243,7 @@ public DiagnosticAnalysisResult DropExceptSyntax() semanticLocals: ImmutableDictionary>.Empty, nonLocals: ImmutableDictionary>.Empty, others: [], - documentIds: null, - fromBuild: false); + documentIds: null); } private static ImmutableHashSet GetDocumentIds( From fa2870561481f0d705d88865b13348a44ea88637 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 4 Feb 2025 12:04:34 -0800 Subject: [PATCH 023/305] Usings --- .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index eec0871924a2a..dbd36baef06cc 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.SolutionCrawler; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 From 16edb403508228064318cd4ca2ae05facbdd463c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 4 Feb 2025 12:19:36 -0800 Subject: [PATCH 024/305] Remove OldResult value from ProjectAnalysisData --- ...gnosticIncrementalAnalyzer.AnalysisData.cs | 194 ++++++++---------- .../DiagnosticIncrementalAnalyzer.Executor.cs | 2 +- 2 files changed, 89 insertions(+), 107 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs index 3be6f73e60c14..de338f5d4dbfb 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs @@ -10,139 +10,121 @@ using Microsoft.CodeAnalysis.Workspaces.Diagnostics; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 +namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2; + +internal partial class DiagnosticIncrementalAnalyzer { - internal partial class DiagnosticIncrementalAnalyzer + /// + /// Simple data holder for local diagnostics for an analyzer + /// + private readonly struct DocumentAnalysisData { + public static readonly DocumentAnalysisData Empty = new(VersionStamp.Default, lineCount: 0, []); + /// - /// Simple data holder for local diagnostics for an analyzer + /// Version of the diagnostic data. /// - private readonly struct DocumentAnalysisData - { - public static readonly DocumentAnalysisData Empty = new(VersionStamp.Default, lineCount: 0, []); + public readonly VersionStamp Version; - /// - /// Version of the diagnostic data. - /// - public readonly VersionStamp Version; - - /// - /// Number of lines in the document. - /// - public readonly int LineCount; + /// + /// Number of lines in the document. + /// + public readonly int LineCount; - /// - /// Current data that matches the version. - /// - public readonly ImmutableArray Items; + /// + /// Current data that matches the version. + /// + public readonly ImmutableArray Items; - /// - /// Last set of data we broadcasted to outer world, or . - /// - public readonly ImmutableArray OldItems; + /// + /// Last set of data we broadcasted to outer world, or . + /// + public readonly ImmutableArray OldItems; - public DocumentAnalysisData(VersionStamp version, int lineCount, ImmutableArray items) - { - Debug.Assert(!items.IsDefault); + public DocumentAnalysisData(VersionStamp version, int lineCount, ImmutableArray items) + { + Debug.Assert(!items.IsDefault); - Version = version; - LineCount = lineCount; - Items = items; - OldItems = default; - } + Version = version; + LineCount = lineCount; + Items = items; + OldItems = default; + } - public DocumentAnalysisData(VersionStamp version, int lineCount, ImmutableArray oldItems, ImmutableArray newItems) - : this(version, lineCount, newItems) - { - Debug.Assert(!oldItems.IsDefault); - OldItems = oldItems; - } + public DocumentAnalysisData(VersionStamp version, int lineCount, ImmutableArray oldItems, ImmutableArray newItems) + : this(version, lineCount, newItems) + { + Debug.Assert(!oldItems.IsDefault); + OldItems = oldItems; } + } + /// + /// Data holder for all diagnostics for a project for an analyzer + /// + private readonly struct ProjectAnalysisData + { /// - /// Data holder for all diagnostics for a project for an analyzer + /// ProjectId of this data /// - private readonly struct ProjectAnalysisData - { - /// - /// ProjectId of this data - /// - public readonly ProjectId ProjectId; - - /// - /// Version of the Items - /// - public readonly VersionStamp Version; - - /// - /// Current data that matches the version - /// - public readonly ImmutableDictionary Result; - - /// - /// When present, holds onto last data we broadcasted to outer world. - /// - public readonly ImmutableDictionary? OldResult; - - public ProjectAnalysisData(ProjectId projectId, VersionStamp version, ImmutableDictionary result) - { - ProjectId = projectId; - Version = version; - Result = result; + public readonly ProjectId ProjectId; - OldResult = null; - } + /// + /// Version of the Items + /// + public readonly VersionStamp Version; - public ProjectAnalysisData( - ProjectId projectId, - VersionStamp version, - ImmutableDictionary oldResult, - ImmutableDictionary newResult) - : this(projectId, version, newResult) - { - OldResult = oldResult; - } + /// + /// Current data that matches the version + /// + public readonly ImmutableDictionary Result; + + public ProjectAnalysisData(ProjectId projectId, VersionStamp version, ImmutableDictionary result) + { + ProjectId = projectId; + Version = version; + Result = result; + } + + public DiagnosticAnalysisResult GetResult(DiagnosticAnalyzer analyzer) + => GetResultOrEmpty(Result, analyzer, ProjectId, Version); - public DiagnosticAnalysisResult GetResult(DiagnosticAnalyzer analyzer) - => GetResultOrEmpty(Result, analyzer, ProjectId, Version); + public bool TryGetResult(DiagnosticAnalyzer analyzer, out DiagnosticAnalysisResult result) + => Result.TryGetValue(analyzer, out result); - public bool TryGetResult(DiagnosticAnalyzer analyzer, out DiagnosticAnalysisResult result) - => Result.TryGetValue(analyzer, out result); + public static async Task CreateAsync(Project project, IEnumerable stateSets, bool avoidLoadingData, CancellationToken cancellationToken) + { + VersionStamp? version = null; - public static async Task CreateAsync(Project project, IEnumerable stateSets, bool avoidLoadingData, CancellationToken cancellationToken) + var builder = ImmutableDictionary.CreateBuilder(); + foreach (var stateSet in stateSets) { - VersionStamp? version = null; + var state = stateSet.GetOrCreateProjectState(project.Id); + var result = await state.GetAnalysisDataAsync(project, avoidLoadingData, cancellationToken).ConfigureAwait(false); + Contract.ThrowIfFalse(project.Id == result.ProjectId); - var builder = ImmutableDictionary.CreateBuilder(); - foreach (var stateSet in stateSets) + if (!version.HasValue) { - var state = stateSet.GetOrCreateProjectState(project.Id); - var result = await state.GetAnalysisDataAsync(project, avoidLoadingData, cancellationToken).ConfigureAwait(false); - Contract.ThrowIfFalse(project.Id == result.ProjectId); - - if (!version.HasValue) - { - version = result.Version; - } - else if (version.Value != VersionStamp.Default && version.Value != result.Version) - { - // if not all version is same, set version as default. - // this can happen at the initial data loading or - // when document is closed and we put active file state to project state - version = VersionStamp.Default; - } - - builder.Add(stateSet.Analyzer, result); + version = result.Version; } - - if (!version.HasValue) + else if (version.Value != VersionStamp.Default && version.Value != result.Version) { - // there is no saved data to return. - return new ProjectAnalysisData(project.Id, VersionStamp.Default, ImmutableDictionary.Empty); + // if not all version is same, set version as default. + // this can happen at the initial data loading or + // when document is closed and we put active file state to project state + version = VersionStamp.Default; } - return new ProjectAnalysisData(project.Id, version.Value, builder.ToImmutable()); + builder.Add(stateSet.Analyzer, result); } + + if (!version.HasValue) + { + // there is no saved data to return. + return new ProjectAnalysisData(project.Id, VersionStamp.Default, ImmutableDictionary.Empty); + } + + return new ProjectAnalysisData(project.Id, version.Value, builder.ToImmutable()); } } } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs index 82f948660ab2d..4977866b130ed 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs @@ -44,7 +44,7 @@ private async Task GetProjectAnalysisDataAsync( // Now we run analyzers but filter out some information. So on such projects, there will be some perf degradation. result = await RemoveCompilerSemanticErrorsIfProjectNotLoadedAsync(result, project, cancellationToken).ConfigureAwait(false); - return new ProjectAnalysisData(project.Id, version, existingData.Result, result); + return new ProjectAnalysisData(project.Id, version, result); } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { From ce2afa775c5d39f07c1f66622c68d622a112fc21 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 4 Feb 2025 12:31:52 -0800 Subject: [PATCH 025/305] Remove dead code from diagnostics subsystem --- ...gnosticIncrementalAnalyzer.ProjectState.cs | 7 --- ...gnosticIncrementalAnalyzer.StateManager.cs | 16 ----- .../DiagnosticIncrementalAnalyzer.StateSet.cs | 63 ------------------- .../Diagnostics/DiagnosticAnalysisResult.cs | 3 - 4 files changed, 89 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index d738aa7ec02de..91ed7122d58d4 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; using Roslyn.Utilities; @@ -241,12 +240,6 @@ public async ValueTask SaveToInMemoryStorageAsync(Project project, DiagnosticAna AddToInMemoryStorage(serializerVersion, project, document: null, result.ProjectId, NonLocalStateName, result.GetOtherDiagnostics()); } - public void ResetVersion() - { - // reset version of cached data so that we can recalculate new data (ex, OnDocumentReset) - _lastResult = _lastResult.Reset(); - } - public async ValueTask MergeAsync(ActiveFileState state, TextDocument document, IGlobalOptionService globalOptions) { Contract.ThrowIfFalse(state.DocumentId == document.Id); diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs index 78e1d431cdbe2..83e5cfeece232 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs @@ -111,22 +111,6 @@ public async Task> GetOrCreateStateSetsAsync(Project pr return null; } - public bool OnProjectRemoved(IEnumerable stateSets, ProjectId projectId) - { - var removed = false; - foreach (var stateSet in stateSets) - { - removed |= stateSet.OnProjectRemoved(projectId); - } - - lock (_projectAnalyzerStateMap) - { - _projectAnalyzerStateMap = _projectAnalyzerStateMap.Remove(projectId); - } - - return removed; - } - private void RaiseProjectAnalyzerReferenceChanged(ProjectAnalyzerReferenceChangedEventArgs args) => ProjectAnalyzerReferenceChanged?.Invoke(this, args); diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs index 3a6afd3e5633a..0dc6627b39282 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs @@ -123,69 +123,6 @@ public async Task OnDocumentOpenedAsync(TextDocument document) return true; } - public async Task OnDocumentClosedAsync(TextDocument document, IGlobalOptionService globalOptions) - { - // can not be cancelled - // remove active file state and put it in project state - if (!_activeFileStates.TryRemove(document.Id, out var activeFileState)) - { - return false; - } - - // active file exist, put it in the project state - var projectState = GetOrCreateProjectState(document.Project.Id); - await projectState.MergeAsync(activeFileState, document, globalOptions).ConfigureAwait(false); - return true; - } - - public bool OnDocumentReset(TextDocument document) - { - var changed = false; - // can not be cancelled - // remove active file state and put it in project state - if (TryGetActiveFileState(document.Id, out var activeFileState)) - { - activeFileState.ResetVersion(); - changed |= true; - } - - if (TryGetProjectState(document.Project.Id, out var projectState)) - { - projectState.ResetVersion(); - changed |= true; - } - - return changed; - } - - public bool OnDocumentRemoved(DocumentId id) - { - // remove active file state for removed document - var removed = false; - if (_activeFileStates.TryRemove(id, out _)) - { - removed = true; - } - // remove state for the file that got removed. - if (_projectStates.TryGetValue(id.ProjectId, out var state)) - { - removed |= state.OnDocumentRemoved(id); - } - - return removed; - } - - public bool OnProjectRemoved(ProjectId id) - { - // remove state for project that got removed. - if (_projectStates.TryRemove(id, out var state)) - { - return state.OnProjectRemoved(id); - } - - return false; - } - public void OnRemoved() { // ths stateset is being removed. diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs index 9cd424ee9c284..7bce9cb0b93ae 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs @@ -266,9 +266,6 @@ public DiagnosticAnalysisResult ToAggregatedForm() public DiagnosticAnalysisResult UpdateAggregatedResult(VersionStamp version, DocumentId documentId, bool fromBuild) => new(ProjectId, version, DocumentIdsOrEmpty.Add(documentId), isEmpty: false, fromBuild: fromBuild); - public DiagnosticAnalysisResult Reset() - => new(ProjectId, VersionStamp.Default, DocumentIds, IsEmpty, FromBuild); - public DiagnosticAnalysisResult DropExceptSyntax() { // quick bail out From 03edf139c3f53a946b201363a26908fdc6834f75 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 4 Feb 2025 12:41:26 -0800 Subject: [PATCH 026/305] Remove unnecessary diagnostic eventing code --- ...rojectAnalyzerReferenceChangedEventArgs.cs | 33 ++++++++----------- ...ntalAnalyzer.StateManager.ProjectStates.cs | 26 +++------------ 2 files changed, 19 insertions(+), 40 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectAnalyzerReferenceChangedEventArgs.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectAnalyzerReferenceChangedEventArgs.cs index f5b259f4e6372..03ab339f2ec7d 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectAnalyzerReferenceChangedEventArgs.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectAnalyzerReferenceChangedEventArgs.cs @@ -5,28 +5,23 @@ using System; using System.Collections.Immutable; -namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 +namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2; + +internal partial class DiagnosticIncrementalAnalyzer { - internal partial class DiagnosticIncrementalAnalyzer + /// + /// EventArgs for + /// + /// this event args contains information such as the has changed + /// and what has changed. + /// + private sealed class ProjectAnalyzerReferenceChangedEventArgs : EventArgs { - /// - /// EventArgs for - /// - /// this event args contains information such as the has changed - /// and what has changed. - /// - private class ProjectAnalyzerReferenceChangedEventArgs : EventArgs - { - public readonly Project Project; - public readonly ImmutableArray Added; - public readonly ImmutableArray Removed; + public readonly ImmutableArray Removed; - public ProjectAnalyzerReferenceChangedEventArgs(Project project, ImmutableArray added, ImmutableArray removed) - { - Project = project; - Added = added; - Removed = removed; - } + public ProjectAnalyzerReferenceChangedEventArgs(ImmutableArray removed) + { + Removed = removed; } } } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs index 38990e7851c12..11bddca4858a9 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs @@ -107,8 +107,7 @@ private async Task UpdateProjectStateSetsAsync(Project if (projectStateSets == null) { projectStateSets = CreateProjectStateSets(project); - - analyzerReferenceChangedEventArgs = GetProjectAnalyzerReferenceChangedEventArgs(project, projectStateSets.Value.MapPerReferences, projectStateSets.Value.StateSetMap); + analyzerReferenceChangedEventArgs = GetProjectAnalyzerReferenceChangedEventArgs(project); // update cache. _projectAnalyzerStateMap = _projectAnalyzerStateMap.SetItem(project.Id, projectStateSets.Value); @@ -121,37 +120,22 @@ private async Task UpdateProjectStateSetsAsync(Project return projectStateSets.Value; } - private ProjectAnalyzerReferenceChangedEventArgs? GetProjectAnalyzerReferenceChangedEventArgs( - Project project, - ImmutableDictionary> newMapPerReference, - ImmutableDictionary newMap) + private ProjectAnalyzerReferenceChangedEventArgs? GetProjectAnalyzerReferenceChangedEventArgs(Project project) { // No need to use _projectAnalyzerStateMapGuard during reads of _projectAnalyzerStateMap if (!_projectAnalyzerStateMap.TryGetValue(project.Id, out var entry)) { - // no previous references and we still don't have any references - if (newMap.Count == 0) - { - return null; - } - // new reference added - return new ProjectAnalyzerReferenceChangedEventArgs(project, newMap.Values.ToImmutableArrayOrEmpty(), []); + return null; } - Debug.Assert(!entry.AnalyzerReferences.Equals(project.AnalyzerReferences)); - // there has been change. find out what has changed - var addedStates = DiffStateSets(project.AnalyzerReferences.Except(entry.AnalyzerReferences), newMapPerReference, newMap); var removedStates = DiffStateSets(entry.AnalyzerReferences.Except(project.AnalyzerReferences), entry.MapPerReferences, entry.StateSetMap); - // nothing has changed - if (addedStates.Length == 0 && removedStates.Length == 0) - { + if (removedStates.Length == 0) return null; - } - return new ProjectAnalyzerReferenceChangedEventArgs(project, addedStates, removedStates); + return new ProjectAnalyzerReferenceChangedEventArgs(removedStates); } private static ImmutableArray DiffStateSets( From 93b09f45670c41af5b4f4b0d831fd1adc9b6e942 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 4 Feb 2025 12:58:17 -0800 Subject: [PATCH 027/305] Remove more dead code --- ...gnosticIncrementalAnalyzer.ProjectState.cs | 56 ------------------- 1 file changed, 56 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index 91ed7122d58d4..e4982aec1f011 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -240,62 +240,6 @@ public async ValueTask SaveToInMemoryStorageAsync(Project project, DiagnosticAna AddToInMemoryStorage(serializerVersion, project, document: null, result.ProjectId, NonLocalStateName, result.GetOtherDiagnostics()); } - public async ValueTask MergeAsync(ActiveFileState state, TextDocument document, IGlobalOptionService globalOptions) - { - Contract.ThrowIfFalse(state.DocumentId == document.Id); - - // merge active file state to project state - var lastResult = _lastResult; - - var syntax = state.GetAnalysisData(AnalysisKind.Syntax); - var semantic = state.GetAnalysisData(AnalysisKind.Semantic); - - var project = document.Project; - - // if project didn't successfully loaded, then it is same as FSA off - var fullAnalysis = _owner.Analyzer.IsFullSolutionAnalysisEnabled(globalOptions, project.Language) && - await project.HasSuccessfullyLoadedAsync(CancellationToken.None).ConfigureAwait(false); - - // keep from build flag if full analysis is off - var fromBuild = fullAnalysis ? false : lastResult.FromBuild; - - // if it is allowed to keep project state, check versions and if they are same, bail out. - // if full solution analysis is off or we are asked to reset document state, we always merge. - if (fullAnalysis && - syntax.Version != VersionStamp.Default && - syntax.Version == semantic.Version && - syntax.Version == lastResult.Version) - { - // all data is in sync already. - return; - } - - // we have mixed versions or full analysis is off, set it to default so that it can be re-calculated next time so data can be in sync. - var version = VersionStamp.Default; - - // serialization can't be canceled. - var serializerVersion = version; - - // save active file diagnostics back to project state - AddToInMemoryStorage(serializerVersion, project, document, document.Id, SyntaxStateName, syntax.Items); - AddToInMemoryStorage(serializerVersion, project, document, document.Id, SemanticStateName, semantic.Items); - - // save last aggregated form of analysis result - _lastResult = _lastResult.UpdateAggregatedResult(version, state.DocumentId, fromBuild); - } - - public bool OnDocumentRemoved(DocumentId id) - { - RemoveInMemoryCacheEntries(id); - return !IsEmpty(id); - } - - public bool OnProjectRemoved(ProjectId id) - { - RemoveInMemoryCacheEntry(id, NonLocalStateName); - return !IsEmpty(); - } - private async Task LoadInitialAnalysisDataAsync(Project project, CancellationToken cancellationToken) { // loading data can be canceled any time. From 31af3854b0c9d3b6894d16c4715c83d9d0a5c895 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 4 Feb 2025 13:14:46 -0800 Subject: [PATCH 028/305] remove more dead code --- .../DiagnosticIncrementalAnalyzer.StateSet.cs | 45 ------------------- 1 file changed, 45 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs index 0dc6627b39282..4c6c0fc2cf745 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs @@ -38,51 +38,6 @@ public StateSet(string language, DiagnosticAnalyzer analyzer, bool isHostAnalyze _projectStates = new ConcurrentDictionary(concurrencyLevel: 2, capacity: 1); } - public IEnumerable GetProjectsWithDiagnostics() - { - // quick bail out - if (_activeFileStates.IsEmpty && _projectStates.IsEmpty) - return []; - - if (_activeFileStates.Count == 1 && _projectStates.IsEmpty) - { - // see whether we actually have diagnostics - var (documentId, state) = _activeFileStates.First(); - if (state.IsEmpty) - return []; - - // we do have diagnostics - return [documentId.ProjectId]; - } - - return new HashSet( - _activeFileStates.Where(kv => !kv.Value.IsEmpty) - .Select(kv => kv.Key.ProjectId) - .Concat(_projectStates.Where(kv => !kv.Value.IsEmpty()) - .Select(kv => kv.Key))); - } - - [PerformanceSensitive("https://github.com/dotnet/roslyn/issues/34761", AllowCaptures = false, AllowGenericEnumeration = false)] - public void CollectDocumentsWithDiagnostics(ProjectId projectId, HashSet set) - { - RoslynDebug.Assert(set != null); - - // Collect active documents with diagnostics - - foreach (var (documentId, state) in _activeFileStates) - { - if (documentId.ProjectId == projectId && !state.IsEmpty) - { - set.Add(documentId); - } - } - - if (_projectStates.TryGetValue(projectId, out var projectState) && !projectState.IsEmpty()) - { - set.UnionWith(projectState.GetDocumentsWithDiagnostics()); - } - } - public bool IsActiveFile(DocumentId documentId) => _activeFileStates.ContainsKey(documentId); From 51a72bab4e02b86a1ce007ad4982bc1353d5f737 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 4 Feb 2025 13:42:03 -0800 Subject: [PATCH 029/305] remove delete --- .../DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs index 2f48d82960e42..fea3ed2cee2df 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs @@ -68,8 +68,6 @@ private sealed class LatestDiagnosticsForSpanGetter private readonly bool _incrementalAnalysis; private readonly DiagnosticKind _diagnosticKind; - private delegate Task> DiagnosticsGetterAsync(DiagnosticAnalyzer analyzer, DocumentAnalysisExecutor executor, CancellationToken cancellationToken); - public static async Task CreateAsync( DiagnosticIncrementalAnalyzer owner, TextDocument document, From c96c6b9f4810721e7383d2f157ad6ade72f660d4 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 4 Feb 2025 13:44:41 -0800 Subject: [PATCH 030/305] Remove more checks --- ...osticIncrementalAnalyzer_GetDiagnostics.cs | 37 ++++++------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index ee5c05c28dda5..41003309f08c3 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -52,24 +52,16 @@ protected abstract Task ProduceDiagnosticsAsync( public async Task> GetDiagnosticsAsync(CancellationToken cancellationToken) { - if (ProjectId != null) - { - var project = Solution.GetProject(ProjectId); - if (project == null) - return []; - - // return diagnostics specific to one project or document - var includeProjectNonLocalResult = DocumentId == null; - return await ProduceProjectDiagnosticsAsync( - [project], project => _getDocuments(project, DocumentId), includeProjectNonLocalResult, cancellationToken).ConfigureAwait(false); - } + var project = Solution.GetProject(ProjectId); + if (project == null) + return []; - return await ProduceSolutionDiagnosticsAsync(Solution, cancellationToken).ConfigureAwait(false); + // return diagnostics specific to one project or document + var includeProjectNonLocalResult = DocumentId == null; + return await ProduceProjectDiagnosticsAsync( + [project], project => _getDocuments(project, DocumentId), includeProjectNonLocalResult, cancellationToken).ConfigureAwait(false); } - protected Task> ProduceSolutionDiagnosticsAsync(Solution solution, CancellationToken cancellationToken) - => ProduceProjectDiagnosticsAsync(solution.Projects, static project => project.DocumentIds, includeProjectNonLocalResult: true, cancellationToken); - protected async Task> ProduceProjectDiagnosticsAsync( IEnumerable projects, Func> getDocumentIds, bool includeProjectNonLocalResult, CancellationToken cancellationToken) @@ -212,17 +204,12 @@ private sealed class IdeLatestDiagnosticGetter( public async Task> GetProjectDiagnosticsAsync(CancellationToken cancellationToken) { - if (ProjectId != null) - { - var project = Solution.GetProject(ProjectId); - if (project is null) - return []; - - return await ProduceProjectDiagnosticsAsync( - [project], static _ => [], includeProjectNonLocalResult: true, cancellationToken).ConfigureAwait(false); - } + var project = Solution.GetProject(ProjectId); + if (project is null) + return []; - return await ProduceSolutionDiagnosticsAsync(Solution, cancellationToken).ConfigureAwait(false); + return await ProduceProjectDiagnosticsAsync( + [project], static _ => [], includeProjectNonLocalResult: true, cancellationToken).ConfigureAwait(false); } protected override bool ShouldIncludeDiagnostic(DiagnosticData diagnostic) From 25f65be72632bd91723c31f3030dde6a3af99ec8 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 4 Feb 2025 13:50:51 -0800 Subject: [PATCH 031/305] Remove more checks --- ...osticIncrementalAnalyzer_GetDiagnostics.cs | 49 ++++++++++--------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index 41003309f08c3..38c8a73829ad3 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -8,7 +8,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Shared.Utilities; +using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 @@ -48,7 +48,7 @@ private abstract class DiagnosticGetter( protected virtual bool ShouldIncludeDiagnostic(DiagnosticData diagnostic) => true; protected abstract Task ProduceDiagnosticsAsync( - Project project, IReadOnlyList documentIds, bool includeProjectNonLocalResult, Action callback, CancellationToken cancellationToken); + Project project, IReadOnlyList documentIds, bool includeProjectNonLocalResult, ArrayBuilder builder, CancellationToken cancellationToken); public async Task> GetDiagnosticsAsync(CancellationToken cancellationToken) { @@ -59,29 +59,27 @@ public async Task> GetDiagnosticsAsync(Cancellati // return diagnostics specific to one project or document var includeProjectNonLocalResult = DocumentId == null; return await ProduceProjectDiagnosticsAsync( - [project], project => _getDocuments(project, DocumentId), includeProjectNonLocalResult, cancellationToken).ConfigureAwait(false); + project, project => _getDocuments(project, DocumentId), includeProjectNonLocalResult, cancellationToken).ConfigureAwait(false); } protected async Task> ProduceProjectDiagnosticsAsync( - IEnumerable projects, Func> getDocumentIds, + Project project, Func> getDocumentIds, bool includeProjectNonLocalResult, CancellationToken cancellationToken) { // PERF: run projects in parallel rather than running CompilationWithAnalyzer with concurrency == true. // We do this to not get into thread starvation causing hundreds of threads to be spawned. - return await ProducerConsumer.RunParallelAsync( - source: projects, - produceItems: static (project, callback, args, cancellationToken) => args.@this.ProduceDiagnosticsAsync( - project, args.getDocumentIds(project), args.includeProjectNonLocalResult, callback, cancellationToken), - args: (@this: this, getDocumentIds, includeProjectNonLocalResult), - cancellationToken).ConfigureAwait(false); + using var _ = ArrayBuilder.GetInstance(out var builder); + await this.ProduceDiagnosticsAsync( + project, getDocumentIds(project), includeProjectNonLocalResult, builder, cancellationToken).ConfigureAwait(false); + return builder.ToImmutableAndClear(); } - protected void InvokeCallback(Action callback, ImmutableArray diagnostics) + protected void InvokeCallback(ArrayBuilder builder, ImmutableArray diagnostics) { foreach (var diagnostic in diagnostics) { if (ShouldIncludeDiagnostic(diagnostic)) - callback(diagnostic); + builder.Add(diagnostic); } } } @@ -98,7 +96,7 @@ private sealed class IdeCachedDiagnosticGetter( { protected override async Task ProduceDiagnosticsAsync( Project project, IReadOnlyList documentIds, bool includeProjectNonLocalResult, - Action callback, CancellationToken cancellationToken) + ArrayBuilder builder, CancellationToken cancellationToken) { foreach (var stateSet in StateManager.GetStateSets(project.Id)) { @@ -106,18 +104,18 @@ protected override async Task ProduceDiagnosticsAsync( { if (IncludeLocalDocumentDiagnostics) { - InvokeCallback(callback, await GetDiagnosticsAsync(stateSet, project, documentId, AnalysisKind.Syntax, cancellationToken).ConfigureAwait(false)); - InvokeCallback(callback, await GetDiagnosticsAsync(stateSet, project, documentId, AnalysisKind.Semantic, cancellationToken).ConfigureAwait(false)); + InvokeCallback(builder, await GetDiagnosticsAsync(stateSet, project, documentId, AnalysisKind.Syntax, cancellationToken).ConfigureAwait(false)); + InvokeCallback(builder, await GetDiagnosticsAsync(stateSet, project, documentId, AnalysisKind.Semantic, cancellationToken).ConfigureAwait(false)); } if (IncludeNonLocalDocumentDiagnostics) - InvokeCallback(callback, await GetDiagnosticsAsync(stateSet, project, documentId, AnalysisKind.NonLocal, cancellationToken).ConfigureAwait(false)); + InvokeCallback(builder, await GetDiagnosticsAsync(stateSet, project, documentId, AnalysisKind.NonLocal, cancellationToken).ConfigureAwait(false)); } if (includeProjectNonLocalResult) { // include project diagnostics if there is no target document - InvokeCallback(callback, await GetProjectStateDiagnosticsAsync(stateSet, project, documentId: null, AnalysisKind.NonLocal, cancellationToken).ConfigureAwait(false)); + InvokeCallback(builder, await GetProjectStateDiagnosticsAsync(stateSet, project, documentId: null, AnalysisKind.NonLocal, cancellationToken).ConfigureAwait(false)); } } } @@ -209,15 +207,18 @@ public async Task> GetProjectDiagnosticsAsync(Can return []; return await ProduceProjectDiagnosticsAsync( - [project], static _ => [], includeProjectNonLocalResult: true, cancellationToken).ConfigureAwait(false); + project, static _ => [], includeProjectNonLocalResult: true, cancellationToken).ConfigureAwait(false); } protected override bool ShouldIncludeDiagnostic(DiagnosticData diagnostic) => _diagnosticIds == null || _diagnosticIds.Contains(diagnostic.Id); protected override async Task ProduceDiagnosticsAsync( - Project project, IReadOnlyList documentIds, bool includeProjectNonLocalResult, - Action callback, CancellationToken cancellationToken) + Project project, + IReadOnlyList documentIds, + bool includeProjectNonLocalResult, + ArrayBuilder builder, + CancellationToken cancellationToken) { // get analyzers that are not suppressed. var stateSetsForProject = await StateManager.GetOrCreateStateSetsAsync(project, cancellationToken).ConfigureAwait(false); @@ -236,18 +237,18 @@ protected override async Task ProduceDiagnosticsAsync( { if (IncludeLocalDocumentDiagnostics) { - InvokeCallback(callback, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.Syntax)); - InvokeCallback(callback, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.Semantic)); + InvokeCallback(builder, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.Syntax)); + InvokeCallback(builder, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.Semantic)); } if (IncludeNonLocalDocumentDiagnostics) - InvokeCallback(callback, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.NonLocal)); + InvokeCallback(builder, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.NonLocal)); } if (includeProjectNonLocalResult) { // include project diagnostics if there is no target document - InvokeCallback(callback, analysisResult.GetOtherDiagnostics()); + InvokeCallback(builder, analysisResult.GetOtherDiagnostics()); } } } From ef2c757792efff7043b304a3d769151295417d88 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 4 Feb 2025 13:51:45 -0800 Subject: [PATCH 032/305] Inline --- .../DiagnosticIncrementalAnalyzer_GetDiagnostics.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index 38c8a73829ad3..ae326e86d029b 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -59,18 +59,18 @@ public async Task> GetDiagnosticsAsync(Cancellati // return diagnostics specific to one project or document var includeProjectNonLocalResult = DocumentId == null; return await ProduceProjectDiagnosticsAsync( - project, project => _getDocuments(project, DocumentId), includeProjectNonLocalResult, cancellationToken).ConfigureAwait(false); + project, _getDocuments(project, DocumentId), includeProjectNonLocalResult, cancellationToken).ConfigureAwait(false); } protected async Task> ProduceProjectDiagnosticsAsync( - Project project, Func> getDocumentIds, + Project project, IReadOnlyList documentIds, bool includeProjectNonLocalResult, CancellationToken cancellationToken) { // PERF: run projects in parallel rather than running CompilationWithAnalyzer with concurrency == true. // We do this to not get into thread starvation causing hundreds of threads to be spawned. using var _ = ArrayBuilder.GetInstance(out var builder); await this.ProduceDiagnosticsAsync( - project, getDocumentIds(project), includeProjectNonLocalResult, builder, cancellationToken).ConfigureAwait(false); + project, documentIds, includeProjectNonLocalResult, builder, cancellationToken).ConfigureAwait(false); return builder.ToImmutableAndClear(); } @@ -207,7 +207,7 @@ public async Task> GetProjectDiagnosticsAsync(Can return []; return await ProduceProjectDiagnosticsAsync( - project, static _ => [], includeProjectNonLocalResult: true, cancellationToken).ConfigureAwait(false); + project, documentIds: [], includeProjectNonLocalResult: true, cancellationToken).ConfigureAwait(false); } protected override bool ShouldIncludeDiagnostic(DiagnosticData diagnostic) From 91cc9dc7f024e220cd169d084bbb4f3cef13b139 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 4 Feb 2025 13:59:29 -0800 Subject: [PATCH 033/305] Fix unnecessary casts of generic types to specific types in analyzer driver (#76917) Co-authored-by: Todd Grunke --- ...river.GroupedAnalyzerActionsForAnalyzer.cs | 65 ++-- .../DiagnosticAnalyzer/AnalyzerDriver.cs | 4 +- .../DiagnosticAnalyzer/AnalyzerExecutor.cs | 359 +++++++++--------- .../DiagnosticStartAnalysisScope.cs | 16 +- 4 files changed, 239 insertions(+), 205 deletions(-) diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.GroupedAnalyzerActionsForAnalyzer.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.GroupedAnalyzerActionsForAnalyzer.cs index aa67d28c38cd7..021710ef3d925 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.GroupedAnalyzerActionsForAnalyzer.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.GroupedAnalyzerActionsForAnalyzer.cs @@ -4,8 +4,8 @@ using System.Collections.Immutable; using System.Diagnostics; -using System.Linq; using Microsoft.CodeAnalysis.Collections; +using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.Diagnostics { @@ -37,7 +37,7 @@ public GroupedAnalyzerActionsForAnalyzer(DiagnosticAnalyzer analyzer, in Analyze public AnalyzerActions AnalyzerActions { get; } [Conditional("DEBUG")] - private static void VerifyActions(in ImmutableArray actions, DiagnosticAnalyzer analyzer) + private static void VerifyActions(ArrayBuilder actions, DiagnosticAnalyzer analyzer) where TAnalyzerAction : AnalyzerAction { foreach (var action in actions) @@ -46,22 +46,33 @@ private static void VerifyActions(in ImmutableArray GetFilteredActions(in ImmutableArray actions) + private void AddFilteredActions( + ImmutableArray actions, + ArrayBuilder builder) where TAnalyzerAction : AnalyzerAction - => GetFilteredActions(actions, _analyzer, _analyzerActionsNeedFiltering); + { + AddFilteredActions(actions, _analyzer, _analyzerActionsNeedFiltering, builder); + } - private static ImmutableArray GetFilteredActions( + private static void AddFilteredActions( in ImmutableArray actions, DiagnosticAnalyzer analyzer, - bool analyzerActionsNeedFiltering) + bool analyzerActionsNeedFiltering, + ArrayBuilder builder) where TAnalyzerAction : AnalyzerAction { if (!analyzerActionsNeedFiltering) { - return actions; + builder.AddRange(actions); + } + else + { + foreach (var action in actions) + { + if (action.Analyzer == analyzer) + builder.Add(action); + } } - - return actions.WhereAsArray((action, analyzer) => action.Analyzer == analyzer, analyzer); } public ImmutableSegmentedDictionary>> NodeActionsByAnalyzerAndKind @@ -70,14 +81,15 @@ public ImmutableSegmentedDictionary(_analyzer) : - AnalyzerActions.GetSyntaxNodeActions(); + var nodeActions = ArrayBuilder>.GetInstance(); + if (_analyzerActionsNeedFiltering) + AnalyzerActions.AddSyntaxNodeActions(_analyzer, nodeActions); + else + AnalyzerActions.AddSyntaxNodeActions(nodeActions); + VerifyActions(nodeActions, _analyzer); - var analyzerActionsByKind = !nodeActions.IsEmpty ? - AnalyzerExecutor.GetNodeActionsByKind(nodeActions) : - ImmutableSegmentedDictionary>>.Empty; - RoslynImmutableInterlocked.InterlockedInitialize(ref _lazyNodeActionsByKind, analyzerActionsByKind); + RoslynImmutableInterlocked.InterlockedInitialize(ref _lazyNodeActionsByKind, AnalyzerExecutor.GetNodeActionsByKind(nodeActions)); + nodeActions.Free(); } return _lazyNodeActionsByKind; @@ -90,12 +102,11 @@ public ImmutableSegmentedDictionary.GetInstance(); + AddFilteredActions(AnalyzerActions.OperationActions, operationActions); VerifyActions(operationActions, _analyzer); - var analyzerActionsByKind = operationActions.Any() ? - AnalyzerExecutor.GetOperationActionsByKind(operationActions) : - ImmutableSegmentedDictionary>.Empty; - RoslynImmutableInterlocked.InterlockedInitialize(ref _lazyOperationActionsByKind, analyzerActionsByKind); + RoslynImmutableInterlocked.InterlockedInitialize(ref _lazyOperationActionsByKind, AnalyzerExecutor.GetOperationActionsByKind(operationActions)); + operationActions.Free(); } return _lazyOperationActionsByKind; @@ -108,9 +119,10 @@ private ImmutableArray> CodeBloc { if (_lazyCodeBlockStartActions.IsDefault) { - var codeBlockActions = GetFilteredActions(AnalyzerActions.GetCodeBlockStartActions()); + var codeBlockActions = ArrayBuilder>.GetInstance(); + AddFilteredActions(AnalyzerActions.GetCodeBlockStartActions(), codeBlockActions); VerifyActions(codeBlockActions, _analyzer); - ImmutableInterlocked.InterlockedInitialize(ref _lazyCodeBlockStartActions, codeBlockActions); + ImmutableInterlocked.InterlockedInitialize(ref _lazyCodeBlockStartActions, codeBlockActions.ToImmutableAndFree()); } return _lazyCodeBlockStartActions; @@ -144,9 +156,10 @@ private static ImmutableArray GetExecutableCodeActions( { if (lazyCodeBlockActions.IsDefault) { - codeBlockActions = GetFilteredActions(codeBlockActions, analyzer, analyzerActionsNeedFiltering); - VerifyActions(codeBlockActions, analyzer); - ImmutableInterlocked.InterlockedInitialize(ref lazyCodeBlockActions, codeBlockActions); + var finalActions = ArrayBuilder.GetInstance(); + AddFilteredActions(codeBlockActions, analyzer, analyzerActionsNeedFiltering, finalActions); + VerifyActions(finalActions, analyzer); + ImmutableInterlocked.InterlockedInitialize(ref lazyCodeBlockActions, finalActions.ToImmutableAndFree()); } return lazyCodeBlockActions; diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs index fa01b2f7f0e9f..0e8e91913e027 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs @@ -2782,7 +2782,7 @@ void executeOperationsActionsByKind(ImmutableArray operationsToAnaly } } - void executeOperationsBlockActions(ImmutableArray operationBlocksToAnalyze, ImmutableArray operationsToAnalyze, IEnumerable codeBlockActions) + void executeOperationsBlockActions(ImmutableArray operationBlocksToAnalyze, ImmutableArray operationsToAnalyze, ArrayBuilder codeBlockActions) { if (!shouldExecuteOperationBlockActions) { @@ -2810,7 +2810,7 @@ void executeOperationsBlockActions(ImmutableArray operationBlocksToA } } - void executeCodeBlockActions(ImmutableArray executableCodeBlocks, IEnumerable codeBlockActions) + void executeCodeBlockActions(ImmutableArray executableCodeBlocks, ArrayBuilder codeBlockActions) { if (executableCodeBlocks.IsEmpty || !shouldExecuteCodeBlockActions) { diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs index f21aa30a5b999..bd81e6296edee 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs @@ -722,9 +722,9 @@ private void ExecuteOperationAction( /// Execute code block actions for the given analyzer for the given declaration. /// public void ExecuteCodeBlockActions( - IEnumerable> codeBlockStartActions, - IEnumerable codeBlockActions, - IEnumerable codeBlockEndActions, + ImmutableArray> startActions, + ImmutableArray actions, + ImmutableArray endActions, DiagnosticAnalyzer analyzer, SyntaxNode declaredNode, ISymbol declaredSymbol, @@ -736,32 +736,101 @@ public void ExecuteCodeBlockActions( CancellationToken cancellationToken) where TLanguageKindEnum : struct { - ExecuteBlockActionsCore, CodeBlockAnalyzerAction, SyntaxNodeAnalyzerAction, SyntaxNode, TLanguageKindEnum>( - codeBlockStartActions, codeBlockActions, codeBlockEndActions, analyzer, - declaredNode, declaredSymbol, executableCodeBlocks, (codeBlocks) => codeBlocks.SelectMany( - cb => + Debug.Assert(!executableCodeBlocks.IsEmpty); + + // The actions we discover in 'addActions' and then execute in 'executeActions'. + var ephemeralActions = ArrayBuilder>.GetInstance(); + ExecuteBlockActionsCore( + startActions, + actions, + endActions, + analyzer, + declaredNode, + declaredSymbol, + semanticModel, + isGeneratedCode, + addActions: static (startAction, endActions, args, cancellationToken) => + { + var (@this, startActions, analyzer, declaredNode, declaredSymbol, executableCodeBlocks, semanticModel, getKind, filterSpan, isGeneratedCode, ephemeralActions) = args; + + var scope = new HostCodeBlockStartAnalysisScope(startAction.Analyzer); + var startContext = new AnalyzerCodeBlockStartAnalysisContext( + scope, declaredNode, declaredSymbol, semanticModel, @this.AnalyzerOptions, filterSpan, isGeneratedCode, cancellationToken); + + // Catch Exception from the start action. + @this.ExecuteAndCatchIfThrows( + startAction.Analyzer, + static args => args.startAction.Action(args.startContext), + argument: (startAction, startContext), + new AnalysisContextInfo(@this.Compilation, declaredSymbol, declaredNode), + cancellationToken); + + endActions.AddAll(scope.CodeBlockEndActions); + ephemeralActions.AddRange(scope.SyntaxNodeActions); + }, + executeActions: static (diagReporter, isSupportedDiagnostic, args, cancellationToken) => + { + var (@this, startActions, analyzer, declaredNode, declaredSymbol, executableCodeBlocks, semanticModel, getKind, filterSpan, isGeneratedCode, ephemeralActions) = args; + if (ephemeralActions.Any()) { - var filter = semanticModel.GetSyntaxNodesToAnalyzeFilter(cb, declaredSymbol); + var executableNodeActionsByKind = GetNodeActionsByKind(ephemeralActions); + var syntaxNodesToAnalyze = ArrayBuilder.GetInstance(); - if (filter is object) + foreach (var block in executableCodeBlocks) { - return cb.DescendantNodesAndSelf(descendIntoChildren: filter).Where(filter); - } - else - { - return cb.DescendantNodesAndSelf(); + var filter = semanticModel.GetSyntaxNodesToAnalyzeFilter(block, declaredSymbol); + if (filter is not null) + { + foreach (var descendantNode in block.DescendantNodesAndSelf(descendIntoChildren: filter)) + { + if (filter(descendantNode)) + syntaxNodesToAnalyze.Add(descendantNode); + } + } + else + { + syntaxNodesToAnalyze.AddRange(block.DescendantNodesAndSelf()); + } } - }), - semanticModel, getKind, filterSpan, isGeneratedCode, cancellationToken); + + @this.ExecuteSyntaxNodeActions( + syntaxNodesToAnalyze, executableNodeActionsByKind, + analyzer, declaredSymbol, semanticModel, getKind, diagReporter, + isSupportedDiagnostic, filterSpan, isGeneratedCode, + hasCodeBlockStartOrSymbolStartActions: startActions.Any(), + cancellationToken); + syntaxNodesToAnalyze.Free(); + } + }, + executeBlockActions: static (blockActions, diagReporter, isSupportedDiagnostic, args, cancellationToken) => + { + var (@this, startActions, analyzer, declaredNode, declaredSymbol, executableCodeBlocks, semanticModel, getKind, filterSpan, isGeneratedCode, ephemeralActions) = args; + + var context = new CodeBlockAnalysisContext(declaredNode, declaredSymbol, semanticModel, + @this.AnalyzerOptions, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, filterSpan, isGeneratedCode, cancellationToken); + + foreach (var blockAction in blockActions) + { + @this.ExecuteAndCatchIfThrows( + blockAction.Analyzer, + static data => data.blockAction.Action(data.context), + (blockAction, context), + new AnalysisContextInfo(@this.Compilation, declaredSymbol, declaredNode), + cancellationToken); + } + }, + argument: (@this: this, startActions, analyzer, declaredNode, declaredSymbol, executableCodeBlocks, semanticModel, getKind, filterSpan, isGeneratedCode, ephemeralActions), + cancellationToken); + ephemeralActions.Free(); } /// /// Execute operation block actions for the given analyzer for the given declaration. /// public void ExecuteOperationBlockActions( - IEnumerable operationBlockStartActions, - IEnumerable operationBlockActions, - IEnumerable operationBlockEndActions, + ImmutableArray startActions, + ImmutableArray actions, + ImmutableArray endActions, DiagnosticAnalyzer analyzer, SyntaxNode declaredNode, ISymbol declaredSymbol, @@ -772,36 +841,95 @@ public void ExecuteOperationBlockActions( bool isGeneratedCode, CancellationToken cancellationToken) { - ExecuteBlockActionsCore( - operationBlockStartActions, operationBlockActions, operationBlockEndActions, analyzer, - declaredNode, declaredSymbol, operationBlocks, (blocks) => operations, semanticModel, - getKind: null, filterSpan, isGeneratedCode, cancellationToken); + Debug.Assert(!operationBlocks.IsEmpty); + + // The actions we discover in 'addActions' and then execute in 'executeActions'. + var ephemeralActions = ArrayBuilder.GetInstance(); + ExecuteBlockActionsCore( + startActions, + actions, + endActions, + analyzer, + declaredNode, + declaredSymbol, + semanticModel, + isGeneratedCode, + addActions: static (startAction, endActions, args, cancellationToken) => + { + var (@this, startActions, analyzer, declaredNode, operationBlocks, declaredSymbol, operations, semanticModel, filterSpan, isGeneratedCode, ephemeralActions) = args; + var scope = new HostOperationBlockStartAnalysisScope(startAction.Analyzer); + var startContext = new AnalyzerOperationBlockStartAnalysisContext( + scope, operationBlocks, declaredSymbol, semanticModel.Compilation, @this.AnalyzerOptions, + @this.GetControlFlowGraph, declaredNode.SyntaxTree, filterSpan, isGeneratedCode, cancellationToken); + + // Catch Exception from the start action. + @this.ExecuteAndCatchIfThrows( + startAction.Analyzer, + static args => args.startAction.Action(args.startContext), + argument: (startAction, startContext), + new AnalysisContextInfo(@this.Compilation, declaredSymbol), + cancellationToken); + + endActions.AddAll(scope.OperationBlockEndActions); + ephemeralActions.AddRange(scope.OperationActions); + }, + executeActions: static (diagReporter, isSupportedDiagnostic, args, cancellationToken) => + { + var (@this, startActions, analyzer, declaredNode, operationBlocks, declaredSymbol, operations, semanticModel, filterSpan, isGeneratedCode, ephemeralActions) = args; + if (ephemeralActions.Any()) + { + @this.ExecuteOperationActions( + operations, GetOperationActionsByKind(ephemeralActions), + analyzer, declaredSymbol, semanticModel, diagReporter, + isSupportedDiagnostic, filterSpan, isGeneratedCode, + hasOperationBlockStartOrSymbolStartActions: startActions.Any(), + cancellationToken); + } + }, + executeBlockActions: static (blockActions, diagReporter, isSupportedDiagnostic, args, cancellationToken) => + { + var (@this, startActions, analyzer, declaredNode, operationBlocks, declaredSymbol, operations, semanticModel, filterSpan, isGeneratedCode, ephemeralActions) = args; + + var context = new OperationBlockAnalysisContext(operationBlocks, declaredSymbol, semanticModel.Compilation, + @this.AnalyzerOptions, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, @this.GetControlFlowGraph, declaredNode.SyntaxTree, filterSpan, isGeneratedCode, cancellationToken); + + foreach (var blockAction in blockActions) + { + @this.ExecuteAndCatchIfThrows( + blockAction.Analyzer, + static data => data.blockAction.Action(data.context), + (blockAction, context), + new AnalysisContextInfo(@this.Compilation, declaredSymbol), + cancellationToken); + } + }, + argument: (@this: this, startActions, analyzer, declaredNode, operationBlocks, declaredSymbol, operations, semanticModel, filterSpan, isGeneratedCode, ephemeralActions), + cancellationToken); + ephemeralActions.Free(); } - private void ExecuteBlockActionsCore( - IEnumerable startActions, - IEnumerable actions, - IEnumerable endActions, - DiagnosticAnalyzer analyzer, - SyntaxNode declaredNode, - ISymbol declaredSymbol, - ImmutableArray executableBlocks, - Func, IEnumerable> getNodesToAnalyze, - SemanticModel semanticModel, - Func? getKind, - TextSpan? filterSpan, - bool isGeneratedCode, - CancellationToken cancellationToken) - where TLanguageKindEnum : struct - where TBlockStartAction : AnalyzerAction - where TBlockAction : AnalyzerAction - where TNodeAction : AnalyzerAction + private void ExecuteBlockActionsCore( + ImmutableArray startActions, + ImmutableArray actions, + ImmutableArray endActions, + DiagnosticAnalyzer analyzer, + SyntaxNode declaredNode, + ISymbol declaredSymbol, + SemanticModel semanticModel, + bool isGeneratedCode, + Action, TArgs, CancellationToken> addActions, + Action, TArgs, CancellationToken> executeActions, + Action, AnalyzerDiagnosticReporter, Func, TArgs, CancellationToken> executeBlockActions, + TArgs argument, + CancellationToken cancellationToken) + where TBlockStartAction : AnalyzerAction + where TBlockAction : AnalyzerAction + where TArgs : struct { Debug.Assert(declaredNode != null); Debug.Assert(declaredSymbol != null); Debug.Assert(CanHaveExecutableCodeBlock(declaredSymbol)); Debug.Assert(startActions.Any() || endActions.Any() || actions.Any()); - Debug.Assert(!executableBlocks.IsEmpty); if (isGeneratedCode && _shouldSkipAnalysisOnGeneratedCode(analyzer) || IsAnalyzerSuppressedForTree(analyzer, declaredNode.SyntaxTree, cancellationToken)) @@ -813,10 +941,6 @@ private void ExecuteBlockActionsCore.GetInstance(); var blockActions = PooledHashSet.GetInstance(); - var executableNodeActions = ArrayBuilder.GetInstance(); - var syntaxNodeActions = executableNodeActions as ArrayBuilder>; - var operationActions = executableNodeActions as ArrayBuilder; - ImmutableArray operationBlocks = executableBlocks[0] is IOperation ? (ImmutableArray)(object)executableBlocks : ImmutableArray.Empty; // Include the code block actions. blockActions.AddAll(actions); @@ -828,46 +952,7 @@ private void ExecuteBlockActionsCore codeBlockStartAction) - { - var codeBlockEndActions = blockEndActions as PooledHashSet; - var codeBlockScope = new HostCodeBlockStartAnalysisScope(startAction.Analyzer); - var blockStartContext = new AnalyzerCodeBlockStartAnalysisContext( - codeBlockScope, declaredNode, declaredSymbol, semanticModel, AnalyzerOptions, filterSpan, isGeneratedCode, cancellationToken); - - ExecuteAndCatchIfThrows( - startAction.Analyzer, - static data => data.codeBlockStartAction.Action(data.blockStartContext), - (codeBlockStartAction, blockStartContext), - new AnalysisContextInfo(Compilation, declaredSymbol, declaredNode), - cancellationToken); - - codeBlockEndActions!.AddAll(codeBlockScope.CodeBlockEndActions); - syntaxNodeActions!.AddRange(codeBlockScope.SyntaxNodeActions); - } - else - { - if (startAction is OperationBlockStartAnalyzerAction operationBlockStartAction) - { - var operationBlockEndActions = blockEndActions as PooledHashSet; - var operationBlockScope = new HostOperationBlockStartAnalysisScope(startAction.Analyzer); - var operationStartContext = new AnalyzerOperationBlockStartAnalysisContext( - operationBlockScope, operationBlocks, declaredSymbol, semanticModel.Compilation, AnalyzerOptions, - GetControlFlowGraph, declaredNode.SyntaxTree, filterSpan, isGeneratedCode, cancellationToken); - - ExecuteAndCatchIfThrows( - startAction.Analyzer, - static data => data.operationBlockStartAction.Action(data.operationStartContext), - (operationBlockStartAction, operationStartContext), - new AnalysisContextInfo(Compilation, declaredSymbol), - cancellationToken); - - operationBlockEndActions!.AddAll(operationBlockScope.OperationBlockEndActions); - operationActions!.AddRange(operationBlockScope.OperationActions); - } - } - } + addActions(startAction, blockEndActions, argument, cancellationToken); using var _ = PooledDelegates.GetPooledFunction( static (d, ct, arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), @@ -875,92 +960,22 @@ private void ExecuteBlockActionsCore isSupportedDiagnostic); // Execute stateful executable node analyzers, if any. - if (executableNodeActions.Any()) - { - if (syntaxNodeActions != null) - { - Debug.Assert(getKind != null); + executeActions(diagReporter, isSupportedDiagnostic, argument, cancellationToken); - var executableNodeActionsByKind = GetNodeActionsByKind(syntaxNodeActions); - var syntaxNodesToAnalyze = (IEnumerable)getNodesToAnalyze(executableBlocks); - ExecuteSyntaxNodeActions(syntaxNodesToAnalyze, executableNodeActionsByKind, analyzer, declaredSymbol, semanticModel, getKind, diagReporter, isSupportedDiagnostic, filterSpan, isGeneratedCode, hasCodeBlockStartOrSymbolStartActions: startActions.Any(), cancellationToken); - } - else if (operationActions != null) - { - var operationActionsByKind = GetOperationActionsByKind(operationActions); - var operationsToAnalyze = (IEnumerable)getNodesToAnalyze(executableBlocks); - ExecuteOperationActions(operationsToAnalyze, operationActionsByKind, analyzer, declaredSymbol, semanticModel, diagReporter, isSupportedDiagnostic, filterSpan, isGeneratedCode, hasOperationBlockStartOrSymbolStartActions: startActions.Any(), cancellationToken); - } - } - - executableNodeActions.Free(); - - ExecuteBlockActions(blockActions, declaredNode, declaredSymbol, analyzer, semanticModel, operationBlocks, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, filterSpan, isGeneratedCode, cancellationToken); - ExecuteBlockActions(blockEndActions, declaredNode, declaredSymbol, analyzer, semanticModel, operationBlocks, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, filterSpan, isGeneratedCode, cancellationToken); + executeBlockActions(blockActions, diagReporter, isSupportedDiagnostic, argument, cancellationToken); + executeBlockActions(blockEndActions, diagReporter, isSupportedDiagnostic, argument, cancellationToken); diagReporter.Free(); - } - - private void ExecuteBlockActions( - PooledHashSet blockActions, - SyntaxNode declaredNode, - ISymbol declaredSymbol, - DiagnosticAnalyzer analyzer, - SemanticModel semanticModel, - ImmutableArray operationBlocks, - Action addDiagnostic, - Func isSupportedDiagnostic, - TextSpan? filterSpan, - bool isGeneratedCode, - CancellationToken cancellationToken) - where TBlockAction : AnalyzerAction - { - Debug.Assert(!isGeneratedCode || !_shouldSkipAnalysisOnGeneratedCode(analyzer)); - Debug.Assert(!IsAnalyzerSuppressedForTree(analyzer, declaredNode.SyntaxTree, cancellationToken)); - - foreach (var blockAction in blockActions) - { - var codeBlockAction = blockAction as CodeBlockAnalyzerAction; - if (codeBlockAction != null) - { - // This context is a struct, so it's fine to create a new one for each action. - var context = new CodeBlockAnalysisContext(declaredNode, declaredSymbol, semanticModel, - AnalyzerOptions, addDiagnostic, isSupportedDiagnostic, filterSpan, isGeneratedCode, cancellationToken); - - ExecuteAndCatchIfThrows( - codeBlockAction.Analyzer, - static data => data.codeBlockAction.Action(data.context), - (codeBlockAction, context), - new AnalysisContextInfo(Compilation, declaredSymbol, declaredNode), - cancellationToken); - } - else - { - var operationBlockAction = blockAction as OperationBlockAnalyzerAction; - if (operationBlockAction != null) - { - // This context is a struct, so it's fine to create a new one for each action. - var context = new OperationBlockAnalysisContext(operationBlocks, declaredSymbol, semanticModel.Compilation, - AnalyzerOptions, addDiagnostic, isSupportedDiagnostic, GetControlFlowGraph, declaredNode.SyntaxTree, filterSpan, isGeneratedCode, cancellationToken); - - ExecuteAndCatchIfThrows( - operationBlockAction.Analyzer, - static data => data.operationBlockAction.Action(data.context), - (operationBlockAction, context), - new AnalysisContextInfo(Compilation, declaredSymbol), - cancellationToken); - } - } - } - blockActions.Free(); + blockEndActions.Free(); } internal static ImmutableSegmentedDictionary>> GetNodeActionsByKind( - IEnumerable> nodeActions) + ArrayBuilder> nodeActions) where TLanguageKindEnum : struct { - Debug.Assert(nodeActions != null && nodeActions.Any()); + if (nodeActions.IsEmpty) + return ImmutableSegmentedDictionary>>.Empty; var nodeActionsByKind = PooledDictionary>>.GetInstance(); foreach (var nodeAction in nodeActions) @@ -978,7 +993,7 @@ internal static ImmutableSegmentedDictionary public void ExecuteSyntaxNodeActions( - IEnumerable nodesToAnalyze, + ArrayBuilder nodesToAnalyze, ImmutableSegmentedDictionary>> nodeActionsByKind, DiagnosticAnalyzer analyzer, SemanticModel model, @@ -1008,7 +1023,7 @@ public void ExecuteSyntaxNodeActions( } private void ExecuteSyntaxNodeActions( - IEnumerable nodesToAnalyze, + ArrayBuilder nodesToAnalyze, ImmutableSegmentedDictionary>> nodeActionsByKind, DiagnosticAnalyzer analyzer, ISymbol containingSymbol, @@ -1052,9 +1067,11 @@ private void ExecuteSyntaxNodeActions( } } - internal static ImmutableSegmentedDictionary> GetOperationActionsByKind(IEnumerable operationActions) + internal static ImmutableSegmentedDictionary> GetOperationActionsByKind( + ArrayBuilder operationActions) { - Debug.Assert(operationActions.Any()); + if (operationActions.IsEmpty) + return ImmutableSegmentedDictionary>.Empty; var operationActionsByKind = PooledDictionary>.GetInstance(); foreach (var operationAction in operationActions) @@ -1076,7 +1093,7 @@ internal static ImmutableSegmentedDictionary public void ExecuteOperationActions( - IEnumerable operationsToAnalyze, + ImmutableArray operationsToAnalyze, ImmutableSegmentedDictionary> operationActionsByKind, DiagnosticAnalyzer analyzer, SemanticModel model, @@ -1104,7 +1121,7 @@ public void ExecuteOperationActions( } private void ExecuteOperationActions( - IEnumerable operationsToAnalyze, + ImmutableArray operationsToAnalyze, ImmutableSegmentedDictionary> operationActionsByKind, DiagnosticAnalyzer analyzer, ISymbol containingSymbol, @@ -1234,7 +1251,7 @@ internal static bool HandleAnalyzerException( Func? analyzerExceptionFilter, CancellationToken cancellationToken) { - if (!ExceptionFilter(exception, analyzerExceptionFilter, cancellationToken)) + if (!exceptionFilter(exception, analyzerExceptionFilter, cancellationToken)) { return false; } @@ -1252,7 +1269,7 @@ internal static bool HandleAnalyzerException( return true; - static bool ExceptionFilter(Exception ex, Func? analyzerExceptionFilter, CancellationToken cancellationToken) + static bool exceptionFilter(Exception ex, Func? analyzerExceptionFilter, CancellationToken cancellationToken) { if ((ex as OperationCanceledException)?.CancellationToken == cancellationToken) { diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticStartAnalysisScope.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticStartAnalysisScope.cs index fb6dcccfdfd45..ba5874c3d1d0a 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticStartAnalysisScope.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticStartAnalysisScope.cs @@ -820,14 +820,20 @@ internal readonly ImmutableArray return _codeBlockStartActions.OfType>().ToImmutableArray(); } - internal readonly ImmutableArray> GetSyntaxNodeActions() where TLanguageKindEnum : struct + internal readonly void AddSyntaxNodeActions( + ArrayBuilder> builder) where TLanguageKindEnum : struct { - return _syntaxNodeActions.OfType>().ToImmutableArray(); + foreach (var action in _syntaxNodeActions) + { + if (action is SyntaxNodeAnalyzerAction stronglyTypedAction) + builder.Add(stronglyTypedAction); + } } - internal readonly ImmutableArray> GetSyntaxNodeActions(DiagnosticAnalyzer analyzer) where TLanguageKindEnum : struct + internal readonly void AddSyntaxNodeActions( + DiagnosticAnalyzer analyzer, + ArrayBuilder> builder) where TLanguageKindEnum : struct { - var builder = ArrayBuilder>.GetInstance(); foreach (var action in _syntaxNodeActions) { if (action.Analyzer == analyzer && @@ -836,8 +842,6 @@ internal readonly ImmutableArray> Ge builder.Add(syntaxNodeAction); } } - - return builder.ToImmutableAndFree(); } internal readonly ImmutableArray OperationBlockActions From 36b3603abf5d2327fdef8c0b0910d15f84922edd Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 4 Feb 2025 14:18:23 -0800 Subject: [PATCH 034/305] rename --- ...nosticIncrementalAnalyzer_GetDiagnostics.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index ae326e86d029b..bcc890af3f9f3 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -74,7 +74,7 @@ await this.ProduceDiagnosticsAsync( return builder.ToImmutableAndClear(); } - protected void InvokeCallback(ArrayBuilder builder, ImmutableArray diagnostics) + protected void AddIncludedDiagnostics(ArrayBuilder builder, ImmutableArray diagnostics) { foreach (var diagnostic in diagnostics) { @@ -104,18 +104,18 @@ protected override async Task ProduceDiagnosticsAsync( { if (IncludeLocalDocumentDiagnostics) { - InvokeCallback(builder, await GetDiagnosticsAsync(stateSet, project, documentId, AnalysisKind.Syntax, cancellationToken).ConfigureAwait(false)); - InvokeCallback(builder, await GetDiagnosticsAsync(stateSet, project, documentId, AnalysisKind.Semantic, cancellationToken).ConfigureAwait(false)); + AddIncludedDiagnostics(builder, await GetDiagnosticsAsync(stateSet, project, documentId, AnalysisKind.Syntax, cancellationToken).ConfigureAwait(false)); + AddIncludedDiagnostics(builder, await GetDiagnosticsAsync(stateSet, project, documentId, AnalysisKind.Semantic, cancellationToken).ConfigureAwait(false)); } if (IncludeNonLocalDocumentDiagnostics) - InvokeCallback(builder, await GetDiagnosticsAsync(stateSet, project, documentId, AnalysisKind.NonLocal, cancellationToken).ConfigureAwait(false)); + AddIncludedDiagnostics(builder, await GetDiagnosticsAsync(stateSet, project, documentId, AnalysisKind.NonLocal, cancellationToken).ConfigureAwait(false)); } if (includeProjectNonLocalResult) { // include project diagnostics if there is no target document - InvokeCallback(builder, await GetProjectStateDiagnosticsAsync(stateSet, project, documentId: null, AnalysisKind.NonLocal, cancellationToken).ConfigureAwait(false)); + AddIncludedDiagnostics(builder, await GetProjectStateDiagnosticsAsync(stateSet, project, documentId: null, AnalysisKind.NonLocal, cancellationToken).ConfigureAwait(false)); } } } @@ -237,18 +237,18 @@ protected override async Task ProduceDiagnosticsAsync( { if (IncludeLocalDocumentDiagnostics) { - InvokeCallback(builder, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.Syntax)); - InvokeCallback(builder, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.Semantic)); + AddIncludedDiagnostics(builder, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.Syntax)); + AddIncludedDiagnostics(builder, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.Semantic)); } if (IncludeNonLocalDocumentDiagnostics) - InvokeCallback(builder, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.NonLocal)); + AddIncludedDiagnostics(builder, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.NonLocal)); } if (includeProjectNonLocalResult) { // include project diagnostics if there is no target document - InvokeCallback(builder, analysisResult.GetOtherDiagnostics()); + AddIncludedDiagnostics(builder, analysisResult.GetOtherDiagnostics()); } } } From 1241deb0c5649446a6f517b84211e86dcf5d997d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 4 Feb 2025 14:19:03 -0800 Subject: [PATCH 035/305] remove comment --- .../EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index bcc890af3f9f3..01830f0465500 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -66,8 +66,6 @@ protected async Task> ProduceProjectDiagnosticsAs Project project, IReadOnlyList documentIds, bool includeProjectNonLocalResult, CancellationToken cancellationToken) { - // PERF: run projects in parallel rather than running CompilationWithAnalyzer with concurrency == true. - // We do this to not get into thread starvation causing hundreds of threads to be spawned. using var _ = ArrayBuilder.GetInstance(out var builder); await this.ProduceDiagnosticsAsync( project, documentIds, includeProjectNonLocalResult, builder, cancellationToken).ConfigureAwait(false); From 9467ffa068774db10899304023d4b793519e1799 Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Wed, 29 Jan 2025 18:27:49 -0800 Subject: [PATCH 036/305] Add some hacks around the fast-up-to-date checks around VSIX items This hacks around the fact that the Arcade bits we're currently picking up are older and don't correctly define the VSIX up-to-date checks needed. This overrides those targets and copies in the bits we're going to consume from the VS SDK directly. --- ...tudio.FastUpToDateCheckWorkarounds.targets | 32 +++++++++++++++++++ eng/targets/VisualStudio.targets | 4 +++ 2 files changed, 36 insertions(+) create mode 100644 eng/targets/VisualStudio.FastUpToDateCheckWorkarounds.targets diff --git a/eng/targets/VisualStudio.FastUpToDateCheckWorkarounds.targets b/eng/targets/VisualStudio.FastUpToDateCheckWorkarounds.targets new file mode 100644 index 0000000000000..c6572e1048c9b --- /dev/null +++ b/eng/targets/VisualStudio.FastUpToDateCheckWorkarounds.targets @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + $(CollectUpToDateCheckInputDesignTimeDependsOn);AddUpToDateCheckVSIXSourceItems + + + + + + + + + + + + + \ No newline at end of file diff --git a/eng/targets/VisualStudio.targets b/eng/targets/VisualStudio.targets index 64e8116ca4adf..455efd978efcc 100644 --- a/eng/targets/VisualStudio.targets +++ b/eng/targets/VisualStudio.targets @@ -147,6 +147,10 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/LanguageServer/ExternalAccess/Copilot/PublicAPI.Shipped.txt b/src/LanguageServer/ExternalAccess/Copilot/PublicAPI.Shipped.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/LanguageServer/ExternalAccess/Copilot/PublicAPI.Unshipped.txt b/src/LanguageServer/ExternalAccess/Copilot/PublicAPI.Unshipped.txt new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/src/LanguageServer/ExternalAccess/Copilot/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ + diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Microsoft.CodeAnalysis.LanguageServer.csproj b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Microsoft.CodeAnalysis.LanguageServer.csproj index a76305b4a2923..548bd594e2c19 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Microsoft.CodeAnalysis.LanguageServer.csproj +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Microsoft.CodeAnalysis.LanguageServer.csproj @@ -80,6 +80,7 @@ + diff --git a/src/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj b/src/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj index 0f49abbd7a401..6672c306da015 100644 --- a/src/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj +++ b/src/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj @@ -84,6 +84,7 @@ + From 1c3aec5760f4524b2f7c45e473952497a700688f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Feb 2025 09:27:09 -0800 Subject: [PATCH 055/305] Remove entirely unused type in diagnostic subsystem --- ...gnosticIncrementalAnalyzer.AnalysisData.cs | 65 ++----------------- 1 file changed, 7 insertions(+), 58 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs index 3fd5a8a4be200..fe6d4245de0ee 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs @@ -2,9 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; @@ -14,77 +12,28 @@ namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2; internal partial class DiagnosticIncrementalAnalyzer { - /// - /// Simple data holder for local diagnostics for an analyzer - /// - private readonly struct DocumentAnalysisData - { - public static readonly DocumentAnalysisData Empty = new(VersionStamp.Default, lineCount: 0, []); - - /// - /// Version of the diagnostic data. - /// - public readonly VersionStamp Version; - - /// - /// Number of lines in the document. - /// - public readonly int LineCount; - - /// - /// Current data that matches the version. - /// - public readonly ImmutableArray Items; - - /// - /// Last set of data we broadcasted to outer world, or . - /// - public readonly ImmutableArray OldItems; - - public DocumentAnalysisData(VersionStamp version, int lineCount, ImmutableArray items) - { - Debug.Assert(!items.IsDefault); - - Version = version; - LineCount = lineCount; - Items = items; - OldItems = default; - } - - public DocumentAnalysisData(VersionStamp version, int lineCount, ImmutableArray oldItems, ImmutableArray newItems) - : this(version, lineCount, newItems) - { - Debug.Assert(!oldItems.IsDefault); - OldItems = oldItems; - } - } - /// /// Data holder for all diagnostics for a project for an analyzer /// - private readonly struct ProjectAnalysisData + private readonly struct ProjectAnalysisData( + ProjectId projectId, + VersionStamp version, + ImmutableDictionary result) { /// /// ProjectId of this data /// - public readonly ProjectId ProjectId; + public readonly ProjectId ProjectId = projectId; /// /// Version of the Items /// - public readonly VersionStamp Version; + public readonly VersionStamp Version = version; /// /// Current data that matches the version /// - public readonly ImmutableDictionary Result; - - public ProjectAnalysisData(ProjectId projectId, VersionStamp version, ImmutableDictionary result) - { - ProjectId = projectId; - Version = version; - Result = result; - } + public readonly ImmutableDictionary Result = result; public DiagnosticAnalysisResult GetResult(DiagnosticAnalyzer analyzer) => GetResultOrEmpty(Result, analyzer, ProjectId, Version); From 3810e2e5011de1bcc60e6c305a9fd70867f1e444 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Feb 2025 09:31:23 -0800 Subject: [PATCH 056/305] Remove unused methods --- .../EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index 26b1624548a88..84bd0eacdc28d 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -42,12 +42,6 @@ public ProjectState(StateSet owner, ProjectId projectId) public ImmutableHashSet GetDocumentsWithDiagnostics() => _lastResult.DocumentIdsOrEmpty; - public bool IsEmpty() - => _lastResult.IsEmpty; - - public bool IsEmpty(DocumentId documentId) - => IsEmpty(_lastResult, documentId); - /// /// Return all diagnostics for the given project stored in this state /// From 8e12b3139290e857d01e613f6be6202cfdbfaa14 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Feb 2025 09:31:37 -0800 Subject: [PATCH 057/305] Remove unused methods --- .../EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index 84bd0eacdc28d..19a572315f6f7 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -39,9 +39,6 @@ public ProjectState(StateSet owner, ProjectId projectId) _lastResult = DiagnosticAnalysisResult.CreateInitialResult(projectId); } - public ImmutableHashSet GetDocumentsWithDiagnostics() - => _lastResult.DocumentIdsOrEmpty; - /// /// Return all diagnostics for the given project stored in this state /// From 7f68865c877c129ffee645facc2878a8ccd7e7e2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Feb 2025 09:35:02 -0800 Subject: [PATCH 058/305] Remove unused methods --- .../Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs index e3b90823013d6..b049f0413be31 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs @@ -221,9 +221,6 @@ public ImmutableArray GetOtherDiagnostics() public DiagnosticAnalysisResult ToAggregatedForm() => new(ProjectId, Version, DocumentIds, IsEmpty); - public DiagnosticAnalysisResult UpdateAggregatedResult(VersionStamp version, DocumentId documentId) - => new(ProjectId, version, DocumentIdsOrEmpty.Add(documentId), isEmpty: false); - public DiagnosticAnalysisResult DropExceptSyntax() { // quick bail out From 950d6072f9a8fe7243c3930a177982944dd11e98 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Feb 2025 09:40:18 -0800 Subject: [PATCH 059/305] Switch to strongly typed keys in diagnostics subsystem --- ...sticIncrementalAnalyzer.InMemoryStorage.cs | 23 ++++--------- ...gnosticIncrementalAnalyzer.ProjectState.cs | 34 ++++++++----------- .../DiagnosticAnalyzer/DiagnosticComputer.cs | 1 + 3 files changed, 23 insertions(+), 35 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InMemoryStorage.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InMemoryStorage.cs index ca96681acd08d..fbd4ad6e013cb 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InMemoryStorage.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InMemoryStorage.cs @@ -4,6 +4,7 @@ using System.Collections.Concurrent; using System.Collections.Immutable; +using Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 @@ -13,10 +14,10 @@ internal partial class DiagnosticIncrementalAnalyzer private static class InMemoryStorage { // the reason using nested map rather than having tuple as key is so that I dont have a gigantic map - private static readonly ConcurrentDictionary> s_map = + private static readonly ConcurrentDictionary> s_map = new(concurrencyLevel: 2, capacity: 10); - public static bool TryGetValue(DiagnosticAnalyzer analyzer, (object key, string stateKey) key, out CacheEntry entry) + public static bool TryGetValue(DiagnosticAnalyzer analyzer, (ProjectOrDocumentId key, string stateKey) key, out CacheEntry entry) { AssertKey(key); @@ -25,16 +26,16 @@ public static bool TryGetValue(DiagnosticAnalyzer analyzer, (object key, string analyzerMap.TryGetValue(key, out entry); } - public static void Cache(DiagnosticAnalyzer analyzer, (object key, string stateKey) key, CacheEntry entry) + public static void Cache(DiagnosticAnalyzer analyzer, (ProjectOrDocumentId key, string stateKey) key, CacheEntry entry) { AssertKey(key); // add new cache entry - var analyzerMap = s_map.GetOrAdd(analyzer, _ => new ConcurrentDictionary<(object key, string stateKey), CacheEntry>(concurrencyLevel: 2, capacity: 10)); + var analyzerMap = s_map.GetOrAdd(analyzer, _ => new ConcurrentDictionary<(ProjectOrDocumentId key, string stateKey), CacheEntry>(concurrencyLevel: 2, capacity: 10)); analyzerMap[key] = entry; } - public static void Remove(DiagnosticAnalyzer analyzer, (object key, string stateKey) key) + public static void Remove(DiagnosticAnalyzer analyzer, (ProjectOrDocumentId key, string stateKey) key) { AssertKey(key); // remove the entry @@ -63,16 +64,6 @@ private static void AssertKey((object key, string stateKey) key) } // in memory cache entry - private readonly struct CacheEntry - { - public readonly VersionStamp Version; - public readonly ImmutableArray Diagnostics; - - public CacheEntry(VersionStamp version, ImmutableArray diagnostics) - { - Version = version; - Diagnostics = diagnostics; - } - } + private readonly record struct CacheEntry(VersionStamp Version, ImmutableArray Diagnostics); } } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index 26b1624548a88..9426acc708ac8 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -226,12 +227,12 @@ public async ValueTask SaveToInMemoryStorageAsync(Project project, DiagnosticAna continue; } - AddToInMemoryStorage(serializerVersion, project, document, document.Id, SyntaxStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Syntax)); - AddToInMemoryStorage(serializerVersion, project, document, document.Id, SemanticStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Semantic)); - AddToInMemoryStorage(serializerVersion, project, document, document.Id, NonLocalStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.NonLocal)); + AddToInMemoryStorage(serializerVersion, new(document.Id), SyntaxStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Syntax)); + AddToInMemoryStorage(serializerVersion, new(document.Id), SemanticStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Semantic)); + AddToInMemoryStorage(serializerVersion, new(document.Id), NonLocalStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.NonLocal)); } - AddToInMemoryStorage(serializerVersion, project, document: null, result.ProjectId, NonLocalStateName, result.GetOtherDiagnostics()); + AddToInMemoryStorage(serializerVersion, new(result.ProjectId), NonLocalStateName, result.GetOtherDiagnostics()); } private async Task LoadInitialAnalysisDataAsync(Project project, CancellationToken cancellationToken) @@ -286,11 +287,8 @@ private async Task LoadInitialProjectAnalysisDataAsync } private void AddToInMemoryStorage( - VersionStamp serializerVersion, Project project, TextDocument? document, object key, string stateKey, ImmutableArray diagnostics) + VersionStamp serializerVersion, ProjectOrDocumentId key, string stateKey, ImmutableArray diagnostics) { - Contract.ThrowIfFalse(document == null || document.Project == project); - - // if serialization fail, hold it in the memory InMemoryStorage.Cache(_owner.Analyzer, (key, stateKey), new CacheEntry(serializerVersion, diagnostics)); } @@ -300,7 +298,7 @@ private async ValueTask TryGetDiagnosticsFromInMemoryStorageAsync(VersionS var project = document.Project; var documentId = document.Id; - var diagnostics = await GetDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, document, documentId, SyntaxStateName, cancellationToken).ConfigureAwait(false); + var diagnostics = await GetDiagnosticsFromInMemoryStorageAsync(serializerVersion, new(documentId), SyntaxStateName, cancellationToken).ConfigureAwait(false); if (!diagnostics.IsDefault) { builder.AddSyntaxLocals(documentId, diagnostics); @@ -310,7 +308,7 @@ private async ValueTask TryGetDiagnosticsFromInMemoryStorageAsync(VersionS success = false; } - diagnostics = await GetDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, document, documentId, SemanticStateName, cancellationToken).ConfigureAwait(false); + diagnostics = await GetDiagnosticsFromInMemoryStorageAsync(serializerVersion, new(documentId), SemanticStateName, cancellationToken).ConfigureAwait(false); if (!diagnostics.IsDefault) { builder.AddSemanticLocals(documentId, diagnostics); @@ -320,7 +318,7 @@ private async ValueTask TryGetDiagnosticsFromInMemoryStorageAsync(VersionS success = false; } - diagnostics = await GetDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, document, documentId, NonLocalStateName, cancellationToken).ConfigureAwait(false); + diagnostics = await GetDiagnosticsFromInMemoryStorageAsync(serializerVersion, new(documentId), NonLocalStateName, cancellationToken).ConfigureAwait(false); if (!diagnostics.IsDefault) { builder.AddNonLocals(documentId, diagnostics); @@ -335,7 +333,7 @@ private async ValueTask TryGetDiagnosticsFromInMemoryStorageAsync(VersionS private async ValueTask TryGetProjectDiagnosticsFromInMemoryStorageAsync(VersionStamp serializerVersion, Project project, Builder builder, CancellationToken cancellationToken) { - var diagnostics = await GetDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, document: null, project.Id, NonLocalStateName, cancellationToken).ConfigureAwait(false); + var diagnostics = await GetDiagnosticsFromInMemoryStorageAsync(serializerVersion, new(project.Id), NonLocalStateName, cancellationToken).ConfigureAwait(false); if (!diagnostics.IsDefault) { builder.AddOthers(diagnostics); @@ -346,10 +344,8 @@ private async ValueTask TryGetProjectDiagnosticsFromInMemoryStorageAsync(V } private ValueTask> GetDiagnosticsFromInMemoryStorageAsync( - VersionStamp serializerVersion, Project project, TextDocument? document, object key, string stateKey, CancellationToken _) + VersionStamp serializerVersion, ProjectOrDocumentId key, string stateKey, CancellationToken _) { - Contract.ThrowIfFalse(document == null || document.Project == project); - return InMemoryStorage.TryGetValue(_owner.Analyzer, (key, stateKey), out var entry) && serializerVersion == entry.Version ? new(entry.Diagnostics) : default; @@ -366,12 +362,12 @@ private void RemoveInMemoryCache(DiagnosticAnalysisResult lastResult) private void RemoveInMemoryCacheEntries(DocumentId id) { - RemoveInMemoryCacheEntry(id, SyntaxStateName); - RemoveInMemoryCacheEntry(id, SemanticStateName); - RemoveInMemoryCacheEntry(id, NonLocalStateName); + RemoveInMemoryCacheEntry(new(id), SyntaxStateName); + RemoveInMemoryCacheEntry(new(id), SemanticStateName); + RemoveInMemoryCacheEntry(new(id), NonLocalStateName); } - private void RemoveInMemoryCacheEntry(object key, string stateKey) + private void RemoveInMemoryCacheEntry(ProjectOrDocumentId key, string stateKey) { // remove in memory cache if entry exist InMemoryStorage.Remove(_owner.Analyzer, (key, stateKey)); diff --git a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs index d8b597c517736..45f6e5bc6314f 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs @@ -576,6 +576,7 @@ private async Task CreateCompilationWithAnal { hostAnalyzerBuilder.AddRange(analyzers); } + analyzerMapBuilder.AppendAnalyzerMap(analyzers); } From d1762680c6f67e32991e7320b4c24b00c819cbc9 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Feb 2025 09:47:24 -0800 Subject: [PATCH 060/305] Remove unnecessary async from diagnostics subsystem --- ...gnosticIncrementalAnalyzer.ProjectState.cs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index 9426acc708ac8..f96154fd1b3d1 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -91,7 +91,7 @@ public async Task GetAnalysisDataAsync(Project project if (document == null) continue; - if (!await TryGetDiagnosticsFromInMemoryStorageAsync(serializerVersion, document, builder, cancellationToken).ConfigureAwait(false)) + if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder, cancellationToken)) { Debug.Assert(lastResult.Version == VersionStamp.Default); @@ -101,7 +101,7 @@ public async Task GetAnalysisDataAsync(Project project } } - if (!await TryGetProjectDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, builder, cancellationToken).ConfigureAwait(false)) + if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder, cancellationToken)) { // this can happen if SaveAsync is not yet called but active file merge happened. one of case is if user did build before the very first // analysis happened. @@ -136,7 +136,7 @@ public async Task GetAnalysisDataAsync(TextDocument do var serializerVersion = lastResult.Version; var builder = new Builder(document.Project, lastResult.Version); - if (!await TryGetDiagnosticsFromInMemoryStorageAsync(serializerVersion, document, builder, cancellationToken).ConfigureAwait(false)) + if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder, cancellationToken)) { Debug.Assert(lastResult.Version == VersionStamp.Default); @@ -177,7 +177,7 @@ public async Task GetProjectAnalysisDataAsync(Project var serializerVersion = lastResult.Version; var builder = new Builder(project, lastResult.Version); - if (!await TryGetProjectDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, builder, cancellationToken).ConfigureAwait(false)) + if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder, cancellationToken)) { // this can happen if SaveAsync is not yet called but active file merge happened. one of case is if user did build before the very first // analysis happened. @@ -246,11 +246,11 @@ private async Task LoadInitialAnalysisDataAsync(Projec { cancellationToken.ThrowIfCancellationRequested(); - if (!await TryGetDiagnosticsFromInMemoryStorageAsync(serializerVersion, document, builder, cancellationToken).ConfigureAwait(false)) + if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder, cancellationToken)) continue; } - if (!await TryGetProjectDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, builder, cancellationToken).ConfigureAwait(false)) + if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder, cancellationToken)) return DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default); return builder.ToResult(); @@ -265,7 +265,7 @@ private async Task LoadInitialAnalysisDataAsync(TextDo var serializerVersion = version; var builder = new Builder(project, version); - if (!await TryGetDiagnosticsFromInMemoryStorageAsync(serializerVersion, document, builder, cancellationToken).ConfigureAwait(false)) + if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder, cancellationToken)) { return DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default); } @@ -280,7 +280,7 @@ private async Task LoadInitialProjectAnalysisDataAsync var serializerVersion = version; var builder = new Builder(project, version); - if (!await TryGetProjectDiagnosticsFromInMemoryStorageAsync(serializerVersion, project, builder, cancellationToken).ConfigureAwait(false)) + if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder, cancellationToken)) return DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default); return builder.ToResult(); @@ -292,13 +292,13 @@ private void AddToInMemoryStorage( InMemoryStorage.Cache(_owner.Analyzer, (key, stateKey), new CacheEntry(serializerVersion, diagnostics)); } - private async ValueTask TryGetDiagnosticsFromInMemoryStorageAsync(VersionStamp serializerVersion, TextDocument document, Builder builder, CancellationToken cancellationToken) + private bool TryGetDiagnosticsFromInMemoryStorage(VersionStamp serializerVersion, TextDocument document, Builder builder, CancellationToken cancellationToken) { var success = true; var project = document.Project; var documentId = document.Id; - var diagnostics = await GetDiagnosticsFromInMemoryStorageAsync(serializerVersion, new(documentId), SyntaxStateName, cancellationToken).ConfigureAwait(false); + var diagnostics = GetDiagnosticsFromInMemoryStorage(serializerVersion, new(documentId), SyntaxStateName, cancellationToken); if (!diagnostics.IsDefault) { builder.AddSyntaxLocals(documentId, diagnostics); @@ -308,7 +308,7 @@ private async ValueTask TryGetDiagnosticsFromInMemoryStorageAsync(VersionS success = false; } - diagnostics = await GetDiagnosticsFromInMemoryStorageAsync(serializerVersion, new(documentId), SemanticStateName, cancellationToken).ConfigureAwait(false); + diagnostics = GetDiagnosticsFromInMemoryStorage(serializerVersion, new(documentId), SemanticStateName, cancellationToken); if (!diagnostics.IsDefault) { builder.AddSemanticLocals(documentId, diagnostics); @@ -318,7 +318,7 @@ private async ValueTask TryGetDiagnosticsFromInMemoryStorageAsync(VersionS success = false; } - diagnostics = await GetDiagnosticsFromInMemoryStorageAsync(serializerVersion, new(documentId), NonLocalStateName, cancellationToken).ConfigureAwait(false); + diagnostics = GetDiagnosticsFromInMemoryStorage(serializerVersion, new(documentId), NonLocalStateName, cancellationToken); if (!diagnostics.IsDefault) { builder.AddNonLocals(documentId, diagnostics); @@ -331,9 +331,9 @@ private async ValueTask TryGetDiagnosticsFromInMemoryStorageAsync(VersionS return success; } - private async ValueTask TryGetProjectDiagnosticsFromInMemoryStorageAsync(VersionStamp serializerVersion, Project project, Builder builder, CancellationToken cancellationToken) + private bool TryGetProjectDiagnosticsFromInMemoryStorage(VersionStamp serializerVersion, Project project, Builder builder, CancellationToken cancellationToken) { - var diagnostics = await GetDiagnosticsFromInMemoryStorageAsync(serializerVersion, new(project.Id), NonLocalStateName, cancellationToken).ConfigureAwait(false); + var diagnostics = GetDiagnosticsFromInMemoryStorage(serializerVersion, new(project.Id), NonLocalStateName, cancellationToken); if (!diagnostics.IsDefault) { builder.AddOthers(diagnostics); @@ -343,11 +343,11 @@ private async ValueTask TryGetProjectDiagnosticsFromInMemoryStorageAsync(V return false; } - private ValueTask> GetDiagnosticsFromInMemoryStorageAsync( + private ImmutableArray GetDiagnosticsFromInMemoryStorage( VersionStamp serializerVersion, ProjectOrDocumentId key, string stateKey, CancellationToken _) { return InMemoryStorage.TryGetValue(_owner.Analyzer, (key, stateKey), out var entry) && serializerVersion == entry.Version - ? new(entry.Diagnostics) + ? entry.Diagnostics : default; } From 0ba72bdb989a774d81ab6d108aec4731c9a92341 Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Wed, 5 Feb 2025 10:06:26 -0800 Subject: [PATCH 061/305] Realize less of the syntax tree during AbstractSemanticModelReuseLanguageService.GetPreviousBodyNode (#77032) Realize less of the syntax tree during AbstractSemanticModelReuseLanguageService.GetPreviousBodyNode During the typing scenario in the scrolling speedometer test, about 13% of allocations are under GetPreviousBodyNode, due to realizing a bunch of nodes in the syntax tree to attempt to map from a method node in one tree to the corresponding node in similarly structured tree. Instead, as we know the trees have similar structure, just walk up the ancestors in the current tree to generate a node index path, and apply that path to the previous tree to find the appropriate method node. --- ...stractSemanticModelReuseLanguageService.cs | 57 +++++++++++++++---- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/src/Workspaces/Core/Portable/SemanticModelReuse/AbstractSemanticModelReuseLanguageService.cs b/src/Workspaces/Core/Portable/SemanticModelReuse/AbstractSemanticModelReuseLanguageService.cs index 0d4712b4ce423..58b31221b2e31 100644 --- a/src/Workspaces/Core/Portable/SemanticModelReuse/AbstractSemanticModelReuseLanguageService.cs +++ b/src/Workspaces/Core/Portable/SemanticModelReuse/AbstractSemanticModelReuseLanguageService.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.SemanticModelReuse; @@ -106,26 +107,58 @@ protected SyntaxNode GetPreviousBodyNode(SyntaxNode previousRoot, SyntaxNode cur } else { - using var pooledCurrentMembers = this.SyntaxFacts.GetMethodLevelMembers(currentRoot); - var currentMembers = pooledCurrentMembers.Object; + // Walk up the ancestor nodes of currentBodyNode, finding child indexes up to the root. + using var _ = ArrayBuilder.GetInstance(out var indexPath); + GetNodeChildIndexPathToRootReversed(currentBodyNode, indexPath); - var index = currentMembers.IndexOf(currentBodyNode); - if (index < 0) + // Then use those indexes to walk back down the previous tree to find the equivalent node. + var previousNode = previousRoot; + for (var i = indexPath.Count - 1; i >= 0; i--) { - Debug.Fail($"Unhandled member type in {nameof(GetPreviousBodyNode)}"); - return null; + var childIndex = indexPath[i]; + var children = previousNode.ChildNodesAndTokens(); + + if (children.Count <= childIndex) + { + Debug.Fail("Member count shouldn't have changed as there were no top level edits."); + return null; + } + + var childAsNode = children[childIndex].AsNode(); + if (childAsNode is null) + { + Debug.Fail("Child at indicated index should be a node as there were no top level edits."); + return null; + } + + previousNode = childAsNode; } - using var pooledPreviousMembers = this.SyntaxFacts.GetMethodLevelMembers(previousRoot); - var previousMembers = pooledPreviousMembers.Object; + return previousNode; + } + } + + private static void GetNodeChildIndexPathToRootReversed(SyntaxNode node, ArrayBuilder path) + { + var current = node; + var parent = current.Parent; - if (currentMembers.Count != previousMembers.Count) + while (parent != null) + { + var childIndex = 0; + foreach (var child in parent.ChildNodesAndTokens()) { - Debug.Fail("Member count shouldn't have changed as there were no top level edits."); - return null; + if (child.AsNode() == current) + { + path.Add(childIndex); + break; + } + + childIndex++; } - return previousMembers[index]; + current = parent; + parent = current.Parent; } } From 7ca7de33915aba1272e1a93751bf1450d04afe57 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Feb 2025 11:35:20 -0800 Subject: [PATCH 062/305] Allow more results than just the original file in nav to searches --- ...actNavigateToSearchService.NormalSearch.cs | 67 ++++++++++++++++--- .../IRemoteNavigateToSearchService.cs | 2 +- .../Services/SyntaxFacts/CSharpSyntaxFacts.cs | 33 +-------- .../Core/CompilerExtensions.projitems | 1 + .../SyntaxFacts/AbstractSyntaxFacts.cs | 21 ++++++ .../Core/Services/SyntaxFacts/ISyntaxFacts.cs | 8 ++- .../SyntaxFacts/VisualBasicSyntaxFacts.vb | 21 +----- 7 files changed, 93 insertions(+), 60 deletions(-) create mode 100644 src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/AbstractSyntaxFacts.cs diff --git a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs index 0cdad427b8d35..f9733ec8a0889 100644 --- a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs +++ b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs @@ -8,8 +8,11 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.PatternMatching; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; @@ -35,16 +38,16 @@ public async Task SearchDocumentAsync( await client.TryInvokeAsync( document.Project, (service, solutionInfo, callbackId, cancellationToken) => - service.SearchDocumentAsync(solutionInfo, document.Id, searchPattern, [.. kinds], callbackId, cancellationToken), + service.SearchDocumentAndRelatedDocumentsAsync(solutionInfo, document.Id, searchPattern, [.. kinds], callbackId, cancellationToken), callback, cancellationToken).ConfigureAwait(false); return; } - await SearchDocumentInCurrentProcessAsync(document, searchPattern, kinds, onItemsFound, cancellationToken).ConfigureAwait(false); + await SearchDocumentAndRelatedDocumentsInCurrentProcessAsync(document, searchPattern, kinds, onItemsFound, cancellationToken).ConfigureAwait(false); } - public static async Task SearchDocumentInCurrentProcessAsync( + public static Task SearchDocumentAndRelatedDocumentsInCurrentProcessAsync( Document document, string searchPattern, IImmutableSet kinds, @@ -54,12 +57,60 @@ public static async Task SearchDocumentInCurrentProcessAsync( var (patternName, patternContainerOpt) = PatternMatcher.GetNameAndContainer(searchPattern); var declaredSymbolInfoKindsSet = new DeclaredSymbolInfoKindSet(kinds); - var results = new ConcurrentSet(); - await SearchSingleDocumentAsync( - document, patternName, patternContainerOpt, declaredSymbolInfoKindsSet, t => results.Add(t), cancellationToken).ConfigureAwait(false); + // In parallel, search both the document requested, and any relevant 'related documents' we find for it. - if (results.Count > 0) - await onItemsFound([.. results], default, cancellationToken).ConfigureAwait(false); + return Task.WhenAll( + SearchDocumentsInCurrentProcessAsync([document]), + SearchRelatedDocumentsInCurrentProcessAsync()); + + Task SearchDocumentsInCurrentProcessAsync(ImmutableArray documents) + => ProducerConsumer.RunParallelAsync( + documents, + async (document, onItemFound, args, cancellationToken) => await SearchSingleDocumentAsync( + document, patternName, patternContainerOpt, declaredSymbolInfoKindsSet, onItemFound, cancellationToken).ConfigureAwait(false), + onItemsFound, + args: default, + cancellationToken); + + async Task SearchRelatedDocumentsInCurrentProcessAsync() + { + var relatedDocuments = await GetRelatedDocumentsAsync().ConfigureAwait(false); + await SearchDocumentsInCurrentProcessAsync(relatedDocuments).ConfigureAwait(false); + } + + async Task> GetRelatedDocumentsAsync() + { + // For C#/VB we define 'related documents' as those containing types in the inheritance chain of types in + // the originating file (as well as all partial parts of the original and inheritance types). This way a + // user can search for symbols scoped to the 'current document' and still get results for the members found + // in partial parts + + var solution = document.Project.Solution; + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var semanticModel = await document.GetRequiredNullableDisabledSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var syntaxFacts = document.GetRequiredLanguageService(); + + using var _1 = ArrayBuilder.GetInstance(out var topLevelNodes); + using var _2 = PooledHashSet.GetInstance(out var relatedDocuments); + + syntaxFacts.AddTopLevelMembers(root, topLevelNodes); + + foreach (var topLevelMember in topLevelNodes) + { + if (semanticModel.GetDeclaredSymbol(topLevelMember, cancellationToken) is not INamedTypeSymbol namedTypeSymbol) + continue; + + foreach (var type in namedTypeSymbol.GetBaseTypesAndThis()) + { + foreach (var reference in type.DeclaringSyntaxReferences) + relatedDocuments.AddIfNotNull(solution.GetDocument(reference.SyntaxTree)); + } + } + + // Ensure we don't search the original document we were already searching. + relatedDocuments.Remove(document); + return [.. relatedDocuments]; + } } public async Task SearchProjectsAsync( diff --git a/src/Features/Core/Portable/NavigateTo/IRemoteNavigateToSearchService.cs b/src/Features/Core/Portable/NavigateTo/IRemoteNavigateToSearchService.cs index e50aa7fad8794..ef31f92f7c540 100644 --- a/src/Features/Core/Portable/NavigateTo/IRemoteNavigateToSearchService.cs +++ b/src/Features/Core/Portable/NavigateTo/IRemoteNavigateToSearchService.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.NavigateTo; internal interface IRemoteNavigateToSearchService { - ValueTask SearchDocumentAsync(Checksum solutionChecksum, DocumentId documentId, string searchPattern, ImmutableArray kinds, RemoteServiceCallbackId callbackId, CancellationToken cancellationToken); + ValueTask SearchDocumentAndRelatedDocumentsAsync(Checksum solutionChecksum, DocumentId documentId, string searchPattern, ImmutableArray kinds, RemoteServiceCallbackId callbackId, CancellationToken cancellationToken); ValueTask SearchProjectsAsync(Checksum solutionChecksum, ImmutableArray projectIds, ImmutableArray priorityDocumentIds, string searchPattern, ImmutableArray kinds, RemoteServiceCallbackId callbackId, CancellationToken cancellationToken); ValueTask SearchGeneratedDocumentsAsync(Checksum solutionChecksum, ImmutableArray projectIds, string searchPattern, ImmutableArray kinds, RemoteServiceCallbackId callbackId, CancellationToken cancellationToken); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs index 360493f97d2ec..9cbc626969729 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs @@ -23,13 +23,10 @@ namespace Microsoft.CodeAnalysis.CSharp.LanguageService; -internal class CSharpSyntaxFacts : ISyntaxFacts +internal class CSharpSyntaxFacts : AbstractSyntaxFacts, ISyntaxFacts { internal static readonly CSharpSyntaxFacts Instance = new(); - // Specifies false for trimOnFree as these objects commonly exceed the default ObjectPool threshold - private static readonly ObjectPool> s_syntaxNodeListPool = new ObjectPool>(() => [], trimOnFree: false); - protected CSharpSyntaxFacts() { } @@ -732,11 +729,7 @@ EnumMemberDeclarationSyntax or } public bool IsTopLevelNodeWithMembers([NotNullWhen(true)] SyntaxNode? node) - { - return node is BaseNamespaceDeclarationSyntax or - TypeDeclarationSyntax or - EnumDeclarationSyntax; - } + => node is BaseNamespaceDeclarationSyntax or BaseTypeDeclarationSyntax; private const string dotToken = "."; @@ -889,30 +882,10 @@ private static void AppendTypeParameterList(StringBuilder builder, TypeParameter } } - public PooledObject> GetTopLevelAndMethodLevelMembers(SyntaxNode? root) - { - var pooledObject = s_syntaxNodeListPool.GetPooledObject(); - var list = pooledObject.Object; - - AppendMembers(root, list, topLevel: true, methodLevel: true); - - return pooledObject; - } - - public PooledObject> GetMethodLevelMembers(SyntaxNode? root) - { - var pooledObject = s_syntaxNodeListPool.GetPooledObject(); - var list = pooledObject.Object; - - AppendMembers(root, list, topLevel: false, methodLevel: true); - - return pooledObject; - } - public SyntaxList GetMembersOfTypeDeclaration(SyntaxNode typeDeclaration) => ((TypeDeclarationSyntax)typeDeclaration).Members; - private void AppendMembers(SyntaxNode? node, List list, bool topLevel, bool methodLevel) + protected override void AppendMembers(SyntaxNode? node, ArrayBuilder list, bool topLevel, bool methodLevel) { Debug.Assert(topLevel || methodLevel); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems index 56d65226cc0ea..f06b5b75653bd 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems @@ -436,6 +436,7 @@ + diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/AbstractSyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/AbstractSyntaxFacts.cs new file mode 100644 index 0000000000000..4b5bbc83d00ee --- /dev/null +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/AbstractSyntaxFacts.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.PooledObjects; + +namespace Microsoft.CodeAnalysis.LanguageService; + +internal abstract class AbstractSyntaxFacts +{ + public void AddTopLevelAndMethodLevelMembers(SyntaxNode? root, ArrayBuilder result) + => AppendMembers(root, result, topLevel: true, methodLevel: true); + + public void AddTopLevelMembers(SyntaxNode? root, ArrayBuilder result) + => AppendMembers(root, result, topLevel: true, methodLevel: false); + + public void AddMethodLevelMembers(SyntaxNode? root, ArrayBuilder result) + => AppendMembers(root, result, topLevel: false, methodLevel: true); + + protected abstract void AppendMembers(SyntaxNode? node, ArrayBuilder list, bool topLevel, bool methodLevel); +} diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs index e0abf008de631..7a2544c4bf028 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs @@ -7,6 +7,7 @@ using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Threading; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.LanguageService; @@ -410,9 +411,12 @@ void GetPartsOfTupleExpression(SyntaxNode node, SyntaxNode? ConvertToSingleLine(SyntaxNode? node, bool useElasticTrivia = false); // Violation. This is a feature level API. - PooledObject> GetTopLevelAndMethodLevelMembers(SyntaxNode? root); + void AddTopLevelAndMethodLevelMembers(SyntaxNode? root, ArrayBuilder result); // Violation. This is a feature level API. - PooledObject> GetMethodLevelMembers(SyntaxNode? root); + void AddTopLevelMembers(SyntaxNode? root, ArrayBuilder result); + // Violation. This is a feature level API. + void AddMethodLevelMembers(SyntaxNode? root, ArrayBuilder result); + SyntaxList GetMembersOfTypeDeclaration(SyntaxNode typeDeclaration); // Violation. This is a feature level API. diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb index 0c464486a45f4..c414cd2de2103 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb @@ -21,6 +21,7 @@ Imports Microsoft.CodeAnalysis.Editing Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService Friend Class VisualBasicSyntaxFacts + Inherits AbstractSyntaxFacts Implements ISyntaxFacts Private Const DoesNotExistInVBErrorMessage = "This feature does not exist in VB" @@ -888,24 +889,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService Return TextSpan.FromBounds(list.First.SpanStart, list.Last.Span.End) End Function - Public Function GetTopLevelAndMethodLevelMembers(root As SyntaxNode) As PooledObject(Of List(Of SyntaxNode)) Implements ISyntaxFacts.GetTopLevelAndMethodLevelMembers - Dim pooledList = PooledObject(Of List(Of SyntaxNode)).Create(s_syntaxNodeListPool) - Dim list = pooledList.Object - - AppendMembers(root, list, topLevel:=True, methodLevel:=True) - - Return pooledList - End Function - - Public Function GetMethodLevelMembers(root As SyntaxNode) As PooledObject(Of List(Of SyntaxNode)) Implements ISyntaxFacts.GetMethodLevelMembers - Dim pooledList = PooledObject(Of List(Of SyntaxNode)).Create(s_syntaxNodeListPool) - Dim list = pooledList.Object - - AppendMembers(root, list, topLevel:=False, methodLevel:=True) - - Return pooledList - End Function - Public Function GetMembersOfTypeDeclaration(typeDeclaration As SyntaxNode) As SyntaxList(Of SyntaxNode) Implements ISyntaxFacts.GetMembersOfTypeDeclaration Return DirectCast(typeDeclaration, TypeBlockSyntax).Members End Function @@ -1050,7 +1033,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService End If End Sub - Private Sub AppendMembers(node As SyntaxNode, list As List(Of SyntaxNode), topLevel As Boolean, methodLevel As Boolean) + Protected Overrides Sub AppendMembers(node As SyntaxNode, list As ArrayBuilder(Of SyntaxNode), topLevel As Boolean, methodLevel As Boolean) Debug.Assert(topLevel OrElse methodLevel) For Each member In node.GetMembers() From dead7da22c8c969036a17c6c0f28c12dddc2ac67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Wed, 5 Feb 2025 12:07:22 -0800 Subject: [PATCH 063/305] Move async utils to Microsoft.CodeAnalysis.Threading source package (#76937) * Move to Threading namespace * Microsoft.CodeAnalysis.Threading.Package source package --- Roslyn.sln | 17 +++++++++ eng/config/PublishData.json | 1 + eng/test-rebuild.ps1 | 1 + .../OrderModifiers/OrderModifiersTests.vb | 36 +++++++++--------- .../Microsoft.CodeAnalysis.CodeStyle.csproj | 1 + .../Compilation/CSharpSemanticModel.cs | 2 +- .../Portable/InternalUtilities/VoidResult.cs | 23 ----------- .../YieldAwaitableExtensions.cs | 24 ------------ .../Core/Portable/Text/CompositeText.cs | 4 +- .../Operations/VisualBasicOperationFactory.vb | 29 +++++++------- .../Symbols/Metadata/PE/PEPropertySymbol.vb | 8 ++-- .../RetargetingSymbolTranslator.vb | 9 ++--- .../Source/SourceNamedTypeSymbol_ComClass.vb | 3 +- .../Semantics/StaticLocalsSemanticTests.vb | 10 ++--- .../Semantics/InterpolatedStringTests.vb | 6 ++- .../DocumentationComments/DocCommentTests.vb | 22 +++++------ .../Threading}/AsyncBatchingWorkQueue`0.cs | 5 ++- .../Threading}/AsyncBatchingWorkQueue`1.cs | 3 +- .../Threading}/AsyncBatchingWorkQueue`2.cs | 24 ++++++++---- .../Threading}/CancellationSeries.cs | 2 +- .../Threading}/ConfiguredYieldAwaitable.cs | 2 +- ...soft.CodeAnalysis.Threading.Package.csproj | 38 +++++++++++++++++++ ...Microsoft.CodeAnalysis.Threading.projitems | 30 +++++++++++++++ .../Microsoft.CodeAnalysis.Threading.shproj | 14 +++++++ .../Threading}/TaskExtensions.cs | 7 +--- .../Threading}/TestHooks/IAsyncToken.cs | 0 .../IAsynchronousOperationListener.cs | 0 .../IAsynchronousOperationListenerProvider.cs | 0 .../TestHooks/IExpeditableDelaySource.cs | 0 .../Threading}/ValueTaskExtensions.cs | 4 +- src/Dependencies/Threading/VoidResult.cs | 22 +++++++++++ .../Threading/YieldAwaitableExtensions.cs | 23 +++++++++++ .../BackgroundWorkIndicatorContext.cs | 1 + ...assificationBufferTaggerProvider.Tagger.cs | 1 + ...lassificationTaggerProvider.TagComputer.cs | 1 + .../DefinitionContextTracker.cs | 1 + .../Aggregator/SettingsAggregator.cs | 1 + .../AsyncCompletion/CompletionSource.cs | 1 + .../Core/Interactive/InteractiveSession.cs | 1 + .../NavigationBar/NavigationBarController.cs | 1 + .../Core/Remote/SolutionChecksumUpdater.cs | 1 + ...ventSources.WorkspaceChangedEventSource.cs | 1 + ...acySolutionEventsWorkspaceEventListener.cs | 1 + ...actAsynchronousTaggerProvider.TagSource.cs | 1 + ...ousTaggerProvider.TagSource_ProduceTags.cs | 1 + .../Core/Tagging/TaggerMainThreadManager.cs | 1 + .../AsynchronousOperationListenerTests.cs | 2 +- .../IntelliSense/CompletionServiceTests.vb | 3 +- .../AbstractCommandHandlerTestState.cs | 2 +- .../ReorderParameters.InvocationLocations.vb | 6 +-- .../AbstractVisualBasicCodeActionTest.vb | 6 +-- .../AbstractContextTests.vb | 10 ++--- ...agnosticProviderBasedUserDiagnosticTest.vb | 4 +- .../ExtractMethodTests.ControlFlowAnalysis.vb | 6 +-- .../ExtractMethodTests.TriviaProcessor.vb | 10 ++--- .../Formatting/FormattingEngineTests_Venus.vb | 14 +++---- .../SmartTokenFormatter_FormatTokenTests.vb | 2 +- ...ctVisualBasicSignatureHelpProviderTests.vb | 4 +- .../CodeRefactoringService.cs | 1 + .../CompletionService.ProviderManager.cs | 1 + ...ractImportCompletionCacheServiceFactory.cs | 2 +- .../IImportCompletionCacheService.cs | 2 +- .../UnitTestingIdleProcessor.cs | 1 + .../UnitTestingWorkCoordinator.cs | 1 + ...ateToSearchService.CachedDocumentSearch.cs | 1 + ...ToSearchService.GeneratedDocumentSearch.cs | 1 + ...actNavigateToSearchService.NormalSearch.cs | 1 + .../AbstractNavigateToSearchService.cs | 1 + .../IRemoteNavigateToSearchService.cs | 1 + .../Syntax/AbstractBlockStructureProvider.cs | 1 + ...tractVisualBasicCodeActionTest_NoEditor.vb | 4 +- ...roviderBasedUserDiagnosticTest_NoEditor.vb | 4 +- .../GenerateVariable/GenerateVariableTests.vb | 29 +++++++------- ...rosoft.CodeAnalysis.InteractiveHost.csproj | 4 +- .../LanguageServerProjectSystem.cs | 1 + .../Razor/RazorDynamicFileInfoProvider.cs | 1 + .../Protocol/Handler/AbstractRefreshQueue.cs | 1 + .../References/FindUsagesLSPContext.cs | 1 + .../SourceGeneratorRefreshQueue.cs | 1 + .../Def/ColorSchemes/ColorSchemeApplier.cs | 2 +- .../VisualStudioDesignerAttributeService.cs | 1 + .../DocumentOutlineViewModel.cs | 1 + ...bstractTableDataSourceFindUsagesContext.cs | 1 + .../KeybindingResetDetector.cs | 1 + ...tractCreateServicesOnTextViewConnection.cs | 2 +- .../PackageInstallerServiceFactory.cs | 1 + .../PdbSourceDocumentOutputWindowLogger.cs | 2 +- .../Core/Def/Progression/GraphQueryManager.cs | 1 + .../Def/ProjectSystem/FileChangeWatcher.cs | 1 + .../AbstractDelayStartedService.cs | 1 + .../ExternalErrorDiagnosticUpdateSource.cs | 1 + .../Core/Def/Telemetry/FileLogger.cs | 1 + .../Workspace/SourceGeneratedFileManager.cs | 1 + .../Impl/CodeModel/ProjectCodeModelFactory.cs | 1 + .../AnalyzerItem/AnalyzerItemSource.cs | 1 + .../BaseDiagnosticAndGeneratorItemSource.cs | 1 + .../SourceGeneratedFileItemSource.cs | 1 + .../Test.Next/Services/AssetProviderTests.cs | 1 + .../InProcess/AutomationElementHelper.cs | 1 + .../SymbolTree/SymbolTreeInfoCacheService.cs | 1 + .../Microsoft.CodeAnalysis.Workspaces.csproj | 1 + ...tractGlobalOperationNotificationService.cs | 1 + .../Shared/TestHooks/TaskExtensions.cs | 2 +- .../SQLite/v2/SQLitePersistentStorage.cs | 1 + .../v2/SQLitePersistentStorage_FlushWrites.cs | 1 + .../TemporaryStorageService.cs | 1 + .../IsolatedAnalyzerFileReference.cs | 4 +- ...tchedPortableExecutableReferenceFactory.cs | 1 + .../ProjectSystem/ProjectSystemProject.cs | 1 + ...pilationState.RegularCompilationTracker.cs | 1 + ...pilationState.TranslationAction_Actions.cs | 1 + .../Solution/SolutionCompilationState.cs | 1 + ...coverableTextAndVersion.RecoverableText.cs | 1 + .../Core/Portable/Workspace/Workspace.cs | 1 + .../Workspace/Workspace_SourceGeneration.cs | 1 + .../CoreTest/UtilityTest/AsyncLazyTests.cs | 2 +- .../UtilityTest/CancellationSeriesTests.cs | 1 + .../Core/AbstractAssetProviderExtensions.cs | 1 + .../Remote/Core/SolutionAssetStorage.Scope.cs | 1 + .../EditAndContinueLogReporter.cs | 1 + .../RemoteNavigateToSearchService.cs | 1 + ...essTelemetryService.PerformanceReporter.cs | 1 + ...teSemanticClassificationService.Caching.cs | 1 + .../Core/CompilerExtensions.projitems | 6 --- .../Compiler/Core/Utilities/AsyncLazy`1.cs | 2 +- .../Core/Utilities/ProducerConsumer.cs | 1 + .../Utilities/RoslynParallel.NetFramework.cs | 5 ++- 127 files changed, 373 insertions(+), 206 deletions(-) delete mode 100644 src/Compilers/Core/Portable/InternalUtilities/VoidResult.cs delete mode 100644 src/Compilers/Core/Portable/InternalUtilities/YieldAwaitableExtensions.cs rename src/{Workspaces/Core/Portable/Shared/Utilities => Dependencies/Threading}/AsyncBatchingWorkQueue`0.cs (89%) rename src/{Workspaces/Core/Portable/Shared/Utilities => Dependencies/Threading}/AsyncBatchingWorkQueue`1.cs (96%) rename src/{Workspaces/Core/Portable/Shared/Utilities => Dependencies/Threading}/AsyncBatchingWorkQueue`2.cs (96%) rename src/{Workspaces/Core/Portable/Utilities => Dependencies/Threading}/CancellationSeries.cs (99%) rename src/{Compilers/Core/Portable/InternalUtilities => Dependencies/Threading}/ConfiguredYieldAwaitable.cs (98%) create mode 100644 src/Dependencies/Threading/Microsoft.CodeAnalysis.Threading.Package.csproj create mode 100644 src/Dependencies/Threading/Microsoft.CodeAnalysis.Threading.projitems create mode 100644 src/Dependencies/Threading/Microsoft.CodeAnalysis.Threading.shproj rename src/{Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/TestHooks => Dependencies/Threading}/TaskExtensions.cs (95%) rename src/{Workspaces/Core/Portable/Shared => Dependencies/Threading}/TestHooks/IAsyncToken.cs (100%) rename src/{Workspaces/Core/Portable/Shared => Dependencies/Threading}/TestHooks/IAsynchronousOperationListener.cs (100%) rename src/{Workspaces/Core/Portable/Shared => Dependencies/Threading}/TestHooks/IAsynchronousOperationListenerProvider.cs (100%) rename src/{Workspaces/SharedUtilitiesAndExtensions/Compiler/Core => Dependencies/Threading}/TestHooks/IExpeditableDelaySource.cs (100%) rename src/{Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/TestHooks => Dependencies/Threading}/ValueTaskExtensions.cs (98%) create mode 100644 src/Dependencies/Threading/VoidResult.cs create mode 100644 src/Dependencies/Threading/YieldAwaitableExtensions.cs diff --git a/Roslyn.sln b/Roslyn.sln index c49637445381e..c0ea20393604e 100644 --- a/Roslyn.sln +++ b/Roslyn.sln @@ -569,6 +569,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ExternalAccess", "ExternalA EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.LanguageServer.Protocol.Test.Utilities", "src\LanguageServer\Protocol.TestUtilities\Microsoft.CodeAnalysis.LanguageServer.Protocol.Test.Utilities.csproj", "{7465CE63-A7B7-475F-8C57-48D2F8BC665A}" EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Microsoft.CodeAnalysis.Threading", "src\Dependencies\Threading\Microsoft.CodeAnalysis.Threading.shproj", "{967723E8-4FDD-447B-99F6-4F8C47CB5433}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.Threading.Package", "src\Dependencies\Threading\Microsoft.CodeAnalysis.Threading.Package.csproj", "{2559DAF9-784E-4C29-E0E1-70204B1FD56E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1399,6 +1403,10 @@ Global {7465CE63-A7B7-475F-8C57-48D2F8BC665A}.Debug|Any CPU.Build.0 = Debug|Any CPU {7465CE63-A7B7-475F-8C57-48D2F8BC665A}.Release|Any CPU.ActiveCfg = Release|Any CPU {7465CE63-A7B7-475F-8C57-48D2F8BC665A}.Release|Any CPU.Build.0 = Release|Any CPU + {2559DAF9-784E-4C29-E0E1-70204B1FD56E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2559DAF9-784E-4C29-E0E1-70204B1FD56E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2559DAF9-784E-4C29-E0E1-70204B1FD56E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2559DAF9-784E-4C29-E0E1-70204B1FD56E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1661,6 +1669,8 @@ Global {1AE9182D-B03E-4B00-B32E-37AE01715F57} = {EE97CB90-33BB-4F3A-9B3D-69375DEC6AC6} {806F0C6F-3640-4C92-8D55-6767B1535467} = {D449D505-CC6A-4E0B-AF1B-976E2D0AE67A} {7465CE63-A7B7-475F-8C57-48D2F8BC665A} = {D449D505-CC6A-4E0B-AF1B-976E2D0AE67A} + {967723E8-4FDD-447B-99F6-4F8C47CB5433} = {C2D1346B-9665-4150-B644-075CF1636BAA} + {2559DAF9-784E-4C29-E0E1-70204B1FD56E} = {C2D1346B-9665-4150-B644-075CF1636BAA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {604E6B91-7BC0-4126-AE07-D4D2FEFC3D29} @@ -1679,9 +1689,12 @@ Global src\Compilers\VisualBasic\BasicAnalyzerDriver\BasicAnalyzerDriver.projitems*{2523d0e6-df32-4a3e-8ae0-a19bffae2ef6}*SharedItemsImports = 5 src\Analyzers\VisualBasic\Analyzers\VisualBasicAnalyzers.projitems*{2531a8c4-97dd-47bc-a79c-b7846051e137}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Compiler\VisualBasic\VisualBasicCompilerExtensions.projitems*{2531a8c4-97dd-47bc-a79c-b7846051e137}*SharedItemsImports = 5 + src\Dependencies\Collections\Microsoft.CodeAnalysis.Collections.projitems*{2559daf9-784e-4c29-e0e1-70204b1fd56e}*SharedItemsImports = 5 + src\Dependencies\PooledObjects\Microsoft.CodeAnalysis.PooledObjects.projitems*{2559daf9-784e-4c29-e0e1-70204b1fd56e}*SharedItemsImports = 5 src\Analyzers\Core\Analyzers\Analyzers.projitems*{275812ee-dedb-4232-9439-91c9757d2ae4}*SharedItemsImports = 5 src\Dependencies\Collections\Microsoft.CodeAnalysis.Collections.projitems*{275812ee-dedb-4232-9439-91c9757d2ae4}*SharedItemsImports = 5 src\Dependencies\PooledObjects\Microsoft.CodeAnalysis.PooledObjects.projitems*{275812ee-dedb-4232-9439-91c9757d2ae4}*SharedItemsImports = 5 + src\Dependencies\Threading\Microsoft.CodeAnalysis.Threading.projitems*{275812ee-dedb-4232-9439-91c9757d2ae4}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Compiler\Core\CompilerExtensions.projitems*{275812ee-dedb-4232-9439-91c9757d2ae4}*SharedItemsImports = 5 src\ExpressionEvaluator\VisualBasic\Source\ResultProvider\BasicResultProvider.projitems*{3140fe61-0856-4367-9aa3-8081b9a80e35}*SharedItemsImports = 13 src\ExpressionEvaluator\CSharp\Source\ResultProvider\CSharpResultProvider.projitems*{3140fe61-0856-4367-9aa3-8081b9a80e36}*SharedItemsImports = 13 @@ -1697,6 +1710,7 @@ Global src\Analyzers\CSharp\Tests\CSharpAnalyzers.UnitTests.projitems*{58969243-7f59-4236-93d0-c93b81f569b3}*SharedItemsImports = 13 src\Dependencies\Collections\Microsoft.CodeAnalysis.Collections.projitems*{5f8d2414-064a-4b3a-9b42-8e2a04246be5}*SharedItemsImports = 5 src\Dependencies\PooledObjects\Microsoft.CodeAnalysis.PooledObjects.projitems*{5f8d2414-064a-4b3a-9b42-8e2a04246be5}*SharedItemsImports = 5 + src\Dependencies\Threading\Microsoft.CodeAnalysis.Threading.projitems*{5f8d2414-064a-4b3a-9b42-8e2a04246be5}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Compiler\Core\CompilerExtensions.projitems*{5f8d2414-064a-4b3a-9b42-8e2a04246be5}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Workspace\Core\WorkspaceExtensions.projitems*{5f8d2414-064a-4b3a-9b42-8e2a04246be5}*SharedItemsImports = 5 src\Analyzers\Core\CodeFixes\CodeFixes.projitems*{5ff1e493-69cc-4d0b-83f2-039f469a04e1}*SharedItemsImports = 5 @@ -1709,8 +1723,11 @@ Global src\Analyzers\Core\Analyzers\Analyzers.projitems*{76e96966-4780-4040-8197-bde2879516f4}*SharedItemsImports = 13 src\Analyzers\VisualBasic\Tests\VisualBasicAnalyzers.UnitTests.projitems*{7b7f4153-ae93-4908-b8f0-430871589f83}*SharedItemsImports = 13 src\Compilers\VisualBasic\vbc\VbcCommandLine.projitems*{810b02ad-2ea5-4422-88ac-b71b8ab0df0b}*SharedItemsImports = 13 + src\Dependencies\Collections\Microsoft.CodeAnalysis.Collections.projitems*{8e2a252e-a140-45a6-a81a-2652996ea589}*SharedItemsImports = 5 + src\Dependencies\Threading\Microsoft.CodeAnalysis.Threading.projitems*{8e2a252e-a140-45a6-a81a-2652996ea589}*SharedItemsImports = 5 src\Analyzers\VisualBasic\Analyzers\VisualBasicAnalyzers.projitems*{94faf461-2e74-4dbb-9813-6b2cde6f1880}*SharedItemsImports = 13 src\Compilers\Server\VBCSCompiler\VBCSCompilerCommandLine.projitems*{9508f118-f62e-4c16-a6f4-7c3b56e166ad}*SharedItemsImports = 5 + src\Dependencies\Threading\Microsoft.CodeAnalysis.Threading.projitems*{967723e8-4fdd-447b-99f6-4f8c47cb5433}*SharedItemsImports = 13 src\Compilers\VisualBasic\vbc\VbcCommandLine.projitems*{975cd834-45f4-4ea0-a395-cb60dbd0e214}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Workspace\Core\WorkspaceExtensions.projitems*{99f594b1-3916-471d-a761-a6731fc50e9a}*SharedItemsImports = 13 src\Analyzers\VisualBasic\CodeFixes\VisualBasicCodeFixes.projitems*{9f9ccc78-7487-4127-9d46-db23e501f001}*SharedItemsImports = 13 diff --git a/eng/config/PublishData.json b/eng/config/PublishData.json index e4ee59538e82e..2059299b840fa 100644 --- a/eng/config/PublishData.json +++ b/eng/config/PublishData.json @@ -34,6 +34,7 @@ "Microsoft.CodeAnalysis.Debugging": "arcade", "Microsoft.CodeAnalysis.PooledObjects": "arcade", "Microsoft.CodeAnalysis.Collections": "arcade", + "Microsoft.CodeAnalysis.Threading": "arcade", "Microsoft.CodeAnalysis.Features": "arcade", "Microsoft.CodeAnalysis.EditorFeatures": "vssdk", "Microsoft.CodeAnalysis.EditorFeatures.Common": "vssdk", diff --git a/eng/test-rebuild.ps1 b/eng/test-rebuild.ps1 index ed0e3aa5c0273..294a34d7e776c 100644 --- a/eng/test-rebuild.ps1 +++ b/eng/test-rebuild.ps1 @@ -61,6 +61,7 @@ try { " --exclude netstandard2.0\Microsoft.CodeAnalysis.Collections.Package.dll" + " --exclude netstandard2.0\Microsoft.CodeAnalysis.Debugging.Package.dll" + " --exclude netstandard2.0\Microsoft.CodeAnalysis.PooledObjects.Package.dll" + + " --exclude netstandard2.0\Microsoft.CodeAnalysis.Threading.Package.dll" + " --exclude netcoreapp3.1\Microsoft.CodeAnalysis.Workspaces.UnitTests.dll" + " --exclude net472\Zip\tools\vsixexpinstaller\System.ValueTuple.dll" + " --exclude net472\Zip\tools\vsixexpinstaller\VSIXExpInstaller.exe" + diff --git a/src/Analyzers/VisualBasic/Tests/OrderModifiers/OrderModifiersTests.vb b/src/Analyzers/VisualBasic/Tests/OrderModifiers/OrderModifiersTests.vb index b245626da6838..7775994eef4a2 100644 --- a/src/Analyzers/VisualBasic/Tests/OrderModifiers/OrderModifiersTests.vb +++ b/src/Analyzers/VisualBasic/Tests/OrderModifiers/OrderModifiersTests.vb @@ -29,7 +29,7 @@ end class End Function - public async Function TestStruct() As Threading.Tasks.Task + Public Async Function TestStruct() As Task Await TestInRegularAndScript1Async( "[|friend|] protected structure C @@ -40,7 +40,7 @@ end structure") End Function - Public Async Function TestInterface() As Threading.Tasks.Task + Public Async Function TestInterface() As Task Await TestInRegularAndScript1Async( "[|friend|] protected interface C end interface", @@ -49,7 +49,7 @@ end interface") End Function - Public Async Function TestEnum() As Threading.Tasks.Task + Public Async Function TestEnum() As Task Await TestInRegularAndScript1Async( "[|friend|] protected enum C end enum", @@ -58,14 +58,14 @@ end enum") End Function - Public Async Function TestDelegate() As Threading.Tasks.Task + Public Async Function TestDelegate() As Task Await TestInRegularAndScript1Async( "[|friend|] protected delegate sub D()", "protected friend delegate sub D()") End Function - Public Async Function TestMethodStatement() As Threading.Tasks.Task + Public Async Function TestMethodStatement() As Task Await TestInRegularAndScript1Async( "class C [|mustoverride|] protected sub M() @@ -76,7 +76,7 @@ end class") End Function - Public Async Function TestMethodBlock() As Threading.Tasks.Task + Public Async Function TestMethodBlock() As Task Await TestInRegularAndScript1Async( "class C [|friend|] protected sub M() @@ -89,7 +89,7 @@ end class") End Function - Public Async Function TestField() As Threading.Tasks.Task + Public Async Function TestField() As Task Await TestInRegularAndScript1Async( "class C [|friend|] protected dim a as integer @@ -100,7 +100,7 @@ end class") End Function - Public Async Function TestConstructor() As Threading.Tasks.Task + Public Async Function TestConstructor() As Task Await TestInRegularAndScript1Async( "class C [|friend|] protected sub new() @@ -113,7 +113,7 @@ end class") End Function - Public Async Function TestPropertyStatement() As Threading.Tasks.Task + Public Async Function TestPropertyStatement() As Task Await TestInRegularAndScript1Async( "class C [|readonly|] protected property P as integer @@ -124,7 +124,7 @@ end class") End Function - Public Async Function TestPropertyBlock() As Threading.Tasks.Task + Public Async Function TestPropertyBlock() As Task Await TestInRegularAndScript1Async( "class C [|readonly|] protected property P as integer @@ -141,7 +141,7 @@ end class") End Function - Public Async Function TestAccessor() As Threading.Tasks.Task + Public Async Function TestAccessor() As Task Await TestInRegularAndScript1Async( "class C public property P as integer @@ -160,7 +160,7 @@ end class End Function - Public Async Function TestPropertyEvent() As Threading.Tasks.Task + Public Async Function TestPropertyEvent() As Task Await TestInRegularAndScript1Async( "class C [|friend|] protected custom event E as Action @@ -173,7 +173,7 @@ end class") End Function - Public Async Function TestFieldEvent() As Threading.Tasks.Task + Public Async Function TestFieldEvent() As Task Await TestInRegularAndScript1Async( "class C [|friend|] protected event E as Action @@ -184,7 +184,7 @@ end class") End Function - Public Async Function TestOperator() As Threading.Tasks.Task + Public Async Function TestOperator() As Task Await TestInRegularAndScript1Async( "class C [|shared|] public operator +(c1 as integer, c2 as integer) as integer @@ -199,7 +199,7 @@ end class End Function - Public Async Function TestConversionOperator() As Threading.Tasks.Task + Public Async Function TestConversionOperator() As Task Await TestInRegularAndScript1Async( "class C [|shared|] public widening operator CType(x as integer) as boolean @@ -212,7 +212,7 @@ end class") End Function - Public Async Function TestFixAll1() As Threading.Tasks.Task + Public Async Function TestFixAll1() As Task Await TestInRegularAndScript1Async( "{|FixAllInDocument:friend|} protected class C friend protected class Nested @@ -225,7 +225,7 @@ end class") End Function - Public Async Function TestFixAll2() As Threading.Tasks.Task + Public Async Function TestFixAll2() As Task Await TestInRegularAndScript1Async( "friend protected class C {|FixAllInDocument:friend|} protected class Nested @@ -240,7 +240,7 @@ end class End Function - Public Async Function TestTrivia1() As Threading.Tasks.Task + Public Async Function TestTrivia1() As Task Await TestInRegularAndScript1Async( " ''' Doc comment diff --git a/src/CodeStyle/Core/Analyzers/Microsoft.CodeAnalysis.CodeStyle.csproj b/src/CodeStyle/Core/Analyzers/Microsoft.CodeAnalysis.CodeStyle.csproj index 48f34cef5364a..c2756b64e73a8 100644 --- a/src/CodeStyle/Core/Analyzers/Microsoft.CodeAnalysis.CodeStyle.csproj +++ b/src/CodeStyle/Core/Analyzers/Microsoft.CodeAnalysis.CodeStyle.csproj @@ -49,5 +49,6 @@ + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index 33c4e8adad8b9..49c2ebda77ec0 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -1679,7 +1679,7 @@ private ImmutableArray LookupSymbolsInternal( } if (name == null) - results.RemoveWhere(static (symbol, _, _) => !symbol.CanBeReferencedByName, arg: default(VoidResult)); + results.RemoveWhere(static (symbol, _, _) => !symbol.CanBeReferencedByName, arg: 0); return results.ToImmutableAndFree(); } diff --git a/src/Compilers/Core/Portable/InternalUtilities/VoidResult.cs b/src/Compilers/Core/Portable/InternalUtilities/VoidResult.cs deleted file mode 100644 index ea481f9017836..0000000000000 --- a/src/Compilers/Core/Portable/InternalUtilities/VoidResult.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -namespace Roslyn.Utilities -{ - /// - /// Explicitly indicates result is void - /// - internal readonly struct VoidResult : IEquatable - { - public override bool Equals(object? obj) - => obj is VoidResult; - - public override int GetHashCode() - => 0; - - public bool Equals(VoidResult other) - => true; - } -} diff --git a/src/Compilers/Core/Portable/InternalUtilities/YieldAwaitableExtensions.cs b/src/Compilers/Core/Portable/InternalUtilities/YieldAwaitableExtensions.cs deleted file mode 100644 index dd62cb31b5d23..0000000000000 --- a/src/Compilers/Core/Portable/InternalUtilities/YieldAwaitableExtensions.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Runtime.CompilerServices; -using System.Threading.Tasks; - -namespace Roslyn.Utilities -{ - internal static class YieldAwaitableExtensions - { - /// - /// Implements ConfigureAwait(bool) for . The resulting behavior in asynchronous code - /// is the same as one would expect for . - /// - /// The awaitable provided by . - /// - /// An object used to await this yield. - public static ConfiguredYieldAwaitable ConfigureAwait(this YieldAwaitable awaitable, bool continueOnCapturedContext) - { - return new ConfiguredYieldAwaitable(awaitable, continueOnCapturedContext); - } - } -} diff --git a/src/Compilers/Core/Portable/Text/CompositeText.cs b/src/Compilers/Core/Portable/Text/CompositeText.cs index b4fdc92beec36..41c20d8861fc6 100644 --- a/src/Compilers/Core/Portable/Text/CompositeText.cs +++ b/src/Compilers/Core/Portable/Text/CompositeText.cs @@ -208,7 +208,7 @@ private static void RemoveSplitLineBreaksAndEmptySegments(ArrayBuilder 1) { // Remove empty segments before checking for split line breaks - segments.RemoveWhere(static (s, _, _) => s.Length == 0, default(VoidResult)); + segments.RemoveWhere(static (s, _, _) => s.Length == 0, arg: 0); var splitLineBreakFound = false; for (int i = 1; i < segments.Count; i++) @@ -230,7 +230,7 @@ private static void RemoveSplitLineBreaksAndEmptySegments(ArrayBuilder s.Length == 0, default(VoidResult)); + segments.RemoveWhere(static (s, _, _) => s.Length == 0, arg: 0); } } } diff --git a/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb b/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb index 3d6408f888fcf..89ce274086a30 100644 --- a/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb +++ b/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb @@ -4,6 +4,7 @@ Imports System.Collections.Concurrent Imports System.Collections.Immutable +Imports System.Threading Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.VisualBasic Imports Microsoft.CodeAnalysis.VisualBasic.Symbols @@ -43,9 +44,9 @@ Namespace Microsoft.CodeAnalysis.Operations End If If _lazyPlaceholderToParentMap Is Nothing Then - Threading.Interlocked.CompareExchange(_lazyPlaceholderToParentMap, - New ConcurrentDictionary(Of BoundValuePlaceholderBase, BoundNode)(concurrencyLevel:=2, capacity:=10, comparer:=ReferenceEqualityComparer.Instance), - Nothing) + Interlocked.CompareExchange(_lazyPlaceholderToParentMap, + New ConcurrentDictionary(Of BoundValuePlaceholderBase, BoundNode)(concurrencyLevel:=2, capacity:=10, comparer:=ReferenceEqualityComparer.Instance), + Nothing) End If Dim knownParent = _lazyPlaceholderToParentMap.GetOrAdd(placeholderOpt, parent) @@ -445,7 +446,7 @@ Namespace Microsoft.CodeAnalysis.Operations If(boundCall.ReceiverOpt?.Kind <> BoundKind.MyClassReference, False) Dim boundReceiver As BoundExpression = If(boundCall.ReceiverOpt, boundCall.MethodGroupOpt?.ReceiverOpt) - Dim receiver as IOperation = CreateReceiverOperation(boundReceiver, targetMethod) + Dim receiver As IOperation = CreateReceiverOperation(boundReceiver, targetMethod) Dim arguments As ImmutableArray(Of IArgumentOperation) = DeriveArguments(boundCall) Dim syntax As SyntaxNode = boundCall.Syntax @@ -471,7 +472,7 @@ Namespace Microsoft.CodeAnalysis.Operations End Function Private Function CreateBoundArrayAccessOperation(boundArrayAccess As BoundArrayAccess) As IArrayElementReferenceOperation - Dim arrayReference as IOperation = Create(boundArrayAccess.Expression) + Dim arrayReference As IOperation = Create(boundArrayAccess.Expression) Dim indices = CreateFromArray(Of BoundExpression, IOperation)(boundArrayAccess.Indices) Dim syntax As SyntaxNode = boundArrayAccess.Syntax Dim type As ITypeSymbol = boundArrayAccess.Type @@ -819,7 +820,7 @@ Namespace Microsoft.CodeAnalysis.Operations Debug.Assert(boundObjectCreationExpression.ConstructorOpt IsNot Nothing OrElse boundObjectCreationExpression.Arguments.IsEmpty()) Dim constructor As IMethodSymbol = boundObjectCreationExpression.ConstructorOpt Dim initializer As IObjectOrCollectionInitializerOperation = DirectCast(Create(boundObjectCreationExpression.InitializerOpt), IObjectOrCollectionInitializerOperation) - Dim arguments as ImmutableArray(Of IArgumentOperation) = DeriveArguments(boundObjectCreationExpression) + Dim arguments As ImmutableArray(Of IArgumentOperation) = DeriveArguments(boundObjectCreationExpression) Dim syntax As SyntaxNode = boundObjectCreationExpression.Syntax Dim type As ITypeSymbol = boundObjectCreationExpression.Type @@ -881,7 +882,7 @@ Namespace Microsoft.CodeAnalysis.Operations Dim instance As IOperation = CreateReceiverOperation( If(boundPropertyAccess.ReceiverOpt, boundPropertyAccess.PropertyGroupOpt?.ReceiverOpt), [property]) - Dim arguments as ImmutableArray(Of IArgumentOperation) = DeriveArguments(boundPropertyAccess) + Dim arguments As ImmutableArray(Of IArgumentOperation) = DeriveArguments(boundPropertyAccess) Dim syntax As SyntaxNode = boundPropertyAccess.Syntax Dim type As ITypeSymbol = boundPropertyAccess.Type @@ -1058,9 +1059,9 @@ Namespace Microsoft.CodeAnalysis.Operations End Function Private Function CreateBoundIfStatementOperation(boundIfStatement As BoundIfStatement) As IConditionalOperation - Dim condition as IOperation = Create(boundIfStatement.Condition) - Dim whenTrue as IOperation = Create(boundIfStatement.Consequence) - Dim whenFalse as IOperation = Create(boundIfStatement.AlternativeOpt) + Dim condition As IOperation = Create(boundIfStatement.Condition) + Dim whenTrue As IOperation = Create(boundIfStatement.Consequence) + Dim whenFalse As IOperation = Create(boundIfStatement.AlternativeOpt) Dim syntax As SyntaxNode = boundIfStatement.Syntax Dim type As ITypeSymbol = Nothing Dim constantValue As ConstantValue = Nothing @@ -1281,7 +1282,7 @@ Namespace Microsoft.CodeAnalysis.Operations End Function Private Function CreateBoundCatchBlockOperation(boundCatchBlock As BoundCatchBlock) As ICatchClauseOperation - Dim exceptionDeclarationOrExpression as IOperation = CreateBoundCatchBlockExceptionDeclarationOrExpression(boundCatchBlock) + Dim exceptionDeclarationOrExpression As IOperation = CreateBoundCatchBlockExceptionDeclarationOrExpression(boundCatchBlock) Dim filter As IOperation = Create(boundCatchBlock.ExceptionFilterOpt) Dim handler As IBlockOperation = DirectCast(Create(boundCatchBlock.Body), IBlockOperation) Dim exceptionType As ITypeSymbol = If(boundCatchBlock.ExceptionSourceOpt?.Type, DirectCast(_semanticModel.Compilation, VisualBasicCompilation).GetWellKnownType(WellKnownType.System_Exception)) @@ -1417,8 +1418,8 @@ Namespace Microsoft.CodeAnalysis.Operations DirectCast(_semanticModel.Compilation.GetSpecialType(SpecialType.System_Boolean), TypeSymbol), SynthesizedLocalKind.LockTaken, syntaxOpt:=boundSyncLockStatement.LockExpression.Syntax)) - Dim lockedValue as IOperation = Create(boundSyncLockStatement.LockExpression) - Dim body as IOperation = Create(boundSyncLockStatement.Body) + Dim lockedValue As IOperation = Create(boundSyncLockStatement.LockExpression) + Dim body As IOperation = Create(boundSyncLockStatement.Body) Dim syntax As SyntaxNode = boundSyncLockStatement.Syntax Dim isImplicit As Boolean = boundSyncLockStatement.WasCompilerGenerated Return New LockOperation(lockedValue, body, lockTakenSymbol, _semanticModel, syntax, isImplicit) @@ -1664,7 +1665,7 @@ Namespace Microsoft.CodeAnalysis.Operations GetSpecialTypeMember(SpecialMember.System_Nullable_T_GetValueOrDefault), MethodSymbol) If method IsNot Nothing Then - Dim receiver as IOperation = CreateReceiverOperation(boundNullableIsTrueOperator.Operand, method) + Dim receiver As IOperation = CreateReceiverOperation(boundNullableIsTrueOperator.Operand, method) Return New InvocationOperation(method.AsMember(DirectCast(boundNullableIsTrueOperator.Operand.Type, NamedTypeSymbol)), constrainedToType:=Nothing, receiver, isVirtual:=False, diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEPropertySymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEPropertySymbol.vb index 5c7571f2bc81b..0264bc6c8e9e2 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEPropertySymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEPropertySymbol.vb @@ -4,14 +4,14 @@ Imports System.Collections.Immutable Imports System.Globalization -Imports System.Threading Imports System.Reflection Imports System.Reflection.Metadata +Imports System.Reflection.Metadata.Ecma335 +Imports System.Runtime.CompilerServices +Imports System.Threading Imports Microsoft.Cci Imports Microsoft.CodeAnalysis.PooledObjects -Imports System.Reflection.Metadata.Ecma335 Imports Microsoft.CodeAnalysis.VisualBasic.Emit -Imports System.Runtime.CompilerServices Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE @@ -270,7 +270,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE ''' Friend Sub SetIsWithEvents(value As Boolean) Dim newValue = If(value, ThreeState.True, ThreeState.False) - Dim origValue = Threading.Interlocked.CompareExchange(Me._isWithEvents, newValue, ThreeState.Unknown) + Dim origValue = Interlocked.CompareExchange(Me._isWithEvents, newValue, ThreeState.Unknown) Debug.Assert(origValue = ThreeState.Unknown OrElse origValue = newValue, "Tried changing already known IsWithEvent value.") End Sub diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.vb b/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.vb index 6c0c7d59ddfda..3ff5fc6e14437 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.vb @@ -3,15 +3,12 @@ ' See the LICENSE file in the project root for more information. Imports System.Collections.Concurrent -Imports System.Collections.Generic Imports System.Collections.Immutable -Imports System.Collections.ObjectModel +Imports System.Threading Imports Microsoft.Cci Imports Microsoft.CodeAnalysis.PooledObjects -Imports Microsoft.CodeAnalysis.Text -Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE Imports Microsoft.CodeAnalysis.VisualBasic.Symbols -Imports Microsoft.CodeAnalysis.VisualBasic.Syntax +Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Retargeting @@ -649,7 +646,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Retargeting Dim newTypeModifiers = RetargetModifiers(oldTypeModifiers, modifiersHaveChanged) Dim newRefModifiers = RetargetModifiers(oldRefModifiers, modifiersHaveChanged) - Threading.Interlocked.CompareExchange(lazyCustomModifiers, CustomModifiersTuple.Create(newTypeModifiers, newRefModifiers), Nothing) + Interlocked.CompareExchange(lazyCustomModifiers, CustomModifiersTuple.Create(newTypeModifiers, newRefModifiers), Nothing) End If Return lazyCustomModifiers diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceNamedTypeSymbol_ComClass.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceNamedTypeSymbol_ComClass.vb index 16d84b989c8db..eef1641c6bcca 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceNamedTypeSymbol_ComClass.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceNamedTypeSymbol_ComClass.vb @@ -4,6 +4,7 @@ Imports System.Collections.Immutable Imports System.Runtime.InteropServices +Imports System.Threading Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.VisualBasic.Emit Imports Microsoft.CodeAnalysis.VisualBasic.Symbols @@ -763,7 +764,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property - Friend Overrides Sub GenerateDeclarationErrors(cancellationToken As Threading.CancellationToken) + Friend Overrides Sub GenerateDeclarationErrors(cancellationToken As CancellationToken) Throw ExceptionUtilities.Unreachable End Sub diff --git a/src/Compilers/VisualBasic/Test/Emit/Semantics/StaticLocalsSemanticTests.vb b/src/Compilers/VisualBasic/Test/Emit/Semantics/StaticLocalsSemanticTests.vb index b0e00bafb735d..2f3b931ab752d 100644 --- a/src/Compilers/VisualBasic/Test/Emit/Semantics/StaticLocalsSemanticTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/Semantics/StaticLocalsSemanticTests.vb @@ -2,14 +2,12 @@ ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. +Imports System.Threading Imports Microsoft.CodeAnalysis -Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Syntax -Imports Microsoft.CodeAnalysis.VisualBasic.UnitTests.Emit Imports Roslyn.Test.Utilities -Imports Xunit Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests @@ -346,8 +344,8 @@ End Class Public Sub Semantic_StaticLocalDeclaration_LateBound() ' test late bind ' call ToString() on object defeat the purpose - Dim currCulture = Threading.Thread.CurrentThread.CurrentCulture - Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture + Dim currCulture = Thread.CurrentThread.CurrentCulture + Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture Try 'Declare static local which is late bound Dim compilationDef = CreateCompilationWithMscorlib40AndVBRuntime( @@ -391,7 +389,7 @@ After:5.5]]>) Catch ex As Exception Assert.Null(ex) Finally - Threading.Thread.CurrentThread.CurrentCulture = currCulture + Thread.CurrentThread.CurrentCulture = currCulture End Try End Sub diff --git a/src/Compilers/VisualBasic/Test/Semantic/Semantics/InterpolatedStringTests.vb b/src/Compilers/VisualBasic/Test/Semantic/Semantics/InterpolatedStringTests.vb index da29a53e273c8..5eeaf3b59a046 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Semantics/InterpolatedStringTests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Semantics/InterpolatedStringTests.vb @@ -1,6 +1,8 @@ ' Licensed to the .NET Foundation under one or more agreements. ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. + +Imports System.Threading Imports Microsoft.CodeAnalysis.VisualBasic Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports Microsoft.CodeAnalysis.VisualBasic.SyntaxFacts @@ -555,7 +557,7 @@ BC42322: Runtime errors might occur when converting 'String' to 'IFormattable'. Public Sub InvariantCulture() - Dim previousCulture = Threading.Thread.CurrentThread.CurrentCulture + Dim previousCulture = Thread.CurrentThread.CurrentCulture Dim verifier = CompileAndVerify( @@ -584,7 +586,7 @@ End Module , expectedOutput:="1,51,51,51.5") - Assert.Equal(previousCulture, Threading.Thread.CurrentThread.CurrentCulture) + Assert.Equal(previousCulture, Thread.CurrentThread.CurrentCulture) End Sub diff --git a/src/Compilers/VisualBasic/Test/Symbol/DocumentationComments/DocCommentTests.vb b/src/Compilers/VisualBasic/Test/Symbol/DocumentationComments/DocCommentTests.vb index 1757a5bb79bf9..8d2f0944c2544 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/DocumentationComments/DocCommentTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/DocumentationComments/DocCommentTests.vb @@ -3,15 +3,15 @@ ' See the LICENSE file in the project root for more information. Imports System.Collections.Immutable +Imports System.IO +Imports System.Text +Imports System.Threading +Imports Microsoft.CodeAnalysis.Test.Utilities Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE Imports Microsoft.CodeAnalysis.VisualBasic.Syntax -Imports Microsoft.CodeAnalysis.Test.Utilities -Imports System.Xml.Linq -Imports System.Text -Imports System.IO -Imports Roslyn.Test.Utilities Imports Microsoft.CodeAnalysis.VisualBasic.VisualBasicCompilation +Imports Roslyn.Test.Utilities Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Public Class DocCommentTests @@ -12129,8 +12129,8 @@ xmlDoc) If preferred Is Nothing Then ensureEnglishUICulture = False Else - saveUICulture = Threading.Thread.CurrentThread.CurrentUICulture - Threading.Thread.CurrentThread.CurrentUICulture = preferred + saveUICulture = Thread.CurrentThread.CurrentUICulture + Thread.CurrentThread.CurrentUICulture = preferred End If End If @@ -12138,7 +12138,7 @@ xmlDoc) diagnostics = compilation.GetDiagnostics(CompilationStage.Compile).ToArray() Finally If ensureEnglishUICulture Then - Threading.Thread.CurrentThread.CurrentUICulture = saveUICulture + Thread.CurrentThread.CurrentUICulture = saveUICulture End If End Try @@ -12170,8 +12170,8 @@ xmlDoc) If preferred Is Nothing Then ensureEnglishUICulture = False Else - saveUICulture = Threading.Thread.CurrentThread.CurrentUICulture - Threading.Thread.CurrentThread.CurrentUICulture = preferred + saveUICulture = Thread.CurrentThread.CurrentUICulture + Thread.CurrentThread.CurrentUICulture = preferred End If End If @@ -12179,7 +12179,7 @@ xmlDoc) emitResult = compilation.Emit(output, xmlDocumentationStream:=xml) Finally If ensureEnglishUICulture Then - Threading.Thread.CurrentThread.CurrentUICulture = saveUICulture + Thread.CurrentThread.CurrentUICulture = saveUICulture End If End Try diff --git a/src/Workspaces/Core/Portable/Shared/Utilities/AsyncBatchingWorkQueue`0.cs b/src/Dependencies/Threading/AsyncBatchingWorkQueue`0.cs similarity index 89% rename from src/Workspaces/Core/Portable/Shared/Utilities/AsyncBatchingWorkQueue`0.cs rename to src/Dependencies/Threading/AsyncBatchingWorkQueue`0.cs index fd21607855f71..c6b81aff2e2e4 100644 --- a/src/Workspaces/Core/Portable/Shared/Utilities/AsyncBatchingWorkQueue`0.cs +++ b/src/Dependencies/Threading/AsyncBatchingWorkQueue`0.cs @@ -8,8 +8,9 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Roslyn.Utilities; -namespace Roslyn.Utilities; +namespace Microsoft.CodeAnalysis.Threading; /// internal sealed class AsyncBatchingWorkQueue( @@ -22,5 +23,5 @@ private static Func, CancellationToken, Value => (items, ct) => processBatchAsync(ct); public void AddWork(bool cancelExistingWork = false) - => base.AddWork(default(VoidResult), cancelExistingWork); + => AddWork(default(VoidResult), cancelExistingWork); } diff --git a/src/Workspaces/Core/Portable/Shared/Utilities/AsyncBatchingWorkQueue`1.cs b/src/Dependencies/Threading/AsyncBatchingWorkQueue`1.cs similarity index 96% rename from src/Workspaces/Core/Portable/Shared/Utilities/AsyncBatchingWorkQueue`1.cs rename to src/Dependencies/Threading/AsyncBatchingWorkQueue`1.cs index 98aa376ccf429..a2f8d275e3409 100644 --- a/src/Workspaces/Core/Portable/Shared/Utilities/AsyncBatchingWorkQueue`1.cs +++ b/src/Dependencies/Threading/AsyncBatchingWorkQueue`1.cs @@ -8,8 +8,9 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Roslyn.Utilities; -namespace Roslyn.Utilities; +namespace Microsoft.CodeAnalysis.Threading; /// internal class AsyncBatchingWorkQueue( diff --git a/src/Workspaces/Core/Portable/Shared/Utilities/AsyncBatchingWorkQueue`2.cs b/src/Dependencies/Threading/AsyncBatchingWorkQueue`2.cs similarity index 96% rename from src/Workspaces/Core/Portable/Shared/Utilities/AsyncBatchingWorkQueue`2.cs rename to src/Dependencies/Threading/AsyncBatchingWorkQueue`2.cs index db07db90e48bd..89b331ff3e1b7 100644 --- a/src/Workspaces/Core/Portable/Shared/Utilities/AsyncBatchingWorkQueue`2.cs +++ b/src/Dependencies/Threading/AsyncBatchingWorkQueue`2.cs @@ -11,8 +11,9 @@ using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Roslyn.Utilities; -namespace Roslyn.Utilities; +namespace Microsoft.CodeAnalysis.Threading; /// /// A queue where items can be added to to be processed in batches after some delay has passed. When processing @@ -140,10 +141,16 @@ public void CancelExistingWork() public void AddWork(TItem item, bool cancelExistingWork = false) { - using var _ = ArrayBuilder.GetInstance(out var items); - items.Add(item); - - AddWork(items, cancelExistingWork); + var items = ArrayBuilder.GetInstance(); + try + { + items.Add(item); + AddWork(items, cancelExistingWork); + } + finally + { + items.Free(); + } } public void AddWork(IEnumerable items, bool cancelExistingWork = false) @@ -254,9 +261,7 @@ void AddItemsToBatch(IEnumerable items) var batchResultTask = _processBatchAsync(nextBatch, batchCancellationToken).Preserve(); await batchResultTask.NoThrowAwaitableInternal(false); if (batchResultTask.IsCompletedSuccessfully) - { return batchResultTask.Result; - } else if (batchResultTask.IsCanceled && !_entireQueueCancellationToken.IsCancellationRequested) { // Don't bubble up cancellation to the queue for the nested batch cancellation. Just because we decided @@ -265,8 +270,11 @@ void AddItemsToBatch(IEnumerable items) } else { + Contract.ThrowIfFalse(batchResultTask.IsCompleted); + // Realize the completed result to force the exception to be thrown. - batchResultTask.VerifyCompleted(); + batchResultTask.GetAwaiter().GetResult(); + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Workspaces/Core/Portable/Utilities/CancellationSeries.cs b/src/Dependencies/Threading/CancellationSeries.cs similarity index 99% rename from src/Workspaces/Core/Portable/Utilities/CancellationSeries.cs rename to src/Dependencies/Threading/CancellationSeries.cs index bddcaf175195f..1a477841243e2 100644 --- a/src/Workspaces/Core/Portable/Utilities/CancellationSeries.cs +++ b/src/Dependencies/Threading/CancellationSeries.cs @@ -11,7 +11,7 @@ using System; using System.Threading; -namespace Roslyn.Utilities; +namespace Microsoft.CodeAnalysis.Threading; /// /// Produces a series of objects such that requesting a new token diff --git a/src/Compilers/Core/Portable/InternalUtilities/ConfiguredYieldAwaitable.cs b/src/Dependencies/Threading/ConfiguredYieldAwaitable.cs similarity index 98% rename from src/Compilers/Core/Portable/InternalUtilities/ConfiguredYieldAwaitable.cs rename to src/Dependencies/Threading/ConfiguredYieldAwaitable.cs index 5bfa632d7488f..80502b3fbc1fc 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/ConfiguredYieldAwaitable.cs +++ b/src/Dependencies/Threading/ConfiguredYieldAwaitable.cs @@ -7,7 +7,7 @@ using System.Threading; using System.Threading.Tasks; -namespace Roslyn.Utilities +namespace Microsoft.CodeAnalysis.Threading { /// /// A custom awaiter that supports for diff --git a/src/Dependencies/Threading/Microsoft.CodeAnalysis.Threading.Package.csproj b/src/Dependencies/Threading/Microsoft.CodeAnalysis.Threading.Package.csproj new file mode 100644 index 0000000000000..b25752edccacb --- /dev/null +++ b/src/Dependencies/Threading/Microsoft.CodeAnalysis.Threading.Package.csproj @@ -0,0 +1,38 @@ + + + + + netstandard2.0 + false + none + false + true + + + true + true + Microsoft.CodeAnalysis.Threading + false + + Package containing sources of Microsoft .NET Compiler Platform ("Roslyn") threading utilities. + + + $(NoWarn);NU5128 + + + + + + + + + + + + + + + + + + diff --git a/src/Dependencies/Threading/Microsoft.CodeAnalysis.Threading.projitems b/src/Dependencies/Threading/Microsoft.CodeAnalysis.Threading.projitems new file mode 100644 index 0000000000000..1e6df17045318 --- /dev/null +++ b/src/Dependencies/Threading/Microsoft.CodeAnalysis.Threading.projitems @@ -0,0 +1,30 @@ + + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 967723E8-4FDD-447B-99F6-4F8C47CB5433 + + + Microsoft.CodeAnalysis.Threading + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Dependencies/Threading/Microsoft.CodeAnalysis.Threading.shproj b/src/Dependencies/Threading/Microsoft.CodeAnalysis.Threading.shproj new file mode 100644 index 0000000000000..d47a9e3d50b06 --- /dev/null +++ b/src/Dependencies/Threading/Microsoft.CodeAnalysis.Threading.shproj @@ -0,0 +1,14 @@ + + + + + 967723E8-4FDD-447B-99F6-4F8C47CB5433 + 14.0 + + + + + + + + \ No newline at end of file diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/TestHooks/TaskExtensions.cs b/src/Dependencies/Threading/TaskExtensions.cs similarity index 95% rename from src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/TestHooks/TaskExtensions.cs rename to src/Dependencies/Threading/TaskExtensions.cs index 9f4d7dc88b518..f35eb18a71e10 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/TestHooks/TaskExtensions.cs +++ b/src/Dependencies/Threading/TaskExtensions.cs @@ -3,13 +3,10 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; -using System.Threading; using System.Threading.Tasks; -using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Shared.TestHooks; +namespace Microsoft.CodeAnalysis.Threading; internal static partial class TaskExtensions { @@ -50,7 +47,6 @@ public readonly struct NoThrowTaskAwaitable /// Whether the continuation should be scheduled on the current sync context. public NoThrowTaskAwaitable(Task task, bool captureContext) { - Contract.ThrowIfNull(task, nameof(task)); _task = task; _captureContext = captureContext; } @@ -87,7 +83,6 @@ public NoThrowTaskAwaiter GetAwaiter() /// if set to true [capture context]. public NoThrowTaskAwaiter(Task task, bool captureContext) { - Contract.ThrowIfNull(task, nameof(task)); _task = task; _captureContext = captureContext; } diff --git a/src/Workspaces/Core/Portable/Shared/TestHooks/IAsyncToken.cs b/src/Dependencies/Threading/TestHooks/IAsyncToken.cs similarity index 100% rename from src/Workspaces/Core/Portable/Shared/TestHooks/IAsyncToken.cs rename to src/Dependencies/Threading/TestHooks/IAsyncToken.cs diff --git a/src/Workspaces/Core/Portable/Shared/TestHooks/IAsynchronousOperationListener.cs b/src/Dependencies/Threading/TestHooks/IAsynchronousOperationListener.cs similarity index 100% rename from src/Workspaces/Core/Portable/Shared/TestHooks/IAsynchronousOperationListener.cs rename to src/Dependencies/Threading/TestHooks/IAsynchronousOperationListener.cs diff --git a/src/Workspaces/Core/Portable/Shared/TestHooks/IAsynchronousOperationListenerProvider.cs b/src/Dependencies/Threading/TestHooks/IAsynchronousOperationListenerProvider.cs similarity index 100% rename from src/Workspaces/Core/Portable/Shared/TestHooks/IAsynchronousOperationListenerProvider.cs rename to src/Dependencies/Threading/TestHooks/IAsynchronousOperationListenerProvider.cs diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/TestHooks/IExpeditableDelaySource.cs b/src/Dependencies/Threading/TestHooks/IExpeditableDelaySource.cs similarity index 100% rename from src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/TestHooks/IExpeditableDelaySource.cs rename to src/Dependencies/Threading/TestHooks/IExpeditableDelaySource.cs diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/TestHooks/ValueTaskExtensions.cs b/src/Dependencies/Threading/ValueTaskExtensions.cs similarity index 98% rename from src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/TestHooks/ValueTaskExtensions.cs rename to src/Dependencies/Threading/ValueTaskExtensions.cs index 183f634b0bb43..366c3014edcda 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/TestHooks/ValueTaskExtensions.cs +++ b/src/Dependencies/Threading/ValueTaskExtensions.cs @@ -4,11 +4,9 @@ using System; using System.Runtime.CompilerServices; -using System.Runtime.ExceptionServices; using System.Threading.Tasks; -using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Shared.TestHooks; +namespace Microsoft.CodeAnalysis.Threading; internal static class ValueTaskExtensions { diff --git a/src/Dependencies/Threading/VoidResult.cs b/src/Dependencies/Threading/VoidResult.cs new file mode 100644 index 0000000000000..2e626ce7209d5 --- /dev/null +++ b/src/Dependencies/Threading/VoidResult.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace Microsoft.CodeAnalysis.Threading; + +/// +/// Explicitly indicates result is void +/// +internal readonly struct VoidResult : IEquatable +{ + public override bool Equals(object? obj) + => obj is VoidResult; + + public override int GetHashCode() + => 0; + + public bool Equals(VoidResult other) + => true; +} diff --git a/src/Dependencies/Threading/YieldAwaitableExtensions.cs b/src/Dependencies/Threading/YieldAwaitableExtensions.cs new file mode 100644 index 0000000000000..6cd6f6db5794b --- /dev/null +++ b/src/Dependencies/Threading/YieldAwaitableExtensions.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.Threading; + +internal static class YieldAwaitableExtensions +{ + /// + /// Implements ConfigureAwait(bool) for . The resulting behavior in asynchronous code + /// is the same as one would expect for . + /// + /// The awaitable provided by . + /// + /// An object used to await this yield. + public static ConfiguredYieldAwaitable ConfigureAwait(this YieldAwaitable awaitable, bool continueOnCapturedContext) + { + return new ConfiguredYieldAwaitable(awaitable, continueOnCapturedContext); + } +} diff --git a/src/EditorFeatures/Core.Wpf/BackgroundWorkIndicator/BackgroundWorkIndicatorContext.cs b/src/EditorFeatures/Core.Wpf/BackgroundWorkIndicator/BackgroundWorkIndicatorContext.cs index 037808e696ec0..8e4e985af0ed3 100644 --- a/src/EditorFeatures/Core.Wpf/BackgroundWorkIndicator/BackgroundWorkIndicatorContext.cs +++ b/src/EditorFeatures/Core.Wpf/BackgroundWorkIndicator/BackgroundWorkIndicatorContext.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.CodeAnalysis.Threading; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Adornments; using Microsoft.VisualStudio.Text.Editor; diff --git a/src/EditorFeatures/Core/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs b/src/EditorFeatures/Core/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs index 26e47c2026d5c..7fa4b6d4cc0de 100644 --- a/src/EditorFeatures/Core/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs +++ b/src/EditorFeatures/Core/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs @@ -15,6 +15,7 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Threading; using Microsoft.CodeAnalysis.Utilities; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Tagging; diff --git a/src/EditorFeatures/Core/Classification/Syntactic/SyntacticClassificationTaggerProvider.TagComputer.cs b/src/EditorFeatures/Core/Classification/Syntactic/SyntacticClassificationTaggerProvider.TagComputer.cs index 75f3ca53e80f3..47b10e864bfcf 100644 --- a/src/EditorFeatures/Core/Classification/Syntactic/SyntacticClassificationTaggerProvider.TagComputer.cs +++ b/src/EditorFeatures/Core/Classification/Syntactic/SyntacticClassificationTaggerProvider.TagComputer.cs @@ -15,6 +15,7 @@ using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; +using Microsoft.CodeAnalysis.Threading; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Tagging; using Microsoft.VisualStudio.Utilities; diff --git a/src/EditorFeatures/Core/CodeDefinitionWindow/DefinitionContextTracker.cs b/src/EditorFeatures/Core/CodeDefinitionWindow/DefinitionContextTracker.cs index dac6664277e6d..427695b244831 100644 --- a/src/EditorFeatures/Core/CodeDefinitionWindow/DefinitionContextTracker.cs +++ b/src/EditorFeatures/Core/CodeDefinitionWindow/DefinitionContextTracker.cs @@ -22,6 +22,7 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Threading; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Utilities; diff --git a/src/EditorFeatures/Core/EditorConfigSettings/Aggregator/SettingsAggregator.cs b/src/EditorFeatures/Core/EditorConfigSettings/Aggregator/SettingsAggregator.cs index 2260badd1ab46..72c59049b7fd8 100644 --- a/src/EditorFeatures/Core/EditorConfigSettings/Aggregator/SettingsAggregator.cs +++ b/src/EditorFeatures/Core/EditorConfigSettings/Aggregator/SettingsAggregator.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.EditorConfigSettings; diff --git a/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/CompletionSource.cs b/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/CompletionSource.cs index 2908b93d0f156..631cd5208e197 100644 --- a/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/CompletionSource.cs +++ b/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/CompletionSource.cs @@ -21,6 +21,7 @@ using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; +using Microsoft.CodeAnalysis.Threading; using Microsoft.VisualStudio.Core.Imaging; using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion; using Microsoft.VisualStudio.Text; diff --git a/src/EditorFeatures/Core/Interactive/InteractiveSession.cs b/src/EditorFeatures/Core/Interactive/InteractiveSession.cs index 8b4ee5b63197d..47aef36e9a97c 100644 --- a/src/EditorFeatures/Core/Interactive/InteractiveSession.cs +++ b/src/EditorFeatures/Core/Interactive/InteractiveSession.cs @@ -26,6 +26,7 @@ namespace Microsoft.CodeAnalysis.Interactive; using InteractiveHost::Microsoft.CodeAnalysis.Interactive; using Microsoft.CodeAnalysis.Collections; +using Microsoft.CodeAnalysis.Threading; using RelativePathResolver = Scripting::Microsoft.CodeAnalysis.RelativePathResolver; internal sealed class InteractiveSession : IDisposable diff --git a/src/EditorFeatures/Core/NavigationBar/NavigationBarController.cs b/src/EditorFeatures/Core/NavigationBar/NavigationBarController.cs index f988ab4ffc1d4..dfb89e2b7740c 100644 --- a/src/EditorFeatures/Core/NavigationBar/NavigationBarController.cs +++ b/src/EditorFeatures/Core/NavigationBar/NavigationBarController.cs @@ -17,6 +17,7 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Threading; using Microsoft.CodeAnalysis.Workspaces; using Microsoft.VisualStudio.Text; using Roslyn.Utilities; diff --git a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs index 6f1b14d5dc446..1ed0f29c3c823 100644 --- a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs +++ b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote; diff --git a/src/EditorFeatures/Core/Shared/Tagging/EventSources/TaggerEventSources.WorkspaceChangedEventSource.cs b/src/EditorFeatures/Core/Shared/Tagging/EventSources/TaggerEventSources.WorkspaceChangedEventSource.cs index 2c627b9a63a9e..d7301c29e8156 100644 --- a/src/EditorFeatures/Core/Shared/Tagging/EventSources/TaggerEventSources.WorkspaceChangedEventSource.cs +++ b/src/EditorFeatures/Core/Shared/Tagging/EventSources/TaggerEventSources.WorkspaceChangedEventSource.cs @@ -4,6 +4,7 @@ using System.Threading; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Threading; using Microsoft.VisualStudio.Text; using Roslyn.Utilities; diff --git a/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs b/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs index 123abe40b4e5d..5fe501dff9197 100644 --- a/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs +++ b/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs @@ -15,6 +15,7 @@ using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.SolutionCrawler; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LegacySolutionEvents; diff --git a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource.cs b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource.cs index 18d853a4388a0..3a7ed489a5f72 100644 --- a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource.cs +++ b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Threading; using Microsoft.CodeAnalysis.Workspaces; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; diff --git a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs index a071145057f31..c9eb12fb47e54 100644 --- a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs +++ b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs @@ -17,6 +17,7 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Threading; using Microsoft.CodeAnalysis.Utilities; using Microsoft.CodeAnalysis.Workspaces; using Microsoft.VisualStudio.Text; diff --git a/src/EditorFeatures/Core/Tagging/TaggerMainThreadManager.cs b/src/EditorFeatures/Core/Tagging/TaggerMainThreadManager.cs index 5378232757c2d..c7606e884ed3c 100644 --- a/src/EditorFeatures/Core/Tagging/TaggerMainThreadManager.cs +++ b/src/EditorFeatures/Core/Tagging/TaggerMainThreadManager.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; using TaggerUIData = (bool isVisible, Microsoft.VisualStudio.Text.SnapshotPoint? caretPosition, Roslyn.Utilities.OneOrMany spansToTag); diff --git a/src/EditorFeatures/Test/Utilities/AsynchronousOperationListenerTests.cs b/src/EditorFeatures/Test/Utilities/AsynchronousOperationListenerTests.cs index 010ed854b6fa1..697d94b619477 100644 --- a/src/EditorFeatures/Test/Utilities/AsynchronousOperationListenerTests.cs +++ b/src/EditorFeatures/Test/Utilities/AsynchronousOperationListenerTests.cs @@ -9,7 +9,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Shared.TestHooks; -using Roslyn.Utilities; +using Microsoft.CodeAnalysis.Threading; using Xunit; namespace Microsoft.CodeAnalysis.Editor.UnitTests.Utilities; diff --git a/src/EditorFeatures/Test2/IntelliSense/CompletionServiceTests.vb b/src/EditorFeatures/Test2/IntelliSense/CompletionServiceTests.vb index ab5edbe9480e7..f1b0f2327a4a7 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CompletionServiceTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CompletionServiceTests.vb @@ -4,6 +4,7 @@ Imports System.Collections.Immutable Imports System.Composition +Imports System.Threading Imports Microsoft.CodeAnalysis.Completion Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.Host @@ -134,7 +135,7 @@ $$ Return Task.CompletedTask End Function - Public Overrides Function GetDescriptionAsync(document As Document, item As CompletionItem, cancellationToken As Threading.CancellationToken) As Task(Of CompletionDescription) + Public Overrides Function GetDescriptionAsync(document As Document, item As CompletionItem, cancellationToken As CancellationToken) As Task(Of CompletionDescription) Return Task.FromResult(CompletionDescription.FromText(DescriptionText)) End Function End Class diff --git a/src/EditorFeatures/TestUtilities/AbstractCommandHandlerTestState.cs b/src/EditorFeatures/TestUtilities/AbstractCommandHandlerTestState.cs index 3ac452b07a1a8..6b00f7ebc79e0 100644 --- a/src/EditorFeatures/TestUtilities/AbstractCommandHandlerTestState.cs +++ b/src/EditorFeatures/TestUtilities/AbstractCommandHandlerTestState.cs @@ -225,7 +225,7 @@ public CaretPosition GetCaretPoint() => TextView.Caret.Position; /// - /// Used in synchronous methods to ensure all outstanding work has been + /// Used in synchronous methods to ensure all outstanding work has been /// completed. /// public void AssertNoAsynchronousOperationsRunning() diff --git a/src/EditorFeatures/VisualBasicTest/ChangeSignature/ReorderParameters.InvocationLocations.vb b/src/EditorFeatures/VisualBasicTest/ChangeSignature/ReorderParameters.InvocationLocations.vb index 0b6e46604c2db..aab4aded50829 100644 --- a/src/EditorFeatures/VisualBasicTest/ChangeSignature/ReorderParameters.InvocationLocations.vb +++ b/src/EditorFeatures/VisualBasicTest/ChangeSignature/ReorderParameters.InvocationLocations.vb @@ -585,7 +585,7 @@ End Class]]>.NormalizedValue() #Region "Code Refactoring" - Public Async Function ReorderIndexerParameters_CodeRefactoring_InMethodDeclaration() As Threading.Tasks.Task + Public Async Function ReorderIndexerParameters_CodeRefactoring_InMethodDeclaration() As Task Dim markup = .NormalizedValue() End Function - Public Async Function ReorderIndexerParameters_CodeRefactoring_NotInMethodBody() As Threading.Tasks.Task + Public Async Function ReorderIndexerParameters_CodeRefactoring_NotInMethodBody() As Task Dim markup = .NormalizedValue() End Function - Public Async Function ReorderIndexerParameters_CodeRefactoring_InCallSite_ViaCodeAction() As Threading.Tasks.Task + Public Async Function ReorderIndexerParameters_CodeRefactoring_InCallSite_ViaCodeAction() As Task Dim markup = - Public Async Function TestExitSub() As Threading.Tasks.Task + Public Async Function TestExitSub() As Task Dim code = Class Test Sub Test() [|Exit Sub|] @@ -26,7 +26,7 @@ End Class End Function - Public Async Function TestExitFunction() As Threading.Tasks.Task + Public Async Function TestExitFunction() As Task Dim code = Class Test Function Test1() As Integer Console.Write(42) @@ -271,7 +271,7 @@ End Class End Function - Public Async Function BugFix6313_2() As Threading.Tasks.Task + Public Async Function BugFix6313_2() As Task Dim code = Imports System Class A diff --git a/src/EditorFeatures/VisualBasicTest/ExtractMethod/ExtractMethodTests.TriviaProcessor.vb b/src/EditorFeatures/VisualBasicTest/ExtractMethod/ExtractMethodTests.TriviaProcessor.vb index dd7e134ac7838..14a593768af58 100644 --- a/src/EditorFeatures/VisualBasicTest/ExtractMethod/ExtractMethodTests.TriviaProcessor.vb +++ b/src/EditorFeatures/VisualBasicTest/ExtractMethod/ExtractMethodTests.TriviaProcessor.vb @@ -9,7 +9,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.ExtractMethod Public Class TriviaProcessor - Public Async Function TestCommentBeforeCode() As Threading.Tasks.Task + Public Async Function TestCommentBeforeCode() As Task Dim code = Class C Sub M() [|'comment @@ -32,7 +32,7 @@ End Class End Function - Public Async Function LineContinuation() As Threading.Tasks.Task + Public Async Function LineContinuation() As Task Dim code = Module Program Sub Main Dim x = [|1. _ @@ -55,7 +55,7 @@ End Module End Function - Public Async Function LineContinuation2() As Threading.Tasks.Task + Public Async Function LineContinuation2() As Task Dim code = Imports System Imports System.Collections.Generic Imports System.Linq @@ -98,7 +98,7 @@ End Module End Function - Public Async Function ImplicitLineContinuation() As Threading.Tasks.Task + Public Async Function ImplicitLineContinuation() As Task Dim code = Imports System.Linq Module A Sub Main() @@ -123,7 +123,7 @@ End Module End Function - Public Async Function ImplicitLineContinuation2() As Threading.Tasks.Task + Public Async Function ImplicitLineContinuation2() As Task Dim code = Imports System.Linq Module A Sub Main() diff --git a/src/EditorFeatures/VisualBasicTest/Formatting/FormattingEngineTests_Venus.vb b/src/EditorFeatures/VisualBasicTest/Formatting/FormattingEngineTests_Venus.vb index 0aef18c39f966..a8a0221d97a60 100644 --- a/src/EditorFeatures/VisualBasicTest/Formatting/FormattingEngineTests_Venus.vb +++ b/src/EditorFeatures/VisualBasicTest/Formatting/FormattingEngineTests_Venus.vb @@ -14,7 +14,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Formatting End Sub - Public Async Function SimpleOneLineNugget() As Threading.Tasks.Task + Public Async Function SimpleOneLineNugget() As Task Dim code = Imports System Imports System.Collections.Generic Imports System.Linq @@ -44,7 +44,7 @@ End Module - Public Async Function SimpleScriptBlock() As Threading.Tasks.Task + Public Async Function SimpleScriptBlock() As Task Dim code = Imports System Imports System.Collections.Generic Imports System.Linq @@ -75,7 +75,7 @@ End Module End Function - Public Async Function SimpleMultiLineNugget() As Threading.Tasks.Task + Public Async Function SimpleMultiLineNugget() As Task Dim code = Imports System Imports System.Collections.Generic Imports System.Linq @@ -113,7 +113,7 @@ End Module - Public Async Function SimpleQueryWithinNugget() As Threading.Tasks.Task + Public Async Function SimpleQueryWithinNugget() As Task Dim code = Imports System Imports System.Collections.Generic Imports System.Linq @@ -151,7 +151,7 @@ End Module End Function - Public Async Function SingleLineFunctionLambdaInNugget() As Threading.Tasks.Task + Public Async Function SingleLineFunctionLambdaInNugget() As Task Dim code = Imports System Imports System.Collections.Generic Imports System.Linq @@ -186,7 +186,7 @@ End Module End Function - Public Async Function MultiLineFunctionLambdaInNugget() As Threading.Tasks.Task + Public Async Function MultiLineFunctionLambdaInNugget() As Task Dim code = Imports System Imports System.Collections.Generic Imports System.Linq @@ -246,7 +246,7 @@ End Module ''' The rule has to be set up for each set of spans, currently we test just one Private Shared Async Function AssertFormatWithBaseIndentAfterReplacingLfToCrLfAsync(content As String, expected As String, - baseIndentation As Integer) As Threading.Tasks.Task + baseIndentation As Integer) As Task ' do this since xml value put only vbLf content = content.Replace(vbLf, vbCrLf) diff --git a/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartTokenFormatter_FormatTokenTests.vb b/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartTokenFormatter_FormatTokenTests.vb index 6aa0312032bef..e57a62172f560 100644 --- a/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartTokenFormatter_FormatTokenTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartTokenFormatter_FormatTokenTests.vb @@ -174,7 +174,7 @@ End Class Assert.NotNull(Await Record.ExceptionAsync(Function() TestAsync(codeWithMarkup, indentation, indentStyle:=indentStyle))) End Function - Private Shared Async Function TestAsync(codeWithMarkup As String, indentation As Integer, Optional indentStyle As FormattingOptions2.IndentStyle = FormattingOptions2.IndentStyle.Smart) As Threading.Tasks.Task + Private Shared Async Function TestAsync(codeWithMarkup As String, indentation As Integer, Optional indentStyle As FormattingOptions2.IndentStyle = FormattingOptions2.IndentStyle.Smart) As Task Dim code As String = Nothing Dim position As Integer = 0 MarkupTestFile.GetPosition(codeWithMarkup, code, position) diff --git a/src/EditorFeatures/VisualBasicTest/SignatureHelp/AbstractVisualBasicSignatureHelpProviderTests.vb b/src/EditorFeatures/VisualBasicTest/SignatureHelp/AbstractVisualBasicSignatureHelpProviderTests.vb index 81f35e47e6fed..28bd205b74a1e 100644 --- a/src/EditorFeatures/VisualBasicTest/SignatureHelp/AbstractVisualBasicSignatureHelpProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/SignatureHelp/AbstractVisualBasicSignatureHelpProviderTests.vb @@ -11,7 +11,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.SignatureHelp ' We want to skip script testing in all VB stuff for now. - Protected Overrides Function TestAsync(markupWithPositionAndOptSpan As String, Optional expectedOrderedItemsOrNull As IEnumerable(Of SignatureHelpTestItem) = Nothing, Optional usePreviousCharAsTrigger As Boolean = False, Optional sourceCodeKind As Microsoft.CodeAnalysis.SourceCodeKind? = Nothing, Optional experimental As Boolean = False) As Threading.Tasks.Task + Protected Overrides Function TestAsync(markupWithPositionAndOptSpan As String, Optional expectedOrderedItemsOrNull As IEnumerable(Of SignatureHelpTestItem) = Nothing, Optional usePreviousCharAsTrigger As Boolean = False, Optional sourceCodeKind As Microsoft.CodeAnalysis.SourceCodeKind? = Nothing, Optional experimental As Boolean = False) As Task If (sourceCodeKind.HasValue) Then Return MyBase.TestAsync(markupWithPositionAndOptSpan, expectedOrderedItemsOrNull, usePreviousCharAsTrigger, sourceCodeKind, experimental) Else @@ -19,7 +19,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.SignatureHelp End If End Function - Protected Overrides Function VerifyCurrentParameterNameAsync(markupWithPosition As String, expectedParameterName As String, Optional sourceCodeKind As Microsoft.CodeAnalysis.SourceCodeKind? = Nothing) As Threading.Tasks.Task + Protected Overrides Function VerifyCurrentParameterNameAsync(markupWithPosition As String, expectedParameterName As String, Optional sourceCodeKind As Microsoft.CodeAnalysis.SourceCodeKind? = Nothing) As Task If (sourceCodeKind.HasValue) Then Return MyBase.VerifyCurrentParameterNameAsync(markupWithPosition, expectedParameterName, sourceCodeKind) Else diff --git a/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs b/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs index 741e36da818da..0c9811d172e7e 100644 --- a/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs @@ -19,6 +19,7 @@ using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Telemetry; using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeRefactorings; diff --git a/src/Features/Core/Portable/Completion/CompletionService.ProviderManager.cs b/src/Features/Core/Portable/Completion/CompletionService.ProviderManager.cs index 3eb3abbd0a57f..546ffa37175d9 100644 --- a/src/Features/Core/Portable/Completion/CompletionService.ProviderManager.cs +++ b/src/Features/Core/Portable/Completion/CompletionService.ProviderManager.cs @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Shared.Utilities; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Completion; diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractImportCompletionCacheServiceFactory.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractImportCompletionCacheServiceFactory.cs index e91c21220a3bb..4799c399e5108 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractImportCompletionCacheServiceFactory.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractImportCompletionCacheServiceFactory.cs @@ -11,7 +11,7 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.TestHooks; -using Roslyn.Utilities; +using Microsoft.CodeAnalysis.Threading; namespace Microsoft.CodeAnalysis.Completion.Providers; diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/IImportCompletionCacheService.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/IImportCompletionCacheService.cs index 64b2d8787533e..7c3bb764c5faa 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/IImportCompletionCacheService.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/IImportCompletionCacheService.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using Microsoft.CodeAnalysis.Host; -using Roslyn.Utilities; +using Microsoft.CodeAnalysis.Threading; namespace Microsoft.CodeAnalysis.Completion.Providers; diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingIdleProcessor.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingIdleProcessor.cs index 1852b45b27be3..94ccaba167f4a 100644 --- a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingIdleProcessor.cs +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingIdleProcessor.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler; diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.cs index f3850a0e9f046..6b4436bfaa40a 100644 --- a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.cs +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.cs @@ -14,6 +14,7 @@ using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler; diff --git a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.CachedDocumentSearch.cs b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.CachedDocumentSearch.cs index f43c6f2ca15b6..9113e819be5c8 100644 --- a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.CachedDocumentSearch.cs +++ b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.CachedDocumentSearch.cs @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Storage; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.NavigateTo; diff --git a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.GeneratedDocumentSearch.cs b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.GeneratedDocumentSearch.cs index 90e55bc5a6137..dfbf67bc3e488 100644 --- a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.GeneratedDocumentSearch.cs +++ b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.GeneratedDocumentSearch.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.PatternMatching; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Shared.Utilities; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.NavigateTo; diff --git a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs index 0cdad427b8d35..5a41d42bee68d 100644 --- a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs +++ b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.PatternMatching; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Shared.Utilities; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.NavigateTo; diff --git a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.cs b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.cs index c25d5ffd8c675..30d264e854da2 100644 --- a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.cs +++ b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.NavigateTo; diff --git a/src/Features/Core/Portable/NavigateTo/IRemoteNavigateToSearchService.cs b/src/Features/Core/Portable/NavigateTo/IRemoteNavigateToSearchService.cs index e50aa7fad8794..e9cd9dd7b5402 100644 --- a/src/Features/Core/Portable/NavigateTo/IRemoteNavigateToSearchService.cs +++ b/src/Features/Core/Portable/NavigateTo/IRemoteNavigateToSearchService.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Storage; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.NavigateTo; diff --git a/src/Features/Core/Portable/Structure/Syntax/AbstractBlockStructureProvider.cs b/src/Features/Core/Portable/Structure/Syntax/AbstractBlockStructureProvider.cs index 27ed0aeb698ca..30b0271f8ecb1 100644 --- a/src/Features/Core/Portable/Structure/Syntax/AbstractBlockStructureProvider.cs +++ b/src/Features/Core/Portable/Structure/Syntax/AbstractBlockStructureProvider.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Structure; diff --git a/src/Features/VisualBasicTest/CodeActions/AbstractVisualBasicCodeActionTest_NoEditor.vb b/src/Features/VisualBasicTest/CodeActions/AbstractVisualBasicCodeActionTest_NoEditor.vb index 8a077a9b0adc5..3373cd2cba8f3 100644 --- a/src/Features/VisualBasicTest/CodeActions/AbstractVisualBasicCodeActionTest_NoEditor.vb +++ b/src/Features/VisualBasicTest/CodeActions/AbstractVisualBasicCodeActionTest_NoEditor.vb @@ -26,7 +26,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeRefactorings Return input.Replace("\n", vbCrLf) End Function - Protected Overloads Async Function TestAsync(initialMarkup As XElement, expected As XElement, Optional index As Integer = 0, Optional parseOptions As ParseOptions = Nothing) As Threading.Tasks.Task + Protected Overloads Async Function TestAsync(initialMarkup As XElement, expected As XElement, Optional index As Integer = 0, Optional parseOptions As ParseOptions = Nothing) As Task Dim initialMarkupStr = initialMarkup.ConvertTestSourceTag() Dim expectedStr = expected.ConvertTestSourceTag() If parseOptions Is Nothing Then @@ -36,7 +36,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeRefactorings End If End Function - Protected Overloads Async Function TestMissingAsync(initialMarkup As XElement) As Threading.Tasks.Task + Protected Overloads Async Function TestMissingAsync(initialMarkup As XElement) As Task Dim initialMarkupStr = initialMarkup.ConvertTestSourceTag() Await MyBase.TestMissingAsync(initialMarkupStr, diff --git a/src/Features/VisualBasicTest/Diagnostics/AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest_NoEditor.vb b/src/Features/VisualBasicTest/Diagnostics/AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest_NoEditor.vb index da635844e6056..279b58ab310ff 100644 --- a/src/Features/VisualBasicTest/Diagnostics/AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest_NoEditor.vb +++ b/src/Features/VisualBasicTest/Diagnostics/AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest_NoEditor.vb @@ -28,7 +28,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics End Function Friend Overloads Async Function TestAsync( - initialMarkup As XElement, expected As XElement, Optional index As Integer = 0) As Threading.Tasks.Task + initialMarkup As XElement, expected As XElement, Optional index As Integer = 0) As Task Dim initialMarkupStr = initialMarkup.ConvertTestSourceTag() Dim expectedStr = expected.ConvertTestSourceTag() @@ -37,7 +37,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics index:=index) End Function - Protected Overloads Async Function TestMissingAsync(initialMarkup As XElement) As Threading.Tasks.Task + Protected Overloads Async Function TestMissingAsync(initialMarkup As XElement) As Task Dim initialMarkupStr = initialMarkup.ConvertTestSourceTag() Await MyBase.TestMissingAsync(initialMarkupStr, New TestParameters(parseOptions:=Nothing, compilationOptions:=_compilationOptions)) diff --git a/src/Features/VisualBasicTest/GenerateVariable/GenerateVariableTests.vb b/src/Features/VisualBasicTest/GenerateVariable/GenerateVariableTests.vb index bd95346f49e3a..ff58f72d0e3ca 100644 --- a/src/Features/VisualBasicTest/GenerateVariable/GenerateVariableTests.vb +++ b/src/Features/VisualBasicTest/GenerateVariable/GenerateVariableTests.vb @@ -3,6 +3,7 @@ ' See the LICENSE file in the project root for more information. Imports System.Collections.Immutable +Imports System.Threading.Tasks Imports Microsoft.CodeAnalysis.CodeActions Imports Microsoft.CodeAnalysis.CodeFixes Imports Microsoft.CodeAnalysis.Diagnostics @@ -22,7 +23,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Genera End Function - Public Async Function TestGenerateSimpleProperty() As Threading.Tasks.Task + Public Async Function TestGenerateSimpleProperty() As Task Await TestInRegularAndScriptAsync( "Module Program Sub Main(args As String()) @@ -39,7 +40,7 @@ End Module") End Function - Public Async Function TestGenerateSimpleField() As Threading.Tasks.Task + Public Async Function TestGenerateSimpleField() As Task Await TestInRegularAndScriptAsync( "Module Program Sub Main(args As String()) @@ -57,7 +58,7 @@ index:=1) End Function - Public Async Function TestGenerateReadOnlyField() As Threading.Tasks.Task + Public Async Function TestGenerateReadOnlyField() As Task Await TestInRegularAndScriptAsync( "Module Program Sub Main(args As String()) @@ -75,7 +76,7 @@ index:=2) End Function - Public Async Function TestGenerateFromAssignment() As Threading.Tasks.Task + Public Async Function TestGenerateFromAssignment() As Task Await TestInRegularAndScriptAsync( "Class C Shared Sub M @@ -138,7 +139,7 @@ index:=1) End Function - Public Async Function TestGenerateProtectedSharedFieldIntoBase() As Threading.Tasks.Task + Public Async Function TestGenerateProtectedSharedFieldIntoBase() As Task Await TestInRegularAndScriptAsync( "Class Base End Class @@ -173,7 +174,7 @@ End Class") End Function - Public Async Function TestGenerateFriendAccessibilityForField() As Threading.Tasks.Task + Public Async Function TestGenerateFriendAccessibilityForField() As Task Await TestInRegularAndScriptAsync( "Class A End Class @@ -194,7 +195,7 @@ index:=1) End Function - Public Async Function TestGeneratePropertyOnInterface1() As Threading.Tasks.Task + Public Async Function TestGeneratePropertyOnInterface1() As Task Await TestInRegularAndScriptAsync( "Interface IGoo End Interface @@ -216,7 +217,7 @@ End Class") End Function - Public Async Function TestGeneratePropertyOnInterface2() As Threading.Tasks.Task + Public Async Function TestGeneratePropertyOnInterface2() As Task Await TestInRegularAndScriptAsync( "Interface IGoo End Interface @@ -238,7 +239,7 @@ End Class", index:=1) End Function - Public Async Function TestGeneratePropertyIntoModule() As Threading.Tasks.Task + Public Async Function TestGeneratePropertyIntoModule() As Task Await TestInRegularAndScriptAsync( "Module Program Sub Main(args As String()) @@ -263,7 +264,7 @@ End Class") End Function - Public Async Function TestFieldPropertyIntoModule() As Threading.Tasks.Task + Public Async Function TestFieldPropertyIntoModule() As Task Await TestInRegularAndScriptAsync( "Module Program Sub Main(args As String()) @@ -947,7 +948,7 @@ End Class") End Function - Public Async Function TestFieldWithAnonymousTypeType() As Threading.Tasks.Task + Public Async Function TestFieldWithAnonymousTypeType() As Task Await TestInRegularAndScriptAsync( "Imports System Imports System.Collections.Generic @@ -2712,7 +2713,7 @@ end class", index:=1) End Function - Public Async Function TestGenerateSimplePropertyInSyncLock() As Threading.Tasks.Task + Public Async Function TestGenerateSimplePropertyInSyncLock() As Task Await TestInRegularAndScriptAsync( "Module Program Sub Main(args As String()) @@ -2731,7 +2732,7 @@ End Module") End Function - Public Async Function TestGenerateSimpleFieldInSyncLock() As Threading.Tasks.Task + Public Async Function TestGenerateSimpleFieldInSyncLock() As Task Await TestInRegularAndScriptAsync( "Module Program Sub Main(args As String()) @@ -2751,7 +2752,7 @@ index:=1) End Function - Public Async Function TestGenerateReadOnlyFieldInSyncLock() As Threading.Tasks.Task + Public Async Function TestGenerateReadOnlyFieldInSyncLock() As Task Await TestInRegularAndScriptAsync( "Module Program Sub Main(args As String()) diff --git a/src/Interactive/Host/Microsoft.CodeAnalysis.InteractiveHost.csproj b/src/Interactive/Host/Microsoft.CodeAnalysis.InteractiveHost.csproj index d06fa7a7bee4a..067bc093a599f 100644 --- a/src/Interactive/Host/Microsoft.CodeAnalysis.InteractiveHost.csproj +++ b/src/Interactive/Host/Microsoft.CodeAnalysis.InteractiveHost.csproj @@ -37,8 +37,6 @@ - - @@ -76,4 +74,6 @@ + + \ No newline at end of file diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectSystem.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectSystem.cs index 781feabf5d46e..9a6376252f3cf 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectSystem.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectSystem.cs @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis.ProjectSystem; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Shared.Utilities; +using Microsoft.CodeAnalysis.Threading; using Microsoft.CodeAnalysis.Workspaces.ProjectSystem; using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.Composition; diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs index b064fb31132c3..214a518c3ec98 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.LanguageServer.LanguageServer; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.Razor; diff --git a/src/LanguageServer/Protocol/Handler/AbstractRefreshQueue.cs b/src/LanguageServer/Protocol/Handler/AbstractRefreshQueue.cs index 5eec735c049bd..f4b0498caabc9 100644 --- a/src/LanguageServer/Protocol/Handler/AbstractRefreshQueue.cs +++ b/src/LanguageServer/Protocol/Handler/AbstractRefreshQueue.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Threading; using Roslyn.LanguageServer.Protocol; using Roslyn.Utilities; using StreamJsonRpc; diff --git a/src/LanguageServer/Protocol/Handler/References/FindUsagesLSPContext.cs b/src/LanguageServer/Protocol/Handler/References/FindUsagesLSPContext.cs index 4196abef13bbe..93e03de3f093a 100644 --- a/src/LanguageServer/Protocol/Handler/References/FindUsagesLSPContext.cs +++ b/src/LanguageServer/Protocol/Handler/References/FindUsagesLSPContext.cs @@ -20,6 +20,7 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Threading; using Roslyn.LanguageServer.Protocol; using Roslyn.Text.Adornments; using Roslyn.Utilities; diff --git a/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorRefreshQueue.cs b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorRefreshQueue.cs index f1e315fc18004..08643a167efda 100644 --- a/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorRefreshQueue.cs +++ b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorRefreshQueue.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Threading; using Roslyn.LanguageServer.Protocol; using Roslyn.Utilities; using StreamJsonRpc; diff --git a/src/VisualStudio/Core/Def/ColorSchemes/ColorSchemeApplier.cs b/src/VisualStudio/Core/Def/ColorSchemes/ColorSchemeApplier.cs index b9ac22121c1fd..04e21d0fe143c 100644 --- a/src/VisualStudio/Core/Def/ColorSchemes/ColorSchemeApplier.cs +++ b/src/VisualStudio/Core/Def/ColorSchemes/ColorSchemeApplier.cs @@ -13,12 +13,12 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Threading; using Microsoft.VisualStudio; using Microsoft.VisualStudio.Settings; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Threading; -using Roslyn.Utilities; using Task = System.Threading.Tasks.Task; namespace Microsoft.CodeAnalysis.ColorSchemes; diff --git a/src/VisualStudio/Core/Def/DesignerAttribute/VisualStudioDesignerAttributeService.cs b/src/VisualStudio/Core/Def/DesignerAttribute/VisualStudioDesignerAttributeService.cs index 09e002ab96aa5..699bfc38552a4 100644 --- a/src/VisualStudio/Core/Def/DesignerAttribute/VisualStudioDesignerAttributeService.cs +++ b/src/VisualStudio/Core/Def/DesignerAttribute/VisualStudioDesignerAttributeService.cs @@ -20,6 +20,7 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Threading; using Microsoft.VisualStudio.Designer.Interfaces; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; using Microsoft.VisualStudio.Shell.Interop; diff --git a/src/VisualStudio/Core/Def/DocumentOutline/DocumentOutlineViewModel.cs b/src/VisualStudio/Core/Def/DocumentOutline/DocumentOutlineViewModel.cs index 439e12fccf8fa..02e1bffa43eb0 100644 --- a/src/VisualStudio/Core/Def/DocumentOutline/DocumentOutlineViewModel.cs +++ b/src/VisualStudio/Core/Def/DocumentOutline/DocumentOutlineViewModel.cs @@ -20,6 +20,7 @@ using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Threading; using Microsoft.CodeAnalysis.Utilities; using Microsoft.VisualStudio.LanguageServer.Client; using Microsoft.VisualStudio.LanguageServices.Utilities; diff --git a/src/VisualStudio/Core/Def/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs b/src/VisualStudio/Core/Def/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs index a4a0895b1616d..0a7c6416824b2 100644 --- a/src/VisualStudio/Core/Def/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs +++ b/src/VisualStudio/Core/Def/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs @@ -25,6 +25,7 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Threading; using Microsoft.VisualStudio.Shell.FindAllReferences; using Microsoft.VisualStudio.Shell.TableControl; using Microsoft.VisualStudio.Shell.TableManager; diff --git a/src/VisualStudio/Core/Def/KeybindingReset/KeybindingResetDetector.cs b/src/VisualStudio/Core/Def/KeybindingReset/KeybindingResetDetector.cs index e94d9c9579b6b..cd088e1f5d81c 100644 --- a/src/VisualStudio/Core/Def/KeybindingReset/KeybindingResetDetector.cs +++ b/src/VisualStudio/Core/Def/KeybindingReset/KeybindingResetDetector.cs @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Threading; using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities; using Microsoft.VisualStudio.OLE.Interop; using Microsoft.VisualStudio.PlatformUI.OleComponentSupport; diff --git a/src/VisualStudio/Core/Def/LanguageService/AbstractCreateServicesOnTextViewConnection.cs b/src/VisualStudio/Core/Def/LanguageService/AbstractCreateServicesOnTextViewConnection.cs index 629a09e27f115..01e7b4d0faf02 100644 --- a/src/VisualStudio/Core/Def/LanguageService/AbstractCreateServicesOnTextViewConnection.cs +++ b/src/VisualStudio/Core/Def/LanguageService/AbstractCreateServicesOnTextViewConnection.cs @@ -16,9 +16,9 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Snippets; +using Microsoft.CodeAnalysis.Threading; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService; diff --git a/src/VisualStudio/Core/Def/Packaging/PackageInstallerServiceFactory.cs b/src/VisualStudio/Core/Def/Packaging/PackageInstallerServiceFactory.cs index a602ba3b033f2..9e2c2a3b16ae2 100644 --- a/src/VisualStudio/Core/Def/Packaging/PackageInstallerServiceFactory.cs +++ b/src/VisualStudio/Core/Def/Packaging/PackageInstallerServiceFactory.cs @@ -26,6 +26,7 @@ using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.SymbolSearch; +using Microsoft.CodeAnalysis.Threading; using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; using Microsoft.VisualStudio.LanguageServices.SymbolSearch; diff --git a/src/VisualStudio/Core/Def/PdbSourceDocument/PdbSourceDocumentOutputWindowLogger.cs b/src/VisualStudio/Core/Def/PdbSourceDocument/PdbSourceDocumentOutputWindowLogger.cs index 7a880dd52a724..9992c08ca693c 100644 --- a/src/VisualStudio/Core/Def/PdbSourceDocument/PdbSourceDocumentOutputWindowLogger.cs +++ b/src/VisualStudio/Core/Def/PdbSourceDocument/PdbSourceDocumentOutputWindowLogger.cs @@ -13,9 +13,9 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PdbSourceDocument; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Threading; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.PdbSourceDocument; diff --git a/src/VisualStudio/Core/Def/Progression/GraphQueryManager.cs b/src/VisualStudio/Core/Def/Progression/GraphQueryManager.cs index 034074da3a89b..d9bcc816f68cf 100644 --- a/src/VisualStudio/Core/Def/Progression/GraphQueryManager.cs +++ b/src/VisualStudio/Core/Def/Progression/GraphQueryManager.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Threading; using Microsoft.VisualStudio.GraphModel; using Roslyn.Utilities; diff --git a/src/VisualStudio/Core/Def/ProjectSystem/FileChangeWatcher.cs b/src/VisualStudio/Core/Def/ProjectSystem/FileChangeWatcher.cs index c4c5124e4bf76..d25c1736e9826 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/FileChangeWatcher.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/FileChangeWatcher.cs @@ -14,6 +14,7 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.ProjectSystem; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Threading; using Microsoft.VisualStudio.Shell.Interop; using Roslyn.Utilities; using IVsAsyncFileChangeEx2 = Microsoft.VisualStudio.Shell.IVsAsyncFileChangeEx2; diff --git a/src/VisualStudio/Core/Def/SymbolSearch/AbstractDelayStartedService.cs b/src/VisualStudio/Core/Def/SymbolSearch/AbstractDelayStartedService.cs index df77fde5013ba..73455ee6880d2 100644 --- a/src/VisualStudio/Core/Def/SymbolSearch/AbstractDelayStartedService.cs +++ b/src/VisualStudio/Core/Def/SymbolSearch/AbstractDelayStartedService.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Threading; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; using Roslyn.Utilities; diff --git a/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs b/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs index e370f5eee08c0..eb8d2f0c68786 100644 --- a/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs +++ b/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs @@ -19,6 +19,7 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Threading; using Microsoft.ServiceHub.Framework; using Microsoft.VisualStudio.RpcContracts.DiagnosticManagement; using Microsoft.VisualStudio.RpcContracts.Utilities; diff --git a/src/VisualStudio/Core/Def/Telemetry/FileLogger.cs b/src/VisualStudio/Core/Def/Telemetry/FileLogger.cs index 916429085c48e..92187c85c6a8b 100644 --- a/src/VisualStudio/Core/Def/Telemetry/FileLogger.cs +++ b/src/VisualStudio/Core/Def/Telemetry/FileLogger.cs @@ -17,6 +17,7 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Shared.Utilities; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Telemetry; diff --git a/src/VisualStudio/Core/Def/Workspace/SourceGeneratedFileManager.cs b/src/VisualStudio/Core/Def/Workspace/SourceGeneratedFileManager.cs index 0af0d25e5fda4..2f9c4cde491f2 100644 --- a/src/VisualStudio/Core/Def/Workspace/SourceGeneratedFileManager.cs +++ b/src/VisualStudio/Core/Def/Workspace/SourceGeneratedFileManager.cs @@ -18,6 +18,7 @@ using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; +using Microsoft.CodeAnalysis.Threading; using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.Imaging; using Microsoft.VisualStudio.Imaging.Interop; diff --git a/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs b/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs index e27de36eb5a39..c1a74289d86d3 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs @@ -17,6 +17,7 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Threading; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Threading; using Roslyn.Utilities; diff --git a/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerItem/AnalyzerItemSource.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerItem/AnalyzerItemSource.cs index 7db53244947e0..0cf1db496727d 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerItem/AnalyzerItemSource.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerItem/AnalyzerItemSource.cs @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.SourceGeneration; +using Microsoft.CodeAnalysis.Threading; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Shell; using Roslyn.Utilities; diff --git a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/BaseDiagnosticAndGeneratorItemSource.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/BaseDiagnosticAndGeneratorItemSource.cs index 09c14500e946e..2197f03fa4d48 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/BaseDiagnosticAndGeneratorItemSource.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/BaseDiagnosticAndGeneratorItemSource.cs @@ -15,6 +15,7 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.SourceGeneration; +using Microsoft.CodeAnalysis.Threading; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Shell; using Roslyn.Utilities; diff --git a/src/VisualStudio/Core/Impl/SolutionExplorer/SourceGeneratedFileItems/SourceGeneratedFileItemSource.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/SourceGeneratedFileItems/SourceGeneratedFileItemSource.cs index 96bc24025d51c..c667b11bce123 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/SourceGeneratedFileItems/SourceGeneratedFileItemSource.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/SourceGeneratedFileItems/SourceGeneratedFileItemSource.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Threading; using Microsoft.Internal.VisualStudio.PlatformUI; using Microsoft.VisualStudio.Language.Intellisense; using Roslyn.Utilities; diff --git a/src/VisualStudio/Core/Test.Next/Services/AssetProviderTests.cs b/src/VisualStudio/Core/Test.Next/Services/AssetProviderTests.cs index 06f61fddcb5f2..b9908670866a8 100644 --- a/src/VisualStudio/Core/Test.Next/Services/AssetProviderTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/AssetProviderTests.cs @@ -17,6 +17,7 @@ using Microsoft.CodeAnalysis.Remote.Testing; using Microsoft.CodeAnalysis.Serialization; using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; using Xunit; diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/AutomationElementHelper.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/AutomationElementHelper.cs index e00ced80c3da6..6e4e2f37b90bd 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/AutomationElementHelper.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/AutomationElementHelper.cs @@ -4,6 +4,7 @@ using System; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; using Roslyn.VisualStudio.IntegrationTests.InProcess; using UIAutomationClient; diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfoCacheService.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfoCacheService.cs index 79fc5f5b76625..cb4122d233d18 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfoCacheService.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfoCacheService.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindSymbols.SymbolTree; diff --git a/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj index 7ddf1ce681c17..4af3832981b4e 100644 --- a/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj @@ -165,4 +165,5 @@ + diff --git a/src/Workspaces/Core/Portable/Notification/AbstractGlobalOperationNotificationService.cs b/src/Workspaces/Core/Portable/Notification/AbstractGlobalOperationNotificationService.cs index 1256b2fa12588..c8a88b0ac843d 100644 --- a/src/Workspaces/Core/Portable/Notification/AbstractGlobalOperationNotificationService.cs +++ b/src/Workspaces/Core/Portable/Notification/AbstractGlobalOperationNotificationService.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Notification; diff --git a/src/Workspaces/Core/Portable/Shared/TestHooks/TaskExtensions.cs b/src/Workspaces/Core/Portable/Shared/TestHooks/TaskExtensions.cs index 5db50f3c9a6a9..61a50ca6b3f81 100644 --- a/src/Workspaces/Core/Portable/Shared/TestHooks/TaskExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/TestHooks/TaskExtensions.cs @@ -4,8 +4,8 @@ using System; using System.Diagnostics.CodeAnalysis; -using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Shared.TestHooks; diff --git a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage.cs b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage.cs index 9ea33b38eff0f..d39132afee723 100644 --- a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage.cs +++ b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage.cs @@ -14,6 +14,7 @@ using Microsoft.CodeAnalysis.SQLite.Interop; using Microsoft.CodeAnalysis.SQLite.v2.Interop; using Microsoft.CodeAnalysis.Storage; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.SQLite.v2; diff --git a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_FlushWrites.cs b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_FlushWrites.cs index a796a791df4ef..1021f3f6e872e 100644 --- a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_FlushWrites.cs +++ b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_FlushWrites.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.SQLite.v2; diff --git a/src/Workspaces/Core/Portable/TemporaryStorage/TemporaryStorageService.cs b/src/Workspaces/Core/Portable/TemporaryStorage/TemporaryStorageService.cs index adf3210139dbd..259db7cc54e15 100644 --- a/src/Workspaces/Core/Portable/TemporaryStorage/TemporaryStorageService.cs +++ b/src/Workspaces/Core/Portable/TemporaryStorage/TemporaryStorageService.cs @@ -15,6 +15,7 @@ using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Host; diff --git a/src/Workspaces/Core/Portable/Workspace/IsolatedAnalyzerFileReference.cs b/src/Workspaces/Core/Portable/Workspace/IsolatedAnalyzerFileReference.cs index 6db69d9a65935..614c890088fa6 100644 --- a/src/Workspaces/Core/Portable/Workspace/IsolatedAnalyzerFileReference.cs +++ b/src/Workspaces/Core/Portable/Workspace/IsolatedAnalyzerFileReference.cs @@ -7,9 +7,9 @@ using System; using System.Collections.Immutable; using System.Runtime.CompilerServices; -using Microsoft.CodeAnalysis.Diagnostics; using System.Runtime.Loader; -using Roslyn.Utilities; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Threading; namespace Microsoft.CodeAnalysis; diff --git a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/FileWatchedPortableExecutableReferenceFactory.cs b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/FileWatchedPortableExecutableReferenceFactory.cs index 66880110e594b..f68e3fa6861e7 100644 --- a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/FileWatchedPortableExecutableReferenceFactory.cs +++ b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/FileWatchedPortableExecutableReferenceFactory.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ProjectSystem; diff --git a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProject.cs b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProject.cs index aa75a5cd9f367..11133139e4f0b 100644 --- a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProject.cs +++ b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProject.cs @@ -22,6 +22,7 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Telemetry; using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; using static Microsoft.CodeAnalysis.Workspaces.ProjectSystem.ProjectSystemProjectFactory; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.RegularCompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.RegularCompilationTracker.cs index f63b4425e06ab..13d81e8ee9fa9 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.RegularCompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.RegularCompilationTracker.cs @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs index 32d50e7f8e9fd..dfb473ea9a080 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Utilities; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs index 0c403bf2167b6..42a42b6becaad 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs @@ -19,6 +19,7 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; using ReferenceEqualityComparer = Roslyn.Utilities.ReferenceEqualityComparer; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/VersionSource/RecoverableTextAndVersion.RecoverableText.cs b/src/Workspaces/Core/Portable/Workspace/Solution/VersionSource/RecoverableTextAndVersion.RecoverableText.cs index f8ded8d24ae96..6997357e85bf7 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/VersionSource/RecoverableTextAndVersion.RecoverableText.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/VersionSource/RecoverableTextAndVersion.RecoverableText.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace.cs b/src/Workspaces/Core/Portable/Workspace/Workspace.cs index 2e71d615b880c..3c7117483f2af 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace.cs @@ -21,6 +21,7 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace_SourceGeneration.cs b/src/Workspaces/Core/Portable/Workspace/Workspace_SourceGeneration.cs index 2de8d674b6024..e4bdc6b38c9a0 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace_SourceGeneration.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace_SourceGeneration.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; diff --git a/src/Workspaces/CoreTest/UtilityTest/AsyncLazyTests.cs b/src/Workspaces/CoreTest/UtilityTest/AsyncLazyTests.cs index ff97ddfac212b..417597ee5f63c 100644 --- a/src/Workspaces/CoreTest/UtilityTest/AsyncLazyTests.cs +++ b/src/Workspaces/CoreTest/UtilityTest/AsyncLazyTests.cs @@ -5,8 +5,8 @@ using System; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; diff --git a/src/Workspaces/CoreTest/UtilityTest/CancellationSeriesTests.cs b/src/Workspaces/CoreTest/UtilityTest/CancellationSeriesTests.cs index b34fddf770451..2634f592348ee 100644 --- a/src/Workspaces/CoreTest/UtilityTest/CancellationSeriesTests.cs +++ b/src/Workspaces/CoreTest/UtilityTest/CancellationSeriesTests.cs @@ -11,6 +11,7 @@ using System; using System.Linq; using System.Threading; +using Microsoft.CodeAnalysis.Threading; using Xunit; namespace Roslyn.Utilities diff --git a/src/Workspaces/Remote/Core/AbstractAssetProviderExtensions.cs b/src/Workspaces/Remote/Core/AbstractAssetProviderExtensions.cs index b58173f4c6138..2739d0b762650 100644 --- a/src/Workspaces/Remote/Core/AbstractAssetProviderExtensions.cs +++ b/src/Workspaces/Remote/Core/AbstractAssetProviderExtensions.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Serialization; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote; diff --git a/src/Workspaces/Remote/Core/SolutionAssetStorage.Scope.cs b/src/Workspaces/Remote/Core/SolutionAssetStorage.Scope.cs index 845f823512bf4..0dd69213db851 100644 --- a/src/Workspaces/Remote/Core/SolutionAssetStorage.Scope.cs +++ b/src/Workspaces/Remote/Core/SolutionAssetStorage.Scope.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Serialization; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote; diff --git a/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/EditAndContinueLogReporter.cs b/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/EditAndContinueLogReporter.cs index d00166c5ccccf..451a80dd1ab58 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/EditAndContinueLogReporter.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/EditAndContinueLogReporter.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.BrokeredServices; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Threading; using Microsoft.VisualStudio.Debugger.Contracts.HotReload; using Roslyn.Utilities; diff --git a/src/Workspaces/Remote/ServiceHub/Services/NavigateToSearch/RemoteNavigateToSearchService.cs b/src/Workspaces/Remote/ServiceHub/Services/NavigateToSearch/RemoteNavigateToSearchService.cs index a22cacfb58b09..e9599fcfe2ede 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/NavigateToSearch/RemoteNavigateToSearchService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/NavigateToSearch/RemoteNavigateToSearchService.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.NavigateTo; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Storage; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote; diff --git a/src/Workspaces/Remote/ServiceHub/Services/ProcessTelemetry/RemoteProcessTelemetryService.PerformanceReporter.cs b/src/Workspaces/Remote/ServiceHub/Services/ProcessTelemetry/RemoteProcessTelemetryService.PerformanceReporter.cs index 12a60c27203f5..3200e4d81149e 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/ProcessTelemetry/RemoteProcessTelemetryService.PerformanceReporter.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/ProcessTelemetry/RemoteProcessTelemetryService.PerformanceReporter.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Remote.Diagnostics; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Threading; using Microsoft.VisualStudio.Telemetry; using Roslyn.Utilities; using RoslynLogger = Microsoft.CodeAnalysis.Internal.Log.Logger; diff --git a/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.Caching.cs b/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.Caching.cs index 4374242bb0683..19df6b40c64ea 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.Caching.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.Caching.cs @@ -17,6 +17,7 @@ using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Storage; using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems index 56d65226cc0ea..cebfc2a68264f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems @@ -15,7 +15,6 @@ - @@ -55,8 +54,6 @@ - - @@ -493,9 +490,6 @@ - - - diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/AsyncLazy`1.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/AsyncLazy`1.cs index d1443088d9025..d3de9e77f609f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/AsyncLazy`1.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/AsyncLazy`1.cs @@ -1,13 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. - using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.Threading; namespace Roslyn.Utilities; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ProducerConsumer.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ProducerConsumer.cs index a4b3e0854ad7b..99c936f6700f8 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ProducerConsumer.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ProducerConsumer.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Shared.Utilities; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/RoslynParallel.NetFramework.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/RoslynParallel.NetFramework.cs index dd626976d8108..6f12759f5ce86 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/RoslynParallel.NetFramework.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/RoslynParallel.NetFramework.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -// - #if !NET #pragma warning disable CA1068 // CancellationToken parameters must come last @@ -12,6 +10,8 @@ #pragma warning disable IDE2004 // Blank line not allowed after constructor initializer colon #pragma warning disable VSTHRD200 // Use "Async" suffix for async methods +#nullable disable + // Ported from // https://github.com/dotnet/runtime/blob/main/src/libraries/System.Threading.Tasks.Parallel/src/System/Threading/Tasks/Parallel.ForEachAsync.cs // With only changes to make the code work on NetFx. Where changes have been made, the original code is kept around in @@ -22,6 +22,7 @@ using System.Diagnostics; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Shared.Utilities; From 3d2e17b36dcc0a1f5a0469bb04a1e39dc1240c1f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Feb 2025 12:57:59 -0800 Subject: [PATCH 064/305] Search related type sections in other documents when doing a document-filtered nav to search --- ...aryPragmaSuppressionsDiagnosticAnalyzer.cs | 6 +- .../NavigateTo/NavigateToSearcherTests.cs | 149 ++++++++++++++++++ .../GoToAdjacentMemberCommandHandler.cs | 7 +- .../CSharpNavigateToSearchService.cs | 4 +- ...actNavigateToSearchService.NormalSearch.cs | 58 +++++-- .../VisualBasicNavigateToSearchService.vb | 2 +- ...lAnalyzer.IncrementalMemberEditAnalyzer.cs | 5 +- ...crementalMemberEditAnalyzer_MemberSpans.cs | 5 +- .../RemoteNavigateToSearchService.cs | 4 +- .../SyntaxFacts/VisualBasicSyntaxFacts.vb | 15 +- 10 files changed, 218 insertions(+), 37 deletions(-) diff --git a/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/AbstractRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/AbstractRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer.cs index e52174131c908..d33129015ca0f 100644 --- a/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/AbstractRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/AbstractRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer.cs @@ -745,10 +745,10 @@ private async Task ProcessSuppressMessageAttributesAsync( return false; } - using var pooledDeclarationNodes = SyntaxFacts.GetTopLevelAndMethodLevelMembers(root); - var declarationNodes = pooledDeclarationNodes.Object; + using var _1 = ArrayBuilder.GetInstance(out var declarationNodes); + this.SyntaxFacts.AddTopLevelAndMethodLevelMembers(root, declarationNodes); - using var _ = PooledHashSet.GetInstance(out var processedPartialSymbols); + using var _2 = PooledHashSet.GetInstance(out var processedPartialSymbols); if (declarationNodes.Count > 0) { foreach (var node in declarationNodes) diff --git a/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToSearcherTests.cs b/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToSearcherTests.cs index 8b2080aeaf537..fcf9c6e1c08da 100644 --- a/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToSearcherTests.cs +++ b/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToSearcherTests.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Concurrent; using System.Collections.Immutable; using System.Linq; using System.Threading; @@ -12,6 +13,7 @@ using Microsoft.CodeAnalysis.NavigateTo; using Microsoft.CodeAnalysis.Navigation; using Microsoft.CodeAnalysis.PatternMatching; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Moq; @@ -354,6 +356,153 @@ public class D Assert.True(searchGeneratedDocumentsAsyncCalled); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77051")] + public async Task DocumentScopeRelatedDocuments_Inheritance() + { + using var workspace = EditorTestWorkspace.Create(""" + + + + public class C : Base + { + // Starting search here. + void Goo1() { } + } + + + public class Base + { + // Should find this. + void Goo2() { } + } + public class Other + { + // Should not find this. + void Goo3() { } + } + + + + """, composition: FirstActiveAndVisibleComposition); + + var hostMock = new Mock(MockBehavior.Strict); + hostMock.Setup(h => h.IsFullyLoadedAsync(It.IsAny())).Returns(() => new ValueTask(true)); + + var project = workspace.CurrentSolution.Projects.Single(); + var searchService = project.GetRequiredLanguageService(); + + // Ensure that returning null for the search service doesn't crash. + hostMock.Setup(h => h.GetNavigateToSearchService(It.IsAny())).Returns(() => searchService); + + var callback = new TestNavigateToSearchCallback(); + + var searcher = NavigateToSearcher.Create( + workspace.CurrentSolution, + callback, + "Goo", + kinds: searchService.KindsProvided, + hostMock.Object); + + await searcher.SearchAsync(NavigateToSearchScope.Document, CancellationToken.None); + + Assert.Equal(2, callback.Results.Count); + + var firstDocument = project.Documents.Single(d => d.FilePath!.Contains("file1")); + var secondDocument = project.Documents.Single(d => d.FilePath!.Contains("file2")); + + var firstDocumentResult = Assert.Single(callback.Results, r => r.NavigableItem.Document.Id == firstDocument.Id); + var secondDocumentResult = Assert.Single(callback.Results, r => r.NavigableItem.Document.Id == secondDocument.Id); + + Assert.Equal("Goo1", firstDocumentResult.Name); + Assert.Equal("Goo2", secondDocumentResult.Name); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77051")] + public async Task DocumentScopeRelatedDocuments_Partial() + { + using var workspace = EditorTestWorkspace.Create(""" + + + + public partial class C + { + // Starting search here. + void Goo1() { } + } + + + public class Base + { + // Should not find this. + void Goo2() { } + } + public partial class C + { + // Should find this. + void Goo3() { } + } + + + + """, composition: FirstActiveAndVisibleComposition); + + var hostMock = new Mock(MockBehavior.Strict); + hostMock.Setup(h => h.IsFullyLoadedAsync(It.IsAny())).Returns(() => new ValueTask(true)); + + var project = workspace.CurrentSolution.Projects.Single(); + var searchService = project.GetRequiredLanguageService(); + + // Ensure that returning null for the search service doesn't crash. + hostMock.Setup(h => h.GetNavigateToSearchService(It.IsAny())).Returns(() => searchService); + + var callback = new TestNavigateToSearchCallback(); + + var searcher = NavigateToSearcher.Create( + workspace.CurrentSolution, + callback, + "Goo", + kinds: searchService.KindsProvided, + hostMock.Object); + + await searcher.SearchAsync(NavigateToSearchScope.Document, CancellationToken.None); + + Assert.Equal(2, callback.Results.Count); + + var firstDocument = project.Documents.Single(d => d.FilePath!.Contains("file1")); + var secondDocument = project.Documents.Single(d => d.FilePath!.Contains("file2")); + + var firstDocumentResult = Assert.Single(callback.Results, r => r.NavigableItem.Document.Id == firstDocument.Id); + var secondDocumentResult = Assert.Single(callback.Results, r => r.NavigableItem.Document.Id == secondDocument.Id); + + Assert.Equal("Goo1", firstDocumentResult.Name); + Assert.Equal("Goo3", secondDocumentResult.Name); + } + + private sealed class TestNavigateToSearchCallback : INavigateToSearchCallback + { + public readonly ConcurrentBag Results = []; + + public void Done(bool isFullyLoaded) + { + } + + public void ReportIncomplete() + { + } + + public Task AddResultsAsync(ImmutableArray results, CancellationToken cancellationToken) + { + foreach (var result in results) + this.Results.Add(result); + + return Task.CompletedTask; + } + + public void ReportProgress(int current, int maximum) + { + } + } + private sealed class MockAdvancedNavigateToSearchService : IAdvancedNavigateToSearchService { public IImmutableSet KindsProvided => AbstractNavigateToSearchService.AllKinds; diff --git a/src/EditorFeatures/Core/Editor/GoToAdjacentMemberCommandHandler.cs b/src/EditorFeatures/Core/Editor/GoToAdjacentMemberCommandHandler.cs index 923049a4c1d8b..adae30a2f34da 100644 --- a/src/EditorFeatures/Core/Editor/GoToAdjacentMemberCommandHandler.cs +++ b/src/EditorFeatures/Core/Editor/GoToAdjacentMemberCommandHandler.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Commanding; @@ -103,12 +104,10 @@ private bool ExecuteCommandImpl(EditorCommandArgs args, bool gotoNextMember, Com /// internal static int? GetTargetPosition(ISyntaxFactsService service, SyntaxNode root, int caretPosition, bool next) { - using var pooledMembers = service.GetMethodLevelMembers(root); - var members = pooledMembers.Object; + using var _ = ArrayBuilder.GetInstance(out var members); + service.AddMethodLevelMembers(root, members); if (members.Count == 0) - { return null; - } var starts = members.Select(m => MemberStart(m)).ToArray(); var index = Array.BinarySearch(starts, caretPosition); diff --git a/src/Features/CSharp/Portable/NavigateTo/CSharpNavigateToSearchService.cs b/src/Features/CSharp/Portable/NavigateTo/CSharpNavigateToSearchService.cs index 1ba731b48c9ce..bece1c625bb40 100644 --- a/src/Features/CSharp/Portable/NavigateTo/CSharpNavigateToSearchService.cs +++ b/src/Features/CSharp/Portable/NavigateTo/CSharpNavigateToSearchService.cs @@ -12,6 +12,4 @@ namespace Microsoft.CodeAnalysis.CSharp.NavigateTo; [ExportLanguageService(typeof(INavigateToSearchService), LanguageNames.CSharp), Shared] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal class CSharpNavigateToSearchService() : AbstractNavigateToSearchService -{ -} +internal sealed class CSharpNavigateToSearchService() : AbstractNavigateToSearchService; diff --git a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs index f9733ec8a0889..9be57517c5186 100644 --- a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs +++ b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs @@ -12,8 +12,10 @@ using Microsoft.CodeAnalysis.PatternMatching; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Shared; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; +using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.NavigateTo; @@ -47,7 +49,7 @@ await client.TryInvokeAsync( await SearchDocumentAndRelatedDocumentsInCurrentProcessAsync(document, searchPattern, kinds, onItemsFound, cancellationToken).ConfigureAwait(false); } - public static Task SearchDocumentAndRelatedDocumentsInCurrentProcessAsync( + public static async Task SearchDocumentAndRelatedDocumentsInCurrentProcessAsync( Document document, string searchPattern, IImmutableSet kinds, @@ -57,17 +59,30 @@ public static Task SearchDocumentAndRelatedDocumentsInCurrentProcessAsync( var (patternName, patternContainerOpt) = PatternMatcher.GetNameAndContainer(searchPattern); var declaredSymbolInfoKindsSet = new DeclaredSymbolInfoKindSet(kinds); - // In parallel, search both the document requested, and any relevant 'related documents' we find for it. + // In parallel, search both the document requested, and any relevant 'related documents' we find for it. For the + // original document, search the entirety of it. For related documents, only search the spans of the + // partial-types/inheriting-types that we find for the types in this starting document. + var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - return Task.WhenAll( - SearchDocumentsInCurrentProcessAsync([document]), - SearchRelatedDocumentsInCurrentProcessAsync()); + await Task.WhenAll( + SearchDocumentsInCurrentProcessAsync([(document, new NormalizedTextSpanCollection(new TextSpan(0, text.Length)))]), + SearchRelatedDocumentsInCurrentProcessAsync()).ConfigureAwait(false); - Task SearchDocumentsInCurrentProcessAsync(ImmutableArray documents) + Task SearchDocumentsInCurrentProcessAsync(ImmutableArray<(Document document, NormalizedTextSpanCollection spans)> documentAndSpans) => ProducerConsumer.RunParallelAsync( - documents, - async (document, onItemFound, args, cancellationToken) => await SearchSingleDocumentAsync( - document, patternName, patternContainerOpt, declaredSymbolInfoKindsSet, onItemFound, cancellationToken).ConfigureAwait(false), + documentAndSpans, + async (documentAndSpan, onItemFound, args, cancellationToken) => await SearchSingleDocumentAsync( + documentAndSpan.document, patternName, patternContainerOpt, declaredSymbolInfoKindsSet, + item => + { + // Ensure that the results found while searching the single document intersect the desired + // subrange of the document we're searching in. For the primary document this will always + // succeed (since we're searching the full document). But for related documents this may fail + // if the results is not in the span of any of the types in those files we're searching. + if (documentAndSpan.spans.IntersectsWith(item.DeclaredSymbolInfo.Span)) + onItemFound(item); + }, + cancellationToken).ConfigureAwait(false), onItemsFound, args: default, cancellationToken); @@ -78,23 +93,26 @@ async Task SearchRelatedDocumentsInCurrentProcessAsync() await SearchDocumentsInCurrentProcessAsync(relatedDocuments).ConfigureAwait(false); } - async Task> GetRelatedDocumentsAsync() + async Task> GetRelatedDocumentsAsync() { // For C#/VB we define 'related documents' as those containing types in the inheritance chain of types in // the originating file (as well as all partial parts of the original and inheritance types). This way a // user can search for symbols scoped to the 'current document' and still get results for the members found - // in partial parts + // in partial parts. var solution = document.Project.Solution; var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var semanticModel = await document.GetRequiredNullableDisabledSemanticModelAsync(cancellationToken).ConfigureAwait(false); var syntaxFacts = document.GetRequiredLanguageService(); - using var _1 = ArrayBuilder.GetInstance(out var topLevelNodes); - using var _2 = PooledHashSet.GetInstance(out var relatedDocuments); - + using var _ = ArrayBuilder.GetInstance(out var topLevelNodes); syntaxFacts.AddTopLevelMembers(root, topLevelNodes); + // Keep track of all of the interesting spans in each document we find. Note: we will convert this to a + // NormalizedTextSpanCollection before returning it. That way the span of an outer partial type will + // encompass the span of an inner one and we won't get duplicates for the same symbol. + var documentToTextSpans = new MultiDictionary(); + foreach (var topLevelMember in topLevelNodes) { if (semanticModel.GetDeclaredSymbol(topLevelMember, cancellationToken) is not INamedTypeSymbol namedTypeSymbol) @@ -103,13 +121,19 @@ async Task> GetRelatedDocumentsAsync() foreach (var type in namedTypeSymbol.GetBaseTypesAndThis()) { foreach (var reference in type.DeclaringSyntaxReferences) - relatedDocuments.AddIfNotNull(solution.GetDocument(reference.SyntaxTree)); + { + var relatedDocument = solution.GetDocument(reference.SyntaxTree); + if (relatedDocument is null) + continue; + + documentToTextSpans.Add(relatedDocument, reference.Span); + } } } // Ensure we don't search the original document we were already searching. - relatedDocuments.Remove(document); - return [.. relatedDocuments]; + documentToTextSpans.Remove(document); + return documentToTextSpans.SelectAsArray(kvp => (kvp.Key, new NormalizedTextSpanCollection(kvp.Value))); } } diff --git a/src/Features/VisualBasic/Portable/NavigateTo/VisualBasicNavigateToSearchService.vb b/src/Features/VisualBasic/Portable/NavigateTo/VisualBasicNavigateToSearchService.vb index 6e2901f52da73..5ba3943fdc734 100644 --- a/src/Features/VisualBasic/Portable/NavigateTo/VisualBasicNavigateToSearchService.vb +++ b/src/Features/VisualBasic/Portable/NavigateTo/VisualBasicNavigateToSearchService.vb @@ -8,7 +8,7 @@ Imports Microsoft.CodeAnalysis.NavigateTo Namespace Microsoft.CodeAnalysis.VisualBasic.NavigateTo - Friend Class VisualBasicNavigateToSearchService + Friend NotInheritable Class VisualBasicNavigateToSearchService Inherits AbstractNavigateToSearchService diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs index 9a075257bb932..8c0aa63811599 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs @@ -201,8 +201,9 @@ async Task ExecuteAnalyzersAsync( } var syntaxFacts = document.GetRequiredLanguageService(); - using var pooledMembers = syntaxFacts.GetMethodLevelMembers(root); - var members = pooledMembers.Object; + + using var _ = ArrayBuilder.GetInstance(out var members); + syntaxFacts.AddMethodLevelMembers(root, members); var memberSpans = members.SelectAsArray(member => member.FullSpan); var changedMemberId = members.IndexOf(changedMember); diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer_MemberSpans.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer_MemberSpans.cs index c76750e900a4c..893bc071d1e99 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer_MemberSpans.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer_MemberSpans.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -47,8 +48,8 @@ static async Task> CreateMemberSpansAsync(Document docu var service = document.GetRequiredLanguageService(); var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - using var pooledMembers = service.GetMethodLevelMembers(root); - var members = pooledMembers.Object; + using var _ = ArrayBuilder.GetInstance(out var members); + service.AddMethodLevelMembers(root, members); return members.SelectAsArray(m => m.FullSpan); } diff --git a/src/Workspaces/Remote/ServiceHub/Services/NavigateToSearch/RemoteNavigateToSearchService.cs b/src/Workspaces/Remote/ServiceHub/Services/NavigateToSearch/RemoteNavigateToSearchService.cs index a22cacfb58b09..c03cdb90d64ff 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/NavigateToSearch/RemoteNavigateToSearchService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/NavigateToSearch/RemoteNavigateToSearchService.cs @@ -44,7 +44,7 @@ public ValueTask HydrateAsync(Checksum solutionChecksum, CancellationToken cance return RunServiceAsync(solutionChecksum, solution => ValueTaskFactory.CompletedTask, cancellationToken); } - public ValueTask SearchDocumentAsync( + public ValueTask SearchDocumentAndRelatedDocumentsAsync( Checksum solutionChecksum, DocumentId documentId, string searchPattern, @@ -57,7 +57,7 @@ public ValueTask SearchDocumentAsync( var document = solution.GetRequiredDocument(documentId); var (onItemsFound, onProjectCompleted) = GetCallbacks(callbackId, cancellationToken); - await AbstractNavigateToSearchService.SearchDocumentInCurrentProcessAsync( + await AbstractNavigateToSearchService.SearchDocumentAndRelatedDocumentsInCurrentProcessAsync( document, searchPattern, kinds.ToImmutableHashSet(), onItemsFound, cancellationToken).ConfigureAwait(false); }, cancellationToken); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb index c414cd2de2103..7b2795760a82b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb @@ -28,9 +28,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService Public Shared ReadOnly Property Instance As New VisualBasicSyntaxFacts - ' Specifies false for trimOnFree as these objects commonly exceed the default ObjectPool threshold - Private Shared ReadOnly s_syntaxNodeListPool As ObjectPool(Of List(Of SyntaxNode)) = New ObjectPool(Of List(Of SyntaxNode))(Function() New List(Of SyntaxNode), trimOnFree:=False) - Protected Sub New() End Sub @@ -1981,6 +1978,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService Return DirectCast(node, LiteralExpressionSyntax).Token End Function + Private Sub ISyntaxFacts_AddTopLevelAndMethodLevelMembers(root As SyntaxNode, result As ArrayBuilder(Of SyntaxNode)) Implements ISyntaxFacts.AddTopLevelAndMethodLevelMembers + AddTopLevelAndMethodLevelMembers(root, result) + End Sub + + Private Sub ISyntaxFacts_AddTopLevelMembers(root As SyntaxNode, result As ArrayBuilder(Of SyntaxNode)) Implements ISyntaxFacts.AddTopLevelMembers + AddTopLevelMembers(root, result) + End Sub + + Private Sub ISyntaxFacts_AddMethodLevelMembers(root As SyntaxNode, result As ArrayBuilder(Of SyntaxNode)) Implements ISyntaxFacts.AddMethodLevelMembers + AddMethodLevelMembers(root, result) + End Sub + #End Region End Class From 58092a9358bed3f5a1fd2859966993daff8d33d0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Feb 2025 13:21:43 -0800 Subject: [PATCH 065/305] Fix asserts --- .../DiagnosticIncrementalAnalyzer.InMemoryStorage.cs | 9 --------- .../Protocol/Handler/Diagnostics/ProjectOrDocumentId.cs | 4 +--- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InMemoryStorage.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InMemoryStorage.cs index fbd4ad6e013cb..4895a9c81c5aa 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InMemoryStorage.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InMemoryStorage.cs @@ -19,8 +19,6 @@ private static class InMemoryStorage public static bool TryGetValue(DiagnosticAnalyzer analyzer, (ProjectOrDocumentId key, string stateKey) key, out CacheEntry entry) { - AssertKey(key); - entry = default; return s_map.TryGetValue(analyzer, out var analyzerMap) && analyzerMap.TryGetValue(key, out entry); @@ -28,8 +26,6 @@ public static bool TryGetValue(DiagnosticAnalyzer analyzer, (ProjectOrDocumentId public static void Cache(DiagnosticAnalyzer analyzer, (ProjectOrDocumentId key, string stateKey) key, CacheEntry entry) { - AssertKey(key); - // add new cache entry var analyzerMap = s_map.GetOrAdd(analyzer, _ => new ConcurrentDictionary<(ProjectOrDocumentId key, string stateKey), CacheEntry>(concurrencyLevel: 2, capacity: 10)); analyzerMap[key] = entry; @@ -37,7 +33,6 @@ public static void Cache(DiagnosticAnalyzer analyzer, (ProjectOrDocumentId key, public static void Remove(DiagnosticAnalyzer analyzer, (ProjectOrDocumentId key, string stateKey) key) { - AssertKey(key); // remove the entry if (!s_map.TryGetValue(analyzer, out var analyzerMap)) { @@ -57,10 +52,6 @@ public static void DropCache(DiagnosticAnalyzer analyzer) // drop any cache related to given analyzer s_map.TryRemove(analyzer, out _); } - - // make sure key is either documentId or projectId - private static void AssertKey((object key, string stateKey) key) - => Contract.ThrowIfFalse(key.key is DocumentId or ProjectId); } // in memory cache entry diff --git a/src/LanguageServer/Protocol/Handler/Diagnostics/ProjectOrDocumentId.cs b/src/LanguageServer/Protocol/Handler/Diagnostics/ProjectOrDocumentId.cs index ac1f4f63653a6..e9924b7e75bc3 100644 --- a/src/LanguageServer/Protocol/Handler/Diagnostics/ProjectOrDocumentId.cs +++ b/src/LanguageServer/Protocol/Handler/Diagnostics/ProjectOrDocumentId.cs @@ -10,18 +10,16 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; /// Wrapper around project and document ids for convenience in caching diagnostic results and /// use in the /// -internal readonly struct ProjectOrDocumentId +internal readonly record struct ProjectOrDocumentId { /// /// Non-null if this represents a documentId. Used for equality comparisons. /// - [SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used for equality comparison.")] private readonly DocumentId? _documentId; /// /// Non-null if this represents a projectId. Used for equality comparisons. /// - [SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used for equality comparison.")] private readonly ProjectId? _projectId; public ProjectOrDocumentId(ProjectId projectId) From 1c056f7416d37bf00f0fef84ac635a568c4d3273 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Feb 2025 14:22:00 -0800 Subject: [PATCH 066/305] Apply suggestions from code review --- .../CSharpTest/NavigateTo/NavigateToSearcherTests.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToSearcherTests.cs b/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToSearcherTests.cs index fcf9c6e1c08da..636fe5c32e1f9 100644 --- a/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToSearcherTests.cs +++ b/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToSearcherTests.cs @@ -391,7 +391,6 @@ void Goo3() { } var project = workspace.CurrentSolution.Projects.Single(); var searchService = project.GetRequiredLanguageService(); - // Ensure that returning null for the search service doesn't crash. hostMock.Setup(h => h.GetNavigateToSearchService(It.IsAny())).Returns(() => searchService); var callback = new TestNavigateToSearchCallback(); @@ -452,7 +451,6 @@ void Goo3() { } var project = workspace.CurrentSolution.Projects.Single(); var searchService = project.GetRequiredLanguageService(); - // Ensure that returning null for the search service doesn't crash. hostMock.Setup(h => h.GetNavigateToSearchService(It.IsAny())).Returns(() => searchService); var callback = new TestNavigateToSearchCallback(); From 23b0bd3909acf1d07371966d03c470c1d6e63a58 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Feb 2025 14:28:33 -0800 Subject: [PATCH 067/305] Use null for simplicity --- ...bstractNavigateToSearchService.NormalSearch.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs index 91cc19bb89041..236f041cde978 100644 --- a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs +++ b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs @@ -61,15 +61,16 @@ public static async Task SearchDocumentAndRelatedDocumentsInCurrentProcessAsync( var declaredSymbolInfoKindsSet = new DeclaredSymbolInfoKindSet(kinds); // In parallel, search both the document requested, and any relevant 'related documents' we find for it. For the - // original document, search the entirety of it. For related documents, only search the spans of the - // partial-types/inheriting-types that we find for the types in this starting document. + // original document, search the entirety of it (by passing 'null' in for the 'spans' argument). For related + // documents, only search the spans of the partial-types/inheriting-types that we find for the types in this + // starting document. var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); await Task.WhenAll( - SearchDocumentsInCurrentProcessAsync([(document, new NormalizedTextSpanCollection(new TextSpan(0, text.Length)))]), + SearchDocumentsInCurrentProcessAsync([(document, spans: null)]), SearchRelatedDocumentsInCurrentProcessAsync()).ConfigureAwait(false); - Task SearchDocumentsInCurrentProcessAsync(ImmutableArray<(Document document, NormalizedTextSpanCollection spans)> documentAndSpans) + Task SearchDocumentsInCurrentProcessAsync(ImmutableArray<(Document document, NormalizedTextSpanCollection? spans)> documentAndSpans) => ProducerConsumer.RunParallelAsync( documentAndSpans, async (documentAndSpan, onItemFound, args, cancellationToken) => await SearchSingleDocumentAsync( @@ -80,7 +81,7 @@ Task SearchDocumentsInCurrentProcessAsync(ImmutableArray<(Document document, Nor // subrange of the document we're searching in. For the primary document this will always // succeed (since we're searching the full document). But for related documents this may fail // if the results is not in the span of any of the types in those files we're searching. - if (documentAndSpan.spans.IntersectsWith(item.DeclaredSymbolInfo.Span)) + if (documentAndSpan.spans is null || documentAndSpan.spans.IntersectsWith(item.DeclaredSymbolInfo.Span)) onItemFound(item); }, cancellationToken).ConfigureAwait(false), @@ -94,7 +95,7 @@ async Task SearchRelatedDocumentsInCurrentProcessAsync() await SearchDocumentsInCurrentProcessAsync(relatedDocuments).ConfigureAwait(false); } - async Task> GetRelatedDocumentsAsync() + async Task> GetRelatedDocumentsAsync() { // For C#/VB we define 'related documents' as those containing types in the inheritance chain of types in // the originating file (as well as all partial parts of the original and inheritance types). This way a @@ -134,7 +135,7 @@ async Task SearchRelatedDocumentsInCurrentProcessAsync() // Ensure we don't search the original document we were already searching. documentToTextSpans.Remove(document); - return documentToTextSpans.SelectAsArray(kvp => (kvp.Key, new NormalizedTextSpanCollection(kvp.Value))); + return documentToTextSpans.SelectAsArray(kvp => (kvp.Key, new NormalizedTextSpanCollection(kvp.Value)))!; } } From f3ef599b3ab3dfbfc3d8208c548691c75c8006fe Mon Sep 17 00:00:00 2001 From: "gel@microsoft.com" Date: Wed, 5 Feb 2025 14:29:35 -0800 Subject: [PATCH 068/305] Move Copilot EA project to reference Features layer instead of EditorFeatures --- Roslyn.sln | 14 +- ...otCodeAnalysisService.ReflectionWrapper.cs | 178 ------------------ .../ExternalAccess/Copilot/.editorconfig | 0 .../Analyzer/CopilotChecksumWrapper.cs | 2 +- .../Copilot/Analyzer/CopilotUtilities.cs | 0 ...xternalCSharpCopilotCodeAnalysisService.cs | 1 - .../ICSharpCopilotMapCodeService.cs | 0 .../AbstractCopilotCodeAnalysisService.cs | 0 .../CSharpCopilotCodeAnalysisService.cs | 32 ++-- .../CodeMapper/CopilotCSharpMapCodeService.cs | 0 .../CSharpCopilotRelatedDocumentsService.cs | 0 .../Copilot/InternalAPI.Shipped.txt | 0 .../Copilot/InternalAPI.Unshipped.txt | 0 ...CodeAnalysis.ExternalAccess.Copilot.csproj | 10 +- .../Copilot/PublicAPI.Shipped.txt | 0 .../Copilot/PublicAPI.Unshipped.txt | 0 .../ICopilotRelatedDocumentsService.cs | 0 .../VS.ExternalAPIs.Roslyn.Package.csproj | 2 +- .../Setup/Roslyn.VisualStudio.Setup.csproj | 2 +- 19 files changed, 25 insertions(+), 216 deletions(-) delete mode 100644 src/EditorFeatures/ExternalAccess/Copilot/Internal/Analyzer/CSharp/CSharpCopilotCodeAnalysisService.ReflectionWrapper.cs rename src/{EditorFeatures => Features}/ExternalAccess/Copilot/.editorconfig (100%) rename src/{EditorFeatures => Features}/ExternalAccess/Copilot/Analyzer/CopilotChecksumWrapper.cs (92%) rename src/{EditorFeatures => Features}/ExternalAccess/Copilot/Analyzer/CopilotUtilities.cs (100%) rename src/{EditorFeatures => Features}/ExternalAccess/Copilot/Analyzer/IExternalCSharpCopilotCodeAnalysisService.cs (99%) rename src/{EditorFeatures => Features}/ExternalAccess/Copilot/CodeMapper/ICSharpCopilotMapCodeService.cs (100%) rename src/{EditorFeatures => Features}/ExternalAccess/Copilot/Internal/Analyzer/AbstractCopilotCodeAnalysisService.cs (100%) rename src/{EditorFeatures => Features}/ExternalAccess/Copilot/Internal/Analyzer/CSharp/CSharpCopilotCodeAnalysisService.cs (65%) rename src/{EditorFeatures => Features}/ExternalAccess/Copilot/Internal/CodeMapper/CopilotCSharpMapCodeService.cs (100%) rename src/{EditorFeatures => Features}/ExternalAccess/Copilot/Internal/RelatedDocuments/CSharpCopilotRelatedDocumentsService.cs (100%) rename src/{EditorFeatures => Features}/ExternalAccess/Copilot/InternalAPI.Shipped.txt (100%) rename src/{EditorFeatures => Features}/ExternalAccess/Copilot/InternalAPI.Unshipped.txt (100%) rename src/{EditorFeatures => Features}/ExternalAccess/Copilot/Microsoft.CodeAnalysis.ExternalAccess.Copilot.csproj (78%) rename src/{EditorFeatures => Features}/ExternalAccess/Copilot/PublicAPI.Shipped.txt (100%) rename src/{EditorFeatures => Features}/ExternalAccess/Copilot/PublicAPI.Unshipped.txt (100%) rename src/{EditorFeatures => Features}/ExternalAccess/Copilot/RelatedDocuments/ICopilotRelatedDocumentsService.cs (100%) diff --git a/Roslyn.sln b/Roslyn.sln index c49637445381e..47142e59b229d 100644 --- a/Roslyn.sln +++ b/Roslyn.sln @@ -535,8 +535,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.Exte EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost", "src\Workspaces\MSBuild\BuildHost\Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj", "{B1481D94-682E-46EC-ADBE-A16EB46FEEE9}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.ExternalAccess.Copilot", "src\EditorFeatures\ExternalAccess\Copilot\Microsoft.CodeAnalysis.ExternalAccess.Copilot.csproj", "{5E8FB6D6-6C5C-42E6-9220-1EAA7ED9BCAD}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.ExternalAccess.EditorConfigGenerator", "src\VisualStudio\ExternalAccess\EditorConfigGenerator\Microsoft.CodeAnalysis.ExternalAccess.EditorConfigGenerator.csproj", "{09AEDEE4-6358-47C9-8022-3BD37A518070}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.Features.DiagnosticsTests.Utilities", "src\Features\DiagnosticsTestUtilities\Microsoft.CodeAnalysis.Features.DiagnosticsTests.Utilities.csproj", "{5BABC440-4F1B-46E8-9068-DD7F02ED25D3}" @@ -569,6 +567,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ExternalAccess", "ExternalA EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.LanguageServer.Protocol.Test.Utilities", "src\LanguageServer\Protocol.TestUtilities\Microsoft.CodeAnalysis.LanguageServer.Protocol.Test.Utilities.csproj", "{7465CE63-A7B7-475F-8C57-48D2F8BC665A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CodeAnalysis.ExternalAccess.Copilot", "src\Features\ExternalAccess\Copilot\Microsoft.CodeAnalysis.ExternalAccess.Copilot.csproj", "{5D60CF30-28A9-9F0F-7610-D90E4A69AE73}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1355,10 +1355,6 @@ Global {B1481D94-682E-46EC-ADBE-A16EB46FEEE9}.Debug|Any CPU.Build.0 = Debug|Any CPU {B1481D94-682E-46EC-ADBE-A16EB46FEEE9}.Release|Any CPU.ActiveCfg = Release|Any CPU {B1481D94-682E-46EC-ADBE-A16EB46FEEE9}.Release|Any CPU.Build.0 = Release|Any CPU - {5E8FB6D6-6C5C-42E6-9220-1EAA7ED9BCAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5E8FB6D6-6C5C-42E6-9220-1EAA7ED9BCAD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5E8FB6D6-6C5C-42E6-9220-1EAA7ED9BCAD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5E8FB6D6-6C5C-42E6-9220-1EAA7ED9BCAD}.Release|Any CPU.Build.0 = Release|Any CPU {09AEDEE4-6358-47C9-8022-3BD37A518070}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {09AEDEE4-6358-47C9-8022-3BD37A518070}.Debug|Any CPU.Build.0 = Debug|Any CPU {09AEDEE4-6358-47C9-8022-3BD37A518070}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -1399,6 +1395,10 @@ Global {7465CE63-A7B7-475F-8C57-48D2F8BC665A}.Debug|Any CPU.Build.0 = Debug|Any CPU {7465CE63-A7B7-475F-8C57-48D2F8BC665A}.Release|Any CPU.ActiveCfg = Release|Any CPU {7465CE63-A7B7-475F-8C57-48D2F8BC665A}.Release|Any CPU.Build.0 = Release|Any CPU + {5D60CF30-28A9-9F0F-7610-D90E4A69AE73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5D60CF30-28A9-9F0F-7610-D90E4A69AE73}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5D60CF30-28A9-9F0F-7610-D90E4A69AE73}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5D60CF30-28A9-9F0F-7610-D90E4A69AE73}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1644,7 +1644,6 @@ Global {A833B11C-5072-4A1F-A32B-2700433B0D3D} = {806F0C6F-3640-4C92-8D55-6767B1535467} {8988270E-393A-4B92-AC1A-534F903CFD34} = {8977A560-45C2-4EC2-A849-97335B382C74} {B1481D94-682E-46EC-ADBE-A16EB46FEEE9} = {55A62CFA-1155-46F1-ADF3-BEEE51B58AB5} - {5E8FB6D6-6C5C-42E6-9220-1EAA7ED9BCAD} = {1AE9182D-B03E-4B00-B32E-37AE01715F57} {09AEDEE4-6358-47C9-8022-3BD37A518070} = {5880FECB-91F1-4AB8-8726-75EAFA8A918E} {5BABC440-4F1B-46E8-9068-DD7F02ED25D3} = {3E5FE3DB-45F7-4D83-9097-8F05D3B3AEC6} {5762E483-75CE-4328-A410-511F30737712} = {3E5FE3DB-45F7-4D83-9097-8F05D3B3AEC6} @@ -1661,6 +1660,7 @@ Global {1AE9182D-B03E-4B00-B32E-37AE01715F57} = {EE97CB90-33BB-4F3A-9B3D-69375DEC6AC6} {806F0C6F-3640-4C92-8D55-6767B1535467} = {D449D505-CC6A-4E0B-AF1B-976E2D0AE67A} {7465CE63-A7B7-475F-8C57-48D2F8BC665A} = {D449D505-CC6A-4E0B-AF1B-976E2D0AE67A} + {5D60CF30-28A9-9F0F-7610-D90E4A69AE73} = {58A2876A-618D-4AE6-A136-E44B42BBDE11} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {604E6B91-7BC0-4126-AE07-D4D2FEFC3D29} diff --git a/src/EditorFeatures/ExternalAccess/Copilot/Internal/Analyzer/CSharp/CSharpCopilotCodeAnalysisService.ReflectionWrapper.cs b/src/EditorFeatures/ExternalAccess/Copilot/Internal/Analyzer/CSharp/CSharpCopilotCodeAnalysisService.ReflectionWrapper.cs deleted file mode 100644 index 6f33ec3b27cf8..0000000000000 --- a/src/EditorFeatures/ExternalAccess/Copilot/Internal/Analyzer/CSharp/CSharpCopilotCodeAnalysisService.ReflectionWrapper.cs +++ /dev/null @@ -1,178 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Immutable; -using System.Reflection; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Text; -using Microsoft.VisualStudio; -using Microsoft.VisualStudio.Shell.ServiceBroker; -using IServiceProvider = System.IServiceProvider; - -namespace Microsoft.CodeAnalysis.ExternalAccess.Copilot.Internal.Analyzer.CSharp; - -using AnalyzeDocumentAsyncDelegateType = Func>>; -using GetAvailablePromptTitlesAsyncDelegateType = Func>>; -using GetCachedDiagnosticsAsyncDelegateType = Func>>; -using IsAvailableAsyncDelegateType = Func>; -using StartRefinementSessionAsyncDelegateType = Func; -using GetOnTheFlyDocsAsyncDelegateType = Func, string, CancellationToken, Task<(string responseString, bool isQuotaExceeded)>>; -using IsAnyExclusionAsyncDelegateType = Func>; -using IsFileExcludedAsyncDelegateType = Func>; - -internal sealed partial class CSharpCopilotCodeAnalysisService -{ - // A temporary helper to get access to the implementation of IExternalCSharpCopilotCodeAnalysisService, until it can be MEF exported. - private sealed class ReflectionWrapper : IExternalCSharpCopilotCodeAnalysisService - { - private const string CopilotRoslynDllName = "Microsoft.VisualStudio.Copilot.Roslyn, Version=0.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"; - private const string InternalCSharpCopilotAnalyzerTypeFullName = "Microsoft.VisualStudio.Copilot.Roslyn.Analyzer.InternalCSharpCopilotAnalyzer"; - - private const string IsAvailableAsyncMethodName = "IsAvailableAsync"; - private const string GetAvailablePromptTitlesAsyncMethodName = "GetAvailablePromptTitlesAsync"; - private const string AnalyzeDocumentAsyncMethodName = "AnalyzeDocumentAsync"; - private const string GetCachedDiagnosticsAsyncMethodName = "GetCachedDiagnosticsAsync"; - private const string StartRefinementSessionAsyncMethodName = "StartRefinementSessionAsync"; - private const string GetOnTheFlyDocsAsyncMethodName = "GetOnTheFlyDocsAsync"; - private const string IsFileExcludedAsyncMethodName = "IsFileExcludedAsync"; - - // Create and cache closed delegate to ensure we use a singleton object and with better performance. - private readonly Type? _analyzerType; - private readonly object? _analyzerInstance; - private readonly Lazy _lazyIsAvailableAsyncDelegate; - private readonly Lazy _lazyGetAvailablePromptTitlesAsyncDelegate; - private readonly Lazy _lazyAnalyzeDocumentAsyncDelegate; - private readonly Lazy _lazyGetCachedDiagnosticsAsyncDelegate; - private readonly Lazy _lazyStartRefinementSessionAsyncDelegate; - private readonly Lazy _lazyGetOnTheFlyDocsAsyncDelegate; - private readonly Lazy _lazyIsFileExcludedAsyncDelegate; - - public ReflectionWrapper(IServiceProvider serviceProvider, IVsService brokeredServiceContainer) - { - try - { - var assembly = Assembly.Load(CopilotRoslynDllName); - var analyzerType = assembly.GetType(InternalCSharpCopilotAnalyzerTypeFullName); - if (analyzerType is not null) - { - var analyzerInstance = Activator.CreateInstance(analyzerType, serviceProvider, brokeredServiceContainer); - if (analyzerInstance is not null) - { - _analyzerType = analyzerType; - _analyzerInstance = analyzerInstance; - } - } - } - catch - { - // Catch all here since failure is expected if user has no copilot chat or an older version of it installed. - } - - _lazyIsAvailableAsyncDelegate = new(CreateIsAvailableAsyncDelegate, LazyThreadSafetyMode.PublicationOnly); - _lazyGetAvailablePromptTitlesAsyncDelegate = new(CreateGetAvailablePromptTitlesAsyncDelegate, LazyThreadSafetyMode.PublicationOnly); - _lazyAnalyzeDocumentAsyncDelegate = new(CreateAnalyzeDocumentAsyncDelegate, LazyThreadSafetyMode.PublicationOnly); - _lazyGetCachedDiagnosticsAsyncDelegate = new(CreateGetCachedDiagnosticsAsyncDelegate, LazyThreadSafetyMode.PublicationOnly); - _lazyStartRefinementSessionAsyncDelegate = new(CreateStartRefinementSessionAsyncDelegate, LazyThreadSafetyMode.PublicationOnly); - _lazyGetOnTheFlyDocsAsyncDelegate = new(CreateGetOnTheFlyDocsAsyncDelegate, LazyThreadSafetyMode.PublicationOnly); - _lazyIsFileExcludedAsyncDelegate = new(CreateIsFileExcludedAsyncDelegate, LazyThreadSafetyMode.PublicationOnly); - } - - private T? CreateDelegate(string methodName, Type[] types) where T : Delegate - { - try - { - if (_analyzerInstance is null || _analyzerType is null) - return null; - - if (_analyzerType.GetMethod(methodName, types) is MethodInfo methodInfo) - return (T)Delegate.CreateDelegate(typeof(T), _analyzerInstance, methodInfo); - } - catch - { - // Catch all here since failure is expected if user has no copilot chat or an older version of it installed - } - - return null; - } - - private IsAvailableAsyncDelegateType? CreateIsAvailableAsyncDelegate() - => CreateDelegate(IsAvailableAsyncMethodName, [typeof(CancellationToken)]); - - private GetAvailablePromptTitlesAsyncDelegateType? CreateGetAvailablePromptTitlesAsyncDelegate() - => CreateDelegate(GetAvailablePromptTitlesAsyncMethodName, [typeof(Document), typeof(CancellationToken)]); - - private AnalyzeDocumentAsyncDelegateType? CreateAnalyzeDocumentAsyncDelegate() - => CreateDelegate(AnalyzeDocumentAsyncMethodName, [typeof(Document), typeof(TextSpan?), typeof(string), typeof(CancellationToken)]); - - private GetCachedDiagnosticsAsyncDelegateType? CreateGetCachedDiagnosticsAsyncDelegate() - => CreateDelegate(GetCachedDiagnosticsAsyncMethodName, [typeof(Document), typeof(string), typeof(CancellationToken)]); - - private StartRefinementSessionAsyncDelegateType? CreateStartRefinementSessionAsyncDelegate() - => CreateDelegate(StartRefinementSessionAsyncMethodName, [typeof(Document), typeof(Document), typeof(Diagnostic), typeof(CancellationToken)]); - - private GetOnTheFlyDocsAsyncDelegateType? CreateGetOnTheFlyDocsAsyncDelegate() - => CreateDelegate(GetOnTheFlyDocsAsyncMethodName, [typeof(string), typeof(ImmutableArray), typeof(string), typeof(CancellationToken)]); - - private IsFileExcludedAsyncDelegateType? CreateIsFileExcludedAsyncDelegate() - => CreateDelegate(IsFileExcludedAsyncMethodName, [typeof(string), typeof(CancellationToken)]); - - public async Task IsAvailableAsync(CancellationToken cancellationToken) - { - if (_lazyIsAvailableAsyncDelegate.Value is null) - return false; - - return await _lazyIsAvailableAsyncDelegate.Value(cancellationToken).ConfigureAwait(false); - } - - public async Task> GetAvailablePromptTitlesAsync(Document document, CancellationToken cancellationToken) - { - if (_lazyGetAvailablePromptTitlesAsyncDelegate.Value is null) - return []; - - return await _lazyGetAvailablePromptTitlesAsyncDelegate.Value(document, cancellationToken).ConfigureAwait(false); - } - - public async Task> AnalyzeDocumentAsync(Document document, TextSpan? span, string promptTitle, CancellationToken cancellationToken) - { - if (_lazyAnalyzeDocumentAsyncDelegate.Value is null) - return []; - - return await _lazyAnalyzeDocumentAsyncDelegate.Value(document, span, promptTitle, cancellationToken).ConfigureAwait(false); - } - - public async Task> GetCachedDiagnosticsAsync(Document document, string promptTitle, CancellationToken cancellationToken) - { - if (_lazyGetCachedDiagnosticsAsyncDelegate.Value is null) - return []; - - return await _lazyGetCachedDiagnosticsAsyncDelegate.Value(document, promptTitle, cancellationToken).ConfigureAwait(false); - } - - public Task StartRefinementSessionAsync(Document oldDocument, Document newDocument, Diagnostic? primaryDiagnostic, CancellationToken cancellationToken) - { - if (_lazyStartRefinementSessionAsyncDelegate.Value is null) - return Task.CompletedTask; - - return _lazyStartRefinementSessionAsyncDelegate.Value(oldDocument, newDocument, primaryDiagnostic, cancellationToken); - } - - public async Task<(string responseString, bool isQuotaExceeded)> GetOnTheFlyDocsAsync(string symbolSignature, ImmutableArray declarationCode, string language, CancellationToken cancellationToken) - { - if (_lazyGetOnTheFlyDocsAsyncDelegate.Value is null) - return (string.Empty, false); - - return await _lazyGetOnTheFlyDocsAsyncDelegate.Value(symbolSignature, declarationCode, language, cancellationToken).ConfigureAwait(false); - } - - public async Task IsFileExcludedAsync(string filePath, CancellationToken cancellationToken) - { - if (_lazyIsFileExcludedAsyncDelegate.Value is null) - return false; - - return await _lazyIsFileExcludedAsyncDelegate.Value(filePath, cancellationToken).ConfigureAwait(false); - } - } -} diff --git a/src/EditorFeatures/ExternalAccess/Copilot/.editorconfig b/src/Features/ExternalAccess/Copilot/.editorconfig similarity index 100% rename from src/EditorFeatures/ExternalAccess/Copilot/.editorconfig rename to src/Features/ExternalAccess/Copilot/.editorconfig diff --git a/src/EditorFeatures/ExternalAccess/Copilot/Analyzer/CopilotChecksumWrapper.cs b/src/Features/ExternalAccess/Copilot/Analyzer/CopilotChecksumWrapper.cs similarity index 92% rename from src/EditorFeatures/ExternalAccess/Copilot/Analyzer/CopilotChecksumWrapper.cs rename to src/Features/ExternalAccess/Copilot/Analyzer/CopilotChecksumWrapper.cs index 8c78970c45ca5..8bf7c472a6250 100644 --- a/src/EditorFeatures/ExternalAccess/Copilot/Analyzer/CopilotChecksumWrapper.cs +++ b/src/Features/ExternalAccess/Copilot/Analyzer/CopilotChecksumWrapper.cs @@ -8,7 +8,7 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Copilot; /// /// Exposed to provide an efficient checksum implementation. -/// Intended usage including cahching responses w/o retaining potentially long strings. +/// Intended usage including caching responses w/o retaining potentially long strings. /// internal sealed class CopilotChecksumWrapper { diff --git a/src/EditorFeatures/ExternalAccess/Copilot/Analyzer/CopilotUtilities.cs b/src/Features/ExternalAccess/Copilot/Analyzer/CopilotUtilities.cs similarity index 100% rename from src/EditorFeatures/ExternalAccess/Copilot/Analyzer/CopilotUtilities.cs rename to src/Features/ExternalAccess/Copilot/Analyzer/CopilotUtilities.cs diff --git a/src/EditorFeatures/ExternalAccess/Copilot/Analyzer/IExternalCSharpCopilotCodeAnalysisService.cs b/src/Features/ExternalAccess/Copilot/Analyzer/IExternalCSharpCopilotCodeAnalysisService.cs similarity index 99% rename from src/EditorFeatures/ExternalAccess/Copilot/Analyzer/IExternalCSharpCopilotCodeAnalysisService.cs rename to src/Features/ExternalAccess/Copilot/Analyzer/IExternalCSharpCopilotCodeAnalysisService.cs index 1a0fed9bd383b..8bd3c115522e8 100644 --- a/src/EditorFeatures/ExternalAccess/Copilot/Analyzer/IExternalCSharpCopilotCodeAnalysisService.cs +++ b/src/Features/ExternalAccess/Copilot/Analyzer/IExternalCSharpCopilotCodeAnalysisService.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; diff --git a/src/EditorFeatures/ExternalAccess/Copilot/CodeMapper/ICSharpCopilotMapCodeService.cs b/src/Features/ExternalAccess/Copilot/CodeMapper/ICSharpCopilotMapCodeService.cs similarity index 100% rename from src/EditorFeatures/ExternalAccess/Copilot/CodeMapper/ICSharpCopilotMapCodeService.cs rename to src/Features/ExternalAccess/Copilot/CodeMapper/ICSharpCopilotMapCodeService.cs diff --git a/src/EditorFeatures/ExternalAccess/Copilot/Internal/Analyzer/AbstractCopilotCodeAnalysisService.cs b/src/Features/ExternalAccess/Copilot/Internal/Analyzer/AbstractCopilotCodeAnalysisService.cs similarity index 100% rename from src/EditorFeatures/ExternalAccess/Copilot/Internal/Analyzer/AbstractCopilotCodeAnalysisService.cs rename to src/Features/ExternalAccess/Copilot/Internal/Analyzer/AbstractCopilotCodeAnalysisService.cs diff --git a/src/EditorFeatures/ExternalAccess/Copilot/Internal/Analyzer/CSharp/CSharpCopilotCodeAnalysisService.cs b/src/Features/ExternalAccess/Copilot/Internal/Analyzer/CSharp/CSharpCopilotCodeAnalysisService.cs similarity index 65% rename from src/EditorFeatures/ExternalAccess/Copilot/Internal/Analyzer/CSharp/CSharpCopilotCodeAnalysisService.cs rename to src/Features/ExternalAccess/Copilot/Internal/Analyzer/CSharp/CSharpCopilotCodeAnalysisService.cs index 8d5a5a91088af..3b2460250fd74 100644 --- a/src/EditorFeatures/ExternalAccess/Copilot/Internal/Analyzer/CSharp/CSharpCopilotCodeAnalysisService.cs +++ b/src/Features/ExternalAccess/Copilot/Internal/Analyzer/CSharp/CSharpCopilotCodeAnalysisService.cs @@ -16,49 +16,41 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Microsoft.VisualStudio; -using Microsoft.VisualStudio.Shell; -using Microsoft.VisualStudio.Shell.ServiceBroker; namespace Microsoft.CodeAnalysis.ExternalAccess.Copilot.Internal.Analyzer.CSharp; [ExportLanguageService(typeof(ICopilotCodeAnalysisService), LanguageNames.CSharp), Shared] -internal sealed partial class CSharpCopilotCodeAnalysisService : AbstractCopilotCodeAnalysisService +internal sealed class CSharpCopilotCodeAnalysisService : AbstractCopilotCodeAnalysisService { - private readonly Lazy _lazyExternalCopilotService; + private IExternalCSharpCopilotCodeAnalysisService AnalysisService { get; } [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public CSharpCopilotCodeAnalysisService( - [Import(AllowDefault = true)] IExternalCSharpCopilotCodeAnalysisService? externalCopilotService, - IDiagnosticsRefresher diagnosticsRefresher, - SVsServiceProvider serviceProvider, - IVsService brokeredServiceContainer + [Import] IExternalCSharpCopilotCodeAnalysisService externalCopilotService, + IDiagnosticsRefresher diagnosticsRefresher ) : base(diagnosticsRefresher) { - _lazyExternalCopilotService = new Lazy(GetExternalService, LazyThreadSafetyMode.PublicationOnly); - - IExternalCSharpCopilotCodeAnalysisService GetExternalService() - => externalCopilotService ?? new ReflectionWrapper(serviceProvider, brokeredServiceContainer); + AnalysisService = externalCopilotService; } protected override Task> AnalyzeDocumentCoreAsync(Document document, TextSpan? span, string promptTitle, CancellationToken cancellationToken) - => _lazyExternalCopilotService.Value.AnalyzeDocumentAsync(document, span, promptTitle, cancellationToken); + => AnalysisService.AnalyzeDocumentAsync(document, span, promptTitle, cancellationToken); protected override Task> GetAvailablePromptTitlesCoreAsync(Document document, CancellationToken cancellationToken) - => _lazyExternalCopilotService.Value.GetAvailablePromptTitlesAsync(document, cancellationToken); + => AnalysisService.GetAvailablePromptTitlesAsync(document, cancellationToken); protected override Task> GetCachedDiagnosticsCoreAsync(Document document, string promptTitle, CancellationToken cancellationToken) - => _lazyExternalCopilotService.Value.GetCachedDiagnosticsAsync(document, promptTitle, cancellationToken); + => AnalysisService.GetCachedDiagnosticsAsync(document, promptTitle, cancellationToken); protected override Task IsAvailableCoreAsync(CancellationToken cancellationToken) - => _lazyExternalCopilotService.Value.IsAvailableAsync(cancellationToken); + => AnalysisService.IsAvailableAsync(cancellationToken); protected override Task StartRefinementSessionCoreAsync(Document oldDocument, Document newDocument, Diagnostic? primaryDiagnostic, CancellationToken cancellationToken) - => _lazyExternalCopilotService.Value.StartRefinementSessionAsync(oldDocument, newDocument, primaryDiagnostic, cancellationToken); + => AnalysisService.StartRefinementSessionAsync(oldDocument, newDocument, primaryDiagnostic, cancellationToken); protected override Task<(string responseString, bool isQuotaExceeded)> GetOnTheFlyDocsCoreAsync(string symbolSignature, ImmutableArray declarationCode, string language, CancellationToken cancellationToken) - => _lazyExternalCopilotService.Value.GetOnTheFlyDocsAsync(symbolSignature, declarationCode, language, cancellationToken); + => AnalysisService.GetOnTheFlyDocsAsync(symbolSignature, declarationCode, language, cancellationToken); protected override async Task> GetDiagnosticsIntersectWithSpanAsync( Document document, IReadOnlyList diagnostics, TextSpan span, CancellationToken cancellationToken) @@ -81,5 +73,5 @@ protected override async Task> GetDiagnosticsIntersec } protected override Task IsFileExcludedCoreAsync(string filePath, CancellationToken cancellationToken) - => _lazyExternalCopilotService.Value.IsFileExcludedAsync(filePath, cancellationToken); + => AnalysisService.IsFileExcludedAsync(filePath, cancellationToken); } diff --git a/src/EditorFeatures/ExternalAccess/Copilot/Internal/CodeMapper/CopilotCSharpMapCodeService.cs b/src/Features/ExternalAccess/Copilot/Internal/CodeMapper/CopilotCSharpMapCodeService.cs similarity index 100% rename from src/EditorFeatures/ExternalAccess/Copilot/Internal/CodeMapper/CopilotCSharpMapCodeService.cs rename to src/Features/ExternalAccess/Copilot/Internal/CodeMapper/CopilotCSharpMapCodeService.cs diff --git a/src/EditorFeatures/ExternalAccess/Copilot/Internal/RelatedDocuments/CSharpCopilotRelatedDocumentsService.cs b/src/Features/ExternalAccess/Copilot/Internal/RelatedDocuments/CSharpCopilotRelatedDocumentsService.cs similarity index 100% rename from src/EditorFeatures/ExternalAccess/Copilot/Internal/RelatedDocuments/CSharpCopilotRelatedDocumentsService.cs rename to src/Features/ExternalAccess/Copilot/Internal/RelatedDocuments/CSharpCopilotRelatedDocumentsService.cs diff --git a/src/EditorFeatures/ExternalAccess/Copilot/InternalAPI.Shipped.txt b/src/Features/ExternalAccess/Copilot/InternalAPI.Shipped.txt similarity index 100% rename from src/EditorFeatures/ExternalAccess/Copilot/InternalAPI.Shipped.txt rename to src/Features/ExternalAccess/Copilot/InternalAPI.Shipped.txt diff --git a/src/EditorFeatures/ExternalAccess/Copilot/InternalAPI.Unshipped.txt b/src/Features/ExternalAccess/Copilot/InternalAPI.Unshipped.txt similarity index 100% rename from src/EditorFeatures/ExternalAccess/Copilot/InternalAPI.Unshipped.txt rename to src/Features/ExternalAccess/Copilot/InternalAPI.Unshipped.txt diff --git a/src/EditorFeatures/ExternalAccess/Copilot/Microsoft.CodeAnalysis.ExternalAccess.Copilot.csproj b/src/Features/ExternalAccess/Copilot/Microsoft.CodeAnalysis.ExternalAccess.Copilot.csproj similarity index 78% rename from src/EditorFeatures/ExternalAccess/Copilot/Microsoft.CodeAnalysis.ExternalAccess.Copilot.csproj rename to src/Features/ExternalAccess/Copilot/Microsoft.CodeAnalysis.ExternalAccess.Copilot.csproj index 0da1aa938d434..8735cf01ed4cd 100644 --- a/src/EditorFeatures/ExternalAccess/Copilot/Microsoft.CodeAnalysis.ExternalAccess.Copilot.csproj +++ b/src/Features/ExternalAccess/Copilot/Microsoft.CodeAnalysis.ExternalAccess.Copilot.csproj @@ -4,13 +4,13 @@ Library Microsoft.CodeAnalysis.ExternalAccess.Copilot - net472 + $(NetVSShared);net472 true Microsoft.CodeAnalysis.ExternalAccess.Copilot - A supporting package for Copilot Chat: + A supporting package for GitHub Copilot: https://devdiv.visualstudio.com/DefaultCollection/DevDiv/_git/VisualStudio.Conversations @@ -24,11 +24,7 @@ - - - - - + diff --git a/src/EditorFeatures/ExternalAccess/Copilot/PublicAPI.Shipped.txt b/src/Features/ExternalAccess/Copilot/PublicAPI.Shipped.txt similarity index 100% rename from src/EditorFeatures/ExternalAccess/Copilot/PublicAPI.Shipped.txt rename to src/Features/ExternalAccess/Copilot/PublicAPI.Shipped.txt diff --git a/src/EditorFeatures/ExternalAccess/Copilot/PublicAPI.Unshipped.txt b/src/Features/ExternalAccess/Copilot/PublicAPI.Unshipped.txt similarity index 100% rename from src/EditorFeatures/ExternalAccess/Copilot/PublicAPI.Unshipped.txt rename to src/Features/ExternalAccess/Copilot/PublicAPI.Unshipped.txt diff --git a/src/EditorFeatures/ExternalAccess/Copilot/RelatedDocuments/ICopilotRelatedDocumentsService.cs b/src/Features/ExternalAccess/Copilot/RelatedDocuments/ICopilotRelatedDocumentsService.cs similarity index 100% rename from src/EditorFeatures/ExternalAccess/Copilot/RelatedDocuments/ICopilotRelatedDocumentsService.cs rename to src/Features/ExternalAccess/Copilot/RelatedDocuments/ICopilotRelatedDocumentsService.cs diff --git a/src/NuGet/VS.ExternalAPIs.Roslyn.Package/VS.ExternalAPIs.Roslyn.Package.csproj b/src/NuGet/VS.ExternalAPIs.Roslyn.Package/VS.ExternalAPIs.Roslyn.Package.csproj index 92a958498c6cb..33d9538cb027a 100644 --- a/src/NuGet/VS.ExternalAPIs.Roslyn.Package/VS.ExternalAPIs.Roslyn.Package.csproj +++ b/src/NuGet/VS.ExternalAPIs.Roslyn.Package/VS.ExternalAPIs.Roslyn.Package.csproj @@ -39,7 +39,7 @@ - + diff --git a/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj b/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj index f02ebb53bedc9..b64520f07272f 100644 --- a/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj +++ b/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj @@ -50,7 +50,7 @@ true BindingRedirect - + Microsoft.CodeAnalysis.ExternalAccess.Copilot BuiltProjectOutputGroup true From 6d3b13a3d8f7cc4185252408bb838052bff36f96 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Tue, 4 Feb 2025 14:46:55 -0800 Subject: [PATCH 069/305] Consume mibc data from VSCode profiling runs --- azure-pipelines-official.yml | 13 +++++++++++++ .../Microsoft.CodeAnalysis.LanguageServer.csproj | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/azure-pipelines-official.yml b/azure-pipelines-official.yml index d2a8322ffaa92..6237d65cacde8 100644 --- a/azure-pipelines-official.yml +++ b/azure-pipelines-official.yml @@ -16,6 +16,11 @@ resources: type: git name: 1ESPipelineTemplates/1ESPipelineTemplates ref: refs/tags/release + pipelines: + - pipeline: profilingInputs + source: dotnet-vscode-csharp-profiling + branch: main + trigger: none parameters: - name: IbcDrop @@ -89,6 +94,9 @@ variables: - ${{ if and(notin(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.SourceBranch'], 'refs/heads/main')) }}: - name: enableSourceIndex value: true + - name: VSCodeOptimizationDataRoot + value: $(Pipeline.Workspace)/profilingInputs/merged mibc + extends: template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates @@ -273,6 +281,10 @@ extends: feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json condition: and(succeeded(), in(variables['SignType'], 'test', 'real')) + - download: profilingInputs + artifact: merged mibc + displayName: Download VSCode optimization inputs + - task: PowerShell@2 displayName: Build inputs: @@ -294,6 +306,7 @@ extends: -officialVisualStudioDropAccessToken $(_DevDivDropAccessToken) /p:RepositoryName=$(Build.Repository.Name) /p:VisualStudioDropName=$(VisualStudio.DropName) + /p:VSCodeOptimizationDataRoot="$(VSCodeOptimizationDataRoot)" /p:DotNetSignType=$(SignType) /p:DotnetPublishUsingPipelines=true /p:IgnoreIbcMergeErrors=true diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Microsoft.CodeAnalysis.LanguageServer.csproj b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Microsoft.CodeAnalysis.LanguageServer.csproj index a76305b4a2923..9e2e89b34ad45 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Microsoft.CodeAnalysis.LanguageServer.csproj +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Microsoft.CodeAnalysis.LanguageServer.csproj @@ -54,6 +54,7 @@ win-x64;win-arm64;linux-x64;linux-arm64;linux-musl-x64;linux-musl-arm64;osx-x64;osx-arm64 true + + + + + + + + $(PublishReadyToRunCrossgen2ExtraArgs);--opt-cross-module:*;--non-local-generics-module:"$(TargetName)" + + + From bb10dfd9eefb4d4d9beb07ae08f0908edded1567 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Feb 2025 14:49:15 -0800 Subject: [PATCH 070/305] Keep around large array instances for speedometer --- ...aryPragmaSuppressionsDiagnosticAnalyzer.cs | 3 +- .../GoToAdjacentMemberCommandHandler.cs | 3 +- ...lAnalyzer.IncrementalMemberEditAnalyzer.cs | 4 +-- ...crementalMemberEditAnalyzer_MemberSpans.cs | 3 +- .../Compiler/Core/ObjectPools/ArrayBuilder.cs | 32 ++++++++++++++++--- .../Compiler/Core/ObjectPools/IPooled.cs | 2 +- .../Core/ObjectPools/PooledDictionary.cs | 4 +++ .../Core/ObjectPools/PooledDisposer.cs | 4 +-- .../Core/ObjectPools/PooledHashSet.cs | 4 +++ .../Core/ObjectPools/PooledStringBuilder.cs | 15 +++++++++ 10 files changed, 62 insertions(+), 12 deletions(-) diff --git a/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/AbstractRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/AbstractRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer.cs index d33129015ca0f..53f687fb9cd9a 100644 --- a/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/AbstractRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/AbstractRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer.cs @@ -745,7 +745,8 @@ private async Task ProcessSuppressMessageAttributesAsync( return false; } - using var _1 = ArrayBuilder.GetInstance(out var declarationNodes); + // Specifies false for discardLargeInstances as these objects commonly exceed the default ArrayBuilder capacity threshold. + using var _1 = ArrayBuilder.GetInstance(discardLargeInstances: false, out var declarationNodes); this.SyntaxFacts.AddTopLevelAndMethodLevelMembers(root, declarationNodes); using var _2 = PooledHashSet.GetInstance(out var processedPartialSymbols); diff --git a/src/EditorFeatures/Core/Editor/GoToAdjacentMemberCommandHandler.cs b/src/EditorFeatures/Core/Editor/GoToAdjacentMemberCommandHandler.cs index adae30a2f34da..c53f4d5102135 100644 --- a/src/EditorFeatures/Core/Editor/GoToAdjacentMemberCommandHandler.cs +++ b/src/EditorFeatures/Core/Editor/GoToAdjacentMemberCommandHandler.cs @@ -104,7 +104,8 @@ private bool ExecuteCommandImpl(EditorCommandArgs args, bool gotoNextMember, Com /// internal static int? GetTargetPosition(ISyntaxFactsService service, SyntaxNode root, int caretPosition, bool next) { - using var _ = ArrayBuilder.GetInstance(out var members); + // Specifies false for discardLargeInstances as these objects commonly exceed the default ArrayBuilder capacity threshold. + using var _ = ArrayBuilder.GetInstance(discardLargeInstances: false, out var members); service.AddMethodLevelMembers(root, members); if (members.Count == 0) return null; diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs index 8c0aa63811599..bfbd34725b9fb 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; @@ -202,7 +201,8 @@ async Task ExecuteAnalyzersAsync( var syntaxFacts = document.GetRequiredLanguageService(); - using var _ = ArrayBuilder.GetInstance(out var members); + // Specifies false for discardLargeInstances as these objects commonly exceed the default ArrayBuilder capacity threshold. + using var _ = ArrayBuilder.GetInstance(discardLargeInstances: false, out var members); syntaxFacts.AddMethodLevelMembers(root, members); var memberSpans = members.SelectAsArray(member => member.FullSpan); diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer_MemberSpans.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer_MemberSpans.cs index 893bc071d1e99..05f4d5a6eaa09 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer_MemberSpans.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer_MemberSpans.cs @@ -48,7 +48,8 @@ static async Task> CreateMemberSpansAsync(Document docu var service = document.GetRequiredLanguageService(); var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - using var _ = ArrayBuilder.GetInstance(out var members); + // Specifies false for discardLargeInstances as these objects commonly exceed the default ArrayBuilder capacity threshold. + using var _ = ArrayBuilder.GetInstance(discardLargeInstances: false, out var members); service.AddMethodLevelMembers(root, members); return members.SelectAsArray(m => m.FullSpan); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/ArrayBuilder.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/ArrayBuilder.cs index c40663de34f02..c5d2dcf735559 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/ArrayBuilder.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/ArrayBuilder.cs @@ -7,10 +7,7 @@ namespace Microsoft.CodeAnalysis.PooledObjects; internal sealed partial class ArrayBuilder : IPooled { public static PooledDisposer> GetInstance(out ArrayBuilder instance) - { - instance = GetInstance(); - return new PooledDisposer>(instance); - } + => GetInstance(discardLargeInstances: true, out instance); public static PooledDisposer> GetInstance(int capacity, out ArrayBuilder instance) { @@ -23,4 +20,31 @@ public static PooledDisposer> GetInstance(int capacity, T fillWi instance = GetInstance(capacity, fillWithValue); return new PooledDisposer>(instance); } + + public static PooledDisposer> GetInstance(bool discardLargeInstances, out ArrayBuilder instance) + { + instance = GetInstance(); + return new PooledDisposer>(instance, discardLargeInstances); + } + + void IPooled.Free(bool discardLargeInstances) + { + var pool = _pool; + if (pool != null) + { + if (!discardLargeInstances || _builder.Capacity < PooledArrayLengthLimitExclusive) + { + if (this.Count != 0) + { + this.Clear(); + } + + pool.Free(this); + } + else + { + pool.ForgetTrackedObject(this); + } + } + } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/IPooled.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/IPooled.cs index 6a0d0d14d4128..0b687f3f3e697 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/IPooled.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/IPooled.cs @@ -6,5 +6,5 @@ namespace Microsoft.CodeAnalysis.PooledObjects; internal interface IPooled { - void Free(); + void Free(bool discardLargeInstances); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledDictionary.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledDictionary.cs index f431ba4ee31ce..c8e35d932b555 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledDictionary.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledDictionary.cs @@ -11,4 +11,8 @@ public static PooledDisposer> GetInstance(out PooledDicti instance = GetInstance(); return new PooledDisposer>(instance); } + + // Nothing special to do here. + void IPooled.Free(bool discardLargeInstance) + => this.Free(); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledDisposer.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledDisposer.cs index 1e922f60e5382..895dd39043fa7 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledDisposer.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledDisposer.cs @@ -8,9 +8,9 @@ namespace Microsoft.CodeAnalysis.PooledObjects; [NonCopyable] -internal readonly struct PooledDisposer(TPoolable instance) : IDisposable +internal readonly struct PooledDisposer(TPoolable instance, bool discardLargeInstances = true) : IDisposable where TPoolable : class, IPooled { void IDisposable.Dispose() - => instance?.Free(); + => instance?.Free(discardLargeInstances); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledHashSet.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledHashSet.cs index 7c68246ee11ae..64e7ea3c10b95 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledHashSet.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledHashSet.cs @@ -13,4 +13,8 @@ public static PooledDisposer> GetInstance(out PooledHashSet instance = GetInstance(); return new PooledDisposer>(instance); } + + // Nothing special to do here. + void IPooled.Free(bool discardLargeInstance) + => this.Free(); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledStringBuilder.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledStringBuilder.cs index 2d18c1ee3ed89..f3ad5fb7d612b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledStringBuilder.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledStringBuilder.cs @@ -14,4 +14,19 @@ public static PooledDisposer GetInstance(out StringBuilder instance = pooledInstance; return new PooledDisposer(pooledInstance); } + + void IPooled.Free(bool discardLargeInstance) + { + var builder = this.Builder; + + if (!discardLargeInstance || builder.Capacity <= 1024) + { + builder.Clear(); + _pool.Free(this); + } + else + { + _pool.ForgetTrackedObject(this); + } + } } From 53b67f1d67b77f4610fdc18e2fb9611d09726072 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Feb 2025 14:55:44 -0800 Subject: [PATCH 071/305] Use a separate pool --- .../Compiler/Core/ObjectPools/ArrayBuilder.cs | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/ArrayBuilder.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/ArrayBuilder.cs index c5d2dcf735559..1d6d7630eb2d9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/ArrayBuilder.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/ArrayBuilder.cs @@ -6,6 +6,8 @@ namespace Microsoft.CodeAnalysis.PooledObjects; internal sealed partial class ArrayBuilder : IPooled { + private static readonly ObjectPool> s_keepLargeInstancesPool = CreatePool(); + public static PooledDisposer> GetInstance(out ArrayBuilder instance) => GetInstance(discardLargeInstances: true, out instance); @@ -23,28 +25,24 @@ public static PooledDisposer> GetInstance(int capacity, T fillWi public static PooledDisposer> GetInstance(bool discardLargeInstances, out ArrayBuilder instance) { - instance = GetInstance(); + // If we're discarding large instances (the default behavior), then just use the normal pool. If we're not, use + // a specific pool so that *other* normal callers don't accidentally get it and discard it. + instance = discardLargeInstances ? GetInstance() : s_keepLargeInstancesPool.Allocate(); return new PooledDisposer>(instance, discardLargeInstances); } void IPooled.Free(bool discardLargeInstances) { - var pool = _pool; - if (pool != null) + // If we're discarding large instances, use the default behavior (which already does that). Otherwise, always + // clear and free the instance back to its originating pool. + if (discardLargeInstances) { - if (!discardLargeInstances || _builder.Capacity < PooledArrayLengthLimitExclusive) - { - if (this.Count != 0) - { - this.Clear(); - } - - pool.Free(this); - } - else - { - pool.ForgetTrackedObject(this); - } + Free(); + } + else + { + this.Clear(); + _pool?.Free(this); } } } From a9bf3476fffc77854b81c386e384e3c05dc24177 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Feb 2025 14:56:59 -0800 Subject: [PATCH 072/305] Make consistent --- .../Core/ObjectPools/PooledStringBuilder.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledStringBuilder.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledStringBuilder.cs index f3ad5fb7d612b..f751e5c355352 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledStringBuilder.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledStringBuilder.cs @@ -15,18 +15,18 @@ public static PooledDisposer GetInstance(out StringBuilder return new PooledDisposer(pooledInstance); } - void IPooled.Free(bool discardLargeInstance) + void IPooled.Free(bool discardLargeInstances) { - var builder = this.Builder; - - if (!discardLargeInstance || builder.Capacity <= 1024) + // If we're discarding large instances, use the default behavior (which already does that). Otherwise, always + // clear and free the instance back to its originating pool. + if (discardLargeInstances) { - builder.Clear(); - _pool.Free(this); + Free(); } else { - _pool.ForgetTrackedObject(this); + this.Builder.Clear(); + _pool.Free(this); } } } From bd449326d80a626b8e102edee2e63f98d7600eef Mon Sep 17 00:00:00 2001 From: David Barbet Date: Wed, 5 Feb 2025 15:11:36 -0800 Subject: [PATCH 073/305] fix extra whitespace insertion for text edits --- .../Completion/CompletionCapabilityHelper.cs | 2 + .../Completion/CompletionResultFactory.cs | 8 ++- .../Completion/CompletionTests.cs | 56 +++++++++++++++++++ 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/LanguageServer/Protocol/Handler/Completion/CompletionCapabilityHelper.cs b/src/LanguageServer/Protocol/Handler/Completion/CompletionCapabilityHelper.cs index 6ed2dfe1ef6f1..aefec686e77a4 100644 --- a/src/LanguageServer/Protocol/Handler/Completion/CompletionCapabilityHelper.cs +++ b/src/LanguageServer/Protocol/Handler/Completion/CompletionCapabilityHelper.cs @@ -28,6 +28,7 @@ internal sealed class CompletionCapabilityHelper public bool SupportsMarkdownDocumentation { get; } public ISet SupportedItemKinds { get; } public ISet SupportedItemTags { get; } + public ISet SupportedInsertTextModes { get; } public CompletionCapabilityHelper(ClientCapabilities clientCapabilities) : this(supportsVSExtensions: clientCapabilities.HasVisualStudioLspCapability(), @@ -45,6 +46,7 @@ public CompletionCapabilityHelper(bool supportsVSExtensions, CompletionSetting? SupportDefaultCommitCharacters = completionSetting?.CompletionListSetting?.ItemDefaults?.Contains(CommitCharactersPropertyName) == true; SupportedItemKinds = completionSetting?.CompletionItemKind?.ValueSet?.ToSet() ?? SpecializedCollections.EmptySet(); SupportedItemTags = completionSetting?.CompletionItem?.TagSupport?.ValueSet?.ToSet() ?? SpecializedCollections.EmptySet(); + SupportedInsertTextModes = completionSetting?.CompletionItem?.InsertTextModeSupport?.ValueSet?.ToSet() ?? SpecializedCollections.EmptySet(); // internal VS LSP if (supportsVSExtensions) diff --git a/src/LanguageServer/Protocol/Handler/Completion/CompletionResultFactory.cs b/src/LanguageServer/Protocol/Handler/Completion/CompletionResultFactory.cs index ae0bf6c7d66df..9fa30b7023723 100644 --- a/src/LanguageServer/Protocol/Handler/Completion/CompletionResultFactory.cs +++ b/src/LanguageServer/Protocol/Handler/Completion/CompletionResultFactory.cs @@ -86,7 +86,7 @@ internal static class CompletionResultFactory ItemDefaults = new LSP.CompletionListItemDefaults { EditRange = capabilityHelper.SupportDefaultEditRange ? ProtocolConversions.TextSpanToRange(defaultSpan, documentText) : null, - Data = capabilityHelper.SupportCompletionListData ? resolveData : null + Data = capabilityHelper.SupportCompletionListData ? resolveData : null, }, // VS internal @@ -97,6 +97,12 @@ internal static class CompletionResultFactory Data = capabilityHelper.SupportVSInternalCompletionListData ? resolveData : null, }; + if (capabilityHelper.SupportedInsertTextModes.Contains(LSP.InsertTextMode.AsIs)) + { + // By default, all text edits we create include the appropriate whitespace, so tell the client to leave it as-is (if it supports the option). + completionList.ItemDefaults.InsertTextMode = LSP.InsertTextMode.AsIs; + } + PromoteCommonCommitCharactersOntoList(); if (completionList.ItemDefaults is { EditRange: null, CommitCharacters: null, Data: null }) diff --git a/src/LanguageServer/ProtocolUnitTests/Completion/CompletionTests.cs b/src/LanguageServer/ProtocolUnitTests/Completion/CompletionTests.cs index bf88d490792f3..ad00091a58b60 100644 --- a/src/LanguageServer/ProtocolUnitTests/Completion/CompletionTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Completion/CompletionTests.cs @@ -1471,6 +1471,62 @@ public async Task EditRangeShouldNotEndAtCursorPosition(bool mutatingLspWorkspac Assert.Equal(new() { Start = new(2, 0), End = new(2, 8) }, results.ItemDefaults.EditRange.Value.First); } + [Theory, CombinatorialData] + public async Task TestHasInsertTextModeIfSupportedAsync(bool mutatingLspWorkspace) + { + var markup = +@"class A +{ + void M() + { + {|caret:|} + } +}"; + var capabilities = CreateCoreCompletionCapabilities(); + capabilities.TextDocument.Completion.CompletionItem = new LSP.CompletionItemSetting + { + InsertTextModeSupport = new LSP.InsertTextModeSupportSetting { ValueSet = [LSP.InsertTextMode.AsIs] } + }; + + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, capabilities); + var completionParams = CreateCompletionParams( + testLspServer.GetLocations("caret").Single(), + invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit, + triggerCharacter: "\0", + triggerKind: LSP.CompletionTriggerKind.Invoked); + + var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false); + Assert.Equal(LSP.InsertTextMode.AsIs, results.ItemDefaults.InsertTextMode); + } + + [Theory, CombinatorialData] + public async Task TestDoesNotHaveInsertTextModeIfNotSupportedAsync(bool mutatingLspWorkspace) + { + var markup = +@"class A +{ + void M() + { + {|caret:|} + } +}"; + var capabilities = CreateCoreCompletionCapabilities(); + capabilities.TextDocument.Completion.CompletionItem = new LSP.CompletionItemSetting + { + InsertTextModeSupport = new LSP.InsertTextModeSupportSetting { ValueSet = [] } + }; + + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, capabilities); + var completionParams = CreateCompletionParams( + testLspServer.GetLocations("caret").Single(), + invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit, + triggerCharacter: "\0", + triggerKind: LSP.CompletionTriggerKind.Invoked); + + var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false); + Assert.Null(results.ItemDefaults.InsertTextMode); + } + internal static Task RunGetCompletionsAsync(TestLspServer testLspServer, LSP.CompletionParams completionParams) { return testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentCompletionName, From 884626e4813b0d419bb64c97d9ad92a851ed3ca5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Feb 2025 16:22:18 -0800 Subject: [PATCH 074/305] remove text --- .../NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs index 236f041cde978..2e2554a5ae5a8 100644 --- a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs +++ b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs @@ -64,8 +64,6 @@ public static async Task SearchDocumentAndRelatedDocumentsInCurrentProcessAsync( // original document, search the entirety of it (by passing 'null' in for the 'spans' argument). For related // documents, only search the spans of the partial-types/inheriting-types that we find for the types in this // starting document. - var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - await Task.WhenAll( SearchDocumentsInCurrentProcessAsync([(document, spans: null)]), SearchRelatedDocumentsInCurrentProcessAsync()).ConfigureAwait(false); From 578800e10add447a454e1615ae0fd9632a9e0006 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Feb 2025 16:26:03 -0800 Subject: [PATCH 075/305] static lambdas --- ...actNavigateToSearchService.NormalSearch.cs | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs index 2e2554a5ae5a8..430cdf81db059 100644 --- a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs +++ b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs @@ -71,20 +71,24 @@ await Task.WhenAll( Task SearchDocumentsInCurrentProcessAsync(ImmutableArray<(Document document, NormalizedTextSpanCollection? spans)> documentAndSpans) => ProducerConsumer.RunParallelAsync( documentAndSpans, - async (documentAndSpan, onItemFound, args, cancellationToken) => await SearchSingleDocumentAsync( - documentAndSpan.document, patternName, patternContainerOpt, declaredSymbolInfoKindsSet, - item => - { - // Ensure that the results found while searching the single document intersect the desired - // subrange of the document we're searching in. For the primary document this will always - // succeed (since we're searching the full document). But for related documents this may fail - // if the results is not in the span of any of the types in those files we're searching. - if (documentAndSpan.spans is null || documentAndSpan.spans.IntersectsWith(item.DeclaredSymbolInfo.Span)) - onItemFound(item); - }, - cancellationToken).ConfigureAwait(false), - onItemsFound, - args: default, + produceItems: static async (documentAndSpan, onItemFound, args, cancellationToken) => + { + var (patternName, patternContainerOpt, declaredSymbolInfoKindsSet, onItemsFound) = args; + await SearchSingleDocumentAsync( + documentAndSpan.document, patternName, patternContainerOpt, declaredSymbolInfoKindsSet, + item => + { + // Ensure that the results found while searching the single document intersect the desired + // subrange of the document we're searching in. For the primary document this will always + // succeed (since we're searching the full document). But for related documents this may fail + // if the results is not in the span of any of the types in those files we're searching. + if (documentAndSpan.spans is null || documentAndSpan.spans.IntersectsWith(item.DeclaredSymbolInfo.Span)) + onItemFound(item); + }, + cancellationToken).ConfigureAwait(false); + }, + consumeItems: static (values, args, cancellationToken) => args.onItemsFound(values, default, cancellationToken), + args: (patternName, patternContainerOpt, declaredSymbolInfoKindsSet, onItemsFound), cancellationToken); async Task SearchRelatedDocumentsInCurrentProcessAsync() From 07b8ec327c99097dc902313a79373c46153e74f7 Mon Sep 17 00:00:00 2001 From: "gel@microsoft.com" Date: Wed, 5 Feb 2025 15:17:33 -0800 Subject: [PATCH 076/305] Fix --- Roslyn.sln | 4 ++-- .../Internal/Analyzer/AbstractCopilotCodeAnalysisService.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Roslyn.sln b/Roslyn.sln index a97649403b3ad..905f0de701071 100644 --- a/Roslyn.sln +++ b/Roslyn.sln @@ -535,6 +535,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.Exte EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost", "src\Workspaces\MSBuild\BuildHost\Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj", "{B1481D94-682E-46EC-ADBE-A16EB46FEEE9}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.ExternalAccess.Copilot", "src\Features\ExternalAccess\Copilot\Microsoft.CodeAnalysis.ExternalAccess.Copilot.csproj", "{5D60CF30-28A9-9F0F-7610-D90E4A69AE73}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.ExternalAccess.EditorConfigGenerator", "src\VisualStudio\ExternalAccess\EditorConfigGenerator\Microsoft.CodeAnalysis.ExternalAccess.EditorConfigGenerator.csproj", "{09AEDEE4-6358-47C9-8022-3BD37A518070}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.Features.DiagnosticsTests.Utilities", "src\Features\DiagnosticsTestUtilities\Microsoft.CodeAnalysis.Features.DiagnosticsTests.Utilities.csproj", "{5BABC440-4F1B-46E8-9068-DD7F02ED25D3}" @@ -567,8 +569,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ExternalAccess", "ExternalA EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.LanguageServer.Protocol.Test.Utilities", "src\LanguageServer\Protocol.TestUtilities\Microsoft.CodeAnalysis.LanguageServer.Protocol.Test.Utilities.csproj", "{7465CE63-A7B7-475F-8C57-48D2F8BC665A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CodeAnalysis.ExternalAccess.Copilot", "src\Features\ExternalAccess\Copilot\Microsoft.CodeAnalysis.ExternalAccess.Copilot.csproj", "{5D60CF30-28A9-9F0F-7610-D90E4A69AE73}" -EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Microsoft.CodeAnalysis.Threading", "src\Dependencies\Threading\Microsoft.CodeAnalysis.Threading.shproj", "{967723E8-4FDD-447B-99F6-4F8C47CB5433}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.Threading.Package", "src\Dependencies\Threading\Microsoft.CodeAnalysis.Threading.Package.csproj", "{2559DAF9-784E-4C29-E0E1-70204B1FD56E}" diff --git a/src/Features/ExternalAccess/Copilot/Internal/Analyzer/AbstractCopilotCodeAnalysisService.cs b/src/Features/ExternalAccess/Copilot/Internal/Analyzer/AbstractCopilotCodeAnalysisService.cs index a75f5e8f6f026..a48c1afcde012 100644 --- a/src/Features/ExternalAccess/Copilot/Internal/Analyzer/AbstractCopilotCodeAnalysisService.cs +++ b/src/Features/ExternalAccess/Copilot/Internal/Analyzer/AbstractCopilotCodeAnalysisService.cs @@ -161,7 +161,7 @@ public async Task> GetCachedDocumentDiagnosticsAsync( protected virtual Task> GetDiagnosticsIntersectWithSpanAsync(Document document, IReadOnlyList diagnostics, TextSpan span, CancellationToken cancellationToken) { - return Task.FromResult(diagnostics.WhereAsArray((diagnostic, _) => diagnostic.Location.SourceSpan.IntersectsWith(span), state: (object)null)); + return Task.FromResult(diagnostics.WhereAsArray((diagnostic, _) => diagnostic.Location.SourceSpan.IntersectsWith(span), state: (object?)null)); } public async Task StartRefinementSessionAsync(Document oldDocument, Document newDocument, Diagnostic? primaryDiagnostic, CancellationToken cancellationToken) From 42afc4c164e5fb87124997481beb5908ca34e216 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Feb 2025 11:35:20 -0800 Subject: [PATCH 077/305] Provide a simple ArrayBuilder api that does not drop array builders when they have a large number of elements in them Search related type sections in other documents when doing a document-filtered nav to search Use null for simplicity Apply suggestions from code review Keep around large array instances for speedometer Use a separate pool Make consistent Revert Expose similar api on string builder --- ...aryPragmaSuppressionsDiagnosticAnalyzer.cs | 7 ++-- .../GoToAdjacentMemberCommandHandler.cs | 8 ++--- ...lAnalyzer.IncrementalMemberEditAnalyzer.cs | 7 ++-- ...crementalMemberEditAnalyzer_MemberSpans.cs | 6 ++-- .../Services/SyntaxFacts/CSharpSyntaxFacts.cs | 33 ++--------------- .../Core/CompilerExtensions.projitems | 1 + .../Compiler/Core/ObjectPools/ArrayBuilder.cs | 30 +++++++++++++--- .../Compiler/Core/ObjectPools/IPooled.cs | 2 +- .../Core/ObjectPools/PooledDictionary.cs | 4 +++ .../Core/ObjectPools/PooledDisposer.cs | 4 +-- .../Core/ObjectPools/PooledHashSet.cs | 4 +++ .../Core/ObjectPools/PooledStringBuilder.cs | 26 ++++++++++++-- .../SyntaxFacts/AbstractSyntaxFacts.cs | 21 +++++++++++ .../Core/Services/SyntaxFacts/ISyntaxFacts.cs | 8 +++-- .../SyntaxFacts/VisualBasicSyntaxFacts.vb | 36 ++++++++----------- 15 files changed, 122 insertions(+), 75 deletions(-) create mode 100644 src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/AbstractSyntaxFacts.cs diff --git a/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/AbstractRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/AbstractRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer.cs index e52174131c908..53f687fb9cd9a 100644 --- a/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/AbstractRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/AbstractRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer.cs @@ -745,10 +745,11 @@ private async Task ProcessSuppressMessageAttributesAsync( return false; } - using var pooledDeclarationNodes = SyntaxFacts.GetTopLevelAndMethodLevelMembers(root); - var declarationNodes = pooledDeclarationNodes.Object; + // Specifies false for discardLargeInstances as these objects commonly exceed the default ArrayBuilder capacity threshold. + using var _1 = ArrayBuilder.GetInstance(discardLargeInstances: false, out var declarationNodes); + this.SyntaxFacts.AddTopLevelAndMethodLevelMembers(root, declarationNodes); - using var _ = PooledHashSet.GetInstance(out var processedPartialSymbols); + using var _2 = PooledHashSet.GetInstance(out var processedPartialSymbols); if (declarationNodes.Count > 0) { foreach (var node in declarationNodes) diff --git a/src/EditorFeatures/Core/Editor/GoToAdjacentMemberCommandHandler.cs b/src/EditorFeatures/Core/Editor/GoToAdjacentMemberCommandHandler.cs index 923049a4c1d8b..c53f4d5102135 100644 --- a/src/EditorFeatures/Core/Editor/GoToAdjacentMemberCommandHandler.cs +++ b/src/EditorFeatures/Core/Editor/GoToAdjacentMemberCommandHandler.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Commanding; @@ -103,12 +104,11 @@ private bool ExecuteCommandImpl(EditorCommandArgs args, bool gotoNextMember, Com /// internal static int? GetTargetPosition(ISyntaxFactsService service, SyntaxNode root, int caretPosition, bool next) { - using var pooledMembers = service.GetMethodLevelMembers(root); - var members = pooledMembers.Object; + // Specifies false for discardLargeInstances as these objects commonly exceed the default ArrayBuilder capacity threshold. + using var _ = ArrayBuilder.GetInstance(discardLargeInstances: false, out var members); + service.AddMethodLevelMembers(root, members); if (members.Count == 0) - { return null; - } var starts = members.Select(m => MemberStart(m)).ToArray(); var index = Array.BinarySearch(starts, caretPosition); diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs index 9a075257bb932..bfbd34725b9fb 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; @@ -201,8 +200,10 @@ async Task ExecuteAnalyzersAsync( } var syntaxFacts = document.GetRequiredLanguageService(); - using var pooledMembers = syntaxFacts.GetMethodLevelMembers(root); - var members = pooledMembers.Object; + + // Specifies false for discardLargeInstances as these objects commonly exceed the default ArrayBuilder capacity threshold. + using var _ = ArrayBuilder.GetInstance(discardLargeInstances: false, out var members); + syntaxFacts.AddMethodLevelMembers(root, members); var memberSpans = members.SelectAsArray(member => member.FullSpan); var changedMemberId = members.IndexOf(changedMember); diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer_MemberSpans.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer_MemberSpans.cs index c76750e900a4c..05f4d5a6eaa09 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer_MemberSpans.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer_MemberSpans.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -47,8 +48,9 @@ static async Task> CreateMemberSpansAsync(Document docu var service = document.GetRequiredLanguageService(); var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - using var pooledMembers = service.GetMethodLevelMembers(root); - var members = pooledMembers.Object; + // Specifies false for discardLargeInstances as these objects commonly exceed the default ArrayBuilder capacity threshold. + using var _ = ArrayBuilder.GetInstance(discardLargeInstances: false, out var members); + service.AddMethodLevelMembers(root, members); return members.SelectAsArray(m => m.FullSpan); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs index 360493f97d2ec..9cbc626969729 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs @@ -23,13 +23,10 @@ namespace Microsoft.CodeAnalysis.CSharp.LanguageService; -internal class CSharpSyntaxFacts : ISyntaxFacts +internal class CSharpSyntaxFacts : AbstractSyntaxFacts, ISyntaxFacts { internal static readonly CSharpSyntaxFacts Instance = new(); - // Specifies false for trimOnFree as these objects commonly exceed the default ObjectPool threshold - private static readonly ObjectPool> s_syntaxNodeListPool = new ObjectPool>(() => [], trimOnFree: false); - protected CSharpSyntaxFacts() { } @@ -732,11 +729,7 @@ EnumMemberDeclarationSyntax or } public bool IsTopLevelNodeWithMembers([NotNullWhen(true)] SyntaxNode? node) - { - return node is BaseNamespaceDeclarationSyntax or - TypeDeclarationSyntax or - EnumDeclarationSyntax; - } + => node is BaseNamespaceDeclarationSyntax or BaseTypeDeclarationSyntax; private const string dotToken = "."; @@ -889,30 +882,10 @@ private static void AppendTypeParameterList(StringBuilder builder, TypeParameter } } - public PooledObject> GetTopLevelAndMethodLevelMembers(SyntaxNode? root) - { - var pooledObject = s_syntaxNodeListPool.GetPooledObject(); - var list = pooledObject.Object; - - AppendMembers(root, list, topLevel: true, methodLevel: true); - - return pooledObject; - } - - public PooledObject> GetMethodLevelMembers(SyntaxNode? root) - { - var pooledObject = s_syntaxNodeListPool.GetPooledObject(); - var list = pooledObject.Object; - - AppendMembers(root, list, topLevel: false, methodLevel: true); - - return pooledObject; - } - public SyntaxList GetMembersOfTypeDeclaration(SyntaxNode typeDeclaration) => ((TypeDeclarationSyntax)typeDeclaration).Members; - private void AppendMembers(SyntaxNode? node, List list, bool topLevel, bool methodLevel) + protected override void AppendMembers(SyntaxNode? node, ArrayBuilder list, bool topLevel, bool methodLevel) { Debug.Assert(topLevel || methodLevel); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems index cebfc2a68264f..eb02e14e9ba88 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems @@ -433,6 +433,7 @@ + diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/ArrayBuilder.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/ArrayBuilder.cs index c40663de34f02..1d6d7630eb2d9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/ArrayBuilder.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/ArrayBuilder.cs @@ -6,11 +6,10 @@ namespace Microsoft.CodeAnalysis.PooledObjects; internal sealed partial class ArrayBuilder : IPooled { + private static readonly ObjectPool> s_keepLargeInstancesPool = CreatePool(); + public static PooledDisposer> GetInstance(out ArrayBuilder instance) - { - instance = GetInstance(); - return new PooledDisposer>(instance); - } + => GetInstance(discardLargeInstances: true, out instance); public static PooledDisposer> GetInstance(int capacity, out ArrayBuilder instance) { @@ -23,4 +22,27 @@ public static PooledDisposer> GetInstance(int capacity, T fillWi instance = GetInstance(capacity, fillWithValue); return new PooledDisposer>(instance); } + + public static PooledDisposer> GetInstance(bool discardLargeInstances, out ArrayBuilder instance) + { + // If we're discarding large instances (the default behavior), then just use the normal pool. If we're not, use + // a specific pool so that *other* normal callers don't accidentally get it and discard it. + instance = discardLargeInstances ? GetInstance() : s_keepLargeInstancesPool.Allocate(); + return new PooledDisposer>(instance, discardLargeInstances); + } + + void IPooled.Free(bool discardLargeInstances) + { + // If we're discarding large instances, use the default behavior (which already does that). Otherwise, always + // clear and free the instance back to its originating pool. + if (discardLargeInstances) + { + Free(); + } + else + { + this.Clear(); + _pool?.Free(this); + } + } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/IPooled.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/IPooled.cs index 6a0d0d14d4128..0b687f3f3e697 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/IPooled.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/IPooled.cs @@ -6,5 +6,5 @@ namespace Microsoft.CodeAnalysis.PooledObjects; internal interface IPooled { - void Free(); + void Free(bool discardLargeInstances); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledDictionary.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledDictionary.cs index f431ba4ee31ce..c8e35d932b555 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledDictionary.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledDictionary.cs @@ -11,4 +11,8 @@ public static PooledDisposer> GetInstance(out PooledDicti instance = GetInstance(); return new PooledDisposer>(instance); } + + // Nothing special to do here. + void IPooled.Free(bool discardLargeInstance) + => this.Free(); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledDisposer.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledDisposer.cs index 1e922f60e5382..895dd39043fa7 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledDisposer.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledDisposer.cs @@ -8,9 +8,9 @@ namespace Microsoft.CodeAnalysis.PooledObjects; [NonCopyable] -internal readonly struct PooledDisposer(TPoolable instance) : IDisposable +internal readonly struct PooledDisposer(TPoolable instance, bool discardLargeInstances = true) : IDisposable where TPoolable : class, IPooled { void IDisposable.Dispose() - => instance?.Free(); + => instance?.Free(discardLargeInstances); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledHashSet.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledHashSet.cs index 7c68246ee11ae..64e7ea3c10b95 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledHashSet.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledHashSet.cs @@ -13,4 +13,8 @@ public static PooledDisposer> GetInstance(out PooledHashSet instance = GetInstance(); return new PooledDisposer>(instance); } + + // Nothing special to do here. + void IPooled.Free(bool discardLargeInstance) + => this.Free(); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledStringBuilder.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledStringBuilder.cs index 2d18c1ee3ed89..af156fd99bdd9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledStringBuilder.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledStringBuilder.cs @@ -8,10 +8,32 @@ namespace Microsoft.CodeAnalysis.PooledObjects; internal sealed partial class PooledStringBuilder : IPooled { + private static readonly ObjectPool s_keepLargeInstancesPool = CreatePool(); + public static PooledDisposer GetInstance(out StringBuilder instance) + => GetInstance(discardLargeInstances: true, out instance); + + public static PooledDisposer GetInstance(bool discardLargeInstances, out StringBuilder instance) { - var pooledInstance = GetInstance(); + // If we're discarding large instances (the default behavior), then just use the normal pool. If we're not, use + // a specific pool so that *other* normal callers don't accidentally get it and discard it. + var pooledInstance = discardLargeInstances ? GetInstance() : s_keepLargeInstancesPool.Allocate(); instance = pooledInstance; - return new PooledDisposer(pooledInstance); + return new PooledDisposer(pooledInstance, discardLargeInstances); + } + + void IPooled.Free(bool discardLargeInstances) + { + // If we're discarding large instances, use the default behavior (which already does that). Otherwise, always + // clear and free the instance back to its originating pool. + if (discardLargeInstances) + { + Free(); + } + else + { + this.Builder.Clear(); + _pool.Free(this); + } } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/AbstractSyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/AbstractSyntaxFacts.cs new file mode 100644 index 0000000000000..4b5bbc83d00ee --- /dev/null +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/AbstractSyntaxFacts.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.PooledObjects; + +namespace Microsoft.CodeAnalysis.LanguageService; + +internal abstract class AbstractSyntaxFacts +{ + public void AddTopLevelAndMethodLevelMembers(SyntaxNode? root, ArrayBuilder result) + => AppendMembers(root, result, topLevel: true, methodLevel: true); + + public void AddTopLevelMembers(SyntaxNode? root, ArrayBuilder result) + => AppendMembers(root, result, topLevel: true, methodLevel: false); + + public void AddMethodLevelMembers(SyntaxNode? root, ArrayBuilder result) + => AppendMembers(root, result, topLevel: false, methodLevel: true); + + protected abstract void AppendMembers(SyntaxNode? node, ArrayBuilder list, bool topLevel, bool methodLevel); +} diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs index e0abf008de631..7a2544c4bf028 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs @@ -7,6 +7,7 @@ using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Threading; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.LanguageService; @@ -410,9 +411,12 @@ void GetPartsOfTupleExpression(SyntaxNode node, SyntaxNode? ConvertToSingleLine(SyntaxNode? node, bool useElasticTrivia = false); // Violation. This is a feature level API. - PooledObject> GetTopLevelAndMethodLevelMembers(SyntaxNode? root); + void AddTopLevelAndMethodLevelMembers(SyntaxNode? root, ArrayBuilder result); // Violation. This is a feature level API. - PooledObject> GetMethodLevelMembers(SyntaxNode? root); + void AddTopLevelMembers(SyntaxNode? root, ArrayBuilder result); + // Violation. This is a feature level API. + void AddMethodLevelMembers(SyntaxNode? root, ArrayBuilder result); + SyntaxList GetMembersOfTypeDeclaration(SyntaxNode typeDeclaration); // Violation. This is a feature level API. diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb index 0c464486a45f4..7b2795760a82b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb @@ -21,15 +21,13 @@ Imports Microsoft.CodeAnalysis.Editing Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService Friend Class VisualBasicSyntaxFacts + Inherits AbstractSyntaxFacts Implements ISyntaxFacts Private Const DoesNotExistInVBErrorMessage = "This feature does not exist in VB" Public Shared ReadOnly Property Instance As New VisualBasicSyntaxFacts - ' Specifies false for trimOnFree as these objects commonly exceed the default ObjectPool threshold - Private Shared ReadOnly s_syntaxNodeListPool As ObjectPool(Of List(Of SyntaxNode)) = New ObjectPool(Of List(Of SyntaxNode))(Function() New List(Of SyntaxNode), trimOnFree:=False) - Protected Sub New() End Sub @@ -888,24 +886,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService Return TextSpan.FromBounds(list.First.SpanStart, list.Last.Span.End) End Function - Public Function GetTopLevelAndMethodLevelMembers(root As SyntaxNode) As PooledObject(Of List(Of SyntaxNode)) Implements ISyntaxFacts.GetTopLevelAndMethodLevelMembers - Dim pooledList = PooledObject(Of List(Of SyntaxNode)).Create(s_syntaxNodeListPool) - Dim list = pooledList.Object - - AppendMembers(root, list, topLevel:=True, methodLevel:=True) - - Return pooledList - End Function - - Public Function GetMethodLevelMembers(root As SyntaxNode) As PooledObject(Of List(Of SyntaxNode)) Implements ISyntaxFacts.GetMethodLevelMembers - Dim pooledList = PooledObject(Of List(Of SyntaxNode)).Create(s_syntaxNodeListPool) - Dim list = pooledList.Object - - AppendMembers(root, list, topLevel:=False, methodLevel:=True) - - Return pooledList - End Function - Public Function GetMembersOfTypeDeclaration(typeDeclaration As SyntaxNode) As SyntaxList(Of SyntaxNode) Implements ISyntaxFacts.GetMembersOfTypeDeclaration Return DirectCast(typeDeclaration, TypeBlockSyntax).Members End Function @@ -1050,7 +1030,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService End If End Sub - Private Sub AppendMembers(node As SyntaxNode, list As List(Of SyntaxNode), topLevel As Boolean, methodLevel As Boolean) + Protected Overrides Sub AppendMembers(node As SyntaxNode, list As ArrayBuilder(Of SyntaxNode), topLevel As Boolean, methodLevel As Boolean) Debug.Assert(topLevel OrElse methodLevel) For Each member In node.GetMembers() @@ -1998,6 +1978,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService Return DirectCast(node, LiteralExpressionSyntax).Token End Function + Private Sub ISyntaxFacts_AddTopLevelAndMethodLevelMembers(root As SyntaxNode, result As ArrayBuilder(Of SyntaxNode)) Implements ISyntaxFacts.AddTopLevelAndMethodLevelMembers + AddTopLevelAndMethodLevelMembers(root, result) + End Sub + + Private Sub ISyntaxFacts_AddTopLevelMembers(root As SyntaxNode, result As ArrayBuilder(Of SyntaxNode)) Implements ISyntaxFacts.AddTopLevelMembers + AddTopLevelMembers(root, result) + End Sub + + Private Sub ISyntaxFacts_AddMethodLevelMembers(root As SyntaxNode, result As ArrayBuilder(Of SyntaxNode)) Implements ISyntaxFacts.AddMethodLevelMembers + AddMethodLevelMembers(root, result) + End Sub + #End Region End Class From 274fead8618c33879ca80517468a407d68c019a3 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Feb 2025 17:23:59 -0800 Subject: [PATCH 078/305] Fix --- .../Core/ObjectPools/PooledStringBuilder.cs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledStringBuilder.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledStringBuilder.cs index 7c1cd07314901..af156fd99bdd9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledStringBuilder.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledStringBuilder.cs @@ -36,19 +36,4 @@ void IPooled.Free(bool discardLargeInstances) _pool.Free(this); } } - - void IPooled.Free(bool discardLargeInstances) - { - // If we're discarding large instances, use the default behavior (which already does that). Otherwise, always - // clear and free the instance back to its originating pool. - if (discardLargeInstances) - { - Free(); - } - else - { - this.Builder.Clear(); - _pool.Free(this); - } - } } From e83c680ed7942e0ddec6f45f1981a4d9c7f8085a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Feb 2025 18:49:59 -0800 Subject: [PATCH 079/305] Further cleanup and code reduction in analyzer driver. (#76921) Co-authored-by: Todd Grunke --- .../DiagnosticAnalyzer/AnalyzerExecutor.cs | 186 +++++++++--------- 1 file changed, 94 insertions(+), 92 deletions(-) diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs index bd81e6296edee..19d913bc9aa2a 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs @@ -667,21 +667,18 @@ public void ExecuteAdditionalFileActions( private void ExecuteSyntaxNodeAction( SyntaxNodeAnalyzerAction syntaxNodeAction, SyntaxNode node, - ISymbol containingSymbol, - SemanticModel semanticModel, + ExecutionData executionData, Action addDiagnostic, Func isSupportedDiagnostic, - TextSpan? filterSpan, - bool isGeneratedCode, CancellationToken cancellationToken) where TLanguageKindEnum : struct { - Debug.Assert(!isGeneratedCode || !_shouldSkipAnalysisOnGeneratedCode(syntaxNodeAction.Analyzer)); + Debug.Assert(!executionData.IsGeneratedCode || !_shouldSkipAnalysisOnGeneratedCode(syntaxNodeAction.Analyzer)); Debug.Assert(!IsAnalyzerSuppressedForTree(syntaxNodeAction.Analyzer, node.SyntaxTree, cancellationToken)); var syntaxNodeContext = new SyntaxNodeAnalysisContext( - node, containingSymbol, semanticModel, AnalyzerOptions, addDiagnostic, - isSupportedDiagnostic, filterSpan, isGeneratedCode, cancellationToken); + node, executionData.DeclaredSymbol, executionData.SemanticModel, AnalyzerOptions, addDiagnostic, + isSupportedDiagnostic, executionData.FilterSpan, executionData.IsGeneratedCode, cancellationToken); ExecuteAndCatchIfThrows( syntaxNodeAction.Analyzer, @@ -694,21 +691,18 @@ private void ExecuteSyntaxNodeAction( private void ExecuteOperationAction( OperationAnalyzerAction operationAction, IOperation operation, - ISymbol containingSymbol, - SemanticModel semanticModel, + ExecutionData executionData, Action addDiagnostic, Func isSupportedDiagnostic, - TextSpan? filterSpan, - bool isGeneratedCode, CancellationToken cancellationToken) { - Debug.Assert(!isGeneratedCode || !_shouldSkipAnalysisOnGeneratedCode(operationAction.Analyzer)); - Debug.Assert(!IsAnalyzerSuppressedForTree(operationAction.Analyzer, semanticModel.SyntaxTree, cancellationToken)); + Debug.Assert(!executionData.IsGeneratedCode || !_shouldSkipAnalysisOnGeneratedCode(operationAction.Analyzer)); + Debug.Assert(!IsAnalyzerSuppressedForTree(operationAction.Analyzer, executionData.SemanticModel.SyntaxTree, cancellationToken)); var operationContext = new OperationAnalysisContext( - operation, containingSymbol, semanticModel.Compilation, + operation, executionData.DeclaredSymbol, executionData.SemanticModel.Compilation, AnalyzerOptions, addDiagnostic, isSupportedDiagnostic, GetControlFlowGraph, - filterSpan, isGeneratedCode, cancellationToken); + executionData.FilterSpan, executionData.IsGeneratedCode, cancellationToken); ExecuteAndCatchIfThrows( operationAction.Analyzer, @@ -718,6 +712,20 @@ private void ExecuteOperationAction( cancellationToken); } + private readonly struct ExecutionData( + DiagnosticAnalyzer analyzer, + ISymbol declaredSymbol, + SemanticModel semanticModel, + TextSpan? filterSpan, + bool isGeneratedCode) + { + public readonly DiagnosticAnalyzer Analyzer = analyzer; + public readonly ISymbol DeclaredSymbol = declaredSymbol; + public readonly SemanticModel SemanticModel = semanticModel; + public readonly TextSpan? FilterSpan = filterSpan; + public readonly bool IsGeneratedCode = isGeneratedCode; + } + /// /// Execute code block actions for the given analyzer for the given declaration. /// @@ -744,33 +752,31 @@ public void ExecuteCodeBlockActions( startActions, actions, endActions, - analyzer, declaredNode, - declaredSymbol, - semanticModel, - isGeneratedCode, - addActions: static (startAction, endActions, args, cancellationToken) => + new ExecutionData(analyzer, declaredSymbol, semanticModel, filterSpan, isGeneratedCode), + addActions: static (startAction, endActions, executionData, args, cancellationToken) => { - var (@this, startActions, analyzer, declaredNode, declaredSymbol, executableCodeBlocks, semanticModel, getKind, filterSpan, isGeneratedCode, ephemeralActions) = args; + var (@this, startActions, executableCodeBlocks, declaredNode, getKind, ephemeralActions) = args; var scope = new HostCodeBlockStartAnalysisScope(startAction.Analyzer); var startContext = new AnalyzerCodeBlockStartAnalysisContext( - scope, declaredNode, declaredSymbol, semanticModel, @this.AnalyzerOptions, filterSpan, isGeneratedCode, cancellationToken); + scope, declaredNode, executionData.DeclaredSymbol, executionData.SemanticModel, + @this.AnalyzerOptions, executionData.FilterSpan, executionData.IsGeneratedCode, cancellationToken); // Catch Exception from the start action. @this.ExecuteAndCatchIfThrows( startAction.Analyzer, static args => args.startAction.Action(args.startContext), argument: (startAction, startContext), - new AnalysisContextInfo(@this.Compilation, declaredSymbol, declaredNode), + new AnalysisContextInfo(@this.Compilation, executionData.DeclaredSymbol, declaredNode), cancellationToken); endActions.AddAll(scope.CodeBlockEndActions); ephemeralActions.AddRange(scope.SyntaxNodeActions); }, - executeActions: static (diagReporter, isSupportedDiagnostic, args, cancellationToken) => + executeActions: static (diagReporter, isSupportedDiagnostic, executionData, args, cancellationToken) => { - var (@this, startActions, analyzer, declaredNode, declaredSymbol, executableCodeBlocks, semanticModel, getKind, filterSpan, isGeneratedCode, ephemeralActions) = args; + var (@this, startActions, executableCodeBlocks, declaredNode, getKind, ephemeralActions) = args; if (ephemeralActions.Any()) { var executableNodeActionsByKind = GetNodeActionsByKind(ephemeralActions); @@ -778,7 +784,7 @@ public void ExecuteCodeBlockActions( foreach (var block in executableCodeBlocks) { - var filter = semanticModel.GetSyntaxNodesToAnalyzeFilter(block, declaredSymbol); + var filter = executionData.SemanticModel.GetSyntaxNodesToAnalyzeFilter(block, executionData.DeclaredSymbol); if (filter is not null) { foreach (var descendantNode in block.DescendantNodesAndSelf(descendIntoChildren: filter)) @@ -794,20 +800,19 @@ public void ExecuteCodeBlockActions( } @this.ExecuteSyntaxNodeActions( - syntaxNodesToAnalyze, executableNodeActionsByKind, - analyzer, declaredSymbol, semanticModel, getKind, diagReporter, - isSupportedDiagnostic, filterSpan, isGeneratedCode, + syntaxNodesToAnalyze, executableNodeActionsByKind, executionData, + getKind, diagReporter, isSupportedDiagnostic, hasCodeBlockStartOrSymbolStartActions: startActions.Any(), cancellationToken); syntaxNodesToAnalyze.Free(); } }, - executeBlockActions: static (blockActions, diagReporter, isSupportedDiagnostic, args, cancellationToken) => + executeBlockActions: static (blockActions, diagReporter, isSupportedDiagnostic, executionData, args, cancellationToken) => { - var (@this, startActions, analyzer, declaredNode, declaredSymbol, executableCodeBlocks, semanticModel, getKind, filterSpan, isGeneratedCode, ephemeralActions) = args; + var (@this, startActions, executableCodeBlocks, declaredNode, getKind, ephemeralActions) = args; - var context = new CodeBlockAnalysisContext(declaredNode, declaredSymbol, semanticModel, - @this.AnalyzerOptions, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, filterSpan, isGeneratedCode, cancellationToken); + var context = new CodeBlockAnalysisContext(declaredNode, executionData.DeclaredSymbol, executionData.SemanticModel, + @this.AnalyzerOptions, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, executionData.FilterSpan, executionData.IsGeneratedCode, cancellationToken); foreach (var blockAction in blockActions) { @@ -815,11 +820,11 @@ public void ExecuteCodeBlockActions( blockAction.Analyzer, static data => data.blockAction.Action(data.context), (blockAction, context), - new AnalysisContextInfo(@this.Compilation, declaredSymbol, declaredNode), + new AnalysisContextInfo(@this.Compilation, executionData.DeclaredSymbol, declaredNode), cancellationToken); } }, - argument: (@this: this, startActions, analyzer, declaredNode, declaredSymbol, executableCodeBlocks, semanticModel, getKind, filterSpan, isGeneratedCode, ephemeralActions), + argument: (@this: this, startActions, executableCodeBlocks, declaredNode, getKind, ephemeralActions), cancellationToken); ephemeralActions.Free(); } @@ -849,49 +854,46 @@ public void ExecuteOperationBlockActions( startActions, actions, endActions, - analyzer, declaredNode, - declaredSymbol, - semanticModel, - isGeneratedCode, - addActions: static (startAction, endActions, args, cancellationToken) => + new ExecutionData(analyzer, declaredSymbol, semanticModel, filterSpan, isGeneratedCode), + addActions: static (startAction, endActions, executionData, args, cancellationToken) => { - var (@this, startActions, analyzer, declaredNode, operationBlocks, declaredSymbol, operations, semanticModel, filterSpan, isGeneratedCode, ephemeralActions) = args; + var (@this, startActions, declaredNode, operationBlocks, operations, ephemeralActions) = args; var scope = new HostOperationBlockStartAnalysisScope(startAction.Analyzer); var startContext = new AnalyzerOperationBlockStartAnalysisContext( - scope, operationBlocks, declaredSymbol, semanticModel.Compilation, @this.AnalyzerOptions, - @this.GetControlFlowGraph, declaredNode.SyntaxTree, filterSpan, isGeneratedCode, cancellationToken); + scope, operationBlocks, executionData.DeclaredSymbol, executionData.SemanticModel.Compilation, @this.AnalyzerOptions, + @this.GetControlFlowGraph, declaredNode.SyntaxTree, executionData.FilterSpan, executionData.IsGeneratedCode, cancellationToken); // Catch Exception from the start action. @this.ExecuteAndCatchIfThrows( startAction.Analyzer, static args => args.startAction.Action(args.startContext), argument: (startAction, startContext), - new AnalysisContextInfo(@this.Compilation, declaredSymbol), + new AnalysisContextInfo(@this.Compilation, executionData.DeclaredSymbol), cancellationToken); endActions.AddAll(scope.OperationBlockEndActions); ephemeralActions.AddRange(scope.OperationActions); }, - executeActions: static (diagReporter, isSupportedDiagnostic, args, cancellationToken) => + executeActions: static (diagReporter, isSupportedDiagnostic, executionData, args, cancellationToken) => { - var (@this, startActions, analyzer, declaredNode, operationBlocks, declaredSymbol, operations, semanticModel, filterSpan, isGeneratedCode, ephemeralActions) = args; + var (@this, startActions, declaredNode, operationBlocks, operations, ephemeralActions) = args; if (ephemeralActions.Any()) { @this.ExecuteOperationActions( operations, GetOperationActionsByKind(ephemeralActions), - analyzer, declaredSymbol, semanticModel, diagReporter, - isSupportedDiagnostic, filterSpan, isGeneratedCode, + executionData, diagReporter, isSupportedDiagnostic, hasOperationBlockStartOrSymbolStartActions: startActions.Any(), cancellationToken); } }, - executeBlockActions: static (blockActions, diagReporter, isSupportedDiagnostic, args, cancellationToken) => + executeBlockActions: static (blockActions, diagReporter, isSupportedDiagnostic, executionData, args, cancellationToken) => { - var (@this, startActions, analyzer, declaredNode, operationBlocks, declaredSymbol, operations, semanticModel, filterSpan, isGeneratedCode, ephemeralActions) = args; + var (@this, startActions, declaredNode, operationBlocks, operations, ephemeralActions) = args; - var context = new OperationBlockAnalysisContext(operationBlocks, declaredSymbol, semanticModel.Compilation, - @this.AnalyzerOptions, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, @this.GetControlFlowGraph, declaredNode.SyntaxTree, filterSpan, isGeneratedCode, cancellationToken); + var context = new OperationBlockAnalysisContext(operationBlocks, executionData.DeclaredSymbol, @this.Compilation, + @this.AnalyzerOptions, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, @this.GetControlFlowGraph, declaredNode.SyntaxTree, + executionData.FilterSpan, executionData.IsGeneratedCode, cancellationToken); foreach (var blockAction in blockActions) { @@ -899,11 +901,11 @@ public void ExecuteOperationBlockActions( blockAction.Analyzer, static data => data.blockAction.Action(data.context), (blockAction, context), - new AnalysisContextInfo(@this.Compilation, declaredSymbol), + new AnalysisContextInfo(@this.Compilation, executionData.DeclaredSymbol), cancellationToken); } }, - argument: (@this: this, startActions, analyzer, declaredNode, operationBlocks, declaredSymbol, operations, semanticModel, filterSpan, isGeneratedCode, ephemeralActions), + argument: (@this: this, startActions, declaredNode, operationBlocks, operations, ephemeralActions), cancellationToken); ephemeralActions.Free(); } @@ -912,14 +914,11 @@ private void ExecuteBlockActionsCore( ImmutableArray startActions, ImmutableArray actions, ImmutableArray endActions, - DiagnosticAnalyzer analyzer, SyntaxNode declaredNode, - ISymbol declaredSymbol, - SemanticModel semanticModel, - bool isGeneratedCode, - Action, TArgs, CancellationToken> addActions, - Action, TArgs, CancellationToken> executeActions, - Action, AnalyzerDiagnosticReporter, Func, TArgs, CancellationToken> executeBlockActions, + ExecutionData executionData, + Action, ExecutionData, TArgs, CancellationToken> addActions, + Action, ExecutionData, TArgs, CancellationToken> executeActions, + Action, AnalyzerDiagnosticReporter, Func, ExecutionData, TArgs, CancellationToken> executeBlockActions, TArgs argument, CancellationToken cancellationToken) where TBlockStartAction : AnalyzerAction @@ -927,12 +926,12 @@ private void ExecuteBlockActionsCore( where TArgs : struct { Debug.Assert(declaredNode != null); - Debug.Assert(declaredSymbol != null); - Debug.Assert(CanHaveExecutableCodeBlock(declaredSymbol)); + Debug.Assert(executionData.DeclaredSymbol != null); + Debug.Assert(CanHaveExecutableCodeBlock(executionData.DeclaredSymbol)); Debug.Assert(startActions.Any() || endActions.Any() || actions.Any()); - if (isGeneratedCode && _shouldSkipAnalysisOnGeneratedCode(analyzer) || - IsAnalyzerSuppressedForTree(analyzer, declaredNode.SyntaxTree, cancellationToken)) + if (executionData.IsGeneratedCode && _shouldSkipAnalysisOnGeneratedCode(executionData.Analyzer) || + IsAnalyzerSuppressedForTree(executionData.Analyzer, declaredNode.SyntaxTree, cancellationToken)) { return; } @@ -948,22 +947,23 @@ private void ExecuteBlockActionsCore( // Include the initial code block end actions. blockEndActions.AddAll(endActions); - var diagReporter = GetAddSemanticDiagnostic(semanticModel.SyntaxTree, declaredNode.FullSpan, analyzer, cancellationToken); + var diagReporter = GetAddSemanticDiagnostic( + executionData.SemanticModel.SyntaxTree, declaredNode.FullSpan, executionData.Analyzer, cancellationToken); // Include the stateful actions. foreach (var startAction in startActions) - addActions(startAction, blockEndActions, argument, cancellationToken); + addActions(startAction, blockEndActions, executionData, argument, cancellationToken); using var _ = PooledDelegates.GetPooledFunction( - static (d, ct, arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), - (self: this, analyzer), + static (d, ct, arg) => arg.self.IsSupportedDiagnostic(arg.Analyzer, d, ct), + (self: this, executionData.Analyzer), out Func isSupportedDiagnostic); // Execute stateful executable node analyzers, if any. - executeActions(diagReporter, isSupportedDiagnostic, argument, cancellationToken); + executeActions(diagReporter, isSupportedDiagnostic, executionData, argument, cancellationToken); - executeBlockActions(blockActions, diagReporter, isSupportedDiagnostic, argument, cancellationToken); - executeBlockActions(blockEndActions, diagReporter, isSupportedDiagnostic, argument, cancellationToken); + executeBlockActions(blockActions, diagReporter, isSupportedDiagnostic, executionData, argument, cancellationToken); + executeBlockActions(blockEndActions, diagReporter, isSupportedDiagnostic, executionData, argument, cancellationToken); diagReporter.Free(); blockActions.Free(); @@ -1018,28 +1018,29 @@ public void ExecuteSyntaxNodeActions( static (d, ct, arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), (self: this, analyzer), out Func isSupportedDiagnostic); - ExecuteSyntaxNodeActions(nodesToAnalyze, nodeActionsByKind, analyzer, declaredSymbol, model, getKind, diagReporter, isSupportedDiagnostic, filterSpan, isGeneratedCode, hasCodeBlockStartOrSymbolStartActions, cancellationToken); + + ExecuteSyntaxNodeActions( + nodesToAnalyze, nodeActionsByKind, + new ExecutionData(analyzer, declaredSymbol, model, filterSpan, isGeneratedCode), + getKind, diagReporter, isSupportedDiagnostic, hasCodeBlockStartOrSymbolStartActions, cancellationToken); + diagReporter.Free(); } private void ExecuteSyntaxNodeActions( ArrayBuilder nodesToAnalyze, ImmutableSegmentedDictionary>> nodeActionsByKind, - DiagnosticAnalyzer analyzer, - ISymbol containingSymbol, - SemanticModel model, + ExecutionData executionData, Func getKind, AnalyzerDiagnosticReporter diagReporter, Func isSupportedDiagnostic, - TextSpan? filterSpan, - bool isGeneratedCode, bool hasCodeBlockStartOrSymbolStartActions, CancellationToken cancellationToken) where TLanguageKindEnum : struct { Debug.Assert(nodeActionsByKind.Any()); - Debug.Assert(!isGeneratedCode || !_shouldSkipAnalysisOnGeneratedCode(analyzer)); - Debug.Assert(!IsAnalyzerSuppressedForTree(analyzer, model.SyntaxTree, cancellationToken)); + Debug.Assert(!executionData.IsGeneratedCode || !_shouldSkipAnalysisOnGeneratedCode(executionData.Analyzer)); + Debug.Assert(!IsAnalyzerSuppressedForTree(executionData.Analyzer, executionData.SemanticModel.SyntaxTree, cancellationToken)); foreach (var node in nodesToAnalyze) { @@ -1049,7 +1050,7 @@ private void ExecuteSyntaxNodeActions( if (nodeActionsByKind.TryGetValue(getKind(node), out var actionsForKind)) { RoslynDebug.Assert(!actionsForKind.IsEmpty, $"Unexpected empty action collection in {nameof(nodeActionsByKind)}"); - if (ShouldExecuteNode(node, analyzer, cancellationToken)) + if (ShouldExecuteNode(node, executionData.Analyzer, cancellationToken)) { // If analyzer hasn't registered any CodeBlockStart or SymbolStart actions, then update the filter span // for local diagnostics to be the callback node's full span. @@ -1060,7 +1061,7 @@ private void ExecuteSyntaxNodeActions( foreach (var action in actionsForKind) { - ExecuteSyntaxNodeAction(action, node, containingSymbol, model, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, filterSpan, isGeneratedCode, cancellationToken); + ExecuteSyntaxNodeAction(action, node, executionData, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, cancellationToken); } } } @@ -1116,27 +1117,28 @@ public void ExecuteOperationActions( static (d, ct, arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), (self: this, analyzer), out Func isSupportedDiagnostic); - ExecuteOperationActions(operationsToAnalyze, operationActionsByKind, analyzer, declaredSymbol, model, diagReporter, isSupportedDiagnostic, filterSpan, isGeneratedCode, hasOperationBlockStartOrSymbolStartActions, cancellationToken); + + ExecuteOperationActions( + operationsToAnalyze, operationActionsByKind, + new ExecutionData(analyzer, declaredSymbol, model, filterSpan, isGeneratedCode), + diagReporter, isSupportedDiagnostic, hasOperationBlockStartOrSymbolStartActions, cancellationToken); + diagReporter.Free(); } private void ExecuteOperationActions( ImmutableArray operationsToAnalyze, ImmutableSegmentedDictionary> operationActionsByKind, - DiagnosticAnalyzer analyzer, - ISymbol containingSymbol, - SemanticModel model, + ExecutionData executionData, AnalyzerDiagnosticReporter diagReporter, Func isSupportedDiagnostic, - TextSpan? filterSpan, - bool isGeneratedCode, bool hasOperationBlockStartOrSymbolStartActions, CancellationToken cancellationToken) { Debug.Assert(operationActionsByKind != null); Debug.Assert(operationActionsByKind.Any()); - Debug.Assert(!isGeneratedCode || !_shouldSkipAnalysisOnGeneratedCode(analyzer)); - Debug.Assert(!IsAnalyzerSuppressedForTree(analyzer, model.SyntaxTree, cancellationToken)); + Debug.Assert(!executionData.IsGeneratedCode || !_shouldSkipAnalysisOnGeneratedCode(executionData.Analyzer)); + Debug.Assert(!IsAnalyzerSuppressedForTree(executionData.Analyzer, executionData.SemanticModel.SyntaxTree, cancellationToken)); foreach (var operation in operationsToAnalyze) { @@ -1146,7 +1148,7 @@ private void ExecuteOperationActions( if (operationActionsByKind.TryGetValue(operation.Kind, out var actionsForKind)) { RoslynDebug.Assert(!actionsForKind.IsEmpty, $"Unexpected empty action collection in {nameof(operationActionsByKind)}"); - if (ShouldExecuteOperation(operation, analyzer, cancellationToken)) + if (ShouldExecuteOperation(operation, executionData.Analyzer, cancellationToken)) { // If analyzer hasn't registered any OperationBlockStart or SymbolStart actions, then update // the filter span for local diagnostics to be the callback operation's full span. @@ -1157,7 +1159,7 @@ private void ExecuteOperationActions( foreach (var action in actionsForKind) { - ExecuteOperationAction(action, operation, containingSymbol, model, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, filterSpan, isGeneratedCode, cancellationToken); + ExecuteOperationAction(action, operation, executionData, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, cancellationToken); } } } From 5bb01b40fcffd2a778f329f5d4e8c2b3ee2907f3 Mon Sep 17 00:00:00 2001 From: dotnet bot Date: Wed, 5 Feb 2025 11:51:01 -0800 Subject: [PATCH 080/305] Localized file check-in by OneLocBuild Task: Build definition ID 327: Build ID 2635853 --- src/EditorFeatures/Core/xlf/EditorFeaturesResources.cs.xlf | 2 +- src/EditorFeatures/Core/xlf/EditorFeaturesResources.de.xlf | 2 +- src/EditorFeatures/Core/xlf/EditorFeaturesResources.es.xlf | 2 +- src/EditorFeatures/Core/xlf/EditorFeaturesResources.fr.xlf | 2 +- src/EditorFeatures/Core/xlf/EditorFeaturesResources.it.xlf | 2 +- src/EditorFeatures/Core/xlf/EditorFeaturesResources.ja.xlf | 2 +- src/EditorFeatures/Core/xlf/EditorFeaturesResources.ko.xlf | 2 +- src/EditorFeatures/Core/xlf/EditorFeaturesResources.pl.xlf | 2 +- src/EditorFeatures/Core/xlf/EditorFeaturesResources.pt-BR.xlf | 2 +- src/EditorFeatures/Core/xlf/EditorFeaturesResources.ru.xlf | 2 +- src/EditorFeatures/Core/xlf/EditorFeaturesResources.tr.xlf | 2 +- src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hans.xlf | 2 +- src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hant.xlf | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.cs.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.cs.xlf index cda4910f11c0d..02ee0c7190b58 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.cs.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.cs.xlf @@ -19,7 +19,7 @@ Chat limit reached, [upgrade now] or wait for the limit to reset. - Chat limit reached, [upgrade now] or wait for the limit to reset. + Bylo dosaženo limitu chatu. [Upgradujte nyní] nebo počkejte na resetování limitu. The text surrounded by "[" and "]" characters will be hyperlinked. Please ensure the localized text still has "[" and "]" characters. diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.de.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.de.xlf index 223527ee2ae83..251a1a6ba30ba 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.de.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.de.xlf @@ -19,7 +19,7 @@ Chat limit reached, [upgrade now] or wait for the limit to reset. - Chat limit reached, [upgrade now] or wait for the limit to reset. + Das Chatlimit wurde erreicht, [upgrade now] oder warten Sie, bis das Limit zurückgesetzt wurde. The text surrounded by "[" and "]" characters will be hyperlinked. Please ensure the localized text still has "[" and "]" characters. diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.es.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.es.xlf index 8f96fcc9c21d7..c655afaa7ef4a 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.es.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.es.xlf @@ -19,7 +19,7 @@ Chat limit reached, [upgrade now] or wait for the limit to reset. - Chat limit reached, [upgrade now] or wait for the limit to reset. + Se alcanzó el límite de chats, [upgrade now] o esperar a que se restablezca el límite. The text surrounded by "[" and "]" characters will be hyperlinked. Please ensure the localized text still has "[" and "]" characters. diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.fr.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.fr.xlf index cd4dc9731af30..961b6268a1cba 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.fr.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.fr.xlf @@ -19,7 +19,7 @@ Chat limit reached, [upgrade now] or wait for the limit to reset. - Chat limit reached, [upgrade now] or wait for the limit to reset. + La limite de conversation a été atteinte, [upgrade now] ou attendez la réinitialisation de la limite. The text surrounded by "[" and "]" characters will be hyperlinked. Please ensure the localized text still has "[" and "]" characters. diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.it.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.it.xlf index 264651a81a1d2..b6a038b54c877 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.it.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.it.xlf @@ -19,7 +19,7 @@ Chat limit reached, [upgrade now] or wait for the limit to reset. - Chat limit reached, [upgrade now] or wait for the limit to reset. + È stato raggiunto il limite di chat, [upgrade now] o attendere la reimpostazione del limite. The text surrounded by "[" and "]" characters will be hyperlinked. Please ensure the localized text still has "[" and "]" characters. diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ja.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ja.xlf index 162b9ff3a5da9..acf184340e87a 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ja.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ja.xlf @@ -19,7 +19,7 @@ Chat limit reached, [upgrade now] or wait for the limit to reset. - Chat limit reached, [upgrade now] or wait for the limit to reset. + チャットの制限に達しました。[今すぐアップグレード] するか、制限がリセットされるまでお待ちください。 The text surrounded by "[" and "]" characters will be hyperlinked. Please ensure the localized text still has "[" and "]" characters. diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ko.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ko.xlf index 967a8c3d65f76..f55e5980a48bc 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ko.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ko.xlf @@ -19,7 +19,7 @@ Chat limit reached, [upgrade now] or wait for the limit to reset. - Chat limit reached, [upgrade now] or wait for the limit to reset. + 채팅 제한에 도달했습니다. [upgrade now] 또는 제한이 재설정될 때까지 기다리세요. The text surrounded by "[" and "]" characters will be hyperlinked. Please ensure the localized text still has "[" and "]" characters. diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pl.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pl.xlf index 13c31e460e558..0e512ff2be43d 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pl.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pl.xlf @@ -19,7 +19,7 @@ Chat limit reached, [upgrade now] or wait for the limit to reset. - Chat limit reached, [upgrade now] or wait for the limit to reset. + Osiągnięto limit czatu, [upgrade now] lub poczekaj na zresetowanie limitu. The text surrounded by "[" and "]" characters will be hyperlinked. Please ensure the localized text still has "[" and "]" characters. diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pt-BR.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pt-BR.xlf index f0b76297e818e..40b3167932d02 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pt-BR.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pt-BR.xlf @@ -19,7 +19,7 @@ Chat limit reached, [upgrade now] or wait for the limit to reset. - Chat limit reached, [upgrade now] or wait for the limit to reset. + Limite de chat atingido, [upgrade now] ou aguarde até que o limite seja redefinido. The text surrounded by "[" and "]" characters will be hyperlinked. Please ensure the localized text still has "[" and "]" characters. diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ru.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ru.xlf index 882882e284155..6d2105d4343be 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ru.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ru.xlf @@ -19,7 +19,7 @@ Chat limit reached, [upgrade now] or wait for the limit to reset. - Chat limit reached, [upgrade now] or wait for the limit to reset. + Достигнут предел использования чата. [Повысьте статус сейчас] или дождитесь сброса ограничения. The text surrounded by "[" and "]" characters will be hyperlinked. Please ensure the localized text still has "[" and "]" characters. diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.tr.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.tr.xlf index 6aff6dc59e379..be6fcde4ffa57 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.tr.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.tr.xlf @@ -19,7 +19,7 @@ Chat limit reached, [upgrade now] or wait for the limit to reset. - Chat limit reached, [upgrade now] or wait for the limit to reset. + Sohbet sınırına ulaşıldı, [upgrade now] veya sınırın sıfırlanması için bekleyin. The text surrounded by "[" and "]" characters will be hyperlinked. Please ensure the localized text still has "[" and "]" characters. diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hans.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hans.xlf index 0bed7187047f0..a985dc67447b9 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hans.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hans.xlf @@ -19,7 +19,7 @@ Chat limit reached, [upgrade now] or wait for the limit to reset. - Chat limit reached, [upgrade now] or wait for the limit to reset. + 已达到聊天限制,[upgrade now] 或等待重置限制。 The text surrounded by "[" and "]" characters will be hyperlinked. Please ensure the localized text still has "[" and "]" characters. diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hant.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hant.xlf index 7daf637ef2194..9ebef803db3be 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hant.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hant.xlf @@ -19,7 +19,7 @@ Chat limit reached, [upgrade now] or wait for the limit to reset. - Chat limit reached, [upgrade now] or wait for the limit to reset. + 已達到聊天限制,[upgrade now] 或等候重設限制。 The text surrounded by "[" and "]" characters will be hyperlinked. Please ensure the localized text still has "[" and "]" characters. From 8a3fb46f36ed1bf2c73e4b6e0c0ad8401f59b009 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 6 Feb 2025 15:59:19 +0100 Subject: [PATCH 081/305] Revert "System.CommandLine update" (#77078) --- eng/Version.Details.xml | 8 ++--- eng/Versions.props | 2 +- src/Features/Lsif/Generator/Program.cs | 16 ++++----- .../Program.cs | 33 ++++++++++--------- src/Tools/BuildValidator/Program.cs | 18 +++++----- src/Workspaces/MSBuild/BuildHost/Program.cs | 10 +++--- 6 files changed, 44 insertions(+), 43 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 740a4b801c34a..7d5a6a3201211 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -13,14 +13,14 @@ 3f926184cacc477e3ddccd1eb4f3ebcb9a2b81a5 - + https://github.com/dotnet/command-line-api - 060374e56c1b2e741b6525ca8417006efb54fbd7 + feb61c7f328a2401d74f4317b39d02126cfdfe24 - + https://github.com/dotnet/command-line-api - 060374e56c1b2e741b6525ca8417006efb54fbd7 + feb61c7f328a2401d74f4317b39d02126cfdfe24 diff --git a/eng/Versions.props b/eng/Versions.props index 43f29daca07ae..08c62a289f638 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -46,7 +46,7 @@ Versions managed by Arcade (see Versions.Details.xml) --> - 2.0.0-beta4.25072.1 + 2.0.0-beta4.24528.1 8.0.0 8.0.0 8.0.0 diff --git a/src/Features/Lsif/Generator/Program.cs b/src/Features/Lsif/Generator/Program.cs index f3ee22fb79c11..940a0c69698c6 100644 --- a/src/Features/Lsif/Generator/Program.cs +++ b/src/Features/Lsif/Generator/Program.cs @@ -26,17 +26,17 @@ internal static class Program { public static Task Main(string[] args) { - var solution = new Option("--solution") { Description = "input solution file" }.AcceptExistingOnly(); - var project = new Option("--project") { Description = "input project file" }.AcceptExistingOnly(); - var compilerInvocation = new Option("--compiler-invocation") { Description = "path to a .json file that contains the information for a csc/vbc invocation" }.AcceptExistingOnly(); - var binLog = new Option("--binlog") { Description = "path to a MSBuild binlog that csc/vbc invocations will be extracted from" }.AcceptExistingOnly(); - var output = new Option("--output") { Description = "file to write the LSIF output to, instead of the console", DefaultValueFactory = _ => null }; + var solution = new CliOption("--solution") { Description = "input solution file" }.AcceptExistingOnly(); + var project = new CliOption("--project") { Description = "input project file" }.AcceptExistingOnly(); + var compilerInvocation = new CliOption("--compiler-invocation") { Description = "path to a .json file that contains the information for a csc/vbc invocation" }.AcceptExistingOnly(); + var binLog = new CliOption("--binlog") { Description = "path to a MSBuild binlog that csc/vbc invocations will be extracted from" }.AcceptExistingOnly(); + var output = new CliOption("--output") { Description = "file to write the LSIF output to, instead of the console", DefaultValueFactory = _ => null }; output.AcceptLegalFilePathsOnly(); - var outputFormat = new Option("--output-format") { Description = "format of LSIF output", DefaultValueFactory = _ => LsifFormat.Line }; - var log = new Option("--log") { Description = "file to write a log to", DefaultValueFactory = _ => null }; + var outputFormat = new CliOption("--output-format") { Description = "format of LSIF output", DefaultValueFactory = _ => LsifFormat.Line }; + var log = new CliOption("--log") { Description = "file to write a log to", DefaultValueFactory = _ => null }; log.AcceptLegalFilePathsOnly(); - var generateCommand = new RootCommand("generates an LSIF file") + var generateCommand = new CliRootCommand("generates an LSIF file") { solution, project, diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Program.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Program.cs index f5602d249fbd2..5c80f55e9dde6 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Program.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Program.cs @@ -17,6 +17,7 @@ using Microsoft.CodeAnalysis.LanguageServer.Logging; using Microsoft.CodeAnalysis.LanguageServer.Services; using Microsoft.CodeAnalysis.LanguageServer.StarredSuggestions; +using Microsoft.CodeAnalysis.Options; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Console; using Roslyn.Utilities; @@ -102,7 +103,7 @@ static async Task RunAsync(ServerConfiguration serverConfiguration, Cancellation // LSP server doesn't have the pieces yet to support 'balanced' mode for source-generators. Hardcode us to // 'automatic' for now. - var globalOptionService = exportProvider.GetExportedValue(); + var globalOptionService = exportProvider.GetExportedValue(); globalOptionService.SetGlobalOption(WorkspaceConfigurationOptionsStorage.SourceGeneratorExecution, SourceGeneratorExecutionPreference.Automatic); // The log file directory passed to us by VSCode might not exist yet, though its parent directory is guaranteed to exist. @@ -180,79 +181,79 @@ static async Task RunAsync(ServerConfiguration serverConfiguration, Cancellation } } -static RootCommand CreateCommandLineParser() +static CliRootCommand CreateCommandLineParser() { - var debugOption = new Option("--debug") + var debugOption = new CliOption("--debug") { Description = "Flag indicating if the debugger should be launched on startup.", Required = false, DefaultValueFactory = _ => false, }; - var brokeredServicePipeNameOption = new Option("--brokeredServicePipeName") + var brokeredServicePipeNameOption = new CliOption("--brokeredServicePipeName") { Description = "The name of the pipe used to connect to a remote process (if one exists).", Required = false, }; - var logLevelOption = new Option("--logLevel") + var logLevelOption = new CliOption("--logLevel") { Description = "The minimum log verbosity.", Required = true, }; - var starredCompletionsPathOption = new Option("--starredCompletionComponentPath") + var starredCompletionsPathOption = new CliOption("--starredCompletionComponentPath") { Description = "The location of the starred completion component (if one exists).", Required = false, }; - var telemetryLevelOption = new Option("--telemetryLevel") + var telemetryLevelOption = new CliOption("--telemetryLevel") { Description = "Telemetry level, Defaults to 'off'. Example values: 'all', 'crash', 'error', or 'off'.", Required = false, }; - var extensionLogDirectoryOption = new Option("--extensionLogDirectory") + var extensionLogDirectoryOption = new CliOption("--extensionLogDirectory") { Description = "The directory where we should write log files to", Required = true, }; - var sessionIdOption = new Option("--sessionId") + var sessionIdOption = new CliOption("--sessionId") { Description = "Session Id to use for telemetry", Required = false }; - var extensionAssemblyPathsOption = new Option("--extension") + var extensionAssemblyPathsOption = new CliOption("--extension") { Description = "Full paths of extension assemblies to load (optional).", Required = false }; - var devKitDependencyPathOption = new Option("--devKitDependencyPath") + var devKitDependencyPathOption = new CliOption("--devKitDependencyPath") { Description = "Full path to the Roslyn dependency used with DevKit (optional).", Required = false }; - var razorSourceGeneratorOption = new Option("--razorSourceGenerator") + var razorSourceGeneratorOption = new CliOption("--razorSourceGenerator") { Description = "Full path to the Razor source generator (optional).", Required = false }; - var razorDesignTimePathOption = new Option("--razorDesignTimePath") + var razorDesignTimePathOption = new CliOption("--razorDesignTimePath") { Description = "Full path to the Razor design time target path (optional).", Required = false }; - var serverPipeNameOption = new Option("--pipe") + var serverPipeNameOption = new CliOption("--pipe") { Description = "The name of the pipe the server will connect to.", Required = false }; - var useStdIoOption = new Option("--stdio") + var useStdIoOption = new CliOption("--stdio") { Description = "Use stdio for communication with the client.", Required = false, @@ -260,7 +261,7 @@ static RootCommand CreateCommandLineParser() }; - var rootCommand = new RootCommand() + var rootCommand = new CliRootCommand() { debugOption, brokeredServicePipeNameOption, diff --git a/src/Tools/BuildValidator/Program.cs b/src/Tools/BuildValidator/Program.cs index 3a3c437a1ae7d..7f19bb818826a 100644 --- a/src/Tools/BuildValidator/Program.cs +++ b/src/Tools/BuildValidator/Program.cs @@ -31,45 +31,45 @@ static int Main(string[] args) { System.Diagnostics.Trace.Listeners.Clear(); - var assembliesPath = new Option("--assembliesPath") + var assembliesPath = new CliOption("--assembliesPath") { Description = BuildValidatorResources.Path_to_assemblies_to_rebuild_can_be_specified_one_or_more_times, Required = true, Arity = ArgumentArity.OneOrMore, }; - var exclude = new Option("--exclude") + var exclude = new CliOption("--exclude") { Description = BuildValidatorResources.Assemblies_to_be_excluded_substring_match, Arity = ArgumentArity.ZeroOrMore, }; - var source = new Option("--sourcePath") + var source = new CliOption("--sourcePath") { Description = BuildValidatorResources.Path_to_sources_to_use_in_rebuild, Required = true, }; - var referencesPath = new Option("--referencesPath") + var referencesPath = new CliOption("--referencesPath") { Description = BuildValidatorResources.Path_to_referenced_assemblies_can_be_specified_zero_or_more_times, Arity = ArgumentArity.ZeroOrMore, }; - var verbose = new Option("--verbose") + var verbose = new CliOption("--verbose") { Description = BuildValidatorResources.Output_verbose_log_information }; - var quiet = new Option("--quiet") + var quiet = new CliOption("--quiet") { Description = BuildValidatorResources.Do_not_output_log_information_to_console }; - var debug = new Option("--debug") + var debug = new CliOption("--debug") { Description = BuildValidatorResources.Output_debug_info_when_rebuild_is_not_equal_to_the_original }; - var debugPath = new Option("--debugPath") + var debugPath = new CliOption("--debugPath") { Description = BuildValidatorResources.Path_to_output_debug_info }; - var rootCommand = new RootCommand + var rootCommand = new CliRootCommand { assembliesPath, exclude, diff --git a/src/Workspaces/MSBuild/BuildHost/Program.cs b/src/Workspaces/MSBuild/BuildHost/Program.cs index b3a286b67419d..48605a392742a 100644 --- a/src/Workspaces/MSBuild/BuildHost/Program.cs +++ b/src/Workspaces/MSBuild/BuildHost/Program.cs @@ -16,11 +16,11 @@ internal static class Program { internal static async Task Main(string[] args) { - var pipeOption = new Option("--pipe") { Required = true }; - var propertyOption = new Option("--property") { Arity = ArgumentArity.ZeroOrMore }; - var binaryLogOption = new Option("--binlog") { Required = false }; - var localeOption = new Option("--locale") { Required = true }; - var command = new RootCommand { pipeOption, binaryLogOption, propertyOption, localeOption }; + var pipeOption = new CliOption("--pipe") { Required = true }; + var propertyOption = new CliOption("--property") { Arity = ArgumentArity.ZeroOrMore }; + var binaryLogOption = new CliOption("--binlog") { Required = false }; + var localeOption = new CliOption("--locale") { Required = true }; + var command = new CliRootCommand { pipeOption, binaryLogOption, propertyOption, localeOption }; var parsedArguments = command.Parse(args); var pipeName = parsedArguments.GetValue(pipeOption)!; var properties = parsedArguments.GetValue(propertyOption)!; From 4403cf4f95d4f5777fdd8c6323477e490f4db0ea Mon Sep 17 00:00:00 2001 From: Michael Render Date: Thu, 6 Feb 2025 14:27:10 -0500 Subject: [PATCH 082/305] Maintain whitespace when converting to switch expression --- ...entToExpressionCodeFixProvider.Rewriter.cs | 2 +- ...ConvertSwitchStatementToExpressionTests.cs | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/Analyzers/CSharp/CodeFixes/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionCodeFixProvider.Rewriter.cs b/src/Analyzers/CSharp/CodeFixes/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionCodeFixProvider.Rewriter.cs index 032c0859edc56..21e667d22aeb9 100644 --- a/src/Analyzers/CSharp/CodeFixes/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionCodeFixProvider.Rewriter.cs +++ b/src/Analyzers/CSharp/CodeFixes/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionCodeFixProvider.Rewriter.cs @@ -246,7 +246,7 @@ private ExpressionSyntax RewriteSwitchStatement( var switchStatement = topLevel ? AddCastIfNecessary(node) : node; return SwitchExpression( - switchStatement.Expression.Parenthesize(), + switchStatement.Expression.WithTrailingTrivia().Parenthesize(), Token(leading: default, SyntaxKind.SwitchKeyword, node.CloseParenToken.TrailingTrivia), Token(leading: default, SyntaxKind.OpenBraceToken, node.OpenBraceToken.TrailingTrivia), SeparatedList( diff --git a/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs b/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs index 753c56b4a8cc8..7bd42eb4c1321 100644 --- a/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs +++ b/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs @@ -2587,4 +2587,44 @@ public static int RefactorReplacementDemoMethod(int argument) LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77081")] + public async Task VerifyTupleSwitch() + { + await VerifyCS.VerifyCodeFixAsync( + """ + class Program + { + bool M(int i, string j) + { + [|switch|] (i, j) + { + case (0, _): + return true; + + case (_, null): + return true; + + default: + return false; + } + } + } + """, + """ + class Program + { + bool M(int i, string j) + { + return (i, j) switch + { + (0, _) => true, + (_, null) => true, + _ => false, + }; + } + } + """); + } + } From 51c453a3c7d2524783cb703b6fcb7db4ace4781e Mon Sep 17 00:00:00 2001 From: Michael Render Date: Thu, 6 Feb 2025 14:36:52 -0500 Subject: [PATCH 083/305] PR feedback --- ...onvertSwitchStatementToExpressionCodeFixProvider.Rewriter.cs | 2 +- .../ConvertSwitchStatementToExpressionTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Analyzers/CSharp/CodeFixes/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionCodeFixProvider.Rewriter.cs b/src/Analyzers/CSharp/CodeFixes/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionCodeFixProvider.Rewriter.cs index 21e667d22aeb9..ffd4639567ebe 100644 --- a/src/Analyzers/CSharp/CodeFixes/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionCodeFixProvider.Rewriter.cs +++ b/src/Analyzers/CSharp/CodeFixes/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionCodeFixProvider.Rewriter.cs @@ -246,7 +246,7 @@ private ExpressionSyntax RewriteSwitchStatement( var switchStatement = topLevel ? AddCastIfNecessary(node) : node; return SwitchExpression( - switchStatement.Expression.WithTrailingTrivia().Parenthesize(), + switchStatement.Expression.WithoutTrailingTrivia().Parenthesize(), Token(leading: default, SyntaxKind.SwitchKeyword, node.CloseParenToken.TrailingTrivia), Token(leading: default, SyntaxKind.OpenBraceToken, node.OpenBraceToken.TrailingTrivia), SeparatedList( diff --git a/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs b/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs index 7bd42eb4c1321..0993b154050bf 100644 --- a/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs +++ b/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs @@ -2589,7 +2589,7 @@ public static int RefactorReplacementDemoMethod(int argument) } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77081")] - public async Task VerifyTupleSwitch() + public async Task TestTupleSwitch() { await VerifyCS.VerifyCodeFixAsync( """ From ec22503c78a3e9e60571d845d8b937a253e2a163 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Feb 2025 11:59:14 -0800 Subject: [PATCH 084/305] Remove unnecessary cancellation token --- ...gnosticIncrementalAnalyzer.ProjectState.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index 267aeea066b2e..fc1d4ec4c30b7 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -82,7 +82,7 @@ public async Task GetAnalysisDataAsync(Project project if (document == null) continue; - if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder, cancellationToken)) + if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder)) { Debug.Assert(lastResult.Version == VersionStamp.Default); @@ -92,7 +92,7 @@ public async Task GetAnalysisDataAsync(Project project } } - if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder, cancellationToken)) + if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder)) { // this can happen if SaveAsync is not yet called but active file merge happened. one of case is if user did build before the very first // analysis happened. @@ -127,7 +127,7 @@ public async Task GetAnalysisDataAsync(TextDocument do var serializerVersion = lastResult.Version; var builder = new Builder(document.Project, lastResult.Version); - if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder, cancellationToken)) + if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder)) { Debug.Assert(lastResult.Version == VersionStamp.Default); @@ -168,7 +168,7 @@ public async Task GetProjectAnalysisDataAsync(Project var serializerVersion = lastResult.Version; var builder = new Builder(project, lastResult.Version); - if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder, cancellationToken)) + if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder)) { // this can happen if SaveAsync is not yet called but active file merge happened. one of case is if user did build before the very first // analysis happened. @@ -237,11 +237,11 @@ private async Task LoadInitialAnalysisDataAsync(Projec { cancellationToken.ThrowIfCancellationRequested(); - if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder, cancellationToken)) + if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder)) continue; } - if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder, cancellationToken)) + if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder)) return DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default); return builder.ToResult(); @@ -256,7 +256,7 @@ private async Task LoadInitialAnalysisDataAsync(TextDo var serializerVersion = version; var builder = new Builder(project, version); - if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder, cancellationToken)) + if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder)) { return DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default); } @@ -271,7 +271,7 @@ private async Task LoadInitialProjectAnalysisDataAsync var serializerVersion = version; var builder = new Builder(project, version); - if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder, cancellationToken)) + if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder)) return DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default); return builder.ToResult(); @@ -283,13 +283,13 @@ private void AddToInMemoryStorage( InMemoryStorage.Cache(_owner.Analyzer, (key, stateKey), new CacheEntry(serializerVersion, diagnostics)); } - private bool TryGetDiagnosticsFromInMemoryStorage(VersionStamp serializerVersion, TextDocument document, Builder builder, CancellationToken cancellationToken) + private bool TryGetDiagnosticsFromInMemoryStorage(VersionStamp serializerVersion, TextDocument document, Builder builder) { var success = true; var project = document.Project; var documentId = document.Id; - var diagnostics = GetDiagnosticsFromInMemoryStorage(serializerVersion, new(documentId), SyntaxStateName, cancellationToken); + var diagnostics = GetDiagnosticsFromInMemoryStorage(serializerVersion, new(documentId), SyntaxStateName); if (!diagnostics.IsDefault) { builder.AddSyntaxLocals(documentId, diagnostics); @@ -299,7 +299,7 @@ private bool TryGetDiagnosticsFromInMemoryStorage(VersionStamp serializerVersion success = false; } - diagnostics = GetDiagnosticsFromInMemoryStorage(serializerVersion, new(documentId), SemanticStateName, cancellationToken); + diagnostics = GetDiagnosticsFromInMemoryStorage(serializerVersion, new(documentId), SemanticStateName); if (!diagnostics.IsDefault) { builder.AddSemanticLocals(documentId, diagnostics); @@ -309,7 +309,7 @@ private bool TryGetDiagnosticsFromInMemoryStorage(VersionStamp serializerVersion success = false; } - diagnostics = GetDiagnosticsFromInMemoryStorage(serializerVersion, new(documentId), NonLocalStateName, cancellationToken); + diagnostics = GetDiagnosticsFromInMemoryStorage(serializerVersion, new(documentId), NonLocalStateName); if (!diagnostics.IsDefault) { builder.AddNonLocals(documentId, diagnostics); @@ -322,9 +322,9 @@ private bool TryGetDiagnosticsFromInMemoryStorage(VersionStamp serializerVersion return success; } - private bool TryGetProjectDiagnosticsFromInMemoryStorage(VersionStamp serializerVersion, Project project, Builder builder, CancellationToken cancellationToken) + private bool TryGetProjectDiagnosticsFromInMemoryStorage(VersionStamp serializerVersion, Project project, Builder builder) { - var diagnostics = GetDiagnosticsFromInMemoryStorage(serializerVersion, new(project.Id), NonLocalStateName, cancellationToken); + var diagnostics = GetDiagnosticsFromInMemoryStorage(serializerVersion, new(project.Id), NonLocalStateName); if (!diagnostics.IsDefault) { builder.AddOthers(diagnostics); @@ -335,7 +335,7 @@ private bool TryGetProjectDiagnosticsFromInMemoryStorage(VersionStamp serializer } private ImmutableArray GetDiagnosticsFromInMemoryStorage( - VersionStamp serializerVersion, ProjectOrDocumentId key, string stateKey, CancellationToken _) + VersionStamp serializerVersion, ProjectOrDocumentId key, string stateKey) { return InMemoryStorage.TryGetValue(_owner.Analyzer, (key, stateKey), out var entry) && serializerVersion == entry.Version ? entry.Diagnostics From 66148a73331790968a197c815cead5aad9c00c11 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Thu, 6 Feb 2025 15:03:22 -0500 Subject: [PATCH 085/305] Update whitespace --- .../ConvertSwitchStatementToExpressionTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs b/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs index 0993b154050bf..92f63f7957d56 100644 --- a/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs +++ b/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs @@ -2626,5 +2626,4 @@ bool M(int i, string j) } """); } - } From 84443f21bf60465892164d7cc74759bc171000dc Mon Sep 17 00:00:00 2001 From: Michael Render Date: Thu, 6 Feb 2025 15:03:51 -0500 Subject: [PATCH 086/305] Update src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs --- .../ConvertSwitchStatementToExpressionTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs b/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs index 92f63f7957d56..c9fe657b46bbb 100644 --- a/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs +++ b/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs @@ -2626,4 +2626,3 @@ bool M(int i, string j) } """); } -} From 59080e381cd07a7a8bf73f775c56e5f10ddae1f0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Feb 2025 12:06:50 -0800 Subject: [PATCH 087/305] Make method synchronous --- .../DiagnosticIncrementalAnalyzer.ProjectState.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index fc1d4ec4c30b7..4e09bafd0c6e5 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -49,16 +49,14 @@ public async Task GetAnalysisDataAsync(Project project var lastResult = _lastResult; Contract.ThrowIfFalse(lastResult.ProjectId == project.Id); + var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); if (lastResult.IsDefault) - { - return await LoadInitialAnalysisDataAsync(project, cancellationToken).ConfigureAwait(false); - } + return LoadInitialAnalysisData(project, version, cancellationToken); RoslynDebug.Assert(lastResult.DocumentIds != null); // PERF: avoid loading data if version is not right one. // avoid loading data flag is there as a strictly perf optimization. - var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); if (lastResult.Version != version) { return lastResult; @@ -226,22 +224,21 @@ public async ValueTask SaveToInMemoryStorageAsync(Project project, DiagnosticAna AddToInMemoryStorage(serializerVersion, new(result.ProjectId), NonLocalStateName, result.GetOtherDiagnostics()); } - private async Task LoadInitialAnalysisDataAsync(Project project, CancellationToken cancellationToken) + private DiagnosticAnalysisResult LoadInitialAnalysisData( + Project project, VersionStamp version, CancellationToken cancellationToken) { // loading data can be canceled any time. - var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); - var serializerVersion = version; var builder = new Builder(project, version); foreach (var document in project.Documents) { cancellationToken.ThrowIfCancellationRequested(); - if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder)) + if (!TryGetDiagnosticsFromInMemoryStorage(version, document, builder)) continue; } - if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder)) + if (!TryGetProjectDiagnosticsFromInMemoryStorage(version, project, builder)) return DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default); return builder.ToResult(); From a005560b696e176a83bd76e7a13e419e409177c9 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Thu, 6 Feb 2025 15:07:14 -0500 Subject: [PATCH 088/305] Fix whitespace --- .../ConvertSwitchStatementToExpressionTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs b/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs index c9fe657b46bbb..92f63f7957d56 100644 --- a/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs +++ b/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs @@ -2626,3 +2626,4 @@ bool M(int i, string j) } """); } +} From 96d8242067f6345a9e7146a2e1982640bc9879b0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Feb 2025 12:09:13 -0800 Subject: [PATCH 089/305] Make method synchronous --- ...gnosticIncrementalAnalyzer.ProjectState.cs | 29 +++++++------------ 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index 4e09bafd0c6e5..c189ce0d70e89 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -108,12 +108,9 @@ public async Task GetAnalysisDataAsync(TextDocument do var lastResult = _lastResult; Contract.ThrowIfFalse(lastResult.ProjectId == document.Project.Id); - if (lastResult.IsDefault) - { - return await LoadInitialAnalysisDataAsync(document, cancellationToken).ConfigureAwait(false); - } - var version = await GetDiagnosticVersionAsync(document.Project, cancellationToken).ConfigureAwait(false); + if (lastResult.IsDefault) + return LoadInitialAnalysisData(document, version); // if given document doesnt have any diagnostics, return empty. if (IsEmpty(lastResult, document.Id)) @@ -145,12 +142,10 @@ public async Task GetProjectAnalysisDataAsync(Project var lastResult = _lastResult; Contract.ThrowIfFalse(lastResult.ProjectId == project.Id); + var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); if (lastResult.IsDefault) - { - return await LoadInitialProjectAnalysisDataAsync(project, cancellationToken).ConfigureAwait(false); - } + return LoadInitialProjectAnalysisData(project, version); - var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); if (avoidLoadingData && lastResult.Version != version) { return lastResult; @@ -244,31 +239,27 @@ private DiagnosticAnalysisResult LoadInitialAnalysisData( return builder.ToResult(); } - private async Task LoadInitialAnalysisDataAsync(TextDocument document, CancellationToken cancellationToken) + private DiagnosticAnalysisResult LoadInitialAnalysisData( + TextDocument document, VersionStamp version) { // loading data can be canceled any time. var project = document.Project; - var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); - var serializerVersion = version; var builder = new Builder(project, version); - if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder)) - { + if (!TryGetDiagnosticsFromInMemoryStorage(version, document, builder)) return DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default); - } return builder.ToResult(); } - private async Task LoadInitialProjectAnalysisDataAsync(Project project, CancellationToken cancellationToken) + private DiagnosticAnalysisResult LoadInitialProjectAnalysisData( + Project project, VersionStamp version) { // loading data can be canceled any time. - var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); - var serializerVersion = version; var builder = new Builder(project, version); - if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder)) + if (!TryGetProjectDiagnosticsFromInMemoryStorage(version, project, builder)) return DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default); return builder.ToResult(); From c0b08cfeed888ee859cb0d5bdabef1a9c3efde92 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Feb 2025 12:12:00 -0800 Subject: [PATCH 090/305] Remove unnecessary parameter in diagnostics --- .../EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs | 6 +----- .../DiagnosticIncrementalAnalyzer_GetDiagnostics.cs | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index 267aeea066b2e..e9894647aea65 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -141,7 +141,7 @@ public async Task GetAnalysisDataAsync(TextDocument do /// /// Return all no location diagnostics for the given project stored in this state /// - public async Task GetProjectAnalysisDataAsync(Project project, bool avoidLoadingData, CancellationToken cancellationToken) + public async Task GetProjectAnalysisDataAsync(Project project, CancellationToken cancellationToken) { // make a copy of last result. var lastResult = _lastResult; @@ -153,10 +153,6 @@ public async Task GetProjectAnalysisDataAsync(Project } var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); - if (avoidLoadingData && lastResult.Version != version) - { - return lastResult; - } // if given document doesn't have any diagnostics, return empty. if (lastResult.IsEmpty) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index c1dae10697d64..0eb0d66a30c11 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -175,7 +175,7 @@ private static async Task> GetProjectStateDiagnos } Contract.ThrowIfFalse(kind == AnalysisKind.NonLocal); - var nonLocalResult = await state.GetProjectAnalysisDataAsync(project, avoidLoadingData: false, cancellationToken: cancellationToken).ConfigureAwait(false); + var nonLocalResult = await state.GetProjectAnalysisDataAsync(project, cancellationToken: cancellationToken).ConfigureAwait(false); return nonLocalResult.GetOtherDiagnostics(); } } From 0a67fab2a4ef02c08ac85cd50b95ceda1b368845 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Feb 2025 12:58:47 -0800 Subject: [PATCH 091/305] Remove unnecessary lambda in diag service --- .../MockDiagnosticAnalyzerService.cs | 8 +++----- .../Diagnostics/IDiagnosticAnalyzerService.cs | 10 +--------- .../Diagnostics/DiagnosticAnalyzerService.cs | 4 ++-- ...osticIncrementalAnalyzer_GetDiagnostics.cs | 20 +++++++++---------- ...stractWorkspaceDocumentDiagnosticSource.cs | 2 -- 5 files changed, 16 insertions(+), 28 deletions(-) diff --git a/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs b/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs index 77b15aa2bfaf7..1a56c8b3f86cb 100644 --- a/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs +++ b/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; using System.Linq; @@ -12,7 +11,6 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; @@ -21,12 +19,12 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics; [Export(typeof(IDiagnosticAnalyzerService)), Shared, PartNotDiscoverable] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal class MockDiagnosticAnalyzerService() : IDiagnosticAnalyzerService +internal sealed class MockDiagnosticAnalyzerService() : IDiagnosticAnalyzerService { private readonly ArrayBuilder<(DiagnosticData Diagnostic, DiagnosticKind KindFilter)> _diagnosticsWithKindFilter = ArrayBuilder<(DiagnosticData Diagnostic, DiagnosticKind KindFilter)>.GetInstance(); public bool RequestedRefresh; - public void AddDiagnostic(DiagnosticData diagnostic, DiagnosticKind diagnosticKind) + private void AddDiagnostic(DiagnosticData diagnostic, DiagnosticKind diagnosticKind) => _diagnosticsWithKindFilter.Add((diagnostic, diagnosticKind)); public void AddDiagnostics(ImmutableArray diagnostics, DiagnosticKind diagnosticKind) @@ -53,7 +51,7 @@ public Task> GetCachedDiagnosticsAsync(Workspace public Task> GetDiagnosticsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) => throw new NotImplementedException(); - public Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, Func>? getDocuments, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) + public Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) => throw new NotImplementedException(); public Task> GetDiagnosticsForSpanAsync(TextDocument document, TextSpan? range, Func? shouldIncludeDiagnostic, bool includeCompilerDiagnostics, ICodeActionRequestPriorityProvider priorityProvider, DiagnosticKind diagnosticKind, bool isExplicit, CancellationToken cancellationToken) diff --git a/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs index 3870348bec005..363f0f7dba373 100644 --- a/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs @@ -73,7 +73,7 @@ Task> GetCachedDiagnosticsAsync( /// project must be analyzed to get the complete set of non-local document diagnostics. /// /// Cancellation token. - Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, Func>? getDocumentIds, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken); + Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken); /// /// Get project diagnostics (diagnostics with no source location) of the given diagnostic ids and/or analyzers from @@ -147,12 +147,4 @@ public static Task> GetDiagnosticsForSpanAsync(th includeCompilerDiagnostics: true, priorityProvider, diagnosticKind, isExplicit, cancellationToken); } - - public static Task> GetDiagnosticsForIdsAsync( - this IDiagnosticAnalyzerService service, Solution solution, ProjectId projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) - { - return service.GetDiagnosticsForIdsAsync( - solution, projectId, documentId, diagnosticIds, shouldIncludeAnalyzer, getDocumentIds: null, - includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics, cancellationToken); - } } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs index b274ffc237985..fdcac04551541 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs @@ -107,10 +107,10 @@ public async Task ForceAnalyzeProjectAsync(Project project, CancellationToken ca } public Task> GetDiagnosticsForIdsAsync( - Solution solution, ProjectId projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, Func>? getDocuments, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) + Solution solution, ProjectId projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) { var analyzer = CreateIncrementalAnalyzer(solution.Workspace); - return analyzer.GetDiagnosticsForIdsAsync(solution, projectId, documentId, diagnosticIds, shouldIncludeAnalyzer, getDocuments, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics, cancellationToken); + return analyzer.GetDiagnosticsForIdsAsync(solution, projectId, documentId, diagnosticIds, shouldIncludeAnalyzer, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics, cancellationToken); } public Task> GetProjectDiagnosticsForIdsAsync( diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index c1dae10697d64..21be9cc506e0c 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -18,18 +18,17 @@ internal partial class DiagnosticIncrementalAnalyzer public Task> GetCachedDiagnosticsAsync(Solution solution, ProjectId projectId, DocumentId? documentId, CancellationToken cancellationToken) => new IdeCachedDiagnosticGetter(this, solution, projectId, documentId).GetDiagnosticsAsync(cancellationToken); - public Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, Func>? getDocuments, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) - => new IdeLatestDiagnosticGetter(this, solution, projectId, documentId, diagnosticIds, shouldIncludeAnalyzer, getDocuments, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics).GetDiagnosticsAsync(cancellationToken); + public Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) + => new IdeLatestDiagnosticGetter(this, solution, projectId, documentId, diagnosticIds, shouldIncludeAnalyzer, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics).GetDiagnosticsAsync(cancellationToken); public Task> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId projectId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) - => new IdeLatestDiagnosticGetter(this, solution, projectId, documentId: null, diagnosticIds, shouldIncludeAnalyzer, getDocuments: null, includeLocalDocumentDiagnostics: false, includeNonLocalDocumentDiagnostics).GetProjectDiagnosticsAsync(cancellationToken); + => new IdeLatestDiagnosticGetter(this, solution, projectId, documentId: null, diagnosticIds, shouldIncludeAnalyzer, includeLocalDocumentDiagnostics: false, includeNonLocalDocumentDiagnostics).GetProjectDiagnosticsAsync(cancellationToken); private abstract class DiagnosticGetter( DiagnosticIncrementalAnalyzer owner, Solution solution, ProjectId projectId, DocumentId? documentId, - Func>? getDocuments, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics) { @@ -41,8 +40,6 @@ private abstract class DiagnosticGetter( protected readonly bool IncludeLocalDocumentDiagnostics = includeLocalDocumentDiagnostics; protected readonly bool IncludeNonLocalDocumentDiagnostics = includeNonLocalDocumentDiagnostics; - private readonly Func> _getDocuments = getDocuments ?? (static (project, documentId) => documentId != null ? [documentId] : project.DocumentIds); - protected StateManager StateManager => Owner._stateManager; protected virtual bool ShouldIncludeDiagnostic(DiagnosticData diagnostic) => true; @@ -59,7 +56,11 @@ public async Task> GetDiagnosticsAsync(Cancellati // return diagnostics specific to one project or document var includeProjectNonLocalResult = DocumentId == null; return await ProduceProjectDiagnosticsAsync( - project, _getDocuments(project, DocumentId), includeProjectNonLocalResult, cancellationToken).ConfigureAwait(false); + project, + // Ensure we compute and return diagnostics for both the normal docs and the additional docs in this + // project if no specific document id was requested. + this.DocumentId != null ? [this.DocumentId] : [.. project.DocumentIds, .. project.AdditionalDocumentIds], + includeProjectNonLocalResult, cancellationToken).ConfigureAwait(false); } protected async Task> ProduceProjectDiagnosticsAsync( @@ -88,7 +89,7 @@ private sealed class IdeCachedDiagnosticGetter( ProjectId projectId, DocumentId? documentId) : DiagnosticGetter( - owner, solution, projectId, documentId, getDocuments: null, + owner, solution, projectId, documentId, includeLocalDocumentDiagnostics: documentId != null, includeNonLocalDocumentDiagnostics: documentId != null) { @@ -187,11 +188,10 @@ private sealed class IdeLatestDiagnosticGetter( DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, - Func>? getDocuments, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics) : DiagnosticGetter( - owner, solution, projectId, documentId, getDocuments, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics) + owner, solution, projectId, documentId, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics) { private readonly ImmutableHashSet? _diagnosticIds = diagnosticIds; private readonly Func? _shouldIncludeAnalyzer = shouldIncludeAnalyzer; diff --git a/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractWorkspaceDocumentDiagnosticSource.cs b/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractWorkspaceDocumentDiagnosticSource.cs index 9becd49893ea4..a383a120dda86 100644 --- a/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractWorkspaceDocumentDiagnosticSource.cs +++ b/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractWorkspaceDocumentDiagnosticSource.cs @@ -78,8 +78,6 @@ AsyncLazy> GetLazyDiagnostics() var allDiagnostics = await diagnosticAnalyzerService.GetDiagnosticsForIdsAsync( Document.Project.Solution, Document.Project.Id, documentId: null, diagnosticIds: null, shouldIncludeAnalyzer, - // Ensure we compute and return diagnostics for both the normal docs and the additional docs in this project. - static (project, _) => [.. project.DocumentIds.Concat(project.AdditionalDocumentIds)], includeLocalDocumentDiagnostics: true, includeNonLocalDocumentDiagnostics: true, cancellationToken).ConfigureAwait(false); // TODO(cyrusn): Should we be filtering out suppressed diagnostics here? This is how the From f2ddf6e2a2ea6b2d8eedb7194ec75620bf076e89 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Feb 2025 13:00:21 -0800 Subject: [PATCH 092/305] Remove unused test type --- .../Test/CodeFixes/CodeFixServiceTests.cs | 2 +- .../DiagnosticAnalyzerServiceTests.cs | 2 +- .../MockDiagnosticAnalyzerService.cs | 62 ------------------- 3 files changed, 2 insertions(+), 64 deletions(-) delete mode 100644 src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs diff --git a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs index c9893a6a27f67..c02fd5d305722 100644 --- a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs +++ b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs @@ -1086,7 +1086,7 @@ void M() : root.DescendantNodes().OfType().First().Span; await diagnosticIncrementalAnalyzer.GetDiagnosticsForIdsAsync( - sourceDocument.Project.Solution, sourceDocument.Project.Id, sourceDocument.Id, diagnosticIds: null, shouldIncludeAnalyzer: null, getDocuments: null, + sourceDocument.Project.Solution, sourceDocument.Project.Id, sourceDocument.Id, diagnosticIds: null, shouldIncludeAnalyzer: null, includeLocalDocumentDiagnostics: true, includeNonLocalDocumentDiagnostics: true, CancellationToken.None); // await diagnosticIncrementalAnalyzer.GetTestAccessor().TextDocumentOpenAsync(sourceDocument); diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index c45cdd786d7b2..81a60ed9337ef 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -68,7 +68,7 @@ public async Task TestHasSuccessfullyLoadedBeingFalse() var globalOptions = exportProvider.GetExportedValue(); var diagnostics = await analyzer.GetDiagnosticsForIdsAsync( - workspace.CurrentSolution, projectId: workspace.CurrentSolution.ProjectIds.Single(), documentId: null, diagnosticIds: null, shouldIncludeAnalyzer: null, getDocuments: null, + workspace.CurrentSolution, projectId: workspace.CurrentSolution.ProjectIds.Single(), documentId: null, diagnosticIds: null, shouldIncludeAnalyzer: null, includeLocalDocumentDiagnostics: true, includeNonLocalDocumentDiagnostics: false, CancellationToken.None); Assert.NotEmpty(diagnostics); } diff --git a/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs b/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs deleted file mode 100644 index 1a56c8b3f86cb..0000000000000 --- a/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Immutable; -using System.Composition; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Text; - -namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics; - -[Export(typeof(IDiagnosticAnalyzerService)), Shared, PartNotDiscoverable] -[method: ImportingConstructor] -[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal sealed class MockDiagnosticAnalyzerService() : IDiagnosticAnalyzerService -{ - private readonly ArrayBuilder<(DiagnosticData Diagnostic, DiagnosticKind KindFilter)> _diagnosticsWithKindFilter = ArrayBuilder<(DiagnosticData Diagnostic, DiagnosticKind KindFilter)>.GetInstance(); - public bool RequestedRefresh; - - private void AddDiagnostic(DiagnosticData diagnostic, DiagnosticKind diagnosticKind) - => _diagnosticsWithKindFilter.Add((diagnostic, diagnosticKind)); - - public void AddDiagnostics(ImmutableArray diagnostics, DiagnosticKind diagnosticKind) - { - foreach (var diagnostic in diagnostics) - AddDiagnostic(diagnostic, diagnosticKind); - } - - public void RequestDiagnosticRefresh() - => RequestedRefresh = true; - - public DiagnosticAnalyzerInfoCache AnalyzerInfoCache - => throw new NotImplementedException(); - - public bool ContainsDiagnostics(Workspace workspace, ProjectId projectId) - => throw new NotImplementedException(); - - public Task ForceAnalyzeProjectAsync(Project project, CancellationToken cancellationToken) - => throw new NotImplementedException(); - - public Task> GetCachedDiagnosticsAsync(Workspace workspace, ProjectId projectId, DocumentId? documentId, CancellationToken cancellationToken) - => throw new NotImplementedException(); - - public Task> GetDiagnosticsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) - => throw new NotImplementedException(); - - public Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) - => throw new NotImplementedException(); - - public Task> GetDiagnosticsForSpanAsync(TextDocument document, TextSpan? range, Func? shouldIncludeDiagnostic, bool includeCompilerDiagnostics, ICodeActionRequestPriorityProvider priorityProvider, DiagnosticKind diagnosticKind, bool isExplicit, CancellationToken cancellationToken) - => Task.FromResult(_diagnosticsWithKindFilter.Where(d => diagnosticKind == d.KindFilter).Select(d => d.Diagnostic).ToImmutableArray()); - - public Task> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) - => throw new NotImplementedException(); -} From 9e100f0742cd54d6f5b05a8d91c0bb4a5899ded1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Feb 2025 13:01:08 -0800 Subject: [PATCH 093/305] Cleanup tests --- .../Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb index 0e28b473307df..f5af5f3e7c07e 100644 --- a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb +++ b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb @@ -300,7 +300,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Private ReadOnly _analyzerInfoCache As DiagnosticAnalyzerInfoCache - Public Sub New(Optional data As ImmutableArray(Of DiagnosticData) = Nothing) + Public Sub New() _analyzerInfoCache = New DiagnosticAnalyzerInfoCache() End Sub @@ -321,7 +321,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Return SpecializedTasks.EmptyImmutableArray(Of DiagnosticData)() End Function - Public Function GetDiagnosticsForIdsAsync(solution As Solution, projectId As ProjectId, documentId As DocumentId, diagnosticIds As ImmutableHashSet(Of String), shouldIncludeAnalyzer As Func(Of DiagnosticAnalyzer, Boolean), getDocuments As Func(Of Project, DocumentId, IReadOnlyList(Of DocumentId)), includeLocalDocumentDiagnostics As Boolean, includeNonLocalDocumentDiagnostics As Boolean, cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of DiagnosticData)) Implements IDiagnosticAnalyzerService.GetDiagnosticsForIdsAsync + Public Function GetDiagnosticsForIdsAsync(solution As Solution, projectId As ProjectId, documentId As DocumentId, diagnosticIds As ImmutableHashSet(Of String), shouldIncludeAnalyzer As Func(Of DiagnosticAnalyzer, Boolean), includeLocalDocumentDiagnostics As Boolean, includeNonLocalDocumentDiagnostics As Boolean, cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of DiagnosticData)) Implements IDiagnosticAnalyzerService.GetDiagnosticsForIdsAsync Return SpecializedTasks.EmptyImmutableArray(Of DiagnosticData)() End Function From 9e4d7154040e35b9c0b7997895c2457446ed2ea9 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Feb 2025 13:31:11 -0800 Subject: [PATCH 094/305] Remove 'include compiler diagnostics' flag from diagnostics service --- .../Diagnostics/IDiagnosticAnalyzerService.cs | 6 +----- .../CodeCleanup/AbstractCodeCleanupService.cs | 1 - .../CodeFixService.FixAllDiagnosticProvider.cs | 2 +- .../Protocol/Features/CodeFixes/CodeFixService.cs | 4 ++-- .../Features/Diagnostics/DiagnosticAnalyzerService.cs | 4 +--- ...nosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs | 11 ++--------- .../ExternalDiagnosticUpdateSourceTests.vb | 2 +- 7 files changed, 8 insertions(+), 22 deletions(-) diff --git a/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs index 363f0f7dba373..b84ad1b4839bd 100644 --- a/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs @@ -3,12 +3,10 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Diagnostics; @@ -103,7 +101,6 @@ Task> GetCachedDiagnosticsAsync( /// Task> GetDiagnosticsForSpanAsync( TextDocument document, TextSpan? range, Func? shouldIncludeDiagnostic, - bool includeCompilerDiagnostics, ICodeActionRequestPriorityProvider priorityProvider, DiagnosticKind diagnosticKind, bool isExplicit, @@ -144,7 +141,6 @@ public static Task> GetDiagnosticsForSpanAsync(th { Func? shouldIncludeDiagnostic = diagnosticId != null ? id => id == diagnosticId : null; return service.GetDiagnosticsForSpanAsync(document, range, shouldIncludeDiagnostic, - includeCompilerDiagnostics: true, priorityProvider, - diagnosticKind, isExplicit, cancellationToken); + priorityProvider, diagnosticKind, isExplicit, cancellationToken); } } diff --git a/src/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs b/src/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs index 6f546fa963e47..16ee27f71f880 100644 --- a/src/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs +++ b/src/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs @@ -202,7 +202,6 @@ private async Task ApplyCodeFixesForSpecificDiagnosticIdAsync( // Compute diagnostics for everything that is not an IDE analyzer var diagnostics = await _diagnosticService.GetDiagnosticsForSpanAsync(document, range, shouldIncludeDiagnostic: static diagnosticId => !(IDEDiagnosticIdToOptionMappingHelper.IsKnownIDEDiagnosticId(diagnosticId)), - includeCompilerDiagnostics: true, priorityProvider: new DefaultCodeActionRequestPriorityProvider(), DiagnosticKind.All, isExplicit: false, cancellationToken).ConfigureAwait(false); diff --git a/src/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs b/src/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs index 16b67fd2bdb45..628855831a7cd 100644 --- a/src/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs +++ b/src/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs @@ -57,7 +57,7 @@ public override async Task> GetDocumentSpanDiagnosticsAs { bool shouldIncludeDiagnostic(string id) => _diagnosticIds == null || _diagnosticIds.Contains(id); var diagnostics = Filter(await _diagnosticService.GetDiagnosticsForSpanAsync( - document, fixAllSpan, shouldIncludeDiagnostic, includeCompilerDiagnostics: true, + document, fixAllSpan, shouldIncludeDiagnostic, priorityProvider: new DefaultCodeActionRequestPriorityProvider(), DiagnosticKind.All, isExplicit: false, cancellationToken).ConfigureAwait(false)); Contract.ThrowIfFalse(diagnostics.All(d => d.DocumentId != null)); diff --git a/src/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs b/src/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs index c78d9bd421e96..d4e282074b310 100644 --- a/src/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs +++ b/src/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs @@ -109,7 +109,7 @@ public CodeFixService( { allDiagnostics = await _diagnosticService.GetDiagnosticsForSpanAsync( document, range, GetShouldIncludeDiagnosticPredicate(document, priorityProvider), - includeCompilerDiagnostics: true, priorityProvider, DiagnosticKind.All, isExplicit: false, cancellationToken).ConfigureAwait(false); + priorityProvider, DiagnosticKind.All, isExplicit: false, cancellationToken).ConfigureAwait(false); // NOTE(cyrusn): We do not include suppressed diagnostics here as they are effectively hidden from the // user in the editor. As far as the user is concerned, there is no squiggle for it and no lightbulb @@ -199,7 +199,7 @@ public async IAsyncEnumerable StreamFixesAsync( { diagnostics = await _diagnosticService.GetDiagnosticsForSpanAsync( document, range, GetShouldIncludeDiagnosticPredicate(document, priorityProvider), - includeCompilerDiagnostics: true, priorityProvider, DiagnosticKind.All, isExplicit: true, cancellationToken).ConfigureAwait(false); + priorityProvider, DiagnosticKind.All, isExplicit: true, cancellationToken).ConfigureAwait(false); if (!includeSuppressionFixes) diagnostics = diagnostics.WhereAsArray(d => !d.IsSuppressed); } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs index fdcac04551541..53f3f5b775dde 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs @@ -76,7 +76,6 @@ public async Task> GetDiagnosticsForSpanAsync( TextDocument document, TextSpan? range, Func? shouldIncludeDiagnostic, - bool includeCompilerDiagnostics, ICodeActionRequestPriorityProvider priorityProvider, DiagnosticKind diagnosticKinds, bool isExplicit, @@ -89,8 +88,7 @@ public async Task> GetDiagnosticsForSpanAsync( priorityProvider ??= new DefaultCodeActionRequestPriorityProvider(); return await analyzer.GetDiagnosticsForSpanAsync( - document, range, shouldIncludeDiagnostic, includeCompilerDiagnostics, - priorityProvider, diagnosticKinds, isExplicit, cancellationToken).ConfigureAwait(false); + document, range, shouldIncludeDiagnostic, priorityProvider, diagnosticKinds, isExplicit, cancellationToken).ConfigureAwait(false); } public Task> GetCachedDiagnosticsAsync( diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs index 40fd2eafa68d8..5e2c2f96dbba8 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs @@ -26,7 +26,6 @@ public async Task> GetDiagnosticsForSpanAsync( TextDocument document, TextSpan? range, Func? shouldIncludeDiagnostic, - bool includeCompilerDiagnostics, ICodeActionRequestPriorityProvider priorityProvider, DiagnosticKind diagnosticKinds, bool isExplicit, @@ -35,8 +34,7 @@ public async Task> GetDiagnosticsForSpanAsync( using var _ = ArrayBuilder.GetInstance(out var list); var getter = await LatestDiagnosticsForSpanGetter.CreateAsync( - this, document, range, includeCompilerDiagnostics, - priorityProvider, shouldIncludeDiagnostic, diagnosticKinds, isExplicit, cancellationToken).ConfigureAwait(false); + this, document, range, priorityProvider, shouldIncludeDiagnostic, diagnosticKinds, isExplicit, cancellationToken).ConfigureAwait(false); await getter.GetAsync(list, cancellationToken).ConfigureAwait(false); return list.ToImmutableAndClear(); @@ -62,7 +60,6 @@ private sealed class LatestDiagnosticsForSpanGetter private readonly TextSpan? _range; private readonly ICodeActionRequestPriorityProvider _priorityProvider; private readonly Func? _shouldIncludeDiagnostic; - private readonly bool _includeCompilerDiagnostics; private readonly bool _isExplicit; private readonly bool _logPerformanceInfo; private readonly bool _incrementalAnalysis; @@ -72,7 +69,6 @@ public static async Task CreateAsync( DiagnosticIncrementalAnalyzer owner, TextDocument document, TextSpan? range, - bool includeCompilerDiagnostics, ICodeActionRequestPriorityProvider priorityProvider, Func? shouldIncludeDiagnostic, DiagnosticKind diagnosticKinds, @@ -102,7 +98,7 @@ public static async Task CreateAsync( && document is Document { SupportsSyntaxTree: true }; return new LatestDiagnosticsForSpanGetter( - owner, compilationWithAnalyzers, document, text, stateSets, shouldIncludeDiagnostic, includeCompilerDiagnostics, + owner, compilationWithAnalyzers, document, text, stateSets, shouldIncludeDiagnostic, range, priorityProvider, isExplicit, logPerformanceInfo, incrementalAnalysis, diagnosticKinds); } @@ -151,7 +147,6 @@ private LatestDiagnosticsForSpanGetter( SourceText text, ImmutableArray stateSets, Func? shouldIncludeDiagnostic, - bool includeCompilerDiagnostics, TextSpan? range, ICodeActionRequestPriorityProvider priorityProvider, bool isExplicit, @@ -165,7 +160,6 @@ private LatestDiagnosticsForSpanGetter( _text = text; _stateSets = stateSets; _shouldIncludeDiagnostic = shouldIncludeDiagnostic; - _includeCompilerDiagnostics = includeCompilerDiagnostics; _range = range; _priorityProvider = priorityProvider; _isExplicit = isExplicit; @@ -462,7 +456,6 @@ private bool ShouldInclude(DiagnosticData diagnostic) { return diagnostic.DocumentId == _document.Id && (_range == null || _range.Value.IntersectsWith(diagnostic.DataLocation.UnmappedFileSpan.GetClampedTextSpan(_text))) - && (_includeCompilerDiagnostics || !diagnostic.CustomTags.Any(static t => t is WellKnownDiagnosticTags.Compiler)) && (_shouldIncludeDiagnostic == null || _shouldIncludeDiagnostic(diagnostic.Id)); } } diff --git a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb index f5af5f3e7c07e..5de26bc25e2c9 100644 --- a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb +++ b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb @@ -313,7 +313,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub RequestDiagnosticRefresh() Implements IDiagnosticAnalyzerService.RequestDiagnosticRefresh End Sub - Public Function GetDiagnosticsForSpanAsync(document As TextDocument, range As TextSpan?, shouldIncludeDiagnostic As Func(Of String, Boolean), includeCompilerDiagnostics As Boolean, priority As ICodeActionRequestPriorityProvider, diagnosticKinds As DiagnosticKind, isExplicit As Boolean, cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of DiagnosticData)) Implements IDiagnosticAnalyzerService.GetDiagnosticsForSpanAsync + Public Function GetDiagnosticsForSpanAsync(document As TextDocument, range As TextSpan?, shouldIncludeDiagnostic As Func(Of String, Boolean), priority As ICodeActionRequestPriorityProvider, diagnosticKinds As DiagnosticKind, isExplicit As Boolean, cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of DiagnosticData)) Implements IDiagnosticAnalyzerService.GetDiagnosticsForSpanAsync Return SpecializedTasks.EmptyImmutableArray(Of DiagnosticData) End Function From df985782bba811df4238d5b6e874785da2fdf4ef Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Feb 2025 14:11:40 -0800 Subject: [PATCH 095/305] Remove unnecessary field from CodeAnalysisDiagnosticAnalyzerService --- .../CodeAnalysisDiagnosticAnalyzerService.cs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/Features/Core/Portable/Diagnostics/CodeAnalysisDiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/CodeAnalysisDiagnosticAnalyzerService.cs index 543242d1f71ed..163b4c17b526c 100644 --- a/src/Features/Core/Portable/Diagnostics/CodeAnalysisDiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/Diagnostics/CodeAnalysisDiagnosticAnalyzerService.cs @@ -23,14 +23,12 @@ internal sealed class CodeAnalysisDiagnosticAnalyzerServiceFactory() : IWorkspac public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) { var diagnosticAnalyzerService = workspaceServices.SolutionServices.ExportProvider.GetExports().Single().Value; - var diagnosticsRefresher = workspaceServices.SolutionServices.ExportProvider.GetExports().Single().Value; - return new CodeAnalysisDiagnosticAnalyzerService(diagnosticAnalyzerService, diagnosticsRefresher, workspaceServices.Workspace); + return new CodeAnalysisDiagnosticAnalyzerService(diagnosticAnalyzerService, workspaceServices.Workspace); } private sealed class CodeAnalysisDiagnosticAnalyzerService : ICodeAnalysisDiagnosticAnalyzerService { private readonly IDiagnosticAnalyzerService _diagnosticAnalyzerService; - private readonly IDiagnosticsRefresher _diagnosticsRefresher; private readonly Workspace _workspace; /// @@ -51,11 +49,9 @@ private sealed class CodeAnalysisDiagnosticAnalyzerService : ICodeAnalysisDiagno public CodeAnalysisDiagnosticAnalyzerService( IDiagnosticAnalyzerService diagnosticAnalyzerService, - IDiagnosticsRefresher diagnosticsRefresher, Workspace workspace) { _diagnosticAnalyzerService = diagnosticAnalyzerService; - _diagnosticsRefresher = diagnosticsRefresher; _workspace = workspace; _workspace.WorkspaceChanged += OnWorkspaceChanged; @@ -74,7 +70,7 @@ private void OnWorkspaceChanged(object? sender, WorkspaceChangeEventArgs e) _clearedProjectIds.Clear(); // Let LSP know so that it requests up to date info, and will see our cached info disappear. - _diagnosticsRefresher.RequestWorkspaceRefresh(); + _diagnosticAnalyzerService.RequestDiagnosticRefresh(); break; } } @@ -85,7 +81,7 @@ public void Clear() _clearedProjectIds.AddRange(_analyzedProjectIds); // Let LSP know so that it requests up to date info, and will see our cached info disappear. - _diagnosticsRefresher.RequestWorkspaceRefresh(); + _diagnosticAnalyzerService.RequestDiagnosticRefresh(); } public bool HasProjectBeenAnalyzed(ProjectId projectId) => _analyzedProjectIds.Contains(projectId); @@ -128,9 +124,10 @@ private async ValueTask AnalyzeProjectCoreAsync(Project project, Action onAfterProjectAnalyzed(project); // Finally, invoke a workspace refresh request for LSP client to pull onto these diagnostics. - // TODO: Below call will eventually be replaced with a special workspace refresh request that skips - // pulling document diagnostics and also does not add any delay for pulling workspace diagnostics. - _diagnosticsRefresher.RequestWorkspaceRefresh(); + // + // TODO: Below call will eventually be replaced with a special workspace refresh request that skips pulling + // document diagnostics and also does not add any delay for pulling workspace diagnostics. + _diagnosticAnalyzerService.RequestDiagnosticRefresh(); } /// From 26f63dc8870734b624b4e2fdaa322b540ea84113 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Feb 2025 14:11:48 -0800 Subject: [PATCH 096/305] Remove null resiliency --- .../Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs index b274ffc237985..60b051f092a92 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs @@ -70,7 +70,7 @@ public static bool IsGlobalOptionAffectingDiagnostics(IOption2 option) option == s_crashOnAnalyzerException; public void RequestDiagnosticRefresh() - => _diagnosticsRefresher?.RequestWorkspaceRefresh(); + => _diagnosticsRefresher.RequestWorkspaceRefresh(); public async Task> GetDiagnosticsForSpanAsync( TextDocument document, From 29e3374fe60465869bb13f277ca244efd0f32b54 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Feb 2025 14:51:32 -0800 Subject: [PATCH 097/305] Simplify caching api between CodeAnalysisService and DiagnosticAnalysisService --- .../CodeAnalysisDiagnosticAnalyzerService.cs | 46 +++++++++++-------- .../ICodeAnalysisDiagnosticAnalyzerService.cs | 31 +++++++------ .../Diagnostics/IDiagnosticAnalyzerService.cs | 42 ++++++++--------- 3 files changed, 64 insertions(+), 55 deletions(-) diff --git a/src/Features/Core/Portable/Diagnostics/CodeAnalysisDiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/CodeAnalysisDiagnosticAnalyzerService.cs index 543242d1f71ed..fae536bdb1d04 100644 --- a/src/Features/Core/Portable/Diagnostics/CodeAnalysisDiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/Diagnostics/CodeAnalysisDiagnosticAnalyzerService.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Concurrent; using System.Collections.Immutable; using System.Composition; using System.Linq; @@ -34,18 +35,18 @@ private sealed class CodeAnalysisDiagnosticAnalyzerService : ICodeAnalysisDiagno private readonly Workspace _workspace; /// - /// List of projects that we've finished running "run code analysis" on. Cached results can now be returned for - /// these through and . + /// Mapping of projects to the diagnostics for the projects that we've finished running "run code analysis" on. + /// Cached results can now be returned for these through + /// and . /// - private readonly ConcurrentSet _analyzedProjectIds = []; + private readonly ConcurrentDictionary> _analyzedProjectToDiagnostics = []; /// /// Previously analyzed projects that we no longer want to report results for. This happens when an explicit /// build is kicked off. At that point, we want the build results to win out for a particular project. We mark - /// this project (as opposed to removing from ) as we want our LSP handler to - /// still think it should process it, as that will the cause the diagnostics to be removed when they now - /// transition to an empty list returned from this type. + /// this project (as opposed to removing from ) as we want our LSP + /// handler to still think it should process it, as that will the cause the diagnostics to be removed when they + /// now transition to an empty list returned from this type. /// private readonly ConcurrentSet _clearedProjectIds = []; @@ -70,7 +71,7 @@ private void OnWorkspaceChanged(object? sender, WorkspaceChangeEventArgs e) case WorkspaceChangeKind.SolutionReloaded: case WorkspaceChangeKind.SolutionRemoved: - _analyzedProjectIds.Clear(); + _analyzedProjectToDiagnostics.Clear(); _clearedProjectIds.Clear(); // Let LSP know so that it requests up to date info, and will see our cached info disappear. @@ -82,13 +83,14 @@ private void OnWorkspaceChanged(object? sender, WorkspaceChangeEventArgs e) public void Clear() { // Clear the list of analyzed projects. - _clearedProjectIds.AddRange(_analyzedProjectIds); + _clearedProjectIds.AddRange(_analyzedProjectToDiagnostics.Keys); // Let LSP know so that it requests up to date info, and will see our cached info disappear. _diagnosticsRefresher.RequestWorkspaceRefresh(); } - public bool HasProjectBeenAnalyzed(ProjectId projectId) => _analyzedProjectIds.Contains(projectId); + public bool HasProjectBeenAnalyzed(ProjectId projectId) + => _analyzedProjectToDiagnostics.ContainsKey(projectId); public async Task RunAnalysisAsync(Solution solution, ProjectId? projectId, Action onAfterProjectAnalyzed, CancellationToken cancellationToken) { @@ -115,11 +117,11 @@ await RoslynParallel.ForEachAsync( private async ValueTask AnalyzeProjectCoreAsync(Project project, Action onAfterProjectAnalyzed, CancellationToken cancellationToken) { // Execute force analysis for the project. - await _diagnosticAnalyzerService.ForceAnalyzeProjectAsync(project, cancellationToken).ConfigureAwait(false); + var diagnostics = await _diagnosticAnalyzerService.ForceAnalyzeProjectAsync(project, cancellationToken).ConfigureAwait(false); // Add the given project to the analyzed projects list **after** analysis has completed. // We need this ordering to ensure that 'HasProjectBeenAnalyzed' call above functions correctly. - _analyzedProjectIds.Add(project.Id); + _analyzedProjectToDiagnostics[project.Id] = diagnostics; // Remove from the cleared list now that we've run a more recent "run code analysis" on this project. _clearedProjectIds.Remove(project.Id); @@ -141,14 +143,16 @@ private async ValueTask AnalyzeProjectCoreAsync(Project project, Action /// /// Only returns non-suppressed diagnostics. /// - public async Task> GetLastComputedDocumentDiagnosticsAsync(DocumentId documentId, CancellationToken cancellationToken) + public ImmutableArray GetLastComputedDocumentDiagnostics(DocumentId documentId) { if (_clearedProjectIds.Contains(documentId.ProjectId)) return []; - var diagnostics = await _diagnosticAnalyzerService.GetCachedDiagnosticsAsync( - _workspace, documentId.ProjectId, documentId, cancellationToken).ConfigureAwait(false); - return diagnostics.WhereAsArray(d => !d.IsSuppressed); + if (!_analyzedProjectToDiagnostics.TryGetValue(documentId.ProjectId, out var diagnostics)) + return []; + + return diagnostics.WhereAsArray(static (d, documentId) => + !d.IsSuppressed && d.DataLocation.DocumentId == documentId, documentId); } /// @@ -159,14 +163,16 @@ public async Task> GetLastComputedDocumentDiagnos /// /// Only returns non-suppressed diagnostics. /// - public async Task> GetLastComputedProjectDiagnosticsAsync(ProjectId projectId, CancellationToken cancellationToken) + public ImmutableArray GetLastComputedProjectDiagnostics(ProjectId projectId) { if (_clearedProjectIds.Contains(projectId)) return []; - var diagnostics = await _diagnosticAnalyzerService.GetCachedDiagnosticsAsync( - _workspace, projectId, documentId: null, cancellationToken).ConfigureAwait(false); - return diagnostics.WhereAsArray(d => !d.IsSuppressed); + if (!_analyzedProjectToDiagnostics.TryGetValue(projectId, out var diagnostics)) + return []; + + return diagnostics.WhereAsArray(static (d, projectId) => + !d.IsSuppressed && d.ProjectId == projectId && d.DataLocation.DocumentId == null, projectId); } } } diff --git a/src/Features/Core/Portable/Diagnostics/ICodeAnalysisDiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/ICodeAnalysisDiagnosticAnalyzerService.cs index ec4a395e3e964..5b80a8e4b01bd 100644 --- a/src/Features/Core/Portable/Diagnostics/ICodeAnalysisDiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/Diagnostics/ICodeAnalysisDiagnosticAnalyzerService.cs @@ -18,36 +18,39 @@ internal interface ICodeAnalysisDiagnosticAnalyzerService : IWorkspaceService void Clear(); /// - /// Runs all the applicable analyzers on the given project or entire solution if is null. + /// Runs all the applicable analyzers on the given project or entire solution if is + /// null. /// Task RunAnalysisAsync(Solution solution, ProjectId? projectId, Action onAfterProjectAnalyzed, CancellationToken cancellationToken); /// - /// Returns true if was invoked - /// on either the current or a prior snapshot of the project or containing solution for the given . - /// This method will keep returning true for a given project ID once any given snapshot of the project has been analyzed. - /// This changes once the solution is closed/reloaded, at which point all the projects are returned back to not analyzed state - /// and this method will return false. + /// Returns true if was + /// invoked on either the current or a prior snapshot of the project or containing solution for the given . This method will keep returning true for a given project ID once any given snapshot of the + /// project has been analyzed. This changes once the solution is closed/reloaded, at which point all the projects + /// are returned back to not analyzed state and this method will return false. /// bool HasProjectBeenAnalyzed(ProjectId projectId); /// - /// Returns analyzer diagnostics reported on the given > from the last - /// invocation on the containing project or solution. - /// The caller is expected to check prior to calling this method. + /// Returns analyzer diagnostics reported on the given > from the last invocation on the containing + /// project or solution. The caller is expected to check prior to + /// calling this method. /// /// /// Note that the returned diagnostics may not be from the latest document snapshot. /// - Task> GetLastComputedDocumentDiagnosticsAsync(DocumentId documentId, CancellationToken cancellationToken); + ImmutableArray GetLastComputedDocumentDiagnostics(DocumentId documentId); /// - /// Returns analyzer diagnostics without any document location reported on the given > from the last - /// invocation on the given project or solution. - /// The caller is expected to check prior to calling this method. + /// Returns analyzer diagnostics without any document location reported on the given > + /// from the last + /// invocation on the given project or solution. The caller is expected to check prior to calling this method. /// /// /// Note that the returned diagnostics may not be from the latest project snapshot. /// - Task> GetLastComputedProjectDiagnosticsAsync(ProjectId projectId, CancellationToken cancellationToken); + ImmutableArray GetLastComputedProjectDiagnostics(ProjectId projectId); } diff --git a/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs index 3870348bec005..76aaf56a413ac 100644 --- a/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs @@ -25,30 +25,30 @@ internal interface IDiagnosticAnalyzerService /// void RequestDiagnosticRefresh(); - /// - /// Get diagnostics currently stored in the source. returned diagnostic might be out-of-date if solution has changed but analyzer hasn't run for the new solution. - /// - /// Workspace for the document/project/solution to compute diagnostics for. - /// Required project to scope the returned diagnostics. - /// Optional document to scope the returned diagnostics. When provided, only local - /// diagnostics to that document are returned and non-local diagnostics are not returned. When absent, only - /// non-local diagnostics are included and local diagnostics are not returned. - /// Cancellation token. - /// - /// Local diagnostics are the ones that are reported by analyzers on the same file for which the callback was received - /// and hence can be computed by analyzing a single file in isolation. - /// - /// Non-local diagnostics are the ones reported by analyzers either at compilation end callback OR - /// in a different file from which the callback was made. Entire project must be analyzed to get the - /// complete set of non-local document diagnostics. - /// - Task> GetCachedDiagnosticsAsync( - Workspace workspace, ProjectId projectId, DocumentId? documentId, CancellationToken cancellationToken); + ///// + ///// Get diagnostics currently stored in the source. returned diagnostic might be out-of-date if solution has changed but analyzer hasn't run for the new solution. + ///// + ///// Workspace for the document/project/solution to compute diagnostics for. + ///// Required project to scope the returned diagnostics. + ///// Optional document to scope the returned diagnostics. When provided, only local + ///// diagnostics to that document are returned and non-local diagnostics are not returned. When absent, only + ///// non-local diagnostics are included and local diagnostics are not returned. + ///// Cancellation token. + ///// + ///// Local diagnostics are the ones that are reported by analyzers on the same file for which the callback was received + ///// and hence can be computed by analyzing a single file in isolation. + ///// + ///// Non-local diagnostics are the ones reported by analyzers either at compilation end callback OR + ///// in a different file from which the callback was made. Entire project must be analyzed to get the + ///// complete set of non-local document diagnostics. + ///// + //Task> GetCachedDiagnosticsAsync( + // Workspace workspace, ProjectId projectId, DocumentId? documentId, CancellationToken cancellationToken); /// - /// Force analyzes the given project by running all applicable analyzers on the project and caching the reported analyzer diagnostics. + /// Force analyzes the given project by running all applicable analyzers on the project. /// - Task ForceAnalyzeProjectAsync(Project project, CancellationToken cancellationToken); + Task> ForceAnalyzeProjectAsync(Project project, CancellationToken cancellationToken); /// /// Get diagnostics of the given diagnostic ids and/or analyzers from the given solution. all diagnostics returned From 3d308f727b1d0e15eb7f44326d50e2600e298628 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Feb 2025 15:02:04 -0800 Subject: [PATCH 098/305] In progress --- .../Test/CodeFixes/CodeFixServiceTests.cs | 2 +- .../DiagnosticAnalyzerServiceTests.cs | 43 +++----- .../MockDiagnosticAnalyzerService.cs | 6 +- .../Diagnostics/IDiagnosticAnalyzerService.cs | 20 ---- .../Diagnostics/DiagnosticAnalyzerService.cs | 11 +- ...osticIncrementalAnalyzer_GetDiagnostics.cs | 101 ------------------ .../AbstractProjectDiagnosticSource.cs | 2 +- ...stractWorkspaceDocumentDiagnosticSource.cs | 2 +- .../ExternalDiagnosticUpdateSourceTests.vb | 6 +- 9 files changed, 23 insertions(+), 170 deletions(-) diff --git a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs index c9893a6a27f67..a75511c53fcd8 100644 --- a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs +++ b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs @@ -1055,7 +1055,7 @@ void M() // We enable full solution analysis so the 'AnalyzeDocumentAsync' doesn't skip analysis based on whether the document is active/open. workspace.GlobalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp, BackgroundAnalysisScope.FullSolution); - await diagnosticIncrementalAnalyzer.ForceAnalyzeProjectAsync(sourceDocument.Project, CancellationToken.None); + var diagnostics = await diagnosticIncrementalAnalyzer.ForceAnalyzeProjectAsync(sourceDocument.Project, CancellationToken.None); await VerifyCachedDiagnosticsAsync(sourceDocument, expectedCachedDiagnostic: diagnosticOnFixLineInPriorSnapshot, testSpan, diagnosticIncrementalAnalyzer); // Compute and apply code edit diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index c45cdd786d7b2..55549b6bddc15 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -178,8 +178,7 @@ public async Task TestDisabledByDefaultAnalyzerEnabledWithEditorConfig(bool enab Assert.True(applied); var exportProvider = workspace.Services.SolutionServices.ExportProvider; - var service = Assert.IsType(exportProvider.GetExportedValue()); - var analyzer = service.CreateIncrementalAnalyzer(workspace); + var service = exportProvider.GetExportedValue(); // listen to events var syntaxDiagnostic = false; @@ -189,7 +188,7 @@ public async Task TestDisabledByDefaultAnalyzerEnabledWithEditorConfig(bool enab // open document workspace.OpenDocument(document.Id); - var diagnostics = await analyzer.ForceAnalyzeProjectAsync(document.Project, CancellationToken.None); + var diagnostics = await service.ForceAnalyzeProjectAsync(document.Project, CancellationToken.None); foreach (var diagnostic in diagnostics) { @@ -220,14 +219,12 @@ private static async Task TestAnalyzerAsync( { var exportProvider = workspace.Services.SolutionServices.ExportProvider; - var service = Assert.IsType(exportProvider.GetExportedValue()); - - var analyzer = service.CreateIncrementalAnalyzer(workspace); + var service = exportProvider.GetExportedValue(); var syntax = false; var semantic = false; - var diagnostics = await analyzer.ForceAnalyzeProjectAsync(document.Project, CancellationToken.None); + var diagnostics = await service.ForceAnalyzeProjectAsync(document.Project, CancellationToken.None); (syntax, semantic) = resultSetter(syntax, semantic, diagnostics); @@ -314,10 +311,9 @@ public async Task TestHostAnalyzerErrorNotLeaking() filePath: "test.cs")])); var exportProvider = workspace.Services.SolutionServices.ExportProvider; - var service = Assert.IsType(exportProvider.GetExportedValue()); + var service = exportProvider.GetExportedValue(); - var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace); - var diagnostics = await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None); + var diagnostics = await service.ForceAnalyzeProjectAsync(project, CancellationToken.None); Assert.NotEmpty(diagnostics); } @@ -400,10 +396,9 @@ private static AdhocWorkspace CreateWorkspaceWithProjectAndAnalyzer(DiagnosticAn private static async Task TestFullSolutionAnalysisForProjectAsync(AdhocWorkspace workspace, Project project, bool expectAnalyzerExecuted) { var exportProvider = workspace.Services.SolutionServices.ExportProvider; - var service = Assert.IsType(exportProvider.GetExportedValue()); + var service = exportProvider.GetExportedValue(); - var incrementalAnalyzer = service.CreateIncrementalAnalyzer(project.Solution.Workspace); - var diagnostics = await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None); + var diagnostics = await service.ForceAnalyzeProjectAsync(project, CancellationToken.None); if (expectAnalyzerExecuted) { @@ -446,14 +441,13 @@ internal async Task TestAdditionalFileAnalyzer(bool registerFromInitialize, bool Assert.True(applied); var exportProvider = workspace.Services.SolutionServices.ExportProvider; - var service = Assert.IsType(exportProvider.GetExportedValue()); + var service = exportProvider.GetExportedValue(); - var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace); var firstAdditionalDocument = project.AdditionalDocuments.FirstOrDefault(); workspace.OpenAdditionalDocument(firstAdditionalDocument.Id); - var diagnostics = await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None); + var diagnostics = await service.ForceAnalyzeProjectAsync(project, CancellationToken.None); var expectedCount = testMultiple ? 4 : 1; @@ -511,11 +505,9 @@ internal async Task TestDiagnosticSuppressor(bool includeAnalyzer, bool includeS var project = workspace.CurrentSolution.Projects.Single(); var document = project.Documents.Single(); - var service = Assert.IsType(workspace.GetService()); + var service = workspace.GetService(); var globalOptions = workspace.GetService(); - var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace); - switch (analysisScope) { case BackgroundAnalysisScope.None: @@ -536,7 +528,7 @@ internal async Task TestDiagnosticSuppressor(bool includeAnalyzer, bool includeS throw ExceptionUtilities.UnexpectedValue(analysisScope); } - var diagnostics = await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None); + var diagnostics = await service.ForceAnalyzeProjectAsync(project, CancellationToken.None); var diagnostic = diagnostics.SingleOrDefault(); if (includeAnalyzer) @@ -630,12 +622,10 @@ void M() else Assert.IsType(document); - var service = Assert.IsType(workspace.GetService()); + var service = workspace.GetService(); var text = await document.GetTextAsync(); - var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace); - switch (analysisScope) { case BackgroundAnalysisScope.None: @@ -661,7 +651,7 @@ void M() break; } - var diagnostics = await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None); + var diagnostics = await service.ForceAnalyzeProjectAsync(project, CancellationToken.None); diagnostics = [.. diagnostics .Where(d => d.Id == IDEDiagnosticIds.RemoveUnnecessarySuppressionDiagnosticId) @@ -870,10 +860,9 @@ internal async Task TestGeneratorProducedDiagnostics(bool fullSolutionAnalysis, workspace.OpenDocument(document.Id); } - var service = Assert.IsType(workspace.GetService()); + var service = workspace.GetService(); - var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace); - var diagnostics = await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None); + var diagnostics = await service.ForceAnalyzeProjectAsync(project, CancellationToken.None); Assert.NotEmpty(diagnostics); } diff --git a/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs b/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs index 77b15aa2bfaf7..835975b209bda 100644 --- a/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs +++ b/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; @@ -44,10 +43,7 @@ public DiagnosticAnalyzerInfoCache AnalyzerInfoCache public bool ContainsDiagnostics(Workspace workspace, ProjectId projectId) => throw new NotImplementedException(); - public Task ForceAnalyzeProjectAsync(Project project, CancellationToken cancellationToken) - => throw new NotImplementedException(); - - public Task> GetCachedDiagnosticsAsync(Workspace workspace, ProjectId projectId, DocumentId? documentId, CancellationToken cancellationToken) + public Task> ForceAnalyzeProjectAsync(Project project, CancellationToken cancellationToken) => throw new NotImplementedException(); public Task> GetDiagnosticsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) diff --git a/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs index 76aaf56a413ac..5ab400a250dc9 100644 --- a/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs @@ -25,26 +25,6 @@ internal interface IDiagnosticAnalyzerService /// void RequestDiagnosticRefresh(); - ///// - ///// Get diagnostics currently stored in the source. returned diagnostic might be out-of-date if solution has changed but analyzer hasn't run for the new solution. - ///// - ///// Workspace for the document/project/solution to compute diagnostics for. - ///// Required project to scope the returned diagnostics. - ///// Optional document to scope the returned diagnostics. When provided, only local - ///// diagnostics to that document are returned and non-local diagnostics are not returned. When absent, only - ///// non-local diagnostics are included and local diagnostics are not returned. - ///// Cancellation token. - ///// - ///// Local diagnostics are the ones that are reported by analyzers on the same file for which the callback was received - ///// and hence can be computed by analyzing a single file in isolation. - ///// - ///// Non-local diagnostics are the ones reported by analyzers either at compilation end callback OR - ///// in a different file from which the callback was made. Entire project must be analyzed to get the - ///// complete set of non-local document diagnostics. - ///// - //Task> GetCachedDiagnosticsAsync( - // Workspace workspace, ProjectId projectId, DocumentId? documentId, CancellationToken cancellationToken); - /// /// Force analyzes the given project by running all applicable analyzers on the project. /// diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs index b274ffc237985..9e8ee4329fee9 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs @@ -93,17 +93,10 @@ public async Task> GetDiagnosticsForSpanAsync( priorityProvider, diagnosticKinds, isExplicit, cancellationToken).ConfigureAwait(false); } - public Task> GetCachedDiagnosticsAsync( - Workspace workspace, ProjectId projectId, DocumentId? documentId, CancellationToken cancellationToken) - { - var analyzer = CreateIncrementalAnalyzer(workspace); - return analyzer.GetCachedDiagnosticsAsync(workspace.CurrentSolution, projectId, documentId, cancellationToken); - } - - public async Task ForceAnalyzeProjectAsync(Project project, CancellationToken cancellationToken) + public async Task> ForceAnalyzeProjectAsync(Project project, CancellationToken cancellationToken) { var analyzer = CreateIncrementalAnalyzer(project.Solution.Workspace); - await analyzer.ForceAnalyzeProjectAsync(project, cancellationToken).ConfigureAwait(false); + return await analyzer.ForceAnalyzeProjectAsync(project, cancellationToken).ConfigureAwait(false); } public Task> GetDiagnosticsForIdsAsync( diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index 0eb0d66a30c11..19b6fffe2317a 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -15,9 +15,6 @@ namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 { internal partial class DiagnosticIncrementalAnalyzer { - public Task> GetCachedDiagnosticsAsync(Solution solution, ProjectId projectId, DocumentId? documentId, CancellationToken cancellationToken) - => new IdeCachedDiagnosticGetter(this, solution, projectId, documentId).GetDiagnosticsAsync(cancellationToken); - public Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, Func>? getDocuments, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) => new IdeLatestDiagnosticGetter(this, solution, projectId, documentId, diagnosticIds, shouldIncludeAnalyzer, getDocuments, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics).GetDiagnosticsAsync(cancellationToken); @@ -82,104 +79,6 @@ protected void AddIncludedDiagnostics(ArrayBuilder builder, Immu } } - private sealed class IdeCachedDiagnosticGetter( - DiagnosticIncrementalAnalyzer owner, - Solution solution, - ProjectId projectId, - DocumentId? documentId) - : DiagnosticGetter( - owner, solution, projectId, documentId, getDocuments: null, - includeLocalDocumentDiagnostics: documentId != null, - includeNonLocalDocumentDiagnostics: documentId != null) - { - protected override async Task ProduceDiagnosticsAsync( - Project project, IReadOnlyList documentIds, bool includeProjectNonLocalResult, - ArrayBuilder builder, CancellationToken cancellationToken) - { - foreach (var stateSet in StateManager.GetStateSets(project.Id)) - { - foreach (var documentId in documentIds) - { - if (IncludeLocalDocumentDiagnostics) - { - AddIncludedDiagnostics(builder, await GetDiagnosticsAsync(stateSet, project, documentId, AnalysisKind.Syntax, cancellationToken).ConfigureAwait(false)); - AddIncludedDiagnostics(builder, await GetDiagnosticsAsync(stateSet, project, documentId, AnalysisKind.Semantic, cancellationToken).ConfigureAwait(false)); - } - - if (IncludeNonLocalDocumentDiagnostics) - AddIncludedDiagnostics(builder, await GetDiagnosticsAsync(stateSet, project, documentId, AnalysisKind.NonLocal, cancellationToken).ConfigureAwait(false)); - } - - if (includeProjectNonLocalResult) - { - // include project diagnostics if there is no target document - AddIncludedDiagnostics(builder, await GetProjectStateDiagnosticsAsync(stateSet, project, documentId: null, AnalysisKind.NonLocal, cancellationToken).ConfigureAwait(false)); - } - } - } - - public async Task> GetSpecificDiagnosticsAsync(DiagnosticAnalyzer analyzer, AnalysisKind analysisKind, CancellationToken cancellationToken) - { - var project = Solution.GetProject(ProjectId); - if (project == null) - { - // when we return cached result, make sure we at least return something that exist in current solution - return []; - } - - var stateSet = await StateManager.GetOrCreateStateSetAsync(project, analyzer, cancellationToken).ConfigureAwait(false); - if (stateSet == null) - { - return []; - } - - var diagnostics = await GetDiagnosticsAsync(stateSet, project, DocumentId, analysisKind, cancellationToken).ConfigureAwait(false); - - return diagnostics; - } - - private static async Task> GetDiagnosticsAsync(StateSet stateSet, Project project, DocumentId? documentId, AnalysisKind kind, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - // active file diagnostics: - if (documentId != null && kind != AnalysisKind.NonLocal && stateSet.IsActiveFile(documentId)) - return []; - - // project diagnostics: - return await GetProjectStateDiagnosticsAsync(stateSet, project, documentId, kind, cancellationToken).ConfigureAwait(false); - } - - private static async Task> GetProjectStateDiagnosticsAsync(StateSet stateSet, Project project, DocumentId? documentId, AnalysisKind kind, CancellationToken cancellationToken) - { - if (!stateSet.TryGetProjectState(project.Id, out var state)) - { - // never analyzed this project yet. - return []; - } - - if (documentId != null) - { - // file doesn't exist in current solution - var document = await project.Solution.GetTextDocumentAsync( - documentId, - cancellationToken).ConfigureAwait(false); - - if (document == null) - { - return []; - } - - var result = await state.GetAnalysisDataAsync(document, cancellationToken).ConfigureAwait(false); - return result.GetDocumentDiagnostics(documentId, kind); - } - - Contract.ThrowIfFalse(kind == AnalysisKind.NonLocal); - var nonLocalResult = await state.GetProjectAnalysisDataAsync(project, cancellationToken: cancellationToken).ConfigureAwait(false); - return nonLocalResult.GetOtherDiagnostics(); - } - } - private sealed class IdeLatestDiagnosticGetter( DiagnosticIncrementalAnalyzer owner, Solution solution, diff --git a/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractProjectDiagnosticSource.cs b/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractProjectDiagnosticSource.cs index 0c122523e9b86..433c165cdf7de 100644 --- a/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractProjectDiagnosticSource.cs +++ b/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractProjectDiagnosticSource.cs @@ -74,7 +74,7 @@ public override Task> GetDiagnosticsAsync( RequestContext context, CancellationToken cancellationToken) { - return codeAnalysisService.GetLastComputedProjectDiagnosticsAsync(Project.Id, cancellationToken); + return Task.FromResult(codeAnalysisService.GetLastComputedProjectDiagnostics(Project.Id)); } } } diff --git a/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractWorkspaceDocumentDiagnosticSource.cs b/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractWorkspaceDocumentDiagnosticSource.cs index 9becd49893ea4..c3db91aa242c5 100644 --- a/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractWorkspaceDocumentDiagnosticSource.cs +++ b/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractWorkspaceDocumentDiagnosticSource.cs @@ -105,7 +105,7 @@ public override Task> GetDiagnosticsAsync( RequestContext context, CancellationToken cancellationToken) { - return codeAnalysisService.GetLastComputedDocumentDiagnosticsAsync(Document.Id, cancellationToken); + return Task.FromResult(codeAnalysisService.GetLastComputedDocumentDiagnostics(Document.Id)); } } } diff --git a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb index 0e28b473307df..9486133082db8 100644 --- a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb +++ b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb @@ -317,10 +317,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Return SpecializedTasks.EmptyImmutableArray(Of DiagnosticData) End Function - Public Function GetCachedDiagnosticsAsync(workspace As Workspace, projectId As ProjectId, documentId As DocumentId, cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of DiagnosticData)) Implements IDiagnosticAnalyzerService.GetCachedDiagnosticsAsync - Return SpecializedTasks.EmptyImmutableArray(Of DiagnosticData)() - End Function - Public Function GetDiagnosticsForIdsAsync(solution As Solution, projectId As ProjectId, documentId As DocumentId, diagnosticIds As ImmutableHashSet(Of String), shouldIncludeAnalyzer As Func(Of DiagnosticAnalyzer, Boolean), getDocuments As Func(Of Project, DocumentId, IReadOnlyList(Of DocumentId)), includeLocalDocumentDiagnostics As Boolean, includeNonLocalDocumentDiagnostics As Boolean, cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of DiagnosticData)) Implements IDiagnosticAnalyzerService.GetDiagnosticsForIdsAsync Return SpecializedTasks.EmptyImmutableArray(Of DiagnosticData)() End Function @@ -329,7 +325,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Return SpecializedTasks.EmptyImmutableArray(Of DiagnosticData)() End Function - Public Function ForceAnalyzeProjectAsync(project As Project, cancellationToken As CancellationToken) As Task Implements IDiagnosticAnalyzerService.ForceAnalyzeProjectAsync + Public Function ForceAnalyzeProjectAsync(project As Project, cancellationToken As CancellationToken) As Task(Of Implements IDiagnosticAnalyzerService.ForceAnalyzeProjectAsync Throw New NotImplementedException() End Function End Class From e62cf24fcc9b5525ae3b65aa22585f1d9a73798b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Feb 2025 15:05:12 -0800 Subject: [PATCH 099/305] Update tests --- .../Test/CodeFixes/CodeFixServiceTests.cs | 50 ++++++++----------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs index a75511c53fcd8..0c03bef0d7ae8 100644 --- a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs +++ b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs @@ -78,7 +78,7 @@ public async Task TestGetFixesAsyncWithDuplicateDiagnostics() var tuple = ServiceSetup(codeFix); using var workspace = tuple.workspace; - GetDocumentAndExtensionManager(tuple.analyzerService, workspace, out var document, out var extensionManager, analyzerReference); + GetDocumentAndExtensionManager(workspace, out var document, out var extensionManager, analyzerReference); // Verify that we do not crash when computing fixes. _ = await tuple.codeFixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0), CancellationToken.None); @@ -105,7 +105,7 @@ public async Task TestGetFixesAsyncHasNoDuplicateConfigurationActions() var tuple = ServiceSetup(codeFix, includeConfigurationFixProviders: true); using var workspace = tuple.workspace; - GetDocumentAndExtensionManager(tuple.analyzerService, workspace, out var document, out var extensionManager, analyzerReference); + GetDocumentAndExtensionManager(workspace, out var document, out var extensionManager, analyzerReference); // Verify registered configuration code actions do not have duplicates. var fixCollections = await tuple.codeFixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0), CancellationToken.None); @@ -136,7 +136,7 @@ public async Task TestGetFixesAsyncForFixableAndNonFixableAnalyzersAsync() var tuple = ServiceSetup(codeFix, includeConfigurationFixProviders: true); using var workspace = tuple.workspace; - GetDocumentAndExtensionManager(tuple.analyzerService, workspace, out var document, out var extensionManager, analyzerReference); + GetDocumentAndExtensionManager(workspace, out var document, out var extensionManager, analyzerReference); // Verify only analyzerWithFix is executed when GetFixesAsync is invoked with 'CodeActionRequestPriority.Normal'. _ = await tuple.codeFixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0), @@ -172,7 +172,7 @@ public async Task TestGetFixesAsyncForDocumentDiagnosticAnalyzerAsync() var tuple = ServiceSetup(codeFix, includeConfigurationFixProviders: false); using var workspace = tuple.workspace; - GetDocumentAndExtensionManager(tuple.analyzerService, workspace, out var document, out var extensionManager, analyzerReference); + GetDocumentAndExtensionManager(workspace, out var document, out var extensionManager, analyzerReference); // Verify both analyzers are executed when GetFixesAsync is invoked with 'CodeActionRequestPriority.Normal'. _ = await tuple.codeFixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0), @@ -202,7 +202,7 @@ public async Task TestGetFixesAsyncForGeneratorDiagnosticAsync() var tuple = ServiceSetup(codeFix, includeConfigurationFixProviders: false); using var workspace = tuple.workspace; - GetDocumentAndExtensionManager(tuple.analyzerService, workspace, out var document, out var extensionManager, analyzerReference); + GetDocumentAndExtensionManager(workspace, out var document, out var extensionManager, analyzerReference); Assert.False(codeFix.Called); var fixCollectionSet = await tuple.codeFixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0), @@ -288,7 +288,7 @@ private static async Task> GetAddedFixesAsync( var errorReported = false; errorReportingService.OnError = message => errorReported = true; - GetDocumentAndExtensionManager(tuple.analyzerService, workspace, out var document, out var extensionManager); + GetDocumentAndExtensionManager(workspace, out var document, out var extensionManager); var reference = new MockAnalyzerReference(codefix, [diagnosticAnalyzer]); var project = workspace.CurrentSolution.Projects.Single().AddAnalyzerReference(reference); document = project.Documents.Single(); @@ -315,7 +315,7 @@ private static async Task GetFirstDiagnosticWithFixWithExceptionValidationAsync( var errorReported = false; errorReportingService.OnError = message => errorReported = true; - GetDocumentAndExtensionManager(tuple.analyzerService, workspace, out var document, out var extensionManager); + GetDocumentAndExtensionManager(workspace, out var document, out var extensionManager); var unused = await tuple.codeFixService.GetMostSevereFixAsync( document, TextSpan.FromBounds(0, 0), new DefaultCodeActionRequestPriorityProvider(), CancellationToken.None); Assert.True(extensionManager.IsDisabled(codefix)); @@ -373,25 +373,13 @@ private static (EditorTestWorkspace workspace, DiagnosticAnalyzerService analyze } private static void GetDocumentAndExtensionManager( - DiagnosticAnalyzerService diagnosticService, EditorTestWorkspace workspace, out TextDocument document, out EditorLayerExtensionManager.ExtensionManager extensionManager, MockAnalyzerReference? analyzerReference = null, - TextDocumentKind documentKind = TextDocumentKind.Document) - => GetDocumentAndExtensionManager(diagnosticService, workspace, out document, out extensionManager, out _, analyzerReference, documentKind); - - private static void GetDocumentAndExtensionManager( - DiagnosticAnalyzerService diagnosticService, - EditorTestWorkspace workspace, - out TextDocument document, - out EditorLayerExtensionManager.ExtensionManager extensionManager, - out DiagnosticIncrementalAnalyzer diagnosticIncrementalAnalyzer, - MockAnalyzerReference? analyzerReference = null, TextDocumentKind documentKind = TextDocumentKind.Document) { // register diagnostic engine to solution crawler - diagnosticIncrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace)!; var reference = analyzerReference ?? new MockAnalyzerReference(); var project = workspace.CurrentSolution.Projects.Single().AddAnalyzerReference(reference); @@ -879,7 +867,7 @@ public async Task TestAdditionalDocumentCodeFixAsync() // Verify available code fixes for .txt additional document var tuple = ServiceSetup(fixers, additionalDocument: new EditorTestHostDocument("Additional Document", filePath: "test.txt")); using var workspace = tuple.workspace; - GetDocumentAndExtensionManager(tuple.analyzerService, workspace, out var txtDocument, out var extensionManager, analyzerReference, documentKind: TextDocumentKind.AdditionalDocument); + GetDocumentAndExtensionManager(workspace, out var txtDocument, out var extensionManager, analyzerReference, documentKind: TextDocumentKind.AdditionalDocument); var txtDocumentCodeFixes = await tuple.codeFixService.GetFixesAsync(txtDocument, TextSpan.FromBounds(0, 1), CancellationToken.None); Assert.Equal(2, txtDocumentCodeFixes.Length); var txtDocumentCodeFixTitles = txtDocumentCodeFixes.Select(s => s.Fixes.Single().Action.Title).ToImmutableArray(); @@ -896,7 +884,7 @@ public async Task TestAdditionalDocumentCodeFixAsync() // Verify available code fixes for .log additional document tuple = ServiceSetup(fixers, additionalDocument: new EditorTestHostDocument("Additional Document", filePath: "test.log")); using var workspace2 = tuple.workspace; - GetDocumentAndExtensionManager(tuple.analyzerService, workspace2, out var logDocument, out extensionManager, analyzerReference, documentKind: TextDocumentKind.AdditionalDocument); + GetDocumentAndExtensionManager(workspace2, out var logDocument, out extensionManager, analyzerReference, documentKind: TextDocumentKind.AdditionalDocument); var logDocumentCodeFixes = await tuple.codeFixService.GetFixesAsync(logDocument, TextSpan.FromBounds(0, 1), CancellationToken.None); var logDocumentCodeFix = Assert.Single(logDocumentCodeFixes); var logDocumentCodeFixTitle = logDocumentCodeFix.Fixes.Single().Action.Title; @@ -1042,8 +1030,9 @@ void M() var tuple = ServiceSetup(codeFix, code: code); using var workspace = tuple.workspace; - GetDocumentAndExtensionManager(tuple.analyzerService, workspace, out var document, - out var extensionManager, out var diagnosticIncrementalAnalyzer, analyzerReference); + var analyzerService = tuple.analyzerService; + GetDocumentAndExtensionManager(workspace, out var document, + out var extensionManager, analyzerReference); var sourceDocument = (Document)document; var root = await sourceDocument.GetRequiredSyntaxRootAsync(CancellationToken.None); @@ -1055,8 +1044,9 @@ void M() // We enable full solution analysis so the 'AnalyzeDocumentAsync' doesn't skip analysis based on whether the document is active/open. workspace.GlobalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp, BackgroundAnalysisScope.FullSolution); - var diagnostics = await diagnosticIncrementalAnalyzer.ForceAnalyzeProjectAsync(sourceDocument.Project, CancellationToken.None); - await VerifyCachedDiagnosticsAsync(sourceDocument, expectedCachedDiagnostic: diagnosticOnFixLineInPriorSnapshot, testSpan, diagnosticIncrementalAnalyzer); + var diagnostics = await analyzerService.ForceAnalyzeProjectAsync(sourceDocument.Project, CancellationToken.None); + await VerifyCachedDiagnosticsAsync( + sourceDocument, expectedCachedDiagnostic: diagnosticOnFixLineInPriorSnapshot, testSpan, diagnostics); // Compute and apply code edit if (editOnFixLine) @@ -1085,7 +1075,7 @@ void M() ? root.DescendantNodes().OfType().First().Span : root.DescendantNodes().OfType().First().Span; - await diagnosticIncrementalAnalyzer.GetDiagnosticsForIdsAsync( + await analyzerService.GetDiagnosticsForIdsAsync( sourceDocument.Project.Solution, sourceDocument.Project.Id, sourceDocument.Id, diagnosticIds: null, shouldIncludeAnalyzer: null, getDocuments: null, includeLocalDocumentDiagnostics: true, includeNonLocalDocumentDiagnostics: true, CancellationToken.None); // await diagnosticIncrementalAnalyzer.GetTestAccessor().TextDocumentOpenAsync(sourceDocument); @@ -1148,10 +1138,12 @@ static bool GetExpectDeprioritization( return addNewLineWithEdit; } - static async Task VerifyCachedDiagnosticsAsync(Document sourceDocument, bool expectedCachedDiagnostic, TextSpan testSpan, DiagnosticIncrementalAnalyzer diagnosticIncrementalAnalyzer) + static async Task VerifyCachedDiagnosticsAsync( + Document sourceDocument, + bool expectedCachedDiagnostic, + TextSpan testSpan, + ImmutableArray cachedDiagnostics) { - var cachedDiagnostics = await diagnosticIncrementalAnalyzer.GetCachedDiagnosticsAsync( - sourceDocument.Project.Solution, sourceDocument.Project.Id, sourceDocument.Id, CancellationToken.None); cachedDiagnostics = cachedDiagnostics.WhereAsArray(d => !d.IsSuppressed); if (!expectedCachedDiagnostic) From a0f8662ffa7b2ee9c4feea2e8dd1fded091d7ea2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Feb 2025 15:06:44 -0800 Subject: [PATCH 100/305] Update tests --- .../Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb index 9486133082db8..b95473b862276 100644 --- a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb +++ b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb @@ -325,7 +325,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Return SpecializedTasks.EmptyImmutableArray(Of DiagnosticData)() End Function - Public Function ForceAnalyzeProjectAsync(project As Project, cancellationToken As CancellationToken) As Task(Of Implements IDiagnosticAnalyzerService.ForceAnalyzeProjectAsync + Public Function ForceAnalyzeProjectAsync(project As Project, cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of DiagnosticData)) Implements IDiagnosticAnalyzerService.ForceAnalyzeProjectAsync Throw New NotImplementedException() End Function End Class From 6ed2edf033e8b020b3f16f0645a8fc672293eb12 Mon Sep 17 00:00:00 2001 From: Ankita Khera <40616383+akhera99@users.noreply.github.com> Date: Thu, 6 Feb 2025 15:12:21 -0800 Subject: [PATCH 101/305] update telemetry version (#77067) --- eng/Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/Directory.Packages.props b/eng/Directory.Packages.props index 319b5c9bb6b20..3de272729059c 100644 --- a/eng/Directory.Packages.props +++ b/eng/Directory.Packages.props @@ -106,7 +106,7 @@ - + From c307ed002d6fd02de5918b7e14f3a1020426f4c9 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Feb 2025 15:16:11 -0800 Subject: [PATCH 102/305] No casting --- .../Test2/CodeFixes/CodeFixServiceTests.vb | 6 +- .../Diagnostics/DiagnosticProviderTests.vb | 4 +- .../Diagnostics/DiagnosticServiceTests.vb | 109 +++++++----------- 3 files changed, 44 insertions(+), 75 deletions(-) diff --git a/src/EditorFeatures/Test2/CodeFixes/CodeFixServiceTests.vb b/src/EditorFeatures/Test2/CodeFixes/CodeFixServiceTests.vb index f2daa492cf9c6..347f4bb79afdb 100644 --- a/src/EditorFeatures/Test2/CodeFixes/CodeFixServiceTests.vb +++ b/src/EditorFeatures/Test2/CodeFixes/CodeFixServiceTests.vb @@ -54,8 +54,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.CodeFixes.UnitTests Dim project = workspace.CurrentSolution.Projects(0) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) - Dim analyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() Dim logger = SpecializedCollections.SingletonEnumerable(New Lazy(Of IErrorLoggerService)(Function() workspace.Services.GetService(Of IErrorLoggerService))) Dim codefixService = New CodeFixService( diagnosticService, @@ -126,8 +125,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.CodeFixes.UnitTests Dim project = workspace.CurrentSolution.Projects(0) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) - Dim analyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() Dim logger = SpecializedCollections.SingletonEnumerable(New Lazy(Of IErrorLoggerService)(Function() workspace.Services.GetService(Of IErrorLoggerService))) Dim codefixService = New CodeFixService( diagnosticService, diff --git a/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb b/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb index 7bcb4b1bbc123..5536152449f15 100644 --- a/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb @@ -283,14 +283,14 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests End Using End Sub - Private Shared Function GetDiagnosticProvider(workspace As EditorTestWorkspace) As DiagnosticAnalyzerService + Private Shared Function GetDiagnosticProvider(workspace As EditorTestWorkspace) As IDiagnosticAnalyzerService Dim compilerAnalyzersMap = DiagnosticExtensions.GetCompilerDiagnosticAnalyzersMap().Add( NoCompilationConstants.LanguageName, ImmutableArray.Create(Of DiagnosticAnalyzer)(New NoCompilationDocumentDiagnosticAnalyzer())) Dim analyzerReference = New TestAnalyzerReferenceByLanguage(compilerAnalyzersMap) workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences({analyzerReference})) - Dim analyzerService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) + Dim analyzerService = workspace.GetService(Of IDiagnosticAnalyzerService)() Return analyzerService End Function diff --git a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb index 2a1058a1e7397..01274bb16ac82 100644 --- a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb @@ -85,7 +85,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim hostAnalyzers = solution.SolutionState.Analyzers Dim project = solution.Projects(0) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() ' Verify available diagnostic descriptors/analyzers Dim descriptorsMap = hostAnalyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) @@ -95,7 +95,6 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Assert.Equal(workspaceDiagnosticAnalyzer.DiagDescriptor.Id, descriptors(0).Id) Dim document = project.Documents.Single() - Dim analyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) Dim diagnostics = Await GetDiagnosticsForDocumentAsync(diagnosticService, document) Assert.Equal(1, diagnostics.Length) @@ -190,7 +189,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim project = solution.Projects(0) Dim hostAnalyzers = solution.SolutionState.Analyzers - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() ' Add project analyzer reference with no analyzers. Dim projectAnalyzersEmpty = ImmutableArray(Of DiagnosticAnalyzer).Empty @@ -234,7 +233,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim projectAnalyzerReference = New AnalyzerImageReference( ImmutableArray.Create(Of DiagnosticAnalyzer)(New TestDiagnosticAnalyzer1(1)), display:=referenceName) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() project = project.WithAnalyzerReferences(ImmutableArray.Create(Of AnalyzerReference)(projectAnalyzerReference)) @@ -270,7 +269,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim project = solution.Projects(0) Dim hostAnalyzers = solution.SolutionState.Analyzers - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() ' Verify available diagnostic descriptors/analyzers Dim descriptorsMap = hostAnalyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) @@ -282,7 +281,6 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim document = project.Documents.Single() Dim span = (Await document.GetSyntaxRootAsync()).FullSpan - Dim analyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) Dim diagnostics = Await GetDiagnosticsForSpanAsync(diagnosticService, document, span) Assert.Equal(1, diagnostics.Length) Assert.Equal(workspaceDiagnosticAnalyzer.DiagDescriptor.Id, diagnostics(0).Id) @@ -340,8 +338,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests solution = p2.Solution Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) - Dim analyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() Dim hostAnalyzers = solution.SolutionState.Analyzers Dim workspaceDescriptors = hostAnalyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache) @@ -382,7 +379,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim analyzerReference = New TestAnalyzerReferenceByLanguage(analyzersMap) workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences({analyzerReference})) - Dim diagnosticService2 = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) + Dim diagnosticService2 = workspace.GetService(Of IDiagnosticAnalyzerService)() Dim descriptors = workspace.CurrentSolution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService2.AnalyzerInfoCache) Assert.Equal(1, descriptors.Count) @@ -430,8 +427,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim analyzerReference2 = CreateAnalyzerFileReference(Assembly.GetExecutingAssembly().Location) project = project.AddAnalyzerReference(analyzerReference2) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) - Dim analyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() Dim descriptorsMap = workspace.CurrentSolution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) ' Verify no duplicate diagnostics. @@ -484,7 +480,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) Assert.Equal(1, descriptorsMap.Count) @@ -493,7 +489,6 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim document = project.Documents.Single() - Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) Dim diagnostics = Await GetDiagnosticsForDocumentAsync(diagnosticService, document) Assert.Empty(diagnostics) @@ -519,14 +514,13 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) Assert.Equal(1, descriptorsMap.Count) Dim document = project.Documents.Single() - Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) Dim root = Await document.GetSyntaxRootAsync().ConfigureAwait(False) Dim diagnostics = Await GetDiagnosticsForSpanAsync(diagnosticService, document, root.FullSpan) Assert.Empty(diagnostics) @@ -565,9 +559,8 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() - Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) Dim span = (Await document.GetSyntaxRootAsync().ConfigureAwait(False)).FullSpan Dim diagnostics = Await GetDiagnosticsForSpanAsync(diagnosticService, document, span).ConfigureAwait(False) Assert.Equal(1, diagnostics.Length) @@ -588,8 +581,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Using workspace = TestWorkspace.CreateWorkspace(test, composition:=s_compositionWithMockDiagnosticUpdateSourceRegistrationService) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) - Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() For Each actionKind As OperationAnalyzer.ActionKind In [Enum].GetValues(GetType(OperationAnalyzer.ActionKind)) Dim solution = workspace.CurrentSolution @@ -632,14 +624,13 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) Assert.Equal(1, descriptorsMap.Count) Dim document = project.Documents.Single() - Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) Dim diagnostics = Await GetDiagnosticsForDocumentAsync(diagnosticService, document) Assert.Equal(1, diagnostics.Length) @@ -666,7 +657,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() ' Ensure no duplicate diagnostics. Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) @@ -674,7 +665,6 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim document = project.Documents.Single() - Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) Dim diagnostics = Await GetDiagnosticsForDocumentAsync(diagnosticService, document) Assert.Equal(1, diagnostics.Length) Dim diagnostic = diagnostics.First() @@ -742,7 +732,7 @@ class AnonymousFunctions project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() ' Ensure no duplicate diagnostics. Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) @@ -750,7 +740,6 @@ class AnonymousFunctions Dim document = project.Documents.Single() - Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) Dim diagnostics = Await GetDiagnosticsForDocumentAsync(diagnosticService, document) Assert.Equal(4, diagnostics.Length) @@ -778,8 +767,7 @@ class AnonymousFunctions project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) - Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) Assert.Equal(1, descriptorsMap.Count) @@ -937,9 +925,8 @@ class AnonymousFunctions Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)) project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() - Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) Assert.Equal(1, descriptorsMap.Count) @@ -977,7 +964,7 @@ class AnonymousFunctions project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) Assert.Equal(1, descriptorsMap.Count) @@ -985,7 +972,6 @@ class AnonymousFunctions Dim document = project.Documents.Single(Function(d) d.Name = "Test1.cs") Dim fullSpan = (Await document.GetSyntaxRootAsync()).FullSpan - Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) Dim diagnostics = Await GetDiagnosticsForSpanAsync(diagnosticService, document, fullSpan) Assert.Equal(1, diagnostics.Length) Assert.Equal(PartialTypeDiagnosticAnalyzer.DiagDescriptor.Id, diagnostics.Single().Id) @@ -1033,13 +1019,12 @@ class AnonymousFunctions project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) Assert.Equal(1, descriptorsMap.Count) ' Verify project diagnostics contains diagnostics reported on both partial definitions. - Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) Dim diagnostics = Await diagnosticService.GetDiagnosticsForIdsAsync( project.Solution, project.Id, documentId:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, includeLocalDocumentDiagnostics:=True, includeNonLocalDocumentDiagnostics:=True, CancellationToken.None) @@ -1085,7 +1070,7 @@ public class B project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) Assert.Equal(1, descriptorsMap.Count) @@ -1093,7 +1078,6 @@ public class B Dim document = project.Documents.Single() Dim fullSpan = (Await document.GetSyntaxRootAsync()).FullSpan - Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) Dim diagnostics = Await GetDiagnosticsForSpanAsync(diagnosticService, document, fullSpan) Assert.Equal(6, diagnostics.Length) Assert.Equal(3, diagnostics.Where(Function(d) d.Id = CodeBlockOrSyntaxNodeAnalyzer.Descriptor1.Id).Count) @@ -1128,7 +1112,7 @@ public class B project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) Assert.Equal(1, descriptorsMap.Count) @@ -1136,7 +1120,6 @@ public class B Dim document = project.Documents.Single() Dim fullSpan = (Await document.GetSyntaxRootAsync()).FullSpan - Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) Dim diagnostics = Await GetDiagnosticsForSpanAsync(diagnosticService, document, fullSpan) Assert.Equal(3, diagnostics.Length) @@ -1171,7 +1154,7 @@ public class B project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) Assert.Equal(1, descriptorsMap.Count) @@ -1180,8 +1163,6 @@ public class B Dim text = Await document.GetTextAsync() Dim fullSpan = (Await document.GetSyntaxRootAsync()).FullSpan - Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) - Dim diagnostics = (Await GetDiagnosticsForSpanAsync(diagnosticService, document, fullSpan)). OrderBy(Function(d) d.DataLocation.UnmappedFileSpan.GetClampedTextSpan(text).Start).ToArray() @@ -1223,7 +1204,7 @@ End Class project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) Assert.Equal(1, descriptorsMap.Count) @@ -1231,7 +1212,6 @@ End Class Dim document = project.Documents.Single() Dim fullSpan = (Await document.GetSyntaxRootAsync()).FullSpan - Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) Dim diagnostics = Await GetDiagnosticsForSpanAsync(diagnosticService, document, fullSpan) Assert.Equal(1, diagnostics.Length) Assert.Equal(1, diagnostics.Where(Function(d) d.Id = MustOverrideMethodAnalyzer.Descriptor1.Id).Count) @@ -1286,7 +1266,7 @@ public class B project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) Assert.Equal(1, descriptorsMap.Count) @@ -1294,7 +1274,6 @@ public class B Dim document = project.Documents.Single() Dim fullSpan = (Await document.GetSyntaxRootAsync()).FullSpan - Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) Dim text = Await document.GetTextAsync() Dim diagnostics = (Await GetDiagnosticsForSpanAsync(diagnosticService, document, fullSpan)). OrderBy(Function(d) d.DataLocation.UnmappedFileSpan.GetClampedTextSpan(text).Start). @@ -1333,7 +1312,7 @@ public class B project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) Assert.Equal(1, descriptorsMap.Count) @@ -1341,7 +1320,6 @@ public class B Dim document = project.Documents.Single() Dim fullSpan = (Await document.GetSyntaxRootAsync()).FullSpan - Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) Dim text = Await document.GetTextAsync() Dim diagnostics = (Await GetDiagnosticsForSpanAsync(diagnosticService, document, fullSpan)). OrderBy(Function(d) d.DataLocation.UnmappedFileSpan.GetClampedTextSpan(text).Start). @@ -1404,8 +1382,7 @@ public class B project = additionalDoc.Project Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) - Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() Await TestCompilationAnalyzerWithAnalyzerOptionsCoreAsync(project, additionalDocText, diagnosticService) @@ -1419,7 +1396,10 @@ public class B End Using End Function - Private Shared Async Function TestCompilationAnalyzerWithAnalyzerOptionsCoreAsync(project As Project, expectedDiagnosticMessage As String, diagnosticService As DiagnosticAnalyzerService) As Task + Private Shared Async Function TestCompilationAnalyzerWithAnalyzerOptionsCoreAsync( + project As Project, + expectedDiagnosticMessage As String, + diagnosticService As IDiagnosticAnalyzerService) As Task Dim descriptorsMap = project.Solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) Assert.Equal(1, descriptorsMap.Count) @@ -1954,14 +1934,13 @@ End Class project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) Assert.Equal(1, descriptorsMap.Count) Dim document = project.Documents.Single() - Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) Dim diagnostics = Await GetDiagnosticsForDocumentAsync(diagnosticService, document) Dim expectedCount = If(onlyStatelessAction, 1, 2) @@ -2014,14 +1993,13 @@ namespace ConsoleApplication1 project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) Assert.Equal(1, descriptorsMap.Count) Dim document = project.Documents.Single() - Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) Dim diagnostics = Await GetDiagnosticsForDocumentAsync(diagnosticService, document) Assert.Equal(1, diagnostics.Length) @@ -2081,8 +2059,7 @@ class MyClass project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) - Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() ' Verify available diagnostic descriptors/analyzers Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) @@ -2120,8 +2097,7 @@ class MyClass project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) - Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() ' Verify available diagnostic descriptors Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) @@ -2131,8 +2107,8 @@ class MyClass Assert.Equal(analyzer.Descriptor.Id, descriptors.Single().Id) ' Get cached project diagnostics. - Dim diagnostics = Await diagnosticService.GetCachedDiagnosticsAsync( - workspace, project.Id, documentId:=Nothing, CancellationToken.None) + Dim diagnostics = Await diagnosticService.ForceAnalyzeProjectAsync( + project, CancellationToken.None) ' in v2, solution crawler never creates non-local hidden diagnostics. ' v2 still creates those for LB and explicit queries such as FixAll. @@ -2180,8 +2156,7 @@ class C Dim span = localDecl.Span Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) - Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() ' Verify diagnostics for span Dim diagnostics = Await GetDiagnosticsForSpanAsync(diagnosticService, document, span) @@ -2221,12 +2196,11 @@ class C project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) Assert.Equal(1, descriptorsMap.Count) - Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) Dim diagnostics = Await diagnosticService.GetDiagnosticsForIdsAsync( project.Solution, project.Id, documentId:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, includeLocalDocumentDiagnostics:=True, includeNonLocalDocumentDiagnostics:=True, CancellationToken.None) @@ -2266,8 +2240,7 @@ class MyClass project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) - Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() ' Get diagnostics for span for the given DiagnosticKind Dim document = project.Documents.Single() @@ -2336,8 +2309,7 @@ class MyClass project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) - Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() ' Get diagnostics for span for fine grained DiagnosticKind in random order Dim document = project.Documents.Single() @@ -2426,7 +2398,7 @@ public class C project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) Assert.Equal(1, descriptorsMap.Count) @@ -2438,7 +2410,6 @@ public class C Assert.Equal("M1", firstMethodDecl.Identifier.ValueText) Dim span = firstMethodDecl.Span - Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) Dim text = Await document.GetTextAsync() Dim diagnostics = Await GetDiagnosticsForSpanAsync(diagnosticService, document, span) Assert.Empty(diagnostics) From 87afb6193a160498a10d134ce11f02e223cdab22 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Feb 2025 15:17:52 -0800 Subject: [PATCH 103/305] No casting --- .../Test/CodeFixes/CodeFixServiceTests.cs | 10 +++++----- .../Diagnostics/DiagnosticAnalyzerServiceTests.cs | 5 ++--- .../CodeAnalysisDiagnosticAnalyzerService.cs | 12 ++++++------ 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs index bf6e56bdc0078..190135f54ce47 100644 --- a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs +++ b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs @@ -43,7 +43,7 @@ public async Task TestGetFirstDiagnosticWithFixAsync() "; using var workspace = TestWorkspace.CreateCSharp(code, composition: s_compositionWithMockDiagnosticUpdateSourceRegistrationService, openDocuments: true); - var diagnosticService = Assert.IsType(workspace.GetService()); + var diagnosticService = workspace.GetService(); var analyzerReference = new TestAnalyzerReferenceByLanguage(DiagnosticExtensions.GetCompilerDiagnosticAnalyzersMap()); workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences([analyzerReference])); @@ -323,7 +323,7 @@ private static async Task GetFirstDiagnosticWithFixWithExceptionValidationAsync( Assert.True(errorReported); } - private static (EditorTestWorkspace workspace, DiagnosticAnalyzerService analyzerService, CodeFixService codeFixService, IErrorLoggerService errorLogger) ServiceSetup( + private static (EditorTestWorkspace workspace, IDiagnosticAnalyzerService analyzerService, CodeFixService codeFixService, IErrorLoggerService errorLogger) ServiceSetup( CodeFixProvider codefix, bool includeConfigurationFixProviders = false, bool throwExceptionInFixerCreation = false, @@ -331,7 +331,7 @@ private static (EditorTestWorkspace workspace, DiagnosticAnalyzerService analyze string code = "class Program { }") => ServiceSetup([codefix], includeConfigurationFixProviders, throwExceptionInFixerCreation, additionalDocument, code); - private static (EditorTestWorkspace workspace, DiagnosticAnalyzerService analyzerService, CodeFixService codeFixService, IErrorLoggerService errorLogger) ServiceSetup( + private static (EditorTestWorkspace workspace, IDiagnosticAnalyzerService analyzerService, CodeFixService codeFixService, IErrorLoggerService errorLogger) ServiceSetup( ImmutableArray codefixers, bool includeConfigurationFixProviders = false, bool throwExceptionInFixerCreation = false, @@ -355,7 +355,7 @@ private static (EditorTestWorkspace workspace, DiagnosticAnalyzerService analyze var analyzerReference = new TestAnalyzerReferenceByLanguage(DiagnosticExtensions.GetCompilerDiagnosticAnalyzersMap()); workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences([analyzerReference])); - var diagnosticService = Assert.IsType(workspace.GetService()); + var diagnosticService = workspace.GetService(); var logger = SpecializedCollections.SingletonEnumerable(new Lazy(() => new TestErrorLogger())); var errorLogger = logger.First().Value; @@ -754,7 +754,7 @@ private static async Task> GetNuGetAndVsixCode using var workspace = TestWorkspace.CreateCSharp(code, composition: s_compositionWithMockDiagnosticUpdateSourceRegistrationService, openDocuments: true); - var diagnosticService = Assert.IsType(workspace.GetService()); + var diagnosticService = workspace.GetService(); var logger = SpecializedCollections.SingletonEnumerable(new Lazy(() => workspace.Services.GetRequiredService())); var fixService = new CodeFixService( diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index 996fafda1370a..3c08729f7baee 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -63,11 +63,10 @@ public async Task TestHasSuccessfullyLoadedBeingFalse() var document = GetDocumentFromIncompleteProject(workspace); var exportProvider = workspace.Services.SolutionServices.ExportProvider; - var service = Assert.IsType(exportProvider.GetExportedValue()); - var analyzer = service.CreateIncrementalAnalyzer(workspace); + var service = exportProvider.GetExportedValue(); var globalOptions = exportProvider.GetExportedValue(); - var diagnostics = await analyzer.GetDiagnosticsForIdsAsync( + var diagnostics = await service.GetDiagnosticsForIdsAsync( workspace.CurrentSolution, projectId: workspace.CurrentSolution.ProjectIds.Single(), documentId: null, diagnosticIds: null, shouldIncludeAnalyzer: null, includeLocalDocumentDiagnostics: true, includeNonLocalDocumentDiagnostics: false, CancellationToken.None); Assert.NotEmpty(diagnostics); diff --git a/src/Features/Core/Portable/Diagnostics/CodeAnalysisDiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/CodeAnalysisDiagnosticAnalyzerService.cs index fae536bdb1d04..d0ec8e047bf0e 100644 --- a/src/Features/Core/Portable/Diagnostics/CodeAnalysisDiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/Diagnostics/CodeAnalysisDiagnosticAnalyzerService.cs @@ -136,9 +136,9 @@ private async ValueTask AnalyzeProjectCoreAsync(Project project, Action } /// - /// Running code analysis on the project force computes and caches the diagnostics on the - /// DiagnosticAnalyzerService. We return these cached document diagnostics here, including both local and - /// non-local document diagnostics. + /// Running code analysis on the project force computes and caches the diagnostics in . We return these cached document diagnostics here, including both + /// local and non-local document diagnostics. /// /// /// Only returns non-suppressed diagnostics. @@ -156,9 +156,9 @@ public ImmutableArray GetLastComputedDocumentDiagnostics(Documen } /// - /// Running code analysis on the project force computes and caches the diagnostics on the - /// DiagnosticAnalyzerService. We return these cached project diagnostics here, i.e. diagnostics with no - /// location, by excluding all local and non-local document diagnostics. + /// Running code analysis on the project force computes and caches the diagnostics in . We return these cached project diagnostics here, i.e. diagnostics + /// with no location, by excluding all local and non-local document diagnostics. /// /// /// Only returns non-suppressed diagnostics. From 040150bad40663125853bbe0281bfe39f329b985 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Feb 2025 15:18:49 -0800 Subject: [PATCH 104/305] No casting --- .../CSharpTest/Diagnostics/Suppression/SuppressionTests.cs | 3 +-- .../Diagnostics/TestDiagnosticAnalyzerDriver.cs | 5 ++--- .../Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb | 3 +-- .../Core/Test/Venus/DocumentService_IntegrationTests.vb | 5 +---- 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTests.cs b/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTests.cs index 7d9906343d41b..10a161b802860 100644 --- a/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTests.cs +++ b/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTests.cs @@ -446,8 +446,7 @@ void Method() var analyzerReference = new AnalyzerImageReference([new CSharpCompilerDiagnosticAnalyzer()]); workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences([analyzerReference])); - var diagnosticService = Assert.IsType(workspace.ExportProvider.GetExportedValue()); - var incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace); + var diagnosticService = workspace.ExportProvider.GetExportedValue(); var suppressionProvider = CreateDiagnosticProviderAndFixer(workspace).Item2; var suppressionProviderFactory = new Lazy(() => suppressionProvider, new CodeChangeProviderMetadata("SuppressionProvider", languages: [LanguageNames.CSharp])); diff --git a/src/LanguageServer/Protocol.TestUtilities/Diagnostics/TestDiagnosticAnalyzerDriver.cs b/src/LanguageServer/Protocol.TestUtilities/Diagnostics/TestDiagnosticAnalyzerDriver.cs index 04e23cd3ff207..aacc5d3037843 100644 --- a/src/LanguageServer/Protocol.TestUtilities/Diagnostics/TestDiagnosticAnalyzerDriver.cs +++ b/src/LanguageServer/Protocol.TestUtilities/Diagnostics/TestDiagnosticAnalyzerDriver.cs @@ -21,7 +21,7 @@ namespace Microsoft.CodeAnalysis.UnitTests.Diagnostics { public class TestDiagnosticAnalyzerDriver { - private readonly DiagnosticAnalyzerService _diagnosticAnalyzerService; + private readonly IDiagnosticAnalyzerService _diagnosticAnalyzerService; private readonly bool _includeSuppressedDiagnostics; private readonly bool _includeNonLocalDocumentDiagnostics; @@ -29,8 +29,7 @@ public TestDiagnosticAnalyzerDriver(Workspace workspace, bool includeSuppressedD { var mefServices = workspace.Services.SolutionServices.ExportProvider; - _diagnosticAnalyzerService = Assert.IsType(mefServices.GetExportedValue()); - _diagnosticAnalyzerService.CreateIncrementalAnalyzer(workspace); + _diagnosticAnalyzerService = mefServices.GetExportedValue(); _includeSuppressedDiagnostics = includeSuppressedDiagnostics; _includeNonLocalDocumentDiagnostics = includeNonLocalDocumentDiagnostics; } diff --git a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb index b73c2556a24c9..09a8034a825f5 100644 --- a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb +++ b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb @@ -233,8 +233,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim project = workspace.CurrentSolution.Projects.First() - Dim service = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) - Dim registration = service.CreateIncrementalAnalyzer(workspace) + Dim service = workspace.GetService(Of IDiagnosticAnalyzerService)() Dim threadingContext = workspace.ExportProvider.GetExportedValue(Of IThreadingContext) Dim testServiceBroker = workspace.ExportProvider.GetExportedValue(Of TestServiceBroker) Dim vsWorkspace = workspace.ExportProvider.GetExportedValue(Of MockVisualStudioWorkspace)() diff --git a/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb b/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb index 85111a14aaf6e..8b27dccbadb98 100644 --- a/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb +++ b/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb @@ -229,14 +229,11 @@ class { } ' confirm there are errors Assert.True(model.GetDiagnostics().Any()) - Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) + Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() ' confirm diagnostic support is off for the document Assert.False(document.SupportsDiagnostics()) - ' register the workspace to the service - diagnosticService.CreateIncrementalAnalyzer(workspace) - ' confirm that IDE doesn't report the diagnostics Dim diagnostics = Await diagnosticService.GetDiagnosticsForIdsAsync( workspace.CurrentSolution, projectId:=Nothing, documentId:=document.Id, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, From bb4e44f5125a9b194e57582a619d8f9d7d02b26a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Feb 2025 15:22:04 -0800 Subject: [PATCH 105/305] delete --- .../MockDiagnosticAnalyzerService.cs | 59 ------------------- 1 file changed, 59 deletions(-) delete mode 100644 src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs diff --git a/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs b/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs deleted file mode 100644 index 36119c41c5802..0000000000000 --- a/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Immutable; -using System.Composition; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Text; - -namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics; - -[Export(typeof(IDiagnosticAnalyzerService)), Shared, PartNotDiscoverable] -[method: ImportingConstructor] -[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal class MockDiagnosticAnalyzerService() : IDiagnosticAnalyzerService -{ - private readonly ArrayBuilder<(DiagnosticData Diagnostic, DiagnosticKind KindFilter)> _diagnosticsWithKindFilter = ArrayBuilder<(DiagnosticData Diagnostic, DiagnosticKind KindFilter)>.GetInstance(); - public bool RequestedRefresh; - - public void AddDiagnostic(DiagnosticData diagnostic, DiagnosticKind diagnosticKind) - => _diagnosticsWithKindFilter.Add((diagnostic, diagnosticKind)); - - public void AddDiagnostics(ImmutableArray diagnostics, DiagnosticKind diagnosticKind) - { - foreach (var diagnostic in diagnostics) - AddDiagnostic(diagnostic, diagnosticKind); - } - - public void RequestDiagnosticRefresh() - => RequestedRefresh = true; - - public DiagnosticAnalyzerInfoCache AnalyzerInfoCache - => throw new NotImplementedException(); - - public bool ContainsDiagnostics(Workspace workspace, ProjectId projectId) - => throw new NotImplementedException(); - - public Task> ForceAnalyzeProjectAsync(Project project, CancellationToken cancellationToken) - => throw new NotImplementedException(); - - public Task> GetDiagnosticsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) - => throw new NotImplementedException(); - - public Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) - => throw new NotImplementedException(); - - public Task> GetDiagnosticsForSpanAsync(TextDocument document, TextSpan? range, Func? shouldIncludeDiagnostic, ICodeActionRequestPriorityProvider priorityProvider, DiagnosticKind diagnosticKind, bool isExplicit, CancellationToken cancellationToken) - => Task.FromResult(_diagnosticsWithKindFilter.Where(d => diagnosticKind == d.KindFilter).Select(d => d.Diagnostic).ToImmutableArray()); - - public Task> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) - => throw new NotImplementedException(); -} From a5ecdcad16c762868b6be7a877b0f9320fb0ba89 Mon Sep 17 00:00:00 2001 From: Fred Silberberg Date: Thu, 6 Feb 2025 18:30:20 -0500 Subject: [PATCH 106/305] Rewrite string.Concat lowering code (#76955) Our existing lowering strategy for string addition is complex, to say the least. It works in a bottom-up fashion, needing to constantly undo and redo existing work as VisitExpression returns and discovered ever-larger sequences of string additions. This is not easily maintainable, and also makes addressing issues like #74538 extremely difficult. As a first step in that direction, I've rewritten how we lower these to instead approach it top-down; when a + that operates on strings is encountered, we gather all possible operands up front, fold as many constant components as we can, and then emit the final string.Concat call all in one go. This should hopefully be significantly easier to maintain in the future, as well as be a much more flexible base for building further optimizations on, such as using string.Concat(ReadOnlySpan) or DefaultInterpolatedStringHandler in the future. --- .../LocalRewriter_BinaryOperator.cs | 12 +- ...ocalRewriter_CompoundAssignmentOperator.cs | 23 +- .../LocalRewriter_StringConcat.cs | 901 +++++++++--------- .../Test/Emit/CodeGen/CodeGenDynamicTests.cs | 16 +- .../Test/Emit/CodeGen/CodeGenStringConcat.cs | 97 +- .../CodeGenSpanBasedStringConcatTests.cs | 375 ++++++-- .../Symbol/Symbols/MissingSpecialMember.cs | 2 +- .../Core/Portable/Collections/Rope.cs | 1 + 8 files changed, 844 insertions(+), 583 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs index 46d37fdd2d589..90283088cfd05 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs @@ -125,6 +125,12 @@ public BoundExpression VisitBinaryOperator(BoundBinaryOperator node, BoundUnaryO return VisitUtf8Addition(node); } + if (IsBinaryStringConcatenation(node)) + { + Debug.Assert(applyParentUnaryOperator is null); + return VisitStringConcatenation(node); + } + // In machine-generated code we frequently end up with binary operator trees that are deep on the left, // such as a + b + c + d ... // To avoid blowing the call stack, we make an explicit stack of the binary operators to the left, @@ -135,7 +141,7 @@ public BoundExpression VisitBinaryOperator(BoundBinaryOperator node, BoundUnaryO for (BoundBinaryOperator? current = node; current != null && current.ConstantValueOpt == null; current = current.Left as BoundBinaryOperator) { // The regular visit mechanism will handle this. - if (current.InterpolatedStringHandlerData is not null || current.OperatorKind is BinaryOperatorKind.Utf8Addition) + if (current.InterpolatedStringHandlerData is not null || current.OperatorKind is BinaryOperatorKind.Utf8Addition || IsBinaryStringConcatenation(current)) { Debug.Assert(stack.Count >= 1); break; @@ -208,7 +214,7 @@ private BoundExpression MakeBinaryOperator( case BinaryOperatorKind.ObjectAndStringConcatenation: case BinaryOperatorKind.StringAndObjectConcatenation: case BinaryOperatorKind.StringConcatenation: - return RewriteStringConcatenation(syntax, operatorKind, loweredLeft, loweredRight, type); + throw ExceptionUtilities.UnexpectedValue(operatorKind); case BinaryOperatorKind.DelegateCombination: return RewriteDelegateOperation(syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_Delegate__Combine); case BinaryOperatorKind.DelegateRemoval: @@ -256,7 +262,7 @@ private BoundExpression MakeBinaryOperator( case BinaryOperatorKind.ObjectAndStringConcatenation: case BinaryOperatorKind.StringAndObjectConcatenation: case BinaryOperatorKind.StringConcatenation: - return RewriteStringConcatenation(syntax, operatorKind, loweredLeft, loweredRight, type); + throw ExceptionUtilities.UnexpectedValue(operatorKind); case BinaryOperatorKind.StringEqual: return RewriteStringEquality(oldNode, syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_String__op_Equality); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs index 6b87177c37ffb..3b295eb9b6e35 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs @@ -22,7 +22,6 @@ public override BoundNode VisitCompoundAssignmentOperator(BoundCompoundAssignmen private BoundExpression VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node, bool used) { Debug.Assert(TypeSymbol.Equals(node.Right.Type, node.Operator.RightType, TypeCompareKind.ConsiderEverything2)); - BoundExpression loweredRight = VisitExpression(node.Right); var temps = ArrayBuilder.GetInstance(); var stores = ArrayBuilder.GetInstance(); @@ -55,6 +54,8 @@ private BoundExpression VisitCompoundAssignmentOperator(BoundCompoundAssignmentO // side before storing the lambda to a temp for use in both possible branches. // The first store to memberAccessReceiver has already been taken care of above by TransformCompoundAssignmentLHS + Debug.Assert(!IsBinaryStringConcatenation(binaryOperator)); + var eventTemps = ArrayBuilder.GetInstance(); var sequence = ArrayBuilder.GetInstance(); @@ -74,6 +75,7 @@ private BoundExpression VisitCompoundAssignmentOperator(BoundCompoundAssignmentO sequence.Add(nonEventStore); // var loweredRight = handler; + BoundExpression loweredRight = VisitExpression(node.Right); if (CanChangeValueBetweenReads(loweredRight)) { loweredRight = _factory.StoreToTemp(loweredRight, out BoundAssignmentOperator possibleHandlerAssignment); @@ -88,7 +90,7 @@ private BoundExpression VisitCompoundAssignmentOperator(BoundCompoundAssignmentO loweredRight); // transformedLHS = storeNonEvent + loweredRight - rewrittenAssignment = rewriteAssignment(lhsRead); + rewrittenAssignment = rewriteAssignment(lhsRead, loweredRight, rightIsVisited: true); Debug.Assert(rewrittenAssignment.Type is { }); // Final conditional @@ -98,7 +100,7 @@ private BoundExpression VisitCompoundAssignmentOperator(BoundCompoundAssignmentO } else { - rewrittenAssignment = rewriteAssignment(lhsRead); + rewrittenAssignment = rewriteAssignment(lhsRead, node.Right, rightIsVisited: false); } Debug.Assert(rewrittenAssignment.Type is { }); @@ -115,7 +117,7 @@ private BoundExpression VisitCompoundAssignmentOperator(BoundCompoundAssignmentO stores.Free(); return result; - BoundExpression rewriteAssignment(BoundExpression leftRead) + BoundExpression rewriteAssignment(BoundExpression leftRead, BoundExpression right, bool rightIsVisited) { SyntaxNode syntax = node.Syntax; @@ -139,7 +141,18 @@ BoundExpression rewriteAssignment(BoundExpression leftRead) RemovePlaceholderReplacement(node.LeftPlaceholder); } - BoundExpression operand = MakeBinaryOperator(syntax, node.Operator.Kind, opLHS, loweredRight, node.Operator.ReturnType, node.Operator.Method, node.Operator.ConstrainedToTypeOpt, isCompoundAssignment: true); + BoundExpression operand; + if (IsBinaryStringConcatenation(node.Operator.Kind)) + { + Debug.Assert(!rightIsVisited); + Debug.Assert(node.Operator.ReturnType is { SpecialType: SpecialType.System_String }); + operand = VisitCompoundAssignmentStringConcatenation(opLHS, right, node.Operator.Kind, node.Syntax); + } + else + { + var loweredRight = rightIsVisited ? right : VisitExpression(right); + operand = MakeBinaryOperator(syntax, node.Operator.Kind, opLHS, loweredRight, node.Operator.ReturnType, node.Operator.Method, node.Operator.ConstrainedToTypeOpt, isCompoundAssignment: true); + } Debug.Assert(node.Left.Type is { }); BoundExpression opFinal = operand; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringConcat.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringConcat.cs index 9b0daebe8a387..24bdbf5987572 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringConcat.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringConcat.cs @@ -14,592 +14,557 @@ namespace Microsoft.CodeAnalysis.CSharp { internal sealed partial class LocalRewriter { - /// - /// The strategy of this rewrite is to do rewrite "locally". - /// We analyze arguments of the concat in a shallow fashion assuming that - /// lowering and optimizations (including this one) is already done for the arguments. - /// Based on the arguments we select the most appropriate pattern for the current node. - /// - /// NOTE: it is not guaranteed that the node that we chose will be the most optimal since we have only - /// local information - i.e. we look at the arguments, but we do not know about siblings. - /// When we move to the parent, the node may be rewritten by this or some another optimization. - /// - /// Example: - /// result = ( "abc" + "def" + null ?? expr1 + "moo" + "baz" ) + expr2 - /// - /// Will rewrite into: - /// result = Concat("abcdef", expr2) - /// - /// However there will be transient nodes like Concat(expr1 + "moo") that will not be present in the - /// resulting tree. - /// - /// - private BoundExpression RewriteStringConcatenation(SyntaxNode syntax, BinaryOperatorKind operatorKind, BoundExpression loweredLeft, BoundExpression loweredRight, TypeSymbol type) + private static bool IsBinaryStringConcatenation([NotNullWhen(true)] BoundBinaryOperator? binaryOperator) + => binaryOperator is { OperatorKind: var kind } && IsBinaryStringConcatenation(kind); + + private static bool IsBinaryStringConcatenation(BinaryOperatorKind binaryOperator) + => binaryOperator is BinaryOperatorKind.StringConcatenation or BinaryOperatorKind.StringAndObjectConcatenation or BinaryOperatorKind.ObjectAndStringConcatenation; + + private BoundExpression VisitCompoundAssignmentStringConcatenation(BoundExpression left, BoundExpression unvisitedRight, BinaryOperatorKind operatorKind, SyntaxNode syntax) { - Debug.Assert( - operatorKind == BinaryOperatorKind.StringConcatenation || - operatorKind == BinaryOperatorKind.StringAndObjectConcatenation || - operatorKind == BinaryOperatorKind.ObjectAndStringConcatenation); + Debug.Assert(IsBinaryStringConcatenation(operatorKind)); + Debug.Assert(!_inExpressionLambda); - if (_inExpressionLambda) + ArrayBuilder arguments; + if (unvisitedRight is BoundBinaryOperator { InterpolatedStringHandlerData: null } rightBinary && IsBinaryStringConcatenation(rightBinary)) + { + CollectAndVisitConcatArguments(rightBinary, left, out arguments); + Debug.Assert(ReferenceEquals(arguments[0], left)); + } + else { - return RewriteStringConcatInExpressionLambda(syntax, operatorKind, loweredLeft, loweredRight, type); + arguments = ArrayBuilder.GetInstance(); + var concatMethods = new WellKnownConcatRelatedMethods(_compilation); + VisitAndAddConcatArgumentInReverseOrder(unvisitedRight, argumentAlreadyVisited: false, arguments, ref concatMethods); + VisitAndAddConcatArgumentInReverseOrder(left, argumentAlreadyVisited: true, arguments, ref concatMethods); + arguments.ReverseContents(); } - // Convert both sides to a string (calling ToString if necessary) - loweredLeft = ConvertConcatExprToString(loweredLeft); - loweredRight = ConvertConcatExprToString(loweredRight); + return CreateStringConcat(syntax, arguments); + } - Debug.Assert(loweredLeft.Type is { } && (loweredLeft.Type.IsStringType() || loweredLeft.Type.IsErrorType()) || loweredLeft.ConstantValueOpt?.IsNull == true); - Debug.Assert(loweredRight.Type is { } && (loweredRight.Type.IsStringType() || loweredRight.Type.IsErrorType()) || loweredRight.ConstantValueOpt?.IsNull == true); + private BoundExpression VisitStringConcatenation(BoundBinaryOperator originalOperator) + { + Debug.Assert(IsBinaryStringConcatenation(originalOperator)); - // try fold two args without flattening. - var folded = TryFoldTwoConcatOperands(loweredLeft, loweredRight); - if (folded != null) + if (_inExpressionLambda) { - return folded; + // If this is an expression tree, we can't optimize anything. Just do a standard visit and return. + return RewriteStringConcatInExpressionLambda(originalOperator); } - // flatten and merge - ( expr1 + "A" ) + ("B" + expr2) ===> (expr1 + "AB" + expr2) - ArrayBuilder leftFlattened = ArrayBuilder.GetInstance(); - ArrayBuilder rightFlattened = ArrayBuilder.GetInstance(); + // We'll walk the children in a depth-first order, pull all the arguments out, and then visit them. We'll fold any constant arguments as + // we go, pulling them all into a string literal. + CollectAndVisitConcatArguments(originalOperator, visitedCompoundAssignmentLeftRead: null, out var arguments); - FlattenConcatArg(loweredLeft, leftFlattened); - FlattenConcatArg(loweredRight, rightFlattened); + return CreateStringConcat(originalOperator.Syntax, arguments); + } - if (leftFlattened.Any() && rightFlattened.Any()) + /// + /// Produces a new string.Concat call in the most efficient manner for the given arguments. It is expected that the arguments are already visited, and the following optimizations + /// have been done: + /// + /// Any consecutive constant strings or chars have been folded. + /// Any nested string.Concat calls have had their arguments deconstructed into . + /// + /// It is not valid to call this method inside an expression tree; that should be handled by a standard recursive rewrite. + /// + private BoundExpression CreateStringConcat(SyntaxNode originalSyntax, ArrayBuilder visitedArguments) + { + Debug.Assert(!_inExpressionLambda); + Debug.Assert(visitedArguments.All(arg => arg.Type!.SpecialType is SpecialType.System_String or SpecialType.System_Char or SpecialType.System_Object)); + // There are a few different lowering patterns that we take: + // + // 1. If all the added expressions were folded into a single constant, we can just return that. + // 2. If all the added expressions are strings, then we want to use one of the `string.Concat(string)`-based overloads: if 4 or less, + // we'll use one of the hardcoded overloads. Otherwise, we'll use `string.Concat(string[])`. + // 3. If all the added expressions are strings or chars, we can use the `string.Concat(ReadOnlySpan)`-based overloads. If there are + // more than 4 arguments, or if `string.Concat(ReadOnlySpan)`-based overloads are not present, we will instead fall back to + // `string.Concat(string[])`. + // 4. If there are objects among the added expression, we'll use the `string.Concat(string)`-based overloads, and call ToString on the + // arguments to avoid boxing structs by converting them into objects. If there are more than 4, we'll use `string.Concat(string[])`. + + switch (visitedArguments) { - folded = TryFoldTwoConcatOperands(leftFlattened.Last(), rightFlattened.First()); - if (folded != null) - { - rightFlattened[0] = folded; - leftFlattened.RemoveLast(); - } + case []: + // All the arguments were null or the empty string. We can just return a constant empty string. + visitedArguments.Free(); + return _factory.StringLiteral(string.Empty); + case [{ ConstantValueOpt.IsString: true } arg]: + // We were able to fold a constant, so we can just return that constant. + visitedArguments.Free(); + return arg; + case [{ ConstantValueOpt: { IsChar: true, CharValue: var @char } } arg]: + // We were able to fold a constant, so we can just return that constant. + visitedArguments.Free(); + return _factory.StringLiteral(@char.ToString()); } - leftFlattened.AddRange(rightFlattened); - rightFlattened.Free(); - - BoundExpression? result; + var concatKind = StringConcatenationRewriteKind.AllStrings; - switch (leftFlattened.Count) + foreach (var arg in visitedArguments) { - case 0: - result = _factory.StringLiteral(string.Empty); - break; + var argumentType = arg.Type; + // Null arguments should have been eliminated before now. + Debug.Assert(argumentType is not null); + switch (argumentType.SpecialType) + { + case SpecialType.System_String: + continue; - case 1: - // All code paths which reach here (through TryFoldTwoConcatOperands) have already called - // RewriteStringConcatenationOneExpr if necessary - result = leftFlattened[0]; - break; + case SpecialType.System_Char: + // If we're concating a constant char, we can just treat it as if it's a one-character string, which is more preferable. + if (concatKind == StringConcatenationRewriteKind.AllStrings && arg.ConstantValueOpt is not { IsChar: true }) + { + concatKind = StringConcatenationRewriteKind.AllStringsOrChars; + } + continue; - case 2: - var left = leftFlattened[0]; - var right = leftFlattened[1]; + default: + concatKind = StringConcatenationRewriteKind.InvolvesObjects; + break; + } - if (!TryRewriteStringConcatenationWithSpanBasedConcat(syntax, leftFlattened, out result)) - { - result = RewriteStringConcatenationTwoExprs(syntax, left, right); - } - break; + // We explicitly continued in the string and char cases, so we're in the worst case InvolvesObject at this point and can stop looping + break; + } - case 3: + switch (concatKind, visitedArguments.Count) + { + case (_, 0): + throw ExceptionUtilities.Unreachable(); + + case (_, 1): + // Only 1 argument. We need to make sure that it's not null, but otherwise we don't need to call Concat and can just use ToString. + var arg = ConvertConcatExprToString(visitedArguments[0]); + visitedArguments.Free(); + return _factory.Coalesce(arg, _factory.StringLiteral(string.Empty)); + + case (StringConcatenationRewriteKind.AllStringsOrChars, <= 4): + // We can use one of the `string.Concat(ReadOnlySpan)`-based overloads. + var concatMember = visitedArguments.Count switch { - var first = leftFlattened[0]; - var second = leftFlattened[1]; - var third = leftFlattened[2]; + 2 => SpecialMember.System_String__Concat_2ReadOnlySpans, + 3 => SpecialMember.System_String__Concat_3ReadOnlySpans, + 4 => SpecialMember.System_String__Concat_4ReadOnlySpans, + _ => throw ExceptionUtilities.Unreachable(), + }; - if (!TryRewriteStringConcatenationWithSpanBasedConcat(syntax, leftFlattened, out result)) - { - result = RewriteStringConcatenationThreeExprs(syntax, first, second, third); - } + bool needsImplicitConversionFromStringToSpan = visitedArguments.Any(arg => arg.Type is { SpecialType: SpecialType.System_String }); + var charType = _compilation.GetSpecialType(SpecialType.System_Char); + + if (!TryGetSpecialTypeMethod(originalSyntax, concatMember, out var spanConcat, isOptional: true) + || !TryGetNeededToSpanMembers(this, originalSyntax, needsImplicitConversionFromStringToSpan, charType, out var readOnlySpanCtorRefParamChar, out var stringImplicitConversionToReadOnlySpan)) + { + goto fallbackStrings; } - break; - case 4: + return RewriteStringConcatenationWithSpanBasedConcat(originalSyntax, _factory, spanConcat, stringImplicitConversionToReadOnlySpan, readOnlySpanCtorRefParamChar, visitedArguments); + + case (StringConcatenationRewriteKind.AllStrings, _): + case (StringConcatenationRewriteKind.AllStringsOrChars, _): + case (StringConcatenationRewriteKind.InvolvesObjects, _): +fallbackStrings: + +#pragma warning disable IDE0055// Fix formatting + // All other cases can be handled here +#pragma warning restore IDE0055// Fix formatting + concatMember = visitedArguments.Count switch + { + 2 => SpecialMember.System_String__ConcatStringString, + 3 => SpecialMember.System_String__ConcatStringStringString, + 4 => SpecialMember.System_String__ConcatStringStringStringString, + >= 5 => SpecialMember.System_String__ConcatStringArray, + _ => throw ExceptionUtilities.UnexpectedValue(visitedArguments.Count), + }; + + for (int i = 0; i < visitedArguments.Count; i++) { - var first = leftFlattened[0]; - var second = leftFlattened[1]; - var third = leftFlattened[2]; - var fourth = leftFlattened[3]; + visitedArguments[i] = ConvertConcatExprToString(visitedArguments[i]); + } - if (!TryRewriteStringConcatenationWithSpanBasedConcat(syntax, leftFlattened, out result)) - { - result = RewriteStringConcatenationFourExprs(syntax, first, second, third, fourth); - } + var finalArguments = visitedArguments.ToImmutableAndFree(); + + if (finalArguments.Length > 4) + { + var array = _factory.ArrayOrEmpty(_factory.SpecialType(SpecialType.System_String), finalArguments); + finalArguments = [array]; } - break; + + var method = UnsafeGetSpecialTypeMethod(originalSyntax, concatMember); + Debug.Assert(method is not null); + return BoundCall.Synthesized(originalSyntax, receiverOpt: null, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method, finalArguments); default: - result = RewriteStringConcatenationManyExprs(syntax, leftFlattened.ToImmutable()); - break; + throw ExceptionUtilities.UnexpectedValue(concatKind); } - - leftFlattened.Free(); - return result; } /// - /// digs into known concat operators and unwraps their arguments - /// otherwise returns the expression as-is - /// - /// Generally we only need to recognize same node patterns that we create as a result of concatenation rewrite. + /// Given an unvisited string concat binary operator and potential compound assignment left-hand side read, visits all the arguments for passing to + /// and performs any optimizations on the arguments that can be done. This + /// includes coalescing consecutive constant strings or chars into a single string constant, and deconstructing nested string.Concat calls. /// - private void FlattenConcatArg(BoundExpression lowered, ArrayBuilder flattened) + private void CollectAndVisitConcatArguments(BoundBinaryOperator originalOperator, BoundExpression? visitedCompoundAssignmentLeftRead, out ArrayBuilder destinationArguments) { - if (TryExtractStringConcatArgs(lowered, out var arguments)) + Debug.Assert(!_inExpressionLambda); + destinationArguments = ArrayBuilder.GetInstance(); + var concatMethods = new WellKnownConcatRelatedMethods(_compilation); + pushArguments(this, originalOperator, destinationArguments, ref concatMethods); + if (visitedCompoundAssignmentLeftRead is not null) { - flattened.AddRange(arguments); + // We don't expect to be able to optimize anything about the compound assignment left read, so we just add it as-is. This assert should be kept in sync + // with the cases that can be optimized by the VisitAndAddConcatArgumentInReverseOrder method below; if we ever find a case that can be optimized, we may + // need to consider whether to do so. The visiting logic in the parent function here depends on only one argument being added for a compound assignment + // left read, so if we ever do introduce optimizations here that result in more than one argument being added to destinationArguments, we'll need to adjust + // that logic. + Debug.Assert(visitedCompoundAssignmentLeftRead is + not (BoundCall or BoundConversion { ConversionKind: ConversionKind.Boxing, Type.SpecialType: SpecialType.System_Object, Operand.Type.SpecialType: SpecialType.System_Char }) + and { ConstantValueOpt: null }); + destinationArguments.Add(visitedCompoundAssignmentLeftRead); } - else - { - // fallback - if nothing above worked, leave arg as-is - flattened.Add(lowered); - } - } + destinationArguments.ReverseContents(); - /// - /// Determines whether an expression is a known string concat operator (with or without a subsequent ?? ""), and extracts - /// its args if so. - /// - /// True if this is a call to a known string concat operator and its arguments are successfully extracted, false otherwise - private bool TryExtractStringConcatArgs(BoundExpression lowered, out ImmutableArray arguments) - { - switch (lowered) + // We push these in reverse order to take advantage of the left-recursive nature of the tree and avoid needing a second stack + static void pushArguments(LocalRewriter self, BoundBinaryOperator binaryOperator, ArrayBuilder arguments, ref WellKnownConcatRelatedMethods concatMethods) { - case BoundCall boundCall: - var method = boundCall.Method; - if (method.IsStatic && method.ContainingType.SpecialType == SpecialType.System_String) + while (true) + { + if (shouldRecurse(binaryOperator.Right, out var right)) { - if ((object)method == (object)_compilation.GetSpecialTypeMember(SpecialMember.System_String__ConcatStringString) || - (object)method == (object)_compilation.GetSpecialTypeMember(SpecialMember.System_String__ConcatStringStringString) || - (object)method == (object)_compilation.GetSpecialTypeMember(SpecialMember.System_String__ConcatStringStringStringString)) - { - arguments = boundCall.Arguments; - return true; - } - - if ((object)method == (object)_compilation.GetSpecialTypeMember(SpecialMember.System_String__ConcatStringArray)) - { - var args = boundCall.Arguments[0] as BoundArrayCreation; - if (args != null) - { - var initializer = args.InitializerOpt; - if (initializer != null) - { - arguments = initializer.Initializers; - return true; - } - } - } + pushArguments(self, right, arguments, ref concatMethods); } - break; - - case BoundNullCoalescingOperator boundCoalesce: - Debug.Assert(boundCoalesce.LeftPlaceholder is null); - Debug.Assert(boundCoalesce.LeftConversion is null); - - // The RHS may be a constant value with an identity conversion to string even - // if it is not a string: in particular, the null literal behaves this way. - // To be safe, check that the constant value is actually a string before - // attempting to access its value as a string. - - var rightConstant = boundCoalesce.RightOperand.ConstantValueOpt; - if (rightConstant != null && rightConstant.IsString && rightConstant.StringValue.Length == 0) + else { - arguments = ImmutableArray.Create(boundCoalesce.LeftOperand); - return true; + self.VisitAndAddConcatArgumentInReverseOrder(binaryOperator.Right, argumentAlreadyVisited: false, arguments, ref concatMethods); } - break; - - case BoundSequence { SideEffects.Length: 0, Value: BoundCall sequenceCall } sequence: - if ((object)sequenceCall.Method == _compilation.GetSpecialTypeMember(SpecialMember.System_String__Concat_2ReadOnlySpans) || - (object)sequenceCall.Method == _compilation.GetSpecialTypeMember(SpecialMember.System_String__Concat_3ReadOnlySpans) || - (object)sequenceCall.Method == _compilation.GetSpecialTypeMember(SpecialMember.System_String__Concat_4ReadOnlySpans)) + if (shouldRecurse(binaryOperator.Left, out var left)) { - // Faced a span-based `string.Concat` call. Since we can produce such call on the previous iterations ourselves, we need to unwrap it. - // The key thing is that we need not to only extract arguments, but also unwrap them from being spans and for chars also wrap them into `ToString` calls. - var wrappedArgs = sequenceCall.Arguments; - var unwrappedArgsBuilder = ArrayBuilder.GetInstance(capacity: wrappedArgs.Length); - - var locals = PooledHashSet.GetInstance(); - locals.AddAll(sequence.Locals); - - foreach (var wrappedArg in wrappedArgs) - { - switch (wrappedArg) - { - // Check whether a call is an implicit `string -> ReadOnlySpan` conversion - case BoundCall { Method: var argMethod, Arguments: [var singleArgument] } when (object)argMethod == _compilation.GetSpecialTypeMember(SpecialMember.System_String__op_Implicit_ToReadOnlySpanOfChar): - unwrappedArgsBuilder.Add(singleArgument); - break; - // This complicated check is for a sequence, which wraps a span around single char. - // The sequence needs to have this shape: `{ locals: , sideEffects: temp = , result: new ReadOnlySpan(in temp) }` - case BoundSequence - { - Locals.Length: 0, - SideEffects: [BoundAssignmentOperator { Right.Type.SpecialType: SpecialType.System_Char } assignment], - Value: BoundObjectCreationExpression { Constructor: var objectCreationConstructor, Arguments: [BoundLocal constructorLocal] } - } when constructorLocal == assignment.Left && - locals.Remove(constructorLocal.LocalSymbol) && - (object)objectCreationConstructor.OriginalDefinition == _compilation.GetSpecialTypeMember(SpecialMember.System_ReadOnlySpan_T__ctor_Reference) && - objectCreationConstructor.ContainingType.IsReadOnlySpanChar(): - var wrappedExpr = ConvertConcatExprToString(assignment.Right); - unwrappedArgsBuilder.Add(wrappedExpr); - break; - default: - locals.Free(); - unwrappedArgsBuilder.Free(); - arguments = default; - return false; - } - } - - if (locals.Count > 0) - { - // Not all locals are part of a known shape - locals.Free(); - unwrappedArgsBuilder.Free(); - arguments = default; - return false; - } + binaryOperator = left; + } + else + { + self.VisitAndAddConcatArgumentInReverseOrder(binaryOperator.Left, argumentAlreadyVisited: false, arguments, ref concatMethods); + break; + } + } - locals.Free(); - arguments = unwrappedArgsBuilder.ToImmutableAndFree(); + static bool shouldRecurse(BoundExpression expr, [NotNullWhen(true)] out BoundBinaryOperator? binaryOperator) + { + binaryOperator = expr as BoundBinaryOperator; + if (IsBinaryStringConcatenation(binaryOperator) && binaryOperator.InterpolatedStringHandlerData is null) + { return true; } - - break; + else + { + binaryOperator = null; + return false; + } + } } - - arguments = default; - return false; } /// - /// folds two concat operands into one expression if possible - /// otherwise returns null + /// Visits the given argument if necessary and adds it to the final arguments list. It is expected that is being in reverse order, due to the left-recursive + /// nature of the binary tree that we're traversing. /// - private BoundExpression? TryFoldTwoConcatOperands(BoundExpression loweredLeft, BoundExpression loweredRight) + /// + /// This method may end up deciding that the passed argument doesn't need to be included in the concat argument list (if, for example, it's a null constant or an empty string), and not add it + /// to . It will also fold consecutive constant strings or chars into a single string constant, to avoid unnecessary concatenation. It may also do other optimizations, + /// such as deconstructing nested string.Concat calls. + /// + private void VisitAndAddConcatArgumentInReverseOrder(BoundExpression argument, bool argumentAlreadyVisited, ArrayBuilder finalArguments, ref WellKnownConcatRelatedMethods wellKnownConcatOptimizationMethods) { - // both left and right are constants - var leftConst = loweredLeft.ConstantValueOpt; - var rightConst = loweredRight.ConstantValueOpt; - - if (leftConst != null && rightConst != null) + Debug.Assert(argument is not BoundBinaryOperator { InterpolatedStringHandlerData: null } op || !IsBinaryStringConcatenation(op)); + if (!argumentAlreadyVisited) { - // const concat may fail to fold if strings are huge. - // This would be unusual. - ConstantValue? concatenated = TryFoldTwoConcatConsts(leftConst, rightConst); - if (concatenated != null) - { - return _factory.StringLiteral(concatenated); - } + argument = VisitExpression(argument); } - // one or another is null. - if (IsNullOrEmptyStringConstant(loweredLeft)) + if (argument is BoundConversion { ConversionKind: ConversionKind.Boxing, Type.SpecialType: SpecialType.System_Object, Operand: { Type.SpecialType: SpecialType.System_Char } operand }) + { + argument = operand; + } + else if (argument is BoundCall call) { - if (IsNullOrEmptyStringConstant(loweredRight)) + if (wellKnownConcatOptimizationMethods.IsWellKnownConcatMethod(call, out var concatArguments)) { - return _factory.Literal(string.Empty); - } + for (int i = concatArguments.Length - 1; i >= 0; i--) + { + VisitAndAddConcatArgumentInReverseOrder(concatArguments[i], argumentAlreadyVisited: true, finalArguments, ref wellKnownConcatOptimizationMethods); + } - return RewriteStringConcatenationOneExpr(loweredRight); + return; + } + else if (wellKnownConcatOptimizationMethods.IsCharToString(call, out var charExpression)) + { + argument = charExpression; + } } - else if (IsNullOrEmptyStringConstant(loweredRight)) + // This is `strValue ?? ""`, possibly from a nested binary addition of an interpolated string. We can just directly use the left operand + else if (argument is BoundNullCoalescingOperator { LeftOperand: { Type.SpecialType: SpecialType.System_String } left, RightOperand: BoundLiteral { ConstantValueOpt: { IsString: true, RopeValue.IsEmpty: true } } }) { - return RewriteStringConcatenationOneExpr(loweredLeft); + argument = left; } - return null; - } + switch (argument.ConstantValueOpt) + { + case { IsNull: true } or { IsString: true, RopeValue.IsEmpty: true }: + // If this is a null constant or an empty string, then we don't need to include it in the final arguments list + return; - private static bool IsNullOrEmptyStringConstant(BoundExpression operand) - { - return (operand.ConstantValueOpt != null && string.IsNullOrEmpty(operand.ConstantValueOpt.StringValue)) || - operand.IsDefaultValue(); - } + case { IsString: true } or { IsChar: true }: + // See if we can merge this argument with the previous one + if (finalArguments.Count > 0 && finalArguments[^1].ConstantValueOpt is { IsString: true } or { IsChar: true }) + { + var constantValue = finalArguments[^1].ConstantValueOpt!; + var previous = getRope(constantValue); + var current = getRope(argument.ConstantValueOpt!); + // We're visiting arguments in reverse order, so we need to prepend this constant value, not append + finalArguments[^1] = _factory.StringLiteral(ConstantValue.CreateFromRope(Rope.Concat(current, previous))); + return; + } - /// - /// folds two concat constants into one if possible - /// otherwise returns null. - /// It is generally always possible to concat constants, unless resulting string would be too large. - /// - private static ConstantValue? TryFoldTwoConcatConsts(ConstantValue leftConst, ConstantValue rightConst) - { - var leftVal = leftConst.StringValue; - var rightVal = rightConst.StringValue; + break; + } + + finalArguments.Add(argument); - if (!leftConst.IsDefaultValue && !rightConst.IsDefaultValue) + static Rope getRope(ConstantValue constantValue) { - Debug.Assert(leftVal is { } && rightVal is { }); - if (leftVal.Length + rightVal.Length < 0) + Debug.Assert(constantValue.IsString || constantValue.IsChar); + if (constantValue.IsString) + { + return constantValue.RopeValue!; + } + else { - return null; + return Rope.ForString(constantValue.CharValue.ToString()); } } - - // TODO: if transient string allocations are an issue, consider introducing constants that contain builders. - // it may be not so easy to even get here though, since typical - // "A" + "B" + "C" + ... cases should be folded in the binder as spec requires so. - // we would be mostly picking here edge cases like "A" + (object)null + "B" + (object)null + ... - return ConstantValue.Create(leftVal + rightVal); } - /// - /// Strangely enough there is such a thing as unary concatenation and it must be rewritten. - /// - private BoundExpression RewriteStringConcatenationOneExpr(BoundExpression loweredOperand) + private enum StringConcatenationRewriteKind { - // If it's a call to 'string.Concat' (or is something which ends in '?? ""', which this method also extracts), - // we know the result cannot be null. Otherwise return loweredOperand ?? "" - if (TryExtractStringConcatArgs(loweredOperand, out _)) - { - return loweredOperand; - } - else - { - return _factory.Coalesce(loweredOperand, _factory.Literal("")); - } + AllStrings, + AllStringsOrChars, + InvolvesObjects, } - private BoundExpression RewriteStringConcatenationTwoExprs(SyntaxNode syntax, BoundExpression loweredLeft, BoundExpression loweredRight) + private struct WellKnownConcatRelatedMethods(CSharpCompilation compilation) { - Debug.Assert(loweredLeft.HasAnyErrors || loweredLeft.Type is { } && loweredLeft.Type.IsStringType()); - Debug.Assert(loweredRight.HasAnyErrors || loweredRight.Type is { } && loweredRight.Type.IsStringType()); + private readonly CSharpCompilation _compilation = compilation; - var method = UnsafeGetSpecialTypeMethod(syntax, SpecialMember.System_String__ConcatStringString); - Debug.Assert((object)method != null); + private MethodSymbol? _concatStringString = ErrorMethodSymbol.UnknownMethod; + private MethodSymbol? _concatStringStringString = ErrorMethodSymbol.UnknownMethod; + private MethodSymbol? _concatStringStringStringString = ErrorMethodSymbol.UnknownMethod; + private MethodSymbol? _concatStringArray = ErrorMethodSymbol.UnknownMethod; + private MethodSymbol? _objectToString = ErrorMethodSymbol.UnknownMethod; - return BoundCall.Synthesized(syntax, receiverOpt: null, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method, loweredLeft, loweredRight); - } - - private BoundExpression RewriteStringConcatenationThreeExprs(SyntaxNode syntax, BoundExpression loweredFirst, BoundExpression loweredSecond, BoundExpression loweredThird) - { - Debug.Assert(loweredFirst.HasAnyErrors || loweredFirst.Type is { } && loweredFirst.Type.IsStringType()); - Debug.Assert(loweredSecond.HasAnyErrors || loweredSecond.Type is { } && loweredSecond.Type.IsStringType()); - Debug.Assert(loweredThird.HasAnyErrors || loweredThird.Type is { } && loweredThird.Type.IsStringType()); + public bool IsWellKnownConcatMethod(BoundCall call, out ImmutableArray arguments) + { + if (!call.ArgsToParamsOpt.IsDefault) + { + // If the arguments were explicitly ordered, we don't want to try doing any optimizations, so just assume that + // it's not a well-known concat method. + arguments = default; + return false; + } - var method = UnsafeGetSpecialTypeMethod(syntax, SpecialMember.System_String__ConcatStringStringString); - Debug.Assert((object)method != null); + if (IsConcatNonArray(call, ref _concatStringString, SpecialMember.System_String__ConcatStringString, out arguments) + || IsConcatNonArray(call, ref _concatStringStringString, SpecialMember.System_String__ConcatStringStringString, out arguments) + || IsConcatNonArray(call, ref _concatStringStringStringString, SpecialMember.System_String__ConcatStringStringStringString, out arguments)) + { + return true; + } - return BoundCall.Synthesized(syntax, receiverOpt: null, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method, ImmutableArray.Create(loweredFirst, loweredSecond, loweredThird)); - } + InitializeField(ref _concatStringArray, SpecialMember.System_String__ConcatStringArray); + if ((object)call.Method == _concatStringArray && call.Arguments[0] is BoundArrayCreation array) + { + arguments = array.InitializerOpt?.Initializers ?? []; + return true; + } - private BoundExpression RewriteStringConcatenationFourExprs(SyntaxNode syntax, BoundExpression loweredFirst, BoundExpression loweredSecond, BoundExpression loweredThird, BoundExpression loweredFourth) - { - Debug.Assert(loweredFirst.HasAnyErrors || loweredFirst.Type is { } && loweredFirst.Type.IsStringType()); - Debug.Assert(loweredSecond.HasAnyErrors || loweredSecond.Type is { } && loweredSecond.Type.IsStringType()); - Debug.Assert(loweredThird.HasAnyErrors || loweredThird.Type is { } && loweredThird.Type.IsStringType()); - Debug.Assert(loweredFourth.HasAnyErrors || loweredFourth.Type is { } && loweredFourth.Type.IsStringType()); + arguments = default; + return false; + } - var method = UnsafeGetSpecialTypeMethod(syntax, SpecialMember.System_String__ConcatStringStringStringString); - Debug.Assert((object)method != null); + public bool IsCharToString(BoundCall call, [NotNullWhen(true)] out BoundExpression? charExpression) + { + InitializeField(ref _objectToString, SpecialMember.System_Object__ToString); + if (call is { Arguments: [], ReceiverOpt.Type: NamedTypeSymbol { SpecialType: SpecialType.System_Char } charType, Method: { Name: "ToString" } method } + && (object)method.GetLeastOverriddenMethod(charType) == _objectToString) + { + charExpression = call.ReceiverOpt; + return true; + } - return BoundCall.Synthesized(syntax, receiverOpt: null, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method, ImmutableArray.Create(loweredFirst, loweredSecond, loweredThird, loweredFourth)); - } + charExpression = null; + return false; + } - private BoundExpression RewriteStringConcatenationManyExprs(SyntaxNode syntax, ImmutableArray loweredArgs) - { - Debug.Assert(loweredArgs.Length > 4); - Debug.Assert(loweredArgs.All(a => a.HasErrors || a.Type is { } && a.Type.IsStringType())); + private readonly void InitializeField(ref MethodSymbol? member, SpecialMember specialMember) + { + if ((object?)member == ErrorMethodSymbol.UnknownMethod) + { + member = _compilation.GetSpecialTypeMember(specialMember) as MethodSymbol; + } + } - var method = UnsafeGetSpecialTypeMethod(syntax, SpecialMember.System_String__ConcatStringArray); - Debug.Assert((object)method != null); + private readonly bool IsConcatNonArray(BoundCall call, ref MethodSymbol? concatMethod, SpecialMember concatSpecialMember, out ImmutableArray arguments) + { + InitializeField(ref concatMethod, concatSpecialMember); - var array = _factory.ArrayOrEmpty(_factory.SpecialType(SpecialType.System_String), loweredArgs); + if ((object)call.Method == concatMethod) + { + arguments = call.Arguments; + return true; + } - return BoundCall.Synthesized(syntax, receiverOpt: null, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method, array); + arguments = default; + return false; + } } - private bool TryRewriteStringConcatenationWithSpanBasedConcat(SyntaxNode syntax, ArrayBuilder args, [NotNullWhen(true)] out BoundExpression? result) + private static bool TryGetNeededToSpanMembers( + LocalRewriter self, + SyntaxNode syntax, + bool needsImplicitConversionFromStringToSpan, + NamedTypeSymbol charType, + [NotNullWhen(true)] out MethodSymbol? readOnlySpanCtorRefParamChar, + out MethodSymbol? stringImplicitConversionToReadOnlySpan) { - // We should have called this only for 2, 3 or 4 arguments - Debug.Assert(args.Count is >= 2 and <= 4); + readOnlySpanCtorRefParamChar = null; + stringImplicitConversionToReadOnlySpan = null; - var preparedArgs = ArrayBuilder.GetInstance(capacity: args.Count); - - var needsSpanRefParamConstructor = false; - var needsImplicitConversionFromStringToSpan = false; - - NamedTypeSymbol? charType = null; - - foreach (var arg in args) + if (self.TryGetSpecialTypeMethod(syntax, SpecialMember.System_ReadOnlySpan_T__ctor_Reference, out MethodSymbol? readOnlySpanCtorRefParamGeneric, isOptional: true) && + readOnlySpanCtorRefParamGeneric.Parameters[0].RefKind != RefKind.Out) { - Debug.Assert(arg.HasAnyErrors || arg.Type?.IsStringType() == true); - - if (arg is BoundCall { ReceiverOpt: { Type: NamedTypeSymbol { SpecialType: SpecialType.System_Char } receiverCharType } receiver } potentialToStringCall && - (object)potentialToStringCall.Method.GetLeastOverriddenMethod(charType) == _compilation.GetSpecialTypeMember(SpecialMember.System_Object__ToString)) - { - needsSpanRefParamConstructor = true; - charType = receiverCharType; - preparedArgs.Add(receiver); - continue; - } - else if (arg.ConstantValueOpt is { IsString: true, StringValue: [char c] }) - { - preparedArgs.Add( - new BoundLiteral( - arg.Syntax, constantValueOpt: ConstantValue.Create(c), - charType ?? _compilation.GetSpecialType(SpecialType.System_Char))); // We will pull 'charType' from BoundCall, if it is bad an error has been already reported elsewhere - continue; - } - - preparedArgs.Add(arg); - needsImplicitConversionFromStringToSpan = true; + var readOnlySpanOfChar = readOnlySpanCtorRefParamGeneric.ContainingType.Construct(charType); + readOnlySpanCtorRefParamChar = readOnlySpanCtorRefParamGeneric.AsMember(readOnlySpanOfChar); } - - // It only makes sense to lower using span-based concat if at least one operand is a char. - // Because otherwise we will just wrap every string operand into span conversion and use span-based concat - // which is unnecessary IL bloat. Thus we require `needsSpanRefParamConstructor` to be true - if (!needsSpanRefParamConstructor) + else { - preparedArgs.Free(); - result = null; return false; } - // Just direct consequence of a condition above since we capture `char` type and set `needsSpanRefParamConstructor` at the same time - Debug.Assert(charType is not null); - - var concatMember = preparedArgs.Count switch - { - 2 => SpecialMember.System_String__Concat_2ReadOnlySpans, - 3 => SpecialMember.System_String__Concat_3ReadOnlySpans, - 4 => SpecialMember.System_String__Concat_4ReadOnlySpans, - _ => throw ExceptionUtilities.Unreachable(), - }; - - if (TryGetSpecialTypeMethod(syntax, concatMember, out MethodSymbol? spanConcat, isOptional: true) && - tryGetNeededToSpanMembers(this, syntax, needsImplicitConversionFromStringToSpan, charType, out MethodSymbol? readOnlySpanCtorRefParamChar, out MethodSymbol? stringImplicitConversionToReadOnlySpan)) + if (needsImplicitConversionFromStringToSpan) { - result = rewriteStringConcatenationWithSpanBasedConcat( - syntax, - _factory, - spanConcat, - stringImplicitConversionToReadOnlySpan, - readOnlySpanCtorRefParamChar, - preparedArgs.ToImmutableAndFree()); - - return true; + return self.TryGetSpecialTypeMethod(syntax, SpecialMember.System_String__op_Implicit_ToReadOnlySpanOfChar, out stringImplicitConversionToReadOnlySpan, isOptional: true); } - preparedArgs.Free(); - result = null; - return false; + return true; + } + + private static BoundExpression RewriteStringConcatenationWithSpanBasedConcat( + SyntaxNode syntax, + SyntheticBoundNodeFactory factory, + MethodSymbol spanConcat, + MethodSymbol? stringImplicitConversionToReadOnlySpan, + MethodSymbol readOnlySpanCtorRefParamChar, + ArrayBuilder args) + { + var localsBuilder = ArrayBuilder.GetInstance(); - static bool tryGetNeededToSpanMembers( - LocalRewriter self, - SyntaxNode syntax, - bool needsImplicitConversionFromStringToSpan, - NamedTypeSymbol charType, - [NotNullWhen(true)] out MethodSymbol? readOnlySpanCtorRefParamChar, - out MethodSymbol? stringImplicitConversionToReadOnlySpan) + for (int i = 0; i < args.Count; i++) { - readOnlySpanCtorRefParamChar = null; - stringImplicitConversionToReadOnlySpan = null; + BoundExpression? arg = args[i]; + Debug.Assert(arg.Type is not null); - if (self.TryGetSpecialTypeMethod(syntax, SpecialMember.System_ReadOnlySpan_T__ctor_Reference, out MethodSymbol? readOnlySpanCtorRefParamGeneric, isOptional: true) && - readOnlySpanCtorRefParamGeneric.Parameters[0].RefKind != RefKind.Out) + if (arg.Type.SpecialType == SpecialType.System_Char) { - var readOnlySpanOfChar = readOnlySpanCtorRefParamGeneric.ContainingType.Construct(charType); - readOnlySpanCtorRefParamChar = readOnlySpanCtorRefParamGeneric.AsMember(readOnlySpanOfChar); + var temp = factory.StoreToTemp(arg, out var tempAssignment); + localsBuilder.Add(temp.LocalSymbol); + + Debug.Assert(readOnlySpanCtorRefParamChar.Parameters[0].RefKind != RefKind.Out); + + var wrappedChar = new BoundObjectCreationExpression( + arg.Syntax, + readOnlySpanCtorRefParamChar, + [temp], + argumentNamesOpt: default, + argumentRefKindsOpt: [readOnlySpanCtorRefParamChar.Parameters[0].RefKind == RefKind.Ref ? RefKind.Ref : RefKindExtensions.StrictIn], + expanded: false, + argsToParamsOpt: default, + defaultArguments: default, + constantValueOpt: null, + initializerExpressionOpt: null, + type: readOnlySpanCtorRefParamChar.ContainingType); + + args[i] = new BoundSequence( + arg.Syntax, + [], + [tempAssignment], + wrappedChar, + wrappedChar.Type); } else { - return false; - } - - if (needsImplicitConversionFromStringToSpan) - { - return self.TryGetSpecialTypeMethod(syntax, SpecialMember.System_String__op_Implicit_ToReadOnlySpanOfChar, out stringImplicitConversionToReadOnlySpan, isOptional: true); + Debug.Assert(arg.HasAnyErrors || arg.Type.SpecialType == SpecialType.System_String); + Debug.Assert(stringImplicitConversionToReadOnlySpan is not null); + args[i] = BoundCall.Synthesized(arg.Syntax, receiverOpt: null, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, stringImplicitConversionToReadOnlySpan, arg); } - - return true; } - static BoundExpression rewriteStringConcatenationWithSpanBasedConcat( - SyntaxNode syntax, - SyntheticBoundNodeFactory factory, - MethodSymbol spanConcat, - MethodSymbol? stringImplicitConversionToReadOnlySpan, - MethodSymbol readOnlySpanCtorRefParamChar, - ImmutableArray args) - { - var preparedArgsBuilder = ArrayBuilder.GetInstance(capacity: args.Length); - var localsBuilder = ArrayBuilder.GetInstance(); + var concatCall = BoundCall.Synthesized(syntax, receiverOpt: null, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, spanConcat, args.ToImmutableAndFree()); - foreach (var arg in args) - { - Debug.Assert(arg.Type is not null); + var oldSyntax = factory.Syntax; + factory.Syntax = syntax; - if (arg.Type.SpecialType == SpecialType.System_Char) - { - var temp = factory.StoreToTemp(arg, out var tempAssignment); - localsBuilder.Add(temp.LocalSymbol); - - Debug.Assert(readOnlySpanCtorRefParamChar.Parameters[0].RefKind != RefKind.Out); - - var wrappedChar = new BoundObjectCreationExpression( - arg.Syntax, - readOnlySpanCtorRefParamChar, - [temp], - argumentNamesOpt: default, - argumentRefKindsOpt: [readOnlySpanCtorRefParamChar.Parameters[0].RefKind == RefKind.Ref ? RefKind.Ref : RefKindExtensions.StrictIn], - expanded: false, - argsToParamsOpt: default, - defaultArguments: default, - constantValueOpt: null, - initializerExpressionOpt: null, - type: readOnlySpanCtorRefParamChar.ContainingType); - - preparedArgsBuilder.Add(new BoundSequence( - arg.Syntax, - [], - [tempAssignment], - wrappedChar, - wrappedChar.Type)); - } - else - { - Debug.Assert(arg.HasAnyErrors || arg.Type.SpecialType == SpecialType.System_String); - Debug.Assert(stringImplicitConversionToReadOnlySpan is not null); - preparedArgsBuilder.Add(BoundCall.Synthesized(arg.Syntax, receiverOpt: null, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, stringImplicitConversionToReadOnlySpan, arg)); - } - } - - var concatCall = BoundCall.Synthesized(syntax, receiverOpt: null, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, spanConcat, preparedArgsBuilder.ToImmutableAndFree()); + var sequence = factory.Sequence( + localsBuilder.ToImmutableAndFree(), + [], + concatCall); - var oldSyntax = factory.Syntax; - factory.Syntax = syntax; - - var sequence = factory.Sequence( - localsBuilder.ToImmutableAndFree(), - [], - concatCall); - - factory.Syntax = oldSyntax; - return sequence; - } + factory.Syntax = oldSyntax; + return sequence; } /// /// Most of the above optimizations are not applicable in expression trees as the operator /// must stay a binary operator. We cannot do much beyond constant folding which is done in binder. /// - private BoundExpression RewriteStringConcatInExpressionLambda(SyntaxNode syntax, BinaryOperatorKind operatorKind, BoundExpression loweredLeft, BoundExpression loweredRight, TypeSymbol type) + private BoundExpression RewriteStringConcatInExpressionLambda(BoundBinaryOperator original) { - SpecialMember member = (operatorKind == BinaryOperatorKind.StringConcatenation) ? - SpecialMember.System_String__ConcatStringString : - SpecialMember.System_String__ConcatObjectObject; + BoundBinaryOperator? current = original; + Debug.Assert(IsBinaryStringConcatenation(current)); + var stack = ArrayBuilder.GetInstance(); + + while (true) + { + stack.Push(current); + + if (current.Left is BoundBinaryOperator left && IsBinaryStringConcatenation(left)) + { + current = left; + } + else + { + break; + } + } + + Debug.Assert(stack.Count > 0); + BoundExpression currentResult = VisitExpression(stack.Peek().Left); + + while (stack.TryPop(out current)) + { + var right = VisitExpression(current.Right); - var method = UnsafeGetSpecialTypeMethod(syntax, member); - Debug.Assert((object)method != null); + SpecialMember member = (current.OperatorKind == BinaryOperatorKind.StringConcatenation) ? + SpecialMember.System_String__ConcatStringString : + SpecialMember.System_String__ConcatObjectObject; - return new BoundBinaryOperator(syntax, operatorKind, constantValueOpt: null, method, constrainedToTypeOpt: null, default(LookupResultKind), loweredLeft, loweredRight, type); + var method = UnsafeGetSpecialTypeMethod(current.Syntax, member); + Debug.Assert(method is not null); + + currentResult = new BoundBinaryOperator(current.Syntax, current.OperatorKind, constantValueOpt: null, method, constrainedToTypeOpt: null, default(LookupResultKind), currentResult, right, current.Type); + } + + stack.Free(); + return currentResult; } /// @@ -621,18 +586,9 @@ private BoundExpression ConvertConcatExprToString(BoundExpression expr) } } - // If expression is of form `constantChar.ToString()` then unwrap it from a `ToString()` call so we can lower it to a constant later - // NOTE: We get `object.ToString()` from a compilation because we just need to compare symbols and don't need all error recovery of `TryGetSpecialTypeMethod` - if (expr is BoundCall { Type.SpecialType: SpecialType.System_String, Method: { Name: "ToString" } method, ReceiverOpt: { Type: NamedTypeSymbol { SpecialType: SpecialType.System_Char } charType, ConstantValueOpt.IsChar: true } } call && - method.GetLeastOverriddenMember(charType) == _compilation.GetSpecialTypeMember(SpecialMember.System_Object__ToString)) - { - expr = call.ReceiverOpt; - } - // Is the expression a constant char? If so, we can // simply make it a literal string instead and avoid any // allocations for converting the char to a string at run time. - // Similarly if it's a literal null, don't do anything special. if (expr is { ConstantValueOpt: { } cv }) { if (cv.SpecialType == SpecialType.System_Char) @@ -641,7 +597,8 @@ private BoundExpression ConvertConcatExprToString(BoundExpression expr) } else if (cv.IsNull) { - return expr; + // Should have been dropped by now. + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDynamicTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDynamicTests.cs index 2331a9abfa16a..a044cf3259317 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDynamicTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDynamicTests.cs @@ -1370,7 +1370,7 @@ .locals init (C.<>c__DisplayClass11_0 V_0, //CS$<>8__locals0 IL_02b5: ldloc.0 IL_02b6: ldfld ""dynamic C.<>c__DisplayClass11_0.dyn"" IL_02bb: stloc.2 - IL_02bc: ldsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__11.<>p__12"" + IL_02bc: ldsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__11.<>p__9"" IL_02c1: brtrue.s IL_02e2 IL_02c3: ldc.i4.0 IL_02c4: ldstr ""event"" @@ -1378,16 +1378,16 @@ .locals init (C.<>c__DisplayClass11_0 V_0, //CS$<>8__locals0 IL_02ce: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_02d3: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.IsEvent(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, System.Type)"" IL_02d8: call ""System.Runtime.CompilerServices.CallSite> System.Runtime.CompilerServices.CallSite>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_02dd: stsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__11.<>p__12"" - IL_02e2: ldsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__11.<>p__12"" + IL_02dd: stsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__11.<>p__9"" + IL_02e2: ldsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__11.<>p__9"" IL_02e7: ldfld ""System.Func System.Runtime.CompilerServices.CallSite>.Target"" - IL_02ec: ldsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__11.<>p__12"" + IL_02ec: ldsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__11.<>p__9"" IL_02f1: ldloc.2 IL_02f2: callvirt ""bool System.Func.Invoke(System.Runtime.CompilerServices.CallSite, object)"" IL_02f7: stloc.3 IL_02f8: ldloc.3 IL_02f9: brtrue.s IL_0348 - IL_02fb: ldsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__11.<>p__11"" + IL_02fb: ldsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__11.<>p__8"" IL_0300: brtrue.s IL_0331 IL_0302: ldc.i4.0 IL_0303: ldstr ""event"" @@ -1403,10 +1403,10 @@ .locals init (C.<>c__DisplayClass11_0 V_0, //CS$<>8__locals0 IL_0321: stelem.ref IL_0322: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.GetMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, System.Type, System.Collections.Generic.IEnumerable)"" IL_0327: call ""System.Runtime.CompilerServices.CallSite> System.Runtime.CompilerServices.CallSite>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_032c: stsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__11.<>p__11"" - IL_0331: ldsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__11.<>p__11"" + IL_032c: stsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__11.<>p__8"" + IL_0331: ldsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__11.<>p__8"" IL_0336: ldfld ""System.Func System.Runtime.CompilerServices.CallSite>.Target"" - IL_033b: ldsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__11.<>p__11"" + IL_033b: ldsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__11.<>p__8"" IL_0340: ldloc.2 IL_0341: callvirt ""object System.Func.Invoke(System.Runtime.CompilerServices.CallSite, object)"" IL_0346: stloc.s V_4 diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenStringConcat.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenStringConcat.cs index 7716182596f00..4624f248f0d10 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenStringConcat.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenStringConcat.cs @@ -361,7 +361,7 @@ .maxstack 5 "); } - [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.TestExecutionNeedsDesktopTypes)] + [Fact] [WorkItem(37830, "https://github.com/dotnet/roslyn/issues/37830")] public void ConcatMerge_MarshalByRefObject() { @@ -405,7 +405,7 @@ static void Main() } } "; - var comp = CompileAndVerify(source, expectedOutput: @"test_field: 2"); + var comp = CompileAndVerify(source, targetFramework: TargetFramework.NetFramework, expectedOutput: ExecutionConditionUtil.IsWindowsDesktop ? @"test_field: 2" : null); comp.VerifyDiagnostics(); // Note: we use ldfld on the field, but not ldflda, because the type is MarshalByRefObject comp.VerifyIL("Test.Main", @" @@ -2104,5 +2104,98 @@ .locals init (T2 V_0, IL_0084: ret }"); } + + [Fact] + public void ConcatNullConditionalAccesses() + { + var source = """ + C c = null; + + System.Console.WriteLine(string.Concat(c?.Prop, "a") + "b"); + + class C + { + public string Prop { get; } + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "ab"); + verifier.VerifyIL("", """ + { + // Code size 29 (0x1d) + .maxstack 2 + IL_0000: ldnull + IL_0001: dup + IL_0002: brtrue.s IL_0008 + IL_0004: pop + IL_0005: ldnull + IL_0006: br.s IL_000d + IL_0008: call "string C.Prop.get" + IL_000d: ldstr "ab" + IL_0012: call "string string.Concat(string, string)" + IL_0017: call "void System.Console.WriteLine(string)" + IL_001c: ret + } + """); + } + + [Fact] + public void CompoundAdditionDirectConcatOptimization() + { + var source = """ + string s1 = "a"; + string s2 = "b"; + string s3 = "c"; + string s4 = "d"; + + s1 += $"{s2}{s3}{s4}"; + + System.Console.WriteLine(s1); + """; + + var verifier = CompileAndVerify(source, expectedOutput: "abcd"); + verifier.VerifyIL("", """ + { + // Code size 37 (0x25) + .maxstack 4 + .locals init (string V_0, //s2 + string V_1, //s3 + string V_2) //s4 + IL_0000: ldstr "a" + IL_0005: ldstr "b" + IL_000a: stloc.0 + IL_000b: ldstr "c" + IL_0010: stloc.1 + IL_0011: ldstr "d" + IL_0016: stloc.2 + IL_0017: ldloc.0 + IL_0018: ldloc.1 + IL_0019: ldloc.2 + IL_001a: call "string string.Concat(string, string, string, string)" + IL_001f: call "void System.Console.WriteLine(string)" + IL_0024: ret + } + """); + } + + [Fact] + public void ConstantCharPlusNull() + { + var source = """ + const char c = 'a'; + System.Console.WriteLine(c + (string)null); + """; + + var verifier = CompileAndVerify(source, expectedOutput: "a"); + verifier.VerifyIL("", """ + { + // Code size 11 (0xb) + .maxstack 1 + IL_0000: ldstr "a" + IL_0005: call "void System.Console.WriteLine(string)" + IL_000a: ret + } + """); + } } } diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenSpanBasedStringConcatTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenSpanBasedStringConcatTests.cs index f0d02647d79a9..3d023b0cff388 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenSpanBasedStringConcatTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenSpanBasedStringConcatTests.cs @@ -586,15 +586,40 @@ static void Main() var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net80); comp.MakeMemberMissing(SpecialMember.System_Object__ToString); - // Although we don't use object.ToString() or char.ToString() in the final codegen we still need object.ToString() during lowering. - // Moreover, we previously reported these errors anyway, so this is not a behavioral change - comp.VerifyEmitDiagnostics( - // (13,47): error CS0656: Missing compiler required member 'System.Object.ToString' - // static string M1(string s, char c) => s + c; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "c").WithArguments("System.Object", "ToString").WithLocation(13, 47), - // (14,43): error CS0656: Missing compiler required member 'System.Object.ToString' - // static string M2(string s, char c) => c + s; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "c").WithArguments("System.Object", "ToString").WithLocation(14, 43)); + // We don't use object.ToString() or char.ToString() in the final codegen. + var verifier = CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsCoreClr ? "sccs" : null, verify: Verification.FailsPEVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Test.M1", """ + { + // Code size 21 (0x15) + .maxstack 2 + .locals init (char V_0) + IL_0000: ldarg.0 + IL_0001: call "System.ReadOnlySpan string.op_Implicit(string)" + IL_0006: ldarg.1 + IL_0007: stloc.0 + IL_0008: ldloca.s V_0 + IL_000a: newobj "System.ReadOnlySpan..ctor(ref readonly char)" + IL_000f: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan)" + IL_0014: ret + } + """); + + verifier.VerifyIL("Test.M2", """ + { + // Code size 21 (0x15) + .maxstack 2 + .locals init (char V_0) + IL_0000: ldarg.1 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly char)" + IL_0009: ldarg.0 + IL_000a: call "System.ReadOnlySpan string.op_Implicit(string)" + IL_000f: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan)" + IL_0014: ret + } + """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66827")] @@ -1479,22 +1504,19 @@ public void M() // Code size 50 (0x32) .maxstack 3 .locals init (char V_0, - char V_1, - char V_2) - IL_0000: ldc.i4.s 97 - IL_0002: stloc.0 - IL_0003: ldloca.s V_0 - IL_0005: newobj "System.ReadOnlySpan..ctor(ref readonly char)" + char V_1) + IL_0000: ldstr "a" + IL_0005: call "System.ReadOnlySpan string.op_Implicit(string)" IL_000a: ldarg.0 IL_000b: ldfld "char C.c" - IL_0010: stloc.1 - IL_0011: ldloca.s V_1 + IL_0010: stloc.0 + IL_0011: ldloca.s V_0 IL_0013: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0018: ldarg.0 IL_0019: call "ref char C.GetC()" IL_001e: ldind.u2 - IL_001f: stloc.2 - IL_0020: ldloca.s V_2 + IL_001f: stloc.1 + IL_0020: ldloca.s V_1 IL_0022: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0027: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_002c: call "void System.Console.Write(string)" @@ -1546,23 +1568,20 @@ private char SneakyLocalChange(ref char local) .maxstack 4 .locals init (char V_0, //c char V_1, - char V_2, - char V_3) + char V_2) IL_0000: ldc.i4.s 97 IL_0002: stloc.0 - IL_0003: ldc.i4.s 97 - IL_0005: stloc.1 - IL_0006: ldloca.s V_1 - IL_0008: newobj "System.ReadOnlySpan..ctor(ref readonly char)" + IL_0003: ldstr "a" + IL_0008: call "System.ReadOnlySpan string.op_Implicit(string)" IL_000d: ldloc.0 - IL_000e: stloc.2 - IL_000f: ldloca.s V_2 + IL_000e: stloc.1 + IL_000f: ldloca.s V_1 IL_0011: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0016: ldarg.0 IL_0017: ldloca.s V_0 IL_0019: call "char C.SneakyLocalChange(ref char)" - IL_001e: stloc.3 - IL_001f: ldloca.s V_3 + IL_001e: stloc.2 + IL_001f: ldloca.s V_2 IL_0021: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0026: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_002b: ret @@ -2117,24 +2136,80 @@ static void Main() var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net80); comp.MakeMemberMissing(SpecialMember.System_Object__ToString); - // Although we don't use object.ToString() or char.ToString() in the final codegen we still need object.ToString() during lowering. - // Moreover, we previously reported these errors anyway, so this is not a behavioral change - comp.VerifyEmitDiagnostics( - // (15,43): error CS0656: Missing compiler required member 'System.Object.ToString' - // static string M1(string s, char c) => c + s + s; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "c").WithArguments("System.Object", "ToString").WithLocation(15, 43), - // (16,47): error CS0656: Missing compiler required member 'System.Object.ToString' - // static string M2(string s, char c) => s + c + s; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "c").WithArguments("System.Object", "ToString").WithLocation(16, 47), - // (17,51): error CS0656: Missing compiler required member 'System.Object.ToString' - // static string M3(string s, char c) => s + s + c; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "c").WithArguments("System.Object", "ToString").WithLocation(17, 51), - // (18,43): error CS0656: Missing compiler required member 'System.Object.ToString' - // static string M4(string s, char c) => c + s + c; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "c").WithArguments("System.Object", "ToString").WithLocation(18, 43), - // (18,51): error CS0656: Missing compiler required member 'System.Object.ToString' - // static string M4(string s, char c) => c + s + c; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "c").WithArguments("System.Object", "ToString").WithLocation(18, 51)); + var verifier = CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsCoreClr ? "cssscsssccsc" : null, verify: Verification.FailsPEVerify); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("Test.M1", """ + { + // Code size 27 (0x1b) + .maxstack 3 + .locals init (char V_0) + IL_0000: ldarg.1 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly char)" + IL_0009: ldarg.0 + IL_000a: call "System.ReadOnlySpan string.op_Implicit(string)" + IL_000f: ldarg.0 + IL_0010: call "System.ReadOnlySpan string.op_Implicit(string)" + IL_0015: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" + IL_001a: ret + } + """); + verifier.VerifyIL("Test.M2", """ + { + // Code size 27 (0x1b) + .maxstack 3 + .locals init (char V_0) + IL_0000: ldarg.0 + IL_0001: call "System.ReadOnlySpan string.op_Implicit(string)" + IL_0006: ldarg.1 + IL_0007: stloc.0 + IL_0008: ldloca.s V_0 + IL_000a: newobj "System.ReadOnlySpan..ctor(ref readonly char)" + IL_000f: ldarg.0 + IL_0010: call "System.ReadOnlySpan string.op_Implicit(string)" + IL_0015: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" + IL_001a: ret + } + """); + verifier.VerifyIL("Test.M3", """ + { + // Code size 27 (0x1b) + .maxstack 3 + .locals init (char V_0) + IL_0000: ldarg.0 + IL_0001: call "System.ReadOnlySpan string.op_Implicit(string)" + IL_0006: ldarg.0 + IL_0007: call "System.ReadOnlySpan string.op_Implicit(string)" + IL_000c: ldarg.1 + IL_000d: stloc.0 + IL_000e: ldloca.s V_0 + IL_0010: newobj "System.ReadOnlySpan..ctor(ref readonly char)" + IL_0015: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" + IL_001a: ret + } + """); + verifier.VerifyIL("Test.M4", """ + { + // Code size 30 (0x1e) + .maxstack 3 + .locals init (char V_0, + char V_1) + IL_0000: ldarg.1 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly char)" + IL_0009: ldarg.0 + IL_000a: call "System.ReadOnlySpan string.op_Implicit(string)" + IL_000f: ldarg.1 + IL_0010: stloc.1 + IL_0011: ldloca.s V_1 + IL_0013: newobj "System.ReadOnlySpan..ctor(ref readonly char)" + IL_0018: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" + IL_001d: ret + } + """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66827")] @@ -3369,28 +3444,25 @@ public void M() .maxstack 4 .locals init (char V_0, char V_1, - char V_2, - char V_3) - IL_0000: ldc.i4.s 97 - IL_0002: stloc.0 - IL_0003: ldloca.s V_0 - IL_0005: newobj "System.ReadOnlySpan..ctor(ref readonly char)" + char V_2) + IL_0000: ldstr "a" + IL_0005: call "System.ReadOnlySpan string.op_Implicit(string)" IL_000a: ldarg.0 IL_000b: ldfld "char C.c" - IL_0010: stloc.1 - IL_0011: ldloca.s V_1 + IL_0010: stloc.0 + IL_0011: ldloca.s V_0 IL_0013: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0018: ldarg.0 IL_0019: call "ref char C.GetC()" IL_001e: ldind.u2 - IL_001f: stloc.2 - IL_0020: ldloca.s V_2 + IL_001f: stloc.1 + IL_0020: ldloca.s V_1 IL_0022: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0027: ldarg.0 IL_0028: call "ref char C.GetC2()" IL_002d: ldind.u2 - IL_002e: stloc.3 - IL_002f: ldloca.s V_3 + IL_002e: stloc.2 + IL_002f: ldloca.s V_2 IL_0031: newobj "System.ReadOnlySpan..ctor(ref readonly char)" IL_0036: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" IL_003b: call "void System.Console.Write(string)" @@ -4754,39 +4826,158 @@ static void Main() var comp = CreateCompilation(source, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net80); comp.MakeMemberMissing(SpecialMember.System_Object__ToString); - // Although we don't use object.ToString() or char.ToString() in the final codegen we still need object.ToString() during lowering. - // Moreover, we previously reported these errors anyway, so this is not a behavioral change - comp.VerifyEmitDiagnostics( - // (18,43): error CS0656: Missing compiler required member 'System.Object.ToString' - // static string M1(string s, char c) => c + s + s + s; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "c").WithArguments("System.Object", "ToString").WithLocation(18, 43), - // (19,47): error CS0656: Missing compiler required member 'System.Object.ToString' - // static string M2(string s, char c) => s + c + s + s; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "c").WithArguments("System.Object", "ToString").WithLocation(19, 47), - // (20,51): error CS0656: Missing compiler required member 'System.Object.ToString' - // static string M3(string s, char c) => s + s + c + s; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "c").WithArguments("System.Object", "ToString").WithLocation(20, 51), - // (21,55): error CS0656: Missing compiler required member 'System.Object.ToString' - // static string M4(string s, char c) => s + s + s + c; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "c").WithArguments("System.Object", "ToString").WithLocation(21, 55), - // (22,43): error CS0656: Missing compiler required member 'System.Object.ToString' - // static string M5(string s, char c) => c + s + c + s; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "c").WithArguments("System.Object", "ToString").WithLocation(22, 43), - // (22,51): error CS0656: Missing compiler required member 'System.Object.ToString' - // static string M5(string s, char c) => c + s + c + s; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "c").WithArguments("System.Object", "ToString").WithLocation(22, 51), - // (23,47): error CS0656: Missing compiler required member 'System.Object.ToString' - // static string M6(string s, char c) => s + c + s + c; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "c").WithArguments("System.Object", "ToString").WithLocation(23, 47), - // (23,55): error CS0656: Missing compiler required member 'System.Object.ToString' - // static string M6(string s, char c) => s + c + s + c; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "c").WithArguments("System.Object", "ToString").WithLocation(23, 55), - // (24,43): error CS0656: Missing compiler required member 'System.Object.ToString' - // static string M7(string s, char c) => c + s + s + c; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "c").WithArguments("System.Object", "ToString").WithLocation(24, 43), - // (24,55): error CS0656: Missing compiler required member 'System.Object.ToString' - // static string M7(string s, char c) => c + s + s + c; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "c").WithArguments("System.Object", "ToString").WithLocation(24, 55)); + // We don't use object.ToString() or char.ToString() in the final codegen. + var verifier = CompileAndVerify(compilation: comp, expectedOutput: ExecutionConditionUtil.IsCoreClr ? "csssscsssscssssccscsscsccssc" : null, verify: Verification.FailsPEVerify); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("Test.M1", """ + { + // Code size 33 (0x21) + .maxstack 4 + .locals init (char V_0) + IL_0000: ldarg.1 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly char)" + IL_0009: ldarg.0 + IL_000a: call "System.ReadOnlySpan string.op_Implicit(string)" + IL_000f: ldarg.0 + IL_0010: call "System.ReadOnlySpan string.op_Implicit(string)" + IL_0015: ldarg.0 + IL_0016: call "System.ReadOnlySpan string.op_Implicit(string)" + IL_001b: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" + IL_0020: ret + } + """); + + verifier.VerifyIL("Test.M2", """ + { + // Code size 33 (0x21) + .maxstack 4 + .locals init (char V_0) + IL_0000: ldarg.0 + IL_0001: call "System.ReadOnlySpan string.op_Implicit(string)" + IL_0006: ldarg.1 + IL_0007: stloc.0 + IL_0008: ldloca.s V_0 + IL_000a: newobj "System.ReadOnlySpan..ctor(ref readonly char)" + IL_000f: ldarg.0 + IL_0010: call "System.ReadOnlySpan string.op_Implicit(string)" + IL_0015: ldarg.0 + IL_0016: call "System.ReadOnlySpan string.op_Implicit(string)" + IL_001b: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" + IL_0020: ret + } + """); + + verifier.VerifyIL("Test.M3", """ + { + // Code size 33 (0x21) + .maxstack 4 + .locals init (char V_0) + IL_0000: ldarg.0 + IL_0001: call "System.ReadOnlySpan string.op_Implicit(string)" + IL_0006: ldarg.0 + IL_0007: call "System.ReadOnlySpan string.op_Implicit(string)" + IL_000c: ldarg.1 + IL_000d: stloc.0 + IL_000e: ldloca.s V_0 + IL_0010: newobj "System.ReadOnlySpan..ctor(ref readonly char)" + IL_0015: ldarg.0 + IL_0016: call "System.ReadOnlySpan string.op_Implicit(string)" + IL_001b: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" + IL_0020: ret + } + """); + + verifier.VerifyIL("Test.M4", """ + { + // Code size 33 (0x21) + .maxstack 4 + .locals init (char V_0) + IL_0000: ldarg.0 + IL_0001: call "System.ReadOnlySpan string.op_Implicit(string)" + IL_0006: ldarg.0 + IL_0007: call "System.ReadOnlySpan string.op_Implicit(string)" + IL_000c: ldarg.0 + IL_000d: call "System.ReadOnlySpan string.op_Implicit(string)" + IL_0012: ldarg.1 + IL_0013: stloc.0 + IL_0014: ldloca.s V_0 + IL_0016: newobj "System.ReadOnlySpan..ctor(ref readonly char)" + IL_001b: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" + IL_0020: ret + } + """); + + verifier.VerifyIL("Test.M5", """ + { + // Code size 36 (0x24) + .maxstack 4 + .locals init (char V_0, + char V_1) + IL_0000: ldarg.1 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly char)" + IL_0009: ldarg.0 + IL_000a: call "System.ReadOnlySpan string.op_Implicit(string)" + IL_000f: ldarg.1 + IL_0010: stloc.1 + IL_0011: ldloca.s V_1 + IL_0013: newobj "System.ReadOnlySpan..ctor(ref readonly char)" + IL_0018: ldarg.0 + IL_0019: call "System.ReadOnlySpan string.op_Implicit(string)" + IL_001e: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" + IL_0023: ret + } + """); + + verifier.VerifyIL("Test.M6", """ + { + // Code size 36 (0x24) + .maxstack 4 + .locals init (char V_0, + char V_1) + IL_0000: ldarg.0 + IL_0001: call "System.ReadOnlySpan string.op_Implicit(string)" + IL_0006: ldarg.1 + IL_0007: stloc.0 + IL_0008: ldloca.s V_0 + IL_000a: newobj "System.ReadOnlySpan..ctor(ref readonly char)" + IL_000f: ldarg.0 + IL_0010: call "System.ReadOnlySpan string.op_Implicit(string)" + IL_0015: ldarg.1 + IL_0016: stloc.1 + IL_0017: ldloca.s V_1 + IL_0019: newobj "System.ReadOnlySpan..ctor(ref readonly char)" + IL_001e: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" + IL_0023: ret + } + """); + + verifier.VerifyIL("Test.M7", """ + { + // Code size 36 (0x24) + .maxstack 4 + .locals init (char V_0, + char V_1) + IL_0000: ldarg.1 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: newobj "System.ReadOnlySpan..ctor(ref readonly char)" + IL_0009: ldarg.0 + IL_000a: call "System.ReadOnlySpan string.op_Implicit(string)" + IL_000f: ldarg.0 + IL_0010: call "System.ReadOnlySpan string.op_Implicit(string)" + IL_0015: ldarg.1 + IL_0016: stloc.1 + IL_0017: ldloca.s V_1 + IL_0019: newobj "System.ReadOnlySpan..ctor(ref readonly char)" + IL_001e: call "string string.Concat(System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan, System.ReadOnlySpan)" + IL_0023: ret + } + """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66827")] diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs index 2bbfa4d5d2c05..d3578cb524e59 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs @@ -1668,7 +1668,7 @@ struct S private string str; public S(char chr) { this.str = chr.ToString(); } public S(string str) { this.str = str; } - public static S operator + (S x, S y) { return new S('(' + x.str + '+' + y.str + ')'); } + public static S operator + (S x, S y) { return new S('(' + x.str + '+' + y.str); } } class C diff --git a/src/Compilers/Core/Portable/Collections/Rope.cs b/src/Compilers/Core/Portable/Collections/Rope.cs index ac06ff20367fa..d70c278ee9864 100644 --- a/src/Compilers/Core/Portable/Collections/Rope.cs +++ b/src/Compilers/Core/Portable/Collections/Rope.cs @@ -19,6 +19,7 @@ internal abstract class Rope public abstract override string ToString(); public abstract string ToString(int maxLength); public abstract int Length { get; } + public bool IsEmpty => Length == 0; protected abstract IEnumerable GetChars(); private Rope() { } From 70ee3ee25bab8663245e93aa467c324f7531c126 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Feb 2025 16:52:09 -0800 Subject: [PATCH 107/305] Make DiagnosticIncrementalAnalyzer a nested private type --- .../Test/CodeFixes/CodeFixServiceTests.cs | 1 - .../DiagnosticAnalyzerServiceTests.cs | 3 +- ...CodeActionOrUserDiagnosticTest_NoEditor.cs | 4 +- .../Diagnostics/DiagnosticAnalyzerService.cs | 15 ++- ...sticAnalyzerService_IncrementalAnalyzer.cs | 3 +- .../DocumentAnalysisExecutor_Helpers.cs | 1 - ...gnosticIncrementalAnalyzer.AnalysisData.cs | 99 +++++++-------- ...cIncrementalAnalyzer.CompilationManager.cs | 11 +- .../DiagnosticIncrementalAnalyzer.Executor.cs | 7 +- ...sticIncrementalAnalyzer.InMemoryStorage.cs | 6 +- ...lAnalyzer.IncrementalMemberEditAnalyzer.cs | 6 +- ...crementalMemberEditAnalyzer_MemberSpans.cs | 6 +- ...rojectAnalyzerReferenceChangedEventArgs.cs | 29 +++-- ...gnosticIncrementalAnalyzer.ProjectState.cs | 7 +- ...ementalAnalyzer.StateManager.HostStates.cs | 6 +- ...ntalAnalyzer.StateManager.ProjectStates.cs | 7 +- ...gnosticIncrementalAnalyzer.StateManager.cs | 6 +- .../DiagnosticIncrementalAnalyzer.StateSet.cs | 6 +- .../EngineV2/DiagnosticIncrementalAnalyzer.cs | 116 +++++++++--------- ...osticIncrementalAnalyzer_GetDiagnostics.cs | 6 +- ...Span.ProjectAndCompilationWithAnalyzers.cs | 6 +- ...crementalAnalyzer_GetDiagnosticsForSpan.cs | 6 +- ...IncrementalAnalyzer_IncrementalAnalyzer.cs | 6 +- ...cs => InProcOrRemoteHostAnalyzerRunner.cs} | 5 +- 24 files changed, 204 insertions(+), 164 deletions(-) rename src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/{DiagnosticIncrementalAnalyzer.InProcOrRemoteHostAnalyzerRunner.cs => InProcOrRemoteHostAnalyzerRunner.cs} (97%) diff --git a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs index 190135f54ce47..6287855eac349 100644 --- a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs +++ b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Diagnostics.EngineV2; using Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; using Microsoft.CodeAnalysis.ErrorLogger; using Microsoft.CodeAnalysis.ErrorReporting; diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index 3c08729f7baee..760822aad0cc9 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -262,8 +262,7 @@ public async Task TestHostAnalyzerOrderingAsync() var service = Assert.IsType(exportProvider.GetExportedValue()); - var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace); - var analyzers = await incrementalAnalyzer.GetAnalyzersTestOnlyAsync(project, CancellationToken.None).ConfigureAwait(false); + var analyzers = await service.GetTestAccessor().GetAnalyzersAsync(project, CancellationToken.None).ConfigureAwait(false); var analyzersArray = analyzers.ToArray(); AssertEx.Equal( diff --git a/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor.cs b/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor.cs index cd4fc6bc994cd..27e98ede07ae8 100644 --- a/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor.cs +++ b/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor.cs @@ -22,7 +22,7 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeRefactorings; -using Microsoft.CodeAnalysis.Diagnostics.EngineV2; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Remote.Testing; @@ -154,7 +154,7 @@ private protected virtual IDocumentServiceProvider GetDocumentServiceProvider() protected virtual TestComposition GetComposition() => FeaturesTestCompositions.Features - .AddAssemblies(typeof(DiagnosticIncrementalAnalyzer).Assembly); + .AddAssemblies(typeof(DiagnosticAnalyzerService).Assembly); protected virtual void InitializeWorkspace(TestWorkspace workspace, TestParameters parameters) { diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs index bd061419047b9..992dadc5aab71 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs @@ -11,7 +11,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeStyle; -using Microsoft.CodeAnalysis.Diagnostics.EngineV2; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; @@ -58,6 +57,9 @@ public DiagnosticAnalyzerService( }); } + public static Task GetDiagnosticVersionAsync(Project project, CancellationToken cancellationToken) + => project.GetDependentVersionAsync(cancellationToken); + public bool CrashOnAnalyzerException => GlobalOptions.GetOption(s_crashOnAnalyzerException); @@ -112,5 +114,16 @@ public Task> GetProjectDiagnosticsForIdsAsync( var analyzer = CreateIncrementalAnalyzer(solution.Workspace); return analyzer.GetProjectDiagnosticsForIdsAsync(solution, projectId, diagnosticIds, shouldIncludeAnalyzer, includeNonLocalDocumentDiagnostics, cancellationToken); } + + public TestAccessor GetTestAccessor() + => new(this); + + public readonly struct TestAccessor(DiagnosticAnalyzerService service) + { + public Task> GetAnalyzersAsync(Project project, CancellationToken cancellationToken) + { + return service.CreateIncrementalAnalyzer(project.Solution.Workspace).GetAnalyzersForTestingPurposesOnlyAsync(project, cancellationToken); + } + } } } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs index 078135d1990a2..aa41aa3478fff 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs @@ -3,14 +3,13 @@ // See the LICENSE file in the project root for more information. using System; -using Microsoft.CodeAnalysis.Diagnostics.EngineV2; using Microsoft.CodeAnalysis.Host.Mef; namespace Microsoft.CodeAnalysis.Diagnostics; internal partial class DiagnosticAnalyzerService { - public DiagnosticIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace) + private DiagnosticIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace) { return _map.GetValue(workspace, _createIncrementalAnalyzer); } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/DocumentAnalysisExecutor_Helpers.cs b/src/LanguageServer/Protocol/Features/Diagnostics/DocumentAnalysisExecutor_Helpers.cs index eecd062437cec..4748effdadb07 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/DocumentAnalysisExecutor_Helpers.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/DocumentAnalysisExecutor_Helpers.cs @@ -9,7 +9,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Diagnostics.EngineV2; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.SolutionCrawler; diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs index fe6d4245de0ee..b668b076cfebf 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs @@ -8,72 +8,75 @@ using Microsoft.CodeAnalysis.Workspaces.Diagnostics; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2; +namespace Microsoft.CodeAnalysis.Diagnostics; -internal partial class DiagnosticIncrementalAnalyzer +internal partial class DiagnosticAnalyzerService { - /// - /// Data holder for all diagnostics for a project for an analyzer - /// - private readonly struct ProjectAnalysisData( - ProjectId projectId, - VersionStamp version, - ImmutableDictionary result) + private partial class DiagnosticIncrementalAnalyzer { /// - /// ProjectId of this data + /// Data holder for all diagnostics for a project for an analyzer /// - public readonly ProjectId ProjectId = projectId; - - /// - /// Version of the Items - /// - public readonly VersionStamp Version = version; + private readonly struct ProjectAnalysisData( + ProjectId projectId, + VersionStamp version, + ImmutableDictionary result) + { + /// + /// ProjectId of this data + /// + public readonly ProjectId ProjectId = projectId; - /// - /// Current data that matches the version - /// - public readonly ImmutableDictionary Result = result; + /// + /// Version of the Items + /// + public readonly VersionStamp Version = version; - public DiagnosticAnalysisResult GetResult(DiagnosticAnalyzer analyzer) - => GetResultOrEmpty(Result, analyzer, ProjectId, Version); + /// + /// Current data that matches the version + /// + public readonly ImmutableDictionary Result = result; - public bool TryGetResult(DiagnosticAnalyzer analyzer, out DiagnosticAnalysisResult result) - => Result.TryGetValue(analyzer, out result); + public DiagnosticAnalysisResult GetResult(DiagnosticAnalyzer analyzer) + => GetResultOrEmpty(Result, analyzer, ProjectId, Version); - public static async Task CreateAsync(Project project, ImmutableArray stateSets, CancellationToken cancellationToken) - { - VersionStamp? version = null; + public bool TryGetResult(DiagnosticAnalyzer analyzer, out DiagnosticAnalysisResult result) + => Result.TryGetValue(analyzer, out result); - var builder = ImmutableDictionary.CreateBuilder(); - foreach (var stateSet in stateSets) + public static async Task CreateAsync(Project project, ImmutableArray stateSets, CancellationToken cancellationToken) { - var state = stateSet.GetOrCreateProjectState(project.Id); - var result = await state.GetAnalysisDataAsync(project, cancellationToken).ConfigureAwait(false); - Contract.ThrowIfFalse(project.Id == result.ProjectId); + VersionStamp? version = null; - if (!version.HasValue) + var builder = ImmutableDictionary.CreateBuilder(); + foreach (var stateSet in stateSets) { - version = result.Version; + var state = stateSet.GetOrCreateProjectState(project.Id); + var result = await state.GetAnalysisDataAsync(project, cancellationToken).ConfigureAwait(false); + Contract.ThrowIfFalse(project.Id == result.ProjectId); + + if (!version.HasValue) + { + version = result.Version; + } + else if (version.Value != VersionStamp.Default && version.Value != result.Version) + { + // if not all version is same, set version as default. + // this can happen at the initial data loading or + // when document is closed and we put active file state to project state + version = VersionStamp.Default; + } + + builder.Add(stateSet.Analyzer, result); } - else if (version.Value != VersionStamp.Default && version.Value != result.Version) + + if (!version.HasValue) { - // if not all version is same, set version as default. - // this can happen at the initial data loading or - // when document is closed and we put active file state to project state - version = VersionStamp.Default; + // there is no saved data to return. + return new ProjectAnalysisData(project.Id, VersionStamp.Default, ImmutableDictionary.Empty); } - builder.Add(stateSet.Analyzer, result); + return new ProjectAnalysisData(project.Id, version.Value, builder.ToImmutable()); } - - if (!version.HasValue) - { - // there is no saved data to return. - return new ProjectAnalysisData(project.Id, VersionStamp.Default, ImmutableDictionary.Empty); - } - - return new ProjectAnalysisData(project.Id, version.Value, builder.ToImmutable()); } } } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs index 9775aef42f602..964a0134e053b 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs @@ -6,10 +6,13 @@ using System.Threading; using System.Threading.Tasks; -namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2; +namespace Microsoft.CodeAnalysis.Diagnostics; -internal partial class DiagnosticIncrementalAnalyzer +internal partial class DiagnosticAnalyzerService { - private static Task CreateCompilationWithAnalyzersAsync(Project project, ImmutableArray stateSets, bool crashOnAnalyzerException, CancellationToken cancellationToken) - => DocumentAnalysisExecutor.CreateCompilationWithAnalyzersAsync(project, stateSets.SelectAsArray(s => !s.IsHostAnalyzer, s => s.Analyzer), stateSets.SelectAsArray(s => s.IsHostAnalyzer, s => s.Analyzer), crashOnAnalyzerException, cancellationToken); + private partial class DiagnosticIncrementalAnalyzer + { + private static Task CreateCompilationWithAnalyzersAsync(Project project, ImmutableArray stateSets, bool crashOnAnalyzerException, CancellationToken cancellationToken) + => DocumentAnalysisExecutor.CreateCompilationWithAnalyzersAsync(project, stateSets.SelectAsArray(s => !s.IsHostAnalyzer, s => s.Analyzer), stateSets.SelectAsArray(s => s.IsHostAnalyzer, s => s.Analyzer), crashOnAnalyzerException, cancellationToken); + } } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs index e064c2c89d7bd..76e52a8f58a0c 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs @@ -11,13 +11,14 @@ using Microsoft.CodeAnalysis.Diagnostics.Telemetry; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 +namespace Microsoft.CodeAnalysis.Diagnostics; + +internal partial class DiagnosticAnalyzerService { - internal partial class DiagnosticIncrementalAnalyzer + private partial class DiagnosticIncrementalAnalyzer { /// /// Return all diagnostics that belong to given project for the given StateSets (analyzers) either from cache or by calculating them diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InMemoryStorage.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InMemoryStorage.cs index 4895a9c81c5aa..9377ee9ca2b9f 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InMemoryStorage.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InMemoryStorage.cs @@ -7,9 +7,11 @@ using Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 +namespace Microsoft.CodeAnalysis.Diagnostics; + +internal partial class DiagnosticAnalyzerService { - internal partial class DiagnosticIncrementalAnalyzer + private partial class DiagnosticIncrementalAnalyzer { private static class InMemoryStorage { diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs index bfbd34725b9fb..8491b88f7638a 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs @@ -15,9 +15,11 @@ using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 +namespace Microsoft.CodeAnalysis.Diagnostics; + +internal partial class DiagnosticAnalyzerService { - internal partial class DiagnosticIncrementalAnalyzer + private partial class DiagnosticIncrementalAnalyzer { /// /// This type performs incremental analysis in presence of edits to only a single member inside a document. diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer_MemberSpans.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer_MemberSpans.cs index 05f4d5a6eaa09..5258d00725e38 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer_MemberSpans.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer_MemberSpans.cs @@ -12,9 +12,11 @@ using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 +namespace Microsoft.CodeAnalysis.Diagnostics; + +internal partial class DiagnosticAnalyzerService { - internal partial class DiagnosticIncrementalAnalyzer + private partial class DiagnosticIncrementalAnalyzer { private sealed partial class IncrementalMemberEditAnalyzer { diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectAnalyzerReferenceChangedEventArgs.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectAnalyzerReferenceChangedEventArgs.cs index 03ab339f2ec7d..6d370680d96e1 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectAnalyzerReferenceChangedEventArgs.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectAnalyzerReferenceChangedEventArgs.cs @@ -5,23 +5,26 @@ using System; using System.Collections.Immutable; -namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2; +namespace Microsoft.CodeAnalysis.Diagnostics; -internal partial class DiagnosticIncrementalAnalyzer +internal partial class DiagnosticAnalyzerService { - /// - /// EventArgs for - /// - /// this event args contains information such as the has changed - /// and what has changed. - /// - private sealed class ProjectAnalyzerReferenceChangedEventArgs : EventArgs + private partial class DiagnosticIncrementalAnalyzer { - public readonly ImmutableArray Removed; - - public ProjectAnalyzerReferenceChangedEventArgs(ImmutableArray removed) + /// + /// EventArgs for + /// + /// this event args contains information such as the has changed + /// and what has changed. + /// + private sealed class ProjectAnalyzerReferenceChangedEventArgs : EventArgs { - Removed = removed; + public readonly ImmutableArray Removed; + + public ProjectAnalyzerReferenceChangedEventArgs(ImmutableArray removed) + { + Removed = removed; + } } } } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index 51ebc0302f628..0beabe22898a1 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -8,16 +8,17 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 +namespace Microsoft.CodeAnalysis.Diagnostics; + +internal partial class DiagnosticAnalyzerService { - internal partial class DiagnosticIncrementalAnalyzer + private partial class DiagnosticIncrementalAnalyzer { /// /// State for diagnostics that belong to a project at given time. diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs index 0cb12b892ce0c..fcb3e7ebed94b 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs @@ -9,9 +9,11 @@ using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 +namespace Microsoft.CodeAnalysis.Diagnostics; + +internal partial class DiagnosticAnalyzerService { - internal partial class DiagnosticIncrementalAnalyzer + private partial class DiagnosticIncrementalAnalyzer { private partial class StateManager { diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs index 11bddca4858a9..f87178c2d353d 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs @@ -4,15 +4,16 @@ using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 +namespace Microsoft.CodeAnalysis.Diagnostics; + +internal partial class DiagnosticAnalyzerService { - internal partial class DiagnosticIncrementalAnalyzer + private partial class DiagnosticIncrementalAnalyzer { private partial class StateManager { diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs index 83e5cfeece232..c9f36a9e504c5 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs @@ -11,9 +11,11 @@ using System.Threading.Tasks; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 +namespace Microsoft.CodeAnalysis.Diagnostics; + +internal partial class DiagnosticAnalyzerService { - internal partial class DiagnosticIncrementalAnalyzer + private partial class DiagnosticIncrementalAnalyzer { /// /// This is in charge of anything related to diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs index 602c4de6d9e51..25d7f5a317611 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs @@ -12,9 +12,11 @@ using Microsoft.CodeAnalysis.Options; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 +namespace Microsoft.CodeAnalysis.Diagnostics; + +internal partial class DiagnosticAnalyzerService { - internal partial class DiagnosticIncrementalAnalyzer + private partial class DiagnosticIncrementalAnalyzer { /// /// this contains all states regarding a diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index 941b3368c11cf..8c853c5b84f1e 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -14,78 +14,78 @@ using Microsoft.CodeAnalysis.Workspaces.Diagnostics; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2; - -/// -/// Diagnostic Analyzer Engine V2 -/// -/// This one follows pattern compiler has set for diagnostic analyzer. -/// -internal partial class DiagnosticIncrementalAnalyzer +namespace Microsoft.CodeAnalysis.Diagnostics; + +internal partial class DiagnosticAnalyzerService { - private readonly DiagnosticAnalyzerTelemetry _telemetry = new(); - private readonly StateManager _stateManager; - private readonly InProcOrRemoteHostAnalyzerRunner _diagnosticAnalyzerRunner; - private readonly IncrementalMemberEditAnalyzer _incrementalMemberEditAnalyzer = new(); - - internal DiagnosticAnalyzerService AnalyzerService { get; } - - [Obsolete(MefConstruction.FactoryMethodMessage, error: true)] - public DiagnosticIncrementalAnalyzer( - DiagnosticAnalyzerService analyzerService, - Workspace workspace, - DiagnosticAnalyzerInfoCache analyzerInfoCache, - IGlobalOptionService globalOptionService) + /// + /// Diagnostic Analyzer Engine V2 + /// + /// This one follows pattern compiler has set for diagnostic analyzer. + /// + private partial class DiagnosticIncrementalAnalyzer { - Contract.ThrowIfNull(analyzerService); + private readonly DiagnosticAnalyzerTelemetry _telemetry = new(); + private readonly StateManager _stateManager; + private readonly InProcOrRemoteHostAnalyzerRunner _diagnosticAnalyzerRunner; + private readonly IncrementalMemberEditAnalyzer _incrementalMemberEditAnalyzer = new(); + + internal DiagnosticAnalyzerService AnalyzerService { get; } + + [Obsolete(MefConstruction.FactoryMethodMessage, error: true)] + public DiagnosticIncrementalAnalyzer( + DiagnosticAnalyzerService analyzerService, + Workspace workspace, + DiagnosticAnalyzerInfoCache analyzerInfoCache, + IGlobalOptionService globalOptionService) + { + Contract.ThrowIfNull(analyzerService); - AnalyzerService = analyzerService; - GlobalOptions = globalOptionService; + AnalyzerService = analyzerService; + GlobalOptions = globalOptionService; - _stateManager = new StateManager(workspace, analyzerInfoCache); - _stateManager.ProjectAnalyzerReferenceChanged += OnProjectAnalyzerReferenceChanged; + _stateManager = new StateManager(workspace, analyzerInfoCache); + _stateManager.ProjectAnalyzerReferenceChanged += OnProjectAnalyzerReferenceChanged; - var enabled = globalOptionService.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler); - _diagnosticAnalyzerRunner = new InProcOrRemoteHostAnalyzerRunner( - enabled, analyzerInfoCache, analyzerService.Listener); - } + var enabled = globalOptionService.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler); + _diagnosticAnalyzerRunner = new InProcOrRemoteHostAnalyzerRunner( + enabled, analyzerInfoCache, analyzerService.Listener); + } - internal IGlobalOptionService GlobalOptions { get; } - internal DiagnosticAnalyzerInfoCache DiagnosticAnalyzerInfoCache => _diagnosticAnalyzerRunner.AnalyzerInfoCache; + internal IGlobalOptionService GlobalOptions { get; } + internal DiagnosticAnalyzerInfoCache DiagnosticAnalyzerInfoCache => _diagnosticAnalyzerRunner.AnalyzerInfoCache; - private void OnProjectAnalyzerReferenceChanged(object? sender, ProjectAnalyzerReferenceChangedEventArgs e) - { - if (e.Removed.Length == 0) + private void OnProjectAnalyzerReferenceChanged(object? sender, ProjectAnalyzerReferenceChangedEventArgs e) { - // nothing to refresh - return; + if (e.Removed.Length == 0) + { + // nothing to refresh + return; + } + + // make sure we drop cache related to the analyzers + foreach (var stateSet in e.Removed) + stateSet.OnRemoved(); } - // make sure we drop cache related to the analyzers - foreach (var stateSet in e.Removed) - stateSet.OnRemoved(); - } - - public static Task GetDiagnosticVersionAsync(Project project, CancellationToken cancellationToken) - => project.GetDependentVersionAsync(cancellationToken); - - private static DiagnosticAnalysisResult GetResultOrEmpty(ImmutableDictionary map, DiagnosticAnalyzer analyzer, ProjectId projectId, VersionStamp version) - { - if (map.TryGetValue(analyzer, out var result)) + private static DiagnosticAnalysisResult GetResultOrEmpty(ImmutableDictionary map, DiagnosticAnalyzer analyzer, ProjectId projectId, VersionStamp version) { - return result; + if (map.TryGetValue(analyzer, out var result)) + { + return result; + } + + return DiagnosticAnalysisResult.CreateEmpty(projectId, version); } - return DiagnosticAnalysisResult.CreateEmpty(projectId, version); - } + public async Task> GetAnalyzersForTestingPurposesOnlyAsync(Project project, CancellationToken cancellationToken) + { + var analyzers = await _stateManager.GetOrCreateStateSetsAsync(project, cancellationToken).ConfigureAwait(false); - internal async Task> GetAnalyzersTestOnlyAsync(Project project, CancellationToken cancellationToken) - { - var analyzers = await _stateManager.GetOrCreateStateSetsAsync(project, cancellationToken).ConfigureAwait(false); + return analyzers.SelectAsArray(s => s.Analyzer); + } - return analyzers.Select(s => s.Analyzer); + private static string GetProjectLogMessage(Project project, ImmutableArray stateSets) + => $"project: ({project.Id}), ({string.Join(Environment.NewLine, stateSets.Select(s => s.Analyzer.ToString()))})"; } - - private static string GetProjectLogMessage(Project project, ImmutableArray stateSets) - => $"project: ({project.Id}), ({string.Join(Environment.NewLine, stateSets.Select(s => s.Analyzer.ToString()))})"; } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index 3bd74fec80883..3d1532fd0183e 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -11,9 +11,11 @@ using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 +namespace Microsoft.CodeAnalysis.Diagnostics; + +internal partial class DiagnosticAnalyzerService { - internal partial class DiagnosticIncrementalAnalyzer + private partial class DiagnosticIncrementalAnalyzer { public Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) => new IdeLatestDiagnosticGetter(this, solution, projectId, documentId, diagnosticIds, shouldIncludeAnalyzer, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics).GetDiagnosticsAsync(cancellationToken); diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.ProjectAndCompilationWithAnalyzers.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.ProjectAndCompilationWithAnalyzers.cs index df33398ea07c7..be9d14efc42f9 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.ProjectAndCompilationWithAnalyzers.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.ProjectAndCompilationWithAnalyzers.cs @@ -2,9 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 +namespace Microsoft.CodeAnalysis.Diagnostics; + +internal partial class DiagnosticAnalyzerService { - internal partial class DiagnosticIncrementalAnalyzer + private partial class DiagnosticIncrementalAnalyzer { private sealed class ProjectAndCompilationWithAnalyzers { diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs index 5e2c2f96dbba8..88b41904c5222 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs @@ -18,9 +18,11 @@ using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 +namespace Microsoft.CodeAnalysis.Diagnostics; + +internal partial class DiagnosticAnalyzerService { - internal partial class DiagnosticIncrementalAnalyzer + private partial class DiagnosticIncrementalAnalyzer { public async Task> GetDiagnosticsForSpanAsync( TextDocument document, diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index ef01e0258d91e..6f6a035fb0bf6 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -12,9 +12,11 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 +namespace Microsoft.CodeAnalysis.Diagnostics; + +internal partial class DiagnosticAnalyzerService { - internal partial class DiagnosticIncrementalAnalyzer + private partial class DiagnosticIncrementalAnalyzer { public async Task> ForceAnalyzeProjectAsync(Project project, CancellationToken cancellationToken) { diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InProcOrRemoteHostAnalyzerRunner.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/InProcOrRemoteHostAnalyzerRunner.cs similarity index 97% rename from src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InProcOrRemoteHostAnalyzerRunner.cs rename to src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/InProcOrRemoteHostAnalyzerRunner.cs index 3fced9e7cdeff..01cf0957dc86b 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InProcOrRemoteHostAnalyzerRunner.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/InProcOrRemoteHostAnalyzerRunner.cs @@ -9,7 +9,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Diagnostics.EngineV2; using Microsoft.CodeAnalysis.Diagnostics.Telemetry; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host; @@ -120,7 +119,7 @@ private async Task Date: Thu, 6 Feb 2025 16:55:03 -0800 Subject: [PATCH 108/305] Simplify --- .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 6f6a035fb0bf6..ebe94f8eec459 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -30,9 +30,7 @@ public async Task> ForceAnalyzeProjectAsync(Proje var activeProjectAnalyzers = stateSets.SelectAsArray(s => !s.IsHostAnalyzer, s => s.Analyzer); var activeHostAnalyzers = stateSets.SelectAsArray(s => s.IsHostAnalyzer, s => s.Analyzer); - CompilationWithAnalyzersPair? compilationWithAnalyzers = null; - - compilationWithAnalyzers = await DocumentAnalysisExecutor.CreateCompilationWithAnalyzersAsync( + var compilationWithAnalyzers = await DocumentAnalysisExecutor.CreateCompilationWithAnalyzersAsync( project, activeProjectAnalyzers, activeHostAnalyzers, AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); var result = await GetProjectAnalysisDataAsync(compilationWithAnalyzers, project, stateSets, cancellationToken).ConfigureAwait(false); From 04d4055d9497337afc4b48cc56d2bbec829bf715 Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Thu, 6 Feb 2025 17:02:47 -0800 Subject: [PATCH 109/305] Always write the Api output files, even if they don't change The calling MSBuild targets already specify inputs and outputs; skipping writing the output files if they're not changed prevents the output timestamp from being updated, so the builds always run (and then force a rebuild of our setup projects). Fixes https://github.com/dotnet/roslyn/issues/77039 --- ...GenerateFilteredReferenceAssembliesTask.cs | 25 +++---------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/src/Tools/SemanticSearch/BuildTask/GenerateFilteredReferenceAssembliesTask.cs b/src/Tools/SemanticSearch/BuildTask/GenerateFilteredReferenceAssembliesTask.cs index 6f394264544f9..be93f54c55fba 100644 --- a/src/Tools/SemanticSearch/BuildTask/GenerateFilteredReferenceAssembliesTask.cs +++ b/src/Tools/SemanticSearch/BuildTask/GenerateFilteredReferenceAssembliesTask.cs @@ -149,31 +149,14 @@ internal void WriteApis(string outputFilePath, byte[] peImage) var newContent = "# Generated, do not update manually\r\n" + string.Join("\r\n", apis); - string currentContent; try { - currentContent = File.ReadAllText(outputFilePath, Encoding.UTF8); + File.WriteAllText(outputFilePath, newContent); + Log.LogMessage($"Baseline updated: '{outputFilePath}'"); } - catch (Exception) - { - currentContent = ""; - } - - if (currentContent != newContent) - { - try - { - File.WriteAllText(outputFilePath, newContent); - Log.LogMessage($"Baseline updated: '{outputFilePath}'"); - } - catch (Exception e) - { - Log.LogError($"Error updating baseline '{outputFilePath}': {e.Message}"); - } - } - else + catch (Exception e) { - Log.LogMessage($"Baseline not updated '{outputFilePath}'"); + Log.LogError($"Error updating baseline '{outputFilePath}': {e.Message}"); } } From 0bc419caf7376b31f6889cf40aec6e12ca44e9fb Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Feb 2025 17:16:52 -0800 Subject: [PATCH 110/305] Inline diagnostic method helpers --- ...ementalAnalyzer.StateManager.HostStates.cs | 15 --------- ...gnosticIncrementalAnalyzer.StateManager.cs | 31 ++++++++++--------- ...osticIncrementalAnalyzer_GetDiagnostics.cs | 2 +- 3 files changed, 17 insertions(+), 31 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs index 0cb12b892ce0c..72a5543da7590 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs @@ -15,21 +15,6 @@ internal partial class DiagnosticIncrementalAnalyzer { private partial class StateManager { - public IEnumerable GetAllHostStateSets() - { - var analyzerReferences = _workspace.CurrentSolution.SolutionState.Analyzers.HostAnalyzerReferences; - foreach (var (key, value) in _hostAnalyzerStateMap) - { - if (key.AnalyzerReferences == analyzerReferences) - { - foreach (var stateSet in value.OrderedStateSets) - { - yield return stateSet; - } - } - } - } - private HostAnalyzerStateSets GetOrCreateHostStateSets(Project project, ProjectAnalyzerStateSets projectStateSets) { var key = new HostAnalyzerStateSetKey(project.Language, project.State.HasSdkCodeStyleAnalyzers, project.Solution.SolutionState.Analyzers.HostAnalyzerReferences); diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs index 83e5cfeece232..f70837aae0fc4 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs @@ -9,6 +9,8 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 @@ -56,27 +58,26 @@ public StateManager(Workspace workspace, DiagnosticAnalyzerInfoCache analyzerInf } /// - /// Return s for the given . + /// Return s for the given . /// This will never create new but will return ones already created. /// - public IEnumerable GetStateSets(ProjectId projectId) + public ImmutableArray GetStateSets(Project project) { - var hostStateSets = GetAllHostStateSets(); + using var _ = ArrayBuilder.GetInstance(out var result); + + var analyzerReferences = project.Solution.SolutionState.Analyzers.HostAnalyzerReferences; + foreach (var (key, value) in _hostAnalyzerStateMap) + { + if (key.AnalyzerReferences == analyzerReferences) + result.AddRange(value.OrderedStateSets); + } // No need to use _projectAnalyzerStateMapGuard during reads of _projectAnalyzerStateMap - return _projectAnalyzerStateMap.TryGetValue(projectId, out var entry) - ? hostStateSets.Concat(entry.StateSetMap.Values) - : hostStateSets; - } + if (_projectAnalyzerStateMap.TryGetValue(project.Id, out var entry)) + result.AddRange(entry.StateSetMap.Values); - /// - /// Return s for the given . - /// This will never create new but will return ones already created. - /// Difference with is that - /// this will only return s that have same language as . - /// - public IEnumerable GetStateSets(Project project) - => GetStateSets(project.Id).Where(s => s.Language == project.Language); + return result.WhereAsArray((stateSet, project) => stateSet.Language == project.Language, project); + } /// /// Return s for the given . diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index bbd51d945f48c..29f4edbb57aba 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -97,7 +97,7 @@ protected override async Task ProduceDiagnosticsAsync( Project project, IReadOnlyList documentIds, bool includeProjectNonLocalResult, ArrayBuilder builder, CancellationToken cancellationToken) { - foreach (var stateSet in StateManager.GetStateSets(project.Id)) + foreach (var stateSet in StateManager.GetStateSets(project)) { foreach (var documentId in documentIds) { From cca49572219d557741618f41855e06ab7eeed071 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Feb 2025 17:24:07 -0800 Subject: [PATCH 111/305] Remove unnecessary language tracking in the diagnostic service --- ...ementalAnalyzer.StateManager.HostStates.cs | 5 +-- ...ntalAnalyzer.StateManager.ProjectStates.cs | 2 +- ...gnosticIncrementalAnalyzer.StateManager.cs | 45 +++---------------- .../DiagnosticIncrementalAnalyzer.StateSet.cs | 4 +- 4 files changed, 10 insertions(+), 46 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs index 0cb12b892ce0c..7447d223198dd 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs @@ -41,11 +41,10 @@ private HostAnalyzerStateSets GetOrCreateHostStateSets(Project project, ProjectA static HostAnalyzerStateSets CreateLanguageSpecificAnalyzerMap(HostAnalyzerStateSetKey arg, (HostDiagnosticAnalyzers HostAnalyzers, ImmutableHashSet ReferenceIdsToRedirect) state) { - var language = arg.Language; - var analyzersPerReference = state.HostAnalyzers.GetOrCreateHostDiagnosticAnalyzersPerReference(language); + var analyzersPerReference = state.HostAnalyzers.GetOrCreateHostDiagnosticAnalyzersPerReference(arg.Language); var (hostAnalyzerCollection, projectAnalyzerCollection) = GetAnalyzerCollections(analyzersPerReference, state.ReferenceIdsToRedirect); - var analyzerMap = CreateStateSetMap(language, projectAnalyzerCollection, hostAnalyzerCollection, includeWorkspacePlaceholderAnalyzers: true); + var analyzerMap = CreateStateSetMap(projectAnalyzerCollection, hostAnalyzerCollection, includeWorkspacePlaceholderAnalyzers: true); return new HostAnalyzerStateSets(analyzerMap); } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs index 11bddca4858a9..0fcf51b28630b 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs @@ -86,7 +86,7 @@ private ProjectAnalyzerStateSets CreateProjectStateSets(Project project) return ProjectAnalyzerStateSets.Default; } - var newMap = CreateStateSetMap(project.Language, analyzersPerReference.Values, [], includeWorkspacePlaceholderAnalyzers: false); + var newMap = CreateStateSetMap(analyzersPerReference.Values, [], includeWorkspacePlaceholderAnalyzers: false); var skippedAnalyzersInfo = project.GetSkippedAnalyzersInfo(_analyzerInfoCache); return new ProjectAnalyzerStateSets(project.AnalyzerReferences, analyzersPerReference, newMap, skippedAnalyzersInfo); } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs index 83e5cfeece232..2fe8dcf587c6b 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs @@ -69,15 +69,6 @@ public IEnumerable GetStateSets(ProjectId projectId) : hostStateSets; } - /// - /// Return s for the given . - /// This will never create new but will return ones already created. - /// Difference with is that - /// this will only return s that have same language as . - /// - public IEnumerable GetStateSets(Project project) - => GetStateSets(project.Id).Where(s => s.Language == project.Language); - /// /// Return s for the given . /// This will either return already created s for the specific snapshot of or @@ -115,7 +106,6 @@ private void RaiseProjectAnalyzerReferenceChanged(ProjectAnalyzerReferenceChange => ProjectAnalyzerReferenceChanged?.Invoke(this, args); private static ImmutableDictionary CreateStateSetMap( - string language, IEnumerable> projectAnalyzerCollection, IEnumerable> hostAnalyzerCollection, bool includeWorkspacePlaceholderAnalyzers) @@ -124,8 +114,8 @@ private static ImmutableDictionary CreateStateSetM if (includeWorkspacePlaceholderAnalyzers) { - builder.Add(FileContentLoadAnalyzer.Instance, new StateSet(language, FileContentLoadAnalyzer.Instance, isHostAnalyzer: true)); - builder.Add(GeneratorDiagnosticsPlaceholderAnalyzer.Instance, new StateSet(language, GeneratorDiagnosticsPlaceholderAnalyzer.Instance, isHostAnalyzer: true)); + builder.Add(FileContentLoadAnalyzer.Instance, new StateSet(FileContentLoadAnalyzer.Instance, isHostAnalyzer: true)); + builder.Add(GeneratorDiagnosticsPlaceholderAnalyzer.Instance, new StateSet(GeneratorDiagnosticsPlaceholderAnalyzer.Instance, isHostAnalyzer: true)); } foreach (var analyzers in projectAnalyzerCollection) @@ -143,7 +133,7 @@ private static ImmutableDictionary CreateStateSetM continue; } - builder.Add(analyzer, new StateSet(language, analyzer, isHostAnalyzer: false)); + builder.Add(analyzer, new StateSet(analyzer, isHostAnalyzer: false)); } } @@ -162,38 +152,15 @@ private static ImmutableDictionary CreateStateSetM continue; } - builder.Add(analyzer, new StateSet(language, analyzer, isHostAnalyzer: true)); + builder.Add(analyzer, new StateSet(analyzer, isHostAnalyzer: true)); } } return builder.ToImmutable(); } - private readonly struct HostAnalyzerStateSetKey : IEquatable - { - public HostAnalyzerStateSetKey(string language, bool hasSdkCodeStyleAnalyzers, IReadOnlyList analyzerReferences) - { - Language = language; - HasSdkCodeStyleAnalyzers = hasSdkCodeStyleAnalyzers; - AnalyzerReferences = analyzerReferences; - } - - public string Language { get; } - public bool HasSdkCodeStyleAnalyzers { get; } - public IReadOnlyList AnalyzerReferences { get; } - - public bool Equals(HostAnalyzerStateSetKey other) - => Language == other.Language && - HasSdkCodeStyleAnalyzers == other.HasSdkCodeStyleAnalyzers && - AnalyzerReferences == other.AnalyzerReferences; - - public override bool Equals(object? obj) - => obj is HostAnalyzerStateSetKey key && Equals(key); - - public override int GetHashCode() - => Hash.Combine(Language.GetHashCode(), - Hash.Combine(HasSdkCodeStyleAnalyzers.GetHashCode(), AnalyzerReferences.GetHashCode())); - } + private readonly record struct HostAnalyzerStateSetKey( + string Language, bool HasSdkCodeStyleAnalyzers, IReadOnlyList AnalyzerReferences); } } } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs index 602c4de6d9e51..842292a499d62 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs @@ -21,16 +21,14 @@ internal partial class DiagnosticIncrementalAnalyzer /// private sealed class StateSet { - public readonly string Language; public readonly DiagnosticAnalyzer Analyzer; public readonly bool IsHostAnalyzer; private readonly ConcurrentSet _activeDocuments; private readonly ConcurrentDictionary _projectStates; - public StateSet(string language, DiagnosticAnalyzer analyzer, bool isHostAnalyzer) + public StateSet(DiagnosticAnalyzer analyzer, bool isHostAnalyzer) { - Language = language; Analyzer = analyzer; IsHostAnalyzer = isHostAnalyzer; From d5535e92af9b3529401df0ec8379e057efd71aa4 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Feb 2025 17:26:26 -0800 Subject: [PATCH 112/305] Update src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs --- .../DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs index 7447d223198dd..a9f335502e429 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs @@ -41,7 +41,8 @@ private HostAnalyzerStateSets GetOrCreateHostStateSets(Project project, ProjectA static HostAnalyzerStateSets CreateLanguageSpecificAnalyzerMap(HostAnalyzerStateSetKey arg, (HostDiagnosticAnalyzers HostAnalyzers, ImmutableHashSet ReferenceIdsToRedirect) state) { - var analyzersPerReference = state.HostAnalyzers.GetOrCreateHostDiagnosticAnalyzersPerReference(arg.Language); + var language = arg.Language; + var analyzersPerReference = state.HostAnalyzers.GetOrCreateHostDiagnosticAnalyzersPerReference(language); var (hostAnalyzerCollection, projectAnalyzerCollection) = GetAnalyzerCollections(analyzersPerReference, state.ReferenceIdsToRedirect); var analyzerMap = CreateStateSetMap(projectAnalyzerCollection, hostAnalyzerCollection, includeWorkspacePlaceholderAnalyzers: true); From 487e274496019b460a9907129a119fe838353e61 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Thu, 6 Feb 2025 17:37:39 -0800 Subject: [PATCH 113/305] Fix syntax generation of explict operator from symbol --- .../Syntax.xml.Internal.Generated.cs | 6 ++++-- .../Syntax.xml.Main.Generated.cs | 3 ++- .../CSharp/Portable/Syntax/Syntax.xml | 1 + .../CodeGeneration/SyntaxGeneratorTests.cs | 18 ++++++++++++++++++ 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs index 78d8aedb8b8f2..0c6bc0fa16bd4 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs @@ -31740,7 +31740,8 @@ public OperatorDeclarationSyntax OperatorDeclaration(CoreSyntax.SyntaxList + diff --git a/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs b/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs index 717c86bb6493b..841028bfa9058 100644 --- a/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs +++ b/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs @@ -1001,6 +1001,24 @@ public void TestConversionOperatorDeclaration() "public static implicit operator global::System.Decimal(global::System.Byte value)\r\n{\r\n}"); } + [Fact, WorkItem(77101, "https://github.com/dotnet/roslyn/issues/77101")] + public void TestExplicitOperatorFromSymbol() + { + var compilation = CSharpCompilation.Create("Test", [SyntaxFactory.ParseSyntaxTree(""" + public class C + { + public static explicit operator int(C c) => 0; + } + """)]); + + var c = compilation.GetTypeByMetadataName("C"); + var op = c.GetMembers().OfType().Where(m => m.MethodKind == MethodKind.Conversion).Single(); + + VerifySyntax( + Generator.OperatorDeclaration(op), + "public static explicit operator global::System.Int32(global::C c)\r\n{\r\n}"); + } + [Fact] public void TestConstructorDeclaration() { From b5ff756ecc9719cbd975d4a32e0497f3add1893b Mon Sep 17 00:00:00 2001 From: "gel@microsoft.com" Date: Thu, 6 Feb 2025 17:34:45 -0800 Subject: [PATCH 114/305] Fix --- .../AbstractCopilotLspServiceDocumentRequestHandler.cs | 7 +++---- .../Handler/AbstractCopilotLspServiceRequestHandler.cs | 7 +++---- .../ExternalAccess/Copilot/Handler/RequestContext.cs | 4 ---- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/LanguageServer/ExternalAccess/Copilot/Handler/AbstractCopilotLspServiceDocumentRequestHandler.cs b/src/LanguageServer/ExternalAccess/Copilot/Handler/AbstractCopilotLspServiceDocumentRequestHandler.cs index 5fa8d73938234..d1d8b753b1d25 100644 --- a/src/LanguageServer/ExternalAccess/Copilot/Handler/AbstractCopilotLspServiceDocumentRequestHandler.cs +++ b/src/LanguageServer/ExternalAccess/Copilot/Handler/AbstractCopilotLspServiceDocumentRequestHandler.cs @@ -13,16 +13,15 @@ namespace Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot; internal abstract class AbstractCopilotLspServiceDocumentRequestHandler : ILspServiceDocumentRequestHandler { - public abstract bool RequiresLSPSolution { get; } public abstract Task HandleRequestAsync(TRequest request, RequestContext context, CancellationToken cancellationToken); public abstract Uri GetTextDocumentIdentifier(TRequest request); - public abstract bool MutatesSolutionState { get; } - bool IMethodHandler.MutatesSolutionState => MutatesSolutionState; - bool ISolutionRequiredHandler.RequiresLSPSolution => RequiresLSPSolution; + bool IMethodHandler.MutatesSolutionState => false; + bool ISolutionRequiredHandler.RequiresLSPSolution => true; TextDocumentIdentifier ITextDocumentIdentifierHandler.GetTextDocumentIdentifier(TRequest request) => new() { Uri = GetTextDocumentIdentifier(request) }; + Task IRequestHandler.HandleRequestAsync(TRequest request, LspRequestContext context, CancellationToken cancellationToken) => HandleRequestAsync(request, new RequestContext(context), cancellationToken); } diff --git a/src/LanguageServer/ExternalAccess/Copilot/Handler/AbstractCopilotLspServiceRequestHandler.cs b/src/LanguageServer/ExternalAccess/Copilot/Handler/AbstractCopilotLspServiceRequestHandler.cs index 826cd9278819b..5b84003e4bded 100644 --- a/src/LanguageServer/ExternalAccess/Copilot/Handler/AbstractCopilotLspServiceRequestHandler.cs +++ b/src/LanguageServer/ExternalAccess/Copilot/Handler/AbstractCopilotLspServiceRequestHandler.cs @@ -11,12 +11,11 @@ namespace Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot; internal abstract class AbstractCopilotLspServiceRequestHandler : ILspServiceRequestHandler { - public abstract bool MutatesSolutionState { get; } - public abstract bool RequiresLSPSolution { get; } public abstract Task HandleRequestAsync(TRequest request, RequestContext context, CancellationToken cancellationToken); - bool IMethodHandler.MutatesSolutionState => MutatesSolutionState; - bool ISolutionRequiredHandler.RequiresLSPSolution => RequiresLSPSolution; + bool IMethodHandler.MutatesSolutionState => false; + bool ISolutionRequiredHandler.RequiresLSPSolution => true; + Task IRequestHandler.HandleRequestAsync(TRequest request, LspRequestContext context, CancellationToken cancellationToken) => HandleRequestAsync(request, new RequestContext(context), cancellationToken); } diff --git a/src/LanguageServer/ExternalAccess/Copilot/Handler/RequestContext.cs b/src/LanguageServer/ExternalAccess/Copilot/Handler/RequestContext.cs index f177c84cbfeca..63838af6995f8 100644 --- a/src/LanguageServer/ExternalAccess/Copilot/Handler/RequestContext.cs +++ b/src/LanguageServer/ExternalAccess/Copilot/Handler/RequestContext.cs @@ -6,14 +6,10 @@ namespace Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot; internal readonly struct RequestContext(LspRequestContext context) { - /// - internal Workspace? Workspace => context.Workspace; /// internal Solution? Solution => context.Solution; /// internal Document? Document => context.Document; - /// - internal Document GetRequiredDocument() => context.GetRequiredDocument(); internal T GetRequiredService() where T : class => context.GetRequiredService(); } From 1a0521e884115fbfcb8a46adffcc2af3e6bf197f Mon Sep 17 00:00:00 2001 From: "gel@microsoft.com" Date: Thu, 6 Feb 2025 17:50:27 -0800 Subject: [PATCH 115/305] fix --- .../ExternalAccess/Copilot/Handler/RequestContext.cs | 1 + .../ExternalAccess/Copilot/InternalAPI.Unshipped.txt | 6 ------ 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/LanguageServer/ExternalAccess/Copilot/Handler/RequestContext.cs b/src/LanguageServer/ExternalAccess/Copilot/Handler/RequestContext.cs index 63838af6995f8..b2c9e058de73f 100644 --- a/src/LanguageServer/ExternalAccess/Copilot/Handler/RequestContext.cs +++ b/src/LanguageServer/ExternalAccess/Copilot/Handler/RequestContext.cs @@ -8,6 +8,7 @@ internal readonly struct RequestContext(LspRequestContext context) { /// internal Solution? Solution => context.Solution; + /// internal Document? Document => context.Document; diff --git a/src/LanguageServer/ExternalAccess/Copilot/InternalAPI.Unshipped.txt b/src/LanguageServer/ExternalAccess/Copilot/InternalAPI.Unshipped.txt index 10866b02b479c..6ae078bace2dc 100644 --- a/src/LanguageServer/ExternalAccess/Copilot/InternalAPI.Unshipped.txt +++ b/src/LanguageServer/ExternalAccess/Copilot/InternalAPI.Unshipped.txt @@ -1,12 +1,8 @@ #nullable enable abstract Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspServiceDocumentRequestHandler.GetTextDocumentIdentifier(TRequest request) -> System.Uri! abstract Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspServiceDocumentRequestHandler.HandleRequestAsync(TRequest request, Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.RequestContext context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! -abstract Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspServiceDocumentRequestHandler.MutatesSolutionState.get -> bool -abstract Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspServiceDocumentRequestHandler.RequiresLSPSolution.get -> bool abstract Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspServiceFactory.CreateILspService(Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.CopilotLspServices lspServices) -> Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspService! abstract Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspServiceRequestHandler.HandleRequestAsync(TRequest request, Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.RequestContext context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! -abstract Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspServiceRequestHandler.MutatesSolutionState.get -> bool -abstract Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspServiceRequestHandler.RequiresLSPSolution.get -> bool Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspService Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspService.AbstractCopilotLspService() -> void Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspServiceDocumentRequestHandler @@ -28,9 +24,7 @@ Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.ExportCopilotStatel Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.ExportCopilotStatelessLspServiceAttribute.ExportCopilotStatelessLspServiceAttribute(System.Type! type) -> void Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.RequestContext Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.RequestContext.Document.get -> Microsoft.CodeAnalysis.Document? -Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.RequestContext.GetRequiredDocument() -> Microsoft.CodeAnalysis.Document! Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.RequestContext.GetRequiredService() -> T! Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.RequestContext.RequestContext() -> void Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.RequestContext.RequestContext(Microsoft.CodeAnalysis.LanguageServer.Handler.RequestContext context) -> void Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.RequestContext.Solution.get -> Microsoft.CodeAnalysis.Solution? -Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.RequestContext.Workspace.get -> Microsoft.CodeAnalysis.Workspace? From c196fae0ac75504c3f7b2c814bab9af222036229 Mon Sep 17 00:00:00 2001 From: "gel@microsoft.com" Date: Thu, 6 Feb 2025 18:12:09 -0800 Subject: [PATCH 116/305] fix --- .../ExternalAccess/Copilot/GlobalUsings.cs | 5 ----- ...CopilotLspServiceDocumentRequestHandler.cs | 6 ++++-- ...AbstractCopilotLspServiceRequestHandler.cs | 21 ------------------- .../Copilot/Handler/RequestContext.cs | 8 ++++--- .../Copilot/InternalAPI.Unshipped.txt | 13 ++++-------- .../AbstractCopilotLspServiceFactory.cs | 4 ++-- .../Copilot/LspServices/CopilotLspServices.cs | 3 --- .../LspServices/CopilotMethodAttribute.cs | 2 +- ...ExportCopilotLspServiceFactoryAttribute.cs | 2 +- ...portCopilotStatelessLspServiceAttribute.cs | 2 +- ...lotLspService.cs => ICopilotLspService.cs} | 2 +- 11 files changed, 19 insertions(+), 49 deletions(-) delete mode 100644 src/LanguageServer/ExternalAccess/Copilot/GlobalUsings.cs delete mode 100644 src/LanguageServer/ExternalAccess/Copilot/Handler/AbstractCopilotLspServiceRequestHandler.cs rename src/LanguageServer/ExternalAccess/Copilot/LspServices/{AbstractCopilotLspService.cs => ICopilotLspService.cs} (83%) diff --git a/src/LanguageServer/ExternalAccess/Copilot/GlobalUsings.cs b/src/LanguageServer/ExternalAccess/Copilot/GlobalUsings.cs deleted file mode 100644 index 44c55592eea35..0000000000000 --- a/src/LanguageServer/ExternalAccess/Copilot/GlobalUsings.cs +++ /dev/null @@ -1,5 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -global using LspRequestContext = Microsoft.CodeAnalysis.LanguageServer.Handler.RequestContext; diff --git a/src/LanguageServer/ExternalAccess/Copilot/Handler/AbstractCopilotLspServiceDocumentRequestHandler.cs b/src/LanguageServer/ExternalAccess/Copilot/Handler/AbstractCopilotLspServiceDocumentRequestHandler.cs index d1d8b753b1d25..59e8e7df7c49f 100644 --- a/src/LanguageServer/ExternalAccess/Copilot/Handler/AbstractCopilotLspServiceDocumentRequestHandler.cs +++ b/src/LanguageServer/ExternalAccess/Copilot/Handler/AbstractCopilotLspServiceDocumentRequestHandler.cs @@ -9,18 +9,20 @@ using Microsoft.CommonLanguageServerProtocol.Framework; using Roslyn.LanguageServer.Protocol; +using LspRequestContext = Microsoft.CodeAnalysis.LanguageServer.Handler.RequestContext; + namespace Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot; internal abstract class AbstractCopilotLspServiceDocumentRequestHandler : ILspServiceDocumentRequestHandler { public abstract Task HandleRequestAsync(TRequest request, RequestContext context, CancellationToken cancellationToken); - public abstract Uri GetTextDocumentIdentifier(TRequest request); + public abstract Uri GetTextDocumentUri(TRequest request); bool IMethodHandler.MutatesSolutionState => false; bool ISolutionRequiredHandler.RequiresLSPSolution => true; TextDocumentIdentifier ITextDocumentIdentifierHandler.GetTextDocumentIdentifier(TRequest request) - => new() { Uri = GetTextDocumentIdentifier(request) }; + => new() { Uri = GetTextDocumentUri(request) }; Task IRequestHandler.HandleRequestAsync(TRequest request, LspRequestContext context, CancellationToken cancellationToken) => HandleRequestAsync(request, new RequestContext(context), cancellationToken); diff --git a/src/LanguageServer/ExternalAccess/Copilot/Handler/AbstractCopilotLspServiceRequestHandler.cs b/src/LanguageServer/ExternalAccess/Copilot/Handler/AbstractCopilotLspServiceRequestHandler.cs deleted file mode 100644 index 5b84003e4bded..0000000000000 --- a/src/LanguageServer/ExternalAccess/Copilot/Handler/AbstractCopilotLspServiceRequestHandler.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.LanguageServer.Handler; -using Microsoft.CommonLanguageServerProtocol.Framework; - -namespace Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot; - -internal abstract class AbstractCopilotLspServiceRequestHandler : ILspServiceRequestHandler -{ - public abstract Task HandleRequestAsync(TRequest request, RequestContext context, CancellationToken cancellationToken); - - bool IMethodHandler.MutatesSolutionState => false; - bool ISolutionRequiredHandler.RequiresLSPSolution => true; - - Task IRequestHandler.HandleRequestAsync(TRequest request, LspRequestContext context, CancellationToken cancellationToken) - => HandleRequestAsync(request, new RequestContext(context), cancellationToken); -} diff --git a/src/LanguageServer/ExternalAccess/Copilot/Handler/RequestContext.cs b/src/LanguageServer/ExternalAccess/Copilot/Handler/RequestContext.cs index b2c9e058de73f..86b7f50b96346 100644 --- a/src/LanguageServer/ExternalAccess/Copilot/Handler/RequestContext.cs +++ b/src/LanguageServer/ExternalAccess/Copilot/Handler/RequestContext.cs @@ -2,15 +2,17 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using LspRequestContext = Microsoft.CodeAnalysis.LanguageServer.Handler.RequestContext; + namespace Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot; internal readonly struct RequestContext(LspRequestContext context) { /// - internal Solution? Solution => context.Solution; + public Solution? Solution => context.Solution; /// - internal Document? Document => context.Document; + public Document? Document => context.Document; - internal T GetRequiredService() where T : class => context.GetRequiredService(); + public T GetRequiredService() where T : class => context.GetRequiredService(); } diff --git a/src/LanguageServer/ExternalAccess/Copilot/InternalAPI.Unshipped.txt b/src/LanguageServer/ExternalAccess/Copilot/InternalAPI.Unshipped.txt index 6ae078bace2dc..4242dae985e23 100644 --- a/src/LanguageServer/ExternalAccess/Copilot/InternalAPI.Unshipped.txt +++ b/src/LanguageServer/ExternalAccess/Copilot/InternalAPI.Unshipped.txt @@ -1,20 +1,14 @@ #nullable enable -abstract Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspServiceDocumentRequestHandler.GetTextDocumentIdentifier(TRequest request) -> System.Uri! +abstract Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspServiceDocumentRequestHandler.GetTextDocumentUri(TRequest request) -> System.Uri! abstract Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspServiceDocumentRequestHandler.HandleRequestAsync(TRequest request, Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.RequestContext context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! -abstract Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspServiceFactory.CreateILspService(Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.CopilotLspServices lspServices) -> Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspService! -abstract Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspServiceRequestHandler.HandleRequestAsync(TRequest request, Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.RequestContext context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! -Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspService -Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspService.AbstractCopilotLspService() -> void +abstract Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspServiceFactory.CreateCopilotLspService(Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.CopilotLspServices lspServices) -> Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.ICopilotLspService! Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspServiceDocumentRequestHandler Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspServiceDocumentRequestHandler.AbstractCopilotLspServiceDocumentRequestHandler() -> void Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspServiceFactory Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspServiceFactory.AbstractCopilotLspServiceFactory() -> void -Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspServiceRequestHandler -Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspServiceRequestHandler.AbstractCopilotLspServiceRequestHandler() -> void Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.CopilotLspServices Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.CopilotLspServices.CopilotLspServices() -> void Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.CopilotLspServices.CopilotLspServices(Microsoft.CodeAnalysis.LanguageServer.LspServices! lspServices) -> void -Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.CopilotLspServices.GetRequiredService() -> T Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.CopilotLspServices.GetService() -> T? Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.CopilotMethodAttribute Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.CopilotMethodAttribute.CopilotMethodAttribute(string! method) -> void @@ -22,9 +16,10 @@ Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.ExportCopilotLspSer Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.ExportCopilotLspServiceFactoryAttribute.ExportCopilotLspServiceFactoryAttribute(System.Type! type) -> void Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.ExportCopilotStatelessLspServiceAttribute Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.ExportCopilotStatelessLspServiceAttribute.ExportCopilotStatelessLspServiceAttribute(System.Type! type) -> void +Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.ICopilotLspService Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.RequestContext Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.RequestContext.Document.get -> Microsoft.CodeAnalysis.Document? Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.RequestContext.GetRequiredService() -> T! Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.RequestContext.RequestContext() -> void Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.RequestContext.RequestContext(Microsoft.CodeAnalysis.LanguageServer.Handler.RequestContext context) -> void -Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.RequestContext.Solution.get -> Microsoft.CodeAnalysis.Solution? +Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.RequestContext.Solution.get -> Microsoft.CodeAnalysis.Solution? \ No newline at end of file diff --git a/src/LanguageServer/ExternalAccess/Copilot/LspServices/AbstractCopilotLspServiceFactory.cs b/src/LanguageServer/ExternalAccess/Copilot/LspServices/AbstractCopilotLspServiceFactory.cs index c6820713aacbc..92229f3afeac4 100644 --- a/src/LanguageServer/ExternalAccess/Copilot/LspServices/AbstractCopilotLspServiceFactory.cs +++ b/src/LanguageServer/ExternalAccess/Copilot/LspServices/AbstractCopilotLspServiceFactory.cs @@ -6,10 +6,10 @@ namespace Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot; internal abstract class AbstractCopilotLspServiceFactory : ILspServiceFactory { - public abstract AbstractCopilotLspService CreateILspService(CopilotLspServices lspServices); + public abstract ICopilotLspService CreateCopilotLspService(CopilotLspServices lspServices); ILspService ILspServiceFactory.CreateILspService(LspServices lspServices, WellKnownLspServerKinds serverKind) { - return CreateILspService(new CopilotLspServices(lspServices)); + return CreateCopilotLspService(new CopilotLspServices(lspServices)); } } diff --git a/src/LanguageServer/ExternalAccess/Copilot/LspServices/CopilotLspServices.cs b/src/LanguageServer/ExternalAccess/Copilot/LspServices/CopilotLspServices.cs index 2095ca0c892aa..b3b4030c31b38 100644 --- a/src/LanguageServer/ExternalAccess/Copilot/LspServices/CopilotLspServices.cs +++ b/src/LanguageServer/ExternalAccess/Copilot/LspServices/CopilotLspServices.cs @@ -6,9 +6,6 @@ namespace Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot; internal readonly struct CopilotLspServices(LspServices lspServices) { - public T GetRequiredService() where T : notnull - => lspServices.GetRequiredService(); - public T? GetService() where T : notnull => lspServices.GetService(); } diff --git a/src/LanguageServer/ExternalAccess/Copilot/LspServices/CopilotMethodAttribute.cs b/src/LanguageServer/ExternalAccess/Copilot/LspServices/CopilotMethodAttribute.cs index 3ba647e8cfd28..decb2ed29d347 100644 --- a/src/LanguageServer/ExternalAccess/Copilot/LspServices/CopilotMethodAttribute.cs +++ b/src/LanguageServer/ExternalAccess/Copilot/LspServices/CopilotMethodAttribute.cs @@ -6,4 +6,4 @@ namespace Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot; -internal class CopilotMethodAttribute(string method) : MethodAttribute(method); +internal sealed class CopilotMethodAttribute(string method) : MethodAttribute(method); diff --git a/src/LanguageServer/ExternalAccess/Copilot/LspServices/ExportCopilotLspServiceFactoryAttribute.cs b/src/LanguageServer/ExternalAccess/Copilot/LspServices/ExportCopilotLspServiceFactoryAttribute.cs index c0df0feb06f67..f3b3197897d93 100644 --- a/src/LanguageServer/ExternalAccess/Copilot/LspServices/ExportCopilotLspServiceFactoryAttribute.cs +++ b/src/LanguageServer/ExternalAccess/Copilot/LspServices/ExportCopilotLspServiceFactoryAttribute.cs @@ -7,5 +7,5 @@ namespace Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot; -internal class ExportCopilotLspServiceFactoryAttribute(Type type) : +internal sealed class ExportCopilotLspServiceFactoryAttribute(Type type) : ExportCSharpVisualBasicLspServiceFactoryAttribute(type, WellKnownLspServerKinds.Any); diff --git a/src/LanguageServer/ExternalAccess/Copilot/LspServices/ExportCopilotStatelessLspServiceAttribute.cs b/src/LanguageServer/ExternalAccess/Copilot/LspServices/ExportCopilotStatelessLspServiceAttribute.cs index 82ef867d864d6..289af03428b10 100644 --- a/src/LanguageServer/ExternalAccess/Copilot/LspServices/ExportCopilotStatelessLspServiceAttribute.cs +++ b/src/LanguageServer/ExternalAccess/Copilot/LspServices/ExportCopilotStatelessLspServiceAttribute.cs @@ -7,5 +7,5 @@ namespace Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot; -internal class ExportCopilotStatelessLspServiceAttribute(Type type) : +internal sealed class ExportCopilotStatelessLspServiceAttribute(Type type) : ExportCSharpVisualBasicStatelessLspServiceAttribute(type, WellKnownLspServerKinds.Any); diff --git a/src/LanguageServer/ExternalAccess/Copilot/LspServices/AbstractCopilotLspService.cs b/src/LanguageServer/ExternalAccess/Copilot/LspServices/ICopilotLspService.cs similarity index 83% rename from src/LanguageServer/ExternalAccess/Copilot/LspServices/AbstractCopilotLspService.cs rename to src/LanguageServer/ExternalAccess/Copilot/LspServices/ICopilotLspService.cs index 07e00b91bcb1e..11774cd3ab9dd 100644 --- a/src/LanguageServer/ExternalAccess/Copilot/LspServices/AbstractCopilotLspService.cs +++ b/src/LanguageServer/ExternalAccess/Copilot/LspServices/ICopilotLspService.cs @@ -4,6 +4,6 @@ namespace Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot; -internal class AbstractCopilotLspService : ILspService +internal interface ICopilotLspService : ILspService { } From a68f3e4250c42d117e500da18c11dca6ba088c19 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Thu, 6 Feb 2025 18:40:22 -0800 Subject: [PATCH 117/305] Update to fix the `checked` operator --- .../Syntax.xml.Internal.Generated.cs | 6 ++---- .../Syntax.xml.Main.Generated.cs | 3 +-- src/Compilers/CSharp/Portable/Syntax/Syntax.xml | 1 - .../CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs | 4 ++-- .../CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs | 6 +++--- 5 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs index 0c6bc0fa16bd4..78d8aedb8b8f2 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs @@ -31740,8 +31740,7 @@ public OperatorDeclarationSyntax OperatorDeclaration(CoreSyntax.SyntaxList - diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs index 2c005f7ad2bd6..d5c6575451d44 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs @@ -277,7 +277,7 @@ private protected override SyntaxNode OperatorDeclaration(string operatorName, b var modifierList = AsModifierList(accessibility, modifiers, SyntaxKind.OperatorDeclaration); var attributes = default(SyntaxList); - if (operatorName is WellKnownMemberNames.ImplicitConversionName or WellKnownMemberNames.ExplicitConversionName) + if (operatorName is WellKnownMemberNames.ImplicitConversionName or WellKnownMemberNames.ExplicitConversionName or WellKnownMemberNames.CheckedExplicitConversionName) { var isImplicit = operatorName is WellKnownMemberNames.ImplicitConversionName; return SyntaxFactory.ConversionOperatorDeclaration( @@ -285,7 +285,7 @@ private protected override SyntaxNode OperatorDeclaration(string operatorName, b isImplicit ? ImplicitKeyword : ExplicitKeyword, explicitInterfaceSpecifier: null, OperatorKeyword, - checkedKeyword: default, + checkedKeyword: CSharp.SyntaxFacts.IsCheckedOperator(operatorName) ? CheckedKeyword : default, returnTypeNode, parameterList, body, expressionBody: null, semicolon); } else diff --git a/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs b/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs index 841028bfa9058..9883526bd0041 100644 --- a/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs +++ b/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs @@ -1002,12 +1002,12 @@ public void TestConversionOperatorDeclaration() } [Fact, WorkItem(77101, "https://github.com/dotnet/roslyn/issues/77101")] - public void TestExplicitOperatorFromSymbol() + public void TestExplicitCheckedOperatorFromSymbol() { var compilation = CSharpCompilation.Create("Test", [SyntaxFactory.ParseSyntaxTree(""" public class C { - public static explicit operator int(C c) => 0; + public static explicit operator checked int(C c) => 0; } """)]); @@ -1016,7 +1016,7 @@ public class C VerifySyntax( Generator.OperatorDeclaration(op), - "public static explicit operator global::System.Int32(global::C c)\r\n{\r\n}"); + "public static explicit operator checked global::System.Int32(global::C c)\r\n{\r\n}"); } [Fact] From 4253f32d7acd1332c6570ba342837fdcfe07d867 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Thu, 6 Feb 2025 19:47:53 -0800 Subject: [PATCH 118/305] Move ArrayBuilder.GetInstance helpers to Microsoft.CodeAnalysis.PooledObjects package (#76971) --- .../Portable/Microsoft.CodeAnalysis.csproj | 4 ++ .../PooledObjects/ArrayBuilder.cs | 47 ++++++++++++++++++ .../PooledObjects}/IPooled.cs | 0 ...osoft.CodeAnalysis.PooledObjects.projitems | 2 + .../PooledObjects/PooledDisposer.cs | 18 +++++++ ...rosoft.CodeAnalysis.InteractiveHost.csproj | 2 + ...alysis.Workspaces.MSBuild.BuildHost.csproj | 1 - .../Core/CompilerExtensions.projitems | 2 - .../Compiler/Core/ObjectPools/ArrayBuilder.cs | 48 ------------------- .../Core/ObjectPools/PooledDisposer.cs | 4 +- 10 files changed, 74 insertions(+), 54 deletions(-) rename src/{Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools => Dependencies/PooledObjects}/IPooled.cs (100%) create mode 100644 src/Dependencies/PooledObjects/PooledDisposer.cs delete mode 100644 src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/ArrayBuilder.cs diff --git a/src/Compilers/Core/Portable/Microsoft.CodeAnalysis.csproj b/src/Compilers/Core/Portable/Microsoft.CodeAnalysis.csproj index 94c47e9a01e9e..dfe5d9e772867 100644 --- a/src/Compilers/Core/Portable/Microsoft.CodeAnalysis.csproj +++ b/src/Compilers/Core/Portable/Microsoft.CodeAnalysis.csproj @@ -7,6 +7,10 @@ true $(NetRoslynSourceBuild);netstandard2.0 $(DefineConstants);COMPILERCORE + + + $(DefineConstants);MICROSOFT_CODEANALYSIS_POOLEDOBJECTS_NO_POOLED_DISPOSER + true full true diff --git a/src/Dependencies/PooledObjects/ArrayBuilder.cs b/src/Dependencies/PooledObjects/ArrayBuilder.cs index c76055bead00d..35ab00e03a50b 100644 --- a/src/Dependencies/PooledObjects/ArrayBuilder.cs +++ b/src/Dependencies/PooledObjects/ArrayBuilder.cs @@ -16,6 +16,9 @@ namespace Microsoft.CodeAnalysis.PooledObjects [DebuggerDisplay("Count = {Count,nq}")] [DebuggerTypeProxy(typeof(ArrayBuilder<>.DebuggerProxy))] internal sealed partial class ArrayBuilder : IReadOnlyCollection, IReadOnlyList, ICollection +#if !MICROSOFT_CODEANALYSIS_POOLEDOBJECTS_NO_POOLED_DISPOSER + , IPooled +#endif { /// /// See for an explanation of this constant value. @@ -720,5 +723,49 @@ public ImmutableArray SelectDistinct(Func selector) set.Free(); return result.ToImmutableAndFree(); } + +#if !MICROSOFT_CODEANALYSIS_POOLEDOBJECTS_NO_POOLED_DISPOSER + + private static readonly ObjectPool> s_keepLargeInstancesPool = CreatePool(); + + public static PooledDisposer> GetInstance(out ArrayBuilder instance) + => GetInstance(discardLargeInstances: true, out instance); + + public static PooledDisposer> GetInstance(int capacity, out ArrayBuilder instance) + { + instance = GetInstance(capacity); + return new PooledDisposer>(instance); + } + + public static PooledDisposer> GetInstance(int capacity, T fillWithValue, out ArrayBuilder instance) + { + instance = GetInstance(capacity, fillWithValue); + return new PooledDisposer>(instance); + } + + public static PooledDisposer> GetInstance(bool discardLargeInstances, out ArrayBuilder instance) + { + // If we're discarding large instances (the default behavior), then just use the normal pool. If we're not, use + // a specific pool so that *other* normal callers don't accidentally get it and discard it. + instance = discardLargeInstances ? GetInstance() : s_keepLargeInstancesPool.Allocate(); + return new PooledDisposer>(instance, discardLargeInstances); + } + + void IPooled.Free(bool discardLargeInstances) + { + // If we're discarding large instances, use the default behavior (which already does that). Otherwise, always + // clear and free the instance back to its originating pool. + if (discardLargeInstances) + { + Free(); + } + else + { + this.Clear(); + _pool?.Free(this); + } + } + +#endif } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/IPooled.cs b/src/Dependencies/PooledObjects/IPooled.cs similarity index 100% rename from src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/IPooled.cs rename to src/Dependencies/PooledObjects/IPooled.cs diff --git a/src/Dependencies/PooledObjects/Microsoft.CodeAnalysis.PooledObjects.projitems b/src/Dependencies/PooledObjects/Microsoft.CodeAnalysis.PooledObjects.projitems index 97a2e3f5ca08f..3b563ade211f9 100644 --- a/src/Dependencies/PooledObjects/Microsoft.CodeAnalysis.PooledObjects.projitems +++ b/src/Dependencies/PooledObjects/Microsoft.CodeAnalysis.PooledObjects.projitems @@ -12,9 +12,11 @@ + + diff --git a/src/Dependencies/PooledObjects/PooledDisposer.cs b/src/Dependencies/PooledObjects/PooledDisposer.cs new file mode 100644 index 0000000000000..e497c4dbd16e9 --- /dev/null +++ b/src/Dependencies/PooledObjects/PooledDisposer.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#if !MICROSOFT_CODEANALYSIS_POOLEDOBJECTS_NO_POOLED_DISPOSER +using System; + +namespace Microsoft.CodeAnalysis.PooledObjects; + +internal readonly partial struct PooledDisposer( + TPoolable instance, + bool discardLargeInstances = true) : IDisposable + where TPoolable : class, IPooled +{ + void IDisposable.Dispose() + => instance?.Free(discardLargeInstances); +} +#endif diff --git a/src/Interactive/Host/Microsoft.CodeAnalysis.InteractiveHost.csproj b/src/Interactive/Host/Microsoft.CodeAnalysis.InteractiveHost.csproj index 067bc093a599f..b68db02519f4d 100644 --- a/src/Interactive/Host/Microsoft.CodeAnalysis.InteractiveHost.csproj +++ b/src/Interactive/Host/Microsoft.CodeAnalysis.InteractiveHost.csproj @@ -59,6 +59,8 @@ + + diff --git a/src/Workspaces/MSBuild/BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj b/src/Workspaces/MSBuild/BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj index 8101f56b8be6c..12380632d540f 100644 --- a/src/Workspaces/MSBuild/BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj +++ b/src/Workspaces/MSBuild/BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj @@ -67,7 +67,6 @@ - diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems index eb02e14e9ba88..97b4da66d0aed 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems @@ -604,9 +604,7 @@ - - diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/ArrayBuilder.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/ArrayBuilder.cs deleted file mode 100644 index 1d6d7630eb2d9..0000000000000 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/ArrayBuilder.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.CodeAnalysis.PooledObjects; - -internal sealed partial class ArrayBuilder : IPooled -{ - private static readonly ObjectPool> s_keepLargeInstancesPool = CreatePool(); - - public static PooledDisposer> GetInstance(out ArrayBuilder instance) - => GetInstance(discardLargeInstances: true, out instance); - - public static PooledDisposer> GetInstance(int capacity, out ArrayBuilder instance) - { - instance = GetInstance(capacity); - return new PooledDisposer>(instance); - } - - public static PooledDisposer> GetInstance(int capacity, T fillWithValue, out ArrayBuilder instance) - { - instance = GetInstance(capacity, fillWithValue); - return new PooledDisposer>(instance); - } - - public static PooledDisposer> GetInstance(bool discardLargeInstances, out ArrayBuilder instance) - { - // If we're discarding large instances (the default behavior), then just use the normal pool. If we're not, use - // a specific pool so that *other* normal callers don't accidentally get it and discard it. - instance = discardLargeInstances ? GetInstance() : s_keepLargeInstancesPool.Allocate(); - return new PooledDisposer>(instance, discardLargeInstances); - } - - void IPooled.Free(bool discardLargeInstances) - { - // If we're discarding large instances, use the default behavior (which already does that). Otherwise, always - // clear and free the instance back to its originating pool. - if (discardLargeInstances) - { - Free(); - } - else - { - this.Clear(); - _pool?.Free(this); - } - } -} diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledDisposer.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledDisposer.cs index 895dd39043fa7..04475f8160883 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledDisposer.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledDisposer.cs @@ -8,9 +8,7 @@ namespace Microsoft.CodeAnalysis.PooledObjects; [NonCopyable] -internal readonly struct PooledDisposer(TPoolable instance, bool discardLargeInstances = true) : IDisposable +internal partial struct PooledDisposer where TPoolable : class, IPooled { - void IDisposable.Dispose() - => instance?.Free(discardLargeInstances); } From b2862ce0521dbc71ede95856d3c95a6becee88b2 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Fri, 7 Feb 2025 09:27:46 -0800 Subject: [PATCH 119/305] Handle nullable postcondition for non-static local function inside field initializer (#76716) --- .../Portable/FlowAnalysis/NullableWalker.cs | 38 +++--- .../Semantic/Semantics/LocalFunctionTests.cs | 121 ++++++++++++++++++ 2 files changed, 144 insertions(+), 15 deletions(-) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 11a0e34b55554..29140c1a41d71 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -1172,6 +1172,10 @@ void makeMemberMaybeNull(MethodSymbol method, string memberName) } } + /// + /// Gets a slot for a static member, or a member of 'this', which is being referenced by a postcondition. + /// Used for "declaration-site" analysis of MemberNotNullAttributes. + /// private int GetSlotForMemberPostCondition(Symbol member) { if (member.Kind != SymbolKind.Field && @@ -1181,22 +1185,20 @@ private int GetSlotForMemberPostCondition(Symbol member) return -1; } - int containingSlot = GetReceiverSlotForMemberPostConditions(_symbol as MethodSymbol); - - if (containingSlot < 0) - { - return -1; - } - + int containingSlot; if (member.IsStatic) { - // Trying to access a static member from a non-static context containingSlot = 0; } - else if (containingSlot == 0) + else { - // Trying to access an instance member from a static context - return -1; + containingSlot = GetReceiverSlotForMemberPostConditions(_symbol as MethodSymbol); + if (containingSlot <= 0) + { + // Either trying to access an instance member from a static context, + // or an invalid slot (-1) was returned + return -1; + } } return GetOrCreateSlot(member, containingSlot); @@ -7147,6 +7149,10 @@ bool tryShortCircuitTargetTypedExpression(BoundExpression argument, BoundExpress } } + /// + /// Applies the member postconditions of to members of or to the appropriate static members. + /// Used for the "use-site" analysis of MemberNotNullAttributes. + /// private void ApplyMemberPostConditions(BoundExpression? receiverOpt, MethodSymbol? method) { if (method is null) @@ -7166,9 +7172,11 @@ private void ApplyMemberPostConditions(BoundExpression? receiverOpt, MethodSymbo ApplyMemberPostConditions(receiverSlot, method); } - // For an instance method, or a non-static local function in an instance method, returns the slot for the `this` parameter - // For a static method, or a static local function, or a local function in a static method, returns 0 - // Otherwise, returns -1 + /// + /// Returns -1 when a null method is passed. In this case there are definitely no member postconditions to apply. + /// Returns the slot for the `this` parameter for an instance method, or a non-static local function in an instance method. + /// Otherwise, returns 0, because postconditions applying to static members of the containing type could be present. + /// private int GetReceiverSlotForMemberPostConditions(MethodSymbol? method) { if (method is null) @@ -7196,7 +7204,7 @@ private int GetReceiverSlotForMemberPostConditions(MethodSymbol? method) return GetOrCreateSlot(thisParameter); } - return -1; + return 0; } private void ApplyMemberPostConditions(int receiverSlot, MethodSymbol method) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs index 44125b156e1d4..a5b2d45978332 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs @@ -10588,5 +10588,126 @@ void M() // [A(p)] void F() { } Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "F").WithArguments("F").WithLocation(13, 21)); } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/76528")] + [InlineData("")] + [InlineData("static ")] + public void Repro76528(string modifiers) + { + var source = $$""" + #nullable enable + + using System; + using System.Diagnostics.CodeAnalysis; + + public class C + { + public static string? _field; + + public Action Prop1 { get; } = () => + { + init(); + Console.WriteLine(_field.Length); + + [MemberNotNull(nameof(_field))] + {{modifiers}}void init() => _field ??= ""; + }; + } + """; + var comp = CreateCompilation([source, MemberNotNullAttributeDefinition]); + comp.VerifyEmitDiagnostics(); + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/76528")] + [InlineData("")] + [InlineData("static ")] + public void Repro76528_FieldInitializer(string modifiers) + { + var source = $$""" + #nullable enable + + using System; + using System.Diagnostics.CodeAnalysis; + + public class C + { + public static string? _field; + + public Action _field2 = () => + { + init(); + Console.WriteLine(_field.Length); + + [MemberNotNull(nameof(_field))] + {{modifiers}}void init() => _field ??= ""; + }; + } + """; + var comp = CreateCompilation([source, MemberNotNullAttributeDefinition]); + comp.VerifyEmitDiagnostics(); + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/76528")] + [InlineData("")] + [InlineData("static ")] + public void Repro76528_StaticLambda(string modifiers) + { + var source = $$""" + #nullable enable + + using System; + using System.Diagnostics.CodeAnalysis; + + public class C + { + public static string? _field; + + public Action Prop1 { get; } = static () => + { + init(); + Console.WriteLine(_field.Length); + + [MemberNotNull(nameof(_field))] + {{modifiers}}void init() => _field ??= ""; + }; + } + """; + var comp = CreateCompilation([source, MemberNotNullAttributeDefinition]); + comp.VerifyEmitDiagnostics(); + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/76528")] + [CombinatorialData] + public void Repro76528_FieldInitializer_PrimaryConstructor(bool isStatic) + { + var source = $$""" + #nullable enable + + using System; + using System.Diagnostics.CodeAnalysis; + + public class C(string p) + { + public static string? _field; + + public Action _field2 = () => + { + init(); + Console.WriteLine(_field.Length); + + [MemberNotNull(nameof(_field))] + {{(isStatic ? "static " : "")}}void init() => _field ??= p; + }; + + string M() => p; + } + """; + var comp = CreateCompilation([source, MemberNotNullAttributeDefinition]); + comp.VerifyEmitDiagnostics(isStatic ? [ + // (16,42): error CS8421: A static local function cannot contain a reference to 'p'. + // static void init() => _field ??= p; + Diagnostic(ErrorCode.ERR_StaticLocalFunctionCannotCaptureVariable, "p").WithArguments("p").WithLocation(16, 42) + ] : []); + } } } From ff8346d311dcdb51feafe3ea7887c6ff59453cb0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 09:28:10 -0800 Subject: [PATCH 120/305] Remove arg --- ...osticAnalyzerService_IncrementalAnalyzer.cs | 5 +---- ...agnosticIncrementalAnalyzer.StateManager.cs | 18 ++++-------------- .../EngineV2/DiagnosticIncrementalAnalyzer.cs | 4 +--- 3 files changed, 6 insertions(+), 21 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs index 078135d1990a2..619650052b2ad 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs @@ -2,9 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using Microsoft.CodeAnalysis.Diagnostics.EngineV2; -using Microsoft.CodeAnalysis.Host.Mef; namespace Microsoft.CodeAnalysis.Diagnostics; @@ -15,13 +13,12 @@ public DiagnosticIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspa return _map.GetValue(workspace, _createIncrementalAnalyzer); } - [Obsolete(MefConstruction.FactoryMethodMessage, error: true)] private DiagnosticIncrementalAnalyzer CreateIncrementalAnalyzerCallback(Workspace workspace) { // subscribe to active context changed event for new workspace workspace.DocumentActiveContextChanged += OnDocumentActiveContextChanged; - return new DiagnosticIncrementalAnalyzer(this, workspace, AnalyzerInfoCache, this.GlobalOptions); + return new DiagnosticIncrementalAnalyzer(this, AnalyzerInfoCache, this.GlobalOptions); } private void OnDocumentActiveContextChanged(object? sender, DocumentActiveContextChangedEventArgs e) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs index bf33986170a85..d4a8d5b4df0a4 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs @@ -20,22 +20,21 @@ internal partial class DiagnosticIncrementalAnalyzer /// /// This is in charge of anything related to /// - private partial class StateManager + private partial class StateManager(DiagnosticAnalyzerInfoCache analyzerInfoCache) { - private readonly Workspace _workspace; - private readonly DiagnosticAnalyzerInfoCache _analyzerInfoCache; + private readonly DiagnosticAnalyzerInfoCache _analyzerInfoCache = analyzerInfoCache; /// /// Analyzers supplied by the host (IDE). These are built-in to the IDE, the compiler, or from an installed IDE extension (VSIX). /// Maps language name to the analyzers and their state. /// - private ImmutableDictionary _hostAnalyzerStateMap; + private ImmutableDictionary _hostAnalyzerStateMap = ImmutableDictionary.Empty; /// /// Analyzers referenced by the project via a PackageReference. Updates are protected by _projectAnalyzerStateMapGuard. /// ImmutableDictionary used to present a safe, non-immutable view to users. /// - private ImmutableDictionary _projectAnalyzerStateMap; + private ImmutableDictionary _projectAnalyzerStateMap = ImmutableDictionary.Empty; /// /// Guard around updating _projectAnalyzerStateMap. This is used in UpdateProjectStateSets to avoid @@ -48,15 +47,6 @@ private partial class StateManager /// public event EventHandler? ProjectAnalyzerReferenceChanged; - public StateManager(Workspace workspace, DiagnosticAnalyzerInfoCache analyzerInfoCache) - { - _workspace = workspace; - _analyzerInfoCache = analyzerInfoCache; - - _hostAnalyzerStateMap = ImmutableDictionary.Empty; - _projectAnalyzerStateMap = ImmutableDictionary.Empty; - } - /// /// Return s for the given . /// This will never create new but will return ones already created. diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index 941b3368c11cf..a12fe5a8aa595 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -30,10 +30,8 @@ internal partial class DiagnosticIncrementalAnalyzer internal DiagnosticAnalyzerService AnalyzerService { get; } - [Obsolete(MefConstruction.FactoryMethodMessage, error: true)] public DiagnosticIncrementalAnalyzer( DiagnosticAnalyzerService analyzerService, - Workspace workspace, DiagnosticAnalyzerInfoCache analyzerInfoCache, IGlobalOptionService globalOptionService) { @@ -42,7 +40,7 @@ public DiagnosticIncrementalAnalyzer( AnalyzerService = analyzerService; GlobalOptions = globalOptionService; - _stateManager = new StateManager(workspace, analyzerInfoCache); + _stateManager = new StateManager(analyzerInfoCache); _stateManager.ProjectAnalyzerReferenceChanged += OnProjectAnalyzerReferenceChanged; var enabled = globalOptionService.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler); From 9e308854b403b7f033ce0cb393cdac697e56dad3 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 09:51:04 -0800 Subject: [PATCH 121/305] Use common helper for diagnostics --- .../DiagnosticIncrementalAnalyzer.CompilationManager.cs | 7 ++++++- .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 9 ++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs index 964a0134e053b..b13fd1018622e 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs @@ -13,6 +13,11 @@ internal partial class DiagnosticAnalyzerService private partial class DiagnosticIncrementalAnalyzer { private static Task CreateCompilationWithAnalyzersAsync(Project project, ImmutableArray stateSets, bool crashOnAnalyzerException, CancellationToken cancellationToken) - => DocumentAnalysisExecutor.CreateCompilationWithAnalyzersAsync(project, stateSets.SelectAsArray(s => !s.IsHostAnalyzer, s => s.Analyzer), stateSets.SelectAsArray(s => s.IsHostAnalyzer, s => s.Analyzer), crashOnAnalyzerException, cancellationToken); + => DocumentAnalysisExecutor.CreateCompilationWithAnalyzersAsync( + project, + stateSets.SelectAsArray(s => !s.IsHostAnalyzer, s => s.Analyzer), + stateSets.SelectAsArray(s => s.IsHostAnalyzer, s => s.Analyzer), + crashOnAnalyzerException, + cancellationToken); } } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index ebe94f8eec459..04d76624c5176 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -25,13 +25,8 @@ public async Task> ForceAnalyzeProjectAsync(Proje var stateSetsForProject = await _stateManager.GetOrCreateStateSetsAsync(project, cancellationToken).ConfigureAwait(false); var stateSets = GetStateSetsForFullSolutionAnalysis(stateSetsForProject, project); - // PERF: get analyzers that are not suppressed and marked as open file only - // this is perf optimization. we cache these result since we know the result. (no diagnostics) - var activeProjectAnalyzers = stateSets.SelectAsArray(s => !s.IsHostAnalyzer, s => s.Analyzer); - var activeHostAnalyzers = stateSets.SelectAsArray(s => s.IsHostAnalyzer, s => s.Analyzer); - - var compilationWithAnalyzers = await DocumentAnalysisExecutor.CreateCompilationWithAnalyzersAsync( - project, activeProjectAnalyzers, activeHostAnalyzers, AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); + var compilationWithAnalyzers = await CreateCompilationWithAnalyzersAsync( + project, stateSets, AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); var result = await GetProjectAnalysisDataAsync(compilationWithAnalyzers, project, stateSets, cancellationToken).ConfigureAwait(false); From e661250e8dd57d25d4f2f67d48e2667012ff4c6b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 10:05:21 -0800 Subject: [PATCH 122/305] Add cache --- .../EngineV2/DiagnosticIncrementalAnalyzer.cs | 1 - ...IncrementalAnalyzer_IncrementalAnalyzer.cs | 41 +++++++++++-------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index 8c853c5b84f1e..231046f3785c3 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 04d76624c5176..f0c91d6cabde2 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Immutable; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ErrorReporting; @@ -18,26 +19,26 @@ internal partial class DiagnosticAnalyzerService { private partial class DiagnosticIncrementalAnalyzer { + private readonly ConditionalWeakTable stateSets, ProjectAnalysisData projectAnalysisData)>> _projectToForceAnalysisData = new(); + public async Task> ForceAnalyzeProjectAsync(Project project, CancellationToken cancellationToken) { try { - var stateSetsForProject = await _stateManager.GetOrCreateStateSetsAsync(project, cancellationToken).ConfigureAwait(false); - var stateSets = GetStateSetsForFullSolutionAnalysis(stateSetsForProject, project); - - var compilationWithAnalyzers = await CreateCompilationWithAnalyzersAsync( - project, stateSets, AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); - - var result = await GetProjectAnalysisDataAsync(compilationWithAnalyzers, project, stateSets, cancellationToken).ConfigureAwait(false); + if (!_projectToForceAnalysisData.TryGetValue(project, out var box)) + { + box = new(await ComputeForceAnalyzeProjectAsync().ConfigureAwait(false)); + box = _projectToForceAnalysisData.GetValue(project, _ => box); + } using var _ = ArrayBuilder.GetInstance(out var diagnostics); - // no cancellation after this point. + var (stateSets, projectAnalysisData) = box.Value; foreach (var stateSet in stateSets) { var state = stateSet.GetOrCreateProjectState(project.Id); - if (result.TryGetResult(stateSet.Analyzer, out var analyzerResult)) + if (projectAnalysisData.TryGetResult(stateSet.Analyzer, out var analyzerResult)) { diagnostics.AddRange(analyzerResult.GetAllDiagnostics()); await state.SaveToInMemoryStorageAsync(project, analyzerResult).ConfigureAwait(false); @@ -50,16 +51,20 @@ public async Task> ForceAnalyzeProjectAsync(Proje { throw ExceptionUtilities.Unreachable(); } - } - /// - /// Return list of to be used for full solution analysis. - /// - private ImmutableArray GetStateSetsForFullSolutionAnalysis(ImmutableArray stateSets, Project project) - { - // Include only analyzers we want to run for full solution analysis. - // Analyzers not included here will never be saved because result is unknown. - return stateSets.WhereAsArray(static (s, arg) => arg.self.IsCandidateForFullSolutionAnalysis(s.Analyzer, s.IsHostAnalyzer, arg.project), (self: this, project)); + async Task<(ImmutableArray stateSets, ProjectAnalysisData projectAnalysisData)> ComputeForceAnalyzeProjectAsync() + { + var allStateSets = await _stateManager.GetOrCreateStateSetsAsync(project, cancellationToken).ConfigureAwait(false); + var fullSolutionAnalysisStateSets = allStateSets.WhereAsArray( + static (stateSet, arg) => arg.self.IsCandidateForFullSolutionAnalysis(stateSet.Analyzer, stateSet.IsHostAnalyzer, arg.project), + (self: this, project)); + + var compilationWithAnalyzers = await CreateCompilationWithAnalyzersAsync( + project, fullSolutionAnalysisStateSets, AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); + + var projectAnalysisData = await GetProjectAnalysisDataAsync(compilationWithAnalyzers, project, fullSolutionAnalysisStateSets, cancellationToken).ConfigureAwait(false); + return (fullSolutionAnalysisStateSets, projectAnalysisData); + } } private bool IsCandidateForFullSolutionAnalysis(DiagnosticAnalyzer analyzer, bool isHostAnalyzer, Project project) From 56b5058da56d647154dd24dee54980cab094b5ed Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 10:13:17 -0800 Subject: [PATCH 123/305] in progress --- ...sticIncrementalAnalyzer.InMemoryStorage.cs | 62 ------------------- ...gnosticIncrementalAnalyzer.ProjectState.cs | 39 +----------- ...IncrementalAnalyzer_IncrementalAnalyzer.cs | 3 - 3 files changed, 3 insertions(+), 101 deletions(-) delete mode 100644 src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InMemoryStorage.cs diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InMemoryStorage.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InMemoryStorage.cs deleted file mode 100644 index 9377ee9ca2b9f..0000000000000 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InMemoryStorage.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Concurrent; -using System.Collections.Immutable; -using Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.Diagnostics; - -internal partial class DiagnosticAnalyzerService -{ - private partial class DiagnosticIncrementalAnalyzer - { - private static class InMemoryStorage - { - // the reason using nested map rather than having tuple as key is so that I dont have a gigantic map - private static readonly ConcurrentDictionary> s_map = - new(concurrencyLevel: 2, capacity: 10); - - public static bool TryGetValue(DiagnosticAnalyzer analyzer, (ProjectOrDocumentId key, string stateKey) key, out CacheEntry entry) - { - entry = default; - return s_map.TryGetValue(analyzer, out var analyzerMap) && - analyzerMap.TryGetValue(key, out entry); - } - - public static void Cache(DiagnosticAnalyzer analyzer, (ProjectOrDocumentId key, string stateKey) key, CacheEntry entry) - { - // add new cache entry - var analyzerMap = s_map.GetOrAdd(analyzer, _ => new ConcurrentDictionary<(ProjectOrDocumentId key, string stateKey), CacheEntry>(concurrencyLevel: 2, capacity: 10)); - analyzerMap[key] = entry; - } - - public static void Remove(DiagnosticAnalyzer analyzer, (ProjectOrDocumentId key, string stateKey) key) - { - // remove the entry - if (!s_map.TryGetValue(analyzer, out var analyzerMap)) - { - return; - } - - analyzerMap.TryRemove(key, out _); - - if (analyzerMap.IsEmpty) - { - s_map.TryRemove(analyzer, out _); - } - } - - public static void DropCache(DiagnosticAnalyzer analyzer) - { - // drop any cache related to given analyzer - s_map.TryRemove(analyzer, out _); - } - } - - // in memory cache entry - private readonly record struct CacheEntry(VersionStamp Version, ImmutableArray Diagnostics); - } -} diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index 0beabe22898a1..13f862fa59fd7 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -31,14 +31,12 @@ private sealed class ProjectState // project id of this state private readonly StateSet _owner; - - // last aggregated analysis result for this project saved - private DiagnosticAnalysisResult _lastResult; + private readonly ProjectId _projectId; public ProjectState(StateSet owner, ProjectId projectId) { _owner = owner; - _lastResult = DiagnosticAnalysisResult.CreateInitialResult(projectId); + _projectId = projectId; } /// @@ -47,8 +45,7 @@ public ProjectState(StateSet owner, ProjectId projectId) public async Task GetAnalysisDataAsync(Project project, CancellationToken cancellationToken) { // make a copy of last result. - var lastResult = _lastResult; - Contract.ThrowIfFalse(lastResult.ProjectId == project.Id); + Contract.ThrowIfFalse(_projectId == project.Id); var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); if (lastResult.IsDefault) @@ -318,36 +315,6 @@ private bool TryGetProjectDiagnosticsFromInMemoryStorage(VersionStamp serializer return false; } - private ImmutableArray GetDiagnosticsFromInMemoryStorage( - VersionStamp serializerVersion, ProjectOrDocumentId key, string stateKey) - { - return InMemoryStorage.TryGetValue(_owner.Analyzer, (key, stateKey), out var entry) && serializerVersion == entry.Version - ? entry.Diagnostics - : default; - } - - private void RemoveInMemoryCache(DiagnosticAnalysisResult lastResult) - { - // remove old cache - foreach (var documentId in lastResult.DocumentIdsOrEmpty) - { - RemoveInMemoryCacheEntries(documentId); - } - } - - private void RemoveInMemoryCacheEntries(DocumentId id) - { - RemoveInMemoryCacheEntry(new(id), SyntaxStateName); - RemoveInMemoryCacheEntry(new(id), SemanticStateName); - RemoveInMemoryCacheEntry(new(id), NonLocalStateName); - } - - private void RemoveInMemoryCacheEntry(ProjectOrDocumentId key, string stateKey) - { - // remove in memory cache if entry exist - InMemoryStorage.Remove(_owner.Analyzer, (key, stateKey)); - } - private static bool IsEmpty(DiagnosticAnalysisResult result, DocumentId documentId) => !result.DocumentIdsOrEmpty.Contains(documentId); diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index f0c91d6cabde2..9b09a61e78506 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -39,10 +39,7 @@ public async Task> ForceAnalyzeProjectAsync(Proje var state = stateSet.GetOrCreateProjectState(project.Id); if (projectAnalysisData.TryGetResult(stateSet.Analyzer, out var analyzerResult)) - { diagnostics.AddRange(analyzerResult.GetAllDiagnostics()); - await state.SaveToInMemoryStorageAsync(project, analyzerResult).ConfigureAwait(false); - } } return diagnostics.ToImmutableAndClear(); From 66139efa51cdc6918c90066f6366b77cac5d7361 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 10:27:18 -0800 Subject: [PATCH 124/305] Merge base and parent type in diagnostic subsystem --- ...osticIncrementalAnalyzer_GetDiagnostics.cs | 55 +++++++------------ 1 file changed, 19 insertions(+), 36 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index 3d1532fd0183e..efd20ab728d24 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -18,33 +18,35 @@ internal partial class DiagnosticAnalyzerService private partial class DiagnosticIncrementalAnalyzer { public Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) - => new IdeLatestDiagnosticGetter(this, solution, projectId, documentId, diagnosticIds, shouldIncludeAnalyzer, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics).GetDiagnosticsAsync(cancellationToken); + => new DiagnosticGetter(this, solution, projectId, documentId, diagnosticIds, shouldIncludeAnalyzer, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics).GetDiagnosticsAsync(cancellationToken); public Task> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId projectId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) - => new IdeLatestDiagnosticGetter(this, solution, projectId, documentId: null, diagnosticIds, shouldIncludeAnalyzer, includeLocalDocumentDiagnostics: false, includeNonLocalDocumentDiagnostics).GetProjectDiagnosticsAsync(cancellationToken); + => new DiagnosticGetter(this, solution, projectId, documentId: null, diagnosticIds, shouldIncludeAnalyzer, includeLocalDocumentDiagnostics: false, includeNonLocalDocumentDiagnostics).GetProjectDiagnosticsAsync(cancellationToken); - private abstract class DiagnosticGetter( + private sealed class DiagnosticGetter( DiagnosticIncrementalAnalyzer owner, Solution solution, ProjectId projectId, DocumentId? documentId, + ImmutableHashSet? diagnosticIds, + Func? shouldIncludeAnalyzer, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics) { - protected readonly DiagnosticIncrementalAnalyzer Owner = owner; - - protected readonly Solution Solution = solution; - protected readonly ProjectId ProjectId = projectId; - protected readonly DocumentId? DocumentId = documentId; - protected readonly bool IncludeLocalDocumentDiagnostics = includeLocalDocumentDiagnostics; - protected readonly bool IncludeNonLocalDocumentDiagnostics = includeNonLocalDocumentDiagnostics; + private readonly DiagnosticIncrementalAnalyzer Owner = owner; - protected StateManager StateManager => Owner._stateManager; + private readonly Solution Solution = solution; + private readonly ProjectId ProjectId = projectId; + private readonly DocumentId? DocumentId = documentId; + private readonly ImmutableHashSet? _diagnosticIds = diagnosticIds; + private readonly Func? _shouldIncludeAnalyzer = shouldIncludeAnalyzer; + private readonly bool IncludeLocalDocumentDiagnostics = includeLocalDocumentDiagnostics; + private readonly bool IncludeNonLocalDocumentDiagnostics = includeNonLocalDocumentDiagnostics; - protected virtual bool ShouldIncludeDiagnostic(DiagnosticData diagnostic) => true; + private StateManager StateManager => Owner._stateManager; - protected abstract Task ProduceDiagnosticsAsync( - Project project, IReadOnlyList documentIds, bool includeProjectNonLocalResult, ArrayBuilder builder, CancellationToken cancellationToken); + private bool ShouldIncludeDiagnostic(DiagnosticData diagnostic) + => _diagnosticIds == null || _diagnosticIds.Contains(diagnostic.Id); public async Task> GetDiagnosticsAsync(CancellationToken cancellationToken) { @@ -62,7 +64,7 @@ public async Task> GetDiagnosticsAsync(Cancellati includeProjectNonLocalResult, cancellationToken).ConfigureAwait(false); } - protected async Task> ProduceProjectDiagnosticsAsync( + private async Task> ProduceProjectDiagnosticsAsync( Project project, IReadOnlyList documentIds, bool includeProjectNonLocalResult, CancellationToken cancellationToken) { @@ -72,7 +74,7 @@ await this.ProduceDiagnosticsAsync( return builder.ToImmutableAndClear(); } - protected void AddIncludedDiagnostics(ArrayBuilder builder, ImmutableArray diagnostics) + private void AddIncludedDiagnostics(ArrayBuilder builder, ImmutableArray diagnostics) { foreach (var diagnostic in diagnostics) { @@ -80,22 +82,6 @@ protected void AddIncludedDiagnostics(ArrayBuilder builder, Immu builder.Add(diagnostic); } } - } - - private sealed class IdeLatestDiagnosticGetter( - DiagnosticIncrementalAnalyzer owner, - Solution solution, - ProjectId projectId, - DocumentId? documentId, - ImmutableHashSet? diagnosticIds, - Func? shouldIncludeAnalyzer, - bool includeLocalDocumentDiagnostics, - bool includeNonLocalDocumentDiagnostics) - : DiagnosticGetter( - owner, solution, projectId, documentId, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics) - { - private readonly ImmutableHashSet? _diagnosticIds = diagnosticIds; - private readonly Func? _shouldIncludeAnalyzer = shouldIncludeAnalyzer; public async Task> GetProjectDiagnosticsAsync(CancellationToken cancellationToken) { @@ -107,10 +93,7 @@ public async Task> GetProjectDiagnosticsAsync(Can project, documentIds: [], includeProjectNonLocalResult: true, cancellationToken).ConfigureAwait(false); } - protected override bool ShouldIncludeDiagnostic(DiagnosticData diagnostic) - => _diagnosticIds == null || _diagnosticIds.Contains(diagnostic.Id); - - protected override async Task ProduceDiagnosticsAsync( + private async Task ProduceDiagnosticsAsync( Project project, IReadOnlyList documentIds, bool includeProjectNonLocalResult, From f5d7181c796e7f0b4dfcf5b2f897e8917d882a2f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 10:37:04 -0800 Subject: [PATCH 125/305] in progrss --- ...rojectAnalyzerReferenceChangedEventArgs.cs | 30 --------- ...gnosticIncrementalAnalyzer.ProjectState.cs | 51 --------------- ...ntalAnalyzer.StateManager.ProjectStates.cs | 64 +------------------ ...gnosticIncrementalAnalyzer.StateManager.cs | 8 --- .../DiagnosticIncrementalAnalyzer.StateSet.cs | 8 --- .../EngineV2/DiagnosticIncrementalAnalyzer.cs | 14 ---- 6 files changed, 2 insertions(+), 173 deletions(-) delete mode 100644 src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectAnalyzerReferenceChangedEventArgs.cs diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectAnalyzerReferenceChangedEventArgs.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectAnalyzerReferenceChangedEventArgs.cs deleted file mode 100644 index 6d370680d96e1..0000000000000 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectAnalyzerReferenceChangedEventArgs.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Immutable; - -namespace Microsoft.CodeAnalysis.Diagnostics; - -internal partial class DiagnosticAnalyzerService -{ - private partial class DiagnosticIncrementalAnalyzer - { - /// - /// EventArgs for - /// - /// this event args contains information such as the has changed - /// and what has changed. - /// - private sealed class ProjectAnalyzerReferenceChangedEventArgs : EventArgs - { - public readonly ImmutableArray Removed; - - public ProjectAnalyzerReferenceChangedEventArgs(ImmutableArray removed) - { - Removed = removed; - } - } - } -} diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index 13f862fa59fd7..a27f9930b9ceb 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -264,57 +264,6 @@ private void AddToInMemoryStorage( InMemoryStorage.Cache(_owner.Analyzer, (key, stateKey), new CacheEntry(serializerVersion, diagnostics)); } - private bool TryGetDiagnosticsFromInMemoryStorage(VersionStamp serializerVersion, TextDocument document, Builder builder) - { - var success = true; - var project = document.Project; - var documentId = document.Id; - - var diagnostics = GetDiagnosticsFromInMemoryStorage(serializerVersion, new(documentId), SyntaxStateName); - if (!diagnostics.IsDefault) - { - builder.AddSyntaxLocals(documentId, diagnostics); - } - else - { - success = false; - } - - diagnostics = GetDiagnosticsFromInMemoryStorage(serializerVersion, new(documentId), SemanticStateName); - if (!diagnostics.IsDefault) - { - builder.AddSemanticLocals(documentId, diagnostics); - } - else - { - success = false; - } - - diagnostics = GetDiagnosticsFromInMemoryStorage(serializerVersion, new(documentId), NonLocalStateName); - if (!diagnostics.IsDefault) - { - builder.AddNonLocals(documentId, diagnostics); - } - else - { - success = false; - } - - return success; - } - - private bool TryGetProjectDiagnosticsFromInMemoryStorage(VersionStamp serializerVersion, Project project, Builder builder) - { - var diagnostics = GetDiagnosticsFromInMemoryStorage(serializerVersion, new(project.Id), NonLocalStateName); - if (!diagnostics.IsDefault) - { - builder.AddOthers(diagnostics); - return true; - } - - return false; - } - private static bool IsEmpty(DiagnosticAnalysisResult result, DocumentId documentId) => !result.DocumentIdsOrEmpty.Contains(documentId); diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs index 03a09b0db03fd..75031bb5d547e 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs @@ -97,81 +97,21 @@ private ProjectAnalyzerStateSets CreateProjectStateSets(Project project) /// private async Task UpdateProjectStateSetsAsync(Project project, CancellationToken cancellationToken) { - ProjectAnalyzerReferenceChangedEventArgs? analyzerReferenceChangedEventArgs = null; - ProjectAnalyzerStateSets? projectStateSets; - // This code is called concurrently for a project, so the guard prevents duplicated effort calculating StateSets. using (await _projectAnalyzerStateMapGuard.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) { - projectStateSets = TryGetProjectStateSets(project); + var projectStateSets = TryGetProjectStateSets(project); if (projectStateSets == null) { projectStateSets = CreateProjectStateSets(project); - analyzerReferenceChangedEventArgs = GetProjectAnalyzerReferenceChangedEventArgs(project); // update cache. _projectAnalyzerStateMap = _projectAnalyzerStateMap.SetItem(project.Id, projectStateSets.Value); } - } - - if (analyzerReferenceChangedEventArgs != null) - RaiseProjectAnalyzerReferenceChanged(analyzerReferenceChangedEventArgs); - - return projectStateSets.Value; - } - - private ProjectAnalyzerReferenceChangedEventArgs? GetProjectAnalyzerReferenceChangedEventArgs(Project project) - { - // No need to use _projectAnalyzerStateMapGuard during reads of _projectAnalyzerStateMap - if (!_projectAnalyzerStateMap.TryGetValue(project.Id, out var entry)) - { - // new reference added - return null; - } - - // there has been change. find out what has changed - var removedStates = DiffStateSets(entry.AnalyzerReferences.Except(project.AnalyzerReferences), entry.MapPerReferences, entry.StateSetMap); - - if (removedStates.Length == 0) - return null; - - return new ProjectAnalyzerReferenceChangedEventArgs(removedStates); - } - - private static ImmutableArray DiffStateSets( - IEnumerable references, - ImmutableDictionary> mapPerReference, - ImmutableDictionary map) - { - if (mapPerReference.Count == 0 || map.Count == 0) - { - // nothing to diff - return []; - } - - var builder = ImmutableArray.CreateBuilder(); - foreach (var reference in references) - { - // check duplication - if (!mapPerReference.TryGetValue(reference.Id, out var analyzers)) - { - continue; - } - - // okay, this is real reference. get stateset - foreach (var analyzer in analyzers) - { - if (!map.TryGetValue(analyzer, out var set)) - { - continue; - } - builder.Add(set); - } + return projectStateSets.Value; } - - return builder.ToImmutableAndClear(); } } } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs index 279f2fd5003a4..691317e766405 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs @@ -43,11 +43,6 @@ private partial class StateManager /// private readonly SemaphoreSlim _projectAnalyzerStateMapGuard = new(1); - /// - /// This will be raised whenever finds change - /// - public event EventHandler? ProjectAnalyzerReferenceChanged; - public StateManager(Workspace workspace, DiagnosticAnalyzerInfoCache analyzerInfoCache) { _workspace = workspace; @@ -104,9 +99,6 @@ public async Task> GetOrCreateStateSetsAsync(Project pr return null; } - private void RaiseProjectAnalyzerReferenceChanged(ProjectAnalyzerReferenceChangedEventArgs args) - => ProjectAnalyzerReferenceChanged?.Invoke(this, args); - private static ImmutableDictionary CreateStateSetMap( IEnumerable> projectAnalyzerCollection, IEnumerable> hostAnalyzerCollection, diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs index d40dfcb20d6d0..b7e0eede0d133 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs @@ -49,14 +49,6 @@ public void AddActiveDocument(DocumentId documentId) public ProjectState GetOrCreateProjectState(ProjectId projectId) => _projectStates.GetOrAdd(projectId, static (id, self) => new ProjectState(self, id), this); - - public void OnRemoved() - { - // ths stateset is being removed. - // TODO: we do this since InMemoryCache is static type. we might consider making it instance object - // of something. - InMemoryStorage.DropCache(Analyzer); - } } } } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index 231046f3785c3..b6a713423d547 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -44,7 +44,6 @@ public DiagnosticIncrementalAnalyzer( GlobalOptions = globalOptionService; _stateManager = new StateManager(workspace, analyzerInfoCache); - _stateManager.ProjectAnalyzerReferenceChanged += OnProjectAnalyzerReferenceChanged; var enabled = globalOptionService.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler); _diagnosticAnalyzerRunner = new InProcOrRemoteHostAnalyzerRunner( @@ -54,19 +53,6 @@ public DiagnosticIncrementalAnalyzer( internal IGlobalOptionService GlobalOptions { get; } internal DiagnosticAnalyzerInfoCache DiagnosticAnalyzerInfoCache => _diagnosticAnalyzerRunner.AnalyzerInfoCache; - private void OnProjectAnalyzerReferenceChanged(object? sender, ProjectAnalyzerReferenceChangedEventArgs e) - { - if (e.Removed.Length == 0) - { - // nothing to refresh - return; - } - - // make sure we drop cache related to the analyzers - foreach (var stateSet in e.Removed) - stateSet.OnRemoved(); - } - private static DiagnosticAnalysisResult GetResultOrEmpty(ImmutableDictionary map, DiagnosticAnalyzer analyzer, ProjectId projectId, VersionStamp version) { if (map.TryGetValue(analyzer, out var result)) From f4ef9a81836c0694e81bace29f7ebccbe6933d1e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 10:44:47 -0800 Subject: [PATCH 126/305] Remove unneeded code --- ...gnosticIncrementalAnalyzer.AnalysisData.cs | 58 +-- .../DiagnosticIncrementalAnalyzer.Executor.cs | 109 +---- ...gnosticIncrementalAnalyzer.ProjectState.cs | 396 ++++++++---------- 3 files changed, 209 insertions(+), 354 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs index b668b076cfebf..1db558c049db7 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs @@ -43,40 +43,40 @@ public DiagnosticAnalysisResult GetResult(DiagnosticAnalyzer analyzer) public bool TryGetResult(DiagnosticAnalyzer analyzer, out DiagnosticAnalysisResult result) => Result.TryGetValue(analyzer, out result); - public static async Task CreateAsync(Project project, ImmutableArray stateSets, CancellationToken cancellationToken) - { - VersionStamp? version = null; + //public static async Task CreateAsync(Project project, ImmutableArray stateSets, CancellationToken cancellationToken) + //{ + // VersionStamp? version = null; - var builder = ImmutableDictionary.CreateBuilder(); - foreach (var stateSet in stateSets) - { - var state = stateSet.GetOrCreateProjectState(project.Id); - var result = await state.GetAnalysisDataAsync(project, cancellationToken).ConfigureAwait(false); - Contract.ThrowIfFalse(project.Id == result.ProjectId); + // var builder = ImmutableDictionary.CreateBuilder(); + // foreach (var stateSet in stateSets) + // { + // var state = stateSet.GetOrCreateProjectState(project.Id); + // var result = await state.GetAnalysisDataAsync(project, cancellationToken).ConfigureAwait(false); + // Contract.ThrowIfFalse(project.Id == result.ProjectId); - if (!version.HasValue) - { - version = result.Version; - } - else if (version.Value != VersionStamp.Default && version.Value != result.Version) - { - // if not all version is same, set version as default. - // this can happen at the initial data loading or - // when document is closed and we put active file state to project state - version = VersionStamp.Default; - } + // if (!version.HasValue) + // { + // version = result.Version; + // } + // else if (version.Value != VersionStamp.Default && version.Value != result.Version) + // { + // // if not all version is same, set version as default. + // // this can happen at the initial data loading or + // // when document is closed and we put active file state to project state + // version = VersionStamp.Default; + // } - builder.Add(stateSet.Analyzer, result); - } + // builder.Add(stateSet.Analyzer, result); + // } - if (!version.HasValue) - { - // there is no saved data to return. - return new ProjectAnalysisData(project.Id, VersionStamp.Default, ImmutableDictionary.Empty); - } + // if (!version.HasValue) + // { + // // there is no saved data to return. + // return new ProjectAnalysisData(project.Id, VersionStamp.Default, ImmutableDictionary.Empty); + // } - return new ProjectAnalysisData(project.Id, version.Value, builder.ToImmutable()); - } + // return new ProjectAnalysisData(project.Id, version.Value, builder.ToImmutable()); + //} } } } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs index 76e52a8f58a0c..4d05566f8e210 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs @@ -31,12 +31,12 @@ private async Task GetProjectAnalysisDataAsync( try { var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); - var existingData = await ProjectAnalysisData.CreateAsync(project, stateSets, cancellationToken).ConfigureAwait(false); + //var existingData = await ProjectAnalysisData.CreateAsync(project, stateSets, cancellationToken).ConfigureAwait(false); - if (existingData.Version == version) - return existingData; + //if (existingData.Version == version) + // return existingData; - var result = await ComputeDiagnosticsAsync(compilationWithAnalyzers, project, stateSets, existingData.Result, cancellationToken).ConfigureAwait(false); + var result = await ComputeDiagnosticsAsync(compilationWithAnalyzers, project, stateSets, cancellationToken).ConfigureAwait(false); // If project is not loaded successfully, get rid of any semantic errors from compiler analyzer. // Note: In the past when project was not loaded successfully we did not run any analyzers on the project. @@ -119,38 +119,17 @@ private async Task> ComputeDiagnosticsAsync( - CompilationWithAnalyzersPair? compilationWithAnalyzers, Project project, ImmutableArray stateSets, - ImmutableDictionary existing, CancellationToken cancellationToken) + CompilationWithAnalyzersPair? compilationWithAnalyzers, + Project project, + ImmutableArray stateSets, + CancellationToken cancellationToken) { try { - // PERF: check whether we can reduce number of analyzers we need to run. - // this can happen since caller could have created the driver with different set of analyzers that are different - // than what we used to create the cache. var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); var ideAnalyzers = stateSets.Select(s => s.Analyzer).Where(a => a is ProjectDiagnosticAnalyzer or DocumentDiagnosticAnalyzer).ToImmutableArrayOrEmpty(); - if (compilationWithAnalyzers != null && TryReduceAnalyzersToRun(compilationWithAnalyzers, version, existing, out var projectAnalyzersToRun, out var hostAnalyzersToRun)) - { - // it looks like we can reduce the set. create new CompilationWithAnalyzer. - // if we reduced to 0, we just pass in null for analyzer drvier. it could be reduced to 0 - // since we might have up to date results for analyzers from compiler but not for - // workspace analyzers. - - var compilationWithReducedAnalyzers = (projectAnalyzersToRun.Length == 0 && hostAnalyzersToRun.Length == 0) ? null : - await DocumentAnalysisExecutor.CreateCompilationWithAnalyzersAsync( - project, - projectAnalyzersToRun, - hostAnalyzersToRun, - AnalyzerService.CrashOnAnalyzerException, - cancellationToken).ConfigureAwait(false); - - var result = await ComputeDiagnosticsAsync(compilationWithReducedAnalyzers, project, ideAnalyzers, cancellationToken).ConfigureAwait(false); - return MergeExistingDiagnostics(version, existing, result); - } - - // we couldn't reduce the set. return await ComputeDiagnosticsAsync(compilationWithAnalyzers, project, ideAnalyzers, cancellationToken).ConfigureAwait(false); } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) @@ -159,78 +138,6 @@ await DocumentAnalysisExecutor.CreateCompilationWithAnalyzersAsync( } } - private static ImmutableDictionary MergeExistingDiagnostics( - VersionStamp version, ImmutableDictionary existing, ImmutableDictionary result) - { - // quick bail out. - if (existing.IsEmpty) - { - return result; - } - - foreach (var (analyzer, results) in existing) - { - if (results.Version != version) - { - continue; - } - - result = result.SetItem(analyzer, results); - } - - return result; - } - - private static bool TryReduceAnalyzersToRun( - CompilationWithAnalyzersPair compilationWithAnalyzers, VersionStamp version, - ImmutableDictionary existing, - out ImmutableArray projectAnalyzers, - out ImmutableArray hostAnalyzers) - { - projectAnalyzers = compilationWithAnalyzers.ProjectAnalyzers.WhereAsArray( - static (analyzer, arg) => - { - if (arg.existing.TryGetValue(analyzer, out var analysisResult) && - analysisResult.Version == arg.version) - { - // we already have up to date result. - return false; - } - - // analyzer that is out of date. - // open file only analyzer is always out of date for project wide data - return true; - }, - (existing, version)); - - hostAnalyzers = compilationWithAnalyzers.HostAnalyzers.WhereAsArray( - static (analyzer, arg) => - { - if (arg.existing.TryGetValue(analyzer, out var analysisResult) && - analysisResult.Version == arg.version) - { - // we already have up to date result. - return false; - } - - // analyzer that is out of date. - // open file only analyzer is always out of date for project wide data - return true; - }, - (existing, version)); - - if (projectAnalyzers.Length == compilationWithAnalyzers.ProjectAnalyzers.Length - && hostAnalyzers.Length == compilationWithAnalyzers.HostAnalyzers.Length) - { - // all of analyzers are out of date. - projectAnalyzers = default; - hostAnalyzers = default; - return false; - } - - return true; - } - private async Task> MergeProjectDiagnosticAnalyzerDiagnosticsAsync( Project project, ImmutableArray ideAnalyzers, diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index a27f9930b9ceb..674a79b6b85ef 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -39,230 +39,178 @@ public ProjectState(StateSet owner, ProjectId projectId) _projectId = projectId; } - /// - /// Return all diagnostics for the given project stored in this state - /// - public async Task GetAnalysisDataAsync(Project project, CancellationToken cancellationToken) - { - // make a copy of last result. - Contract.ThrowIfFalse(_projectId == project.Id); - - var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); - if (lastResult.IsDefault) - return LoadInitialAnalysisData(project, version, cancellationToken); - - RoslynDebug.Assert(lastResult.DocumentIds != null); - - // PERF: avoid loading data if version is not right one. - // avoid loading data flag is there as a strictly perf optimization. - if (lastResult.Version != version) - { - return lastResult; - } - - // if given project doesnt have any diagnostics, return empty. - if (lastResult.IsEmpty) - { - return DiagnosticAnalysisResult.CreateEmpty(lastResult.ProjectId, lastResult.Version); - } - - // loading data can be canceled any time. - var serializerVersion = lastResult.Version; - var builder = new Builder(project, lastResult.Version, lastResult.DocumentIds); - - foreach (var documentId in lastResult.DocumentIds) - { - cancellationToken.ThrowIfCancellationRequested(); - - var document = project.GetDocument(documentId); - if (document == null) - continue; - - if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder)) - { - Debug.Assert(lastResult.Version == VersionStamp.Default); - - // this can happen if we merged back active file diagnostics back to project state but - // project state didn't have diagnostics for the file yet. (since project state was staled) - continue; - } - } - - if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder)) - { - // this can happen if SaveAsync is not yet called but active file merge happened. one of case is if user did build before the very first - // analysis happened. - } - - return builder.ToResult(); - } - - /// - /// Return all diagnostics for the given document stored in this state including non local diagnostics for this document - /// - public async Task GetAnalysisDataAsync(TextDocument document, CancellationToken cancellationToken) - { - // make a copy of last result. - var lastResult = _lastResult; - Contract.ThrowIfFalse(lastResult.ProjectId == document.Project.Id); - - var version = await GetDiagnosticVersionAsync(document.Project, cancellationToken).ConfigureAwait(false); - if (lastResult.IsDefault) - return LoadInitialAnalysisData(document, version); - - // if given document doesnt have any diagnostics, return empty. - if (IsEmpty(lastResult, document.Id)) - { - return DiagnosticAnalysisResult.CreateEmpty(lastResult.ProjectId, lastResult.Version); - } - - // loading data can be canceled any time. - var serializerVersion = lastResult.Version; - var builder = new Builder(document.Project, lastResult.Version); - - if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder)) - { - Debug.Assert(lastResult.Version == VersionStamp.Default); - - // this can happen if we merged back active file diagnostics back to project state but - // project state didn't have diagnostics for the file yet. (since project state was staled) - } - - return builder.ToResult(); - } - - /// - /// Return all no location diagnostics for the given project stored in this state - /// - public async Task GetProjectAnalysisDataAsync(Project project, CancellationToken cancellationToken) - { - // make a copy of last result. - var lastResult = _lastResult; - Contract.ThrowIfFalse(lastResult.ProjectId == project.Id); - - var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); - if (lastResult.IsDefault) - return LoadInitialProjectAnalysisData(project, version); - - // if given document doesn't have any diagnostics, return empty. - if (lastResult.IsEmpty) - { - return DiagnosticAnalysisResult.CreateEmpty(lastResult.ProjectId, lastResult.Version); - } - - // loading data can be canceled any time. - var serializerVersion = lastResult.Version; - var builder = new Builder(project, lastResult.Version); - - if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder)) - { - // this can happen if SaveAsync is not yet called but active file merge happened. one of case is if user did build before the very first - // analysis happened. - } - - return builder.ToResult(); - } - - public async ValueTask SaveToInMemoryStorageAsync(Project project, DiagnosticAnalysisResult result) - { - Contract.ThrowIfTrue(result.IsAggregatedForm); - Contract.ThrowIfNull(result.DocumentIds); - - RemoveInMemoryCache(_lastResult); - - using var _ = PooledHashSet.GetInstance(out var documentIdsToProcess); - documentIdsToProcess.AddRange(_lastResult.DocumentIdsOrEmpty); - documentIdsToProcess.AddRange(result.DocumentIdsOrEmpty); - - // save last aggregated form of analysis result - _lastResult = result.ToAggregatedForm(); - - // serialization can't be canceled. - var serializerVersion = result.Version; - - foreach (var documentId in documentIdsToProcess) - { - var document = project.GetTextDocument(documentId); - - // If we couldn't find a normal document, and all features are enabled for source generated - // documents, attempt to locate a matching source generated document in the project. - if (document is null - && project.Solution.Services.GetService()?.EnableDiagnosticsInSourceGeneratedFiles == true) - { - document = await project.GetSourceGeneratedDocumentAsync(documentId, CancellationToken.None).ConfigureAwait(false); - } - - if (document == null) - { - // it can happen with build synchronization since, in build case, - // we don't have actual snapshot (we have no idea what sources out of proc build has picked up) - // so we might be out of sync. - // example of such cases will be changing anything about solution while building is going on. - // it can be user explicit actions such as unloading project, deleting a file, but also it can be - // something project system or roslyn workspace does such as populating workspace right after - // solution is loaded. - continue; - } - - AddToInMemoryStorage(serializerVersion, new(document.Id), SyntaxStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Syntax)); - AddToInMemoryStorage(serializerVersion, new(document.Id), SemanticStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Semantic)); - AddToInMemoryStorage(serializerVersion, new(document.Id), NonLocalStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.NonLocal)); - } - - AddToInMemoryStorage(serializerVersion, new(result.ProjectId), NonLocalStateName, result.GetOtherDiagnostics()); - } - - private DiagnosticAnalysisResult LoadInitialAnalysisData( - Project project, VersionStamp version, CancellationToken cancellationToken) - { - // loading data can be canceled any time. - var builder = new Builder(project, version); - - foreach (var document in project.Documents) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (!TryGetDiagnosticsFromInMemoryStorage(version, document, builder)) - continue; - } - - if (!TryGetProjectDiagnosticsFromInMemoryStorage(version, project, builder)) - return DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default); - - return builder.ToResult(); - } - - private DiagnosticAnalysisResult LoadInitialAnalysisData( - TextDocument document, VersionStamp version) - { - // loading data can be canceled any time. - var project = document.Project; - - var builder = new Builder(project, version); - - if (!TryGetDiagnosticsFromInMemoryStorage(version, document, builder)) - return DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default); - - return builder.ToResult(); - } - - private DiagnosticAnalysisResult LoadInitialProjectAnalysisData( - Project project, VersionStamp version) - { - // loading data can be canceled any time. - var builder = new Builder(project, version); - - if (!TryGetProjectDiagnosticsFromInMemoryStorage(version, project, builder)) - return DiagnosticAnalysisResult.CreateEmpty(project.Id, VersionStamp.Default); - - return builder.ToResult(); - } - - private void AddToInMemoryStorage( - VersionStamp serializerVersion, ProjectOrDocumentId key, string stateKey, ImmutableArray diagnostics) - { - InMemoryStorage.Cache(_owner.Analyzer, (key, stateKey), new CacheEntry(serializerVersion, diagnostics)); - } + ///// + ///// Return all diagnostics for the given project stored in this state + ///// + //public async Task GetAnalysisDataAsync(Project project, CancellationToken cancellationToken) + //{ + // // make a copy of last result. + // Contract.ThrowIfFalse(_projectId == project.Id); + + // var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); + // if (lastResult.IsDefault) + // return LoadInitialAnalysisData(project, version, cancellationToken); + + // RoslynDebug.Assert(lastResult.DocumentIds != null); + + // // PERF: avoid loading data if version is not right one. + // // avoid loading data flag is there as a strictly perf optimization. + // if (lastResult.Version != version) + // { + // return lastResult; + // } + + // // if given project doesnt have any diagnostics, return empty. + // if (lastResult.IsEmpty) + // { + // return DiagnosticAnalysisResult.CreateEmpty(lastResult.ProjectId, lastResult.Version); + // } + + // // loading data can be canceled any time. + // var serializerVersion = lastResult.Version; + // var builder = new Builder(project, lastResult.Version, lastResult.DocumentIds); + + // foreach (var documentId in lastResult.DocumentIds) + // { + // cancellationToken.ThrowIfCancellationRequested(); + + // var document = project.GetDocument(documentId); + // if (document == null) + // continue; + + // if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder)) + // { + // Debug.Assert(lastResult.Version == VersionStamp.Default); + + // // this can happen if we merged back active file diagnostics back to project state but + // // project state didn't have diagnostics for the file yet. (since project state was staled) + // continue; + // } + // } + + // if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder)) + // { + // // this can happen if SaveAsync is not yet called but active file merge happened. one of case is if user did build before the very first + // // analysis happened. + // } + + // return builder.ToResult(); + //} + + ///// + ///// Return all diagnostics for the given document stored in this state including non local diagnostics for this document + ///// + //public async Task GetAnalysisDataAsync(TextDocument document, CancellationToken cancellationToken) + //{ + // // make a copy of last result. + // var lastResult = _lastResult; + // Contract.ThrowIfFalse(lastResult.ProjectId == document.Project.Id); + + // var version = await GetDiagnosticVersionAsync(document.Project, cancellationToken).ConfigureAwait(false); + // if (lastResult.IsDefault) + // return LoadInitialAnalysisData(document, version); + + // // if given document doesnt have any diagnostics, return empty. + // if (IsEmpty(lastResult, document.Id)) + // { + // return DiagnosticAnalysisResult.CreateEmpty(lastResult.ProjectId, lastResult.Version); + // } + + // // loading data can be canceled any time. + // var serializerVersion = lastResult.Version; + // var builder = new Builder(document.Project, lastResult.Version); + + // if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder)) + // { + // Debug.Assert(lastResult.Version == VersionStamp.Default); + + // // this can happen if we merged back active file diagnostics back to project state but + // // project state didn't have diagnostics for the file yet. (since project state was staled) + // } + + // return builder.ToResult(); + //} + + ///// + ///// Return all no location diagnostics for the given project stored in this state + ///// + //public async Task GetProjectAnalysisDataAsync(Project project, CancellationToken cancellationToken) + //{ + // // make a copy of last result. + // var lastResult = _lastResult; + // Contract.ThrowIfFalse(lastResult.ProjectId == project.Id); + + // var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); + // if (lastResult.IsDefault) + // return LoadInitialProjectAnalysisData(project, version); + + // // if given document doesn't have any diagnostics, return empty. + // if (lastResult.IsEmpty) + // { + // return DiagnosticAnalysisResult.CreateEmpty(lastResult.ProjectId, lastResult.Version); + // } + + // // loading data can be canceled any time. + // var serializerVersion = lastResult.Version; + // var builder = new Builder(project, lastResult.Version); + + // if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder)) + // { + // // this can happen if SaveAsync is not yet called but active file merge happened. one of case is if user did build before the very first + // // analysis happened. + // } + + // return builder.ToResult(); + //} + + //public async ValueTask SaveToInMemoryStorageAsync(Project project, DiagnosticAnalysisResult result) + //{ + // Contract.ThrowIfTrue(result.IsAggregatedForm); + // Contract.ThrowIfNull(result.DocumentIds); + + // RemoveInMemoryCache(_lastResult); + + // using var _ = PooledHashSet.GetInstance(out var documentIdsToProcess); + // documentIdsToProcess.AddRange(_lastResult.DocumentIdsOrEmpty); + // documentIdsToProcess.AddRange(result.DocumentIdsOrEmpty); + + // // save last aggregated form of analysis result + // _lastResult = result.ToAggregatedForm(); + + // // serialization can't be canceled. + // var serializerVersion = result.Version; + + // foreach (var documentId in documentIdsToProcess) + // { + // var document = project.GetTextDocument(documentId); + + // // If we couldn't find a normal document, and all features are enabled for source generated + // // documents, attempt to locate a matching source generated document in the project. + // if (document is null + // && project.Solution.Services.GetService()?.EnableDiagnosticsInSourceGeneratedFiles == true) + // { + // document = await project.GetSourceGeneratedDocumentAsync(documentId, CancellationToken.None).ConfigureAwait(false); + // } + + // if (document == null) + // { + // // it can happen with build synchronization since, in build case, + // // we don't have actual snapshot (we have no idea what sources out of proc build has picked up) + // // so we might be out of sync. + // // example of such cases will be changing anything about solution while building is going on. + // // it can be user explicit actions such as unloading project, deleting a file, but also it can be + // // something project system or roslyn workspace does such as populating workspace right after + // // solution is loaded. + // continue; + // } + + // AddToInMemoryStorage(serializerVersion, new(document.Id), SyntaxStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Syntax)); + // AddToInMemoryStorage(serializerVersion, new(document.Id), SemanticStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Semantic)); + // AddToInMemoryStorage(serializerVersion, new(document.Id), NonLocalStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.NonLocal)); + // } + + // AddToInMemoryStorage(serializerVersion, new(result.ProjectId), NonLocalStateName, result.GetOtherDiagnostics()); + //} private static bool IsEmpty(DiagnosticAnalysisResult result, DocumentId documentId) => !result.DocumentIdsOrEmpty.Contains(documentId); From 26cd9e546a5b46255728e05df00ced45e0cedbd6 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 10:46:24 -0800 Subject: [PATCH 127/305] deletE --- ...gnosticIncrementalAnalyzer.AnalysisData.cs | 35 ------------------- 1 file changed, 35 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs index 1db558c049db7..6c426a2faa241 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs @@ -42,41 +42,6 @@ public DiagnosticAnalysisResult GetResult(DiagnosticAnalyzer analyzer) public bool TryGetResult(DiagnosticAnalyzer analyzer, out DiagnosticAnalysisResult result) => Result.TryGetValue(analyzer, out result); - - //public static async Task CreateAsync(Project project, ImmutableArray stateSets, CancellationToken cancellationToken) - //{ - // VersionStamp? version = null; - - // var builder = ImmutableDictionary.CreateBuilder(); - // foreach (var stateSet in stateSets) - // { - // var state = stateSet.GetOrCreateProjectState(project.Id); - // var result = await state.GetAnalysisDataAsync(project, cancellationToken).ConfigureAwait(false); - // Contract.ThrowIfFalse(project.Id == result.ProjectId); - - // if (!version.HasValue) - // { - // version = result.Version; - // } - // else if (version.Value != VersionStamp.Default && version.Value != result.Version) - // { - // // if not all version is same, set version as default. - // // this can happen at the initial data loading or - // // when document is closed and we put active file state to project state - // version = VersionStamp.Default; - // } - - // builder.Add(stateSet.Analyzer, result); - // } - - // if (!version.HasValue) - // { - // // there is no saved data to return. - // return new ProjectAnalysisData(project.Id, VersionStamp.Default, ImmutableDictionary.Empty); - // } - - // return new ProjectAnalysisData(project.Id, version.Value, builder.ToImmutable()); - //} } } } From d34bbd7a12dd01ec3d4dd61dceff02ac002be0eb Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 10:47:36 -0800 Subject: [PATCH 128/305] rename --- .../EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs | 6 +----- .../DiagnosticIncrementalAnalyzer_GetDiagnostics.cs | 2 +- .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs index 4d05566f8e210..1831fc1bbda3c 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs @@ -23,7 +23,7 @@ private partial class DiagnosticIncrementalAnalyzer /// /// Return all diagnostics that belong to given project for the given StateSets (analyzers) either from cache or by calculating them /// - private async Task GetProjectAnalysisDataAsync( + private async Task ComputeProjectAnalysisDataAsync( CompilationWithAnalyzersPair? compilationWithAnalyzers, Project project, ImmutableArray stateSets, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.Diagnostics_ProjectDiagnostic, GetProjectLogMessage, project, stateSets, cancellationToken)) @@ -31,10 +31,6 @@ private async Task GetProjectAnalysisDataAsync( try { var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); - //var existingData = await ProjectAnalysisData.CreateAsync(project, stateSets, cancellationToken).ConfigureAwait(false); - - //if (existingData.Version == version) - // return existingData; var result = await ComputeDiagnosticsAsync(compilationWithAnalyzers, project, stateSets, cancellationToken).ConfigureAwait(false); diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index efd20ab728d24..05b2ea44005ce 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -107,7 +107,7 @@ private async Task ProduceDiagnosticsAsync( // unlike the suppressed (disabled) analyzer, we will include hidden diagnostic only analyzers here. var compilation = await CreateCompilationWithAnalyzersAsync(project, stateSets, Owner.AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); - var result = await Owner.GetProjectAnalysisDataAsync(compilation, project, stateSets, cancellationToken).ConfigureAwait(false); + var result = await Owner.ComputeProjectAnalysisDataAsync(compilation, project, stateSets, cancellationToken).ConfigureAwait(false); foreach (var stateSet in stateSets) { diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 9b09a61e78506..e4e8c0b1ce1d1 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -59,7 +59,7 @@ public async Task> ForceAnalyzeProjectAsync(Proje var compilationWithAnalyzers = await CreateCompilationWithAnalyzersAsync( project, fullSolutionAnalysisStateSets, AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); - var projectAnalysisData = await GetProjectAnalysisDataAsync(compilationWithAnalyzers, project, fullSolutionAnalysisStateSets, cancellationToken).ConfigureAwait(false); + var projectAnalysisData = await ComputeProjectAnalysisDataAsync(compilationWithAnalyzers, project, fullSolutionAnalysisStateSets, cancellationToken).ConfigureAwait(false); return (fullSolutionAnalysisStateSets, projectAnalysisData); } } From 84f284c11a1d508cba274e9695f163448fef67b3 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 10:48:45 -0800 Subject: [PATCH 129/305] Delete unused --- ...gnosticIncrementalAnalyzer.ProjectState.cs | 229 ------------------ 1 file changed, 229 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index 674a79b6b85ef..958e6ed81d3a7 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -38,235 +38,6 @@ public ProjectState(StateSet owner, ProjectId projectId) _owner = owner; _projectId = projectId; } - - ///// - ///// Return all diagnostics for the given project stored in this state - ///// - //public async Task GetAnalysisDataAsync(Project project, CancellationToken cancellationToken) - //{ - // // make a copy of last result. - // Contract.ThrowIfFalse(_projectId == project.Id); - - // var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); - // if (lastResult.IsDefault) - // return LoadInitialAnalysisData(project, version, cancellationToken); - - // RoslynDebug.Assert(lastResult.DocumentIds != null); - - // // PERF: avoid loading data if version is not right one. - // // avoid loading data flag is there as a strictly perf optimization. - // if (lastResult.Version != version) - // { - // return lastResult; - // } - - // // if given project doesnt have any diagnostics, return empty. - // if (lastResult.IsEmpty) - // { - // return DiagnosticAnalysisResult.CreateEmpty(lastResult.ProjectId, lastResult.Version); - // } - - // // loading data can be canceled any time. - // var serializerVersion = lastResult.Version; - // var builder = new Builder(project, lastResult.Version, lastResult.DocumentIds); - - // foreach (var documentId in lastResult.DocumentIds) - // { - // cancellationToken.ThrowIfCancellationRequested(); - - // var document = project.GetDocument(documentId); - // if (document == null) - // continue; - - // if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder)) - // { - // Debug.Assert(lastResult.Version == VersionStamp.Default); - - // // this can happen if we merged back active file diagnostics back to project state but - // // project state didn't have diagnostics for the file yet. (since project state was staled) - // continue; - // } - // } - - // if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder)) - // { - // // this can happen if SaveAsync is not yet called but active file merge happened. one of case is if user did build before the very first - // // analysis happened. - // } - - // return builder.ToResult(); - //} - - ///// - ///// Return all diagnostics for the given document stored in this state including non local diagnostics for this document - ///// - //public async Task GetAnalysisDataAsync(TextDocument document, CancellationToken cancellationToken) - //{ - // // make a copy of last result. - // var lastResult = _lastResult; - // Contract.ThrowIfFalse(lastResult.ProjectId == document.Project.Id); - - // var version = await GetDiagnosticVersionAsync(document.Project, cancellationToken).ConfigureAwait(false); - // if (lastResult.IsDefault) - // return LoadInitialAnalysisData(document, version); - - // // if given document doesnt have any diagnostics, return empty. - // if (IsEmpty(lastResult, document.Id)) - // { - // return DiagnosticAnalysisResult.CreateEmpty(lastResult.ProjectId, lastResult.Version); - // } - - // // loading data can be canceled any time. - // var serializerVersion = lastResult.Version; - // var builder = new Builder(document.Project, lastResult.Version); - - // if (!TryGetDiagnosticsFromInMemoryStorage(serializerVersion, document, builder)) - // { - // Debug.Assert(lastResult.Version == VersionStamp.Default); - - // // this can happen if we merged back active file diagnostics back to project state but - // // project state didn't have diagnostics for the file yet. (since project state was staled) - // } - - // return builder.ToResult(); - //} - - ///// - ///// Return all no location diagnostics for the given project stored in this state - ///// - //public async Task GetProjectAnalysisDataAsync(Project project, CancellationToken cancellationToken) - //{ - // // make a copy of last result. - // var lastResult = _lastResult; - // Contract.ThrowIfFalse(lastResult.ProjectId == project.Id); - - // var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); - // if (lastResult.IsDefault) - // return LoadInitialProjectAnalysisData(project, version); - - // // if given document doesn't have any diagnostics, return empty. - // if (lastResult.IsEmpty) - // { - // return DiagnosticAnalysisResult.CreateEmpty(lastResult.ProjectId, lastResult.Version); - // } - - // // loading data can be canceled any time. - // var serializerVersion = lastResult.Version; - // var builder = new Builder(project, lastResult.Version); - - // if (!TryGetProjectDiagnosticsFromInMemoryStorage(serializerVersion, project, builder)) - // { - // // this can happen if SaveAsync is not yet called but active file merge happened. one of case is if user did build before the very first - // // analysis happened. - // } - - // return builder.ToResult(); - //} - - //public async ValueTask SaveToInMemoryStorageAsync(Project project, DiagnosticAnalysisResult result) - //{ - // Contract.ThrowIfTrue(result.IsAggregatedForm); - // Contract.ThrowIfNull(result.DocumentIds); - - // RemoveInMemoryCache(_lastResult); - - // using var _ = PooledHashSet.GetInstance(out var documentIdsToProcess); - // documentIdsToProcess.AddRange(_lastResult.DocumentIdsOrEmpty); - // documentIdsToProcess.AddRange(result.DocumentIdsOrEmpty); - - // // save last aggregated form of analysis result - // _lastResult = result.ToAggregatedForm(); - - // // serialization can't be canceled. - // var serializerVersion = result.Version; - - // foreach (var documentId in documentIdsToProcess) - // { - // var document = project.GetTextDocument(documentId); - - // // If we couldn't find a normal document, and all features are enabled for source generated - // // documents, attempt to locate a matching source generated document in the project. - // if (document is null - // && project.Solution.Services.GetService()?.EnableDiagnosticsInSourceGeneratedFiles == true) - // { - // document = await project.GetSourceGeneratedDocumentAsync(documentId, CancellationToken.None).ConfigureAwait(false); - // } - - // if (document == null) - // { - // // it can happen with build synchronization since, in build case, - // // we don't have actual snapshot (we have no idea what sources out of proc build has picked up) - // // so we might be out of sync. - // // example of such cases will be changing anything about solution while building is going on. - // // it can be user explicit actions such as unloading project, deleting a file, but also it can be - // // something project system or roslyn workspace does such as populating workspace right after - // // solution is loaded. - // continue; - // } - - // AddToInMemoryStorage(serializerVersion, new(document.Id), SyntaxStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Syntax)); - // AddToInMemoryStorage(serializerVersion, new(document.Id), SemanticStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Semantic)); - // AddToInMemoryStorage(serializerVersion, new(document.Id), NonLocalStateName, result.GetDocumentDiagnostics(document.Id, AnalysisKind.NonLocal)); - // } - - // AddToInMemoryStorage(serializerVersion, new(result.ProjectId), NonLocalStateName, result.GetOtherDiagnostics()); - //} - - private static bool IsEmpty(DiagnosticAnalysisResult result, DocumentId documentId) - => !result.DocumentIdsOrEmpty.Contains(documentId); - - // we have this builder to avoid allocating collections unnecessarily. - private sealed class Builder - { - private readonly Project _project; - private readonly VersionStamp _version; - private readonly ImmutableHashSet? _documentIds; - - private ImmutableDictionary>.Builder? _syntaxLocals; - private ImmutableDictionary>.Builder? _semanticLocals; - private ImmutableDictionary>.Builder? _nonLocals; - private ImmutableArray _others; - - public Builder(Project project, VersionStamp version, ImmutableHashSet? documentIds = null) - { - _project = project; - _version = version; - _documentIds = documentIds; - } - - public void AddSyntaxLocals(DocumentId documentId, ImmutableArray diagnostics) - => Add(ref _syntaxLocals, documentId, diagnostics); - - public void AddSemanticLocals(DocumentId documentId, ImmutableArray diagnostics) - => Add(ref _semanticLocals, documentId, diagnostics); - - public void AddNonLocals(DocumentId documentId, ImmutableArray diagnostics) - => Add(ref _nonLocals, documentId, diagnostics); - - public void AddOthers(ImmutableArray diagnostics) - => _others = diagnostics; - - private void Add(ref ImmutableDictionary>.Builder? locals, DocumentId documentId, ImmutableArray diagnostics) - { - if (_project.GetDocument(documentId)?.SupportsDiagnostics() == false) - { - return; - } - - locals ??= ImmutableDictionary.CreateBuilder>(); - locals.Add(documentId, diagnostics); - } - - public DiagnosticAnalysisResult ToResult() - { - return DiagnosticAnalysisResult.Create(_project, _version, - _syntaxLocals?.ToImmutable() ?? ImmutableDictionary>.Empty, - _semanticLocals?.ToImmutable() ?? ImmutableDictionary>.Empty, - _nonLocals?.ToImmutable() ?? ImmutableDictionary>.Empty, - _others.NullToEmpty(), - _documentIds); - } - } } } } From 0e07eb2bf1c6fd73852ba5c58a2440408de2168e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 10:52:25 -0800 Subject: [PATCH 130/305] remove unneeded code from ProjectState --- .../DiagnosticIncrementalAnalyzer.ProjectState.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index 958e6ed81d3a7..0f38e837526bd 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -2,18 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; -using System.Diagnostics; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; -using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.SolutionCrawler; -using Microsoft.CodeAnalysis.Workspaces.Diagnostics; -using Roslyn.Utilities; - namespace Microsoft.CodeAnalysis.Diagnostics; internal partial class DiagnosticAnalyzerService From 61fd97599508561c3fbdf34dba3afad34db5751e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 10:54:37 -0800 Subject: [PATCH 131/305] Remove 'initial result' concept --- .../Diagnostics/DiagnosticAnalysisResult.cs | 41 ++----------------- 1 file changed, 3 insertions(+), 38 deletions(-) diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs index b049f0413be31..c34060a8c7584 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs @@ -32,40 +32,23 @@ internal readonly struct DiagnosticAnalysisResult /// /// Syntax diagnostics from this file. /// - private readonly ImmutableDictionary>? _syntaxLocals; + private readonly ImmutableDictionary> _syntaxLocals; /// /// Semantic diagnostics from this file. /// - private readonly ImmutableDictionary>? _semanticLocals; + private readonly ImmutableDictionary> _semanticLocals; /// /// Diagnostics that were produced for these documents, but came from the analysis of other files. /// - private readonly ImmutableDictionary>? _nonLocals; + private readonly ImmutableDictionary> _nonLocals; /// /// Diagnostics that don't have locations. /// private readonly ImmutableArray _others; - private DiagnosticAnalysisResult( - ProjectId projectId, - VersionStamp version, - ImmutableHashSet? documentIds, - bool isEmpty) - { - ProjectId = projectId; - Version = version; - DocumentIds = documentIds; - IsEmpty = isEmpty; - - _syntaxLocals = null; - _semanticLocals = null; - _nonLocals = null; - _others = default; - } - private DiagnosticAnalysisResult( ProjectId projectId, VersionStamp version, @@ -104,15 +87,6 @@ public static DiagnosticAnalysisResult CreateEmpty(ProjectId projectId, VersionS others: []); } - public static DiagnosticAnalysisResult CreateInitialResult(ProjectId projectId) - { - return new DiagnosticAnalysisResult( - projectId, - version: VersionStamp.Default, - documentIds: null, - isEmpty: true); - } - public static DiagnosticAnalysisResult Create( Project project, VersionStamp version, @@ -151,12 +125,6 @@ public static DiagnosticAnalysisResult CreateFromBuilder(DiagnosticAnalysisResul // aggregated form means it has aggregated information but no actual data. public bool IsAggregatedForm => _syntaxLocals == null; - // default analysis result - public bool IsDefault => DocumentIds == null; - - // make sure we don't return null - public ImmutableHashSet DocumentIdsOrEmpty => DocumentIds ?? []; - private ImmutableDictionary>? GetMap(AnalysisKind kind) => kind switch { @@ -218,9 +186,6 @@ public ImmutableArray GetDocumentDiagnostics(DocumentId document public ImmutableArray GetOtherDiagnostics() => (IsAggregatedForm || IsEmpty) ? [] : _others; - public DiagnosticAnalysisResult ToAggregatedForm() - => new(ProjectId, Version, DocumentIds, IsEmpty); - public DiagnosticAnalysisResult DropExceptSyntax() { // quick bail out From a23b30473bde3a6e25fdce9b59b2873bd2d36d4c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 10:55:19 -0800 Subject: [PATCH 132/305] remove aggregated result concept --- .../Diagnostics/DiagnosticAnalysisResult.cs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs index c34060a8c7584..4419aa6accef9 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs @@ -122,9 +122,6 @@ public static DiagnosticAnalysisResult CreateFromBuilder(DiagnosticAnalysisResul builder.DocumentIds); } - // aggregated form means it has aggregated information but no actual data. - public bool IsAggregatedForm => _syntaxLocals == null; - private ImmutableDictionary>? GetMap(AnalysisKind kind) => kind switch { @@ -137,10 +134,8 @@ public static DiagnosticAnalysisResult CreateFromBuilder(DiagnosticAnalysisResul public ImmutableArray GetAllDiagnostics() { // PERF: don't allocation anything if not needed - if (IsAggregatedForm || IsEmpty) - { + if (IsEmpty) return []; - } Contract.ThrowIfNull(_syntaxLocals); Contract.ThrowIfNull(_semanticLocals); @@ -166,10 +161,8 @@ public ImmutableArray GetAllDiagnostics() public ImmutableArray GetDocumentDiagnostics(DocumentId documentId, AnalysisKind kind) { - if (IsAggregatedForm || IsEmpty) - { + if (IsEmpty) return []; - } var map = GetMap(kind); Contract.ThrowIfNull(map); @@ -184,7 +177,7 @@ public ImmutableArray GetDocumentDiagnostics(DocumentId document } public ImmutableArray GetOtherDiagnostics() - => (IsAggregatedForm || IsEmpty) ? [] : _others; + => IsEmpty ? [] : _others; public DiagnosticAnalysisResult DropExceptSyntax() { From fb967b1b814bd9f6b2ca7946d05c80eaf0d27b5d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 10:57:28 -0800 Subject: [PATCH 133/305] Simplify --- .../Portable/Diagnostics/DiagnosticAnalysisResult.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs index 4419aa6accef9..e8a90ff646adf 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs @@ -133,10 +133,6 @@ public static DiagnosticAnalysisResult CreateFromBuilder(DiagnosticAnalysisResul public ImmutableArray GetAllDiagnostics() { - // PERF: don't allocation anything if not needed - if (IsEmpty) - return []; - Contract.ThrowIfNull(_syntaxLocals); Contract.ThrowIfNull(_semanticLocals); Contract.ThrowIfNull(_nonLocals); @@ -144,13 +140,13 @@ public ImmutableArray GetAllDiagnostics() using var _ = ArrayBuilder.GetInstance(out var builder); - foreach (var data in _syntaxLocals.Values) + foreach (var (_, data) in _syntaxLocals) builder.AddRange(data); - foreach (var data in _semanticLocals.Values) + foreach (var (_, data) in _semanticLocals) builder.AddRange(data); - foreach (var data in _nonLocals.Values) + foreach (var (_, data) in _nonLocals) builder.AddRange(data); foreach (var data in _others) From 58d6940fc097e80024887232ef863e6ef9a79234 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 10:58:27 -0800 Subject: [PATCH 134/305] remove more unnecessary code --- .../Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs index e8a90ff646adf..6f42c5c63a5d7 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs @@ -27,7 +27,6 @@ internal readonly struct DiagnosticAnalysisResult /// The set of documents that has any kind of diagnostics on it. /// public readonly ImmutableHashSet? DocumentIds; - public readonly bool IsEmpty; /// /// Syntax diagnostics from this file. @@ -72,7 +71,6 @@ private DiagnosticAnalysisResult( _others = others; DocumentIds = documentIds ?? GetDocumentIds(syntaxLocals, semanticLocals, nonLocals); - IsEmpty = DocumentIds.IsEmpty && _others.IsEmpty; } public static DiagnosticAnalysisResult CreateEmpty(ProjectId projectId, VersionStamp version) @@ -157,9 +155,6 @@ public ImmutableArray GetAllDiagnostics() public ImmutableArray GetDocumentDiagnostics(DocumentId documentId, AnalysisKind kind) { - if (IsEmpty) - return []; - var map = GetMap(kind); Contract.ThrowIfNull(map); @@ -173,7 +168,7 @@ public ImmutableArray GetDocumentDiagnostics(DocumentId document } public ImmutableArray GetOtherDiagnostics() - => IsEmpty ? [] : _others; + => _others; public DiagnosticAnalysisResult DropExceptSyntax() { From 6619762d0456b7bef005658422b3ac23586de2ce Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 11:04:20 -0800 Subject: [PATCH 135/305] Use cache if available --- ...osticIncrementalAnalyzer_GetDiagnostics.cs | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index 05b2ea44005ce..cfd64cc9fad95 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -100,14 +100,10 @@ private async Task ProduceDiagnosticsAsync( ArrayBuilder builder, CancellationToken cancellationToken) { - // get analyzers that are not suppressed. var stateSetsForProject = await StateManager.GetOrCreateStateSetsAsync(project, cancellationToken).ConfigureAwait(false); var stateSets = stateSetsForProject.Where(s => ShouldIncludeStateSet(project, s)).ToImmutableArrayOrEmpty(); - // unlike the suppressed (disabled) analyzer, we will include hidden diagnostic only analyzers here. - var compilation = await CreateCompilationWithAnalyzersAsync(project, stateSets, Owner.AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); - - var result = await Owner.ComputeProjectAnalysisDataAsync(compilation, project, stateSets, cancellationToken).ConfigureAwait(false); + var result = await GetOrComputeProjectAnalysisDataAsync(stateSets).ConfigureAwait(false); foreach (var stateSet in stateSets) { @@ -131,6 +127,23 @@ private async Task ProduceDiagnosticsAsync( AddIncludedDiagnostics(builder, analysisResult.GetOtherDiagnostics()); } } + + async Task GetOrComputeProjectAnalysisDataAsync(ImmutableArray stateSets) + { + // If there was a 'ForceAnalyzeProjectAsync' run for this project, we can piggy back off of the + // prior computed/cached results as they will be a superset of the results we want. + // + // Note: the caller will loop over *its* state sets, grabbing from the full set of data we've cached + // for this project, and filtering down further. So it's ok to return this potentially larger set. + if (this.Owner._projectToForceAnalysisData.TryGetValue(project, out var box)) + return box.Value.projectAnalysisData; + + // Otherwise, just compute for the state sets we care about. + var compilation = await CreateCompilationWithAnalyzersAsync(project, stateSets, Owner.AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); + + var result = await Owner.ComputeProjectAnalysisDataAsync(compilation, project, stateSets, cancellationToken).ConfigureAwait(false); + return result; + } } private bool ShouldIncludeStateSet(Project project, StateSet stateSet) From 72ea22456b0adbf3cb7c0ac582a3fdd961d5b2f2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 11:05:30 -0800 Subject: [PATCH 136/305] Remove project state concept --- ...gnosticIncrementalAnalyzer.ProjectState.cs | 31 ------------------- .../DiagnosticIncrementalAnalyzer.StateSet.cs | 8 ----- ...IncrementalAnalyzer_IncrementalAnalyzer.cs | 2 -- 3 files changed, 41 deletions(-) delete mode 100644 src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs deleted file mode 100644 index 0f38e837526bd..0000000000000 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.CodeAnalysis.Diagnostics; - -internal partial class DiagnosticAnalyzerService -{ - private partial class DiagnosticIncrementalAnalyzer - { - /// - /// State for diagnostics that belong to a project at given time. - /// - private sealed class ProjectState - { - private const string SyntaxStateName = nameof(SyntaxStateName); - private const string SemanticStateName = nameof(SemanticStateName); - private const string NonLocalStateName = nameof(NonLocalStateName); - - // project id of this state - private readonly StateSet _owner; - private readonly ProjectId _projectId; - - public ProjectState(StateSet owner, ProjectId projectId) - { - _owner = owner; - _projectId = projectId; - } - } - } -} diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs index b7e0eede0d133..6dae99f8f349d 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs @@ -27,7 +27,6 @@ private sealed class StateSet public readonly bool IsHostAnalyzer; private readonly ConcurrentSet _activeDocuments; - private readonly ConcurrentDictionary _projectStates; public StateSet(DiagnosticAnalyzer analyzer, bool isHostAnalyzer) { @@ -35,20 +34,13 @@ public StateSet(DiagnosticAnalyzer analyzer, bool isHostAnalyzer) IsHostAnalyzer = isHostAnalyzer; _activeDocuments = []; - _projectStates = new ConcurrentDictionary(concurrencyLevel: 2, capacity: 1); } public bool IsActiveFile(DocumentId documentId) => _activeDocuments.Contains(documentId); - public bool TryGetProjectState(ProjectId projectId, [NotNullWhen(true)] out ProjectState? state) - => _projectStates.TryGetValue(projectId, out state); - public void AddActiveDocument(DocumentId documentId) => _activeDocuments.Add(documentId); - - public ProjectState GetOrCreateProjectState(ProjectId projectId) - => _projectStates.GetOrAdd(projectId, static (id, self) => new ProjectState(self, id), this); } } } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index e4e8c0b1ce1d1..9ee58036e1f4a 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -36,8 +36,6 @@ public async Task> ForceAnalyzeProjectAsync(Proje var (stateSets, projectAnalysisData) = box.Value; foreach (var stateSet in stateSets) { - var state = stateSet.GetOrCreateProjectState(project.Id); - if (projectAnalysisData.TryGetResult(stateSet.Analyzer, out var analyzerResult)) diagnostics.AddRange(analyzerResult.GetAllDiagnostics()); } From 968028d502ea01c24d430a338b27ca19c73933fb Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 11:12:02 -0800 Subject: [PATCH 137/305] Simplify type --- ...gnosticIncrementalAnalyzer.AnalysisData.cs | 20 +------------------ .../DiagnosticIncrementalAnalyzer.Executor.cs | 2 +- .../EngineV2/DiagnosticIncrementalAnalyzer.cs | 11 ---------- ...osticIncrementalAnalyzer_GetDiagnostics.cs | 3 ++- 4 files changed, 4 insertions(+), 32 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs index 6c426a2faa241..fd51052c6b352 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs @@ -18,27 +18,9 @@ private partial class DiagnosticIncrementalAnalyzer /// Data holder for all diagnostics for a project for an analyzer /// private readonly struct ProjectAnalysisData( - ProjectId projectId, - VersionStamp version, ImmutableDictionary result) { - /// - /// ProjectId of this data - /// - public readonly ProjectId ProjectId = projectId; - - /// - /// Version of the Items - /// - public readonly VersionStamp Version = version; - - /// - /// Current data that matches the version - /// - public readonly ImmutableDictionary Result = result; - - public DiagnosticAnalysisResult GetResult(DiagnosticAnalyzer analyzer) - => GetResultOrEmpty(Result, analyzer, ProjectId, Version); + private readonly ImmutableDictionary Result = result; public bool TryGetResult(DiagnosticAnalyzer analyzer, out DiagnosticAnalysisResult result) => Result.TryGetValue(analyzer, out result); diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs index 1831fc1bbda3c..c49b811fc9cf9 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs @@ -39,7 +39,7 @@ private async Task ComputeProjectAnalysisDataAsync( // Now we run analyzers but filter out some information. So on such projects, there will be some perf degradation. result = await RemoveCompilerSemanticErrorsIfProjectNotLoadedAsync(result, project, cancellationToken).ConfigureAwait(false); - return new ProjectAnalysisData(project.Id, version, result); + return new ProjectAnalysisData(result); } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index b6a713423d547..98b897467f8ff 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.SolutionCrawler; -using Microsoft.CodeAnalysis.Workspaces.Diagnostics; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics; @@ -53,16 +52,6 @@ public DiagnosticIncrementalAnalyzer( internal IGlobalOptionService GlobalOptions { get; } internal DiagnosticAnalyzerInfoCache DiagnosticAnalyzerInfoCache => _diagnosticAnalyzerRunner.AnalyzerInfoCache; - private static DiagnosticAnalysisResult GetResultOrEmpty(ImmutableDictionary map, DiagnosticAnalyzer analyzer, ProjectId projectId, VersionStamp version) - { - if (map.TryGetValue(analyzer, out var result)) - { - return result; - } - - return DiagnosticAnalysisResult.CreateEmpty(projectId, version); - } - public async Task> GetAnalyzersForTestingPurposesOnlyAsync(Project project, CancellationToken cancellationToken) { var analyzers = await _stateManager.GetOrCreateStateSetsAsync(project, cancellationToken).ConfigureAwait(false); diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index cfd64cc9fad95..9b27cd74ff881 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -107,7 +107,8 @@ private async Task ProduceDiagnosticsAsync( foreach (var stateSet in stateSets) { - var analysisResult = result.GetResult(stateSet.Analyzer); + if (!result.TryGetResult(stateSet.Analyzer, out var analysisResult)) + continue; foreach (var documentId in documentIds) { From de1e66f7ebc99e595ad4c6361f5591ca1cf595bf Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 11:14:53 -0800 Subject: [PATCH 138/305] Delete uneeded type --- ...gnosticIncrementalAnalyzer.AnalysisData.cs | 29 ------------------- .../DiagnosticIncrementalAnalyzer.Executor.cs | 4 +-- ...osticIncrementalAnalyzer_GetDiagnostics.cs | 11 +++---- ...IncrementalAnalyzer_IncrementalAnalyzer.cs | 10 +++---- 4 files changed, 13 insertions(+), 41 deletions(-) delete mode 100644 src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs deleted file mode 100644 index fd51052c6b352..0000000000000 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Immutable; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Workspaces.Diagnostics; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.Diagnostics; - -internal partial class DiagnosticAnalyzerService -{ - private partial class DiagnosticIncrementalAnalyzer - { - /// - /// Data holder for all diagnostics for a project for an analyzer - /// - private readonly struct ProjectAnalysisData( - ImmutableDictionary result) - { - private readonly ImmutableDictionary Result = result; - - public bool TryGetResult(DiagnosticAnalyzer analyzer, out DiagnosticAnalysisResult result) - => Result.TryGetValue(analyzer, out result); - } - } -} diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs index c49b811fc9cf9..59221c6856594 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs @@ -23,7 +23,7 @@ private partial class DiagnosticIncrementalAnalyzer /// /// Return all diagnostics that belong to given project for the given StateSets (analyzers) either from cache or by calculating them /// - private async Task ComputeProjectAnalysisDataAsync( + private async Task> ComputeDiagnosticAnalysisResultsAsync( CompilationWithAnalyzersPair? compilationWithAnalyzers, Project project, ImmutableArray stateSets, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.Diagnostics_ProjectDiagnostic, GetProjectLogMessage, project, stateSets, cancellationToken)) @@ -39,7 +39,7 @@ private async Task ComputeProjectAnalysisDataAsync( // Now we run analyzers but filter out some information. So on such projects, there will be some perf degradation. result = await RemoveCompilerSemanticErrorsIfProjectNotLoadedAsync(result, project, cancellationToken).ConfigureAwait(false); - return new ProjectAnalysisData(result); + return result; } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index 9b27cd74ff881..f2896f11104ec 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Workspaces.Diagnostics; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics; @@ -103,11 +104,11 @@ private async Task ProduceDiagnosticsAsync( var stateSetsForProject = await StateManager.GetOrCreateStateSetsAsync(project, cancellationToken).ConfigureAwait(false); var stateSets = stateSetsForProject.Where(s => ShouldIncludeStateSet(project, s)).ToImmutableArrayOrEmpty(); - var result = await GetOrComputeProjectAnalysisDataAsync(stateSets).ConfigureAwait(false); + var result = await GetOrComputeDiagnosticAnalysisResultsAsync(stateSets).ConfigureAwait(false); foreach (var stateSet in stateSets) { - if (!result.TryGetResult(stateSet.Analyzer, out var analysisResult)) + if (!result.TryGetValue(stateSet.Analyzer, out var analysisResult)) continue; foreach (var documentId in documentIds) @@ -129,7 +130,7 @@ private async Task ProduceDiagnosticsAsync( } } - async Task GetOrComputeProjectAnalysisDataAsync(ImmutableArray stateSets) + async Task> GetOrComputeDiagnosticAnalysisResultsAsync(ImmutableArray stateSets) { // If there was a 'ForceAnalyzeProjectAsync' run for this project, we can piggy back off of the // prior computed/cached results as they will be a superset of the results we want. @@ -137,12 +138,12 @@ async Task GetOrComputeProjectAnalysisDataAsync(ImmutableAr // Note: the caller will loop over *its* state sets, grabbing from the full set of data we've cached // for this project, and filtering down further. So it's ok to return this potentially larger set. if (this.Owner._projectToForceAnalysisData.TryGetValue(project, out var box)) - return box.Value.projectAnalysisData; + return box.Value.diagnosticAnalysisResults; // Otherwise, just compute for the state sets we care about. var compilation = await CreateCompilationWithAnalyzersAsync(project, stateSets, Owner.AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); - var result = await Owner.ComputeProjectAnalysisDataAsync(compilation, project, stateSets, cancellationToken).ConfigureAwait(false); + var result = await Owner.ComputeDiagnosticAnalysisResultsAsync(compilation, project, stateSets, cancellationToken).ConfigureAwait(false); return result; } } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 9ee58036e1f4a..a374d4c82d853 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -8,9 +8,9 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Workspaces.Diagnostics; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics; @@ -19,7 +19,7 @@ internal partial class DiagnosticAnalyzerService { private partial class DiagnosticIncrementalAnalyzer { - private readonly ConditionalWeakTable stateSets, ProjectAnalysisData projectAnalysisData)>> _projectToForceAnalysisData = new(); + private readonly ConditionalWeakTable stateSets, ImmutableDictionary diagnosticAnalysisResults)>> _projectToForceAnalysisData = new(); public async Task> ForceAnalyzeProjectAsync(Project project, CancellationToken cancellationToken) { @@ -36,7 +36,7 @@ public async Task> ForceAnalyzeProjectAsync(Proje var (stateSets, projectAnalysisData) = box.Value; foreach (var stateSet in stateSets) { - if (projectAnalysisData.TryGetResult(stateSet.Analyzer, out var analyzerResult)) + if (projectAnalysisData.TryGetValue(stateSet.Analyzer, out var analyzerResult)) diagnostics.AddRange(analyzerResult.GetAllDiagnostics()); } @@ -47,7 +47,7 @@ public async Task> ForceAnalyzeProjectAsync(Proje throw ExceptionUtilities.Unreachable(); } - async Task<(ImmutableArray stateSets, ProjectAnalysisData projectAnalysisData)> ComputeForceAnalyzeProjectAsync() + async Task<(ImmutableArray stateSets, ImmutableDictionary diagnosticAnalysisResults)> ComputeForceAnalyzeProjectAsync() { var allStateSets = await _stateManager.GetOrCreateStateSetsAsync(project, cancellationToken).ConfigureAwait(false); var fullSolutionAnalysisStateSets = allStateSets.WhereAsArray( @@ -57,7 +57,7 @@ public async Task> ForceAnalyzeProjectAsync(Proje var compilationWithAnalyzers = await CreateCompilationWithAnalyzersAsync( project, fullSolutionAnalysisStateSets, AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); - var projectAnalysisData = await ComputeProjectAnalysisDataAsync(compilationWithAnalyzers, project, fullSolutionAnalysisStateSets, cancellationToken).ConfigureAwait(false); + var projectAnalysisData = await ComputeDiagnosticAnalysisResultsAsync(compilationWithAnalyzers, project, fullSolutionAnalysisStateSets, cancellationToken).ConfigureAwait(false); return (fullSolutionAnalysisStateSets, projectAnalysisData); } } From 20ebfb1e5401dc432fecf788159ff5860525fad6 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 11:16:43 -0800 Subject: [PATCH 139/305] Simplify state set --- ...gnosticIncrementalAnalyzer.StateManager.cs | 2 +- .../DiagnosticIncrementalAnalyzer.StateSet.cs | 20 ------------------- ...crementalAnalyzer_GetDiagnosticsForSpan.cs | 1 - 3 files changed, 1 insertion(+), 22 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs index 691317e766405..cb565446be94f 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs @@ -41,7 +41,7 @@ private partial class StateManager /// Guard around updating _projectAnalyzerStateMap. This is used in UpdateProjectStateSets to avoid /// duplicated calculations for a project during contentious calls. /// - private readonly SemaphoreSlim _projectAnalyzerStateMapGuard = new(1); + private readonly SemaphoreSlim _projectAnalyzerStateMapGuard = new(initialCount: 1); public StateManager(Workspace workspace, DiagnosticAnalyzerInfoCache analyzerInfoCache) { diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs index 6dae99f8f349d..1323696fd9d7c 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs @@ -2,16 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Options; -using Roslyn.Utilities; - namespace Microsoft.CodeAnalysis.Diagnostics; internal partial class DiagnosticAnalyzerService @@ -26,21 +16,11 @@ private sealed class StateSet public readonly DiagnosticAnalyzer Analyzer; public readonly bool IsHostAnalyzer; - private readonly ConcurrentSet _activeDocuments; - public StateSet(DiagnosticAnalyzer analyzer, bool isHostAnalyzer) { Analyzer = analyzer; IsHostAnalyzer = isHostAnalyzer; - - _activeDocuments = []; } - - public bool IsActiveFile(DocumentId documentId) - => _activeDocuments.Contains(documentId); - - public void AddActiveDocument(DocumentId documentId) - => _activeDocuments.Add(documentId); } } } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs index 88b41904c5222..22c63e847b09a 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs @@ -210,7 +210,6 @@ public async Task GetAsync(ArrayBuilder list, CancellationToken if (includeSyntax || includeSemantic) { - stateSet.AddActiveDocument(_document.Id); if (includeSyntax) { syntaxAnalyzers.Add(new AnalyzerWithState(stateSet.Analyzer, stateSet.IsHostAnalyzer)); From da333acec5b78a82f419c65edf44e0bfba0ee97b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 11:19:40 -0800 Subject: [PATCH 140/305] remove redundant type --- ...lAnalyzer.IncrementalMemberEditAnalyzer.cs | 20 ++++++++--------- ...crementalAnalyzer_GetDiagnosticsForSpan.cs | 22 +++++++++---------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs index 8491b88f7638a..ec86bedf339a8 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs @@ -46,7 +46,7 @@ public void UpdateDocumentWithCachedDiagnostics(Document document) public async Task>> ComputeDiagnosticsAsync( DocumentAnalysisExecutor executor, - ImmutableArray analyzersWithState, + ImmutableArray analyzersWithState, VersionStamp version, Func>> computeAnalyzerDiagnosticsAsync, Func>>> computeDiagnosticsNonIncrementallyAsync, @@ -76,9 +76,9 @@ public async Task.GetInstance(out var spanBasedAnalyzers); - using var _2 = ArrayBuilder.GetInstance(out var documentBasedAnalyzers); - (AnalyzerWithState analyzerWithState, bool spanBased)? compilerAnalyzerData = null; + using var _1 = ArrayBuilder.GetInstance(out var spanBasedAnalyzers); + using var _2 = ArrayBuilder.GetInstance(out var documentBasedAnalyzers); + (StateSet analyzerWithState, bool spanBased)? compilerAnalyzerData = null; foreach (var analyzerWithState in analyzersWithState) { // Check if we have existing cached diagnostics for this analyzer whose version matches the @@ -93,7 +93,7 @@ public async Task oldMemberSpans, PooledDictionary> builder) { @@ -136,12 +136,12 @@ async Task ExecuteCompilerAnalyzerAsync( var (analyzerWithState, spanBased) = compilerAnalyzerData.Value; var span = spanBased ? changedMember.FullSpan : (TextSpan?)null; executor = executor.With(analysisScope.WithSpan(span)); - using var _ = ArrayBuilder.GetInstance(1, analyzerWithState, out var analyzersWithState); + using var _ = ArrayBuilder.GetInstance(1, analyzerWithState, out var analyzersWithState); await ExecuteAnalyzersAsync(executor, analyzersWithState, oldMemberSpans, builder).ConfigureAwait(false); } async Task ExecuteSpanBasedAnalyzersAsync( - ArrayBuilder analyzersWithState, + ArrayBuilder analyzersWithState, ImmutableArray oldMemberSpans, PooledDictionary> builder) { @@ -153,7 +153,7 @@ async Task ExecuteSpanBasedAnalyzersAsync( } async Task ExecuteDocumentBasedAnalyzersAsync( - ArrayBuilder analyzersWithState, + ArrayBuilder analyzersWithState, ImmutableArray oldMemberSpans, PooledDictionary> builder) { @@ -166,7 +166,7 @@ async Task ExecuteDocumentBasedAnalyzersAsync( async Task ExecuteAnalyzersAsync( DocumentAnalysisExecutor executor, - ArrayBuilder analyzersWithState, + ArrayBuilder analyzersWithState, ImmutableArray oldMemberSpans, PooledDictionary> builder) { diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs index 22c63e847b09a..ad5549f909833 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs @@ -175,15 +175,15 @@ public async Task GetAsync(ArrayBuilder list, CancellationToken try { // Try to get cached diagnostics, and also compute non-cached state sets that need diagnostic computation. - using var _1 = ArrayBuilder.GetInstance(out var syntaxAnalyzers); + using var _1 = ArrayBuilder.GetInstance(out var syntaxAnalyzers); // If we are performing incremental member edit analysis to compute diagnostics incrementally, // we divide the analyzers into those that support span-based incremental analysis and // those that do not support incremental analysis and must be executed for the entire document. // Otherwise, if we are not performing incremental analysis, all semantic analyzers are added // to the span-based analyzer set as we want to compute diagnostics only for the given span. - using var _2 = ArrayBuilder.GetInstance(out var semanticSpanBasedAnalyzers); - using var _3 = ArrayBuilder.GetInstance(out var semanticDocumentBasedAnalyzers); + using var _2 = ArrayBuilder.GetInstance(out var semanticSpanBasedAnalyzers); + using var _3 = ArrayBuilder.GetInstance(out var semanticDocumentBasedAnalyzers); using var _4 = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.RequestDiagnostics_Summary, $"Pri{_priorityProvider.Priority.GetPriorityInt()}"); @@ -212,7 +212,7 @@ public async Task GetAsync(ArrayBuilder list, CancellationToken { if (includeSyntax) { - syntaxAnalyzers.Add(new AnalyzerWithState(stateSet.Analyzer, stateSet.IsHostAnalyzer)); + syntaxAnalyzers.Add(stateSet); } if (includeSemantic) @@ -221,7 +221,7 @@ public async Task GetAsync(ArrayBuilder list, CancellationToken stateSet.Analyzer, _incrementalAnalysis, semanticSpanBasedAnalyzers, semanticDocumentBasedAnalyzers); - stateSets.Add(new AnalyzerWithState(stateSet.Analyzer, stateSet.IsHostAnalyzer)); + stateSets.Add(stateSet); } } } @@ -272,11 +272,11 @@ static bool ShouldIncludeAnalyzer( return true; } - static ArrayBuilder GetSemanticAnalysisSelectedStates( + static ArrayBuilder GetSemanticAnalysisSelectedStates( DiagnosticAnalyzer analyzer, bool incrementalAnalysis, - ArrayBuilder semanticSpanBasedAnalyzers, - ArrayBuilder semanticDocumentBasedAnalyzers) + ArrayBuilder semanticSpanBasedAnalyzers, + ArrayBuilder semanticDocumentBasedAnalyzers) { if (!incrementalAnalysis) { @@ -296,7 +296,7 @@ static ArrayBuilder GetSemanticAnalysisSelectedStates( } private async Task ComputeDocumentDiagnosticsAsync( - ImmutableArray analyzersWithState, + ImmutableArray analyzersWithState, AnalysisKind kind, TextSpan? span, ArrayBuilder builder, @@ -306,7 +306,7 @@ private async Task ComputeDocumentDiagnosticsAsync( Debug.Assert(!incrementalAnalysis || kind == AnalysisKind.Semantic); Debug.Assert(!incrementalAnalysis || analyzersWithState.All(analyzerWithState => analyzerWithState.Analyzer.SupportsSpanBasedSemanticDiagnosticAnalysis())); - using var _ = ArrayBuilder.GetInstance(analyzersWithState.Length, out var filteredAnalyzersWithStateBuilder); + using var _ = ArrayBuilder.GetInstance(analyzersWithState.Length, out var filteredAnalyzersWithStateBuilder); foreach (var analyzerWithState in analyzersWithState) { Debug.Assert(_priorityProvider.MatchesPriority(analyzerWithState.Analyzer)); @@ -460,7 +460,5 @@ private bool ShouldInclude(DiagnosticData diagnostic) && (_shouldIncludeDiagnostic == null || _shouldIncludeDiagnostic(diagnostic.Id)); } } - - private sealed record class AnalyzerWithState(DiagnosticAnalyzer Analyzer, bool IsHostAnalyzer); } } From e71f60c91f21377bc0c508e5a060dde9a2c9e14b Mon Sep 17 00:00:00 2001 From: "gel@microsoft.com" Date: Fri, 7 Feb 2025 12:03:41 -0800 Subject: [PATCH 141/305] Address review comments --- eng/config/PublishData.json | 1 + ...actCopilotLspServiceDocumentRequestHandler.cs | 9 ++++----- ...equestContext.cs => CopilotRequestContext.cs} | 11 +++++++---- .../Copilot/InternalAPI.Unshipped.txt | 16 ++++++++-------- .../LspServices/CopilotMethodAttribute.cs | 4 ++++ .../ExportCopilotStatelessLspServiceAttribute.cs | 1 + .../Copilot/LspServices/ICopilotLspService.cs | 3 +++ 7 files changed, 28 insertions(+), 17 deletions(-) rename src/LanguageServer/ExternalAccess/Copilot/Handler/{RequestContext.cs => CopilotRequestContext.cs} (54%) diff --git a/eng/config/PublishData.json b/eng/config/PublishData.json index 2059299b840fa..14de01d982725 100644 --- a/eng/config/PublishData.json +++ b/eng/config/PublishData.json @@ -84,6 +84,7 @@ "Microsoft.CodeAnalysis.ExternalAccess.Xamarin.Remote": "vs-impl", "Microsoft.CodeAnalysis.ExternalAccess.Xaml": "vs-impl", "Microsoft.CodeAnalysis.ExternalAccess.DotNetWatch": "vs-impl", + "Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot": "vs-impl", "Microsoft.CodeAnalysis.Remote.Razor.ServiceHub": "vs-impl", "Microsoft.CodeAnalysis.Remote.ServiceHub": "vs-impl", "Microsoft.CodeAnalysis.Remote.Workspaces": "vs-impl", diff --git a/src/LanguageServer/ExternalAccess/Copilot/Handler/AbstractCopilotLspServiceDocumentRequestHandler.cs b/src/LanguageServer/ExternalAccess/Copilot/Handler/AbstractCopilotLspServiceDocumentRequestHandler.cs index 59e8e7df7c49f..a9965a35987e7 100644 --- a/src/LanguageServer/ExternalAccess/Copilot/Handler/AbstractCopilotLspServiceDocumentRequestHandler.cs +++ b/src/LanguageServer/ExternalAccess/Copilot/Handler/AbstractCopilotLspServiceDocumentRequestHandler.cs @@ -9,13 +9,12 @@ using Microsoft.CommonLanguageServerProtocol.Framework; using Roslyn.LanguageServer.Protocol; -using LspRequestContext = Microsoft.CodeAnalysis.LanguageServer.Handler.RequestContext; - namespace Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot; +/// internal abstract class AbstractCopilotLspServiceDocumentRequestHandler : ILspServiceDocumentRequestHandler { - public abstract Task HandleRequestAsync(TRequest request, RequestContext context, CancellationToken cancellationToken); + public abstract Task HandleRequestAsync(TRequest request, CopilotRequestContext context, CancellationToken cancellationToken); public abstract Uri GetTextDocumentUri(TRequest request); bool IMethodHandler.MutatesSolutionState => false; @@ -24,6 +23,6 @@ internal abstract class AbstractCopilotLspServiceDocumentRequestHandler.GetTextDocumentIdentifier(TRequest request) => new() { Uri = GetTextDocumentUri(request) }; - Task IRequestHandler.HandleRequestAsync(TRequest request, LspRequestContext context, CancellationToken cancellationToken) - => HandleRequestAsync(request, new RequestContext(context), cancellationToken); + Task IRequestHandler.HandleRequestAsync(TRequest request, RequestContext context, CancellationToken cancellationToken) + => HandleRequestAsync(request, new CopilotRequestContext(context), cancellationToken); } diff --git a/src/LanguageServer/ExternalAccess/Copilot/Handler/RequestContext.cs b/src/LanguageServer/ExternalAccess/Copilot/Handler/CopilotRequestContext.cs similarity index 54% rename from src/LanguageServer/ExternalAccess/Copilot/Handler/RequestContext.cs rename to src/LanguageServer/ExternalAccess/Copilot/Handler/CopilotRequestContext.cs index 86b7f50b96346..4203004de33a3 100644 --- a/src/LanguageServer/ExternalAccess/Copilot/Handler/RequestContext.cs +++ b/src/LanguageServer/ExternalAccess/Copilot/Handler/CopilotRequestContext.cs @@ -2,16 +2,19 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using LspRequestContext = Microsoft.CodeAnalysis.LanguageServer.Handler.RequestContext; +using Microsoft.CodeAnalysis.LanguageServer.Handler; namespace Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot; -internal readonly struct RequestContext(LspRequestContext context) +/// +/// Context for requests handled by handlers like +/// +internal readonly struct CopilotRequestContext(RequestContext context) { - /// + /// public Solution? Solution => context.Solution; - /// + /// public Document? Document => context.Document; public T GetRequiredService() where T : class => context.GetRequiredService(); diff --git a/src/LanguageServer/ExternalAccess/Copilot/InternalAPI.Unshipped.txt b/src/LanguageServer/ExternalAccess/Copilot/InternalAPI.Unshipped.txt index 4242dae985e23..77d982df164ff 100644 --- a/src/LanguageServer/ExternalAccess/Copilot/InternalAPI.Unshipped.txt +++ b/src/LanguageServer/ExternalAccess/Copilot/InternalAPI.Unshipped.txt @@ -1,6 +1,6 @@ #nullable enable abstract Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspServiceDocumentRequestHandler.GetTextDocumentUri(TRequest request) -> System.Uri! -abstract Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspServiceDocumentRequestHandler.HandleRequestAsync(TRequest request, Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.RequestContext context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! +abstract Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspServiceDocumentRequestHandler.HandleRequestAsync(TRequest request, Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.CopilotRequestContext context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! abstract Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspServiceFactory.CreateCopilotLspService(Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.CopilotLspServices lspServices) -> Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.ICopilotLspService! Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspServiceDocumentRequestHandler Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.AbstractCopilotLspServiceDocumentRequestHandler.AbstractCopilotLspServiceDocumentRequestHandler() -> void @@ -12,14 +12,14 @@ Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.CopilotLspServices. Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.CopilotLspServices.GetService() -> T? Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.CopilotMethodAttribute Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.CopilotMethodAttribute.CopilotMethodAttribute(string! method) -> void +Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.CopilotRequestContext +Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.CopilotRequestContext.CopilotRequestContext() -> void +Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.CopilotRequestContext.CopilotRequestContext(Microsoft.CodeAnalysis.LanguageServer.Handler.RequestContext context) -> void +Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.CopilotRequestContext.Document.get -> Microsoft.CodeAnalysis.Document? +Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.CopilotRequestContext.GetRequiredService() -> T! +Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.CopilotRequestContext.Solution.get -> Microsoft.CodeAnalysis.Solution? Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.ExportCopilotLspServiceFactoryAttribute Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.ExportCopilotLspServiceFactoryAttribute.ExportCopilotLspServiceFactoryAttribute(System.Type! type) -> void Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.ExportCopilotStatelessLspServiceAttribute Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.ExportCopilotStatelessLspServiceAttribute.ExportCopilotStatelessLspServiceAttribute(System.Type! type) -> void -Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.ICopilotLspService -Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.RequestContext -Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.RequestContext.Document.get -> Microsoft.CodeAnalysis.Document? -Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.RequestContext.GetRequiredService() -> T! -Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.RequestContext.RequestContext() -> void -Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.RequestContext.RequestContext(Microsoft.CodeAnalysis.LanguageServer.Handler.RequestContext context) -> void -Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.RequestContext.Solution.get -> Microsoft.CodeAnalysis.Solution? \ No newline at end of file +Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot.ICopilotLspService \ No newline at end of file diff --git a/src/LanguageServer/ExternalAccess/Copilot/LspServices/CopilotMethodAttribute.cs b/src/LanguageServer/ExternalAccess/Copilot/LspServices/CopilotMethodAttribute.cs index decb2ed29d347..024493d3aafee 100644 --- a/src/LanguageServer/ExternalAccess/Copilot/LspServices/CopilotMethodAttribute.cs +++ b/src/LanguageServer/ExternalAccess/Copilot/LspServices/CopilotMethodAttribute.cs @@ -6,4 +6,8 @@ namespace Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot; +/// +/// An attribute which identifies the method which a Copilot request handler like +/// implements. +/// internal sealed class CopilotMethodAttribute(string method) : MethodAttribute(method); diff --git a/src/LanguageServer/ExternalAccess/Copilot/LspServices/ExportCopilotStatelessLspServiceAttribute.cs b/src/LanguageServer/ExternalAccess/Copilot/LspServices/ExportCopilotStatelessLspServiceAttribute.cs index 289af03428b10..c07744f489501 100644 --- a/src/LanguageServer/ExternalAccess/Copilot/LspServices/ExportCopilotStatelessLspServiceAttribute.cs +++ b/src/LanguageServer/ExternalAccess/Copilot/LspServices/ExportCopilotStatelessLspServiceAttribute.cs @@ -7,5 +7,6 @@ namespace Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot; +/// internal sealed class ExportCopilotStatelessLspServiceAttribute(Type type) : ExportCSharpVisualBasicStatelessLspServiceAttribute(type, WellKnownLspServerKinds.Any); diff --git a/src/LanguageServer/ExternalAccess/Copilot/LspServices/ICopilotLspService.cs b/src/LanguageServer/ExternalAccess/Copilot/LspServices/ICopilotLspService.cs index 11774cd3ab9dd..593eba8b3fbe3 100644 --- a/src/LanguageServer/ExternalAccess/Copilot/LspServices/ICopilotLspService.cs +++ b/src/LanguageServer/ExternalAccess/Copilot/LspServices/ICopilotLspService.cs @@ -4,6 +4,9 @@ namespace Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Copilot; +/// +/// Interface to mark a Copilot LSP service. +/// internal interface ICopilotLspService : ILspService { } From 5f58f9a4ada57ae63d70b251dc47860317a2a04a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 12:31:45 -0800 Subject: [PATCH 142/305] Doc --- .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index a374d4c82d853..c8d7b5f15551c 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -19,6 +19,12 @@ internal partial class DiagnosticAnalyzerService { private partial class DiagnosticIncrementalAnalyzer { + /// + /// Cached data from a real instance to the cached diagnostic data produced by + /// all the analyzers for the project. This data can then be used by to speed up subsequent calls through the normal entry points as long as the project hasn't changed at all. + /// private readonly ConditionalWeakTable stateSets, ImmutableDictionary diagnosticAnalysisResults)>> _projectToForceAnalysisData = new(); public async Task> ForceAnalyzeProjectAsync(Project project, CancellationToken cancellationToken) From 42e54b223afa47456eb0ddfea0a9d623262d3e49 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 12:36:11 -0800 Subject: [PATCH 143/305] Simplify --- .../Diagnostics/DiagnosticAnalysisResult.cs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs index 6f42c5c63a5d7..511a85c79cefc 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; @@ -131,26 +132,20 @@ public static DiagnosticAnalysisResult CreateFromBuilder(DiagnosticAnalysisResul public ImmutableArray GetAllDiagnostics() { - Contract.ThrowIfNull(_syntaxLocals); - Contract.ThrowIfNull(_semanticLocals); - Contract.ThrowIfNull(_nonLocals); - Contract.ThrowIfTrue(_others.IsDefault); - - using var _ = ArrayBuilder.GetInstance(out var builder); + using var result = TemporaryArray.Empty; foreach (var (_, data) in _syntaxLocals) - builder.AddRange(data); + result.AddRange(data); foreach (var (_, data) in _semanticLocals) - builder.AddRange(data); + result.AddRange(data); foreach (var (_, data) in _nonLocals) - builder.AddRange(data); + result.AddRange(data); - foreach (var data in _others) - builder.AddRange(data); + result.AddRange(_others); - return builder.ToImmutableAndClear(); + return result.ToImmutableAndClear(); } public ImmutableArray GetDocumentDiagnostics(DocumentId documentId, AnalysisKind kind) From 6e6d12baa93537c8eea6f837c9239cc13ed3ac4c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 12:44:47 -0800 Subject: [PATCH 144/305] Make local functinos --- .../DiagnosticIncrementalAnalyzer.Executor.cs | 323 +++++++++--------- 1 file changed, 163 insertions(+), 160 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs index 59221c6856594..67f49b5772bcb 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs @@ -32,7 +32,7 @@ private async Task> RemoveCompilerSemanticErrorsIfProjectNotLoadedAsync( - ImmutableDictionary result, Project project, CancellationToken cancellationToken) - { - // see whether solution is loaded successfully - var projectLoadedSuccessfully = await project.HasSuccessfullyLoadedAsync(cancellationToken).ConfigureAwait(false); - if (projectLoadedSuccessfully) + static async Task> RemoveCompilerSemanticErrorsIfProjectNotLoadedAsync( + ImmutableDictionary result, Project project, CancellationToken cancellationToken) { - return result; - } + // see whether solution is loaded successfully + var projectLoadedSuccessfully = await project.HasSuccessfullyLoadedAsync(cancellationToken).ConfigureAwait(false); + if (projectLoadedSuccessfully) + { + return result; + } - var compilerAnalyzer = project.Solution.SolutionState.Analyzers.GetCompilerDiagnosticAnalyzer(project.Language); - if (compilerAnalyzer == null) - { - // this language doesn't support compiler analyzer - return result; - } + var compilerAnalyzer = project.Solution.SolutionState.Analyzers.GetCompilerDiagnosticAnalyzer(project.Language); + if (compilerAnalyzer == null) + { + // this language doesn't support compiler analyzer + return result; + } - if (!result.TryGetValue(compilerAnalyzer, out var analysisResult)) - { - // no result from compiler analyzer - return result; - } + if (!result.TryGetValue(compilerAnalyzer, out var analysisResult)) + { + // no result from compiler analyzer + return result; + } - Logger.Log(FunctionId.Diagnostics_ProjectDiagnostic, p => $"Failed to Load Successfully ({p.FilePath ?? p.Name})", project); + Logger.Log(FunctionId.Diagnostics_ProjectDiagnostic, p => $"Failed to Load Successfully ({p.FilePath ?? p.Name})", project); - // get rid of any result except syntax from compiler analyzer result - var newCompilerAnalysisResult = analysisResult.DropExceptSyntax(); + // get rid of any result except syntax from compiler analyzer result + var newCompilerAnalysisResult = analysisResult.DropExceptSyntax(); - // return new result - return result.SetItem(compilerAnalyzer, newCompilerAnalysisResult); - } + // return new result + return result.SetItem(compilerAnalyzer, newCompilerAnalysisResult); + } - /// - /// Calculate all diagnostics for a given project using analyzers referenced by the project and specified IDE analyzers. - /// - private async Task> ComputeDiagnosticsAsync( - CompilationWithAnalyzersPair? compilationWithAnalyzers, Project project, ImmutableArray ideAnalyzers, CancellationToken cancellationToken) - { - try + // + // Calculate all diagnostics for a given project using analyzers referenced by the project and specified IDE analyzers. + // + async Task> ComputeDiagnosticsForAnalyzersAsync( + CompilationWithAnalyzersPair? compilationWithAnalyzers, + Project project, + ImmutableArray ideAnalyzers, + CancellationToken cancellationToken) { - var result = ImmutableDictionary.Empty; - - // can be null if given project doesn't support compilation. - if (compilationWithAnalyzers?.ProjectAnalyzers.Length > 0 - || compilationWithAnalyzers?.HostAnalyzers.Length > 0) + try { - // calculate regular diagnostic analyzers diagnostics - var resultMap = await _diagnosticAnalyzerRunner.AnalyzeProjectAsync( - project, compilationWithAnalyzers, logPerformanceInfo: false, getTelemetryInfo: true, cancellationToken).ConfigureAwait(false); + var result = ImmutableDictionary.Empty; - result = resultMap.AnalysisResult; + // can be null if given project doesn't support compilation. + if (compilationWithAnalyzers?.ProjectAnalyzers.Length > 0 + || compilationWithAnalyzers?.HostAnalyzers.Length > 0) + { + // calculate regular diagnostic analyzers diagnostics + var resultMap = await _diagnosticAnalyzerRunner.AnalyzeProjectAsync( + project, compilationWithAnalyzers, logPerformanceInfo: false, getTelemetryInfo: true, cancellationToken).ConfigureAwait(false); - // record telemetry data - UpdateAnalyzerTelemetryData(resultMap.TelemetryInfo); - } + result = resultMap.AnalysisResult; - // check whether there is IDE specific project diagnostic analyzer - Debug.Assert(ideAnalyzers.All(a => a is ProjectDiagnosticAnalyzer or DocumentDiagnosticAnalyzer)); - return await MergeProjectDiagnosticAnalyzerDiagnosticsAsync(project, ideAnalyzers, compilationWithAnalyzers?.HostCompilation, result, cancellationToken).ConfigureAwait(false); - } - catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) - { - throw ExceptionUtilities.Unreachable(); + // record telemetry data + UpdateAnalyzerTelemetryData(resultMap.TelemetryInfo); + } + + // check whether there is IDE specific project diagnostic analyzer + Debug.Assert(ideAnalyzers.All(a => a is ProjectDiagnosticAnalyzer or DocumentDiagnosticAnalyzer)); + return await MergeProjectDiagnosticAnalyzerDiagnosticsAsync(project, ideAnalyzers, compilationWithAnalyzers?.HostCompilation, result, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) + { + throw ExceptionUtilities.Unreachable(); + } } - } - private async Task> ComputeDiagnosticsAsync( - CompilationWithAnalyzersPair? compilationWithAnalyzers, - Project project, - ImmutableArray stateSets, - CancellationToken cancellationToken) - { - try + async Task> ComputeDiagnosticsForStateSetsAsync( + CompilationWithAnalyzersPair? compilationWithAnalyzers, + Project project, + ImmutableArray stateSets, + CancellationToken cancellationToken) { - var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); + try + { + var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); - var ideAnalyzers = stateSets.Select(s => s.Analyzer).Where(a => a is ProjectDiagnosticAnalyzer or DocumentDiagnosticAnalyzer).ToImmutableArrayOrEmpty(); + var ideAnalyzers = stateSets.Select(s => s.Analyzer).Where(a => a is ProjectDiagnosticAnalyzer or DocumentDiagnosticAnalyzer).ToImmutableArrayOrEmpty(); - return await ComputeDiagnosticsAsync(compilationWithAnalyzers, project, ideAnalyzers, cancellationToken).ConfigureAwait(false); - } - catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) - { - throw ExceptionUtilities.Unreachable(); + return await ComputeDiagnosticsForAnalyzersAsync(compilationWithAnalyzers, project, ideAnalyzers, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) + { + throw ExceptionUtilities.Unreachable(); + } } - } - private async Task> MergeProjectDiagnosticAnalyzerDiagnosticsAsync( - Project project, - ImmutableArray ideAnalyzers, - Compilation? compilation, - ImmutableDictionary result, - CancellationToken cancellationToken) - { - try + async Task> MergeProjectDiagnosticAnalyzerDiagnosticsAsync( + Project project, + ImmutableArray ideAnalyzers, + Compilation? compilation, + ImmutableDictionary result, + CancellationToken cancellationToken) { - var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); - - (result, var failedDocuments) = await UpdateWithDocumentLoadAndGeneratorFailuresAsync(result, project, version, cancellationToken).ConfigureAwait(false); - - foreach (var analyzer in ideAnalyzers) + try { - var builder = new DiagnosticAnalysisResultBuilder(project, version); + var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); + + (result, var failedDocuments) = await UpdateWithDocumentLoadAndGeneratorFailuresAsync(result, project, version, cancellationToken).ConfigureAwait(false); - switch (analyzer) + foreach (var analyzer in ideAnalyzers) { - case DocumentDiagnosticAnalyzer documentAnalyzer: - foreach (var document in project.Documents) - { - // don't analyze documents whose content failed to load - if (failedDocuments == null || !failedDocuments.Contains(document)) + var builder = new DiagnosticAnalysisResultBuilder(project, version); + + switch (analyzer) + { + case DocumentDiagnosticAnalyzer documentAnalyzer: + foreach (var document in project.Documents) { - var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); - if (tree != null) - { - builder.AddSyntaxDiagnostics(tree, await DocumentAnalysisExecutor.ComputeDocumentDiagnosticAnalyzerDiagnosticsAsync(documentAnalyzer, document, AnalysisKind.Syntax, compilation, cancellationToken).ConfigureAwait(false)); - builder.AddSemanticDiagnostics(tree, await DocumentAnalysisExecutor.ComputeDocumentDiagnosticAnalyzerDiagnosticsAsync(documentAnalyzer, document, AnalysisKind.Semantic, compilation, cancellationToken).ConfigureAwait(false)); - } - else + // don't analyze documents whose content failed to load + if (failedDocuments == null || !failedDocuments.Contains(document)) { - builder.AddExternalSyntaxDiagnostics(document.Id, await DocumentAnalysisExecutor.ComputeDocumentDiagnosticAnalyzerDiagnosticsAsync(documentAnalyzer, document, AnalysisKind.Syntax, compilation, cancellationToken).ConfigureAwait(false)); - builder.AddExternalSemanticDiagnostics(document.Id, await DocumentAnalysisExecutor.ComputeDocumentDiagnosticAnalyzerDiagnosticsAsync(documentAnalyzer, document, AnalysisKind.Semantic, compilation, cancellationToken).ConfigureAwait(false)); + var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); + if (tree != null) + { + builder.AddSyntaxDiagnostics(tree, await DocumentAnalysisExecutor.ComputeDocumentDiagnosticAnalyzerDiagnosticsAsync(documentAnalyzer, document, AnalysisKind.Syntax, compilation, cancellationToken).ConfigureAwait(false)); + builder.AddSemanticDiagnostics(tree, await DocumentAnalysisExecutor.ComputeDocumentDiagnosticAnalyzerDiagnosticsAsync(documentAnalyzer, document, AnalysisKind.Semantic, compilation, cancellationToken).ConfigureAwait(false)); + } + else + { + builder.AddExternalSyntaxDiagnostics(document.Id, await DocumentAnalysisExecutor.ComputeDocumentDiagnosticAnalyzerDiagnosticsAsync(documentAnalyzer, document, AnalysisKind.Syntax, compilation, cancellationToken).ConfigureAwait(false)); + builder.AddExternalSemanticDiagnostics(document.Id, await DocumentAnalysisExecutor.ComputeDocumentDiagnosticAnalyzerDiagnosticsAsync(documentAnalyzer, document, AnalysisKind.Semantic, compilation, cancellationToken).ConfigureAwait(false)); + } } } - } - break; + break; - case ProjectDiagnosticAnalyzer projectAnalyzer: - builder.AddCompilationDiagnostics(await DocumentAnalysisExecutor.ComputeProjectDiagnosticAnalyzerDiagnosticsAsync(projectAnalyzer, project, compilation, cancellationToken).ConfigureAwait(false)); - break; + case ProjectDiagnosticAnalyzer projectAnalyzer: + builder.AddCompilationDiagnostics(await DocumentAnalysisExecutor.ComputeProjectDiagnosticAnalyzerDiagnosticsAsync(projectAnalyzer, project, compilation, cancellationToken).ConfigureAwait(false)); + break; + } + + // merge the result to existing one. + // there can be existing one from compiler driver with empty set. overwrite it with + // ide one. + result = result.SetItem(analyzer, DiagnosticAnalysisResult.CreateFromBuilder(builder)); } - // merge the result to existing one. - // there can be existing one from compiler driver with empty set. overwrite it with - // ide one. - result = result.SetItem(analyzer, DiagnosticAnalysisResult.CreateFromBuilder(builder)); + return result; + } + catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) + { + throw ExceptionUtilities.Unreachable(); } - - return result; - } - catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) - { - throw ExceptionUtilities.Unreachable(); } - } - private async Task<(ImmutableDictionary results, ImmutableHashSet? failedDocuments)> UpdateWithDocumentLoadAndGeneratorFailuresAsync( - ImmutableDictionary results, - Project project, - VersionStamp version, - CancellationToken cancellationToken) - { - ImmutableHashSet.Builder? failedDocuments = null; - ImmutableDictionary>.Builder? lazyLoadDiagnostics = null; - - foreach (var document in project.Documents) + async Task<(ImmutableDictionary results, ImmutableHashSet? failedDocuments)> UpdateWithDocumentLoadAndGeneratorFailuresAsync( + ImmutableDictionary results, + Project project, + VersionStamp version, + CancellationToken cancellationToken) { - var loadDiagnostic = await document.State.GetLoadDiagnosticAsync(cancellationToken).ConfigureAwait(false); - if (loadDiagnostic != null) + ImmutableHashSet.Builder? failedDocuments = null; + ImmutableDictionary>.Builder? lazyLoadDiagnostics = null; + + foreach (var document in project.Documents) { - lazyLoadDiagnostics ??= ImmutableDictionary.CreateBuilder>(); - lazyLoadDiagnostics.Add(document.Id, [DiagnosticData.Create(loadDiagnostic, document)]); + var loadDiagnostic = await document.State.GetLoadDiagnosticAsync(cancellationToken).ConfigureAwait(false); + if (loadDiagnostic != null) + { + lazyLoadDiagnostics ??= ImmutableDictionary.CreateBuilder>(); + lazyLoadDiagnostics.Add(document.Id, [DiagnosticData.Create(loadDiagnostic, document)]); - failedDocuments ??= ImmutableHashSet.CreateBuilder(); - failedDocuments.Add(document); + failedDocuments ??= ImmutableHashSet.CreateBuilder(); + failedDocuments.Add(document); + } } - } - results = results.SetItem( - FileContentLoadAnalyzer.Instance, - DiagnosticAnalysisResult.Create( - project, - version, - syntaxLocalMap: lazyLoadDiagnostics?.ToImmutable() ?? ImmutableDictionary>.Empty, - semanticLocalMap: ImmutableDictionary>.Empty, - nonLocalMap: ImmutableDictionary>.Empty, - others: [], - documentIds: null)); - - var generatorDiagnostics = await _diagnosticAnalyzerRunner.GetSourceGeneratorDiagnosticsAsync(project, cancellationToken).ConfigureAwait(false); - var diagnosticResultBuilder = new DiagnosticAnalysisResultBuilder(project, version); - foreach (var generatorDiagnostic in generatorDiagnostics) - { - // We'll always treat generator diagnostics that are associated with a tree as a local diagnostic, because - // we want that to be refreshed and deduplicated with regular document analysis. - diagnosticResultBuilder.AddDiagnosticTreatedAsLocalSemantic(generatorDiagnostic); - } + results = results.SetItem( + FileContentLoadAnalyzer.Instance, + DiagnosticAnalysisResult.Create( + project, + version, + syntaxLocalMap: lazyLoadDiagnostics?.ToImmutable() ?? ImmutableDictionary>.Empty, + semanticLocalMap: ImmutableDictionary>.Empty, + nonLocalMap: ImmutableDictionary>.Empty, + others: [], + documentIds: null)); + + var generatorDiagnostics = await _diagnosticAnalyzerRunner.GetSourceGeneratorDiagnosticsAsync(project, cancellationToken).ConfigureAwait(false); + var diagnosticResultBuilder = new DiagnosticAnalysisResultBuilder(project, version); + foreach (var generatorDiagnostic in generatorDiagnostics) + { + // We'll always treat generator diagnostics that are associated with a tree as a local diagnostic, because + // we want that to be refreshed and deduplicated with regular document analysis. + diagnosticResultBuilder.AddDiagnosticTreatedAsLocalSemantic(generatorDiagnostic); + } - results = results.SetItem( - GeneratorDiagnosticsPlaceholderAnalyzer.Instance, - DiagnosticAnalysisResult.CreateFromBuilder(diagnosticResultBuilder)); + results = results.SetItem( + GeneratorDiagnosticsPlaceholderAnalyzer.Instance, + DiagnosticAnalysisResult.CreateFromBuilder(diagnosticResultBuilder)); - return (results, failedDocuments?.ToImmutable()); - } + return (results, failedDocuments?.ToImmutable()); + } - private void UpdateAnalyzerTelemetryData(ImmutableDictionary telemetry) - { - foreach (var (analyzer, telemetryInfo) in telemetry) + void UpdateAnalyzerTelemetryData(ImmutableDictionary telemetry) { - var isTelemetryCollectionAllowed = DiagnosticAnalyzerInfoCache.IsTelemetryCollectionAllowed(analyzer); - _telemetry.UpdateAnalyzerActionsTelemetry(analyzer, telemetryInfo, isTelemetryCollectionAllowed); + foreach (var (analyzer, telemetryInfo) in telemetry) + { + var isTelemetryCollectionAllowed = DiagnosticAnalyzerInfoCache.IsTelemetryCollectionAllowed(analyzer); + _telemetry.UpdateAnalyzerActionsTelemetry(analyzer, telemetryInfo, isTelemetryCollectionAllowed); + } } } } From 374007bf3145db986fa4b3909c58c21772b3075c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 12:47:43 -0800 Subject: [PATCH 145/305] Capture values --- .../DiagnosticIncrementalAnalyzer.Executor.cs | 35 +++++++------------ 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs index 67f49b5772bcb..8d7f601c65645 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs @@ -32,12 +32,12 @@ private async Task> RemoveCompilerSemanticErrorsIfProjectNotLoadedAsync( - ImmutableDictionary result, Project project, CancellationToken cancellationToken) + async Task> RemoveCompilerSemanticErrorsIfProjectNotLoadedAsync( + ImmutableDictionary result) { // see whether solution is loaded successfully var projectLoadedSuccessfully = await project.HasSuccessfullyLoadedAsync(cancellationToken).ConfigureAwait(false); @@ -83,10 +83,7 @@ static async Task async Task> ComputeDiagnosticsForAnalyzersAsync( - CompilationWithAnalyzersPair? compilationWithAnalyzers, - Project project, - ImmutableArray ideAnalyzers, - CancellationToken cancellationToken) + ImmutableArray ideAnalyzers) { try { @@ -108,7 +105,7 @@ async Task> Co // check whether there is IDE specific project diagnostic analyzer Debug.Assert(ideAnalyzers.All(a => a is ProjectDiagnosticAnalyzer or DocumentDiagnosticAnalyzer)); - return await MergeProjectDiagnosticAnalyzerDiagnosticsAsync(project, ideAnalyzers, compilationWithAnalyzers?.HostCompilation, result, cancellationToken).ConfigureAwait(false); + return await MergeProjectDiagnosticAnalyzerDiagnosticsAsync(ideAnalyzers, result).ConfigureAwait(false); } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { @@ -117,10 +114,7 @@ async Task> Co } async Task> ComputeDiagnosticsForStateSetsAsync( - CompilationWithAnalyzersPair? compilationWithAnalyzers, - Project project, - ImmutableArray stateSets, - CancellationToken cancellationToken) + ImmutableArray stateSets) { try { @@ -128,7 +122,7 @@ async Task> Co var ideAnalyzers = stateSets.Select(s => s.Analyzer).Where(a => a is ProjectDiagnosticAnalyzer or DocumentDiagnosticAnalyzer).ToImmutableArrayOrEmpty(); - return await ComputeDiagnosticsForAnalyzersAsync(compilationWithAnalyzers, project, ideAnalyzers, cancellationToken).ConfigureAwait(false); + return await ComputeDiagnosticsForAnalyzersAsync(ideAnalyzers).ConfigureAwait(false); } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { @@ -137,17 +131,16 @@ async Task> Co } async Task> MergeProjectDiagnosticAnalyzerDiagnosticsAsync( - Project project, ImmutableArray ideAnalyzers, - Compilation? compilation, - ImmutableDictionary result, - CancellationToken cancellationToken) + ImmutableDictionary result) { try { + var compilation = compilationWithAnalyzers?.HostCompilation; var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); - (result, var failedDocuments) = await UpdateWithDocumentLoadAndGeneratorFailuresAsync(result, project, version, cancellationToken).ConfigureAwait(false); + (result, var failedDocuments) = await UpdateWithDocumentLoadAndGeneratorFailuresAsync( + result, version).ConfigureAwait(false); foreach (var analyzer in ideAnalyzers) { @@ -198,9 +191,7 @@ async Task> Me async Task<(ImmutableDictionary results, ImmutableHashSet? failedDocuments)> UpdateWithDocumentLoadAndGeneratorFailuresAsync( ImmutableDictionary results, - Project project, - VersionStamp version, - CancellationToken cancellationToken) + VersionStamp version) { ImmutableHashSet.Builder? failedDocuments = null; ImmutableDictionary>.Builder? lazyLoadDiagnostics = null; From a53fce1b527790bb213d5fd56916aa9d57208c81 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 12:51:48 -0800 Subject: [PATCH 146/305] Remove version flag --- .../DiagnosticIncrementalAnalyzer.Executor.cs | 16 ++++------------ .../EngineV2/InProcOrRemoteHostAnalyzerRunner.cs | 8 +------- .../Diagnostics/DiagnosticAnalysisResult.cs | 12 ++---------- .../DiagnosticAnalysisResultBuilder.cs | 3 +-- .../Core/Portable/Diagnostics/Extensions.cs | 3 +-- 5 files changed, 9 insertions(+), 33 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs index 8d7f601c65645..69f4dc752ab0e 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs @@ -30,8 +30,6 @@ private async Task> Co { try { - var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); - var ideAnalyzers = stateSets.Select(s => s.Analyzer).Where(a => a is ProjectDiagnosticAnalyzer or DocumentDiagnosticAnalyzer).ToImmutableArrayOrEmpty(); return await ComputeDiagnosticsForAnalyzersAsync(ideAnalyzers).ConfigureAwait(false); @@ -137,14 +133,12 @@ async Task> Me try { var compilation = compilationWithAnalyzers?.HostCompilation; - var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); - (result, var failedDocuments) = await UpdateWithDocumentLoadAndGeneratorFailuresAsync( - result, version).ConfigureAwait(false); + (result, var failedDocuments) = await UpdateWithDocumentLoadAndGeneratorFailuresAsync(result).ConfigureAwait(false); foreach (var analyzer in ideAnalyzers) { - var builder = new DiagnosticAnalysisResultBuilder(project, version); + var builder = new DiagnosticAnalysisResultBuilder(project); switch (analyzer) { @@ -190,8 +184,7 @@ async Task> Me } async Task<(ImmutableDictionary results, ImmutableHashSet? failedDocuments)> UpdateWithDocumentLoadAndGeneratorFailuresAsync( - ImmutableDictionary results, - VersionStamp version) + ImmutableDictionary results) { ImmutableHashSet.Builder? failedDocuments = null; ImmutableDictionary>.Builder? lazyLoadDiagnostics = null; @@ -213,7 +206,6 @@ async Task> Me FileContentLoadAnalyzer.Instance, DiagnosticAnalysisResult.Create( project, - version, syntaxLocalMap: lazyLoadDiagnostics?.ToImmutable() ?? ImmutableDictionary>.Empty, semanticLocalMap: ImmutableDictionary>.Empty, nonLocalMap: ImmutableDictionary>.Empty, @@ -221,7 +213,7 @@ async Task> Me documentIds: null)); var generatorDiagnostics = await _diagnosticAnalyzerRunner.GetSourceGeneratorDiagnosticsAsync(project, cancellationToken).ConfigureAwait(false); - var diagnosticResultBuilder = new DiagnosticAnalysisResultBuilder(project, version); + var diagnosticResultBuilder = new DiagnosticAnalysisResultBuilder(project); foreach (var generatorDiagnostic in generatorDiagnostics) { // We'll always treat generator diagnostics that are associated with a tree as a local diagnostic, because diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/InProcOrRemoteHostAnalyzerRunner.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/InProcOrRemoteHostAnalyzerRunner.cs index 01cf0957dc86b..1203bb1dabee7 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/InProcOrRemoteHostAnalyzerRunner.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/InProcOrRemoteHostAnalyzerRunner.cs @@ -119,8 +119,6 @@ private async Task.Empty; } - // handling of cancellation and exception - var version = await DiagnosticAnalyzerService.GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); - var documentIds = (documentAnalysisScope != null) ? ImmutableHashSet.Create(documentAnalysisScope.TextDocument.Id) : null; return new DiagnosticAnalysisResultMap( @@ -246,7 +241,6 @@ private static async Task IReadOnlyDictionaryExtensions.GetValueOrDefault(projectAnalyzerMap, entry.analyzerId) ?? hostAnalyzerMap[entry.analyzerId], entry => DiagnosticAnalysisResult.Create( project, - version, syntaxLocalMap: Hydrate(entry.diagnosticMap.Syntax, project), semanticLocalMap: Hydrate(entry.diagnosticMap.Semantic, project), nonLocalMap: Hydrate(entry.diagnosticMap.NonLocal, project), diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs index 511a85c79cefc..8a7b8371ab1f0 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs @@ -22,7 +22,6 @@ namespace Microsoft.CodeAnalysis.Workspaces.Diagnostics; internal readonly struct DiagnosticAnalysisResult { public readonly ProjectId ProjectId; - public readonly VersionStamp Version; /// /// The set of documents that has any kind of diagnostics on it. @@ -51,7 +50,6 @@ internal readonly struct DiagnosticAnalysisResult private DiagnosticAnalysisResult( ProjectId projectId, - VersionStamp version, ImmutableDictionary> syntaxLocals, ImmutableDictionary> semanticLocals, ImmutableDictionary> nonLocals, @@ -64,7 +62,6 @@ private DiagnosticAnalysisResult( Debug.Assert(!nonLocals.Values.Any(item => item.IsDefault)); ProjectId = projectId; - Version = version; _syntaxLocals = syntaxLocals; _semanticLocals = semanticLocals; @@ -74,11 +71,10 @@ private DiagnosticAnalysisResult( DocumentIds = documentIds ?? GetDocumentIds(syntaxLocals, semanticLocals, nonLocals); } - public static DiagnosticAnalysisResult CreateEmpty(ProjectId projectId, VersionStamp version) + public static DiagnosticAnalysisResult CreateEmpty(ProjectId projectId) { return new DiagnosticAnalysisResult( projectId, - version, documentIds: [], syntaxLocals: ImmutableDictionary>.Empty, semanticLocals: ImmutableDictionary>.Empty, @@ -88,7 +84,6 @@ public static DiagnosticAnalysisResult CreateEmpty(ProjectId projectId, VersionS public static DiagnosticAnalysisResult Create( Project project, - VersionStamp version, ImmutableDictionary> syntaxLocalMap, ImmutableDictionary> semanticLocalMap, ImmutableDictionary> nonLocalMap, @@ -101,7 +96,6 @@ public static DiagnosticAnalysisResult Create( return new DiagnosticAnalysisResult( project.Id, - version, syntaxLocalMap, semanticLocalMap, nonLocalMap, @@ -113,7 +107,6 @@ public static DiagnosticAnalysisResult CreateFromBuilder(DiagnosticAnalysisResul { return Create( builder.Project, - builder.Version, builder.SyntaxLocals, builder.SemanticLocals, builder.NonLocals, @@ -170,13 +163,12 @@ public DiagnosticAnalysisResult DropExceptSyntax() // quick bail out if (_syntaxLocals == null || _syntaxLocals.Count == 0) { - return CreateEmpty(ProjectId, Version); + return CreateEmpty(ProjectId); } // keep only syntax errors return new DiagnosticAnalysisResult( ProjectId, - Version, _syntaxLocals, semanticLocals: ImmutableDictionary>.Empty, nonLocals: ImmutableDictionary>.Empty, diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResultBuilder.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResultBuilder.cs index c2ec484358ccb..f4b4f6ec2b9bf 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResultBuilder.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResultBuilder.cs @@ -16,10 +16,9 @@ namespace Microsoft.CodeAnalysis.Workspaces.Diagnostics; /// We have this builder to avoid creating collections unnecessarily. /// Expectation is that, most of time, most of analyzers doesn't have any diagnostics. so no need to actually create any objects. /// -internal struct DiagnosticAnalysisResultBuilder(Project project, VersionStamp version) +internal struct DiagnosticAnalysisResultBuilder(Project project) { public readonly Project Project = project; - public readonly VersionStamp Version = version; private HashSet? _lazyDocumentsWithDiagnostics = null; diff --git a/src/Workspaces/Core/Portable/Diagnostics/Extensions.cs b/src/Workspaces/Core/Portable/Diagnostics/Extensions.cs index ae879b375a924..9457027a23a87 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/Extensions.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/Extensions.cs @@ -103,7 +103,6 @@ public static async Task additionalPragmaSuppressionDiagnostics, DocumentAnalysisScope? documentAnalysisScope, Project project, - VersionStamp version, ImmutableArray projectAnalyzers, ImmutableArray hostAnalyzers, SkippedHostAnalyzersInfo skippedAnalyzersInfo, @@ -133,7 +132,7 @@ public static async Task Date: Fri, 7 Feb 2025 13:06:14 -0800 Subject: [PATCH 147/305] Cleanup how we map from projects to compilations --- ...osticIncrementalAnalyzer_GetDiagnostics.cs | 2 +- ...Span.ProjectAndCompilationWithAnalyzers.cs | 23 ---------- ...crementalAnalyzer_GetDiagnosticsForSpan.cs | 45 ++++++++++++------- ...IncrementalAnalyzer_IncrementalAnalyzer.cs | 2 +- 4 files changed, 30 insertions(+), 42 deletions(-) delete mode 100644 src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.ProjectAndCompilationWithAnalyzers.cs diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index efd20ab728d24..a92baa2103b9c 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -105,7 +105,7 @@ private async Task ProduceDiagnosticsAsync( var stateSets = stateSetsForProject.Where(s => ShouldIncludeStateSet(project, s)).ToImmutableArrayOrEmpty(); // unlike the suppressed (disabled) analyzer, we will include hidden diagnostic only analyzers here. - var compilation = await CreateCompilationWithAnalyzersAsync(project, stateSets, Owner.AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); + var compilation = await GetOrCreateCompilationWithAnalyzersAsync(project, stateSets, Owner.AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); var result = await Owner.GetProjectAnalysisDataAsync(compilation, project, stateSets, cancellationToken).ConfigureAwait(false); diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.ProjectAndCompilationWithAnalyzers.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.ProjectAndCompilationWithAnalyzers.cs deleted file mode 100644 index be9d14efc42f9..0000000000000 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.ProjectAndCompilationWithAnalyzers.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.CodeAnalysis.Diagnostics; - -internal partial class DiagnosticAnalyzerService -{ - private partial class DiagnosticIncrementalAnalyzer - { - private sealed class ProjectAndCompilationWithAnalyzers - { - public Project Project { get; } - public CompilationWithAnalyzersPair? CompilationWithAnalyzers { get; } - - public ProjectAndCompilationWithAnalyzers(Project project, CompilationWithAnalyzersPair? compilationWithAnalyzers) - { - Project = project; - CompilationWithAnalyzers = compilationWithAnalyzers; - } - } - } -} diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs index 88b41904c5222..16a2acfcc6a1a 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs @@ -7,6 +7,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; @@ -47,10 +48,7 @@ public async Task> GetDiagnosticsForSpanAsync( /// private sealed class LatestDiagnosticsForSpanGetter { - // PERF: Cache the last Project and corresponding CompilationWithAnalyzers used to compute analyzer diagnostics for span. - // This is now required as async lightbulb will query and execute different priority buckets of analyzers with multiple - // calls, and we want to reuse CompilationWithAnalyzers instance if possible. - private static readonly WeakReference s_lastProjectAndCompilationWithAnalyzers = new(null); + private static readonly ConditionalWeakTable s_projectToCompilationWithAnalyzers = new(); private readonly DiagnosticIncrementalAnalyzer _owner; private readonly TextDocument _document; @@ -110,25 +108,38 @@ public static async Task CreateAsync( bool crashOnAnalyzerException, CancellationToken cancellationToken) { - if (s_lastProjectAndCompilationWithAnalyzers.TryGetTarget(out var projectAndCompilationWithAnalyzers) && - projectAndCompilationWithAnalyzers?.Project == project) + if (!project.SupportsCompilation) + return null; + + if (!s_projectToCompilationWithAnalyzers.TryGetValue(project, out var compilationWithAnalyzersPair)) + compilationWithAnalyzersPair = await ComputeAndCacheCompilationWithAnalyzersAsync().ConfigureAwait(false); + + if (compilationWithAnalyzersPair is null) + return null; + + // Make sure the cached pair matches the state sets we're asking about. if not, recompute and cache + // with the new state sets. + if (HasAllAnalyzers(stateSets, compilationWithAnalyzersPair)) + return compilationWithAnalyzersPair; + + return await ComputeAndCacheCompilationWithAnalyzersAsync().ConfigureAwait(false); + + async Task ComputeAndCacheCompilationWithAnalyzersAsync() { - if (projectAndCompilationWithAnalyzers.CompilationWithAnalyzers == null) - { - return null; - } + var compilationWithAnalyzersPair = await CreateCompilationWithAnalyzersAsync(project, stateSets, crashOnAnalyzerException, cancellationToken).ConfigureAwait(false); - if (HasAllAnalyzers(stateSets, projectAndCompilationWithAnalyzers.CompilationWithAnalyzers)) - { - return projectAndCompilationWithAnalyzers.CompilationWithAnalyzers; - } - } + // Make a best effort attempt to store the latest computed value against these state sets. If this + // fails (because another thread interleaves with this), that's ok. We still return the pair we + // computed, so our caller will still see the right data + s_projectToCompilationWithAnalyzers.Remove(project); + s_projectToCompilationWithAnalyzers.GetValue(project, _ => compilationWithAnalyzersPair); - var compilationWithAnalyzers = await CreateCompilationWithAnalyzersAsync(project, stateSets, crashOnAnalyzerException, cancellationToken).ConfigureAwait(false); + return compilationWithAnalyzersPair; + } s_lastProjectAndCompilationWithAnalyzers.SetTarget(new ProjectAndCompilationWithAnalyzers(project, compilationWithAnalyzers)); return compilationWithAnalyzers; - static bool HasAllAnalyzers(IEnumerable stateSets, CompilationWithAnalyzersPair compilationWithAnalyzers) + static bool HasAllAnalyzers(ImmutableArray stateSets, CompilationWithAnalyzersPair compilationWithAnalyzers) { foreach (var stateSet in stateSets) { diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 04d76624c5176..be858e36cc816 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -25,7 +25,7 @@ public async Task> ForceAnalyzeProjectAsync(Proje var stateSetsForProject = await _stateManager.GetOrCreateStateSetsAsync(project, cancellationToken).ConfigureAwait(false); var stateSets = GetStateSetsForFullSolutionAnalysis(stateSetsForProject, project); - var compilationWithAnalyzers = await CreateCompilationWithAnalyzersAsync( + var compilationWithAnalyzers = await GetOrCreateCompilationWithAnalyzersAsync( project, stateSets, AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); var result = await GetProjectAnalysisDataAsync(compilationWithAnalyzers, project, stateSets, cancellationToken).ConfigureAwait(false); From 8acdcc2e30eb22fbde70bfb7ed6132ef6dffa0e3 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 13:08:21 -0800 Subject: [PATCH 148/305] Fix --- .../Services/DiagnosticAnalyzer/DiagnosticComputer.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs index 45f6e5bc6314f..3a767508ac893 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs @@ -401,8 +401,7 @@ private async Task AnalyzeAsync( { builderMap = builderMap.AddRange(await analysisResult.ToResultBuilderMapAsync( additionalPragmaSuppressionDiagnostics, documentAnalysisScope, - _project, VersionStamp.Default, - projectAnalyzers, hostAnalyzers, skippedAnalyzersInfo, cancellationToken).ConfigureAwait(false)); + _project, projectAnalyzers, hostAnalyzers, skippedAnalyzersInfo, cancellationToken).ConfigureAwait(false)); } var telemetry = getTelemetryInfo From dc09a70c29510b752bf1db34a4ac518a45233bf1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 13:11:04 -0800 Subject: [PATCH 149/305] Update test --- .../Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs b/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs index b5c05e9dfb7c7..425004234ef26 100644 --- a/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs @@ -62,8 +62,7 @@ void Method() Assert.Equal(isHostAnalyzer ? DiagnosticSeverity.Info : DiagnosticSeverity.Hidden, diagnostics[0].Severity); } - [Theory] - [CombinatorialData] + [Theory, CombinatorialData] public async Task TestVisualBasicAnalyzerOptions(bool isHostAnalyzer) { var code = @"Class Test @@ -83,7 +82,7 @@ End Sub ImmutableArray diagnostics; if (isHostAnalyzer) { - Assert.True(analyzerResult.IsEmpty); + Assert.True(analyzerResult.GetAllDiagnostics().IsEmpty); } else { From 29c8e3d35f9a2913234c9f7c910accbf4d117c5c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 13:15:19 -0800 Subject: [PATCH 150/305] In progress --- ...cIncrementalAnalyzer.CompilationManager.cs | 52 ++++++++++++++++ .../DiagnosticIncrementalAnalyzer.StateSet.cs | 61 +++++++++---------- ...crementalAnalyzer_GetDiagnosticsForSpan.cs | 51 ---------------- 3 files changed, 81 insertions(+), 83 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs index b13fd1018622e..2a85007769f16 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs @@ -5,6 +5,7 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; +using ICSharpCode.Decompiler.CSharp.Syntax; namespace Microsoft.CodeAnalysis.Diagnostics; @@ -20,4 +21,55 @@ private partial class DiagnosticIncrementalAnalyzer crashOnAnalyzerException, cancellationToken); } + + private static async Task GetOrCreateCompilationWithAnalyzersAsync( + Project project, + ImmutableArray stateSets, + bool crashOnAnalyzerException, + CancellationToken cancellationToken) + { + if (!project.SupportsCompilation) + return null; + + if (!s_projectToCompilationWithAnalyzers.TryGetValue(project, out var compilationWithAnalyzersPair)) + compilationWithAnalyzersPair = await ComputeAndCacheCompilationWithAnalyzersAsync().ConfigureAwait(false); + + if (compilationWithAnalyzersPair is null) + return null; + + // Make sure the cached pair matches the state sets we're asking about. if not, recompute and cache + // with the new state sets. + if (HasAllAnalyzers(stateSets, compilationWithAnalyzersPair)) + return compilationWithAnalyzersPair; + + return await ComputeAndCacheCompilationWithAnalyzersAsync().ConfigureAwait(false); + + async Task ComputeAndCacheCompilationWithAnalyzersAsync() + { + var compilationWithAnalyzersPair = await CreateCompilationWithAnalyzersAsync(project, stateSets, crashOnAnalyzerException, cancellationToken).ConfigureAwait(false); + + // Make a best effort attempt to store the latest computed value against these state sets. If this + // fails (because another thread interleaves with this), that's ok. We still return the pair we + // computed, so our caller will still see the right data + s_projectToCompilationWithAnalyzers.Remove(project); + s_projectToCompilationWithAnalyzers.GetValue(project, _ => compilationWithAnalyzersPair); + + return compilationWithAnalyzersPair; + } + s_lastProjectAndCompilationWithAnalyzers.SetTarget(new ProjectAndCompilationWithAnalyzers(project, compilationWithAnalyzers)); + return compilationWithAnalyzers; + + static bool HasAllAnalyzers(ImmutableArray stateSets, CompilationWithAnalyzersPair compilationWithAnalyzers) + { + foreach (var stateSet in stateSets) + { + if (stateSet.IsHostAnalyzer && !compilationWithAnalyzers.HostAnalyzers.Contains(stateSet.Analyzer)) + return false; + else if (!stateSet.IsHostAnalyzer && !compilationWithAnalyzers.ProjectAnalyzers.Contains(stateSet.Analyzer)) + return false; + } + + return true; + } + } } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs index d40dfcb20d6d0..ee0183d8c592f 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs @@ -16,47 +16,44 @@ namespace Microsoft.CodeAnalysis.Diagnostics; internal partial class DiagnosticAnalyzerService { - private partial class DiagnosticIncrementalAnalyzer + /// + /// this contains all states regarding a + /// + private sealed class StateSet { - /// - /// this contains all states regarding a - /// - private sealed class StateSet - { - public readonly DiagnosticAnalyzer Analyzer; - public readonly bool IsHostAnalyzer; + public readonly DiagnosticAnalyzer Analyzer; + public readonly bool IsHostAnalyzer; - private readonly ConcurrentSet _activeDocuments; - private readonly ConcurrentDictionary _projectStates; + private readonly ConcurrentSet _activeDocuments; + private readonly ConcurrentDictionary _projectStates; - public StateSet(DiagnosticAnalyzer analyzer, bool isHostAnalyzer) - { - Analyzer = analyzer; - IsHostAnalyzer = isHostAnalyzer; + public StateSet(DiagnosticAnalyzer analyzer, bool isHostAnalyzer) + { + Analyzer = analyzer; + IsHostAnalyzer = isHostAnalyzer; - _activeDocuments = []; - _projectStates = new ConcurrentDictionary(concurrencyLevel: 2, capacity: 1); - } + _activeDocuments = []; + _projectStates = new ConcurrentDictionary(concurrencyLevel: 2, capacity: 1); + } - public bool IsActiveFile(DocumentId documentId) - => _activeDocuments.Contains(documentId); + public bool IsActiveFile(DocumentId documentId) + => _activeDocuments.Contains(documentId); - public bool TryGetProjectState(ProjectId projectId, [NotNullWhen(true)] out ProjectState? state) - => _projectStates.TryGetValue(projectId, out state); + public bool TryGetProjectState(ProjectId projectId, [NotNullWhen(true)] out ProjectState? state) + => _projectStates.TryGetValue(projectId, out state); - public void AddActiveDocument(DocumentId documentId) - => _activeDocuments.Add(documentId); + public void AddActiveDocument(DocumentId documentId) + => _activeDocuments.Add(documentId); - public ProjectState GetOrCreateProjectState(ProjectId projectId) - => _projectStates.GetOrAdd(projectId, static (id, self) => new ProjectState(self, id), this); + public ProjectState GetOrCreateProjectState(ProjectId projectId) + => _projectStates.GetOrAdd(projectId, static (id, self) => new ProjectState(self, id), this); - public void OnRemoved() - { - // ths stateset is being removed. - // TODO: we do this since InMemoryCache is static type. we might consider making it instance object - // of something. - InMemoryStorage.DropCache(Analyzer); - } + public void OnRemoved() + { + // ths stateset is being removed. + // TODO: we do this since InMemoryCache is static type. we might consider making it instance object + // of something. + InMemoryStorage.DropCache(Analyzer); } } } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs index 16a2acfcc6a1a..cd7e83d3b25f8 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs @@ -102,57 +102,6 @@ public static async Task CreateAsync( range, priorityProvider, isExplicit, logPerformanceInfo, incrementalAnalysis, diagnosticKinds); } - private static async Task GetOrCreateCompilationWithAnalyzersAsync( - Project project, - ImmutableArray stateSets, - bool crashOnAnalyzerException, - CancellationToken cancellationToken) - { - if (!project.SupportsCompilation) - return null; - - if (!s_projectToCompilationWithAnalyzers.TryGetValue(project, out var compilationWithAnalyzersPair)) - compilationWithAnalyzersPair = await ComputeAndCacheCompilationWithAnalyzersAsync().ConfigureAwait(false); - - if (compilationWithAnalyzersPair is null) - return null; - - // Make sure the cached pair matches the state sets we're asking about. if not, recompute and cache - // with the new state sets. - if (HasAllAnalyzers(stateSets, compilationWithAnalyzersPair)) - return compilationWithAnalyzersPair; - - return await ComputeAndCacheCompilationWithAnalyzersAsync().ConfigureAwait(false); - - async Task ComputeAndCacheCompilationWithAnalyzersAsync() - { - var compilationWithAnalyzersPair = await CreateCompilationWithAnalyzersAsync(project, stateSets, crashOnAnalyzerException, cancellationToken).ConfigureAwait(false); - - // Make a best effort attempt to store the latest computed value against these state sets. If this - // fails (because another thread interleaves with this), that's ok. We still return the pair we - // computed, so our caller will still see the right data - s_projectToCompilationWithAnalyzers.Remove(project); - s_projectToCompilationWithAnalyzers.GetValue(project, _ => compilationWithAnalyzersPair); - - return compilationWithAnalyzersPair; - } - s_lastProjectAndCompilationWithAnalyzers.SetTarget(new ProjectAndCompilationWithAnalyzers(project, compilationWithAnalyzers)); - return compilationWithAnalyzers; - - static bool HasAllAnalyzers(ImmutableArray stateSets, CompilationWithAnalyzersPair compilationWithAnalyzers) - { - foreach (var stateSet in stateSets) - { - if (stateSet.IsHostAnalyzer && !compilationWithAnalyzers.HostAnalyzers.Contains(stateSet.Analyzer)) - return false; - else if (!stateSet.IsHostAnalyzer && !compilationWithAnalyzers.ProjectAnalyzers.Contains(stateSet.Analyzer)) - return false; - } - - return true; - } - } - private LatestDiagnosticsForSpanGetter( DiagnosticIncrementalAnalyzer owner, CompilationWithAnalyzersPair? compilationWithAnalyzers, From cd9c7c1bce31215ddf07532c959f00a941040234 Mon Sep 17 00:00:00 2001 From: Arun Chander Date: Fri, 7 Feb 2025 13:19:23 -0800 Subject: [PATCH 151/305] Add culture to the extensionmanager reference to unblock d18 --- .../VisualStudioDiagnosticAnalyzerProvider.Factory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerProvider.Factory.cs b/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerProvider.Factory.cs index 0edbbe0210450..6a7cbe8655fe6 100644 --- a/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerProvider.Factory.cs +++ b/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerProvider.Factory.cs @@ -47,7 +47,7 @@ public async Task GetOrCreateProviderAsy // this will allow us to build once and deploy on different versions of VS SxS. var vsDteVersion = Version.Parse(dte.Version.Split(' ')[0]); // DTE.Version is in the format of D[D[.D[D]]][ (?+)], so we need to split out the version part and check for uninitialized Major/Minor below - var assembly = Assembly.Load($"Microsoft.VisualStudio.ExtensionManager, Version={(vsDteVersion.Major == -1 ? 0 : vsDteVersion.Major)}.{(vsDteVersion.Minor == -1 ? 0 : vsDteVersion.Minor)}.0.0, PublicKeyToken=b03f5f7f11d50a3a"); + var assembly = Assembly.Load($"Microsoft.VisualStudio.ExtensionManager, Version={(vsDteVersion.Major == -1 ? 0 : vsDteVersion.Major)}.{(vsDteVersion.Minor == -1 ? 0 : vsDteVersion.Minor)}.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); var typeIExtensionContent = assembly.GetType("Microsoft.VisualStudio.ExtensionManager.IExtensionContent"); var type = assembly.GetType("Microsoft.VisualStudio.ExtensionManager.SVsExtensionManager"); var extensionManager = _serviceProvider.GetService(type); From bb364087813597a93d2bdf792441b1eb0cbc7584 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 13:34:40 -0800 Subject: [PATCH 152/305] move method --- .../DocumentAnalysisExecutor_Helpers.cs | 76 ----------------- ...cIncrementalAnalyzer.CompilationManager.cs | 84 ++++++++++++++++++- 2 files changed, 83 insertions(+), 77 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/DocumentAnalysisExecutor_Helpers.cs b/src/LanguageServer/Protocol/Features/Diagnostics/DocumentAnalysisExecutor_Helpers.cs index 8c03ede203bd1..7de75d8b668ab 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/DocumentAnalysisExecutor_Helpers.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/DocumentAnalysisExecutor_Helpers.cs @@ -128,82 +128,6 @@ static string GetLanguageSpecificId(string? language, string noLanguageId, strin language: language); } - /// - /// Should only be called on a that . - /// - public static async Task CreateCompilationWithAnalyzersAsync( - Project project, - ImmutableArray projectAnalyzers, - ImmutableArray hostAnalyzers, - bool crashOnAnalyzerException, - CancellationToken cancellationToken) - { - var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); - - // Create driver that holds onto compilation and associated analyzers - var filteredProjectAnalyzers = projectAnalyzers.WhereAsArray(static a => !a.IsWorkspaceDiagnosticAnalyzer()); - var filteredHostAnalyzers = hostAnalyzers.WhereAsArray(static a => !a.IsWorkspaceDiagnosticAnalyzer()); - var filteredProjectSuppressors = filteredProjectAnalyzers.WhereAsArray(static a => a is DiagnosticSuppressor); - filteredHostAnalyzers = filteredHostAnalyzers.AddRange(filteredProjectSuppressors); - - // PERF: there is no analyzers for this compilation. - // compilationWithAnalyzer will throw if it is created with no analyzers which is perf optimization. - if (filteredProjectAnalyzers.IsEmpty && filteredHostAnalyzers.IsEmpty) - { - return null; - } - - Contract.ThrowIfFalse(project.SupportsCompilation); - AssertCompilation(project, compilation); - - // in IDE, we always set concurrentAnalysis == false otherwise, we can get into thread starvation due to - // async being used with synchronous blocking concurrency. - var projectAnalyzerOptions = new CompilationWithAnalyzersOptions( - options: project.AnalyzerOptions, - onAnalyzerException: null, - analyzerExceptionFilter: GetAnalyzerExceptionFilter(), - concurrentAnalysis: false, - logAnalyzerExecutionTime: true, - reportSuppressedDiagnostics: true); - var hostAnalyzerOptions = new CompilationWithAnalyzersOptions( - options: project.HostAnalyzerOptions, - onAnalyzerException: null, - analyzerExceptionFilter: GetAnalyzerExceptionFilter(), - concurrentAnalysis: false, - logAnalyzerExecutionTime: true, - reportSuppressedDiagnostics: true); - - // Create driver that holds onto compilation and associated analyzers - return new CompilationWithAnalyzersPair( - filteredProjectAnalyzers.Any() ? compilation.WithAnalyzers(filteredProjectAnalyzers, projectAnalyzerOptions) : null, - filteredHostAnalyzers.Any() ? compilation.WithAnalyzers(filteredHostAnalyzers, hostAnalyzerOptions) : null); - - Func GetAnalyzerExceptionFilter() - { - return ex => - { - if (ex is not OperationCanceledException && crashOnAnalyzerException) - { - // report telemetry - FatalError.ReportAndPropagate(ex); - - // force fail fast (the host might not crash when reporting telemetry): - FailFast.OnFatalException(ex); - } - - return true; - }; - } - } - - [Conditional("DEBUG")] - private static void AssertCompilation(Project project, Compilation compilation1) - { - // given compilation must be from given project. - Contract.ThrowIfFalse(project.TryGetCompilation(out var compilation2)); - Contract.ThrowIfFalse(compilation1 == compilation2); - } - /// /// Return true if the given is not suppressed for the given project. /// NOTE: This API is intended to be used only for performance optimization. diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs index 9ab16e4e7f775..7d135619a47cb 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs @@ -2,10 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics; @@ -33,7 +39,7 @@ internal partial class DiagnosticAnalyzerService if (!s_projectToCompilationWithAnalyzers.TryGetValue(project, out var compilationWithAnalyzersPair) || !HasAllAnalyzers(stateSets, compilationWithAnalyzersPair)) { - compilationWithAnalyzersPair = await DocumentAnalysisExecutor.CreateCompilationWithAnalyzersAsync( + compilationWithAnalyzersPair = await CreateCompilationWithAnalyzersAsync( project, stateSets.SelectAsArray(s => !s.IsHostAnalyzer, s => s.Analyzer), stateSets.SelectAsArray(s => s.IsHostAnalyzer, s => s.Analyzer), @@ -67,5 +73,81 @@ static bool HasAllAnalyzers(ImmutableArray stateSets, CompilationWithA return true; } + + // + // Should only be called on a that . + // + static async Task CreateCompilationWithAnalyzersAsync( + Project project, + ImmutableArray projectAnalyzers, + ImmutableArray hostAnalyzers, + bool crashOnAnalyzerException, + CancellationToken cancellationToken) + { + var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); + + // Create driver that holds onto compilation and associated analyzers + var filteredProjectAnalyzers = projectAnalyzers.WhereAsArray(static a => !a.IsWorkspaceDiagnosticAnalyzer()); + var filteredHostAnalyzers = hostAnalyzers.WhereAsArray(static a => !a.IsWorkspaceDiagnosticAnalyzer()); + var filteredProjectSuppressors = filteredProjectAnalyzers.WhereAsArray(static a => a is DiagnosticSuppressor); + filteredHostAnalyzers = filteredHostAnalyzers.AddRange(filteredProjectSuppressors); + + // PERF: there is no analyzers for this compilation. + // compilationWithAnalyzer will throw if it is created with no analyzers which is perf optimization. + if (filteredProjectAnalyzers.IsEmpty && filteredHostAnalyzers.IsEmpty) + { + return null; + } + + Contract.ThrowIfFalse(project.SupportsCompilation); + AssertCompilation(project, compilation); + + // in IDE, we always set concurrentAnalysis == false otherwise, we can get into thread starvation due to + // async being used with synchronous blocking concurrency. + var projectAnalyzerOptions = new CompilationWithAnalyzersOptions( + options: project.AnalyzerOptions, + onAnalyzerException: null, + analyzerExceptionFilter: GetAnalyzerExceptionFilter(), + concurrentAnalysis: false, + logAnalyzerExecutionTime: true, + reportSuppressedDiagnostics: true); + var hostAnalyzerOptions = new CompilationWithAnalyzersOptions( + options: project.HostAnalyzerOptions, + onAnalyzerException: null, + analyzerExceptionFilter: GetAnalyzerExceptionFilter(), + concurrentAnalysis: false, + logAnalyzerExecutionTime: true, + reportSuppressedDiagnostics: true); + + // Create driver that holds onto compilation and associated analyzers + return new CompilationWithAnalyzersPair( + filteredProjectAnalyzers.Any() ? compilation.WithAnalyzers(filteredProjectAnalyzers, projectAnalyzerOptions) : null, + filteredHostAnalyzers.Any() ? compilation.WithAnalyzers(filteredHostAnalyzers, hostAnalyzerOptions) : null); + + Func GetAnalyzerExceptionFilter() + { + return ex => + { + if (ex is not OperationCanceledException && crashOnAnalyzerException) + { + // report telemetry + FatalError.ReportAndPropagate(ex); + + // force fail fast (the host might not crash when reporting telemetry): + FailFast.OnFatalException(ex); + } + + return true; + }; + } + } + } + + [Conditional("DEBUG")] + private static void AssertCompilation(Project project, Compilation compilation1) + { + // given compilation must be from given project. + Contract.ThrowIfFalse(project.TryGetCompilation(out var compilation2)); + Contract.ThrowIfFalse(compilation1 == compilation2); } } From 488adf09791cdb44caeaab67b7f76a5837b9321c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 13:35:40 -0800 Subject: [PATCH 153/305] Simplify with captures --- ...ticIncrementalAnalyzer.CompilationManager.cs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs index 7d135619a47cb..02d8688f9ec2d 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs @@ -39,12 +39,7 @@ internal partial class DiagnosticAnalyzerService if (!s_projectToCompilationWithAnalyzers.TryGetValue(project, out var compilationWithAnalyzersPair) || !HasAllAnalyzers(stateSets, compilationWithAnalyzersPair)) { - compilationWithAnalyzersPair = await CreateCompilationWithAnalyzersAsync( - project, - stateSets.SelectAsArray(s => !s.IsHostAnalyzer, s => s.Analyzer), - stateSets.SelectAsArray(s => s.IsHostAnalyzer, s => s.Analyzer), - crashOnAnalyzerException, - cancellationToken).ConfigureAwait(false); + compilationWithAnalyzersPair = await CreateCompilationWithAnalyzersAsync().ConfigureAwait(false); // Make a best effort attempt to store the latest computed value against these state sets. If this // fails (because another thread interleaves with this), that's ok. We still return the pair we @@ -77,13 +72,11 @@ static bool HasAllAnalyzers(ImmutableArray stateSets, CompilationWithA // // Should only be called on a that . // - static async Task CreateCompilationWithAnalyzersAsync( - Project project, - ImmutableArray projectAnalyzers, - ImmutableArray hostAnalyzers, - bool crashOnAnalyzerException, - CancellationToken cancellationToken) + async Task CreateCompilationWithAnalyzersAsync() { + var projectAnalyzers = stateSets.SelectAsArray(s => !s.IsHostAnalyzer, s => s.Analyzer); + var hostAnalyzers = stateSets.SelectAsArray(s => s.IsHostAnalyzer, s => s.Analyzer); + var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); // Create driver that holds onto compilation and associated analyzers From 0d9691207b530bc626d6c749d61827768a83b7b2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Feb 2025 13:51:34 -0800 Subject: [PATCH 154/305] Add a paranoia check --- .../DiagnosticIncrementalAnalyzer_GetDiagnostics.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index f2896f11104ec..dc107a58ad2c0 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -137,8 +137,16 @@ async Task> Ge // // Note: the caller will loop over *its* state sets, grabbing from the full set of data we've cached // for this project, and filtering down further. So it's ok to return this potentially larger set. - if (this.Owner._projectToForceAnalysisData.TryGetValue(project, out var box)) + // + // Note: While ForceAnalyzeProjectAsync should always run with a larger state set than us (since it + // runs all analyzers), we still run a paranoia check that the state sets we care about are a subset + // of that call so that we don't accidentally reuse results that would not correspond to what we are + // computing ourselves. + if (this.Owner._projectToForceAnalysisData.TryGetValue(project, out var box) && + stateSets.IsSubsetOf(box.Value.stateSets)) + { return box.Value.diagnosticAnalysisResults; + } // Otherwise, just compute for the state sets we care about. var compilation = await CreateCompilationWithAnalyzersAsync(project, stateSets, Owner.AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); From 2be3696d35ddd351b9b83033d6920579df70c8f8 Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Wed, 5 Feb 2025 14:58:18 -0800 Subject: [PATCH 155/305] Refactor out the existing logger for logging to test outputs We had one in the LSP tests specifically, but this moves it out for general use. It also makes it a proper ILoggerProvider with category support so it can be used with other loggers. --- .../ExportProviderBuilderTests.cs | 2 +- .../LspFileChangeWatcherTests.cs | 8 +-- ...deAnalysis.LanguageServer.UnitTests.csproj | 5 ++ .../TelemetryReporterTests.cs | 2 +- ...LanguageServerClientTests.TestLspClient.cs | 25 ++++++---- .../AbstractLanguageServerClientTests.cs | 6 ++- .../AbstractLanguageServerHostTests.cs | 20 ++++---- .../Utilities/TestLoggerProvider.cs | 25 ---------- .../Utilities/TestOutputLogger.cs | 42 ---------------- .../Logging/TestOutputLoggerProvider.cs | 49 +++++++++++++++++++ 10 files changed, 90 insertions(+), 94 deletions(-) delete mode 100644 src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/TestLoggerProvider.cs delete mode 100644 src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/TestOutputLogger.cs create mode 100644 src/Workspaces/CoreTestUtilities/Logging/TestOutputLoggerProvider.cs diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/ExportProviderBuilderTests.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/ExportProviderBuilderTests.cs index 1cc57db6f508f..e7f9da1ce42ea 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/ExportProviderBuilderTests.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/ExportProviderBuilderTests.cs @@ -86,7 +86,7 @@ public async Task CanFindCodeBaseWhenReservedCharactersInPath(string reservedCha var dllPath = GenerateDll(reservedCharacter, out var assemblyName); - await using var testServer = await TestLspServer.CreateAsync(new Roslyn.LanguageServer.Protocol.ClientCapabilities(), TestOutputLogger, MefCacheDirectory.Path, includeDevKitComponents: true, [dllPath]); + await using var testServer = await TestLspServer.CreateAsync(new Roslyn.LanguageServer.Protocol.ClientCapabilities(), LoggerFactory, MefCacheDirectory.Path, includeDevKitComponents: true, [dllPath]); var assemblies = AppDomain.CurrentDomain.GetAssemblies(); var assembly = Assert.Single(assemblies, a => a.GetName().Name == assemblyName); diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs index 900cf1a438c89..8cacd0b6bbb71 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs @@ -28,7 +28,7 @@ public class LspFileChangeWatcherTests(ITestOutputHelper testOutputHelper) [Fact] public async Task LspFileWatcherNotSupportedWithoutClientSupport() { - await using var testLspServer = await TestLspServer.CreateAsync(new ClientCapabilities(), TestOutputLogger, MefCacheDirectory.Path); + await using var testLspServer = await TestLspServer.CreateAsync(new ClientCapabilities(), LoggerFactory, MefCacheDirectory.Path); Assert.False(LspFileChangeWatcher.SupportsLanguageServerHost(testLspServer.LanguageServerHost)); } @@ -36,7 +36,7 @@ public async Task LspFileWatcherNotSupportedWithoutClientSupport() [Fact] public async Task LspFileWatcherSupportedWithClientSupport() { - await using var testLspServer = await TestLspServer.CreateAsync(_clientCapabilitiesWithFileWatcherSupport, TestOutputLogger, MefCacheDirectory.Path); + await using var testLspServer = await TestLspServer.CreateAsync(_clientCapabilitiesWithFileWatcherSupport, LoggerFactory, MefCacheDirectory.Path); Assert.True(LspFileChangeWatcher.SupportsLanguageServerHost(testLspServer.LanguageServerHost)); } @@ -46,7 +46,7 @@ public async Task CreatingDirectoryWatchRequestsDirectoryWatch() { AsynchronousOperationListenerProvider.Enable(enable: true); - await using var testLspServer = await TestLspServer.CreateAsync(_clientCapabilitiesWithFileWatcherSupport, TestOutputLogger, MefCacheDirectory.Path); + await using var testLspServer = await TestLspServer.CreateAsync(_clientCapabilitiesWithFileWatcherSupport, LoggerFactory, MefCacheDirectory.Path); var lspFileChangeWatcher = new LspFileChangeWatcher( testLspServer.LanguageServerHost, testLspServer.ExportProvider.GetExportedValue()); @@ -76,7 +76,7 @@ public async Task CreatingFileWatchRequestsFileWatch() { AsynchronousOperationListenerProvider.Enable(enable: true); - await using var testLspServer = await TestLspServer.CreateAsync(_clientCapabilitiesWithFileWatcherSupport, TestOutputLogger, MefCacheDirectory.Path); + await using var testLspServer = await TestLspServer.CreateAsync(_clientCapabilitiesWithFileWatcherSupport, LoggerFactory, MefCacheDirectory.Path); var lspFileChangeWatcher = new LspFileChangeWatcher( testLspServer.LanguageServerHost, testLspServer.ExportProvider.GetExportedValue()); diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Microsoft.CodeAnalysis.LanguageServer.UnitTests.csproj b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Microsoft.CodeAnalysis.LanguageServer.UnitTests.csproj index 1a0f088e2eed0..0f61561851842 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Microsoft.CodeAnalysis.LanguageServer.UnitTests.csproj +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Microsoft.CodeAnalysis.LanguageServer.UnitTests.csproj @@ -15,6 +15,11 @@ + + + + + diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/TelemetryReporterTests.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/TelemetryReporterTests.cs index 641514d4eff5d..0a678d0b4fd44 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/TelemetryReporterTests.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/TelemetryReporterTests.cs @@ -15,7 +15,7 @@ public sealed class TelemetryReporterTests(ITestOutputHelper testOutputHelper) private async Task CreateReporterAsync() { var exportProvider = await LanguageServerTestComposition.CreateExportProviderAsync( - TestOutputLogger.Factory, + LoggerFactory, includeDevKitComponents: true, MefCacheDirectory.Path, [], diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/AbstractLanguageServerClientTests.TestLspClient.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/AbstractLanguageServerClientTests.TestLspClient.cs index d3dcb2f7f6b91..b55dfe6160fdd 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/AbstractLanguageServerClientTests.TestLspClient.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/AbstractLanguageServerClientTests.TestLspClient.cs @@ -26,7 +26,7 @@ internal sealed class TestLspClient : ILspClient, IAsyncDisposable private readonly Process _process; private readonly Dictionary _documents; private readonly Dictionary> _locations; - private readonly TestOutputLogger _logger; + private readonly ILoggerFactory _loggerFactory; private readonly JsonRpc _clientRpc; @@ -37,7 +37,7 @@ internal static async Task CreateAsync( string extensionLogsPath, bool includeDevKitComponents, bool debugLsp, - TestOutputLogger logger, + ILoggerFactory loggerFactory, Dictionary? documents = null, Dictionary>? locations = null) { @@ -47,7 +47,7 @@ internal static async Task CreateAsync( var process = Process.Start(processStartInfo); Assert.NotNull(process); - var lspClient = new TestLspClient(process, pipeName, documents ?? [], locations ?? [], logger); + var lspClient = new TestLspClient(process, pipeName, documents ?? [], locations ?? [], loggerFactory); // We've subscribed to Disconnected, but if the process crashed before that point we might have not seen it if (process.HasExited) @@ -124,11 +124,11 @@ static ProcessStartInfo CreateLspStartInfo(string pipeName, string extensionLogs internal ServerCapabilities ServerCapabilities => _serverCapabilities ?? throw new InvalidOperationException("Initialize has not been called"); - private TestLspClient(Process process, string pipeName, Dictionary documents, Dictionary> locations, TestOutputLogger logger) + private TestLspClient(Process process, string pipeName, Dictionary documents, Dictionary> locations, ILoggerFactory loggerFactory) { _documents = documents; _locations = locations; - _logger = logger; + _loggerFactory = loggerFactory; _process = process; _process.EnableRaisingEvents = true; @@ -160,6 +160,8 @@ private TestLspClient(Process process, string pipeName, Dictionary GetMessageLogger(string method) { + var logger = _loggerFactory.CreateLogger($"LSP {method}"); + return (int type, string message) => { var logLevel = (MessageType)type switch @@ -171,19 +173,20 @@ Action GetMessageLogger(string method) MessageType.Debug => LogLevel.Debug, _ => LogLevel.Trace, }; - _logger.Log(logLevel, "[LSP {Method}] {Message}", method, message); + + logger.Log(logLevel, message); }; } } private void Process_OutputDataReceived(object sender, DataReceivedEventArgs e) { - _logger.LogInformation("[LSP STDOUT] {Data}", e.Data); + _loggerFactory.CreateLogger("LSP STDOUT").LogInformation(e.Data); } private void Process_ErrorDataReceived(object sender, DataReceivedEventArgs e) { - _logger.LogCritical("[LSP STDERR] {Data}", e.Data); + _loggerFactory.CreateLogger("LSP STDERR").LogInformation(e.Data); } public async Task ExecuteRequestAsync(string methodName, TRequestType request, CancellationToken cancellationToken) where TRequestType : class @@ -249,9 +252,11 @@ public async ValueTask DisposeAsync() if (Interlocked.CompareExchange(ref _disposed, value: 1, comparand: 0) != 0) return; + var logger = _loggerFactory.CreateLogger("Shutdown"); + if (!_process.HasExited) { - _logger.LogTrace("Sending a Shutdown request to the LSP."); + logger.LogTrace("Sending a Shutdown request to the LSP."); await _clientRpc.InvokeAsync(Methods.ShutdownName); await _clientRpc.NotifyAsync(Methods.ExitName); @@ -262,7 +267,7 @@ public async ValueTask DisposeAsync() _clientRpc.Dispose(); _process.Dispose(); - _logger.LogTrace("Process shut down."); + logger.LogTrace("Process shut down."); } } } diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/AbstractLanguageServerClientTests.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/AbstractLanguageServerClientTests.cs index 72fd371bb1504..971033a4d5d12 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/AbstractLanguageServerClientTests.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/AbstractLanguageServerClientTests.cs @@ -8,6 +8,8 @@ using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Testing; using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.UnitTests; +using Microsoft.Extensions.Logging; using Roslyn.LanguageServer.Protocol; using Roslyn.Test.Utilities; using Roslyn.Utilities; @@ -18,7 +20,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests; public abstract partial class AbstractLanguageServerClientTests(ITestOutputHelper testOutputHelper) : IDisposable { - protected TestOutputLogger TestOutputLogger => new(testOutputHelper); + protected ILoggerFactory LoggerFactory => new LoggerFactory([new TestOutputLoggerProvider(testOutputHelper)]); protected TempRoot TempRoot => new(); protected TempDirectory ExtensionLogsDirectory => TempRoot.CreateDirectory(); @@ -69,7 +71,7 @@ await File.WriteAllTextAsync(projectPath, $""" ExtensionLogsDirectory.Path, includeDevKitComponents, debugLsp, - TestOutputLogger, + LoggerFactory, documents: files, locations: annotatedLocations); diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/AbstractLanguageServerHostTests.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/AbstractLanguageServerHostTests.cs index b543d17c01d00..ae12838c614a2 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/AbstractLanguageServerHostTests.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/AbstractLanguageServerHostTests.cs @@ -4,6 +4,8 @@ using Microsoft.CodeAnalysis.LanguageServer.LanguageServer; using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.UnitTests; +using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.Composition; using Nerdbank.Streams; using Roslyn.LanguageServer.Protocol; @@ -14,20 +16,20 @@ namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests; public abstract class AbstractLanguageServerHostTests : IDisposable { - protected TestOutputLogger TestOutputLogger { get; } + protected ILoggerFactory LoggerFactory { get; } protected TempRoot TempRoot { get; } protected TempDirectory MefCacheDirectory { get; } protected AbstractLanguageServerHostTests(ITestOutputHelper testOutputHelper) { - TestOutputLogger = new TestOutputLogger(testOutputHelper); + LoggerFactory = new LoggerFactory([new TestOutputLoggerProvider(testOutputHelper)]); TempRoot = new(); MefCacheDirectory = TempRoot.CreateDirectory(); } protected Task CreateLanguageServerAsync(bool includeDevKitComponents = true) { - return TestLspServer.CreateAsync(new ClientCapabilities(), TestOutputLogger, MefCacheDirectory.Path, includeDevKitComponents); + return TestLspServer.CreateAsync(new ClientCapabilities(), LoggerFactory, MefCacheDirectory.Path, includeDevKitComponents); } public void Dispose() @@ -42,11 +44,11 @@ protected sealed class TestLspServer : ILspClient, IAsyncDisposable private ServerCapabilities? _serverCapabilities; - internal static async Task CreateAsync(ClientCapabilities clientCapabilities, TestOutputLogger logger, string cacheDirectory, bool includeDevKitComponents = true, string[]? extensionPaths = null) + internal static async Task CreateAsync(ClientCapabilities clientCapabilities, ILoggerFactory loggerFactory, string cacheDirectory, bool includeDevKitComponents = true, string[]? extensionPaths = null) { var exportProvider = await LanguageServerTestComposition.CreateExportProviderAsync( - logger.Factory, includeDevKitComponents, cacheDirectory, extensionPaths, out var _, out var assemblyLoader); - var testLspServer = new TestLspServer(exportProvider, logger, assemblyLoader); + loggerFactory, includeDevKitComponents, cacheDirectory, extensionPaths, out var _, out var assemblyLoader); + var testLspServer = new TestLspServer(exportProvider, loggerFactory, assemblyLoader); var initializeResponse = await testLspServer.ExecuteRequestAsync(Methods.InitializeName, new InitializeParams { Capabilities = clientCapabilities }, CancellationToken.None); Assert.NotNull(initializeResponse?.Capabilities); testLspServer._serverCapabilities = initializeResponse!.Capabilities; @@ -61,12 +63,12 @@ internal static async Task CreateAsync(ClientCapabilities clientC internal ServerCapabilities ServerCapabilities => _serverCapabilities ?? throw new InvalidOperationException("Initialize has not been called"); - private TestLspServer(ExportProvider exportProvider, TestOutputLogger logger, IAssemblyLoader assemblyLoader) + private TestLspServer(ExportProvider exportProvider, ILoggerFactory loggerFactory, IAssemblyLoader assemblyLoader) { - var typeRefResolver = new ExtensionTypeRefResolver(assemblyLoader, logger.Factory); + var typeRefResolver = new ExtensionTypeRefResolver(assemblyLoader, loggerFactory); var (clientStream, serverStream) = FullDuplexStream.CreatePair(); - LanguageServerHost = new LanguageServerHost(serverStream, serverStream, exportProvider, logger, typeRefResolver); + LanguageServerHost = new LanguageServerHost(serverStream, serverStream, exportProvider, loggerFactory.CreateLogger(), typeRefResolver); var messageFormatter = RoslynLanguageServer.CreateJsonMessageFormatter(); _clientRpc = new JsonRpc(new HeaderDelimitedMessageHandler(clientStream, clientStream, messageFormatter)) diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/TestLoggerProvider.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/TestLoggerProvider.cs deleted file mode 100644 index 3ebda77fd10db..0000000000000 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/TestLoggerProvider.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; - -namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests; - -internal class TestLoggerProvider : ILoggerProvider -{ - private readonly ILogger _testLogger; - public TestLoggerProvider(ILogger testLogger) - { - _testLogger = testLogger; - } - - public ILogger CreateLogger(string categoryName) - { - return _testLogger; - } - - public void Dispose() - { - } -} diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/TestOutputLogger.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/TestOutputLogger.cs deleted file mode 100644 index ea77589b3d0f7..0000000000000 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/TestOutputLogger.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; -using Xunit.Abstractions; - -namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests; - -public class TestOutputLogger : ILogger -{ - private readonly ITestOutputHelper _testOutputHelper; - public readonly ILoggerFactory Factory; - - public TestOutputLogger(ITestOutputHelper testOutputHelper) - { - _testOutputHelper = testOutputHelper; - Factory = new LoggerFactory([new TestLoggerProvider(this)]); - } - - public IDisposable BeginScope(TState state) where TState : notnull - { - return new NoOpDisposable(); - } - - public bool IsEnabled(LogLevel logLevel) - { - return true; - } - - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) - { - _testOutputHelper.WriteLine($"[{DateTime.UtcNow:hh:mm:ss.fff}][{logLevel}]{formatter(state, exception)}"); - } - - private sealed class NoOpDisposable : IDisposable - { - public void Dispose() - { - } - } -} diff --git a/src/Workspaces/CoreTestUtilities/Logging/TestOutputLoggerProvider.cs b/src/Workspaces/CoreTestUtilities/Logging/TestOutputLoggerProvider.cs new file mode 100644 index 0000000000000..681622f567569 --- /dev/null +++ b/src/Workspaces/CoreTestUtilities/Logging/TestOutputLoggerProvider.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Microsoft.Extensions.Logging; +using Xunit.Abstractions; + +namespace Microsoft.CodeAnalysis.UnitTests; + +public sealed class TestOutputLoggerProvider(ITestOutputHelper testOutputHelper) : ILoggerProvider +{ + public ILogger CreateLogger(string categoryName) + { + return new TestOutputLogger(testOutputHelper, categoryName); + } + + private sealed class TestOutputLogger(ITestOutputHelper testOutputHelper, string categoryName) : ILogger + { + public IDisposable BeginScope(TState state) where TState : notnull + { + return new NoOpDisposable(); + } + + public bool IsEnabled(LogLevel logLevel) + { + return true; + } + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) + { + testOutputHelper.WriteLine($"[{DateTime.UtcNow:hh:mm:ss.fff}] [{logLevel}] [{categoryName}] {formatter(state, exception)}"); + + if (exception is not null) + testOutputHelper.WriteLine(exception.ToString()); + } + + private sealed class NoOpDisposable : IDisposable + { + public void Dispose() + { + } + } + } + + public void Dispose() + { + } +} From 0f8dc356c96ae91b280aed5d031199a2ea79fbd0 Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Wed, 5 Feb 2025 15:29:37 -0800 Subject: [PATCH 156/305] Pass the ITestOutputHelper to the MSBuildWorkspace logger This allows us to get logs to help diagnose failing tests. --- .../DiagnosticReporterLoggerProvider.cs | 14 ++++------ .../Core/MSBuild/MSBuildProjectLoader.cs | 2 +- .../MSBuild/Core/MSBuild/MSBuildWorkspace.cs | 4 ++- .../MSBuild/Test/MSBuildWorkspaceTestBase.cs | 20 +++++++++++--- src/Workspaces/MSBuild/Test/NetCoreTests.cs | 5 ++-- .../Test/NewlyCreatedProjectsFromDotNetNew.cs | 26 +++++++++---------- .../Test/VisualStudioMSBuildWorkspaceTests.cs | 7 ++++- 7 files changed, 47 insertions(+), 31 deletions(-) diff --git a/src/Workspaces/MSBuild/Core/MSBuild/DiagnosticReporterLoggerProvider.cs b/src/Workspaces/MSBuild/Core/MSBuild/DiagnosticReporterLoggerProvider.cs index 5f3ccc2bb87e6..fea58aff5d40b 100644 --- a/src/Workspaces/MSBuild/Core/MSBuild/DiagnosticReporterLoggerProvider.cs +++ b/src/Workspaces/MSBuild/Core/MSBuild/DiagnosticReporterLoggerProvider.cs @@ -11,7 +11,7 @@ internal class DiagnosticReporterLoggerProvider : ILoggerProvider { private readonly DiagnosticReporter _reporter; - private DiagnosticReporterLoggerProvider(DiagnosticReporter reporter) + public DiagnosticReporterLoggerProvider(DiagnosticReporter reporter) { _reporter = reporter; } @@ -25,14 +25,6 @@ public void Dispose() { } - public static ILoggerFactory CreateLoggerFactoryForDiagnosticReporter(DiagnosticReporter reporter) - { - // Note: it's important we set MinLevel here, or otherwise we'll still get called in Log() for things below LogLevel.Warning. - return new LoggerFactory( - [new DiagnosticReporterLoggerProvider(reporter)], - new LoggerFilterOptions() { MinLevel = LogLevel.Warning }); - } - private sealed class Logger(DiagnosticReporter reporter, string categoryName) : ILogger { public IDisposable? BeginScope(TState state) where TState : notnull @@ -47,6 +39,10 @@ public bool IsEnabled(LogLevel logLevel) public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { + // Despite returning IsEnabled somebody might still call us anyways, so filter it out + if (logLevel < LogLevel.Warning) + return; + var kind = logLevel == LogLevel.Warning ? WorkspaceDiagnosticKind.Warning : WorkspaceDiagnosticKind.Failure; var message = formatter(state, exception); if (!string.IsNullOrEmpty(categoryName)) diff --git a/src/Workspaces/MSBuild/Core/MSBuild/MSBuildProjectLoader.cs b/src/Workspaces/MSBuild/Core/MSBuild/MSBuildProjectLoader.cs index 4487e1f70532f..50971a42caf27 100644 --- a/src/Workspaces/MSBuild/Core/MSBuild/MSBuildProjectLoader.cs +++ b/src/Workspaces/MSBuild/Core/MSBuild/MSBuildProjectLoader.cs @@ -61,7 +61,7 @@ public MSBuildProjectLoader(Workspace workspace, ImmutableDictionary properties, Ho return new MSBuildWorkspace(hostServices, properties.ToImmutableDictionary()); } + internal void AddLoggerProvider(Microsoft.Extensions.Logging.ILoggerProvider loggerProvider) => _loggerFactory.AddProvider(loggerProvider); + /// /// The MSBuild properties used when interpreting project files. /// These are the same properties that are passed to msbuild via the /property:<n>=<v> command line argument. diff --git a/src/Workspaces/MSBuild/Test/MSBuildWorkspaceTestBase.cs b/src/Workspaces/MSBuild/Test/MSBuildWorkspaceTestBase.cs index 692394d9c836e..25215f7b4188c 100644 --- a/src/Workspaces/MSBuild/Test/MSBuildWorkspaceTestBase.cs +++ b/src/Workspaces/MSBuild/Test/MSBuildWorkspaceTestBase.cs @@ -12,16 +12,27 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.UnitTests; using Microsoft.CodeAnalysis.UnitTests.TestFiles; +using Microsoft.Extensions.Logging; using Roslyn.Test.Utilities; using Xunit; +using Xunit.Abstractions; using static Microsoft.CodeAnalysis.MSBuild.UnitTests.SolutionGeneration; using CS = Microsoft.CodeAnalysis.CSharp; using VB = Microsoft.CodeAnalysis.VisualBasic; namespace Microsoft.CodeAnalysis.MSBuild.UnitTests { - public class MSBuildWorkspaceTestBase : WorkspaceTestBase + public abstract class MSBuildWorkspaceTestBase : WorkspaceTestBase { + protected readonly ITestOutputHelper TestOutput; + protected readonly ILoggerFactory LoggerFactory; + + protected MSBuildWorkspaceTestBase(ITestOutputHelper testOutput) + { + TestOutput = testOutput; + LoggerFactory = new LoggerFactory([new TestOutputLoggerProvider(testOutput)]); + } + protected const string MSBuildNamespace = "http://schemas.microsoft.com/developer/msbuild/2003"; protected static void AssertFailures(MSBuildWorkspace workspace, params string[] expectedFailures) @@ -139,16 +150,18 @@ protected async Task SolutionAsync(params IBuilder[] inputs) return await workspace.OpenSolutionAsync(solutionFileName); } - protected static MSBuildWorkspace CreateMSBuildWorkspace(params (string key, string value)[] additionalProperties) + protected MSBuildWorkspace CreateMSBuildWorkspace(params (string key, string value)[] additionalProperties) => CreateMSBuildWorkspace(throwOnWorkspaceFailed: true, skipUnrecognizedProjects: false, additionalProperties: additionalProperties); - protected static MSBuildWorkspace CreateMSBuildWorkspace( + protected MSBuildWorkspace CreateMSBuildWorkspace( bool throwOnWorkspaceFailed = true, bool skipUnrecognizedProjects = false, (string key, string value)[] additionalProperties = null) { additionalProperties ??= []; var workspace = MSBuildWorkspace.Create(CreateProperties(additionalProperties)); + workspace.AddLoggerProvider(new TestOutputLoggerProvider(TestOutput)); + if (throwOnWorkspaceFailed) { workspace.WorkspaceFailed += (s, e) => throw new Exception($"Workspace failure {e.Diagnostic.Kind}:{e.Diagnostic.Message}"); @@ -164,7 +177,6 @@ protected static MSBuildWorkspace CreateMSBuildWorkspace( protected static MSBuildWorkspace CreateMSBuildWorkspace(HostServices hostServices, params (string key, string value)[] additionalProperties) { - return MSBuildWorkspace.Create(CreateProperties(additionalProperties), hostServices); } diff --git a/src/Workspaces/MSBuild/Test/NetCoreTests.cs b/src/Workspaces/MSBuild/Test/NetCoreTests.cs index 73b48329824f5..cb387d55893ef 100644 --- a/src/Workspaces/MSBuild/Test/NetCoreTests.cs +++ b/src/Workspaces/MSBuild/Test/NetCoreTests.cs @@ -17,6 +17,7 @@ using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; +using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.MSBuild.UnitTests { @@ -24,7 +25,7 @@ public class NetCoreTests : MSBuildWorkspaceTestBase { private readonly TempDirectory _nugetCacheDir; - public NetCoreTests() + public NetCoreTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { _nugetCacheDir = SolutionDirectory.CreateDirectory(".packages"); } @@ -276,7 +277,7 @@ public async Task TestOpenProject_NetCoreMultiTFM_ProjectReference() await AssertNetCoreMultiTFMProject(projectFilePath); } - private static async Task AssertNetCoreMultiTFMProject(string projectFilePath) + private async Task AssertNetCoreMultiTFMProject(string projectFilePath) { using var workspace = CreateMSBuildWorkspace(); await workspace.OpenProjectAsync(projectFilePath); diff --git a/src/Workspaces/MSBuild/Test/NewlyCreatedProjectsFromDotNetNew.cs b/src/Workspaces/MSBuild/Test/NewlyCreatedProjectsFromDotNetNew.cs index f2b93e39c4fea..a21c23b1bb4aa 100644 --- a/src/Workspaces/MSBuild/Test/NewlyCreatedProjectsFromDotNetNew.cs +++ b/src/Workspaces/MSBuild/Test/NewlyCreatedProjectsFromDotNetNew.cs @@ -10,6 +10,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.Extensions.Logging; using Roslyn.Test.Utilities; using Xunit; using Xunit.Abstractions; @@ -30,8 +31,6 @@ public class NewlyCreatedProjectsFromDotNetNew : MSBuildWorkspaceTestBase // installation. private const bool ExcludeMauiTemplates = true; - protected ITestOutputHelper TestOutputHelper { get; } - static NewlyCreatedProjectsFromDotNetNew() { // When running on developer machines we will try and use the same global.json @@ -56,9 +55,8 @@ static string GetSolutionFolder() } } - public NewlyCreatedProjectsFromDotNetNew(ITestOutputHelper output) + public NewlyCreatedProjectsFromDotNetNew(ITestOutputHelper testOutput) : base(testOutput) { - TestOutputHelper = output; } [ConditionalTheory(typeof(DotNetSdkMSBuildInstalled))] @@ -101,7 +99,7 @@ public static TheoryData GetProjectTemplateNames(string language) // Console App console C#,F#,VB Common/Console // ... - var result = RunDotNet($"new list --type project --language {language}", output: null); + var result = RunDotNet($"new list --type project --language {language}", loggerFactory: null); var lines = result.Output.Split(["\r", "\n"], StringSplitOptions.RemoveEmptyEntries); @@ -142,13 +140,13 @@ private async Task AssertTemplateProjectLoadsCleanlyAsync(string templateName, s { if (ignoredDiagnostics?.Length > 0) { - TestOutputHelper.WriteLine($"Ignoring compiler diagnostics: \"{string.Join("\", \"", ignoredDiagnostics)}\""); + TestOutput.WriteLine($"Ignoring compiler diagnostics: \"{string.Join("\", \"", ignoredDiagnostics)}\""); } var projectDirectory = SolutionDirectory.Path; var projectFilePath = GetProjectFilePath(projectDirectory, languageName); - CreateNewProject(templateName, projectDirectory, languageName, TestOutputHelper); + CreateNewProject(templateName, projectDirectory, languageName); await AssertProjectLoadsCleanlyAsync(projectFilePath, ignoredDiagnostics ?? []); @@ -166,7 +164,7 @@ static string GetProjectFilePath(string projectDirectory, string languageName) return Path.Combine(projectDirectory, $"{projectName}.{projectExtension}"); } - static void CreateNewProject(string templateName, string outputDirectory, string languageName, ITestOutputHelper output) + void CreateNewProject(string templateName, string outputDirectory, string languageName) { var language = languageName switch { @@ -177,7 +175,7 @@ static void CreateNewProject(string templateName, string outputDirectory, string TryCopyGlobalJson(outputDirectory); - var newResult = RunDotNet($"new \"{templateName}\" -o \"{outputDirectory}\" --language \"{language}\"", output, outputDirectory); + var newResult = RunDotNet($"new \"{templateName}\" -o \"{outputDirectory}\" --language \"{language}\"", LoggerFactory, outputDirectory); // Most templates invoke restore as a post-creation action. However, some, like the // Maui templates, do not run restore since they require additional workloads to be @@ -190,7 +188,7 @@ static void CreateNewProject(string templateName, string outputDirectory, string try { // Attempt a restore and see if we are instructed to install additional workloads. - var restoreResult = RunDotNet($"restore", output, outputDirectory); + var restoreResult = RunDotNet($"restore", LoggerFactory, outputDirectory); } catch (InvalidOperationException ex) when (ex.Message.Contains("command: dotnet workload restore")) { @@ -210,7 +208,7 @@ static void TryCopyGlobalJson(string outputDirectory) File.Copy(s_globalJsonPath, tempGlobalJsonPath); } - static async Task AssertProjectLoadsCleanlyAsync(string projectFilePath, string[] ignoredDiagnostics) + async Task AssertProjectLoadsCleanlyAsync(string projectFilePath, string[] ignoredDiagnostics) { using var workspace = CreateMSBuildWorkspace(); var project = await workspace.OpenProjectAsync(projectFilePath, cancellationToken: CancellationToken.None); @@ -240,7 +238,7 @@ static async Task AssertProjectLoadsCleanlyAsync(string projectFilePath, string[ } } - private static ProcessResult RunDotNet(string arguments, ITestOutputHelper? output, string? workingDirectory = null) + private static ProcessResult RunDotNet(string arguments, ILoggerFactory? loggerFactory, string? workingDirectory = null) { var dotNetExeName = "dotnet" + (Path.DirectorySeparatorChar == '/' ? "" : ".exe"); @@ -264,7 +262,9 @@ private static ProcessResult RunDotNet(string arguments, ITestOutputHelper? outp ])); } - output?.WriteLine(result.Output); + var logger = loggerFactory?.CreateLogger("dotnet.exe output"); + + logger?.LogTrace(result.Output); return result; } diff --git a/src/Workspaces/MSBuild/Test/VisualStudioMSBuildWorkspaceTests.cs b/src/Workspaces/MSBuild/Test/VisualStudioMSBuildWorkspaceTests.cs index 078bb7a6cb6ce..a266d2f7995c8 100644 --- a/src/Workspaces/MSBuild/Test/VisualStudioMSBuildWorkspaceTests.cs +++ b/src/Workspaces/MSBuild/Test/VisualStudioMSBuildWorkspaceTests.cs @@ -30,12 +30,17 @@ using VB = Microsoft.CodeAnalysis.VisualBasic; using Microsoft.CodeAnalysis.CSharp; using Microsoft.VisualStudio.Threading; +using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.MSBuild.UnitTests { [Trait(Traits.Feature, Traits.Features.MSBuildWorkspace)] public class VisualStudioMSBuildWorkspaceTests : MSBuildWorkspaceTestBase { + public VisualStudioMSBuildWorkspaceTests(ITestOutputHelper testOutput) : base(testOutput) + { + } + [ConditionalFact(typeof(VisualStudioMSBuildInstalled))] public void TestCreateMSBuildWorkspace() { @@ -1132,7 +1137,7 @@ public async Task TestOpenSolution_WithUnrecognizedProjectTypeGuidAndUnrecognize Assert.Equal(expected, e.Message); } - private readonly IEnumerable _defaultAssembliesWithoutCSharp = MefHostServices.DefaultAssemblies.Where(a => !a.FullName.Contains("CSharp")); + private static readonly IEnumerable _defaultAssembliesWithoutCSharp = MefHostServices.DefaultAssemblies.Where(a => !a.FullName.Contains("CSharp")); [ConditionalFact(typeof(VisualStudioMSBuildInstalled))] [WorkItem("https://github.com/dotnet/roslyn/issues/3931")] From 651ee91d035cdf8e659c00a384d5d6671aadbe46 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 7 Feb 2025 17:29:39 -0800 Subject: [PATCH 157/305] Set a RID for InteractiveHost32 (#77116) The .NET SDK's RID (and PlatformTarget) inference for .NET Framework only executes on Windows. As a result, building InteractiveHost32 on non-Windows produces a differently-named NuGet package. This is causing us issues in the VMR. Set the RID explicitly like the InteractiveHost64 process does. This ensures that the resulting package name is InteractiveHost32.x86.Symbols when built on Windows or non-Windows. --- src/Interactive/HostProcess/x86/InteractiveHost32.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Interactive/HostProcess/x86/InteractiveHost32.csproj b/src/Interactive/HostProcess/x86/InteractiveHost32.csproj index 70e32290b8987..3f5e9f3862b8e 100644 --- a/src/Interactive/HostProcess/x86/InteractiveHost32.csproj +++ b/src/Interactive/HostProcess/x86/InteractiveHost32.csproj @@ -9,6 +9,7 @@ true true true + win-x86 From 31684819004aca33353167334472c15b0dd1878f Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Sat, 8 Feb 2025 15:31:42 +0100 Subject: [PATCH 158/305] Update data section string literals spec (#77050) * Update data section string literals spec * Add benchmark results * Clarify 600 bytes * Fix typos Co-authored-by: Jan Kotas --------- Co-authored-by: Jan Kotas --- docs/features/string-literals-data-section.md | 63 +++++++++++++++++-- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/docs/features/string-literals-data-section.md b/docs/features/string-literals-data-section.md index 6d61a39a0cf14..133416a2cfa36 100644 --- a/docs/features/string-literals-data-section.md +++ b/docs/features/string-literals-data-section.md @@ -136,15 +136,63 @@ albeit with a disclaimer during the experimental phase of the feature. Throughput of `ldstr` vs `ldsfld` is very similar (both result in one or two move instructions). In the `ldsfld` emit strategy, the `string` instances won't ever be collected by the GC once the generated class is initialized. -`ldstr` has similar behavior, but there are some optimizations in the runtime around `ldstr`, +`ldstr` has similar behavior (GC does not collect the string literals either until the assembly is unloaded), +but there are some optimizations in the runtime around `ldstr`, e.g., they are loaded into a different frozen heap so machine codegen can be more efficient (no need to worry about pointer moves). Generating new types by the compiler means more type loads and hence runtime impact, e.g., startup performance and the overhead of keeping track of these types. +On the other hand, the PE size might be smaller due to UTF-8 vs UTF-16 encoding, +which can result in memory savings since the binary is also loaded to memory by the runtime. +See [below](#runtime-overhead-benchmark) for a more detailed analysis. The generated types are returned from reflection like `Assembly.GetTypes()` which might impact the performance of Dependency Injection and similar systems. +### Runtime overhead benchmark + +| [cost per string literal](https://github.com/jkotas/stringliteralperf) | feature on | feature off | +| --- | --- | --- | +| bytes | 1037 | 550 | +| microseconds | 20.3 | 3.1 | + +The benchmark results above [show](https://github.com/dotnet/roslyn/pull/76139#discussion_r1944144978) +that the runtime overhead of this feature per 100 char string literal +is ~500 bytes of working set memory (~2x of regular string literal) +and ~17 microseconds of startup time (~7x of regular string literal). + +The startup time overhead does depend on the length of the string literal. +It is cost of the type loads and JITing the static constructor. + +The working set has two components: private working set (r/w pages) and non-private working set (r/o pages backed by the binary). +The private working set overhead (~600 bytes) does not depend on the length of the string literal. +Again, it is the cost of the type loads and the static constructor code. +Non-private working set is reduced by this feature since the binary is smaller. +Once the string literal is about 600 characters, +the private working set overhead and non-private working set improvement will break even. +For string literals longer than 600 characters, this feature is total working set improvement. + +
+Why 600 bytes? + +When the feature is off, ~550 bytes cost of 100 char string literal is composed from: +- The string in the binary (~200 bytes). +- The string allocated on the GC heap (~200 bytes). +- Fixed overheads: metadata encoding, runtime hashtable of all allocated string literals, code that referenced the string in the benchmark (~150 bytes). + +When the feature is on, ~1050 bytes cost of 100 char string literal is composed from: +- The string in the binary (~100 bytes). +- The string allocated on the GC heap (~200 bytes). +- Fixed overheads: metadata encoding, the extra types, code that referenced the string in the benchmark (~750 bytes). + +750 - 150 = 600. Vast majority of it are the extra types. + +A bit of the extra fixed overheads with the feature on is probably in the non-private working set. +It is difficult to measure it since there is no managed API to get private vs. non-private working set. +It does not impact the estimate of the break-even point for the total working set. + +
+ ## Implementation `CodeGenerator` obtains [configuration of the feature flag](#configuration) from `Compilation` passed to its constructor. @@ -168,7 +216,7 @@ but that seems to require similar amount of implemented abstract properties/meth as the implementations of `Cci` interfaces require. But implementing `Cci` directly allows us to reuse the same implementation for VB if needed in the future. -## Future work +## Future work and alternatives ### Edit and Continue @@ -209,7 +257,7 @@ We would generate a single `__StaticArrayInitTypeSize=*` structure for the entir add a single `.data` field to `` that points to the blob. At runtime, we would do an offset to where the required data reside in the blob and decode the required length from UTF-8 to UTF-16. -## Alternatives +However, this would be unfriendly to IL trimming. ### Configuration/emit granularity @@ -221,7 +269,8 @@ The idea is that strings from one class are likely used "together" so there is n ### GC -To avoid rooting the `string` references forever, we could turn the fields into `WeakReference`s. +To avoid rooting the `string` references forever, we could turn the fields into `WeakReference`s +(note that this would be quite expensive for both direct overhead and indirectly for the GC due to longer GC pause times). Or we could avoid the caching altogether (each eligible `ldstr` would be replaced with a direct call to `Encoding.UTF8.GetString`). This could be configurable as well. @@ -247,6 +296,12 @@ static class However, that would likely result in worse machine code due to more branches and function calls. +### String interning + +The compiler should report a diagnostic when the feature is enabled together with +`[assembly: System.Runtime.CompilerServices.CompilationRelaxations(0)]`, i.e., string interning enabled, +because that is incompatible with the feature. + [u8-literals]: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-11.0/utf8-string-literals [constant-array-init]: https://github.com/dotnet/roslyn/pull/24621 From d4d7e597288185a9452d6a2b060cba622babf64f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 8 Feb 2025 09:38:32 -0800 Subject: [PATCH 159/305] Make physical property into a computed property --- .../EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs index 1323696fd9d7c..1db25aab7f5a8 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs @@ -14,13 +14,13 @@ private partial class DiagnosticIncrementalAnalyzer private sealed class StateSet { public readonly DiagnosticAnalyzer Analyzer; - public readonly bool IsHostAnalyzer; - public StateSet(DiagnosticAnalyzer analyzer, bool isHostAnalyzer) + public StateSet(DiagnosticAnalyzer analyzer) { Analyzer = analyzer; - IsHostAnalyzer = isHostAnalyzer; } + + public bool IsHostAnalyzer => false; } } } From 4fe6423940ae8cd7b93de7e971ff211c8d46261c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 8 Feb 2025 09:39:19 -0800 Subject: [PATCH 160/305] Make FileContentLoadAnalyzer into a hosts analyzer --- .../DiagnosticIncrementalAnalyzer.StateManager.cs | 2 +- .../DiagnosticIncrementalAnalyzer.StateSet.cs | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs index be98563d609bf..63fdc982c0413 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs @@ -108,7 +108,7 @@ private static ImmutableDictionary CreateStateSetM if (includeWorkspacePlaceholderAnalyzers) { - builder.Add(FileContentLoadAnalyzer.Instance, new StateSet(FileContentLoadAnalyzer.Instance, isHostAnalyzer: true)); + builder.Add(FileContentLoadAnalyzer.Instance, new StateSet(FileContentLoadAnalyzer.Instance)); builder.Add(GeneratorDiagnosticsPlaceholderAnalyzer.Instance, new StateSet(GeneratorDiagnosticsPlaceholderAnalyzer.Instance, isHostAnalyzer: true)); } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs index 1db25aab7f5a8..f19803d36d758 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs @@ -20,7 +20,16 @@ public StateSet(DiagnosticAnalyzer analyzer) Analyzer = analyzer; } - public bool IsHostAnalyzer => false; + public bool IsHostAnalyzer + { + get + { + if (this.Analyzer == FileContentLoadAnalyzer.Instance) + return true; + + return false; + } + } } } } From 5fe179d908bdf13ed4372d5fd3aa057ece9c6360 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 8 Feb 2025 09:40:58 -0800 Subject: [PATCH 161/305] Make GenerateDiagnosticAnalyzer into a hosts analyzer --- .../EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs | 2 +- .../EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs index 63fdc982c0413..5b38a10e930bf 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs @@ -109,7 +109,7 @@ private static ImmutableDictionary CreateStateSetM if (includeWorkspacePlaceholderAnalyzers) { builder.Add(FileContentLoadAnalyzer.Instance, new StateSet(FileContentLoadAnalyzer.Instance)); - builder.Add(GeneratorDiagnosticsPlaceholderAnalyzer.Instance, new StateSet(GeneratorDiagnosticsPlaceholderAnalyzer.Instance, isHostAnalyzer: true)); + builder.Add(GeneratorDiagnosticsPlaceholderAnalyzer.Instance, new StateSet(GeneratorDiagnosticsPlaceholderAnalyzer.Instance)); } foreach (var analyzers in projectAnalyzerCollection) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs index f19803d36d758..aac5475972ce2 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs @@ -27,6 +27,9 @@ public bool IsHostAnalyzer if (this.Analyzer == FileContentLoadAnalyzer.Instance) return true; + if (this.Analyzer == GeneratorDiagnosticsPlaceholderAnalyzer.Instance) + return true; + return false; } } From 0a9f2c83275e7921a6450d7443b8c208972d38d6 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 8 Feb 2025 10:11:15 -0800 Subject: [PATCH 162/305] Move more over --- ...ementalAnalyzer.StateManager.HostStates.cs | 46 +++++++------- ...ntalAnalyzer.StateManager.ProjectStates.cs | 48 ++++++++------- ...gnosticIncrementalAnalyzer.StateManager.cs | 60 +++++++++---------- .../EngineV2/DiagnosticIncrementalAnalyzer.cs | 4 +- ...osticIncrementalAnalyzer_GetDiagnostics.cs | 18 +++--- ...crementalAnalyzer_GetDiagnosticsForSpan.cs | 13 ++-- ...IncrementalAnalyzer_IncrementalAnalyzer.cs | 6 +- 7 files changed, 94 insertions(+), 101 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs index 02d78588a170f..3d97ae224f018 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs @@ -17,7 +17,7 @@ private partial class DiagnosticIncrementalAnalyzer { private partial class StateManager { - private HostAnalyzerStateSets GetOrCreateHostStateSets(Project project, ProjectAnalyzerStateSets projectStateSets) + private HostAnalyzerInfo GetOrCreateHostStateSets(Project project, ProjectAnalyzerInfo projectStateSets) { var key = new HostAnalyzerStateSetKey(project.Language, project.State.HasSdkCodeStyleAnalyzers, project.Solution.SolutionState.Analyzers.HostAnalyzerReferences); // Some Host Analyzers may need to be treated as Project Analyzers so that they do not have access to the @@ -26,15 +26,15 @@ private HostAnalyzerStateSets GetOrCreateHostStateSets(Project project, ProjectA var hostStateSets = ImmutableInterlocked.GetOrAdd(ref _hostAnalyzerStateMap, key, CreateLanguageSpecificAnalyzerMap, (project.Solution.SolutionState.Analyzers, referenceIdsToRedirect)); return hostStateSets.WithExcludedAnalyzers(projectStateSets.SkippedAnalyzersInfo.SkippedAnalyzers); - static HostAnalyzerStateSets CreateLanguageSpecificAnalyzerMap(HostAnalyzerStateSetKey arg, (HostDiagnosticAnalyzers HostAnalyzers, ImmutableHashSet ReferenceIdsToRedirect) state) + static HostAnalyzerInfo CreateLanguageSpecificAnalyzerMap(HostAnalyzerStateSetKey arg, (HostDiagnosticAnalyzers HostAnalyzers, ImmutableHashSet ReferenceIdsToRedirect) state) { var language = arg.Language; var analyzersPerReference = state.HostAnalyzers.GetOrCreateHostDiagnosticAnalyzersPerReference(language); var (hostAnalyzerCollection, projectAnalyzerCollection) = GetAnalyzerCollections(analyzersPerReference, state.ReferenceIdsToRedirect); - var analyzerMap = CreateStateSetMap(projectAnalyzerCollection, hostAnalyzerCollection, includeWorkspacePlaceholderAnalyzers: true); + var (hostAnalyzers, allAnalyzers) = CreateStateSetMap(projectAnalyzerCollection, hostAnalyzerCollection, includeWorkspacePlaceholderAnalyzers: true); - return new HostAnalyzerStateSets(analyzerMap); + return new HostAnalyzerInfo(hostAnalyzers, allAnalyzers); } static (IEnumerable> HostAnalyzerCollection, IEnumerable> ProjectAnalyzerCollection) GetAnalyzerCollections( @@ -91,58 +91,52 @@ static ImmutableHashSet GetFeaturesAnalyzerReferenceIds(HostDiagnosticAn } } - private sealed class HostAnalyzerStateSets + private sealed class HostAnalyzerInfo { private const int FileContentLoadAnalyzerPriority = -4; private const int GeneratorDiagnosticsPlaceholderAnalyzerPriority = -3; private const int BuiltInCompilerPriority = -2; private const int RegularDiagnosticAnalyzerPriority = -1; - // ordered by priority - public readonly ImmutableArray OrderedStateSets; + public readonly ImmutableHashSet HostAnalyzers; + public readonly ImmutableHashSet AllAnalyzers; + public readonly ImmutableArray OrderedAllAnalyzers; - public readonly ImmutableDictionary StateSetMap; - - private HostAnalyzerStateSets(ImmutableDictionary stateSetMap, ImmutableArray orderedStateSets) - { - StateSetMap = stateSetMap; - OrderedStateSets = orderedStateSets; - } - - public HostAnalyzerStateSets(ImmutableDictionary analyzerMap) + public HostAnalyzerInfo( + ImmutableHashSet hostAnalyzers, + ImmutableHashSet allAnalyzers) { - StateSetMap = analyzerMap; + HostAnalyzers = hostAnalyzers; + AllAnalyzers = allAnalyzers; // order statesets // order will be in this order // BuiltIn Compiler Analyzer (C#/VB) < Regular DiagnosticAnalyzers < Document/ProjectDiagnosticAnalyzers - OrderedStateSets = [.. StateSetMap.Values.OrderBy(PriorityComparison)]; + OrderedAllAnalyzers = [.. AllAnalyzers.OrderBy(PriorityComparison)]; } - public HostAnalyzerStateSets WithExcludedAnalyzers(ImmutableHashSet excludedAnalyzers) + public HostAnalyzerInfo WithExcludedAnalyzers(ImmutableHashSet excludedAnalyzers) { if (excludedAnalyzers.IsEmpty) { return this; } - var stateSetMap = StateSetMap.Where(kvp => !excludedAnalyzers.Contains(kvp.Key)).ToImmutableDictionary(); - var orderedStateSets = OrderedStateSets.WhereAsArray(stateSet => !excludedAnalyzers.Contains(stateSet.Analyzer)); - return new HostAnalyzerStateSets(stateSetMap, orderedStateSets); + return new(HostAnalyzers, AllAnalyzers.Except(excludedAnalyzers)); } - private int PriorityComparison(StateSet state1, StateSet state2) + private int PriorityComparison(DiagnosticAnalyzer state1, DiagnosticAnalyzer state2) => GetPriority(state1) - GetPriority(state2); - private static int GetPriority(StateSet state) + private static int GetPriority(DiagnosticAnalyzer state) { // compiler gets highest priority - if (state.Analyzer.IsCompilerAnalyzer()) + if (state.IsCompilerAnalyzer()) { return BuiltInCompilerPriority; } - return state.Analyzer switch + return state switch { FileContentLoadAnalyzer _ => FileContentLoadAnalyzerPriority, GeneratorDiagnosticsPlaceholderAnalyzer _ => GeneratorDiagnosticsPlaceholderAnalyzerPriority, diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs index 75031bb5d547e..98607a596fd7c 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs @@ -17,12 +17,12 @@ private partial class DiagnosticIncrementalAnalyzer { private partial class StateManager { - private readonly struct ProjectAnalyzerStateSets + private readonly struct ProjectAnalyzerInfo { - public static readonly ProjectAnalyzerStateSets Default = new( - [], + public static readonly ProjectAnalyzerInfo Default = new( + analyzerReferences: [], ImmutableDictionary>.Empty, - ImmutableDictionary.Empty, + analyzers: [], SkippedHostAnalyzersInfo.Empty); public readonly IReadOnlyList AnalyzerReferences; @@ -30,31 +30,31 @@ private readonly struct ProjectAnalyzerStateSets // maps analyzer reference id to list of analyzers loaded from the reference public readonly ImmutableDictionary> MapPerReferences; - public readonly ImmutableDictionary StateSetMap; + public readonly ImmutableHashSet Analyzers; public readonly SkippedHostAnalyzersInfo SkippedAnalyzersInfo; - internal ProjectAnalyzerStateSets( + internal ProjectAnalyzerInfo( IReadOnlyList analyzerReferences, ImmutableDictionary> mapPerReferences, - ImmutableDictionary stateSetMap, + ImmutableHashSet analyzers, SkippedHostAnalyzersInfo skippedAnalyzersInfo) { AnalyzerReferences = analyzerReferences; MapPerReferences = mapPerReferences; - StateSetMap = stateSetMap; + Analyzers = analyzers; SkippedAnalyzersInfo = skippedAnalyzersInfo; } } - public IEnumerable GetAllProjectStateSets() + public IEnumerable GetAllProjectStateSets() { // return existing state sets // No need to use _projectAnalyzerStateMapGuard during reads of _projectAnalyzerStateMap - return _projectAnalyzerStateMap.Values.SelectManyAsArray(e => e.StateSetMap.Values); + return _projectAnalyzerStateMap.Values.SelectManyAsArray(e => e.Analyzers); } - private ProjectAnalyzerStateSets? TryGetProjectStateSets(Project project) + private ProjectAnalyzerInfo? TryGetProjectAnalyzerInfo(Project project) { // check if the analyzer references have changed since the last time we updated the map: // No need to use _projectAnalyzerStateMapGuard during reads of _projectAnalyzerStateMap @@ -67,44 +67,50 @@ public IEnumerable GetAllProjectStateSets() return null; } - private async Task GetOrCreateProjectStateSetsAsync(Project project, CancellationToken cancellationToken) - => TryGetProjectStateSets(project) ?? await UpdateProjectStateSetsAsync(project, cancellationToken).ConfigureAwait(false); + private async Task GetOrCreateProjectAnalyzerInfoAsync(Project project, CancellationToken cancellationToken) + => TryGetProjectAnalyzerInfo(project) ?? await UpdateProjectAnalyzerInfo(project, cancellationToken).ConfigureAwait(false); /// /// Creates a new project state sets. /// - private ProjectAnalyzerStateSets CreateProjectStateSets(Project project) + private ProjectAnalyzerInfo CreateProjectAnalyzerInfo(Project project) { if (project.AnalyzerReferences.Count == 0) { - return ProjectAnalyzerStateSets.Default; + return ProjectAnalyzerInfo.Default; } var hostAnalyzers = project.Solution.SolutionState.Analyzers; var analyzersPerReference = hostAnalyzers.CreateProjectDiagnosticAnalyzersPerReference(project); if (analyzersPerReference.Count == 0) { - return ProjectAnalyzerStateSets.Default; + return ProjectAnalyzerInfo.Default; } - var newMap = CreateStateSetMap(analyzersPerReference.Values, [], includeWorkspacePlaceholderAnalyzers: false); + var (newHostAnalyzers, newAllAnalyzers) = CreateStateSetMap( + analyzersPerReference.Values, hostAnalyzerCollection: [], includeWorkspacePlaceholderAnalyzers: false); + + // We passed an empty array for 'hostAnalyzeCollection' above, and we specifically asked to not include + // workspace placeholder analyzers. So we should never get host analyzers back here. + Contract.ThrowIfTrue(newHostAnalyzers.Count > 0); + var skippedAnalyzersInfo = project.GetSkippedAnalyzersInfo(_analyzerInfoCache); - return new ProjectAnalyzerStateSets(project.AnalyzerReferences, analyzersPerReference, newMap, skippedAnalyzersInfo); + return new ProjectAnalyzerInfo(project.AnalyzerReferences, analyzersPerReference, newAllAnalyzers, skippedAnalyzersInfo); } /// /// Updates the map to the given project snapshot. /// - private async Task UpdateProjectStateSetsAsync(Project project, CancellationToken cancellationToken) + private async Task UpdateProjectAnalyzerInfo(Project project, CancellationToken cancellationToken) { // This code is called concurrently for a project, so the guard prevents duplicated effort calculating StateSets. using (await _projectAnalyzerStateMapGuard.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) { - var projectStateSets = TryGetProjectStateSets(project); + var projectStateSets = TryGetProjectAnalyzerInfo(project); if (projectStateSets == null) { - projectStateSets = CreateProjectStateSets(project); + projectStateSets = CreateProjectAnalyzerInfo(project); // update cache. _projectAnalyzerStateMap = _projectAnalyzerStateMap.SetItem(project.Id, projectStateSets.Value); diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs index 5b38a10e930bf..072f84d7462dd 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs @@ -30,13 +30,13 @@ private partial class StateManager(DiagnosticAnalyzerInfoCache analyzerInfoCache /// Analyzers supplied by the host (IDE). These are built-in to the IDE, the compiler, or from an installed IDE extension (VSIX). /// Maps language name to the analyzers and their state. /// - private ImmutableDictionary _hostAnalyzerStateMap = ImmutableDictionary.Empty; + private ImmutableDictionary _hostAnalyzerStateMap = ImmutableDictionary.Empty; /// /// Analyzers referenced by the project via a PackageReference. Updates are protected by _projectAnalyzerStateMapGuard. /// ImmutableDictionary used to present a safe, non-immutable view to users. /// - private ImmutableDictionary _projectAnalyzerStateMap = ImmutableDictionary.Empty; + private ImmutableDictionary _projectAnalyzerStateMap = ImmutableDictionary.Empty; /// /// Guard around updating _projectAnalyzerStateMap. This is used in UpdateProjectStateSets to avoid @@ -48,20 +48,20 @@ private partial class StateManager(DiagnosticAnalyzerInfoCache analyzerInfoCache /// Return s for the given . /// This will never create new but will return ones already created. /// - public ImmutableArray GetStateSets(Project project) + public ImmutableArray GetStateSets(Project project) { - using var _ = ArrayBuilder.GetInstance(out var result); + using var _ = ArrayBuilder.GetInstance(out var result); var analyzerReferences = project.Solution.SolutionState.Analyzers.HostAnalyzerReferences; foreach (var (key, value) in _hostAnalyzerStateMap) { if (key.AnalyzerReferences == analyzerReferences) - result.AddRange(value.OrderedStateSets); + result.AddRange(value.OrderedAllAnalyzers); } // No need to use _projectAnalyzerStateMapGuard during reads of _projectAnalyzerStateMap if (_projectAnalyzerStateMap.TryGetValue(project.Id, out var entry)) - result.AddRange(entry.StateSetMap.Values); + result.AddRange(entry.Analyzers); return result.ToImmutableAndClear(); } @@ -71,10 +71,10 @@ public ImmutableArray GetStateSets(Project project) /// This will either return already created s for the specific snapshot of or /// it will create new s for the and update internal state. /// - public async Task> GetOrCreateStateSetsAsync(Project project, CancellationToken cancellationToken) + public async Task> GetOrCreateAnalyzersAsync(Project project, CancellationToken cancellationToken) { - var projectStateSets = await GetOrCreateProjectStateSetsAsync(project, cancellationToken).ConfigureAwait(false); - return GetOrCreateHostStateSets(project, projectStateSets).OrderedStateSets.AddRange(projectStateSets.StateSetMap.Values); + var projectStateSets = await GetOrCreateProjectAnalyzerInfoAsync(project, cancellationToken).ConfigureAwait(false); + return GetOrCreateHostStateSets(project, projectStateSets).OrderedAllAnalyzers.AddRange(projectStateSets.Analyzers); } /// @@ -82,34 +82,37 @@ public async Task> GetOrCreateStateSetsAsync(Project pr /// This will either return already created for the specific snapshot of or /// it will create new for the . and update internal state. /// - public async Task GetOrCreateStateSetAsync(Project project, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken) + public async Task GetOrCreateStateSetAsync(Project project, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken) { - var projectStateSets = await GetOrCreateProjectStateSetsAsync(project, cancellationToken).ConfigureAwait(false); - if (projectStateSets.StateSetMap.TryGetValue(analyzer, out var stateSet)) + var projectStateSets = await GetOrCreateProjectAnalyzerInfoAsync(project, cancellationToken).ConfigureAwait(false); + if (projectStateSets.Analyzers.Contains(analyzer)) { - return stateSet; + return analyzer; } - var hostStateSetMap = GetOrCreateHostStateSets(project, projectStateSets).StateSetMap; - if (hostStateSetMap.TryGetValue(analyzer, out stateSet)) + var hostStateSetMap = GetOrCreateHostStateSets(project, projectStateSets).AllAnalyzers; + if (hostStateSetMap.Contains(analyzer)) { - return stateSet; + return analyzer; } return null; } - private static ImmutableDictionary CreateStateSetMap( + private static (ImmutableHashSet hostAnalyzers, ImmutableHashSet allAnalyzers) CreateStateSetMap( IEnumerable> projectAnalyzerCollection, IEnumerable> hostAnalyzerCollection, bool includeWorkspacePlaceholderAnalyzers) { - var builder = ImmutableDictionary.CreateBuilder(); + using var _1 = PooledHashSet.GetInstance(out var hostAnalyzers); + using var _2 = PooledHashSet.GetInstance(out var allAnalyzers); if (includeWorkspacePlaceholderAnalyzers) { - builder.Add(FileContentLoadAnalyzer.Instance, new StateSet(FileContentLoadAnalyzer.Instance)); - builder.Add(GeneratorDiagnosticsPlaceholderAnalyzer.Instance, new StateSet(GeneratorDiagnosticsPlaceholderAnalyzer.Instance)); + hostAnalyzers.Add(FileContentLoadAnalyzer.Instance); + hostAnalyzers.Add(GeneratorDiagnosticsPlaceholderAnalyzer.Instance); + allAnalyzers.Add(FileContentLoadAnalyzer.Instance); + allAnalyzers.Add(GeneratorDiagnosticsPlaceholderAnalyzer.Instance); } foreach (var analyzers in projectAnalyzerCollection) @@ -122,12 +125,7 @@ private static ImmutableDictionary CreateStateSetM // #1, all de-duplication should move to DiagnosticAnalyzerInfoCache // #2, not sure whether de-duplication of analyzer itself makes sense. this can only happen // if user deliberately put same analyzer twice. - if (builder.ContainsKey(analyzer)) - { - continue; - } - - builder.Add(analyzer, new StateSet(analyzer, isHostAnalyzer: false)); + allAnalyzers.Add(analyzer); } } @@ -141,16 +139,12 @@ private static ImmutableDictionary CreateStateSetM // #1, all de-duplication should move to DiagnosticAnalyzerInfoCache // #2, not sure whether de-duplication of analyzer itself makes sense. this can only happen // if user deliberately put same analyzer twice. - if (builder.ContainsKey(analyzer)) - { - continue; - } - - builder.Add(analyzer, new StateSet(analyzer, isHostAnalyzer: true)); + allAnalyzers.Add(analyzer); + hostAnalyzers.Add(analyzer); } } - return builder.ToImmutable(); + return (hostAnalyzers.ToImmutableHashSet(), allAnalyzers.ToImmutableHashSet()); } private readonly record struct HostAnalyzerStateSetKey( diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index ad47f221f0ee7..9bf60c0f7a2d9 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -51,9 +51,9 @@ public DiagnosticIncrementalAnalyzer( public async Task> GetAnalyzersForTestingPurposesOnlyAsync(Project project, CancellationToken cancellationToken) { - var analyzers = await _stateManager.GetOrCreateStateSetsAsync(project, cancellationToken).ConfigureAwait(false); + var analyzers = await _stateManager.GetOrCreateAnalyzersAsync(project, cancellationToken).ConfigureAwait(false); - return analyzers.SelectAsArray(s => s.Analyzer); + return analyzers; } private static string GetProjectLogMessage(Project project, ImmutableArray stateSets) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index dc107a58ad2c0..3291528c8fbbc 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -101,14 +101,14 @@ private async Task ProduceDiagnosticsAsync( ArrayBuilder builder, CancellationToken cancellationToken) { - var stateSetsForProject = await StateManager.GetOrCreateStateSetsAsync(project, cancellationToken).ConfigureAwait(false); - var stateSets = stateSetsForProject.Where(s => ShouldIncludeStateSet(project, s)).ToImmutableArrayOrEmpty(); + var analyzersForProject = await StateManager.GetOrCreateAnalyzersAsync(project, cancellationToken).ConfigureAwait(false); + var analyzers = analyzersForProject.WhereAsArray(a => ShouldIncludeAnalyzer(project, a)); - var result = await GetOrComputeDiagnosticAnalysisResultsAsync(stateSets).ConfigureAwait(false); + var result = await GetOrComputeDiagnosticAnalysisResultsAsync(analyzers).ConfigureAwait(false); - foreach (var stateSet in stateSets) + foreach (var analyzer in analyzers) { - if (!result.TryGetValue(stateSet.Analyzer, out var analysisResult)) + if (!result.TryGetValue(analyzer, out var analysisResult)) continue; foreach (var documentId in documentIds) @@ -156,19 +156,19 @@ async Task> Ge } } - private bool ShouldIncludeStateSet(Project project, StateSet stateSet) + private bool ShouldIncludeAnalyzer(Project project, DiagnosticAnalyzer analyzer) { - if (!DocumentAnalysisExecutor.IsAnalyzerEnabledForProject(stateSet.Analyzer, project, Owner.GlobalOptions)) + if (!DocumentAnalysisExecutor.IsAnalyzerEnabledForProject(analyzer, project, Owner.GlobalOptions)) { return false; } - if (_shouldIncludeAnalyzer != null && !_shouldIncludeAnalyzer(stateSet.Analyzer)) + if (_shouldIncludeAnalyzer != null && !_shouldIncludeAnalyzer(analyzer)) { return false; } - if (_diagnosticIds != null && Owner.DiagnosticAnalyzerInfoCache.GetDiagnosticDescriptors(stateSet.Analyzer).All(d => !_diagnosticIds.Contains(d.Id))) + if (_diagnosticIds != null && Owner.DiagnosticAnalyzerInfoCache.GetDiagnosticDescriptors(analyzer).All(d => !_diagnosticIds.Contains(d.Id))) { return false; } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs index ad5549f909833..9fc21ef609e65 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs @@ -78,12 +78,11 @@ public static async Task CreateAsync( CancellationToken cancellationToken) { var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); - var unfilteredStateSets = await owner._stateManager - .GetOrCreateStateSetsAsync(document.Project, cancellationToken) + var unfilteredAnalyzers = await owner._stateManager + .GetOrCreateAnalyzersAsync(document.Project, cancellationToken) .ConfigureAwait(false); - var stateSets = unfilteredStateSets - .Where(s => DocumentAnalysisExecutor.IsAnalyzerEnabledForProject(s.Analyzer, document.Project, owner.GlobalOptions)) - .ToImmutableArray(); + var analyzers = unfilteredAnalyzers + .WhereAsArray(a => DocumentAnalysisExecutor.IsAnalyzerEnabledForProject(a, document.Project, owner.GlobalOptions)); // Note that some callers, such as diagnostic tagger, might pass in a range equal to the entire document span. // We clear out range for such cases as we are computing full document diagnostics. @@ -92,7 +91,7 @@ public static async Task CreateAsync( // We log performance info when we are computing diagnostics for a span var logPerformanceInfo = range.HasValue; - var compilationWithAnalyzers = await GetOrCreateCompilationWithAnalyzersAsync(document.Project, stateSets, owner.AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); + var compilationWithAnalyzers = await GetOrCreateCompilationWithAnalyzersAsync(document.Project, analyzers, owner.AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); // If we are computing full document diagnostics, we will attempt to perform incremental // member edit analysis. This analysis is currently only enabled with LSP pull diagnostics. @@ -100,7 +99,7 @@ public static async Task CreateAsync( && document is Document { SupportsSyntaxTree: true }; return new LatestDiagnosticsForSpanGetter( - owner, compilationWithAnalyzers, document, text, stateSets, shouldIncludeDiagnostic, + owner, compilationWithAnalyzers, document, text, analyzers, shouldIncludeDiagnostic, range, priorityProvider, isExplicit, logPerformanceInfo, incrementalAnalysis, diagnosticKinds); } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index c8d7b5f15551c..a0629c8997b86 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -55,9 +55,9 @@ public async Task> ForceAnalyzeProjectAsync(Proje async Task<(ImmutableArray stateSets, ImmutableDictionary diagnosticAnalysisResults)> ComputeForceAnalyzeProjectAsync() { - var allStateSets = await _stateManager.GetOrCreateStateSetsAsync(project, cancellationToken).ConfigureAwait(false); - var fullSolutionAnalysisStateSets = allStateSets.WhereAsArray( - static (stateSet, arg) => arg.self.IsCandidateForFullSolutionAnalysis(stateSet.Analyzer, stateSet.IsHostAnalyzer, arg.project), + var allAnalyzers = await _stateManager.GetOrCreateAnalyzersAsync(project, cancellationToken).ConfigureAwait(false); + var fullSolutionAnalysisStateSets = allAnalyzers.WhereAsArray( + static (analyzer, arg) => arg.self.IsCandidateForFullSolutionAnalysis(analyzer.Analyzer, analyzer.IsHostAnalyzer, arg.project), (self: this, project)); var compilationWithAnalyzers = await CreateCompilationWithAnalyzersAsync( From f80f2d4cbaf19a1c83fd7bf29e8023f0acc0e898 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 8 Feb 2025 10:21:41 -0800 Subject: [PATCH 163/305] Move more over --- ...cIncrementalAnalyzer.CompilationManager.cs | 10 +- ...lAnalyzer.IncrementalMemberEditAnalyzer.cs | 6 +- ...ementalAnalyzer.StateManager.HostStates.cs | 92 +++++++++---------- ...gnosticIncrementalAnalyzer.StateManager.cs | 11 ++- ...crementalAnalyzer_GetDiagnosticsForSpan.cs | 81 ++++++++-------- 5 files changed, 110 insertions(+), 90 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs index b13fd1018622e..0daed62fecbb4 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs @@ -12,11 +12,15 @@ internal partial class DiagnosticAnalyzerService { private partial class DiagnosticIncrementalAnalyzer { - private static Task CreateCompilationWithAnalyzersAsync(Project project, ImmutableArray stateSets, bool crashOnAnalyzerException, CancellationToken cancellationToken) + private static Task CreateCompilationWithAnalyzersAsync( + Project project, + ImmutableArray analyzers, + HostAnalyzerInfo hostAnalyzerInfo, + bool crashOnAnalyzerException, CancellationToken cancellationToken) => DocumentAnalysisExecutor.CreateCompilationWithAnalyzersAsync( project, - stateSets.SelectAsArray(s => !s.IsHostAnalyzer, s => s.Analyzer), - stateSets.SelectAsArray(s => s.IsHostAnalyzer, s => s.Analyzer), + analyzers.WhereAsArray((a, info) => !info.HostAnalyzers.Contains(a), hostAnalyzerInfo), + analyzers.WhereAsArray((a, info) => info.HostAnalyzers.Contains(a), hostAnalyzerInfo), crashOnAnalyzerException, cancellationToken); } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs index ec86bedf339a8..d713928cd7222 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs @@ -46,7 +46,7 @@ public void UpdateDocumentWithCachedDiagnostics(Document document) public async Task>> ComputeDiagnosticsAsync( DocumentAnalysisExecutor executor, - ImmutableArray analyzersWithState, + ImmutableArray analyzers, VersionStamp version, Func>> computeAnalyzerDiagnosticsAsync, Func>>> computeDiagnosticsNonIncrementallyAsync, @@ -58,7 +58,7 @@ public async Task stateSet.Analyzer.SupportsSpanBasedSemanticDiagnosticAnalysis())); + Debug.Assert(analyzers.All(stateSet => stateSet.Analyzer.SupportsSpanBasedSemanticDiagnosticAnalysis())); var document = (Document)analysisScope.TextDocument; var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); @@ -79,7 +79,7 @@ public async Task.GetInstance(out var spanBasedAnalyzers); using var _2 = ArrayBuilder.GetInstance(out var documentBasedAnalyzers); (StateSet analyzerWithState, bool spanBased)? compilerAnalyzerData = null; - foreach (var analyzerWithState in analyzersWithState) + foreach (var analyzerWithState in analyzers) { // Check if we have existing cached diagnostics for this analyzer whose version matches the // old document version. If so, we can perform span based incremental analysis for the changed member. diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs index 3d97ae224f018..4aec7fe00d549 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs @@ -17,7 +17,7 @@ private partial class DiagnosticIncrementalAnalyzer { private partial class StateManager { - private HostAnalyzerInfo GetOrCreateHostStateSets(Project project, ProjectAnalyzerInfo projectStateSets) + private HostAnalyzerInfo GetOrCreateHostAnalyzerInfo(Project project, ProjectAnalyzerInfo projectStateSets) { var key = new HostAnalyzerStateSetKey(project.Language, project.State.HasSdkCodeStyleAnalyzers, project.Solution.SolutionState.Analyzers.HostAnalyzerReferences); // Some Host Analyzers may need to be treated as Project Analyzers so that they do not have access to the @@ -90,61 +90,61 @@ static ImmutableHashSet GetFeaturesAnalyzerReferenceIds(HostDiagnosticAn return builder.ToImmutable(); } } + } - private sealed class HostAnalyzerInfo + private sealed class HostAnalyzerInfo + { + private const int FileContentLoadAnalyzerPriority = -4; + private const int GeneratorDiagnosticsPlaceholderAnalyzerPriority = -3; + private const int BuiltInCompilerPriority = -2; + private const int RegularDiagnosticAnalyzerPriority = -1; + + public readonly ImmutableHashSet HostAnalyzers; + public readonly ImmutableHashSet AllAnalyzers; + public readonly ImmutableArray OrderedAllAnalyzers; + + public HostAnalyzerInfo( + ImmutableHashSet hostAnalyzers, + ImmutableHashSet allAnalyzers) { - private const int FileContentLoadAnalyzerPriority = -4; - private const int GeneratorDiagnosticsPlaceholderAnalyzerPriority = -3; - private const int BuiltInCompilerPriority = -2; - private const int RegularDiagnosticAnalyzerPriority = -1; - - public readonly ImmutableHashSet HostAnalyzers; - public readonly ImmutableHashSet AllAnalyzers; - public readonly ImmutableArray OrderedAllAnalyzers; - - public HostAnalyzerInfo( - ImmutableHashSet hostAnalyzers, - ImmutableHashSet allAnalyzers) - { - HostAnalyzers = hostAnalyzers; - AllAnalyzers = allAnalyzers; + HostAnalyzers = hostAnalyzers; + AllAnalyzers = allAnalyzers; - // order statesets - // order will be in this order - // BuiltIn Compiler Analyzer (C#/VB) < Regular DiagnosticAnalyzers < Document/ProjectDiagnosticAnalyzers - OrderedAllAnalyzers = [.. AllAnalyzers.OrderBy(PriorityComparison)]; - } + // order statesets + // order will be in this order + // BuiltIn Compiler Analyzer (C#/VB) < Regular DiagnosticAnalyzers < Document/ProjectDiagnosticAnalyzers + OrderedAllAnalyzers = [.. AllAnalyzers.OrderBy(PriorityComparison)]; + } - public HostAnalyzerInfo WithExcludedAnalyzers(ImmutableHashSet excludedAnalyzers) + public HostAnalyzerInfo WithExcludedAnalyzers(ImmutableHashSet excludedAnalyzers) + { + if (excludedAnalyzers.IsEmpty) { - if (excludedAnalyzers.IsEmpty) - { - return this; - } - - return new(HostAnalyzers, AllAnalyzers.Except(excludedAnalyzers)); + return this; } - private int PriorityComparison(DiagnosticAnalyzer state1, DiagnosticAnalyzer state2) - => GetPriority(state1) - GetPriority(state2); + return new(HostAnalyzers, AllAnalyzers.Except(excludedAnalyzers)); + } - private static int GetPriority(DiagnosticAnalyzer state) - { - // compiler gets highest priority - if (state.IsCompilerAnalyzer()) - { - return BuiltInCompilerPriority; - } + private int PriorityComparison(DiagnosticAnalyzer state1, DiagnosticAnalyzer state2) + => GetPriority(state1) - GetPriority(state2); - return state switch - { - FileContentLoadAnalyzer _ => FileContentLoadAnalyzerPriority, - GeneratorDiagnosticsPlaceholderAnalyzer _ => GeneratorDiagnosticsPlaceholderAnalyzerPriority, - DocumentDiagnosticAnalyzer analyzer => Math.Max(0, analyzer.Priority), - ProjectDiagnosticAnalyzer analyzer => Math.Max(0, analyzer.Priority), - _ => RegularDiagnosticAnalyzerPriority, - }; + private static int GetPriority(DiagnosticAnalyzer state) + { + // compiler gets highest priority + if (state.IsCompilerAnalyzer()) + { + return BuiltInCompilerPriority; } + + return state switch + { + FileContentLoadAnalyzer _ => FileContentLoadAnalyzerPriority, + GeneratorDiagnosticsPlaceholderAnalyzer _ => GeneratorDiagnosticsPlaceholderAnalyzerPriority, + DocumentDiagnosticAnalyzer analyzer => Math.Max(0, analyzer.Priority), + ProjectDiagnosticAnalyzer analyzer => Math.Max(0, analyzer.Priority), + _ => RegularDiagnosticAnalyzerPriority, + }; } } } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs index 072f84d7462dd..2c8cfaee10ab4 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs @@ -72,9 +72,16 @@ public ImmutableArray GetStateSets(Project project) /// it will create new s for the and update internal state. /// public async Task> GetOrCreateAnalyzersAsync(Project project, CancellationToken cancellationToken) + { + var hostAnalyzerInfo = await GetOrCreateHostAnalyzerInfoAsync(project, cancellationToken).ConfigureAwait(false); + var projectAnalyzerInfo = await GetOrCreateProjectAnalyzerInfoAsync(project, cancellationToken).ConfigureAwait(false); + return hostAnalyzerInfo.OrderedAllAnalyzers.AddRange(projectAnalyzerInfo.Analyzers); + } + + public async Task GetOrCreateHostAnalyzerInfoAsync(Project project, CancellationToken cancellationToken) { var projectStateSets = await GetOrCreateProjectAnalyzerInfoAsync(project, cancellationToken).ConfigureAwait(false); - return GetOrCreateHostStateSets(project, projectStateSets).OrderedAllAnalyzers.AddRange(projectStateSets.Analyzers); + return GetOrCreateHostAnalyzerInfo(project, projectStateSets); } /// @@ -90,7 +97,7 @@ public async Task> GetOrCreateAnalyzersAsync( return analyzer; } - var hostStateSetMap = GetOrCreateHostStateSets(project, projectStateSets).AllAnalyzers; + var hostStateSetMap = GetOrCreateHostAnalyzerInfo(project, projectStateSets).AllAnalyzers; if (hostStateSetMap.Contains(analyzer)) { return analyzer; diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs index 9fc21ef609e65..10247cc235f00 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs @@ -56,7 +56,7 @@ private sealed class LatestDiagnosticsForSpanGetter private readonly TextDocument _document; private readonly SourceText _text; - private readonly ImmutableArray _stateSets; + private readonly ImmutableArray _analyzers; private readonly CompilationWithAnalyzersPair? _compilationWithAnalyzers; private readonly TextSpan? _range; @@ -78,11 +78,13 @@ public static async Task CreateAsync( CancellationToken cancellationToken) { var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); + var unfilteredAnalyzers = await owner._stateManager .GetOrCreateAnalyzersAsync(document.Project, cancellationToken) .ConfigureAwait(false); var analyzers = unfilteredAnalyzers .WhereAsArray(a => DocumentAnalysisExecutor.IsAnalyzerEnabledForProject(a, document.Project, owner.GlobalOptions)); + var hostAnalyzerInfo = await owner._stateManager.GetOrCreateHostAnalyzerInfoAsync(document.Project, cancellationToken).ConfigureAwait(false); // Note that some callers, such as diagnostic tagger, might pass in a range equal to the entire document span. // We clear out range for such cases as we are computing full document diagnostics. @@ -91,7 +93,8 @@ public static async Task CreateAsync( // We log performance info when we are computing diagnostics for a span var logPerformanceInfo = range.HasValue; - var compilationWithAnalyzers = await GetOrCreateCompilationWithAnalyzersAsync(document.Project, analyzers, owner.AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); + var compilationWithAnalyzers = await GetOrCreateCompilationWithAnalyzersAsync( + document.Project, analyzers, hostAnalyzerInfo, owner.AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); // If we are computing full document diagnostics, we will attempt to perform incremental // member edit analysis. This analysis is currently only enabled with LSP pull diagnostics. @@ -105,7 +108,8 @@ public static async Task CreateAsync( private static async Task GetOrCreateCompilationWithAnalyzersAsync( Project project, - ImmutableArray stateSets, + ImmutableArray analyzers, + HostAnalyzerInfo hostAnalyzerInfo, bool crashOnAnalyzerException, CancellationToken cancellationToken) { @@ -117,23 +121,27 @@ public static async Task CreateAsync( return null; } - if (HasAllAnalyzers(stateSets, projectAndCompilationWithAnalyzers.CompilationWithAnalyzers)) + if (HasAllAnalyzers(analyzers, hostAnalyzerInfo, projectAndCompilationWithAnalyzers.CompilationWithAnalyzers)) { return projectAndCompilationWithAnalyzers.CompilationWithAnalyzers; } } - var compilationWithAnalyzers = await CreateCompilationWithAnalyzersAsync(project, stateSets, crashOnAnalyzerException, cancellationToken).ConfigureAwait(false); + var compilationWithAnalyzers = await CreateCompilationWithAnalyzersAsync( + project, analyzers, hostAnalyzerInfo, crashOnAnalyzerException, cancellationToken).ConfigureAwait(false); s_lastProjectAndCompilationWithAnalyzers.SetTarget(new ProjectAndCompilationWithAnalyzers(project, compilationWithAnalyzers)); return compilationWithAnalyzers; - static bool HasAllAnalyzers(IEnumerable stateSets, CompilationWithAnalyzersPair compilationWithAnalyzers) + static bool HasAllAnalyzers( + ImmutableArray analyzers, + HostAnalyzerInfo hostAnalyzerInfo, + CompilationWithAnalyzersPair compilationWithAnalyzers) { - foreach (var stateSet in stateSets) + foreach (var analyzer in analyzers) { - if (stateSet.IsHostAnalyzer && !compilationWithAnalyzers.HostAnalyzers.Contains(stateSet.Analyzer)) + if (hostAnalyzerInfo.HostAnalyzers.Contains(analyzer) && !compilationWithAnalyzers.HostAnalyzers.Contains(analyzer)) return false; - else if (!stateSet.IsHostAnalyzer && !compilationWithAnalyzers.ProjectAnalyzers.Contains(stateSet.Analyzer)) + else if (!hostAnalyzerInfo.HostAnalyzers.Contains(analyzer) && !compilationWithAnalyzers.ProjectAnalyzers.Contains(analyzer)) return false; } @@ -146,7 +154,7 @@ private LatestDiagnosticsForSpanGetter( CompilationWithAnalyzersPair? compilationWithAnalyzers, TextDocument document, SourceText text, - ImmutableArray stateSets, + ImmutableArray analyzers, Func? shouldIncludeDiagnostic, TextSpan? range, ICodeActionRequestPriorityProvider priorityProvider, @@ -159,7 +167,7 @@ private LatestDiagnosticsForSpanGetter( _compilationWithAnalyzers = compilationWithAnalyzers; _document = document; _text = text; - _stateSets = stateSets; + _analyzers = analyzers; _shouldIncludeDiagnostic = shouldIncludeDiagnostic; _range = range; _priorityProvider = priorityProvider; @@ -174,21 +182,20 @@ public async Task GetAsync(ArrayBuilder list, CancellationToken try { // Try to get cached diagnostics, and also compute non-cached state sets that need diagnostic computation. - using var _1 = ArrayBuilder.GetInstance(out var syntaxAnalyzers); + using var _1 = ArrayBuilder.GetInstance(out var syntaxAnalyzers); // If we are performing incremental member edit analysis to compute diagnostics incrementally, // we divide the analyzers into those that support span-based incremental analysis and // those that do not support incremental analysis and must be executed for the entire document. // Otherwise, if we are not performing incremental analysis, all semantic analyzers are added // to the span-based analyzer set as we want to compute diagnostics only for the given span. - using var _2 = ArrayBuilder.GetInstance(out var semanticSpanBasedAnalyzers); - using var _3 = ArrayBuilder.GetInstance(out var semanticDocumentBasedAnalyzers); + using var _2 = ArrayBuilder.GetInstance(out var semanticSpanBasedAnalyzers); + using var _3 = ArrayBuilder.GetInstance(out var semanticDocumentBasedAnalyzers); using var _4 = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.RequestDiagnostics_Summary, $"Pri{_priorityProvider.Priority.GetPriorityInt()}"); - foreach (var stateSet in _stateSets) + foreach (var analyzer in _analyzers) { - var analyzer = stateSet.Analyzer; if (!ShouldIncludeAnalyzer(analyzer, _shouldIncludeDiagnostic, _priorityProvider, _owner)) continue; @@ -211,16 +218,16 @@ public async Task GetAsync(ArrayBuilder list, CancellationToken { if (includeSyntax) { - syntaxAnalyzers.Add(stateSet); + syntaxAnalyzers.Add(analyzer); } if (includeSemantic) { - var stateSets = GetSemanticAnalysisSelectedStates( - stateSet.Analyzer, _incrementalAnalysis, + var stateSets = GetSemanticAnalysisSelectedAnalyzers( + analyzer, _incrementalAnalysis, semanticSpanBasedAnalyzers, semanticDocumentBasedAnalyzers); - stateSets.Add(stateSet); + stateSets.Add(analyzer); } } } @@ -271,11 +278,11 @@ static bool ShouldIncludeAnalyzer( return true; } - static ArrayBuilder GetSemanticAnalysisSelectedStates( + static ArrayBuilder GetSemanticAnalysisSelectedAnalyzers( DiagnosticAnalyzer analyzer, bool incrementalAnalysis, - ArrayBuilder semanticSpanBasedAnalyzers, - ArrayBuilder semanticDocumentBasedAnalyzers) + ArrayBuilder semanticSpanBasedAnalyzers, + ArrayBuilder semanticDocumentBasedAnalyzers) { if (!incrementalAnalysis) { @@ -295,7 +302,7 @@ static ArrayBuilder GetSemanticAnalysisSelectedStates( } private async Task ComputeDocumentDiagnosticsAsync( - ImmutableArray analyzersWithState, + ImmutableArray analyzers, AnalysisKind kind, TextSpan? span, ArrayBuilder builder, @@ -303,31 +310,33 @@ private async Task ComputeDocumentDiagnosticsAsync( CancellationToken cancellationToken) { Debug.Assert(!incrementalAnalysis || kind == AnalysisKind.Semantic); - Debug.Assert(!incrementalAnalysis || analyzersWithState.All(analyzerWithState => analyzerWithState.Analyzer.SupportsSpanBasedSemanticDiagnosticAnalysis())); + Debug.Assert(!incrementalAnalysis || analyzers.All(analyzer => analyzer.SupportsSpanBasedSemanticDiagnosticAnalysis())); - using var _ = ArrayBuilder.GetInstance(analyzersWithState.Length, out var filteredAnalyzersWithStateBuilder); - foreach (var analyzerWithState in analyzersWithState) + using var _ = ArrayBuilder.GetInstance(analyzers.Length, out var filteredAnalyzers); + foreach (var analyzer in analyzers) { - Debug.Assert(_priorityProvider.MatchesPriority(analyzerWithState.Analyzer)); + Debug.Assert(_priorityProvider.MatchesPriority(analyzer)); // Check if this is an expensive analyzer that needs to be de-prioritized to a lower priority bucket. // If so, we skip this analyzer from execution in the current priority bucket. // We will subsequently execute this analyzer in the lower priority bucket. - if (await TryDeprioritizeAnalyzerAsync(analyzerWithState.Analyzer).ConfigureAwait(false)) + if (await TryDeprioritizeAnalyzerAsync(analyzer).ConfigureAwait(false)) { continue; } - filteredAnalyzersWithStateBuilder.Add(analyzerWithState); + filteredAnalyzers.Add(analyzer); } - if (filteredAnalyzersWithStateBuilder.Count == 0) + if (filteredAnalyzers.Count == 0) return; - analyzersWithState = filteredAnalyzersWithStateBuilder.ToImmutable(); + analyzers = filteredAnalyzers.ToImmutable(); + + var hostAnalyzerInfo = await _owner._stateManager.GetOrCreateHostAnalyzerInfoAsync(_document.Project, cancellationToken).ConfigureAwait(false); - var projectAnalyzers = analyzersWithState.SelectAsArray(stateSet => !stateSet.IsHostAnalyzer, stateSet => stateSet.Analyzer); - var hostAnalyzers = analyzersWithState.SelectAsArray(stateSet => stateSet.IsHostAnalyzer, stateSet => stateSet.Analyzer); + var projectAnalyzers = analyzers.WhereAsArray(static (a, info) => !info.HostAnalyzers.Contains(a), hostAnalyzerInfo); + var hostAnalyzers = analyzers.WhereAsArray(static (a, info) => info.HostAnalyzers.Contains(a), hostAnalyzerInfo); var analysisScope = new DocumentAnalysisScope(_document, span, projectAnalyzers, hostAnalyzers, kind); var executor = new DocumentAnalysisExecutor(analysisScope, _compilationWithAnalyzers, _owner._diagnosticAnalyzerRunner, _isExplicit, _logPerformanceInfo); var version = await GetDiagnosticVersionAsync(_document.Project, cancellationToken).ConfigureAwait(false); @@ -339,7 +348,7 @@ private async Task ComputeDocumentDiagnosticsAsync( diagnosticsMap = await _owner._incrementalMemberEditAnalyzer.ComputeDiagnosticsAsync( executor, - analyzersWithState, + analyzers, version, ComputeDocumentDiagnosticsForAnalyzerCoreAsync, ComputeDocumentDiagnosticsCoreAsync, @@ -352,7 +361,7 @@ private async Task ComputeDocumentDiagnosticsAsync( diagnosticsMap = await ComputeDocumentDiagnosticsCoreAsync(executor, cancellationToken).ConfigureAwait(false); } - foreach (var analyzerWithState in analyzersWithState) + foreach (var analyzerWithState in analyzers) { var diagnostics = diagnosticsMap[analyzerWithState.Analyzer]; builder.AddRange(diagnostics.Where(ShouldInclude)); From d5dd709839d046360902a5c7e433fc8e7e27d2f3 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 8 Feb 2025 10:23:27 -0800 Subject: [PATCH 164/305] Move more over --- .../DiagnosticIncrementalAnalyzer.Executor.cs | 9 ++++++--- .../EngineV2/DiagnosticIncrementalAnalyzer.cs | 4 ++-- ...ticIncrementalAnalyzer_IncrementalAnalyzer.cs | 16 +++++++++------- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs index 69f4dc752ab0e..30d4d0a398e70 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs @@ -24,13 +24,16 @@ private partial class DiagnosticIncrementalAnalyzer /// Return all diagnostics that belong to given project for the given StateSets (analyzers) either from cache or by calculating them /// private async Task> ComputeDiagnosticAnalysisResultsAsync( - CompilationWithAnalyzersPair? compilationWithAnalyzers, Project project, ImmutableArray stateSets, CancellationToken cancellationToken) + CompilationWithAnalyzersPair? compilationWithAnalyzers, + Project project, + ImmutableArray analyzers, + CancellationToken cancellationToken) { - using (Logger.LogBlock(FunctionId.Diagnostics_ProjectDiagnostic, GetProjectLogMessage, project, stateSets, cancellationToken)) + using (Logger.LogBlock(FunctionId.Diagnostics_ProjectDiagnostic, GetProjectLogMessage, project, analyzers, cancellationToken)) { try { - var result = await ComputeDiagnosticsForStateSetsAsync(stateSets).ConfigureAwait(false); + var result = await ComputeDiagnosticsForStateSetsAsync(analyzers).ConfigureAwait(false); // If project is not loaded successfully, get rid of any semantic errors from compiler analyzer. // Note: In the past when project was not loaded successfully we did not run any analyzers on the project. diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index 9bf60c0f7a2d9..630a446d92638 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -56,7 +56,7 @@ public async Task> GetAnalyzersForTestingPurp return analyzers; } - private static string GetProjectLogMessage(Project project, ImmutableArray stateSets) - => $"project: ({project.Id}), ({string.Join(Environment.NewLine, stateSets.Select(s => s.Analyzer.ToString()))})"; + private static string GetProjectLogMessage(Project project, ImmutableArray analyzers) + => $"project: ({project.Id}), ({string.Join(Environment.NewLine, analyzers.Select(a => a.ToString()))})"; } } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index a0629c8997b86..6c1a1ddf98547 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -53,18 +53,20 @@ public async Task> ForceAnalyzeProjectAsync(Proje throw ExceptionUtilities.Unreachable(); } - async Task<(ImmutableArray stateSets, ImmutableDictionary diagnosticAnalysisResults)> ComputeForceAnalyzeProjectAsync() + async Task<(ImmutableArray analyzers, ImmutableDictionary diagnosticAnalysisResults)> ComputeForceAnalyzeProjectAsync() { var allAnalyzers = await _stateManager.GetOrCreateAnalyzersAsync(project, cancellationToken).ConfigureAwait(false); - var fullSolutionAnalysisStateSets = allAnalyzers.WhereAsArray( - static (analyzer, arg) => arg.self.IsCandidateForFullSolutionAnalysis(analyzer.Analyzer, analyzer.IsHostAnalyzer, arg.project), - (self: this, project)); + var hostAnalyzerInfo = await _stateManager.GetOrCreateHostAnalyzerInfoAsync(project, cancellationToken).ConfigureAwait(false); + + var fullSolutionAnalysisAnalyzers = allAnalyzers.WhereAsArray( + static (analyzer, arg) => arg.self.IsCandidateForFullSolutionAnalysis(analyzer, arg.hostAnalyzerInfo.HostAnalyzers.Contains(analyzer), arg.project), + (self: this, project, hostAnalyzerInfo)); var compilationWithAnalyzers = await CreateCompilationWithAnalyzersAsync( - project, fullSolutionAnalysisStateSets, AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); + project, fullSolutionAnalysisAnalyzers, hostAnalyzerInfo, AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); - var projectAnalysisData = await ComputeDiagnosticAnalysisResultsAsync(compilationWithAnalyzers, project, fullSolutionAnalysisStateSets, cancellationToken).ConfigureAwait(false); - return (fullSolutionAnalysisStateSets, projectAnalysisData); + var projectAnalysisData = await ComputeDiagnosticAnalysisResultsAsync(compilationWithAnalyzers, project, fullSolutionAnalysisAnalyzers, cancellationToken).ConfigureAwait(false); + return (fullSolutionAnalysisAnalyzers, projectAnalysisData); } } From 554be89d46fc3897bde95f26ba8ac6e33e7d3106 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 8 Feb 2025 10:26:10 -0800 Subject: [PATCH 165/305] Move more over --- .../DiagnosticIncrementalAnalyzer.Executor.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs index 30d4d0a398e70..5225fffffe8e9 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs @@ -26,14 +26,13 @@ private partial class DiagnosticIncrementalAnalyzer private async Task> ComputeDiagnosticAnalysisResultsAsync( CompilationWithAnalyzersPair? compilationWithAnalyzers, Project project, - ImmutableArray analyzers, - CancellationToken cancellationToken) + ImmutableArray analyzers, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.Diagnostics_ProjectDiagnostic, GetProjectLogMessage, project, analyzers, cancellationToken)) { try { - var result = await ComputeDiagnosticsForStateSetsAsync(analyzers).ConfigureAwait(false); + var result = await ComputeDiagnosticsForIDEAnalyzersAsync(analyzers).ConfigureAwait(false); // If project is not loaded successfully, get rid of any semantic errors from compiler analyzer. // Note: In the past when project was not loaded successfully we did not run any analyzers on the project. @@ -114,12 +113,12 @@ async Task> Co } } - async Task> ComputeDiagnosticsForStateSetsAsync( - ImmutableArray stateSets) + async Task> ComputeDiagnosticsForIDEAnalyzersAsync( + ImmutableArray stateSets) { try { - var ideAnalyzers = stateSets.Select(s => s.Analyzer).Where(a => a is ProjectDiagnosticAnalyzer or DocumentDiagnosticAnalyzer).ToImmutableArrayOrEmpty(); + var ideAnalyzers = stateSets.WhereAsArray(a => a is ProjectDiagnosticAnalyzer or DocumentDiagnosticAnalyzer); return await ComputeDiagnosticsForAnalyzersAsync(ideAnalyzers).ConfigureAwait(false); } From d4cb75cecefa8f84d94559e676bfc2622e862da1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 8 Feb 2025 10:26:52 -0800 Subject: [PATCH 166/305] Move more over --- .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 6c1a1ddf98547..ec263a06d8d8e 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -25,7 +25,7 @@ private partial class DiagnosticIncrementalAnalyzer /// cref="DiagnosticGetter.ProduceDiagnosticsAsync"/> to speed up subsequent calls through the normal entry points as long as the project hasn't changed at all. /// - private readonly ConditionalWeakTable stateSets, ImmutableDictionary diagnosticAnalysisResults)>> _projectToForceAnalysisData = new(); + private readonly ConditionalWeakTable analyzers, ImmutableDictionary diagnosticAnalysisResults)>> _projectToForceAnalysisData = new(); public async Task> ForceAnalyzeProjectAsync(Project project, CancellationToken cancellationToken) { @@ -39,10 +39,10 @@ public async Task> ForceAnalyzeProjectAsync(Proje using var _ = ArrayBuilder.GetInstance(out var diagnostics); - var (stateSets, projectAnalysisData) = box.Value; - foreach (var stateSet in stateSets) + var (analyzers, projectAnalysisData) = box.Value; + foreach (var analyzer in analyzers) { - if (projectAnalysisData.TryGetValue(stateSet.Analyzer, out var analyzerResult)) + if (projectAnalysisData.TryGetValue(analyzer, out var analyzerResult)) diagnostics.AddRange(analyzerResult.GetAllDiagnostics()); } From d763eb231757c8fbb44f9bc030d39811292dc9a2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 8 Feb 2025 10:29:32 -0800 Subject: [PATCH 167/305] Move more over --- ...lAnalyzer.IncrementalMemberEditAnalyzer.cs | 57 ++++++++++--------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs index d713928cd7222..818fe1f97c4a8 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; @@ -58,7 +59,7 @@ public async Task stateSet.Analyzer.SupportsSpanBasedSemanticDiagnosticAnalysis())); + Debug.Assert(analyzers.All(analyzer => analyzer.SupportsSpanBasedSemanticDiagnosticAnalysis())); var document = (Document)analysisScope.TextDocument; var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); @@ -76,25 +77,25 @@ public async Task.GetInstance(out var spanBasedAnalyzers); - using var _2 = ArrayBuilder.GetInstance(out var documentBasedAnalyzers); - (StateSet analyzerWithState, bool spanBased)? compilerAnalyzerData = null; - foreach (var analyzerWithState in analyzers) + using var _1 = ArrayBuilder.GetInstance(out var spanBasedAnalyzers); + using var _2 = ArrayBuilder.GetInstance(out var documentBasedAnalyzers); + (DiagnosticAnalyzer analyzer, bool spanBased)? compilerAnalyzerData = null; + foreach (var analyzer in analyzers) { // Check if we have existing cached diagnostics for this analyzer whose version matches the // old document version. If so, we can perform span based incremental analysis for the changed member. // Otherwise, we have to perform entire document analysis. if (oldDocumentVersion == VersionStamp.Default) { - if (!compilerAnalyzerData.HasValue && analyzerWithState.Analyzer.IsCompilerAnalyzer()) - compilerAnalyzerData = (analyzerWithState, spanBased: true); + if (!compilerAnalyzerData.HasValue && analyzer.IsCompilerAnalyzer()) + compilerAnalyzerData = (analyzer, spanBased: true); else - spanBasedAnalyzers.Add(analyzerWithState); + spanBasedAnalyzers.Add(analyzer); } else { - var analyzerWithStateAndEmptyData = analyzerWithState; - if (!compilerAnalyzerData.HasValue && analyzerWithState.Analyzer.IsCompilerAnalyzer()) + var analyzerWithStateAndEmptyData = analyzer; + if (!compilerAnalyzerData.HasValue && analyzer.IsCompilerAnalyzer()) compilerAnalyzerData = (analyzerWithStateAndEmptyData, spanBased: false); else documentBasedAnalyzers.Add(analyzerWithStateAndEmptyData); @@ -126,59 +127,59 @@ public async Task oldMemberSpans, - PooledDictionary> builder) + Dictionary> builder) { if (!compilerAnalyzerData.HasValue) return; - var (analyzerWithState, spanBased) = compilerAnalyzerData.Value; + var (analyzer, spanBased) = compilerAnalyzerData.Value; var span = spanBased ? changedMember.FullSpan : (TextSpan?)null; executor = executor.With(analysisScope.WithSpan(span)); - using var _ = ArrayBuilder.GetInstance(1, analyzerWithState, out var analyzersWithState); - await ExecuteAnalyzersAsync(executor, analyzersWithState, oldMemberSpans, builder).ConfigureAwait(false); + using var _ = ArrayBuilder.GetInstance(1, analyzer, out var analyzers); + await ExecuteAnalyzersAsync(executor, analyzers, oldMemberSpans, builder).ConfigureAwait(false); } async Task ExecuteSpanBasedAnalyzersAsync( - ArrayBuilder analyzersWithState, + ArrayBuilder analyzers, ImmutableArray oldMemberSpans, - PooledDictionary> builder) + Dictionary> builder) { - if (analyzersWithState.Count == 0) + if (analyzers.Count == 0) return; executor = executor.With(analysisScope.WithSpan(changedMember.FullSpan)); - await ExecuteAnalyzersAsync(executor, analyzersWithState, oldMemberSpans, builder).ConfigureAwait(false); + await ExecuteAnalyzersAsync(executor, analyzers, oldMemberSpans, builder).ConfigureAwait(false); } async Task ExecuteDocumentBasedAnalyzersAsync( - ArrayBuilder analyzersWithState, + ArrayBuilder analyzers, ImmutableArray oldMemberSpans, - PooledDictionary> builder) + Dictionary> builder) { - if (analyzersWithState.Count == 0) + if (analyzers.Count == 0) return; executor = executor.With(analysisScope.WithSpan(null)); - await ExecuteAnalyzersAsync(executor, analyzersWithState, oldMemberSpans, builder).ConfigureAwait(false); + await ExecuteAnalyzersAsync(executor, analyzers, oldMemberSpans, builder).ConfigureAwait(false); } async Task ExecuteAnalyzersAsync( DocumentAnalysisExecutor executor, - ArrayBuilder analyzersWithState, + ArrayBuilder analyzers, ImmutableArray oldMemberSpans, - PooledDictionary> builder) + Dictionary> builder) { var analysisScope = executor.AnalysisScope; Debug.Assert(changedMember != null); Debug.Assert(analysisScope.Kind == AnalysisKind.Semantic); - foreach (var analyzerWithState in analyzersWithState) + foreach (var analyzer in analyzers) { - var diagnostics = await computeAnalyzerDiagnosticsAsync(analyzerWithState.Analyzer, executor, cancellationToken).ConfigureAwait(false); - builder.Add(analyzerWithState.Analyzer, diagnostics); + var diagnostics = await computeAnalyzerDiagnosticsAsync(analyzer, executor, cancellationToken).ConfigureAwait(false); + builder.Add(analyzer, diagnostics); } } } From 229001c41e7eacc206d50241ad27dbc43991907c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 8 Feb 2025 10:30:58 -0800 Subject: [PATCH 168/305] Move more over --- .../DiagnosticIncrementalAnalyzer_GetDiagnostics.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index 3291528c8fbbc..af876a1c2bf56 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -102,6 +102,7 @@ private async Task ProduceDiagnosticsAsync( CancellationToken cancellationToken) { var analyzersForProject = await StateManager.GetOrCreateAnalyzersAsync(project, cancellationToken).ConfigureAwait(false); + var hostAnalyzerInfo = await StateManager.GetOrCreateHostAnalyzerInfoAsync(project, cancellationToken).ConfigureAwait(false); var analyzers = analyzersForProject.WhereAsArray(a => ShouldIncludeAnalyzer(project, a)); var result = await GetOrComputeDiagnosticAnalysisResultsAsync(analyzers).ConfigureAwait(false); @@ -130,7 +131,8 @@ private async Task ProduceDiagnosticsAsync( } } - async Task> GetOrComputeDiagnosticAnalysisResultsAsync(ImmutableArray stateSets) + async Task> GetOrComputeDiagnosticAnalysisResultsAsync( + ImmutableArray analyzers) { // If there was a 'ForceAnalyzeProjectAsync' run for this project, we can piggy back off of the // prior computed/cached results as they will be a superset of the results we want. @@ -143,15 +145,16 @@ async Task> Ge // of that call so that we don't accidentally reuse results that would not correspond to what we are // computing ourselves. if (this.Owner._projectToForceAnalysisData.TryGetValue(project, out var box) && - stateSets.IsSubsetOf(box.Value.stateSets)) + analyzers.IsSubsetOf(box.Value.analyzers)) { return box.Value.diagnosticAnalysisResults; } // Otherwise, just compute for the state sets we care about. - var compilation = await CreateCompilationWithAnalyzersAsync(project, stateSets, Owner.AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); + var compilation = await CreateCompilationWithAnalyzersAsync( + project, analyzers, hostAnalyzerInfo, Owner.AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); - var result = await Owner.ComputeDiagnosticAnalysisResultsAsync(compilation, project, stateSets, cancellationToken).ConfigureAwait(false); + var result = await Owner.ComputeDiagnosticAnalysisResultsAsync(compilation, project, analyzers, cancellationToken).ConfigureAwait(false); return result; } } From a8c13a39aaf94b320c566f58e1df131660fc357d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 8 Feb 2025 10:31:16 -0800 Subject: [PATCH 169/305] All building --- .../DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs index 10247cc235f00..c6bca80f222c3 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs @@ -361,9 +361,9 @@ private async Task ComputeDocumentDiagnosticsAsync( diagnosticsMap = await ComputeDocumentDiagnosticsCoreAsync(executor, cancellationToken).ConfigureAwait(false); } - foreach (var analyzerWithState in analyzers) + foreach (var analyzer in analyzers) { - var diagnostics = diagnosticsMap[analyzerWithState.Analyzer]; + var diagnostics = diagnosticsMap[analyzer]; builder.AddRange(diagnostics.Where(ShouldInclude)); } From 76e831d45da0ac398cb0b07eea3fe7684c5e1c21 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 8 Feb 2025 10:32:49 -0800 Subject: [PATCH 170/305] remove method --- ...gnosticIncrementalAnalyzer.StateManager.cs | 26 +------------ .../DiagnosticIncrementalAnalyzer.StateSet.cs | 38 ------------------- 2 files changed, 2 insertions(+), 62 deletions(-) delete mode 100644 src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs index 2c8cfaee10ab4..d112b09197978 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs @@ -20,7 +20,7 @@ internal partial class DiagnosticAnalyzerService private partial class DiagnosticIncrementalAnalyzer { /// - /// This is in charge of anything related to + /// This is in charge of anything related to /// private partial class StateManager(DiagnosticAnalyzerInfoCache analyzerInfoCache) { @@ -45,29 +45,7 @@ private partial class StateManager(DiagnosticAnalyzerInfoCache analyzerInfoCache private readonly SemaphoreSlim _projectAnalyzerStateMapGuard = new(initialCount: 1); /// - /// Return s for the given . - /// This will never create new but will return ones already created. - /// - public ImmutableArray GetStateSets(Project project) - { - using var _ = ArrayBuilder.GetInstance(out var result); - - var analyzerReferences = project.Solution.SolutionState.Analyzers.HostAnalyzerReferences; - foreach (var (key, value) in _hostAnalyzerStateMap) - { - if (key.AnalyzerReferences == analyzerReferences) - result.AddRange(value.OrderedAllAnalyzers); - } - - // No need to use _projectAnalyzerStateMapGuard during reads of _projectAnalyzerStateMap - if (_projectAnalyzerStateMap.TryGetValue(project.Id, out var entry)) - result.AddRange(entry.Analyzers); - - return result.ToImmutableAndClear(); - } - - /// - /// Return s for the given . + /// Return s for the given . /// This will either return already created s for the specific snapshot of or /// it will create new s for the and update internal state. /// diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs deleted file mode 100644 index aac5475972ce2..0000000000000 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.CodeAnalysis.Diagnostics; - -internal partial class DiagnosticAnalyzerService -{ - private partial class DiagnosticIncrementalAnalyzer - { - /// - /// this contains all states regarding a - /// - private sealed class StateSet - { - public readonly DiagnosticAnalyzer Analyzer; - - public StateSet(DiagnosticAnalyzer analyzer) - { - Analyzer = analyzer; - } - - public bool IsHostAnalyzer - { - get - { - if (this.Analyzer == FileContentLoadAnalyzer.Instance) - return true; - - if (this.Analyzer == GeneratorDiagnosticsPlaceholderAnalyzer.Instance) - return true; - - return false; - } - } - } - } -} From dc94a417d1fb0e093fb6668826f30e631f658461 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 8 Feb 2025 10:33:15 -0800 Subject: [PATCH 171/305] remove method --- ...gnosticIncrementalAnalyzer.StateManager.cs | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs index d112b09197978..cd4b4f6d50de0 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs @@ -46,8 +46,6 @@ private partial class StateManager(DiagnosticAnalyzerInfoCache analyzerInfoCache /// /// Return s for the given . - /// This will either return already created s for the specific snapshot of or - /// it will create new s for the and update internal state. /// public async Task> GetOrCreateAnalyzersAsync(Project project, CancellationToken cancellationToken) { @@ -62,28 +60,6 @@ public async Task GetOrCreateHostAnalyzerInfoAsync(Project pro return GetOrCreateHostAnalyzerInfo(project, projectStateSets); } - /// - /// Return for the given in the context of . - /// This will either return already created for the specific snapshot of or - /// it will create new for the . and update internal state. - /// - public async Task GetOrCreateStateSetAsync(Project project, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken) - { - var projectStateSets = await GetOrCreateProjectAnalyzerInfoAsync(project, cancellationToken).ConfigureAwait(false); - if (projectStateSets.Analyzers.Contains(analyzer)) - { - return analyzer; - } - - var hostStateSetMap = GetOrCreateHostAnalyzerInfo(project, projectStateSets).AllAnalyzers; - if (hostStateSetMap.Contains(analyzer)) - { - return analyzer; - } - - return null; - } - private static (ImmutableHashSet hostAnalyzers, ImmutableHashSet allAnalyzers) CreateStateSetMap( IEnumerable> projectAnalyzerCollection, IEnumerable> hostAnalyzerCollection, From af8226062349b93f9c0968d363ce697d611fc413 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 8 Feb 2025 10:35:50 -0800 Subject: [PATCH 172/305] remove method --- .../DiagnosticIncrementalAnalyzer.Executor.cs | 7 ++++--- ...cIncrementalAnalyzer.StateManager.HostStates.cs | 14 +++++++------- ...crementalAnalyzer.StateManager.ProjectStates.cs | 9 +-------- .../DiagnosticIncrementalAnalyzer.StateManager.cs | 10 +++++----- 4 files changed, 17 insertions(+), 23 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs index 5225fffffe8e9..8a8ed63bd5b83 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs @@ -21,7 +21,8 @@ internal partial class DiagnosticAnalyzerService private partial class DiagnosticIncrementalAnalyzer { /// - /// Return all diagnostics that belong to given project for the given StateSets (analyzers) either from cache or by calculating them + /// Return all diagnostics that belong to given project for the given either + /// from cache or by calculating them. /// private async Task> ComputeDiagnosticAnalysisResultsAsync( CompilationWithAnalyzersPair? compilationWithAnalyzers, @@ -114,11 +115,11 @@ async Task> Co } async Task> ComputeDiagnosticsForIDEAnalyzersAsync( - ImmutableArray stateSets) + ImmutableArray analyzers) { try { - var ideAnalyzers = stateSets.WhereAsArray(a => a is ProjectDiagnosticAnalyzer or DocumentDiagnosticAnalyzer); + var ideAnalyzers = analyzers.WhereAsArray(a => a is ProjectDiagnosticAnalyzer or DocumentDiagnosticAnalyzer); return await ComputeDiagnosticsForAnalyzersAsync(ideAnalyzers).ConfigureAwait(false); } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs index 4aec7fe00d549..ed6f0500181d0 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs @@ -17,22 +17,22 @@ private partial class DiagnosticIncrementalAnalyzer { private partial class StateManager { - private HostAnalyzerInfo GetOrCreateHostAnalyzerInfo(Project project, ProjectAnalyzerInfo projectStateSets) + private HostAnalyzerInfo GetOrCreateHostAnalyzerInfo(Project project, ProjectAnalyzerInfo projectAnalyzerInfo) { - var key = new HostAnalyzerStateSetKey(project.Language, project.State.HasSdkCodeStyleAnalyzers, project.Solution.SolutionState.Analyzers.HostAnalyzerReferences); + var key = new HostAnalyzersKey(project.Language, project.State.HasSdkCodeStyleAnalyzers, project.Solution.SolutionState.Analyzers.HostAnalyzerReferences); // Some Host Analyzers may need to be treated as Project Analyzers so that they do not have access to the // Host fallback options. These ids will be used when building up the Host and Project analyzer collections. var referenceIdsToRedirect = GetReferenceIdsToRedirectAsProjectAnalyzers(project); - var hostStateSets = ImmutableInterlocked.GetOrAdd(ref _hostAnalyzerStateMap, key, CreateLanguageSpecificAnalyzerMap, (project.Solution.SolutionState.Analyzers, referenceIdsToRedirect)); - return hostStateSets.WithExcludedAnalyzers(projectStateSets.SkippedAnalyzersInfo.SkippedAnalyzers); + var hostAnalyzerInfo = ImmutableInterlocked.GetOrAdd(ref _hostAnalyzerStateMap, key, CreateLanguageSpecificAnalyzerMap, (project.Solution.SolutionState.Analyzers, referenceIdsToRedirect)); + return hostAnalyzerInfo.WithExcludedAnalyzers(projectAnalyzerInfo.SkippedAnalyzersInfo.SkippedAnalyzers); - static HostAnalyzerInfo CreateLanguageSpecificAnalyzerMap(HostAnalyzerStateSetKey arg, (HostDiagnosticAnalyzers HostAnalyzers, ImmutableHashSet ReferenceIdsToRedirect) state) + static HostAnalyzerInfo CreateLanguageSpecificAnalyzerMap(HostAnalyzersKey arg, (HostDiagnosticAnalyzers HostAnalyzers, ImmutableHashSet ReferenceIdsToRedirect) state) { var language = arg.Language; var analyzersPerReference = state.HostAnalyzers.GetOrCreateHostDiagnosticAnalyzersPerReference(language); var (hostAnalyzerCollection, projectAnalyzerCollection) = GetAnalyzerCollections(analyzersPerReference, state.ReferenceIdsToRedirect); - var (hostAnalyzers, allAnalyzers) = CreateStateSetMap(projectAnalyzerCollection, hostAnalyzerCollection, includeWorkspacePlaceholderAnalyzers: true); + var (hostAnalyzers, allAnalyzers) = PartitionAnalyzers(projectAnalyzerCollection, hostAnalyzerCollection, includeWorkspacePlaceholderAnalyzers: true); return new HostAnalyzerInfo(hostAnalyzers, allAnalyzers); } @@ -110,7 +110,7 @@ public HostAnalyzerInfo( HostAnalyzers = hostAnalyzers; AllAnalyzers = allAnalyzers; - // order statesets + // order analyzers. // order will be in this order // BuiltIn Compiler Analyzer (C#/VB) < Regular DiagnosticAnalyzers < Document/ProjectDiagnosticAnalyzers OrderedAllAnalyzers = [.. AllAnalyzers.OrderBy(PriorityComparison)]; diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs index 98607a596fd7c..b2b049bf7a83c 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs @@ -47,13 +47,6 @@ internal ProjectAnalyzerInfo( } } - public IEnumerable GetAllProjectStateSets() - { - // return existing state sets - // No need to use _projectAnalyzerStateMapGuard during reads of _projectAnalyzerStateMap - return _projectAnalyzerStateMap.Values.SelectManyAsArray(e => e.Analyzers); - } - private ProjectAnalyzerInfo? TryGetProjectAnalyzerInfo(Project project) { // check if the analyzer references have changed since the last time we updated the map: @@ -87,7 +80,7 @@ private ProjectAnalyzerInfo CreateProjectAnalyzerInfo(Project project) return ProjectAnalyzerInfo.Default; } - var (newHostAnalyzers, newAllAnalyzers) = CreateStateSetMap( + var (newHostAnalyzers, newAllAnalyzers) = PartitionAnalyzers( analyzersPerReference.Values, hostAnalyzerCollection: [], includeWorkspacePlaceholderAnalyzers: false); // We passed an empty array for 'hostAnalyzeCollection' above, and we specifically asked to not include diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs index cd4b4f6d50de0..dd737fa6466f9 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs @@ -30,7 +30,7 @@ private partial class StateManager(DiagnosticAnalyzerInfoCache analyzerInfoCache /// Analyzers supplied by the host (IDE). These are built-in to the IDE, the compiler, or from an installed IDE extension (VSIX). /// Maps language name to the analyzers and their state. /// - private ImmutableDictionary _hostAnalyzerStateMap = ImmutableDictionary.Empty; + private ImmutableDictionary _hostAnalyzerStateMap = ImmutableDictionary.Empty; /// /// Analyzers referenced by the project via a PackageReference. Updates are protected by _projectAnalyzerStateMapGuard. @@ -56,11 +56,11 @@ public async Task> GetOrCreateAnalyzersAsync( public async Task GetOrCreateHostAnalyzerInfoAsync(Project project, CancellationToken cancellationToken) { - var projectStateSets = await GetOrCreateProjectAnalyzerInfoAsync(project, cancellationToken).ConfigureAwait(false); - return GetOrCreateHostAnalyzerInfo(project, projectStateSets); + var projectAnalyzerInfo = await GetOrCreateProjectAnalyzerInfoAsync(project, cancellationToken).ConfigureAwait(false); + return GetOrCreateHostAnalyzerInfo(project, projectAnalyzerInfo); } - private static (ImmutableHashSet hostAnalyzers, ImmutableHashSet allAnalyzers) CreateStateSetMap( + private static (ImmutableHashSet hostAnalyzers, ImmutableHashSet allAnalyzers) PartitionAnalyzers( IEnumerable> projectAnalyzerCollection, IEnumerable> hostAnalyzerCollection, bool includeWorkspacePlaceholderAnalyzers) @@ -108,7 +108,7 @@ private static (ImmutableHashSet hostAnalyzers, ImmutableHas return (hostAnalyzers.ToImmutableHashSet(), allAnalyzers.ToImmutableHashSet()); } - private readonly record struct HostAnalyzerStateSetKey( + private readonly record struct HostAnalyzersKey( string Language, bool HasSdkCodeStyleAnalyzers, IReadOnlyList AnalyzerReferences); } } From 56416aa314ff644aa542cfc50514a4454dea5191 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 8 Feb 2025 10:37:03 -0800 Subject: [PATCH 173/305] rename --- ...crementalAnalyzer.StateManager.ProjectStates.cs | 14 +++++++------- ...ticIncrementalAnalyzer_GetDiagnosticsForSpan.cs | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs index b2b049bf7a83c..718e3ae87012c 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs @@ -61,7 +61,7 @@ internal ProjectAnalyzerInfo( } private async Task GetOrCreateProjectAnalyzerInfoAsync(Project project, CancellationToken cancellationToken) - => TryGetProjectAnalyzerInfo(project) ?? await UpdateProjectAnalyzerInfo(project, cancellationToken).ConfigureAwait(false); + => TryGetProjectAnalyzerInfo(project) ?? await UpdateProjectAnalyzerInfoAsync(project, cancellationToken).ConfigureAwait(false); /// /// Creates a new project state sets. @@ -94,22 +94,22 @@ private ProjectAnalyzerInfo CreateProjectAnalyzerInfo(Project project) /// /// Updates the map to the given project snapshot. /// - private async Task UpdateProjectAnalyzerInfo(Project project, CancellationToken cancellationToken) + private async Task UpdateProjectAnalyzerInfoAsync(Project project, CancellationToken cancellationToken) { // This code is called concurrently for a project, so the guard prevents duplicated effort calculating StateSets. using (await _projectAnalyzerStateMapGuard.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) { - var projectStateSets = TryGetProjectAnalyzerInfo(project); + var projectAnalyzerInfo = TryGetProjectAnalyzerInfo(project); - if (projectStateSets == null) + if (projectAnalyzerInfo == null) { - projectStateSets = CreateProjectAnalyzerInfo(project); + projectAnalyzerInfo = CreateProjectAnalyzerInfo(project); // update cache. - _projectAnalyzerStateMap = _projectAnalyzerStateMap.SetItem(project.Id, projectStateSets.Value); + _projectAnalyzerStateMap = _projectAnalyzerStateMap.SetItem(project.Id, projectAnalyzerInfo.Value); } - return projectStateSets.Value; + return projectAnalyzerInfo.Value; } } } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs index c6bca80f222c3..da4332b7e30a0 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs @@ -223,11 +223,11 @@ public async Task GetAsync(ArrayBuilder list, CancellationToken if (includeSemantic) { - var stateSets = GetSemanticAnalysisSelectedAnalyzers( + var selectedAnalyzers = GetSemanticAnalysisSelectedAnalyzers( analyzer, _incrementalAnalysis, semanticSpanBasedAnalyzers, semanticDocumentBasedAnalyzers); - stateSets.Add(analyzer); + selectedAnalyzers.Add(analyzer); } } } From b094454b8fdf07293394e4a89b35969452df6dbc Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 8 Feb 2025 10:38:26 -0800 Subject: [PATCH 174/305] Remove comments --- ...IncrementalAnalyzer.StateManager.ProjectStates.cs | 3 --- .../DiagnosticIncrementalAnalyzer_GetDiagnostics.cs | 12 ++++++------ ...osticIncrementalAnalyzer_GetDiagnosticsForSpan.cs | 2 -- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs index 718e3ae87012c..cff3769b3cb0a 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs @@ -63,9 +63,6 @@ internal ProjectAnalyzerInfo( private async Task GetOrCreateProjectAnalyzerInfoAsync(Project project, CancellationToken cancellationToken) => TryGetProjectAnalyzerInfo(project) ?? await UpdateProjectAnalyzerInfoAsync(project, cancellationToken).ConfigureAwait(false); - /// - /// Creates a new project state sets. - /// private ProjectAnalyzerInfo CreateProjectAnalyzerInfo(Project project) { if (project.AnalyzerReferences.Count == 0) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index af876a1c2bf56..096caf856ea24 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -137,20 +137,20 @@ async Task> Ge // If there was a 'ForceAnalyzeProjectAsync' run for this project, we can piggy back off of the // prior computed/cached results as they will be a superset of the results we want. // - // Note: the caller will loop over *its* state sets, grabbing from the full set of data we've cached + // Note: the caller will loop over *its* analzyers, grabbing from the full set of data we've cached // for this project, and filtering down further. So it's ok to return this potentially larger set. // - // Note: While ForceAnalyzeProjectAsync should always run with a larger state set than us (since it - // runs all analyzers), we still run a paranoia check that the state sets we care about are a subset - // of that call so that we don't accidentally reuse results that would not correspond to what we are - // computing ourselves. + // Note: While ForceAnalyzeProjectAsync should always run with a larger set of analyzers than us + // (since it runs all analyzers), we still run a paranoia check that the analyzers we care about are + // a subset of that call so that we don't accidentally reuse results that would not correspond to + // what we are computing ourselves. if (this.Owner._projectToForceAnalysisData.TryGetValue(project, out var box) && analyzers.IsSubsetOf(box.Value.analyzers)) { return box.Value.diagnosticAnalysisResults; } - // Otherwise, just compute for the state sets we care about. + // Otherwise, just compute for the analyzers we care about. var compilation = await CreateCompilationWithAnalyzersAsync( project, analyzers, hostAnalyzerInfo, Owner.AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs index da4332b7e30a0..4d401a1ca6452 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs @@ -181,7 +181,6 @@ public async Task GetAsync(ArrayBuilder list, CancellationToken { try { - // Try to get cached diagnostics, and also compute non-cached state sets that need diagnostic computation. using var _1 = ArrayBuilder.GetInstance(out var syntaxAnalyzers); // If we are performing incremental member edit analysis to compute diagnostics incrementally, @@ -232,7 +231,6 @@ public async Task GetAsync(ArrayBuilder list, CancellationToken } } - // Compute diagnostics for non-cached state sets. await ComputeDocumentDiagnosticsAsync(syntaxAnalyzers.ToImmutable(), AnalysisKind.Syntax, _range, list, incrementalAnalysis: false, cancellationToken).ConfigureAwait(false); await ComputeDocumentDiagnosticsAsync(semanticSpanBasedAnalyzers.ToImmutable(), AnalysisKind.Semantic, _range, list, _incrementalAnalysis, cancellationToken).ConfigureAwait(false); await ComputeDocumentDiagnosticsAsync(semanticDocumentBasedAnalyzers.ToImmutable(), AnalysisKind.Semantic, span: null, list, incrementalAnalysis: false, cancellationToken).ConfigureAwait(false); From 268ce43e952475c325023728d5443c19f4e30e0f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 8 Feb 2025 10:41:42 -0800 Subject: [PATCH 175/305] Simplify API --- .../DiagnosticIncrementalAnalyzer.CompilationManager.cs | 4 ++-- ...gnosticIncrementalAnalyzer.StateManager.HostStates.cs | 9 ++++++--- ...iagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs | 8 ++++---- .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 2 +- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs index 0daed62fecbb4..94d69a92dc2ee 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs @@ -19,8 +19,8 @@ private partial class DiagnosticIncrementalAnalyzer bool crashOnAnalyzerException, CancellationToken cancellationToken) => DocumentAnalysisExecutor.CreateCompilationWithAnalyzersAsync( project, - analyzers.WhereAsArray((a, info) => !info.HostAnalyzers.Contains(a), hostAnalyzerInfo), - analyzers.WhereAsArray((a, info) => info.HostAnalyzers.Contains(a), hostAnalyzerInfo), + analyzers.WhereAsArray(static (a, info) => !info.IsHostAnalyzer(a), hostAnalyzerInfo), + analyzers.WhereAsArray(static (a, info) => info.IsHostAnalyzer(a), hostAnalyzerInfo), crashOnAnalyzerException, cancellationToken); } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs index ed6f0500181d0..bfdbf14e97afd 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs @@ -99,7 +99,7 @@ private sealed class HostAnalyzerInfo private const int BuiltInCompilerPriority = -2; private const int RegularDiagnosticAnalyzerPriority = -1; - public readonly ImmutableHashSet HostAnalyzers; + private readonly ImmutableHashSet _hostAnalyzers; public readonly ImmutableHashSet AllAnalyzers; public readonly ImmutableArray OrderedAllAnalyzers; @@ -107,7 +107,7 @@ public HostAnalyzerInfo( ImmutableHashSet hostAnalyzers, ImmutableHashSet allAnalyzers) { - HostAnalyzers = hostAnalyzers; + _hostAnalyzers = hostAnalyzers; AllAnalyzers = allAnalyzers; // order analyzers. @@ -116,6 +116,9 @@ public HostAnalyzerInfo( OrderedAllAnalyzers = [.. AllAnalyzers.OrderBy(PriorityComparison)]; } + public bool IsHostAnalyzer(DiagnosticAnalyzer analyzer) + => _hostAnalyzers.Contains(analyzer); + public HostAnalyzerInfo WithExcludedAnalyzers(ImmutableHashSet excludedAnalyzers) { if (excludedAnalyzers.IsEmpty) @@ -123,7 +126,7 @@ public HostAnalyzerInfo WithExcludedAnalyzers(ImmutableHashSet !info.HostAnalyzers.Contains(a), hostAnalyzerInfo); - var hostAnalyzers = analyzers.WhereAsArray(static (a, info) => info.HostAnalyzers.Contains(a), hostAnalyzerInfo); + var projectAnalyzers = analyzers.WhereAsArray(static (a, info) => !info.IsHostAnalyzer(a), hostAnalyzerInfo); + var hostAnalyzers = analyzers.WhereAsArray(static (a, info) => info.IsHostAnalyzer(a), hostAnalyzerInfo); var analysisScope = new DocumentAnalysisScope(_document, span, projectAnalyzers, hostAnalyzers, kind); var executor = new DocumentAnalysisExecutor(analysisScope, _compilationWithAnalyzers, _owner._diagnosticAnalyzerRunner, _isExplicit, _logPerformanceInfo); var version = await GetDiagnosticVersionAsync(_document.Project, cancellationToken).ConfigureAwait(false); diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index ec263a06d8d8e..7fc12c9dac469 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -59,7 +59,7 @@ public async Task> ForceAnalyzeProjectAsync(Proje var hostAnalyzerInfo = await _stateManager.GetOrCreateHostAnalyzerInfoAsync(project, cancellationToken).ConfigureAwait(false); var fullSolutionAnalysisAnalyzers = allAnalyzers.WhereAsArray( - static (analyzer, arg) => arg.self.IsCandidateForFullSolutionAnalysis(analyzer, arg.hostAnalyzerInfo.HostAnalyzers.Contains(analyzer), arg.project), + static (analyzer, arg) => arg.self.IsCandidateForFullSolutionAnalysis(analyzer, arg.hostAnalyzerInfo.IsHostAnalyzer(analyzer), arg.project), (self: this, project, hostAnalyzerInfo)); var compilationWithAnalyzers = await CreateCompilationWithAnalyzersAsync( From 6f167895e04f51a1195b6463bb55c1f17c0f0649 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 8 Feb 2025 10:42:08 -0800 Subject: [PATCH 176/305] Update src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs --- .../EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs index 8a8ed63bd5b83..9083f2b77e0c4 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs @@ -27,7 +27,8 @@ private partial class DiagnosticIncrementalAnalyzer private async Task> ComputeDiagnosticAnalysisResultsAsync( CompilationWithAnalyzersPair? compilationWithAnalyzers, Project project, - ImmutableArray analyzers, CancellationToken cancellationToken) + ImmutableArray analyzers, + CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.Diagnostics_ProjectDiagnostic, GetProjectLogMessage, project, analyzers, cancellationToken)) { From d9c05f924a163d0a725e5c45178dc98829d1cb38 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 8 Feb 2025 10:43:31 -0800 Subject: [PATCH 177/305] renames --- ...s.cs => DiagnosticIncrementalAnalyzer.HostAnalyzerInfo.cs} | 4 ++-- .../EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/{DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs => DiagnosticIncrementalAnalyzer.HostAnalyzerInfo.cs} (96%) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.HostAnalyzerInfo.cs similarity index 96% rename from src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs rename to src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.HostAnalyzerInfo.cs index bfdbf14e97afd..7dd4af18560a3 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.HostAnalyzerInfo.cs @@ -19,14 +19,14 @@ private partial class StateManager { private HostAnalyzerInfo GetOrCreateHostAnalyzerInfo(Project project, ProjectAnalyzerInfo projectAnalyzerInfo) { - var key = new HostAnalyzersKey(project.Language, project.State.HasSdkCodeStyleAnalyzers, project.Solution.SolutionState.Analyzers.HostAnalyzerReferences); + var key = new HostAnalyzerInfoKey(project.Language, project.State.HasSdkCodeStyleAnalyzers, project.Solution.SolutionState.Analyzers.HostAnalyzerReferences); // Some Host Analyzers may need to be treated as Project Analyzers so that they do not have access to the // Host fallback options. These ids will be used when building up the Host and Project analyzer collections. var referenceIdsToRedirect = GetReferenceIdsToRedirectAsProjectAnalyzers(project); var hostAnalyzerInfo = ImmutableInterlocked.GetOrAdd(ref _hostAnalyzerStateMap, key, CreateLanguageSpecificAnalyzerMap, (project.Solution.SolutionState.Analyzers, referenceIdsToRedirect)); return hostAnalyzerInfo.WithExcludedAnalyzers(projectAnalyzerInfo.SkippedAnalyzersInfo.SkippedAnalyzers); - static HostAnalyzerInfo CreateLanguageSpecificAnalyzerMap(HostAnalyzersKey arg, (HostDiagnosticAnalyzers HostAnalyzers, ImmutableHashSet ReferenceIdsToRedirect) state) + static HostAnalyzerInfo CreateLanguageSpecificAnalyzerMap(HostAnalyzerInfoKey arg, (HostDiagnosticAnalyzers HostAnalyzers, ImmutableHashSet ReferenceIdsToRedirect) state) { var language = arg.Language; var analyzersPerReference = state.HostAnalyzers.GetOrCreateHostDiagnosticAnalyzersPerReference(language); diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs index dd737fa6466f9..e3efbcf14327a 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs @@ -30,7 +30,7 @@ private partial class StateManager(DiagnosticAnalyzerInfoCache analyzerInfoCache /// Analyzers supplied by the host (IDE). These are built-in to the IDE, the compiler, or from an installed IDE extension (VSIX). /// Maps language name to the analyzers and their state. /// - private ImmutableDictionary _hostAnalyzerStateMap = ImmutableDictionary.Empty; + private ImmutableDictionary _hostAnalyzerStateMap = ImmutableDictionary.Empty; /// /// Analyzers referenced by the project via a PackageReference. Updates are protected by _projectAnalyzerStateMapGuard. @@ -108,7 +108,7 @@ private static (ImmutableHashSet hostAnalyzers, ImmutableHas return (hostAnalyzers.ToImmutableHashSet(), allAnalyzers.ToImmutableHashSet()); } - private readonly record struct HostAnalyzersKey( + private readonly record struct HostAnalyzerInfoKey( string Language, bool HasSdkCodeStyleAnalyzers, IReadOnlyList AnalyzerReferences); } } From 8273332ec410a295d4aa920f9e6892cae62df05e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 8 Feb 2025 10:46:05 -0800 Subject: [PATCH 178/305] Simplify --- ...osticIncrementalAnalyzer.StateManager.ProjectStates.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs index cff3769b3cb0a..c3eca357edf17 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs @@ -21,27 +21,21 @@ private readonly struct ProjectAnalyzerInfo { public static readonly ProjectAnalyzerInfo Default = new( analyzerReferences: [], - ImmutableDictionary>.Empty, analyzers: [], SkippedHostAnalyzersInfo.Empty); public readonly IReadOnlyList AnalyzerReferences; - // maps analyzer reference id to list of analyzers loaded from the reference - public readonly ImmutableDictionary> MapPerReferences; - public readonly ImmutableHashSet Analyzers; public readonly SkippedHostAnalyzersInfo SkippedAnalyzersInfo; internal ProjectAnalyzerInfo( IReadOnlyList analyzerReferences, - ImmutableDictionary> mapPerReferences, ImmutableHashSet analyzers, SkippedHostAnalyzersInfo skippedAnalyzersInfo) { AnalyzerReferences = analyzerReferences; - MapPerReferences = mapPerReferences; Analyzers = analyzers; SkippedAnalyzersInfo = skippedAnalyzersInfo; } @@ -85,7 +79,7 @@ private ProjectAnalyzerInfo CreateProjectAnalyzerInfo(Project project) Contract.ThrowIfTrue(newHostAnalyzers.Count > 0); var skippedAnalyzersInfo = project.GetSkippedAnalyzersInfo(_analyzerInfoCache); - return new ProjectAnalyzerInfo(project.AnalyzerReferences, analyzersPerReference, newAllAnalyzers, skippedAnalyzersInfo); + return new ProjectAnalyzerInfo(project.AnalyzerReferences, newAllAnalyzers, skippedAnalyzersInfo); } /// From 74553b7cfdcb63be7caeecad05ac0776fdf807dc Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 8 Feb 2025 10:47:26 -0800 Subject: [PATCH 179/305] Make private --- .../DiagnosticIncrementalAnalyzer.HostAnalyzerInfo.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.HostAnalyzerInfo.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.HostAnalyzerInfo.cs index 7dd4af18560a3..d3b44c716fe82 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.HostAnalyzerInfo.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.HostAnalyzerInfo.cs @@ -100,7 +100,7 @@ private sealed class HostAnalyzerInfo private const int RegularDiagnosticAnalyzerPriority = -1; private readonly ImmutableHashSet _hostAnalyzers; - public readonly ImmutableHashSet AllAnalyzers; + private readonly ImmutableHashSet _allAnalyzers; public readonly ImmutableArray OrderedAllAnalyzers; public HostAnalyzerInfo( @@ -108,12 +108,12 @@ public HostAnalyzerInfo( ImmutableHashSet allAnalyzers) { _hostAnalyzers = hostAnalyzers; - AllAnalyzers = allAnalyzers; + _allAnalyzers = allAnalyzers; // order analyzers. // order will be in this order // BuiltIn Compiler Analyzer (C#/VB) < Regular DiagnosticAnalyzers < Document/ProjectDiagnosticAnalyzers - OrderedAllAnalyzers = [.. AllAnalyzers.OrderBy(PriorityComparison)]; + OrderedAllAnalyzers = [.. _allAnalyzers.OrderBy(PriorityComparison)]; } public bool IsHostAnalyzer(DiagnosticAnalyzer analyzer) @@ -126,7 +126,7 @@ public HostAnalyzerInfo WithExcludedAnalyzers(ImmutableHashSet Date: Sat, 8 Feb 2025 10:56:01 -0800 Subject: [PATCH 180/305] Simplify subset logic --- ...cIncrementalAnalyzer.CompilationManager.cs | 33 +++++-------------- 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs index 02d8688f9ec2d..0795b3454a313 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs @@ -23,7 +23,7 @@ internal partial class DiagnosticAnalyzerService /// passed along with the project. As such, we might not be able to use a prior cached value if the set of state /// sets changes. In that case, a new instance will be created and will be cached for the next caller. /// - private static readonly ConditionalWeakTable s_projectToCompilationWithAnalyzers = new(); + private static readonly ConditionalWeakTable stateSets, CompilationWithAnalyzersPair? compilationWithAnalyzersPair)>> s_projectToCompilationWithAnalyzers = new(); private static async Task GetOrCreateCompilationWithAnalyzersAsync( Project project, @@ -34,12 +34,13 @@ internal partial class DiagnosticAnalyzerService if (!project.SupportsCompilation) return null; - // Make sure the cached pair matches the state sets we're asking about. if not, recompute and cache with - // the new state sets. - if (!s_projectToCompilationWithAnalyzers.TryGetValue(project, out var compilationWithAnalyzersPair) || - !HasAllAnalyzers(stateSets, compilationWithAnalyzersPair)) + // Make sure the cached pair was computed with at least the same state sets we're asking about. if not, + // recompute and cache with the new state sets. + if (!s_projectToCompilationWithAnalyzers.TryGetValue(project, out var tupleBox) || + !stateSets.IsSubsetOf(tupleBox.Value.stateSets)) { - compilationWithAnalyzersPair = await CreateCompilationWithAnalyzersAsync().ConfigureAwait(false); + var compilationWithAnalyzersPair = await CreateCompilationWithAnalyzersAsync().ConfigureAwait(false); + tupleBox = new((stateSets, compilationWithAnalyzersPair)); // Make a best effort attempt to store the latest computed value against these state sets. If this // fails (because another thread interleaves with this), that's ok. We still return the pair we @@ -48,26 +49,10 @@ internal partial class DiagnosticAnalyzerService // Intentionally ignore the result of this. We still want to use the value we computed above, even if // another thread interleaves and sets a different value. - s_projectToCompilationWithAnalyzers.GetValue(project, _ => compilationWithAnalyzersPair); + s_projectToCompilationWithAnalyzers.GetValue(project, _ => tupleBox); } - return compilationWithAnalyzersPair; - - static bool HasAllAnalyzers(ImmutableArray stateSets, CompilationWithAnalyzersPair? compilationWithAnalyzers) - { - if (compilationWithAnalyzers is null) - return false; - - foreach (var stateSet in stateSets) - { - if (stateSet.IsHostAnalyzer && !compilationWithAnalyzers.HostAnalyzers.Contains(stateSet.Analyzer)) - return false; - else if (!stateSet.IsHostAnalyzer && !compilationWithAnalyzers.ProjectAnalyzers.Contains(stateSet.Analyzer)) - return false; - } - - return true; - } + return tupleBox.Value.compilationWithAnalyzersPair; // // Should only be called on a that . From 3040c801e14fb18723f3f49cd54d627985093cef Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 8 Feb 2025 12:28:29 -0800 Subject: [PATCH 181/305] delete file --- ...cIncrementalAnalyzer.CompilationManager.cs | 7 +++--- .../DiagnosticIncrementalAnalyzer.StateSet.cs | 23 ------------------- 2 files changed, 4 insertions(+), 26 deletions(-) delete mode 100644 src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs index 50cbffd7d1468..f04fa0a778fa9 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs @@ -19,9 +19,10 @@ internal partial class DiagnosticAnalyzerService { /// /// Cached data from a to the last instance created - /// for it. Note: the CompilationWithAnalyzersPair instance is dependent on the set of s - /// passed along with the project. As such, we might not be able to use a prior cached value if the set of state - /// sets changes. In that case, a new instance will be created and will be cached for the next caller. + /// for it. Note: the CompilationWithAnalyzersPair instance is dependent on the set of s passed along with the project. As such, we might not be able to use a prior cached + /// value if the set of analyzers changes. In that case, a new instance will be created and will be cached for the + /// next caller. /// private static readonly ConditionalWeakTable analyzers, CompilationWithAnalyzersPair? compilationWithAnalyzersPair)>> s_projectToCompilationWithAnalyzers = new(); diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs deleted file mode 100644 index af13df1bcf9c3..0000000000000 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.CodeAnalysis.Diagnostics; - -internal partial class DiagnosticAnalyzerService -{ - /// - /// this contains all states regarding a - /// - private sealed class StateSet - { - public readonly DiagnosticAnalyzer Analyzer; - public readonly bool IsHostAnalyzer; - - public StateSet(DiagnosticAnalyzer analyzer, bool isHostAnalyzer) - { - Analyzer = analyzer; - IsHostAnalyzer = isHostAnalyzer; - } - } -} From cde937bc1540085a20fefd5814113bfdc827bcaf Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 8 Feb 2025 12:42:14 -0800 Subject: [PATCH 182/305] Simplify data passed to diagnostic service' git push --- .../Test/CodeFixes/CodeFixServiceTests.cs | 2 +- .../DiagnosticAnalyzerServiceTests.cs | 2 +- .../Diagnostics/DiagnosticProviderTests.vb | 2 +- .../Diagnostics/DiagnosticServiceTests.vb | 14 +++++----- .../Diagnostics/IDiagnosticAnalyzerService.cs | 13 ++++----- .../TestDiagnosticAnalyzerDriver.cs | 4 +-- ...CodeFixService.FixAllDiagnosticProvider.cs | 7 ++--- .../Diagnostics/DiagnosticAnalyzerService.cs | 13 +++++---- ...osticIncrementalAnalyzer_GetDiagnostics.cs | 28 ++++++------------- .../AbstractProjectDiagnosticSource.cs | 4 +-- ...stractWorkspaceDocumentDiagnosticSource.cs | 3 +- .../NonLocalDocumentDiagnosticSource.cs | 3 +- .../VisualStudioSuppressionFixService.cs | 4 +-- .../ExternalDiagnosticUpdateSourceTests.vb | 4 +-- .../Venus/DocumentService_IntegrationTests.vb | 2 +- 15 files changed, 45 insertions(+), 60 deletions(-) diff --git a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs index 6287855eac349..f449f7978a0af 100644 --- a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs +++ b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs @@ -1075,7 +1075,7 @@ await VerifyCachedDiagnosticsAsync( : root.DescendantNodes().OfType().First().Span; await analyzerService.GetDiagnosticsForIdsAsync( - sourceDocument.Project.Solution, sourceDocument.Project.Id, sourceDocument.Id, diagnosticIds: null, shouldIncludeAnalyzer: null, + sourceDocument.Project, sourceDocument.Id, diagnosticIds: null, shouldIncludeAnalyzer: null, includeLocalDocumentDiagnostics: true, includeNonLocalDocumentDiagnostics: true, CancellationToken.None); // await diagnosticIncrementalAnalyzer.GetTestAccessor().TextDocumentOpenAsync(sourceDocument); diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index 760822aad0cc9..6b141120ee330 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -67,7 +67,7 @@ public async Task TestHasSuccessfullyLoadedBeingFalse() var globalOptions = exportProvider.GetExportedValue(); var diagnostics = await service.GetDiagnosticsForIdsAsync( - workspace.CurrentSolution, projectId: workspace.CurrentSolution.ProjectIds.Single(), documentId: null, diagnosticIds: null, shouldIncludeAnalyzer: null, + workspace.CurrentSolution.Projects.Single(), documentId: null, diagnosticIds: null, shouldIncludeAnalyzer: null, includeLocalDocumentDiagnostics: true, includeNonLocalDocumentDiagnostics: false, CancellationToken.None); Assert.NotEmpty(diagnostics); } diff --git a/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb b/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb index 5536152449f15..bbb8f0f4a8012 100644 --- a/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb @@ -265,7 +265,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests For Each project In workspace.CurrentSolution.Projects actualDiagnostics.AddRange(diagnosticProvider.GetDiagnosticsForIdsAsync( - workspace.CurrentSolution, project.Id, documentId:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, + project, documentId:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, includeLocalDocumentDiagnostics:=True, includeNonLocalDocumentDiagnostics:=True, CancellationToken.None).Result) Next diff --git a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb index 01274bb16ac82..c52637d2bcb8e 100644 --- a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb @@ -526,7 +526,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Assert.Empty(diagnostics) diagnostics = Await diagnosticService.GetDiagnosticsForIdsAsync( - project.Solution, projectId:=document.Project.Id, documentId:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, + project, documentId:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, includeLocalDocumentDiagnostics:=True, includeNonLocalDocumentDiagnostics:=True, CancellationToken.None) Dim diagnostic = diagnostics.First() Assert.True(diagnostic.Id = "AD0001") @@ -595,7 +595,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim document = project.Documents.Single() Dim diagnostics = Await diagnosticService.GetDiagnosticsForIdsAsync( - project.Solution, project.Id, documentId:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, + project, documentId:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, includeLocalDocumentDiagnostics:=True, includeNonLocalDocumentDiagnostics:=True, CancellationToken.None) Assert.Equal(1, diagnostics.Length) Dim diagnostic = diagnostics.First() @@ -789,7 +789,7 @@ class AnonymousFunctions ' Test "GetDiagnosticsForIdsAsync" does force computation of compilation end diagnostics. ' Verify compilation diagnostics are reported with correct location info when asked for project diagnostics. Dim projectDiagnostics = Await diagnosticService.GetDiagnosticsForIdsAsync( - project.Solution, project.Id, documentId:=Nothing, + project, documentId:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, includeLocalDocumentDiagnostics:=True, includeNonLocalDocumentDiagnostics:=True, CancellationToken.None) Assert.Equal(2, projectDiagnostics.Length) @@ -933,7 +933,7 @@ class AnonymousFunctions ' Verify no duplicate analysis/diagnostics. Dim document = project.Documents.Single() Dim diagnostics = (Await diagnosticService.GetDiagnosticsForIdsAsync( - project.Solution, project.Id, documentId:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, + project, documentId:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, includeLocalDocumentDiagnostics:=True, includeNonLocalDocumentDiagnostics:=True, CancellationToken.None)). Select(Function(d) d.Id = NamedTypeAnalyzer.DiagDescriptor.Id) @@ -1026,7 +1026,7 @@ class AnonymousFunctions ' Verify project diagnostics contains diagnostics reported on both partial definitions. Dim diagnostics = Await diagnosticService.GetDiagnosticsForIdsAsync( - project.Solution, project.Id, documentId:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, + project, documentId:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, includeLocalDocumentDiagnostics:=True, includeNonLocalDocumentDiagnostics:=True, CancellationToken.None) Assert.Equal(2, diagnostics.Length) Dim file1HasDiag = False, file2HasDiag = False @@ -2117,7 +2117,7 @@ class MyClass ' Get diagnostics explicitly Dim hiddenDiagnostics = Await diagnosticService.GetDiagnosticsForIdsAsync( - project.Solution, project.Id, documentId:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, + project, documentId:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, includeLocalDocumentDiagnostics:=True, includeNonLocalDocumentDiagnostics:=True, CancellationToken.None) Assert.Equal(1, hiddenDiagnostics.Length) Assert.Equal(analyzer.Descriptor.Id, hiddenDiagnostics.Single().Id) @@ -2202,7 +2202,7 @@ class C Assert.Equal(1, descriptorsMap.Count) Dim diagnostics = Await diagnosticService.GetDiagnosticsForIdsAsync( - project.Solution, project.Id, documentId:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, + project, documentId:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, includeLocalDocumentDiagnostics:=True, includeNonLocalDocumentDiagnostics:=True, CancellationToken.None) Assert.Empty(diagnostics) End Using diff --git a/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs index 171903b5091ce..3ce287befb209 100644 --- a/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs @@ -31,12 +31,10 @@ internal interface IDiagnosticAnalyzerService /// /// Get diagnostics of the given diagnostic ids and/or analyzers from the given solution. all diagnostics returned /// should be up-to-date with respect to the given solution. Note that for project case, this method returns - /// diagnostics from all project documents as well. Use if you want + /// diagnostics from all project documents as well. Use if you want /// to fetch only project diagnostics without source locations. /// - /// Solution to fetch the diagnostics for. - /// Optional project to scope the returned diagnostics. + /// Project to fetch the diagnostics for. /// Optional document to scope the returned diagnostics. /// Optional set of diagnostic IDs to scope the returned diagnostics. /// Option callback to filter out analyzers to execute for computing diagnostics. @@ -51,7 +49,7 @@ internal interface IDiagnosticAnalyzerService /// project must be analyzed to get the complete set of non-local document diagnostics. /// /// Cancellation token. - Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken); + Task> GetDiagnosticsForIdsAsync(Project project, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken); /// /// Get project diagnostics (diagnostics with no source location) of the given diagnostic ids and/or analyzers from @@ -59,8 +57,7 @@ internal interface IDiagnosticAnalyzerService /// this method doesn't return any document diagnostics. Use to also fetch /// those. /// - /// Solution to fetch the diagnostics for. - /// Optional project to scope the returned diagnostics. + /// Project to fetch the diagnostics for. /// Optional set of diagnostic IDs to scope the returned diagnostics. /// Option callback to filter out analyzers to execute for computing diagnostics. /// @@ -69,7 +66,7 @@ internal interface IDiagnosticAnalyzerService /// Entire project must be analyzed to get the complete set of non-local diagnostics. /// /// Cancellation token. - Task> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId projectId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken); + Task> GetProjectDiagnosticsForIdsAsync(Project project, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken); /// /// Return up to date diagnostics for the given span for the document diff --git a/src/LanguageServer/Protocol.TestUtilities/Diagnostics/TestDiagnosticAnalyzerDriver.cs b/src/LanguageServer/Protocol.TestUtilities/Diagnostics/TestDiagnosticAnalyzerDriver.cs index aacc5d3037843..e2b06c30dfbf6 100644 --- a/src/LanguageServer/Protocol.TestUtilities/Diagnostics/TestDiagnosticAnalyzerDriver.cs +++ b/src/LanguageServer/Protocol.TestUtilities/Diagnostics/TestDiagnosticAnalyzerDriver.cs @@ -48,7 +48,7 @@ private async Task> GetDiagnosticsAsync( { var text = await document.GetTextAsync().ConfigureAwait(false); var dxs = await _diagnosticAnalyzerService.GetDiagnosticsForIdsAsync( - project.Solution, project.Id, document.Id, diagnosticIds: null, shouldIncludeAnalyzer: null, + project, document.Id, diagnosticIds: null, shouldIncludeAnalyzer: null, includeLocalDocumentDiagnostics: true, _includeNonLocalDocumentDiagnostics, CancellationToken.None); dxs = dxs.WhereAsArray(d => _includeSuppressedDiagnostics || !d.IsSuppressed); documentDiagnostics = await CodeAnalysis.Diagnostics.Extensions.ToDiagnosticsAsync( @@ -62,7 +62,7 @@ filterSpan is null if (getProjectDiagnostics) { var dxs = await _diagnosticAnalyzerService.GetDiagnosticsForIdsAsync( - project.Solution, project.Id, documentId: null, diagnosticIds: null, shouldIncludeAnalyzer: null, + project, documentId: null, diagnosticIds: null, shouldIncludeAnalyzer: null, includeLocalDocumentDiagnostics: true, _includeNonLocalDocumentDiagnostics, CancellationToken.None); dxs = dxs.WhereAsArray(d => _includeSuppressedDiagnostics || !d.IsSuppressed); projectDiagnostics = await CodeAnalysis.Diagnostics.Extensions.ToDiagnosticsAsync(dxs.Where(d => d.DocumentId is null), project, CancellationToken.None); diff --git a/src/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs b/src/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs index 628855831a7cd..46e18b82466e5 100644 --- a/src/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs +++ b/src/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs @@ -46,9 +46,8 @@ private ImmutableArray Filter(ImmutableArray dia public override async Task> GetDocumentDiagnosticsAsync(Document document, CancellationToken cancellationToken) { - var solution = document.Project.Solution; var diagnostics = Filter(await _diagnosticService.GetDiagnosticsForIdsAsync( - solution, projectId: document.Project.Id, document.Id, _diagnosticIds, shouldIncludeAnalyzer: null, includeLocalDocumentDiagnostics: true, includeNonLocalDocumentDiagnostics: false, cancellationToken).ConfigureAwait(false)); + document.Project, document.Id, _diagnosticIds, shouldIncludeAnalyzer: null, includeLocalDocumentDiagnostics: true, includeNonLocalDocumentDiagnostics: false, cancellationToken).ConfigureAwait(false)); Contract.ThrowIfFalse(diagnostics.All(d => d.DocumentId != null)); return await diagnostics.ToDiagnosticsAsync(document.Project, cancellationToken).ConfigureAwait(false); } @@ -68,7 +67,7 @@ public override async Task> GetAllDiagnosticsAsync(Proje { // Get all diagnostics for the entire project, including document diagnostics. var diagnostics = Filter(await _diagnosticService.GetDiagnosticsForIdsAsync( - project.Solution, project.Id, documentId: null, _diagnosticIds, shouldIncludeAnalyzer: null, includeLocalDocumentDiagnostics: true, includeNonLocalDocumentDiagnostics: false, cancellationToken).ConfigureAwait(false)); + project, documentId: null, _diagnosticIds, shouldIncludeAnalyzer: null, includeLocalDocumentDiagnostics: true, includeNonLocalDocumentDiagnostics: false, cancellationToken).ConfigureAwait(false)); return await diagnostics.ToDiagnosticsAsync(project, cancellationToken).ConfigureAwait(false); } @@ -76,7 +75,7 @@ public override async Task> GetProjectDiagnosticsAsync(P { // Get all no-location diagnostics for the project, doesn't include document diagnostics. var diagnostics = Filter(await _diagnosticService.GetProjectDiagnosticsForIdsAsync( - project.Solution, project.Id, _diagnosticIds, shouldIncludeAnalyzer: null, includeNonLocalDocumentDiagnostics: false, cancellationToken).ConfigureAwait(false)); + project, _diagnosticIds, shouldIncludeAnalyzer: null, includeNonLocalDocumentDiagnostics: false, cancellationToken).ConfigureAwait(false)); Contract.ThrowIfFalse(diagnostics.All(d => d.DocumentId == null)); return await diagnostics.ToDiagnosticsAsync(project, cancellationToken).ConfigureAwait(false); } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs index 992dadc5aab71..373a7f7be6d87 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Text; @@ -100,19 +101,19 @@ public async Task> ForceAnalyzeProjectAsync(Proje } public Task> GetDiagnosticsForIdsAsync( - Solution solution, ProjectId projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) + Project project, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) { - var analyzer = CreateIncrementalAnalyzer(solution.Workspace); - return analyzer.GetDiagnosticsForIdsAsync(solution, projectId, documentId, diagnosticIds, shouldIncludeAnalyzer, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics, cancellationToken); + var analyzer = CreateIncrementalAnalyzer(project.Solution.Workspace); + return analyzer.GetDiagnosticsForIdsAsync(project, documentId, diagnosticIds, shouldIncludeAnalyzer, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics, cancellationToken); } public Task> GetProjectDiagnosticsForIdsAsync( - Solution solution, ProjectId projectId, ImmutableHashSet? diagnosticIds, + Project project, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) { - var analyzer = CreateIncrementalAnalyzer(solution.Workspace); - return analyzer.GetProjectDiagnosticsForIdsAsync(solution, projectId, diagnosticIds, shouldIncludeAnalyzer, includeNonLocalDocumentDiagnostics, cancellationToken); + var analyzer = CreateIncrementalAnalyzer(project.Solution.Workspace); + return analyzer.GetProjectDiagnosticsForIdsAsync(project, diagnosticIds, shouldIncludeAnalyzer, includeNonLocalDocumentDiagnostics, cancellationToken); } public TestAccessor GetTestAccessor() diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index e4b9a2658b0fc..2c2524dc8f626 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -18,16 +18,15 @@ internal partial class DiagnosticAnalyzerService { private partial class DiagnosticIncrementalAnalyzer { - public Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) - => new DiagnosticGetter(this, solution, projectId, documentId, diagnosticIds, shouldIncludeAnalyzer, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics).GetDiagnosticsAsync(cancellationToken); + public Task> GetDiagnosticsForIdsAsync(Project project, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) + => new DiagnosticGetter(this, project, documentId, diagnosticIds, shouldIncludeAnalyzer, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics).GetDiagnosticsAsync(cancellationToken); - public Task> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId projectId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) - => new DiagnosticGetter(this, solution, projectId, documentId: null, diagnosticIds, shouldIncludeAnalyzer, includeLocalDocumentDiagnostics: false, includeNonLocalDocumentDiagnostics).GetProjectDiagnosticsAsync(cancellationToken); + public Task> GetProjectDiagnosticsForIdsAsync(Project project, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) + => new DiagnosticGetter(this, project, documentId: null, diagnosticIds, shouldIncludeAnalyzer, includeLocalDocumentDiagnostics: false, includeNonLocalDocumentDiagnostics).GetProjectDiagnosticsAsync(cancellationToken); private sealed class DiagnosticGetter( DiagnosticIncrementalAnalyzer owner, - Solution solution, - ProjectId projectId, + Project project, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, @@ -36,8 +35,7 @@ private sealed class DiagnosticGetter( { private readonly DiagnosticIncrementalAnalyzer Owner = owner; - private readonly Solution Solution = solution; - private readonly ProjectId ProjectId = projectId; + private readonly Project Project = project; private readonly DocumentId? DocumentId = documentId; private readonly ImmutableHashSet? _diagnosticIds = diagnosticIds; private readonly Func? _shouldIncludeAnalyzer = shouldIncludeAnalyzer; @@ -51,17 +49,13 @@ private bool ShouldIncludeDiagnostic(DiagnosticData diagnostic) public async Task> GetDiagnosticsAsync(CancellationToken cancellationToken) { - var project = Solution.GetProject(ProjectId); - if (project == null) - return []; - // return diagnostics specific to one project or document var includeProjectNonLocalResult = DocumentId == null; return await ProduceProjectDiagnosticsAsync( - project, + this.Project, // Ensure we compute and return diagnostics for both the normal docs and the additional docs in this // project if no specific document id was requested. - this.DocumentId != null ? [this.DocumentId] : [.. project.DocumentIds, .. project.AdditionalDocumentIds], + this.DocumentId != null ? [this.DocumentId] : [.. this.Project.DocumentIds, .. this.Project.AdditionalDocumentIds], includeProjectNonLocalResult, cancellationToken).ConfigureAwait(false); } @@ -86,12 +80,8 @@ private void AddIncludedDiagnostics(ArrayBuilder builder, Immuta public async Task> GetProjectDiagnosticsAsync(CancellationToken cancellationToken) { - var project = Solution.GetProject(ProjectId); - if (project is null) - return []; - return await ProduceProjectDiagnosticsAsync( - project, documentIds: [], includeProjectNonLocalResult: true, cancellationToken).ConfigureAwait(false); + this.Project, documentIds: [], includeProjectNonLocalResult: true, cancellationToken).ConfigureAwait(false); } private async Task ProduceDiagnosticsAsync( diff --git a/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractProjectDiagnosticSource.cs b/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractProjectDiagnosticSource.cs index 433c165cdf7de..18fc22f98ca51 100644 --- a/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractProjectDiagnosticSource.cs +++ b/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractProjectDiagnosticSource.cs @@ -50,8 +50,8 @@ public override async Task> GetDiagnosticsAsync( // we're passing in. If information is already cached for that snapshot, it will be returned. Otherwise, // it will be computed on demand. Because it is always accurate as per this snapshot, all spans are correct // and do not need to be adjusted. - var diagnostics = await diagnosticAnalyzerService.GetProjectDiagnosticsForIdsAsync(Project.Solution, Project.Id, - diagnosticIds: null, shouldIncludeAnalyzer, includeNonLocalDocumentDiagnostics: false, cancellationToken).ConfigureAwait(false); + var diagnostics = await diagnosticAnalyzerService.GetProjectDiagnosticsForIdsAsync( + Project, diagnosticIds: null, shouldIncludeAnalyzer, includeNonLocalDocumentDiagnostics: false, cancellationToken).ConfigureAwait(false); // TODO(cyrusn): In the future we could consider reporting these, but with a flag on the diagnostic mentioning // that it is suppressed and should be hidden from the task list by default. diff --git a/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractWorkspaceDocumentDiagnosticSource.cs b/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractWorkspaceDocumentDiagnosticSource.cs index 888e9ba506776..7777dba417737 100644 --- a/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractWorkspaceDocumentDiagnosticSource.cs +++ b/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractWorkspaceDocumentDiagnosticSource.cs @@ -76,8 +76,7 @@ AsyncLazy> GetLazyDiagnostics() async cancellationToken => { var allDiagnostics = await diagnosticAnalyzerService.GetDiagnosticsForIdsAsync( - Document.Project.Solution, Document.Project.Id, documentId: null, - diagnosticIds: null, shouldIncludeAnalyzer, + Document.Project, documentId: null, diagnosticIds: null, shouldIncludeAnalyzer, includeLocalDocumentDiagnostics: true, includeNonLocalDocumentDiagnostics: true, cancellationToken).ConfigureAwait(false); // TODO(cyrusn): Should we be filtering out suppressed diagnostics here? This is how the diff --git a/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/NonLocalDocumentDiagnosticSource.cs b/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/NonLocalDocumentDiagnosticSource.cs index b2a168f48a928..b089870386125 100644 --- a/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/NonLocalDocumentDiagnosticSource.cs +++ b/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/NonLocalDocumentDiagnosticSource.cs @@ -26,8 +26,7 @@ public override async Task> GetDiagnosticsAsync( // document including those reported as a compilation end diagnostic. These are not included in document pull // (uses GetDiagnosticsForSpan) due to cost. var diagnostics = await diagnosticAnalyzerService.GetDiagnosticsForIdsAsync( - Document.Project.Solution, Document.Project.Id, Document.Id, - diagnosticIds: null, _shouldIncludeAnalyzer, + Document.Project, Document.Id, diagnosticIds: null, _shouldIncludeAnalyzer, includeLocalDocumentDiagnostics: false, includeNonLocalDocumentDiagnostics: true, cancellationToken).ConfigureAwait(false); // TODO(cyrusn): In the future we could consider reporting these, but with a flag on the diagnostic mentioning diff --git a/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs b/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs index 083a2fc3c2efa..e29643e157e4e 100644 --- a/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs +++ b/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs @@ -486,7 +486,7 @@ private async Task>> Ge RoslynDebug.AssertNotNull(latestDocumentDiagnosticsMap); var uniqueDiagnosticIds = group.SelectMany(kvp => kvp.Value.Select(d => d.Id)).ToImmutableHashSet(); - var latestProjectDiagnostics = (await _diagnosticService.GetDiagnosticsForIdsAsync(project.Solution, project.Id, documentId: null, + var latestProjectDiagnostics = (await _diagnosticService.GetDiagnosticsForIdsAsync(project, documentId: null, diagnosticIds: uniqueDiagnosticIds, shouldIncludeAnalyzer: null, includeLocalDocumentDiagnostics: true, includeNonLocalDocumentDiagnostics: true, cancellationToken) .ConfigureAwait(false)).Where(IsDocumentDiagnostic); @@ -576,7 +576,7 @@ private async Task>> Get RoslynDebug.AssertNotNull(latestDiagnosticsToFix); var uniqueDiagnosticIds = diagnostics.Select(d => d.Id).ToImmutableHashSet(); - var latestDiagnosticsFromDiagnosticService = (await _diagnosticService.GetDiagnosticsForIdsAsync(project.Solution, project.Id, documentId: null, + var latestDiagnosticsFromDiagnosticService = (await _diagnosticService.GetDiagnosticsForIdsAsync(project, documentId: null, diagnosticIds: uniqueDiagnosticIds, shouldIncludeAnalyzer: null, includeLocalDocumentDiagnostics: true, includeNonLocalDocumentDiagnostics: true, cancellationToken) .ConfigureAwait(false)); diff --git a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb index 09a8034a825f5..9476f840de109 100644 --- a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb +++ b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb @@ -316,11 +316,11 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Return SpecializedTasks.EmptyImmutableArray(Of DiagnosticData) End Function - Public Function GetDiagnosticsForIdsAsync(solution As Solution, projectId As ProjectId, documentId As DocumentId, diagnosticIds As ImmutableHashSet(Of String), shouldIncludeAnalyzer As Func(Of DiagnosticAnalyzer, Boolean), includeLocalDocumentDiagnostics As Boolean, includeNonLocalDocumentDiagnostics As Boolean, cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of DiagnosticData)) Implements IDiagnosticAnalyzerService.GetDiagnosticsForIdsAsync + Public Function GetDiagnosticsForIdsAsync(project As Project, documentId As DocumentId, diagnosticIds As ImmutableHashSet(Of String), shouldIncludeAnalyzer As Func(Of DiagnosticAnalyzer, Boolean), includeLocalDocumentDiagnostics As Boolean, includeNonLocalDocumentDiagnostics As Boolean, cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of DiagnosticData)) Implements IDiagnosticAnalyzerService.GetDiagnosticsForIdsAsync Return SpecializedTasks.EmptyImmutableArray(Of DiagnosticData)() End Function - Public Function GetProjectDiagnosticsForIdsAsync(solution As Solution, projectId As ProjectId, diagnosticIds As ImmutableHashSet(Of String), shouldIncludeAnalyzer As Func(Of DiagnosticAnalyzer, Boolean), includeNonLocalDocumentDiagnostics As Boolean, cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of DiagnosticData)) Implements IDiagnosticAnalyzerService.GetProjectDiagnosticsForIdsAsync + Public Function GetProjectDiagnosticsForIdsAsync(project As Project, diagnosticIds As ImmutableHashSet(Of String), shouldIncludeAnalyzer As Func(Of DiagnosticAnalyzer, Boolean), includeNonLocalDocumentDiagnostics As Boolean, cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of DiagnosticData)) Implements IDiagnosticAnalyzerService.GetProjectDiagnosticsForIdsAsync Return SpecializedTasks.EmptyImmutableArray(Of DiagnosticData)() End Function diff --git a/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb b/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb index 8b27dccbadb98..38be0fb67008c 100644 --- a/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb +++ b/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb @@ -236,7 +236,7 @@ class { } ' confirm that IDE doesn't report the diagnostics Dim diagnostics = Await diagnosticService.GetDiagnosticsForIdsAsync( - workspace.CurrentSolution, projectId:=Nothing, documentId:=document.Id, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, + document.Project, documentId:=document.Id, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, includeLocalDocumentDiagnostics:=True, includeNonLocalDocumentDiagnostics:=True, CancellationToken.None) Assert.False(diagnostics.Any()) End Using From 4dd46dfeb0e8afff2518c4a31811f863caa7f0bb Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 8 Feb 2025 12:45:12 -0800 Subject: [PATCH 183/305] do not pass state along --- .../DiagnosticIncrementalAnalyzer_GetDiagnostics.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index 2c2524dc8f626..4dd83e658d0ce 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -52,7 +52,6 @@ public async Task> GetDiagnosticsAsync(Cancellati // return diagnostics specific to one project or document var includeProjectNonLocalResult = DocumentId == null; return await ProduceProjectDiagnosticsAsync( - this.Project, // Ensure we compute and return diagnostics for both the normal docs and the additional docs in this // project if no specific document id was requested. this.DocumentId != null ? [this.DocumentId] : [.. this.Project.DocumentIds, .. this.Project.AdditionalDocumentIds], @@ -60,12 +59,12 @@ public async Task> GetDiagnosticsAsync(Cancellati } private async Task> ProduceProjectDiagnosticsAsync( - Project project, IReadOnlyList documentIds, + IReadOnlyList documentIds, bool includeProjectNonLocalResult, CancellationToken cancellationToken) { using var _ = ArrayBuilder.GetInstance(out var builder); await this.ProduceDiagnosticsAsync( - project, documentIds, includeProjectNonLocalResult, builder, cancellationToken).ConfigureAwait(false); + documentIds, includeProjectNonLocalResult, builder, cancellationToken).ConfigureAwait(false); return builder.ToImmutableAndClear(); } @@ -81,16 +80,16 @@ private void AddIncludedDiagnostics(ArrayBuilder builder, Immuta public async Task> GetProjectDiagnosticsAsync(CancellationToken cancellationToken) { return await ProduceProjectDiagnosticsAsync( - this.Project, documentIds: [], includeProjectNonLocalResult: true, cancellationToken).ConfigureAwait(false); + documentIds: [], includeProjectNonLocalResult: true, cancellationToken).ConfigureAwait(false); } private async Task ProduceDiagnosticsAsync( - Project project, IReadOnlyList documentIds, bool includeProjectNonLocalResult, ArrayBuilder builder, CancellationToken cancellationToken) { + var project = this.Project; var analyzersForProject = await StateManager.GetOrCreateAnalyzersAsync(project, cancellationToken).ConfigureAwait(false); var hostAnalyzerInfo = await StateManager.GetOrCreateHostAnalyzerInfoAsync(project, cancellationToken).ConfigureAwait(false); var analyzers = analyzersForProject.WhereAsArray(a => ShouldIncludeAnalyzer(project, a)); From a3e4c64456fa5e495d14df5b3fce548499009bdb Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 8 Feb 2025 17:46:13 -0800 Subject: [PATCH 184/305] Single filter --- ...cIncrementalAnalyzer.CompilationManager.cs | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs index f04fa0a778fa9..a2df12a5c6c8e 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs @@ -82,19 +82,33 @@ internal partial class DiagnosticAnalyzerService Contract.ThrowIfFalse(project.SupportsCompilation); AssertCompilation(project, compilation); + var exceptionFilter = (Exception ex) => + { + if (ex is not OperationCanceledException && crashOnAnalyzerException) + { + // report telemetry + FatalError.ReportAndPropagate(ex); + + // force fail fast (the host might not crash when reporting telemetry): + FailFast.OnFatalException(ex); + } + + return true; + }; + // in IDE, we always set concurrentAnalysis == false otherwise, we can get into thread starvation due to // async being used with synchronous blocking concurrency. var projectAnalyzerOptions = new CompilationWithAnalyzersOptions( options: project.AnalyzerOptions, onAnalyzerException: null, - analyzerExceptionFilter: GetAnalyzerExceptionFilter(), + analyzerExceptionFilter: exceptionFilter, concurrentAnalysis: false, logAnalyzerExecutionTime: true, reportSuppressedDiagnostics: true); var hostAnalyzerOptions = new CompilationWithAnalyzersOptions( options: project.HostAnalyzerOptions, onAnalyzerException: null, - analyzerExceptionFilter: GetAnalyzerExceptionFilter(), + analyzerExceptionFilter: exceptionFilter, concurrentAnalysis: false, logAnalyzerExecutionTime: true, reportSuppressedDiagnostics: true); @@ -103,23 +117,6 @@ internal partial class DiagnosticAnalyzerService return new CompilationWithAnalyzersPair( filteredProjectAnalyzers.Any() ? compilation.WithAnalyzers(filteredProjectAnalyzers, projectAnalyzerOptions) : null, filteredHostAnalyzers.Any() ? compilation.WithAnalyzers(filteredHostAnalyzers, hostAnalyzerOptions) : null); - - Func GetAnalyzerExceptionFilter() - { - return ex => - { - if (ex is not OperationCanceledException && crashOnAnalyzerException) - { - // report telemetry - FatalError.ReportAndPropagate(ex); - - // force fail fast (the host might not crash when reporting telemetry): - FailFast.OnFatalException(ex); - } - - return true; - }; - } } } From 3c5c6868871b2841ce5bc51eda34d1f6ba4e7881 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 8 Feb 2025 17:49:19 -0800 Subject: [PATCH 185/305] Avoid making options --- ...cIncrementalAnalyzer.CompilationManager.cs | 37 ++++++++++--------- .../CompilationWithAnalyzersPair.cs | 2 - 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs index a2df12a5c6c8e..4fe64058fad9f 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs @@ -98,25 +98,28 @@ internal partial class DiagnosticAnalyzerService // in IDE, we always set concurrentAnalysis == false otherwise, we can get into thread starvation due to // async being used with synchronous blocking concurrency. - var projectAnalyzerOptions = new CompilationWithAnalyzersOptions( - options: project.AnalyzerOptions, - onAnalyzerException: null, - analyzerExceptionFilter: exceptionFilter, - concurrentAnalysis: false, - logAnalyzerExecutionTime: true, - reportSuppressedDiagnostics: true); - var hostAnalyzerOptions = new CompilationWithAnalyzersOptions( - options: project.HostAnalyzerOptions, - onAnalyzerException: null, - analyzerExceptionFilter: exceptionFilter, - concurrentAnalysis: false, - logAnalyzerExecutionTime: true, - reportSuppressedDiagnostics: true); + var projectCompilation = !filteredProjectAnalyzers.Any() + ? null + : compilation.WithAnalyzers(filteredProjectAnalyzers, new CompilationWithAnalyzersOptions( + options: project.AnalyzerOptions, + onAnalyzerException: null, + analyzerExceptionFilter: exceptionFilter, + concurrentAnalysis: false, + logAnalyzerExecutionTime: true, + reportSuppressedDiagnostics: true)); + + var hostCompilation = !filteredHostAnalyzers.Any() + ? null + : compilation.WithAnalyzers(filteredHostAnalyzers, new CompilationWithAnalyzersOptions( + options: project.HostAnalyzerOptions, + onAnalyzerException: null, + analyzerExceptionFilter: exceptionFilter, + concurrentAnalysis: false, + logAnalyzerExecutionTime: true, + reportSuppressedDiagnostics: true)); // Create driver that holds onto compilation and associated analyzers - return new CompilationWithAnalyzersPair( - filteredProjectAnalyzers.Any() ? compilation.WithAnalyzers(filteredProjectAnalyzers, projectAnalyzerOptions) : null, - filteredHostAnalyzers.Any() ? compilation.WithAnalyzers(filteredHostAnalyzers, hostAnalyzerOptions) : null); + return new CompilationWithAnalyzersPair(projectCompilation, hostCompilation); } } diff --git a/src/Workspaces/Core/Portable/Diagnostics/CompilationWithAnalyzersPair.cs b/src/Workspaces/Core/Portable/Diagnostics/CompilationWithAnalyzersPair.cs index e9b7f7f73746e..5e308ed7521be 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/CompilationWithAnalyzersPair.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/CompilationWithAnalyzersPair.cs @@ -2,10 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Immutable; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; From 809edddf196024891a4d146f26158df897dc89c5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 9 Feb 2025 11:20:08 -0800 Subject: [PATCH 186/305] Remove inner class --- ...crementalAnalyzer_GetDiagnosticsForSpan.cs | 400 ++++++++---------- 1 file changed, 166 insertions(+), 234 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs index b2ecd328c8678..f25becd322177 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs @@ -3,11 +3,9 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; -using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; @@ -30,107 +28,40 @@ public async Task> GetDiagnosticsForSpanAsync( TextSpan? range, Func? shouldIncludeDiagnostic, ICodeActionRequestPriorityProvider priorityProvider, - DiagnosticKind diagnosticKinds, + DiagnosticKind diagnosticKind, bool isExplicit, CancellationToken cancellationToken) { - using var _ = ArrayBuilder.GetInstance(out var list); + var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); + + var unfilteredAnalyzers = await _stateManager + .GetOrCreateAnalyzersAsync(document.Project, cancellationToken) + .ConfigureAwait(false); + var analyzers = unfilteredAnalyzers + .WhereAsArray(a => DocumentAnalysisExecutor.IsAnalyzerEnabledForProject(a, document.Project, GlobalOptions)); + var hostAnalyzerInfo = await _stateManager.GetOrCreateHostAnalyzerInfoAsync(document.Project, cancellationToken).ConfigureAwait(false); + + // Note that some callers, such as diagnostic tagger, might pass in a range equal to the entire document span. + // We clear out range for such cases as we are computing full document diagnostics. + if (range == new TextSpan(0, text.Length)) + range = null; + + // We log performance info when we are computing diagnostics for a span + var logPerformanceInfo = range.HasValue; + var compilationWithAnalyzers = await GetOrCreateCompilationWithAnalyzersAsync( + document.Project, analyzers, hostAnalyzerInfo, AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); + + // If we are computing full document diagnostics, we will attempt to perform incremental + // member edit analysis. This analysis is currently only enabled with LSP pull diagnostics. + var incrementalAnalysis = !range.HasValue + && document is Document { SupportsSyntaxTree: true }; - var getter = await LatestDiagnosticsForSpanGetter.CreateAsync( - this, document, range, priorityProvider, shouldIncludeDiagnostic, diagnosticKinds, isExplicit, cancellationToken).ConfigureAwait(false); - await getter.GetAsync(list, cancellationToken).ConfigureAwait(false); + using var _ = ArrayBuilder.GetInstance(out var list); + await GetAsync(list).ConfigureAwait(false); return list.ToImmutableAndClear(); - } - /// - /// Get diagnostics for given span either by using cache or calculating it on the spot. - /// - private sealed class LatestDiagnosticsForSpanGetter - { - private readonly DiagnosticIncrementalAnalyzer _owner; - private readonly TextDocument _document; - private readonly SourceText _text; - - private readonly ImmutableArray _analyzers; - private readonly CompilationWithAnalyzersPair? _compilationWithAnalyzers; - - private readonly TextSpan? _range; - private readonly ICodeActionRequestPriorityProvider _priorityProvider; - private readonly Func? _shouldIncludeDiagnostic; - private readonly bool _isExplicit; - private readonly bool _logPerformanceInfo; - private readonly bool _incrementalAnalysis; - private readonly DiagnosticKind _diagnosticKind; - - public static async Task CreateAsync( - DiagnosticIncrementalAnalyzer owner, - TextDocument document, - TextSpan? range, - ICodeActionRequestPriorityProvider priorityProvider, - Func? shouldIncludeDiagnostic, - DiagnosticKind diagnosticKinds, - bool isExplicit, - CancellationToken cancellationToken) - { - var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); - - var unfilteredAnalyzers = await owner._stateManager - .GetOrCreateAnalyzersAsync(document.Project, cancellationToken) - .ConfigureAwait(false); - var analyzers = unfilteredAnalyzers - .WhereAsArray(a => DocumentAnalysisExecutor.IsAnalyzerEnabledForProject(a, document.Project, owner.GlobalOptions)); - var hostAnalyzerInfo = await owner._stateManager.GetOrCreateHostAnalyzerInfoAsync(document.Project, cancellationToken).ConfigureAwait(false); - - // Note that some callers, such as diagnostic tagger, might pass in a range equal to the entire document span. - // We clear out range for such cases as we are computing full document diagnostics. - if (range == new TextSpan(0, text.Length)) - range = null; - - // We log performance info when we are computing diagnostics for a span - var logPerformanceInfo = range.HasValue; - var compilationWithAnalyzers = await GetOrCreateCompilationWithAnalyzersAsync( - document.Project, analyzers, hostAnalyzerInfo, owner.AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); - - // If we are computing full document diagnostics, we will attempt to perform incremental - // member edit analysis. This analysis is currently only enabled with LSP pull diagnostics. - var incrementalAnalysis = !range.HasValue - && document is Document { SupportsSyntaxTree: true }; - - return new LatestDiagnosticsForSpanGetter( - owner, compilationWithAnalyzers, document, text, analyzers, shouldIncludeDiagnostic, - range, priorityProvider, isExplicit, logPerformanceInfo, incrementalAnalysis, diagnosticKinds); - } - - private LatestDiagnosticsForSpanGetter( - DiagnosticIncrementalAnalyzer owner, - CompilationWithAnalyzersPair? compilationWithAnalyzers, - TextDocument document, - SourceText text, - ImmutableArray analyzers, - Func? shouldIncludeDiagnostic, - TextSpan? range, - ICodeActionRequestPriorityProvider priorityProvider, - bool isExplicit, - bool logPerformanceInfo, - bool incrementalAnalysis, - DiagnosticKind diagnosticKind) - { - _owner = owner; - _compilationWithAnalyzers = compilationWithAnalyzers; - _document = document; - _text = text; - _analyzers = analyzers; - _shouldIncludeDiagnostic = shouldIncludeDiagnostic; - _range = range; - _priorityProvider = priorityProvider; - _isExplicit = isExplicit; - _logPerformanceInfo = logPerformanceInfo; - _incrementalAnalysis = incrementalAnalysis; - _diagnosticKind = diagnosticKind; - } - - public async Task GetAsync(ArrayBuilder list, CancellationToken cancellationToken) + async Task GetAsync(ArrayBuilder list) { try { @@ -144,27 +75,27 @@ public async Task GetAsync(ArrayBuilder list, CancellationToken using var _2 = ArrayBuilder.GetInstance(out var semanticSpanBasedAnalyzers); using var _3 = ArrayBuilder.GetInstance(out var semanticDocumentBasedAnalyzers); - using var _4 = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.RequestDiagnostics_Summary, $"Pri{_priorityProvider.Priority.GetPriorityInt()}"); + using var _4 = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.RequestDiagnostics_Summary, $"Pri{priorityProvider.Priority.GetPriorityInt()}"); - foreach (var analyzer in _analyzers) + foreach (var analyzer in analyzers) { - if (!ShouldIncludeAnalyzer(analyzer, _shouldIncludeDiagnostic, _priorityProvider, _owner)) + if (!ShouldIncludeAnalyzer(analyzer, shouldIncludeDiagnostic, priorityProvider, this)) continue; bool includeSyntax = true, includeSemantic = true; - if (_diagnosticKind != DiagnosticKind.All) + if (diagnosticKind != DiagnosticKind.All) { var isCompilerAnalyzer = analyzer.IsCompilerAnalyzer(); includeSyntax = isCompilerAnalyzer - ? _diagnosticKind == DiagnosticKind.CompilerSyntax - : _diagnosticKind == DiagnosticKind.AnalyzerSyntax; + ? diagnosticKind == DiagnosticKind.CompilerSyntax + : diagnosticKind == DiagnosticKind.AnalyzerSyntax; includeSemantic = isCompilerAnalyzer - ? _diagnosticKind == DiagnosticKind.CompilerSemantic - : _diagnosticKind == DiagnosticKind.AnalyzerSemantic; + ? diagnosticKind == DiagnosticKind.CompilerSemantic + : diagnosticKind == DiagnosticKind.AnalyzerSemantic; } includeSyntax = includeSyntax && analyzer.SupportAnalysisKind(AnalysisKind.Syntax); - includeSemantic = includeSemantic && analyzer.SupportAnalysisKind(AnalysisKind.Semantic) && _document is Document; + includeSemantic = includeSemantic && analyzer.SupportAnalysisKind(AnalysisKind.Semantic) && document is Document; if (includeSyntax || includeSemantic) { @@ -176,7 +107,7 @@ public async Task GetAsync(ArrayBuilder list, CancellationToken if (includeSemantic) { var selectedAnalyzers = GetSemanticAnalysisSelectedAnalyzers( - analyzer, _incrementalAnalysis, + analyzer, incrementalAnalysis, semanticSpanBasedAnalyzers, semanticDocumentBasedAnalyzers); selectedAnalyzers.Add(analyzer); @@ -184,8 +115,8 @@ public async Task GetAsync(ArrayBuilder list, CancellationToken } } - await ComputeDocumentDiagnosticsAsync(syntaxAnalyzers.ToImmutable(), AnalysisKind.Syntax, _range, list, incrementalAnalysis: false, cancellationToken).ConfigureAwait(false); - await ComputeDocumentDiagnosticsAsync(semanticSpanBasedAnalyzers.ToImmutable(), AnalysisKind.Semantic, _range, list, _incrementalAnalysis, cancellationToken).ConfigureAwait(false); + await ComputeDocumentDiagnosticsAsync(syntaxAnalyzers.ToImmutable(), AnalysisKind.Syntax, range, list, incrementalAnalysis: false, cancellationToken).ConfigureAwait(false); + await ComputeDocumentDiagnosticsAsync(semanticSpanBasedAnalyzers.ToImmutable(), AnalysisKind.Semantic, range, list, incrementalAnalysis, cancellationToken).ConfigureAwait(false); await ComputeDocumentDiagnosticsAsync(semanticDocumentBasedAnalyzers.ToImmutable(), AnalysisKind.Semantic, span: null, list, incrementalAnalysis: false, cancellationToken).ConfigureAwait(false); return; @@ -194,65 +125,65 @@ public async Task GetAsync(ArrayBuilder list, CancellationToken { throw ExceptionUtilities.Unreachable(); } + } - // Local functions - static bool ShouldIncludeAnalyzer( - DiagnosticAnalyzer analyzer, - Func? shouldIncludeDiagnostic, - ICodeActionRequestPriorityProvider priorityProvider, - DiagnosticIncrementalAnalyzer owner) - { - // Skip executing analyzer if its priority does not match the request priority. - if (!priorityProvider.MatchesPriority(analyzer)) - return false; - - // Special case DocumentDiagnosticAnalyzer to never skip these document analyzers - // based on 'shouldIncludeDiagnostic' predicate. More specifically, TS has special document - // analyzer which report 0 supported diagnostics, but we always want to execute it. - if (analyzer is DocumentDiagnosticAnalyzer) - return true; - - // Special case GeneratorDiagnosticsPlaceholderAnalyzer to never skip it based on - // 'shouldIncludeDiagnostic' predicate. More specifically, this is a placeholder analyzer - // for threading through all source generator reported diagnostics, but this special analyzer - // reports 0 supported diagnostics, and we always want to execute it. - if (analyzer is GeneratorDiagnosticsPlaceholderAnalyzer) - return true; - - // Skip analyzer if none of its reported diagnostics should be included. - if (shouldIncludeDiagnostic != null && - !owner.DiagnosticAnalyzerInfoCache.GetDiagnosticDescriptors(analyzer).Any(static (a, shouldIncludeDiagnostic) => shouldIncludeDiagnostic(a.Id), shouldIncludeDiagnostic)) - { - return false; - } + // Local functions + static bool ShouldIncludeAnalyzer( + DiagnosticAnalyzer analyzer, + Func? shouldIncludeDiagnostic, + ICodeActionRequestPriorityProvider priorityProvider, + DiagnosticIncrementalAnalyzer owner) + { + // Skip executing analyzer if its priority does not match the request priority. + if (!priorityProvider.MatchesPriority(analyzer)) + return false; + + // Special case DocumentDiagnosticAnalyzer to never skip these document analyzers + // based on 'shouldIncludeDiagnostic' predicate. More specifically, TS has special document + // analyzer which report 0 supported diagnostics, but we always want to execute it. + if (analyzer is DocumentDiagnosticAnalyzer) + return true; + // Special case GeneratorDiagnosticsPlaceholderAnalyzer to never skip it based on + // 'shouldIncludeDiagnostic' predicate. More specifically, this is a placeholder analyzer + // for threading through all source generator reported diagnostics, but this special analyzer + // reports 0 supported diagnostics, and we always want to execute it. + if (analyzer is GeneratorDiagnosticsPlaceholderAnalyzer) return true; + + // Skip analyzer if none of its reported diagnostics should be included. + if (shouldIncludeDiagnostic != null && + !owner.DiagnosticAnalyzerInfoCache.GetDiagnosticDescriptors(analyzer).Any(static (a, shouldIncludeDiagnostic) => shouldIncludeDiagnostic(a.Id), shouldIncludeDiagnostic)) + { + return false; } - static ArrayBuilder GetSemanticAnalysisSelectedAnalyzers( - DiagnosticAnalyzer analyzer, - bool incrementalAnalysis, - ArrayBuilder semanticSpanBasedAnalyzers, - ArrayBuilder semanticDocumentBasedAnalyzers) + return true; + } + + static ArrayBuilder GetSemanticAnalysisSelectedAnalyzers( + DiagnosticAnalyzer analyzer, + bool incrementalAnalysis, + ArrayBuilder semanticSpanBasedAnalyzers, + ArrayBuilder semanticDocumentBasedAnalyzers) + { + if (!incrementalAnalysis) { - if (!incrementalAnalysis) - { - // For non-incremental analysis, we always attempt to compute all - // analyzer diagnostics for the requested span. - return semanticSpanBasedAnalyzers; - } - else - { - // We can perform incremental analysis only for analyzers that support - // span-based semantic diagnostic analysis. - return analyzer.SupportsSpanBasedSemanticDiagnosticAnalysis() - ? semanticSpanBasedAnalyzers - : semanticDocumentBasedAnalyzers; - } + // For non-incremental analysis, we always attempt to compute all + // analyzer diagnostics for the requested span. + return semanticSpanBasedAnalyzers; + } + else + { + // We can perform incremental analysis only for analyzers that support + // span-based semantic diagnostic analysis. + return analyzer.SupportsSpanBasedSemanticDiagnosticAnalysis() + ? semanticSpanBasedAnalyzers + : semanticDocumentBasedAnalyzers; } } - private async Task ComputeDocumentDiagnosticsAsync( + async Task ComputeDocumentDiagnosticsAsync( ImmutableArray analyzers, AnalysisKind kind, TextSpan? span, @@ -266,12 +197,12 @@ private async Task ComputeDocumentDiagnosticsAsync( using var _ = ArrayBuilder.GetInstance(analyzers.Length, out var filteredAnalyzers); foreach (var analyzer in analyzers) { - Debug.Assert(_priorityProvider.MatchesPriority(analyzer)); + Debug.Assert(priorityProvider.MatchesPriority(analyzer)); // Check if this is an expensive analyzer that needs to be de-prioritized to a lower priority bucket. // If so, we skip this analyzer from execution in the current priority bucket. // We will subsequently execute this analyzer in the lower priority bucket. - if (await TryDeprioritizeAnalyzerAsync(analyzer).ConfigureAwait(false)) + if (await TryDeprioritizeAnalyzerAsync(analyzer, kind, span).ConfigureAwait(false)) { continue; } @@ -284,20 +215,20 @@ private async Task ComputeDocumentDiagnosticsAsync( analyzers = filteredAnalyzers.ToImmutable(); - var hostAnalyzerInfo = await _owner._stateManager.GetOrCreateHostAnalyzerInfoAsync(_document.Project, cancellationToken).ConfigureAwait(false); + var hostAnalyzerInfo = await _stateManager.GetOrCreateHostAnalyzerInfoAsync(document.Project, cancellationToken).ConfigureAwait(false); var projectAnalyzers = analyzers.WhereAsArray(static (a, info) => !info.IsHostAnalyzer(a), hostAnalyzerInfo); var hostAnalyzers = analyzers.WhereAsArray(static (a, info) => info.IsHostAnalyzer(a), hostAnalyzerInfo); - var analysisScope = new DocumentAnalysisScope(_document, span, projectAnalyzers, hostAnalyzers, kind); - var executor = new DocumentAnalysisExecutor(analysisScope, _compilationWithAnalyzers, _owner._diagnosticAnalyzerRunner, _isExplicit, _logPerformanceInfo); - var version = await GetDiagnosticVersionAsync(_document.Project, cancellationToken).ConfigureAwait(false); + var analysisScope = new DocumentAnalysisScope(document, span, projectAnalyzers, hostAnalyzers, kind); + var executor = new DocumentAnalysisExecutor(analysisScope, compilationWithAnalyzers, _diagnosticAnalyzerRunner, isExplicit, logPerformanceInfo); + var version = await GetDiagnosticVersionAsync(document.Project, cancellationToken).ConfigureAwait(false); ImmutableDictionary> diagnosticsMap; if (incrementalAnalysis) { - using var _2 = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.RequestDiagnostics_Summary, $"Pri{_priorityProvider.Priority.GetPriorityInt()}.Incremental"); + using var _2 = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.RequestDiagnostics_Summary, $"Pri{priorityProvider.Priority.GetPriorityInt()}.Incremental"); - diagnosticsMap = await _owner._incrementalMemberEditAnalyzer.ComputeDiagnosticsAsync( + diagnosticsMap = await _incrementalMemberEditAnalyzer.ComputeDiagnosticsAsync( executor, analyzers, version, @@ -307,7 +238,7 @@ private async Task ComputeDocumentDiagnosticsAsync( } else { - using var _2 = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.RequestDiagnostics_Summary, $"Pri{_priorityProvider.Priority.GetPriorityInt()}.Document"); + using var _2 = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.RequestDiagnostics_Summary, $"Pri{priorityProvider.Priority.GetPriorityInt()}.Document"); diagnosticsMap = await ComputeDocumentDiagnosticsCoreAsync(executor, cancellationToken).ConfigureAwait(false); } @@ -319,77 +250,78 @@ private async Task ComputeDocumentDiagnosticsAsync( } if (incrementalAnalysis) - _owner._incrementalMemberEditAnalyzer.UpdateDocumentWithCachedDiagnostics((Document)_document); + _incrementalMemberEditAnalyzer.UpdateDocumentWithCachedDiagnostics((Document)document); + } - async Task TryDeprioritizeAnalyzerAsync(DiagnosticAnalyzer analyzer) + async Task TryDeprioritizeAnalyzerAsync( + DiagnosticAnalyzer analyzer, AnalysisKind kind, TextSpan? span) + { + // PERF: In order to improve lightbulb performance, we perform de-prioritization optimization for certain analyzers + // that moves the analyzer to a lower priority bucket. However, to ensure that de-prioritization happens for very rare cases, + // we only perform this optimizations when following conditions are met: + // 1. We are performing semantic span-based analysis. + // 2. We are processing 'CodeActionRequestPriority.Normal' priority request. + // 3. Analyzer registers certain actions that are known to lead to high performance impact due to its broad analysis scope, + // such as SymbolStart/End actions and SemanticModel actions. + // 4. Analyzer did not report a diagnostic on the same line in prior document snapshot. + + // Conditions 1. and 2. + if (kind != AnalysisKind.Semantic || + !span.HasValue || + priorityProvider.Priority != CodeActionRequestPriority.Default) { - // PERF: In order to improve lightbulb performance, we perform de-prioritization optimization for certain analyzers - // that moves the analyzer to a lower priority bucket. However, to ensure that de-prioritization happens for very rare cases, - // we only perform this optimizations when following conditions are met: - // 1. We are performing semantic span-based analysis. - // 2. We are processing 'CodeActionRequestPriority.Normal' priority request. - // 3. Analyzer registers certain actions that are known to lead to high performance impact due to its broad analysis scope, - // such as SymbolStart/End actions and SemanticModel actions. - // 4. Analyzer did not report a diagnostic on the same line in prior document snapshot. - - // Conditions 1. and 2. - if (kind != AnalysisKind.Semantic || - !span.HasValue || - _priorityProvider.Priority != CodeActionRequestPriority.Default) - { - return false; - } + return false; + } - Debug.Assert(span.Value.Length < _text.Length); + Debug.Assert(span.Value.Length < text.Length); - // Condition 3. - // Check if this is a candidate analyzer that can be de-prioritized into a lower priority bucket based on registered actions. - if (!await IsCandidateForDeprioritizationBasedOnRegisteredActionsAsync(analyzer).ConfigureAwait(false)) - { - return false; - } + // Condition 3. + // Check if this is a candidate analyzer that can be de-prioritized into a lower priority bucket based on registered actions. + if (!await IsCandidateForDeprioritizationBasedOnRegisteredActionsAsync(analyzer).ConfigureAwait(false)) + { + return false; + } - // 'LightbulbSkipExecutingDeprioritizedAnalyzers' option determines if we want to execute this analyzer - // in low priority bucket or skip it completely. If the option is not set, track the de-prioritized - // analyzer to be executed in low priority bucket. - // Note that 'AddDeprioritizedAnalyzerWithLowPriority' call below mutates the state in the provider to - // track this analyzer. This ensures that when the owner of this provider calls us back to execute - // the low priority bucket, we can still get back to this analyzer and execute it that time. - if (!_owner.GlobalOptions.GetOption(DiagnosticOptionsStorage.LightbulbSkipExecutingDeprioritizedAnalyzers)) - _priorityProvider.AddDeprioritizedAnalyzerWithLowPriority(analyzer); + // 'LightbulbSkipExecutingDeprioritizedAnalyzers' option determines if we want to execute this analyzer + // in low priority bucket or skip it completely. If the option is not set, track the de-prioritized + // analyzer to be executed in low priority bucket. + // Note that 'AddDeprioritizedAnalyzerWithLowPriority' call below mutates the state in the provider to + // track this analyzer. This ensures that when the owner of this provider calls us back to execute + // the low priority bucket, we can still get back to this analyzer and execute it that time. + if (!this.GlobalOptions.GetOption(DiagnosticOptionsStorage.LightbulbSkipExecutingDeprioritizedAnalyzers)) + priorityProvider.AddDeprioritizedAnalyzerWithLowPriority(analyzer); - return true; - } + return true; + } - // Returns true if this is an analyzer that is a candidate to be de-prioritized to - // 'CodeActionRequestPriority.Low' priority for improvement in analyzer - // execution performance for priority buckets above 'Low' priority. - // Based on performance measurements, currently only analyzers which register SymbolStart/End actions - // or SemanticModel actions are considered candidates to be de-prioritized. However, these semantics - // could be changed in future based on performance measurements. - async Task IsCandidateForDeprioritizationBasedOnRegisteredActionsAsync(DiagnosticAnalyzer analyzer) + // Returns true if this is an analyzer that is a candidate to be de-prioritized to + // 'CodeActionRequestPriority.Low' priority for improvement in analyzer + // execution performance for priority buckets above 'Low' priority. + // Based on performance measurements, currently only analyzers which register SymbolStart/End actions + // or SemanticModel actions are considered candidates to be de-prioritized. However, these semantics + // could be changed in future based on performance measurements. + async Task IsCandidateForDeprioritizationBasedOnRegisteredActionsAsync(DiagnosticAnalyzer analyzer) + { + // We deprioritize SymbolStart/End and SemanticModel analyzers from 'Normal' to 'Low' priority bucket, + // as these are computationally more expensive. + // Note that we never de-prioritize compiler analyzer, even though it registers a SemanticModel action. + if (compilationWithAnalyzers == null || + analyzer.IsWorkspaceDiagnosticAnalyzer() || + analyzer.IsCompilerAnalyzer()) { - // We deprioritize SymbolStart/End and SemanticModel analyzers from 'Normal' to 'Low' priority bucket, - // as these are computationally more expensive. - // Note that we never de-prioritize compiler analyzer, even though it registers a SemanticModel action. - if (_compilationWithAnalyzers == null || - analyzer.IsWorkspaceDiagnosticAnalyzer() || - analyzer.IsCompilerAnalyzer()) - { - return false; - } + return false; + } - var telemetryInfo = await _compilationWithAnalyzers.GetAnalyzerTelemetryInfoAsync(analyzer, cancellationToken).ConfigureAwait(false); - if (telemetryInfo == null) - return false; + var telemetryInfo = await compilationWithAnalyzers.GetAnalyzerTelemetryInfoAsync(analyzer, cancellationToken).ConfigureAwait(false); + if (telemetryInfo == null) + return false; - return telemetryInfo.SymbolStartActionsCount > 0 || telemetryInfo.SemanticModelActionsCount > 0; - } + return telemetryInfo.SymbolStartActionsCount > 0 || telemetryInfo.SemanticModelActionsCount > 0; } - private async Task>> ComputeDocumentDiagnosticsCoreAsync( - DocumentAnalysisExecutor executor, - CancellationToken cancellationToken) + static async Task>> ComputeDocumentDiagnosticsCoreAsync( + DocumentAnalysisExecutor executor, + CancellationToken cancellationToken) { using var _ = PooledDictionary>.GetInstance(out var builder); foreach (var analyzer in executor.AnalysisScope.ProjectAnalyzers.ConcatFast(executor.AnalysisScope.HostAnalyzers)) @@ -401,10 +333,10 @@ private async Task> ComputeDocumentDiagnosticsForAnalyzerCoreAsync( - DiagnosticAnalyzer analyzer, - DocumentAnalysisExecutor executor, - CancellationToken cancellationToken) + static async Task> ComputeDocumentDiagnosticsForAnalyzerCoreAsync( + DiagnosticAnalyzer analyzer, + DocumentAnalysisExecutor executor, + CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -412,11 +344,11 @@ private async Task> ComputeDocumentDiagnosticsFor return diagnostics?.ToImmutableArrayOrEmpty() ?? []; } - private bool ShouldInclude(DiagnosticData diagnostic) + bool ShouldInclude(DiagnosticData diagnostic) { - return diagnostic.DocumentId == _document.Id && - (_range == null || _range.Value.IntersectsWith(diagnostic.DataLocation.UnmappedFileSpan.GetClampedTextSpan(_text))) - && (_shouldIncludeDiagnostic == null || _shouldIncludeDiagnostic(diagnostic.Id)); + return diagnostic.DocumentId == document.Id && + (range == null || range.Value.IntersectsWith(diagnostic.DataLocation.UnmappedFileSpan.GetClampedTextSpan(text))) + && (shouldIncludeDiagnostic == null || shouldIncludeDiagnostic(diagnostic.Id)); } } } From 126b5a9eb2a5fd41faae1d868cf4f324815e0274 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 9 Feb 2025 11:23:58 -0800 Subject: [PATCH 187/305] Name --- ...osticIncrementalAnalyzer_GetDiagnostics.cs | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index 4dd83e658d0ce..9224dcb532969 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -35,26 +35,26 @@ private sealed class DiagnosticGetter( { private readonly DiagnosticIncrementalAnalyzer Owner = owner; - private readonly Project Project = project; - private readonly DocumentId? DocumentId = documentId; - private readonly ImmutableHashSet? _diagnosticIds = diagnosticIds; - private readonly Func? _shouldIncludeAnalyzer = shouldIncludeAnalyzer; - private readonly bool IncludeLocalDocumentDiagnostics = includeLocalDocumentDiagnostics; - private readonly bool IncludeNonLocalDocumentDiagnostics = includeNonLocalDocumentDiagnostics; + private readonly Project project = project; + private readonly DocumentId? documentId = documentId; + private readonly ImmutableHashSet? diagnosticIds = diagnosticIds; + private readonly Func? shouldIncludeAnalyzer = shouldIncludeAnalyzer; + private readonly bool includeLocalDocumentDiagnostics = includeLocalDocumentDiagnostics; + private readonly bool includeNonLocalDocumentDiagnostics = includeNonLocalDocumentDiagnostics; - private StateManager StateManager => Owner._stateManager; + private StateManager _stateManager => Owner._stateManager; private bool ShouldIncludeDiagnostic(DiagnosticData diagnostic) - => _diagnosticIds == null || _diagnosticIds.Contains(diagnostic.Id); + => diagnosticIds == null || diagnosticIds.Contains(diagnostic.Id); public async Task> GetDiagnosticsAsync(CancellationToken cancellationToken) { // return diagnostics specific to one project or document - var includeProjectNonLocalResult = DocumentId == null; + var includeProjectNonLocalResult = documentId == null; return await ProduceProjectDiagnosticsAsync( // Ensure we compute and return diagnostics for both the normal docs and the additional docs in this // project if no specific document id was requested. - this.DocumentId != null ? [this.DocumentId] : [.. this.Project.DocumentIds, .. this.Project.AdditionalDocumentIds], + this.documentId != null ? [this.documentId] : [.. this.project.DocumentIds, .. this.project.AdditionalDocumentIds], includeProjectNonLocalResult, cancellationToken).ConfigureAwait(false); } @@ -89,9 +89,9 @@ private async Task ProduceDiagnosticsAsync( ArrayBuilder builder, CancellationToken cancellationToken) { - var project = this.Project; - var analyzersForProject = await StateManager.GetOrCreateAnalyzersAsync(project, cancellationToken).ConfigureAwait(false); - var hostAnalyzerInfo = await StateManager.GetOrCreateHostAnalyzerInfoAsync(project, cancellationToken).ConfigureAwait(false); + var project = this.project; + var analyzersForProject = await _stateManager.GetOrCreateAnalyzersAsync(project, cancellationToken).ConfigureAwait(false); + var hostAnalyzerInfo = await _stateManager.GetOrCreateHostAnalyzerInfoAsync(project, cancellationToken).ConfigureAwait(false); var analyzers = analyzersForProject.WhereAsArray(a => ShouldIncludeAnalyzer(project, a)); var result = await GetOrComputeDiagnosticAnalysisResultsAsync(analyzers).ConfigureAwait(false); @@ -103,13 +103,13 @@ private async Task ProduceDiagnosticsAsync( foreach (var documentId in documentIds) { - if (IncludeLocalDocumentDiagnostics) + if (includeLocalDocumentDiagnostics) { AddIncludedDiagnostics(builder, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.Syntax)); AddIncludedDiagnostics(builder, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.Semantic)); } - if (IncludeNonLocalDocumentDiagnostics) + if (includeNonLocalDocumentDiagnostics) AddIncludedDiagnostics(builder, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.NonLocal)); } @@ -146,26 +146,26 @@ async Task> Ge var result = await Owner.ComputeDiagnosticAnalysisResultsAsync(compilation, project, analyzers, cancellationToken).ConfigureAwait(false); return result; } - } - private bool ShouldIncludeAnalyzer(Project project, DiagnosticAnalyzer analyzer) - { - if (!DocumentAnalysisExecutor.IsAnalyzerEnabledForProject(analyzer, project, Owner.GlobalOptions)) + bool ShouldIncludeAnalyzer(Project project, DiagnosticAnalyzer analyzer) { - return false; - } + if (!DocumentAnalysisExecutor.IsAnalyzerEnabledForProject(analyzer, project, Owner.GlobalOptions)) + { + return false; + } - if (_shouldIncludeAnalyzer != null && !_shouldIncludeAnalyzer(analyzer)) - { - return false; - } + if (shouldIncludeAnalyzer != null && !shouldIncludeAnalyzer(analyzer)) + { + return false; + } - if (_diagnosticIds != null && Owner.DiagnosticAnalyzerInfoCache.GetDiagnosticDescriptors(analyzer).All(d => !_diagnosticIds.Contains(d.Id))) - { - return false; - } + if (diagnosticIds != null && Owner.DiagnosticAnalyzerInfoCache.GetDiagnosticDescriptors(analyzer).All(d => !diagnosticIds.Contains(d.Id))) + { + return false; + } - return true; + return true; + } } } } From 55c02e98fcd690280f42b697c07de95f3d7ff1cc Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 9 Feb 2025 11:43:43 -0800 Subject: [PATCH 188/305] Remove inner class --- ...osticIncrementalAnalyzer_GetDiagnostics.cs | 213 ++++++++---------- 1 file changed, 96 insertions(+), 117 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index 9224dcb532969..5e5eb1342ad32 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -18,57 +18,86 @@ internal partial class DiagnosticAnalyzerService { private partial class DiagnosticIncrementalAnalyzer { - public Task> GetDiagnosticsForIdsAsync(Project project, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) - => new DiagnosticGetter(this, project, documentId, diagnosticIds, shouldIncludeAnalyzer, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics).GetDiagnosticsAsync(cancellationToken); + public async Task> GetDiagnosticsForIdsAsync(Project project, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) + { + // return diagnostics specific to one project or document + var includeProjectNonLocalResult = documentId == null; + return await ProduceProjectDiagnosticsAsync( + project, + diagnosticIds, + shouldIncludeAnalyzer, + // Ensure we compute and return diagnostics for both the normal docs and the additional docs in this + // project if no specific document id was requested. + documentId != null ? [documentId] : [.. project.DocumentIds, .. project.AdditionalDocumentIds], + includeLocalDocumentDiagnostics, + includeNonLocalDocumentDiagnostics, + includeProjectNonLocalResult, cancellationToken).ConfigureAwait(false); + } - public Task> GetProjectDiagnosticsForIdsAsync(Project project, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) - => new DiagnosticGetter(this, project, documentId: null, diagnosticIds, shouldIncludeAnalyzer, includeLocalDocumentDiagnostics: false, includeNonLocalDocumentDiagnostics).GetProjectDiagnosticsAsync(cancellationToken); + public async Task> GetProjectDiagnosticsForIdsAsync( + Project project, + ImmutableHashSet? diagnosticIds, + Func? shouldIncludeAnalyzer, + bool includeNonLocalDocumentDiagnostics, + CancellationToken cancellationToken) + { + return await ProduceProjectDiagnosticsAsync( + project, diagnosticIds, shouldIncludeAnalyzer, + documentIds: [], + includeLocalDocumentDiagnostics: false, + includeNonLocalDocumentDiagnostics: includeNonLocalDocumentDiagnostics, + includeProjectNonLocalResult: true, + cancellationToken).ConfigureAwait(false); + } - private sealed class DiagnosticGetter( - DiagnosticIncrementalAnalyzer owner, + private async Task> ProduceProjectDiagnosticsAsync( Project project, - DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, + IReadOnlyList documentIds, bool includeLocalDocumentDiagnostics, - bool includeNonLocalDocumentDiagnostics) + bool includeNonLocalDocumentDiagnostics, + bool includeProjectNonLocalResult, + CancellationToken cancellationToken) { - private readonly DiagnosticIncrementalAnalyzer Owner = owner; - - private readonly Project project = project; - private readonly DocumentId? documentId = documentId; - private readonly ImmutableHashSet? diagnosticIds = diagnosticIds; - private readonly Func? shouldIncludeAnalyzer = shouldIncludeAnalyzer; - private readonly bool includeLocalDocumentDiagnostics = includeLocalDocumentDiagnostics; - private readonly bool includeNonLocalDocumentDiagnostics = includeNonLocalDocumentDiagnostics; + using var _ = ArrayBuilder.GetInstance(out var builder); - private StateManager _stateManager => Owner._stateManager; + var analyzersForProject = await _stateManager.GetOrCreateAnalyzersAsync(project, cancellationToken).ConfigureAwait(false); + var hostAnalyzerInfo = await _stateManager.GetOrCreateHostAnalyzerInfoAsync(project, cancellationToken).ConfigureAwait(false); + var analyzers = analyzersForProject.WhereAsArray(a => ShouldIncludeAnalyzer(project, a)); - private bool ShouldIncludeDiagnostic(DiagnosticData diagnostic) - => diagnosticIds == null || diagnosticIds.Contains(diagnostic.Id); + var result = await GetOrComputeDiagnosticAnalysisResultsAsync(analyzers).ConfigureAwait(false); - public async Task> GetDiagnosticsAsync(CancellationToken cancellationToken) + foreach (var analyzer in analyzers) { - // return diagnostics specific to one project or document - var includeProjectNonLocalResult = documentId == null; - return await ProduceProjectDiagnosticsAsync( - // Ensure we compute and return diagnostics for both the normal docs and the additional docs in this - // project if no specific document id was requested. - this.documentId != null ? [this.documentId] : [.. this.project.DocumentIds, .. this.project.AdditionalDocumentIds], - includeProjectNonLocalResult, cancellationToken).ConfigureAwait(false); - } + if (!result.TryGetValue(analyzer, out var analysisResult)) + continue; - private async Task> ProduceProjectDiagnosticsAsync( - IReadOnlyList documentIds, - bool includeProjectNonLocalResult, CancellationToken cancellationToken) - { - using var _ = ArrayBuilder.GetInstance(out var builder); - await this.ProduceDiagnosticsAsync( - documentIds, includeProjectNonLocalResult, builder, cancellationToken).ConfigureAwait(false); - return builder.ToImmutableAndClear(); + foreach (var documentId in documentIds) + { + if (includeLocalDocumentDiagnostics) + { + AddIncludedDiagnostics(builder, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.Syntax)); + AddIncludedDiagnostics(builder, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.Semantic)); + } + + if (includeNonLocalDocumentDiagnostics) + AddIncludedDiagnostics(builder, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.NonLocal)); + } + + if (includeProjectNonLocalResult) + { + // include project diagnostics if there is no target document + AddIncludedDiagnostics(builder, analysisResult.GetOtherDiagnostics()); + } } - private void AddIncludedDiagnostics(ArrayBuilder builder, ImmutableArray diagnostics) + return builder.ToImmutableAndClear(); + + bool ShouldIncludeDiagnostic(DiagnosticData diagnostic) + => diagnosticIds == null || diagnosticIds.Contains(diagnostic.Id); + + void AddIncludedDiagnostics(ArrayBuilder builder, ImmutableArray diagnostics) { foreach (var diagnostic in diagnostics) { @@ -77,95 +106,45 @@ private void AddIncludedDiagnostics(ArrayBuilder builder, Immuta } } - public async Task> GetProjectDiagnosticsAsync(CancellationToken cancellationToken) - { - return await ProduceProjectDiagnosticsAsync( - documentIds: [], includeProjectNonLocalResult: true, cancellationToken).ConfigureAwait(false); - } - - private async Task ProduceDiagnosticsAsync( - IReadOnlyList documentIds, - bool includeProjectNonLocalResult, - ArrayBuilder builder, - CancellationToken cancellationToken) + async Task> GetOrComputeDiagnosticAnalysisResultsAsync( + ImmutableArray analyzers) { - var project = this.project; - var analyzersForProject = await _stateManager.GetOrCreateAnalyzersAsync(project, cancellationToken).ConfigureAwait(false); - var hostAnalyzerInfo = await _stateManager.GetOrCreateHostAnalyzerInfoAsync(project, cancellationToken).ConfigureAwait(false); - var analyzers = analyzersForProject.WhereAsArray(a => ShouldIncludeAnalyzer(project, a)); - - var result = await GetOrComputeDiagnosticAnalysisResultsAsync(analyzers).ConfigureAwait(false); - - foreach (var analyzer in analyzers) + // If there was a 'ForceAnalyzeProjectAsync' run for this project, we can piggy back off of the + // prior computed/cached results as they will be a superset of the results we want. + // + // Note: the caller will loop over *its* analzyers, grabbing from the full set of data we've cached + // for this project, and filtering down further. So it's ok to return this potentially larger set. + // + // Note: While ForceAnalyzeProjectAsync should always run with a larger set of analyzers than us + // (since it runs all analyzers), we still run a paranoia check that the analyzers we care about are + // a subset of that call so that we don't accidentally reuse results that would not correspond to + // what we are computing ourselves. + if (_projectToForceAnalysisData.TryGetValue(project, out var box) && + analyzers.IsSubsetOf(box.Value.analyzers)) { - if (!result.TryGetValue(analyzer, out var analysisResult)) - continue; - - foreach (var documentId in documentIds) - { - if (includeLocalDocumentDiagnostics) - { - AddIncludedDiagnostics(builder, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.Syntax)); - AddIncludedDiagnostics(builder, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.Semantic)); - } - - if (includeNonLocalDocumentDiagnostics) - AddIncludedDiagnostics(builder, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.NonLocal)); - } - - if (includeProjectNonLocalResult) - { - // include project diagnostics if there is no target document - AddIncludedDiagnostics(builder, analysisResult.GetOtherDiagnostics()); - } + return box.Value.diagnosticAnalysisResults; } - async Task> GetOrComputeDiagnosticAnalysisResultsAsync( - ImmutableArray analyzers) - { - // If there was a 'ForceAnalyzeProjectAsync' run for this project, we can piggy back off of the - // prior computed/cached results as they will be a superset of the results we want. - // - // Note: the caller will loop over *its* analzyers, grabbing from the full set of data we've cached - // for this project, and filtering down further. So it's ok to return this potentially larger set. - // - // Note: While ForceAnalyzeProjectAsync should always run with a larger set of analyzers than us - // (since it runs all analyzers), we still run a paranoia check that the analyzers we care about are - // a subset of that call so that we don't accidentally reuse results that would not correspond to - // what we are computing ourselves. - if (this.Owner._projectToForceAnalysisData.TryGetValue(project, out var box) && - analyzers.IsSubsetOf(box.Value.analyzers)) - { - return box.Value.diagnosticAnalysisResults; - } - - // Otherwise, just compute for the analyzers we care about. - var compilation = await GetOrCreateCompilationWithAnalyzersAsync( - project, analyzers, hostAnalyzerInfo, Owner.AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); + // Otherwise, just compute for the analyzers we care about. + var compilation = await GetOrCreateCompilationWithAnalyzersAsync( + project, analyzers, hostAnalyzerInfo, AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); - var result = await Owner.ComputeDiagnosticAnalysisResultsAsync(compilation, project, analyzers, cancellationToken).ConfigureAwait(false); - return result; - } + var result = await ComputeDiagnosticAnalysisResultsAsync(compilation, project, analyzers, cancellationToken).ConfigureAwait(false); + return result; + } - bool ShouldIncludeAnalyzer(Project project, DiagnosticAnalyzer analyzer) - { - if (!DocumentAnalysisExecutor.IsAnalyzerEnabledForProject(analyzer, project, Owner.GlobalOptions)) - { - return false; - } + bool ShouldIncludeAnalyzer(Project project, DiagnosticAnalyzer analyzer) + { + if (!DocumentAnalysisExecutor.IsAnalyzerEnabledForProject(analyzer, project, this.GlobalOptions)) + return false; - if (shouldIncludeAnalyzer != null && !shouldIncludeAnalyzer(analyzer)) - { - return false; - } + if (shouldIncludeAnalyzer != null && !shouldIncludeAnalyzer(analyzer)) + return false; - if (diagnosticIds != null && Owner.DiagnosticAnalyzerInfoCache.GetDiagnosticDescriptors(analyzer).All(d => !diagnosticIds.Contains(d.Id))) - { - return false; - } + if (diagnosticIds != null && this.DiagnosticAnalyzerInfoCache.GetDiagnosticDescriptors(analyzer).All(d => !diagnosticIds.Contains(d.Id))) + return false; - return true; - } + return true; } } } From d7c35568836f67d081440d2850fb2f1bba4ab029 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 9 Feb 2025 11:47:29 -0800 Subject: [PATCH 189/305] Cleanup --- .../Diagnostics/DiagnosticAnalyzerService.cs | 2 -- ...DiagnosticIncrementalAnalyzer_GetDiagnostics.cs | 14 +++++--------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs index 373a7f7be6d87..79595a195b782 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; using System.Runtime.CompilerServices; @@ -13,7 +12,6 @@ using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Text; diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index 5e5eb1342ad32..5d6c9799b1038 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -20,18 +20,16 @@ private partial class DiagnosticIncrementalAnalyzer { public async Task> GetDiagnosticsForIdsAsync(Project project, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) { - // return diagnostics specific to one project or document - var includeProjectNonLocalResult = documentId == null; return await ProduceProjectDiagnosticsAsync( - project, - diagnosticIds, - shouldIncludeAnalyzer, + project, diagnosticIds, shouldIncludeAnalyzer, // Ensure we compute and return diagnostics for both the normal docs and the additional docs in this // project if no specific document id was requested. documentId != null ? [documentId] : [.. project.DocumentIds, .. project.AdditionalDocumentIds], includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics, - includeProjectNonLocalResult, cancellationToken).ConfigureAwait(false); + // return diagnostics specific to one project or document + includeProjectNonLocalResult: documentId == null, + cancellationToken).ConfigureAwait(false); } public async Task> GetProjectDiagnosticsForIdsAsync( @@ -85,11 +83,9 @@ private async Task> ProduceProjectDiagnosticsAsyn AddIncludedDiagnostics(builder, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.NonLocal)); } + // include project diagnostics if there is no target document if (includeProjectNonLocalResult) - { - // include project diagnostics if there is no target document AddIncludedDiagnostics(builder, analysisResult.GetOtherDiagnostics()); - } } return builder.ToImmutableAndClear(); From fa379b6315fbeda45376f522b02e6895da919a47 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 9 Feb 2025 11:52:05 -0800 Subject: [PATCH 190/305] xml docs --- .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index f33f2e14ebf7e..a35da10b787f1 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -22,7 +22,7 @@ private partial class DiagnosticIncrementalAnalyzer /// /// Cached data from a real instance to the cached diagnostic data produced by /// all the analyzers for the project. This data can then be used by to speed up subsequent calls through the normal to speed up subsequent calls through the normal entry points as long as the project hasn't changed at all. /// private readonly ConditionalWeakTable analyzers, ImmutableDictionary diagnosticAnalysisResults)>> _projectToForceAnalysisData = new(); From f09861aae3486af3fa854249196bf89d39cb764c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 9 Feb 2025 12:00:59 -0800 Subject: [PATCH 191/305] Make local function --- ...IncrementalAnalyzer_IncrementalAnalyzer.cs | 76 ++++++++++--------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index a35da10b787f1..c91a29b177100 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -59,7 +59,8 @@ public async Task> ForceAnalyzeProjectAsync(Proje var hostAnalyzerInfo = await _stateManager.GetOrCreateHostAnalyzerInfoAsync(project, cancellationToken).ConfigureAwait(false); var fullSolutionAnalysisAnalyzers = allAnalyzers.WhereAsArray( - static (analyzer, arg) => arg.self.IsCandidateForFullSolutionAnalysis(analyzer, arg.hostAnalyzerInfo.IsHostAnalyzer(analyzer), arg.project), + static (analyzer, arg) => IsCandidateForFullSolutionAnalysis( + arg.self.DiagnosticAnalyzerInfoCache, analyzer, arg.hostAnalyzerInfo.IsHostAnalyzer(analyzer), arg.project), (self: this, project, hostAnalyzerInfo)); var compilationWithAnalyzers = await GetOrCreateCompilationWithAnalyzersAsync( @@ -68,49 +69,50 @@ public async Task> ForceAnalyzeProjectAsync(Proje var projectAnalysisData = await ComputeDiagnosticAnalysisResultsAsync(compilationWithAnalyzers, project, fullSolutionAnalysisAnalyzers, cancellationToken).ConfigureAwait(false); return (fullSolutionAnalysisAnalyzers, projectAnalysisData); } - } - private bool IsCandidateForFullSolutionAnalysis(DiagnosticAnalyzer analyzer, bool isHostAnalyzer, Project project) - { - // PERF: Don't query descriptors for compiler analyzer or workspace load analyzer, always execute them. - if (analyzer == FileContentLoadAnalyzer.Instance || - analyzer == GeneratorDiagnosticsPlaceholderAnalyzer.Instance || - analyzer.IsCompilerAnalyzer()) + static bool IsCandidateForFullSolutionAnalysis( + DiagnosticAnalyzerInfoCache infoCache, DiagnosticAnalyzer analyzer, bool isHostAnalyzer, Project project) { - return true; - } + // PERF: Don't query descriptors for compiler analyzer or workspace load analyzer, always execute them. + if (analyzer == FileContentLoadAnalyzer.Instance || + analyzer == GeneratorDiagnosticsPlaceholderAnalyzer.Instance || + analyzer.IsCompilerAnalyzer()) + { + return true; + } - if (analyzer.IsBuiltInAnalyzer()) - { - // always return true for builtin analyzer. we can't use - // descriptor check since many builtin analyzer always return - // hidden descriptor regardless what descriptor it actually - // return on runtime. they do this so that they can control - // severity through option page rather than rule set editor. - // this is special behavior only ide analyzer can do. we hope - // once we support editorconfig fully, third party can use this - // ability as well and we can remove this kind special treatment on builtin - // analyzer. - return true; - } + if (analyzer.IsBuiltInAnalyzer()) + { + // always return true for builtin analyzer. we can't use + // descriptor check since many builtin analyzer always return + // hidden descriptor regardless what descriptor it actually + // return on runtime. they do this so that they can control + // severity through option page rather than rule set editor. + // this is special behavior only ide analyzer can do. we hope + // once we support editorconfig fully, third party can use this + // ability as well and we can remove this kind special treatment on builtin + // analyzer. + return true; + } - if (analyzer is DiagnosticSuppressor) - { - // Always execute diagnostic suppressors. - return true; - } + if (analyzer is DiagnosticSuppressor) + { + // Always execute diagnostic suppressors. + return true; + } - if (project.CompilationOptions is null) - { - // Skip compilation options based checks for non-C#/VB projects. - return true; - } + if (project.CompilationOptions is null) + { + // Skip compilation options based checks for non-C#/VB projects. + return true; + } - // For most of analyzers, the number of diagnostic descriptors is small, so this should be cheap. - var descriptors = DiagnosticAnalyzerInfoCache.GetDiagnosticDescriptors(analyzer); - var analyzerConfigOptions = project.GetAnalyzerConfigOptions(); + // For most of analyzers, the number of diagnostic descriptors is small, so this should be cheap. + var descriptors = infoCache.GetDiagnosticDescriptors(analyzer); + var analyzerConfigOptions = project.GetAnalyzerConfigOptions(); - return descriptors.Any(static (d, arg) => d.GetEffectiveSeverity(arg.CompilationOptions, arg.isHostAnalyzer ? arg.analyzerConfigOptions?.ConfigOptionsWithFallback : arg.analyzerConfigOptions?.ConfigOptionsWithoutFallback, arg.analyzerConfigOptions?.TreeOptions) != ReportDiagnostic.Hidden, (project.CompilationOptions, isHostAnalyzer, analyzerConfigOptions)); + return descriptors.Any(static (d, arg) => d.GetEffectiveSeverity(arg.CompilationOptions, arg.isHostAnalyzer ? arg.analyzerConfigOptions?.ConfigOptionsWithFallback : arg.analyzerConfigOptions?.ConfigOptionsWithoutFallback, arg.analyzerConfigOptions?.TreeOptions) != ReportDiagnostic.Hidden, (project.CompilationOptions, isHostAnalyzer, analyzerConfigOptions)); + } } } } From a01d3b55a02542fd7b4c1a9e59b90c6d7e1f0f58 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 9 Feb 2025 12:13:22 -0800 Subject: [PATCH 192/305] Optimize DiagnosticService on NetCore --- .../DiagnosticIncrementalAnalyzer.CompilationManager.cs | 4 ++++ .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs index 0795b3454a313..162d45868e4e9 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs @@ -42,6 +42,9 @@ internal partial class DiagnosticAnalyzerService var compilationWithAnalyzersPair = await CreateCompilationWithAnalyzersAsync().ConfigureAwait(false); tupleBox = new((stateSets, compilationWithAnalyzersPair)); +#if NET + s_projectToCompilationWithAnalyzers.AddOrUpdate(project, tupleBox); +#else // Make a best effort attempt to store the latest computed value against these state sets. If this // fails (because another thread interleaves with this), that's ok. We still return the pair we // computed, so our caller will still see the right data @@ -50,6 +53,7 @@ internal partial class DiagnosticAnalyzerService // Intentionally ignore the result of this. We still want to use the value we computed above, even if // another thread interleaves and sets a different value. s_projectToCompilationWithAnalyzers.GetValue(project, _ => tupleBox); +#endif } return tupleBox.Value.compilationWithAnalyzersPair; diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 7b95519eefe55..3fb1dc9bf639f 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -34,7 +34,11 @@ public async Task> ForceAnalyzeProjectAsync(Proje if (!_projectToForceAnalysisData.TryGetValue(project, out var box)) { box = new(await ComputeForceAnalyzeProjectAsync().ConfigureAwait(false)); +#if NET + _projectToForceAnalysisData.AddOrUpdate(project, box); +#else box = _projectToForceAnalysisData.GetValue(project, _ => box); +#endif } using var _ = ArrayBuilder.GetInstance(out var diagnostics); From f3075bc8aa3560b384dddce37a401709e1043465 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 9 Feb 2025 12:46:37 -0800 Subject: [PATCH 193/305] Fix --- .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 3fb1dc9bf639f..a2f58d8e977ba 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -34,8 +34,12 @@ public async Task> ForceAnalyzeProjectAsync(Proje if (!_projectToForceAnalysisData.TryGetValue(project, out var box)) { box = new(await ComputeForceAnalyzeProjectAsync().ConfigureAwait(false)); + + // Try to add the new computed data to the CWT. But use any existing value that another thread + // might have beaten us to storing in it. #if NET - _projectToForceAnalysisData.AddOrUpdate(project, box); + _projectToForceAnalysisData.TryAdd(project, box); + Contract.ThrowIfFalse(_projectToForceAnalysisData.TryGetValue(project, out box)); #else box = _projectToForceAnalysisData.GetValue(project, _ => box); #endif From bf3a74f4a097101392a3a0315c9a1b0cf5d3c07f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 10 Feb 2025 12:02:18 -0800 Subject: [PATCH 194/305] Use expression body --- .../Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index 630a446d92638..e968d7d55b57a 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -49,12 +49,8 @@ public DiagnosticIncrementalAnalyzer( internal IGlobalOptionService GlobalOptions { get; } internal DiagnosticAnalyzerInfoCache DiagnosticAnalyzerInfoCache => _diagnosticAnalyzerRunner.AnalyzerInfoCache; - public async Task> GetAnalyzersForTestingPurposesOnlyAsync(Project project, CancellationToken cancellationToken) - { - var analyzers = await _stateManager.GetOrCreateAnalyzersAsync(project, cancellationToken).ConfigureAwait(false); - - return analyzers; - } + public Task> GetAnalyzersForTestingPurposesOnlyAsync(Project project, CancellationToken cancellationToken) + => _stateManager.GetOrCreateAnalyzersAsync(project, cancellationToken); private static string GetProjectLogMessage(Project project, ImmutableArray analyzers) => $"project: ({project.Id}), ({string.Join(Environment.NewLine, analyzers.Select(a => a.ToString()))})"; From 7ef54be935aa2580c55b69fa2d0edb3c174f0518 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 10 Feb 2025 12:05:05 -0800 Subject: [PATCH 195/305] inline --- ...crementalAnalyzer_GetDiagnosticsForSpan.cs | 41 +++++++------------ 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs index b2ecd328c8678..bfb67ed49665e 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs @@ -175,11 +175,22 @@ public async Task GetAsync(ArrayBuilder list, CancellationToken if (includeSemantic) { - var selectedAnalyzers = GetSemanticAnalysisSelectedAnalyzers( - analyzer, _incrementalAnalysis, - semanticSpanBasedAnalyzers, semanticDocumentBasedAnalyzers); - selectedAnalyzers.Add(analyzer); + if (!_incrementalAnalysis) + { + // For non-incremental analysis, we always attempt to compute all + // analyzer diagnostics for the requested span. + semanticSpanBasedAnalyzers.Add(analyzer); + } + else + { + // We can perform incremental analysis only for analyzers that support + // span-based semantic diagnostic analysis. + if (analyzer.SupportsSpanBasedSemanticDiagnosticAnalysis()) + semanticSpanBasedAnalyzers.Add(analyzer); + else + semanticDocumentBasedAnalyzers.Add(analyzer); + } } } } @@ -228,28 +239,6 @@ static bool ShouldIncludeAnalyzer( return true; } - - static ArrayBuilder GetSemanticAnalysisSelectedAnalyzers( - DiagnosticAnalyzer analyzer, - bool incrementalAnalysis, - ArrayBuilder semanticSpanBasedAnalyzers, - ArrayBuilder semanticDocumentBasedAnalyzers) - { - if (!incrementalAnalysis) - { - // For non-incremental analysis, we always attempt to compute all - // analyzer diagnostics for the requested span. - return semanticSpanBasedAnalyzers; - } - else - { - // We can perform incremental analysis only for analyzers that support - // span-based semantic diagnostic analysis. - return analyzer.SupportsSpanBasedSemanticDiagnosticAnalysis() - ? semanticSpanBasedAnalyzers - : semanticDocumentBasedAnalyzers; - } - } } private async Task ComputeDocumentDiagnosticsAsync( From e2cba6c7d1af2fec730201c50b36e9bdd583a011 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 10 Feb 2025 12:06:33 -0800 Subject: [PATCH 196/305] Change cases --- ...nosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs index bfb67ed49665e..ff5077935d209 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs @@ -182,14 +182,15 @@ public async Task GetAsync(ArrayBuilder list, CancellationToken // analyzer diagnostics for the requested span. semanticSpanBasedAnalyzers.Add(analyzer); } - else + else if (analyzer.SupportsSpanBasedSemanticDiagnosticAnalysis()) { // We can perform incremental analysis only for analyzers that support // span-based semantic diagnostic analysis. - if (analyzer.SupportsSpanBasedSemanticDiagnosticAnalysis()) - semanticSpanBasedAnalyzers.Add(analyzer); - else - semanticDocumentBasedAnalyzers.Add(analyzer); + semanticSpanBasedAnalyzers.Add(analyzer); + } + else + { + semanticDocumentBasedAnalyzers.Add(analyzer); } } } From d4a42cf1c91c90b50e902dcc1a956af6fd321b10 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 10 Feb 2025 12:07:10 -0800 Subject: [PATCH 197/305] remove temp --- ...osticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs index 818fe1f97c4a8..8b2c1018d8ff0 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs @@ -94,11 +94,10 @@ public async Task Date: Mon, 10 Feb 2025 12:12:21 -0800 Subject: [PATCH 198/305] No need to pass callbacks --- ...lAnalyzer.IncrementalMemberEditAnalyzer.cs | 8 ++- ...crementalAnalyzer_GetDiagnosticsForSpan.cs | 52 +++++++++---------- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs index 8b2c1018d8ff0..dd609587e37cc 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs @@ -49,8 +49,6 @@ public async Task analyzers, VersionStamp version, - Func>> computeAnalyzerDiagnosticsAsync, - Func>>> computeDiagnosticsNonIncrementallyAsync, CancellationToken cancellationToken) { var analysisScope = executor.AnalysisScope; @@ -68,7 +66,7 @@ public async Task> GetDiagnosticsForSpanAsync( return list.ToImmutableAndClear(); } + private static async Task>> ComputeDocumentDiagnosticsCoreAsync( + DocumentAnalysisExecutor executor, + CancellationToken cancellationToken) + { + using var _ = PooledDictionary>.GetInstance(out var builder); + foreach (var analyzer in executor.AnalysisScope.ProjectAnalyzers.ConcatFast(executor.AnalysisScope.HostAnalyzers)) + { + var diagnostics = await ComputeDocumentDiagnosticsForAnalyzerCoreAsync(analyzer, executor, cancellationToken).ConfigureAwait(false); + builder.Add(analyzer, diagnostics); + } + + return builder.ToImmutableDictionary(); + } + + private static async Task> ComputeDocumentDiagnosticsForAnalyzerCoreAsync( + DiagnosticAnalyzer analyzer, + DocumentAnalysisExecutor executor, + CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var diagnostics = await executor.ComputeDiagnosticsAsync(analyzer, cancellationToken).ConfigureAwait(false); + return diagnostics?.ToImmutableArrayOrEmpty() ?? []; + } + /// /// Get diagnostics for given span either by using cache or calculating it on the spot. /// @@ -291,8 +316,6 @@ private async Task ComputeDocumentDiagnosticsAsync( executor, analyzers, version, - ComputeDocumentDiagnosticsForAnalyzerCoreAsync, - ComputeDocumentDiagnosticsCoreAsync, cancellationToken).ConfigureAwait(false); } else @@ -377,31 +400,6 @@ async Task IsCandidateForDeprioritizationBasedOnRegisteredActionsAsync(Dia } } - private async Task>> ComputeDocumentDiagnosticsCoreAsync( - DocumentAnalysisExecutor executor, - CancellationToken cancellationToken) - { - using var _ = PooledDictionary>.GetInstance(out var builder); - foreach (var analyzer in executor.AnalysisScope.ProjectAnalyzers.ConcatFast(executor.AnalysisScope.HostAnalyzers)) - { - var diagnostics = await ComputeDocumentDiagnosticsForAnalyzerCoreAsync(analyzer, executor, cancellationToken).ConfigureAwait(false); - builder.Add(analyzer, diagnostics); - } - - return builder.ToImmutableDictionary(); - } - - private async Task> ComputeDocumentDiagnosticsForAnalyzerCoreAsync( - DiagnosticAnalyzer analyzer, - DocumentAnalysisExecutor executor, - CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var diagnostics = await executor.ComputeDiagnosticsAsync(analyzer, cancellationToken).ConfigureAwait(false); - return diagnostics?.ToImmutableArrayOrEmpty() ?? []; - } - private bool ShouldInclude(DiagnosticData diagnostic) { return diagnostic.DocumentId == _document.Id && From 4fa21f55b4761f6471df3becb4ab26fefea84ce7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 10 Feb 2025 12:13:38 -0800 Subject: [PATCH 199/305] remove stale comments --- .../DiagnosticIncrementalAnalyzer.StateManager.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs index e3efbcf14327a..cd07e28f14daf 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs @@ -81,11 +81,6 @@ private static (ImmutableHashSet hostAnalyzers, ImmutableHas foreach (var analyzer in analyzers) { Debug.Assert(analyzer != FileContentLoadAnalyzer.Instance && analyzer != GeneratorDiagnosticsPlaceholderAnalyzer.Instance); - - // TODO: - // #1, all de-duplication should move to DiagnosticAnalyzerInfoCache - // #2, not sure whether de-duplication of analyzer itself makes sense. this can only happen - // if user deliberately put same analyzer twice. allAnalyzers.Add(analyzer); } } @@ -95,11 +90,6 @@ private static (ImmutableHashSet hostAnalyzers, ImmutableHas foreach (var analyzer in analyzers) { Debug.Assert(analyzer != FileContentLoadAnalyzer.Instance && analyzer != GeneratorDiagnosticsPlaceholderAnalyzer.Instance); - - // TODO: - // #1, all de-duplication should move to DiagnosticAnalyzerInfoCache - // #2, not sure whether de-duplication of analyzer itself makes sense. this can only happen - // if user deliberately put same analyzer twice. allAnalyzers.Add(analyzer); hostAnalyzers.Add(analyzer); } From 84931775ae116041fdf5c8f50632690d80b6407e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 10 Feb 2025 12:31:00 -0800 Subject: [PATCH 200/305] Cache based on project state not project. --- ...cIncrementalAnalyzer.CompilationManager.cs | 31 +++++++------------ ...IncrementalAnalyzer_IncrementalAnalyzer.cs | 2 +- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs index f04fa0a778fa9..e6b2a8c4016db 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs @@ -24,7 +24,7 @@ internal partial class DiagnosticAnalyzerService /// value if the set of analyzers changes. In that case, a new instance will be created and will be cached for the /// next caller. /// - private static readonly ConditionalWeakTable analyzers, CompilationWithAnalyzersPair? compilationWithAnalyzersPair)>> s_projectToCompilationWithAnalyzers = new(); + private static readonly ConditionalWeakTable analyzers, CompilationWithAnalyzersPair? compilationWithAnalyzersPair)>> s_projectToCompilationWithAnalyzers = new(); private static async Task GetOrCreateCompilationWithAnalyzersAsync( Project project, @@ -36,22 +36,25 @@ internal partial class DiagnosticAnalyzerService if (!project.SupportsCompilation) return null; + var projectState = project.State; + // Make sure the cached pair was computed with at least the same state sets we're asking about. if not, // recompute and cache with the new state sets. - if (!s_projectToCompilationWithAnalyzers.TryGetValue(project, out var tupleBox) || + if (!s_projectToCompilationWithAnalyzers.TryGetValue(projectState, out var tupleBox) || !analyzers.IsSubsetOf(tupleBox.Value.analyzers)) { - var compilationWithAnalyzersPair = await CreateCompilationWithAnalyzersAsync().ConfigureAwait(false); + var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); + var compilationWithAnalyzersPair = CreateCompilationWithAnalyzers(projectState, compilation); tupleBox = new((analyzers, compilationWithAnalyzersPair)); // Make a best effort attempt to store the latest computed value against these state sets. If this // fails (because another thread interleaves with this), that's ok. We still return the pair we // computed, so our caller will still see the right data - s_projectToCompilationWithAnalyzers.Remove(project); + s_projectToCompilationWithAnalyzers.Remove(projectState); // Intentionally ignore the result of this. We still want to use the value we computed above, even if // another thread interleaves and sets a different value. - s_projectToCompilationWithAnalyzers.GetValue(project, _ => tupleBox); + s_projectToCompilationWithAnalyzers.GetValue(projectState, _ => tupleBox); } return tupleBox.Value.compilationWithAnalyzersPair; @@ -59,13 +62,12 @@ internal partial class DiagnosticAnalyzerService // // Should only be called on a that . // - async Task CreateCompilationWithAnalyzersAsync() + CompilationWithAnalyzersPair? CreateCompilationWithAnalyzers( + ProjectState project, Compilation compilation) { var projectAnalyzers = analyzers.WhereAsArray(static (s, info) => !info.IsHostAnalyzer(s), hostAnalyzerInfo); var hostAnalyzers = analyzers.WhereAsArray(static (s, info) => info.IsHostAnalyzer(s), hostAnalyzerInfo); - var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); - // Create driver that holds onto compilation and associated analyzers var filteredProjectAnalyzers = projectAnalyzers.WhereAsArray(static a => !a.IsWorkspaceDiagnosticAnalyzer()); var filteredHostAnalyzers = hostAnalyzers.WhereAsArray(static a => !a.IsWorkspaceDiagnosticAnalyzer()); @@ -79,13 +81,10 @@ internal partial class DiagnosticAnalyzerService return null; } - Contract.ThrowIfFalse(project.SupportsCompilation); - AssertCompilation(project, compilation); - // in IDE, we always set concurrentAnalysis == false otherwise, we can get into thread starvation due to // async being used with synchronous blocking concurrency. var projectAnalyzerOptions = new CompilationWithAnalyzersOptions( - options: project.AnalyzerOptions, + options: project.ProjectAnalyzerOptions, onAnalyzerException: null, analyzerExceptionFilter: GetAnalyzerExceptionFilter(), concurrentAnalysis: false, @@ -122,12 +121,4 @@ Func GetAnalyzerExceptionFilter() } } } - - [Conditional("DEBUG")] - private static void AssertCompilation(Project project, Compilation compilation1) - { - // given compilation must be from given project. - Contract.ThrowIfFalse(project.TryGetCompilation(out var compilation2)); - Contract.ThrowIfFalse(compilation1 == compilation2); - } } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index f33f2e14ebf7e..f50572eac27ff 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -25,7 +25,7 @@ private partial class DiagnosticIncrementalAnalyzer /// cref="DiagnosticGetter.ProduceDiagnosticsAsync"/> to speed up subsequent calls through the normal entry points as long as the project hasn't changed at all. /// - private readonly ConditionalWeakTable analyzers, ImmutableDictionary diagnosticAnalysisResults)>> _projectToForceAnalysisData = new(); + private readonly ConditionalWeakTable analyzers, ImmutableDictionary diagnosticAnalysisResults)>> _projectToForceAnalysisData = new(); public async Task> ForceAnalyzeProjectAsync(Project project, CancellationToken cancellationToken) { From 80ac90659e62f5c60cce23b3f5f42d9fdc68a16e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 10 Feb 2025 12:55:55 -0800 Subject: [PATCH 201/305] Cache based on project state not project. --- ...ticIncrementalAnalyzer.HostAnalyzerInfo.cs | 16 ++++++---- ...ntalAnalyzer.StateManager.ProjectStates.cs | 17 +++++----- ...gnosticIncrementalAnalyzer.StateManager.cs | 14 ++++---- .../EngineV2/DiagnosticIncrementalAnalyzer.cs | 2 +- ...osticIncrementalAnalyzer_GetDiagnostics.cs | 10 ++++-- ...crementalAnalyzer_GetDiagnosticsForSpan.cs | 15 ++++++--- ...IncrementalAnalyzer_IncrementalAnalyzer.cs | 32 +++++++++++++------ .../InProcOrRemoteHostAnalyzerRunner.cs | 2 +- .../Diagnostics/HostDiagnosticAnalyzers.cs | 4 +-- .../Portable/Workspace/Solution/Project.cs | 3 -- 10 files changed, 70 insertions(+), 45 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.HostAnalyzerInfo.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.HostAnalyzerInfo.cs index 292ac0a5d21d9..9a917b7ff7ef5 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.HostAnalyzerInfo.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.HostAnalyzerInfo.cs @@ -17,13 +17,14 @@ private partial class DiagnosticIncrementalAnalyzer { private partial class StateManager { - private HostAnalyzerInfo GetOrCreateHostAnalyzerInfo(Project project, ProjectAnalyzerInfo projectAnalyzerInfo) + private HostAnalyzerInfo GetOrCreateHostAnalyzerInfo( + SolutionState solution, ProjectState project, ProjectAnalyzerInfo projectAnalyzerInfo) { - var key = new HostAnalyzerInfoKey(project.Language, project.State.HasSdkCodeStyleAnalyzers, project.Solution.SolutionState.Analyzers.HostAnalyzerReferences); + var key = new HostAnalyzerInfoKey(project.Language, project.HasSdkCodeStyleAnalyzers, solution.Analyzers.HostAnalyzerReferences); // Some Host Analyzers may need to be treated as Project Analyzers so that they do not have access to the // Host fallback options. These ids will be used when building up the Host and Project analyzer collections. - var referenceIdsToRedirect = GetReferenceIdsToRedirectAsProjectAnalyzers(project); - var hostAnalyzerInfo = ImmutableInterlocked.GetOrAdd(ref _hostAnalyzerStateMap, key, CreateLanguageSpecificAnalyzerMap, (project.Solution.SolutionState.Analyzers, referenceIdsToRedirect)); + var referenceIdsToRedirect = GetReferenceIdsToRedirectAsProjectAnalyzers(solution, project); + var hostAnalyzerInfo = ImmutableInterlocked.GetOrAdd(ref _hostAnalyzerStateMap, key, CreateLanguageSpecificAnalyzerMap, (solution.Analyzers, referenceIdsToRedirect)); return hostAnalyzerInfo.WithExcludedAnalyzers(projectAnalyzerInfo.SkippedAnalyzersInfo.SkippedAnalyzers); static HostAnalyzerInfo CreateLanguageSpecificAnalyzerMap(HostAnalyzerInfoKey arg, (HostDiagnosticAnalyzers HostAnalyzers, ImmutableHashSet ReferenceIdsToRedirect) state) @@ -65,14 +66,15 @@ static HostAnalyzerInfo CreateLanguageSpecificAnalyzerMap(HostAnalyzerInfoKey ar } } - private static ImmutableHashSet GetReferenceIdsToRedirectAsProjectAnalyzers(Project project) + private static ImmutableHashSet GetReferenceIdsToRedirectAsProjectAnalyzers( + SolutionState solution, ProjectState project) { - if (project.State.HasSdkCodeStyleAnalyzers) + if (project.HasSdkCodeStyleAnalyzers) { // When a project uses CodeStyle analyzers added by the SDK, we remove them in favor of the // Features analyzers. We need to then treat the Features analyzers as Project analyzers so // they do not get access to the Host fallback options. - return GetFeaturesAnalyzerReferenceIds(project.Solution.SolutionState.Analyzers); + return GetFeaturesAnalyzerReferenceIds(solution.Analyzers); } return []; diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs index c3eca357edf17..849e5821bdc8f 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs @@ -41,7 +41,7 @@ internal ProjectAnalyzerInfo( } } - private ProjectAnalyzerInfo? TryGetProjectAnalyzerInfo(Project project) + private ProjectAnalyzerInfo? TryGetProjectAnalyzerInfo(ProjectState project) { // check if the analyzer references have changed since the last time we updated the map: // No need to use _projectAnalyzerStateMapGuard during reads of _projectAnalyzerStateMap @@ -54,17 +54,17 @@ internal ProjectAnalyzerInfo( return null; } - private async Task GetOrCreateProjectAnalyzerInfoAsync(Project project, CancellationToken cancellationToken) - => TryGetProjectAnalyzerInfo(project) ?? await UpdateProjectAnalyzerInfoAsync(project, cancellationToken).ConfigureAwait(false); + private async Task GetOrCreateProjectAnalyzerInfoAsync(SolutionState solution, ProjectState project, CancellationToken cancellationToken) + => TryGetProjectAnalyzerInfo(project) ?? await UpdateProjectAnalyzerInfoAsync(solution, project, cancellationToken).ConfigureAwait(false); - private ProjectAnalyzerInfo CreateProjectAnalyzerInfo(Project project) + private ProjectAnalyzerInfo CreateProjectAnalyzerInfo(SolutionState solution, ProjectState project) { if (project.AnalyzerReferences.Count == 0) { return ProjectAnalyzerInfo.Default; } - var hostAnalyzers = project.Solution.SolutionState.Analyzers; + var hostAnalyzers = solution.Analyzers; var analyzersPerReference = hostAnalyzers.CreateProjectDiagnosticAnalyzersPerReference(project); if (analyzersPerReference.Count == 0) { @@ -78,14 +78,15 @@ private ProjectAnalyzerInfo CreateProjectAnalyzerInfo(Project project) // workspace placeholder analyzers. So we should never get host analyzers back here. Contract.ThrowIfTrue(newHostAnalyzers.Count > 0); - var skippedAnalyzersInfo = project.GetSkippedAnalyzersInfo(_analyzerInfoCache); + var skippedAnalyzersInfo = solution.Analyzers.GetSkippedAnalyzersInfo(project, _analyzerInfoCache); return new ProjectAnalyzerInfo(project.AnalyzerReferences, newAllAnalyzers, skippedAnalyzersInfo); } /// /// Updates the map to the given project snapshot. /// - private async Task UpdateProjectAnalyzerInfoAsync(Project project, CancellationToken cancellationToken) + private async Task UpdateProjectAnalyzerInfoAsync( + SolutionState solution, ProjectState project, CancellationToken cancellationToken) { // This code is called concurrently for a project, so the guard prevents duplicated effort calculating StateSets. using (await _projectAnalyzerStateMapGuard.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) @@ -94,7 +95,7 @@ private async Task UpdateProjectAnalyzerInfoAsync(Project p if (projectAnalyzerInfo == null) { - projectAnalyzerInfo = CreateProjectAnalyzerInfo(project); + projectAnalyzerInfo = CreateProjectAnalyzerInfo(solution, project); // update cache. _projectAnalyzerStateMap = _projectAnalyzerStateMap.SetItem(project.Id, projectAnalyzerInfo.Value); diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs index e3efbcf14327a..cce4d08ccf0c9 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs @@ -47,17 +47,19 @@ private partial class StateManager(DiagnosticAnalyzerInfoCache analyzerInfoCache /// /// Return s for the given . /// - public async Task> GetOrCreateAnalyzersAsync(Project project, CancellationToken cancellationToken) + public async Task> GetOrCreateAnalyzersAsync( + SolutionState solution, ProjectState project, CancellationToken cancellationToken) { - var hostAnalyzerInfo = await GetOrCreateHostAnalyzerInfoAsync(project, cancellationToken).ConfigureAwait(false); - var projectAnalyzerInfo = await GetOrCreateProjectAnalyzerInfoAsync(project, cancellationToken).ConfigureAwait(false); + var hostAnalyzerInfo = await GetOrCreateHostAnalyzerInfoAsync(solution, project, cancellationToken).ConfigureAwait(false); + var projectAnalyzerInfo = await GetOrCreateProjectAnalyzerInfoAsync(solution, project, cancellationToken).ConfigureAwait(false); return hostAnalyzerInfo.OrderedAllAnalyzers.AddRange(projectAnalyzerInfo.Analyzers); } - public async Task GetOrCreateHostAnalyzerInfoAsync(Project project, CancellationToken cancellationToken) + public async Task GetOrCreateHostAnalyzerInfoAsync( + SolutionState solution, ProjectState project, CancellationToken cancellationToken) { - var projectAnalyzerInfo = await GetOrCreateProjectAnalyzerInfoAsync(project, cancellationToken).ConfigureAwait(false); - return GetOrCreateHostAnalyzerInfo(project, projectAnalyzerInfo); + var projectAnalyzerInfo = await GetOrCreateProjectAnalyzerInfoAsync(solution, project, cancellationToken).ConfigureAwait(false); + return GetOrCreateHostAnalyzerInfo(solution, project, projectAnalyzerInfo); } private static (ImmutableHashSet hostAnalyzers, ImmutableHashSet allAnalyzers) PartitionAnalyzers( diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index 630a446d92638..08592ff697389 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -51,7 +51,7 @@ public DiagnosticIncrementalAnalyzer( public async Task> GetAnalyzersForTestingPurposesOnlyAsync(Project project, CancellationToken cancellationToken) { - var analyzers = await _stateManager.GetOrCreateAnalyzersAsync(project, cancellationToken).ConfigureAwait(false); + var analyzers = await _stateManager.GetOrCreateAnalyzersAsync(project.Solution.SolutionState, project.State, cancellationToken).ConfigureAwait(false); return analyzers; } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index e4b9a2658b0fc..f88de4dc9f3f9 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -101,8 +101,12 @@ private async Task ProduceDiagnosticsAsync( ArrayBuilder builder, CancellationToken cancellationToken) { - var analyzersForProject = await StateManager.GetOrCreateAnalyzersAsync(project, cancellationToken).ConfigureAwait(false); - var hostAnalyzerInfo = await StateManager.GetOrCreateHostAnalyzerInfoAsync(project, cancellationToken).ConfigureAwait(false); + var solution = project.Solution; + var projectState = project.State; + var analyzersForProject = await StateManager.GetOrCreateAnalyzersAsync( + solution.SolutionState, projectState, cancellationToken).ConfigureAwait(false); + var hostAnalyzerInfo = await StateManager.GetOrCreateHostAnalyzerInfoAsync( + solution.SolutionState, projectState, cancellationToken).ConfigureAwait(false); var analyzers = analyzersForProject.WhereAsArray(a => ShouldIncludeAnalyzer(project, a)); var result = await GetOrComputeDiagnosticAnalysisResultsAsync(analyzers).ConfigureAwait(false); @@ -144,7 +148,7 @@ async Task> Ge // (since it runs all analyzers), we still run a paranoia check that the analyzers we care about are // a subset of that call so that we don't accidentally reuse results that would not correspond to // what we are computing ourselves. - if (this.Owner._projectToForceAnalysisData.TryGetValue(project, out var box) && + if (this.Owner._projectToForceAnalysisData.TryGetValue(projectState, out var box) && analyzers.IsSubsetOf(box.Value.analyzers)) { return box.Value.diagnosticAnalysisResults; diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs index b2ecd328c8678..1ca539fe9461d 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs @@ -75,12 +75,15 @@ public static async Task CreateAsync( { var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); + var project = document.Project; + var solution = project.Solution; var unfilteredAnalyzers = await owner._stateManager - .GetOrCreateAnalyzersAsync(document.Project, cancellationToken) + .GetOrCreateAnalyzersAsync(solution.SolutionState, project.State, cancellationToken) .ConfigureAwait(false); var analyzers = unfilteredAnalyzers - .WhereAsArray(a => DocumentAnalysisExecutor.IsAnalyzerEnabledForProject(a, document.Project, owner.GlobalOptions)); - var hostAnalyzerInfo = await owner._stateManager.GetOrCreateHostAnalyzerInfoAsync(document.Project, cancellationToken).ConfigureAwait(false); + .WhereAsArray(a => DocumentAnalysisExecutor.IsAnalyzerEnabledForProject(a, project, owner.GlobalOptions)); + var hostAnalyzerInfo = await owner._stateManager.GetOrCreateHostAnalyzerInfoAsync( + solution.SolutionState, project.State, cancellationToken).ConfigureAwait(false); // Note that some callers, such as diagnostic tagger, might pass in a range equal to the entire document span. // We clear out range for such cases as we are computing full document diagnostics. @@ -284,13 +287,15 @@ private async Task ComputeDocumentDiagnosticsAsync( analyzers = filteredAnalyzers.ToImmutable(); - var hostAnalyzerInfo = await _owner._stateManager.GetOrCreateHostAnalyzerInfoAsync(_document.Project, cancellationToken).ConfigureAwait(false); + var project = _document.Project; + var hostAnalyzerInfo = await _owner._stateManager.GetOrCreateHostAnalyzerInfoAsync( + project.Solution.SolutionState, project.State, cancellationToken).ConfigureAwait(false); var projectAnalyzers = analyzers.WhereAsArray(static (a, info) => !info.IsHostAnalyzer(a), hostAnalyzerInfo); var hostAnalyzers = analyzers.WhereAsArray(static (a, info) => info.IsHostAnalyzer(a), hostAnalyzerInfo); var analysisScope = new DocumentAnalysisScope(_document, span, projectAnalyzers, hostAnalyzers, kind); var executor = new DocumentAnalysisExecutor(analysisScope, _compilationWithAnalyzers, _owner._diagnosticAnalyzerRunner, _isExplicit, _logPerformanceInfo); - var version = await GetDiagnosticVersionAsync(_document.Project, cancellationToken).ConfigureAwait(false); + var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); ImmutableDictionary> diagnosticsMap; if (incrementalAnalysis) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index f50572eac27ff..5f0b5e5a7235e 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -20,21 +20,31 @@ internal partial class DiagnosticAnalyzerService private partial class DiagnosticIncrementalAnalyzer { /// - /// Cached data from a real instance to the cached diagnostic data produced by + /// Cached data from a real instance to the cached diagnostic data produced by /// all the analyzers for the project. This data can then be used by to speed up subsequent calls through the normal entry points as long as the project hasn't changed at all. /// + /// + /// This table is keyed off of but stores data from on + /// it. Specifically . Normally keying off a ProjectState would not be ok + /// as the ProjectState might stay the same while the SolutionState changed. However, that can't happen as + /// SolutionState has the data for Analyzers computed prior to Projects being added, and then never changes. + /// Practically, solution analyzers are the core Roslyn analyzers themselves we distribute, or analyzers shipped + /// by vsix (not nuget). These analyzers do not get loaded after changing *until* VS restarts. + /// private readonly ConditionalWeakTable analyzers, ImmutableDictionary diagnosticAnalysisResults)>> _projectToForceAnalysisData = new(); public async Task> ForceAnalyzeProjectAsync(Project project, CancellationToken cancellationToken) { + var projectState = project.State; + try { - if (!_projectToForceAnalysisData.TryGetValue(project, out var box)) + if (!_projectToForceAnalysisData.TryGetValue(projectState, out var box)) { box = new(await ComputeForceAnalyzeProjectAsync().ConfigureAwait(false)); - box = _projectToForceAnalysisData.GetValue(project, _ => box); + box = _projectToForceAnalysisData.GetValue(projectState, _ => box); } using var _ = ArrayBuilder.GetInstance(out var diagnostics); @@ -55,12 +65,13 @@ public async Task> ForceAnalyzeProjectAsync(Proje async Task<(ImmutableArray analyzers, ImmutableDictionary diagnosticAnalysisResults)> ComputeForceAnalyzeProjectAsync() { - var allAnalyzers = await _stateManager.GetOrCreateAnalyzersAsync(project, cancellationToken).ConfigureAwait(false); - var hostAnalyzerInfo = await _stateManager.GetOrCreateHostAnalyzerInfoAsync(project, cancellationToken).ConfigureAwait(false); + var solutionState = project.Solution.SolutionState; + var allAnalyzers = await _stateManager.GetOrCreateAnalyzersAsync(solutionState, projectState, cancellationToken).ConfigureAwait(false); + var hostAnalyzerInfo = await _stateManager.GetOrCreateHostAnalyzerInfoAsync(solutionState, projectState, cancellationToken).ConfigureAwait(false); var fullSolutionAnalysisAnalyzers = allAnalyzers.WhereAsArray( - static (analyzer, arg) => arg.self.IsCandidateForFullSolutionAnalysis(analyzer, arg.hostAnalyzerInfo.IsHostAnalyzer(analyzer), arg.project), - (self: this, project, hostAnalyzerInfo)); + static (analyzer, arg) => arg.self.IsCandidateForFullSolutionAnalysis(analyzer, arg.hostAnalyzerInfo.IsHostAnalyzer(analyzer), arg.projectState), + (self: this, projectState, hostAnalyzerInfo)); var compilationWithAnalyzers = await GetOrCreateCompilationWithAnalyzersAsync( project, fullSolutionAnalysisAnalyzers, hostAnalyzerInfo, AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); @@ -70,7 +81,8 @@ public async Task> ForceAnalyzeProjectAsync(Proje } } - private bool IsCandidateForFullSolutionAnalysis(DiagnosticAnalyzer analyzer, bool isHostAnalyzer, Project project) + private bool IsCandidateForFullSolutionAnalysis( + DiagnosticAnalyzer analyzer, bool isHostAnalyzer, ProjectState project) { // PERF: Don't query descriptors for compiler analyzer or workspace load analyzer, always execute them. if (analyzer == FileContentLoadAnalyzer.Instance || @@ -110,7 +122,9 @@ private bool IsCandidateForFullSolutionAnalysis(DiagnosticAnalyzer analyzer, boo var descriptors = DiagnosticAnalyzerInfoCache.GetDiagnosticDescriptors(analyzer); var analyzerConfigOptions = project.GetAnalyzerConfigOptions(); - return descriptors.Any(static (d, arg) => d.GetEffectiveSeverity(arg.CompilationOptions, arg.isHostAnalyzer ? arg.analyzerConfigOptions?.ConfigOptionsWithFallback : arg.analyzerConfigOptions?.ConfigOptionsWithoutFallback, arg.analyzerConfigOptions?.TreeOptions) != ReportDiagnostic.Hidden, (project.CompilationOptions, isHostAnalyzer, analyzerConfigOptions)); + return descriptors.Any(static (d, arg) => d.GetEffectiveSeverity( + arg.CompilationOptions, arg.isHostAnalyzer ? arg.analyzerConfigOptions?.ConfigOptionsWithFallback : arg.analyzerConfigOptions?.ConfigOptionsWithoutFallback, arg.analyzerConfigOptions?.TreeOptions) != ReportDiagnostic.Hidden, + (project.CompilationOptions, isHostAnalyzer, analyzerConfigOptions)); } } } diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/InProcOrRemoteHostAnalyzerRunner.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/InProcOrRemoteHostAnalyzerRunner.cs index 1203bb1dabee7..d43c3016d6487 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/InProcOrRemoteHostAnalyzerRunner.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/InProcOrRemoteHostAnalyzerRunner.cs @@ -131,7 +131,7 @@ private async Task.Empty; diff --git a/src/Workspaces/Core/Portable/Diagnostics/HostDiagnosticAnalyzers.cs b/src/Workspaces/Core/Portable/Diagnostics/HostDiagnosticAnalyzers.cs index e63221b427381..fb72eccc2dc01 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/HostDiagnosticAnalyzers.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/HostDiagnosticAnalyzers.cs @@ -133,7 +133,7 @@ public ImmutableDictionary> CreateDia /// Create identity and s map for given that /// has only project analyzers /// - public ImmutableDictionary> CreateProjectDiagnosticAnalyzersPerReference(Project project) + public ImmutableDictionary> CreateProjectDiagnosticAnalyzersPerReference(ProjectState project) => CreateProjectDiagnosticAnalyzersPerReference(project.AnalyzerReferences, project.Language); public ImmutableDictionary> CreateProjectDiagnosticAnalyzersPerReference(IReadOnlyList projectAnalyzerReferences, string language) @@ -296,7 +296,7 @@ private static ImmutableDictionary> M return current; } - public SkippedHostAnalyzersInfo GetSkippedAnalyzersInfo(Project project, DiagnosticAnalyzerInfoCache infoCache) + public SkippedHostAnalyzersInfo GetSkippedAnalyzersInfo(ProjectState project, DiagnosticAnalyzerInfoCache infoCache) { var box = _skippedHostAnalyzers.GetOrCreateValue(project.AnalyzerReferences); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Project.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Project.cs index c76d53fcca168..260c146144562 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Project.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Project.cs @@ -830,9 +830,6 @@ internal StructuredAnalyzerConfigOptions GetFallbackAnalyzerOptions() private string GetDebuggerDisplay() => this.Name; - internal SkippedHostAnalyzersInfo GetSkippedAnalyzersInfo(DiagnosticAnalyzerInfoCache infoCache) - => Solution.SolutionState.Analyzers.GetSkippedAnalyzersInfo(this, infoCache); - internal async ValueTask GetDocumentAsync(ImmutableArray contentHash, CancellationToken cancellationToken) { var documentId = await State.GetDocumentIdAsync(contentHash, cancellationToken).ConfigureAwait(false); From 1adf017c8952f30092879960f7f8d2496310c211 Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Mon, 10 Feb 2025 13:01:04 -0800 Subject: [PATCH 202/305] Add new workspace event that gives handlers the opportunity to be processed immediately (#76932) Add new workspace event that gives handlers the opportunity to be processed immediately This is in response to allocations seen when the OOP process attempts to operate on a sourcetext version that has not been synchronized over. In those cases, the OOP process requests a full deserialization of the source text causing full buffer allocations in both VS and the CA process. There are several points of asynchronicity in the current system around sending over buffer changes, reducing any of which would reduce the likelihood of needing this full serialization. This PR remove one of point of asynchronicity in the workspace eventing layer and reduces another point of asynchronicity by bypassing the old _textChangeQueue ABWQ and doing a fire and forget instead. Previously, all notifications were done on a delayed basis, by wrapping each notification in the WorkspaceSpace.ScheduleTask call from the RaiseWorkspaceChangedEventAsync method. This method allows callers to indicate they need to be called immediately upon the workspace change. As noted by a doc comment in the PR, these handlers should be very fast. --- .../Core/Remote/SolutionChecksumUpdater.cs | 140 ++++++++++-------- .../Services/ServiceHubServicesTests.cs | 2 +- .../Portable/Workspace/Workspace_Events.cs | 43 +++++- .../IRemoteAssetSynchronizationService.cs | 5 +- .../RemoteAssetSynchronizationService.cs | 37 ++++- .../Compiler/Core/Log/FunctionId.cs | 5 + .../Compiler/Core/Utilities/EventMap.cs | 21 +-- 7 files changed, 166 insertions(+), 87 deletions(-) diff --git a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs index 1ed0f29c3c823..70dabe4a7aa68 100644 --- a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs +++ b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs @@ -3,16 +3,14 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Notification; -using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Telemetry; using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; @@ -34,13 +32,6 @@ internal sealed class SolutionChecksumUpdater private readonly IDocumentTrackingService _documentTrackingService; - /// - /// Queue to push out text changes in a batched fashion when we hear about them. Because these should be short - /// operations (only syncing text changes) we don't cancel this when we enter the paused state. We simply don't - /// start queuing more requests into this until we become unpaused. - /// - private readonly AsyncBatchingWorkQueue<(Document oldDocument, Document newDocument)> _textChangeQueue; - /// /// Queue for kicking off the work to synchronize the primary workspace's solution. /// @@ -54,6 +45,13 @@ internal sealed class SolutionChecksumUpdater private readonly object _gate = new(); private bool _isSynchronizeWorkspacePaused; + private readonly CancellationToken _shutdownToken; + + private const string SynchronizeTextChangesStatusSucceededMetricName = "SucceededCount"; + private const string SynchronizeTextChangesStatusFailedMetricName = "FailedCount"; + private const string SynchronizeTextChangesStatusSucceededKeyName = nameof(SolutionChecksumUpdater) + "." + SynchronizeTextChangesStatusSucceededMetricName; + private const string SynchronizeTextChangesStatusFailedKeyName = nameof(SolutionChecksumUpdater) + "." + SynchronizeTextChangesStatusFailedMetricName; + public SolutionChecksumUpdater( Workspace workspace, IAsynchronousOperationListenerProvider listenerProvider, @@ -66,20 +64,14 @@ public SolutionChecksumUpdater( _workspace = workspace; _documentTrackingService = workspace.Services.GetRequiredService(); + _shutdownToken = shutdownToken; + _synchronizeWorkspaceQueue = new AsyncBatchingWorkQueue( DelayTimeSpan.NearImmediate, SynchronizePrimaryWorkspaceAsync, listener, shutdownToken); - // Text changes and active doc info are tiny messages. So attempt to send them immediately. Just batching - // things up if we get a flurry of notifications. - _textChangeQueue = new AsyncBatchingWorkQueue<(Document oldDocument, Document newDocument)>( - TimeSpan.Zero, - SynchronizeTextChangesAsync, - listener, - shutdownToken); - _synchronizeActiveDocumentQueue = new AsyncBatchingWorkQueue( TimeSpan.Zero, SynchronizeActiveDocumentAsync, @@ -88,6 +80,7 @@ public SolutionChecksumUpdater( // start listening workspace change event _workspace.WorkspaceChanged += OnWorkspaceChanged; + _workspace.WorkspaceChangedImmediate += OnWorkspaceChangedImmediate; _documentTrackingService.ActiveDocumentChanged += OnActiveDocumentChanged; if (_globalOperationService != null) @@ -108,6 +101,7 @@ public void Shutdown() _documentTrackingService.ActiveDocumentChanged -= OnActiveDocumentChanged; _workspace.WorkspaceChanged -= OnWorkspaceChanged; + _workspace.WorkspaceChangedImmediate -= OnWorkspaceChangedImmediate; if (_globalOperationService != null) { @@ -144,14 +138,6 @@ private void ResumeSynchronizingPrimaryWorkspace() private void OnWorkspaceChanged(object? sender, WorkspaceChangeEventArgs e) { - if (e.Kind == WorkspaceChangeKind.DocumentChanged) - { - var oldDocument = e.OldSolution.GetDocument(e.DocumentId); - var newDocument = e.NewSolution.GetDocument(e.DocumentId); - if (oldDocument != null && newDocument != null) - _textChangeQueue.AddWork((oldDocument, newDocument)); - } - // Check if we're currently paused. If so ignore this notification. We don't want to any work in response // to whatever the workspace is doing. lock (_gate) @@ -161,6 +147,20 @@ private void OnWorkspaceChanged(object? sender, WorkspaceChangeEventArgs e) } } + private void OnWorkspaceChangedImmediate(object? sender, WorkspaceChangeEventArgs e) + { + if (e.Kind == WorkspaceChangeKind.DocumentChanged) + { + var documentId = e.DocumentId!; + var oldDocument = e.OldSolution.GetRequiredDocument(documentId); + var newDocument = e.NewSolution.GetRequiredDocument(documentId); + + // Fire-and-forget to dispatch notification of this document change event to the remote side + // and return to the caller as quickly as possible. + _ = DispatchSynchronizeTextChangesAsync(oldDocument, newDocument).ReportNonFatalErrorAsync(); + } + } + private void OnActiveDocumentChanged(object? sender, DocumentId? e) => _synchronizeActiveDocumentQueue.AddWork(); @@ -203,57 +203,81 @@ await client.TryInvokeAsync( cancellationToken).ConfigureAwait(false); } - private async ValueTask SynchronizeTextChangesAsync( - ImmutableSegmentedList<(Document oldDocument, Document newDocument)> values, - CancellationToken cancellationToken) + private async Task DispatchSynchronizeTextChangesAsync( + Document oldDocument, + Document newDocument) { - var client = await RemoteHostClient.TryGetClientAsync(_workspace, cancellationToken).ConfigureAwait(false); - if (client == null) + // Explicitly force a yield point here to ensure this method returns to the caller immediately and that + // all work is done off the calling thread. + await Task.Yield().ConfigureAwait(false); + + // Inform the remote asset synchronization service as quickly as possible + // about the text changes between oldDocument and newDocument. By doing this, we can + // reduce the likelihood of the remote side encountering an unknown checksum and + // requiring a synchronization of the full document contents. + var wasSynchronized = await DispatchSynchronizeTextChangesHelperAsync().ConfigureAwait(false); + if (wasSynchronized == null) return; - // this pushes text changes to the remote side if it can. this is purely perf optimization. whether this - // pushing text change worked or not doesn't affect feature's functionality. - // - // this basically see whether it can cheaply find out text changes between 2 snapshots, if it can, it will - // send out that text changes to remote side. - // - // the remote side, once got the text change, will again see whether it can use that text change information - // without any high cost and create new snapshot from it. - // - // otherwise, it will do the normal behavior of getting full text from VS side. this optimization saves - // times we need to do full text synchronization for typing scenario. - using var _ = ArrayBuilder<(DocumentId id, Checksum textChecksum, ImmutableArray changes, Checksum newTextChecksum)>.GetInstance(out var builder); - - foreach (var (oldDocument, newDocument) in values) + // Update aggregated telemetry with success status of sending the synchronization data. + var metricName = wasSynchronized.Value ? SynchronizeTextChangesStatusSucceededMetricName : SynchronizeTextChangesStatusFailedMetricName; + var keyName = wasSynchronized.Value ? SynchronizeTextChangesStatusSucceededKeyName : SynchronizeTextChangesStatusFailedKeyName; + TelemetryLogging.LogAggregatedCounter(FunctionId.ChecksumUpdater_SynchronizeTextChangesStatus, KeyValueLogMessage.Create(m => { + m[TelemetryLogging.KeyName] = keyName; + m[TelemetryLogging.KeyValue] = 1L; + m[TelemetryLogging.KeyMetricName] = metricName; + })); + + return; + + async Task DispatchSynchronizeTextChangesHelperAsync() + { + var client = await RemoteHostClient.TryGetClientAsync(_workspace, _shutdownToken).ConfigureAwait(false); + if (client == null) + { + // null return value indicates that we were unable to synchronize the text changes, but to not log + // telemetry against that inability as turning off OOP is not a failure. + return null; + } + + // this pushes text changes to the remote side if it can. this is purely perf optimization. whether this + // pushing text change worked or not doesn't affect feature's functionality. + // + // this basically see whether it can cheaply find out text changes between 2 snapshots, if it can, it will + // send out that text changes to remote side. + // + // the remote side, once got the text change, will again see whether it can use that text change information + // without any high cost and create new snapshot from it. + // + // otherwise, it will do the normal behavior of getting full text from VS side. this optimization saves + // times we need to do full text synchronization for typing scenario. if (!oldDocument.TryGetText(out var oldText) || !newDocument.TryGetText(out var newText)) { // we only support case where text already exist - continue; + return false; } // Avoid allocating text before seeing if we can bail out. var changeRanges = newText.GetChangeRanges(oldText).AsImmutable(); if (changeRanges.Length == 0) - continue; + return true; // no benefit here. pulling from remote host is more efficient if (changeRanges is [{ Span.Length: var singleChangeLength }] && singleChangeLength == oldText.Length) - continue; + return true; - var state = await oldDocument.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); - var newState = await newDocument.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); + var state = await oldDocument.State.GetStateChecksumsAsync(_shutdownToken).ConfigureAwait(false); + var newState = await newDocument.State.GetStateChecksumsAsync(_shutdownToken).ConfigureAwait(false); var textChanges = newText.GetTextChanges(oldText).AsImmutable(); - builder.Add((oldDocument.Id, state.Text, textChanges, newState.Text)); - } - if (builder.Count == 0) - return; + await client.TryInvokeAsync( + (service, cancellationToken) => service.SynchronizeTextChangesAsync(oldDocument.Id, state.Text, textChanges, newState.Text, cancellationToken), + _shutdownToken).ConfigureAwait(false); - await client.TryInvokeAsync( - (service, cancellationToken) => service.SynchronizeTextChangesAsync(builder.ToImmutableAndClear(), cancellationToken), - cancellationToken).ConfigureAwait(false); + return true; + } } } diff --git a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs index 2626d6f12bbf8..f061e876b2ab5 100644 --- a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs @@ -95,7 +95,7 @@ public async Task TestRemoteHostTextSynchronize() // sync await client.TryInvokeAsync( - (service, cancellationToken) => service.SynchronizeTextChangesAsync([(oldDocument.Id, oldState.Text, newText.GetTextChanges(oldText).AsImmutable(), newState.Text)], cancellationToken), + (service, cancellationToken) => service.SynchronizeTextChangesAsync(oldDocument.Id, oldState.Text, newText.GetTextChanges(oldText).AsImmutable(), newState.Text, cancellationToken), CancellationToken.None); // check that text already exist in remote side diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace_Events.cs b/src/Workspaces/Core/Portable/Workspace/Workspace_Events.cs index a54a64ccfc8bf..5811a90a8cfb8 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace_Events.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace_Events.cs @@ -19,6 +19,7 @@ public abstract partial class Workspace private readonly EventMap _eventMap = new(); private const string WorkspaceChangeEventName = "WorkspaceChanged"; + private const string WorkspaceChangedImmediateEventName = "WorkspaceChangedImmediate"; private const string WorkspaceFailedEventName = "WorkspaceFailed"; private const string DocumentOpenedEventName = "DocumentOpened"; private const string DocumentClosedEventName = "DocumentClosed"; @@ -42,6 +43,24 @@ public event EventHandler WorkspaceChanged } } + /// + /// An event raised *immediately* whenever the current solution is changed. Handlers + /// should be written to be very fast. Called on the same thread changing the workspace, + /// which may vary depending on the workspace. + /// + internal event EventHandler WorkspaceChangedImmediate + { + add + { + _eventMap.AddEventHandler(WorkspaceChangedImmediateEventName, value); + } + + remove + { + _eventMap.RemoveEventHandler(WorkspaceChangedImmediateEventName, value); + } + } + protected Task RaiseWorkspaceChangedEventAsync(WorkspaceChangeKind kind, Solution oldSolution, Solution newSolution, ProjectId projectId = null, DocumentId documentId = null) { if (newSolution == null) @@ -59,22 +78,34 @@ protected Task RaiseWorkspaceChangedEventAsync(WorkspaceChangeKind kind, Solutio projectId = documentId.ProjectId; } - var ev = GetEventHandlers(WorkspaceChangeEventName); + var args = new WorkspaceChangeEventArgs(kind, oldSolution, newSolution, projectId, documentId); + + var ev = GetEventHandlers(WorkspaceChangedImmediateEventName); + RaiseEventForHandlers(ev, args, FunctionId.Workspace_EventsImmediate); + + ev = GetEventHandlers(WorkspaceChangeEventName); if (ev.HasHandlers) { return this.ScheduleTask(() => { - using (Logger.LogBlock(FunctionId.Workspace_Events, (s, p, d, k) => $"{s.Id} - {p} - {d} {kind.ToString()}", newSolution, projectId, documentId, kind, CancellationToken.None)) - { - var args = new WorkspaceChangeEventArgs(kind, oldSolution, newSolution, projectId, documentId); - ev.RaiseEvent(static (handler, arg) => handler(arg.self, arg.args), (self: this, args)); - } + RaiseEventForHandlers(ev, args, FunctionId.Workspace_Events); }, WorkspaceChangeEventName); } else { return Task.CompletedTask; } + + static void RaiseEventForHandlers( + EventMap.EventHandlerSet> handlers, + WorkspaceChangeEventArgs args, + FunctionId functionId) + { + using (Logger.LogBlock(functionId, (s, p, d, k) => $"{s.Id} - {p} - {d} {args.Kind.ToString()}", args.NewSolution, args.ProjectId, args.DocumentId, args.Kind, CancellationToken.None)) + { + handlers.RaiseEvent(static (handler, args) => handler(args.NewSolution.Workspace, args), args); + } + } } /// diff --git a/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs b/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs index 079bc05f93d52..1574b002ed4ad 100644 --- a/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs +++ b/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs @@ -26,7 +26,10 @@ internal interface IRemoteAssetSynchronizationService /// entire contents of the file over. /// ValueTask SynchronizeTextChangesAsync( - ImmutableArray<(DocumentId documentId, Checksum baseTextChecksum, ImmutableArray textChanges, Checksum newTextChecksum)> changes, + DocumentId documentId, + Checksum baseTextChecksum, + ImmutableArray textChanges, + Checksum newTextChecksum, CancellationToken cancellationToken); /// diff --git a/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs b/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs index f6cc7c147aab3..73d39d3d53a58 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs @@ -2,12 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Serialization; +using Microsoft.CodeAnalysis.Telemetry; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; using RoslynLogger = Microsoft.CodeAnalysis.Internal.Log.Logger; @@ -22,6 +22,11 @@ namespace Microsoft.CodeAnalysis.Remote; internal sealed class RemoteAssetSynchronizationService(in BrokeredServiceBase.ServiceConstructionArguments arguments) : BrokeredServiceBase(in arguments), IRemoteAssetSynchronizationService { + private const string SynchronizeTextChangesAsyncSucceededMetricName = "SucceededCount"; + private const string SynchronizeTextChangesAsyncFailedMetricName = "FailedCount"; + private const string SynchronizeTextChangesAsyncSucceededKeyName = nameof(RemoteAssetSynchronizationService) + "." + SynchronizeTextChangesAsyncSucceededMetricName; + private const string SynchronizeTextChangesAsyncFailedKeyName = nameof(RemoteAssetSynchronizationService) + "." + SynchronizeTextChangesAsyncFailedMetricName; + internal sealed class Factory : FactoryBase { protected override IRemoteAssetSynchronizationService CreateService(in ServiceConstructionArguments arguments) @@ -49,16 +54,32 @@ public ValueTask SynchronizeActiveDocumentAsync(DocumentId? documentId, Cancella } public ValueTask SynchronizeTextChangesAsync( - ImmutableArray<(DocumentId documentId, Checksum baseTextChecksum, ImmutableArray textChanges, Checksum newTextChecksum)> changes, + DocumentId documentId, + Checksum baseTextChecksum, + ImmutableArray textChanges, + Checksum newTextChecksum, CancellationToken cancellationToken) { return RunServiceAsync(async cancellationToken => { - var workspace = GetWorkspace(); + var wasSynchronized = await SynchronizeTextChangesHelperAsync().ConfigureAwait(false); + + var metricName = wasSynchronized ? SynchronizeTextChangesAsyncSucceededMetricName : SynchronizeTextChangesAsyncFailedMetricName; + var keyName = wasSynchronized ? SynchronizeTextChangesAsyncSucceededKeyName : SynchronizeTextChangesAsyncFailedKeyName; + TelemetryLogging.LogAggregatedCounter(FunctionId.RemoteHostService_SynchronizeTextAsyncStatus, KeyValueLogMessage.Create(m => + { + m[TelemetryLogging.KeyName] = keyName; + m[TelemetryLogging.KeyValue] = 1L; + m[TelemetryLogging.KeyMetricName] = metricName; + })); + + return; - using (RoslynLogger.LogBlock(FunctionId.RemoteHostService_SynchronizeTextAsync, cancellationToken)) + async Task SynchronizeTextChangesHelperAsync() { - foreach (var (documentId, baseTextChecksum, textChanges, newTextChecksum) in changes) + var workspace = GetWorkspace(); + + using (RoslynLogger.LogBlock(FunctionId.RemoteHostService_SynchronizeTextAsync, cancellationToken)) { // Try to get the text associated with baseTextChecksum var text = await TryGetSourceTextAsync(WorkspaceManager, workspace, documentId, baseTextChecksum, cancellationToken).ConfigureAwait(false); @@ -66,7 +87,7 @@ public ValueTask SynchronizeTextChangesAsync( { // it won't bring in base text if it is not there already. // text needed will be pulled in when there is request - continue; + return false; } // Now attempt to manually apply the edit, producing the new forked text. Store that directly in @@ -77,9 +98,9 @@ public ValueTask SynchronizeTextChangesAsync( WorkspaceManager.SolutionAssetCache.GetOrAdd(newSerializableText.ContentChecksum, newSerializableText); } - } - return; + return true; + } async static Task TryGetSourceTextAsync( RemoteWorkspaceManager workspaceManager, diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs index 4f123aa508e83..f2e0fc4f14204 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs @@ -639,4 +639,9 @@ internal enum FunctionId VSCode_LanguageServer_Started = 860, VSCode_Project_Load_Started = 861, VSCode_Projects_Load_Completed = 862, + + // 900-999 for items that don't fit into other categories. + Workspace_EventsImmediate = 900, + ChecksumUpdater_SynchronizeTextChangesStatus = 901, + RemoteHostService_SynchronizeTextAsyncStatus = 902, } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/EventMap.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/EventMap.cs index 35ca9ae0fc338..a0dac4f2f76ed 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/EventMap.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/EventMap.cs @@ -155,26 +155,21 @@ public readonly bool HasHandlers public readonly void RaiseEvent(Action invoker, TArg arg) { - // The try/catch here is to find additional telemetry for https://devdiv.visualstudio.com/DevDiv/_queries/query/71ee8553-7220-4b2a-98cf-20edab701fd1/. - // We've realized there's a problem with our eventing, where if an exception is encountered while calling into subscribers to Workspace events, - // we won't notify all of the callers. The expectation is such an exception would be thrown to the SafeStartNew in the workspace's event queue that - // will raise that as a fatal exception, but OperationCancelledExceptions might be able to propagate through and fault the task we are using in the - // chain. I'm choosing to use ReportWithoutCrashAndPropagate, because if our theory here is correct, it seems the first exception isn't actually - // causing crashes, and so if it turns out this is a very common situation I don't want to make a often-benign situation fatal. - try + if (this.HasHandlers) { - if (this.HasHandlers) + foreach (var registry in _registries) { - foreach (var registry in _registries) + try { registry.Invoke(invoker, arg); } + catch (Exception e) when (FatalError.ReportAndCatch(e)) + { + // Catch the exception and continue as this an event handler and propagating the exception would prevent + // other handlers from executing + } } } - catch (Exception e) when (FatalError.ReportAndPropagate(e)) - { - throw ExceptionUtilities.Unreachable(); - } } } } From 99d8eeb69f19385838bf4e15dbe9bfcb50edc0eb Mon Sep 17 00:00:00 2001 From: Ankita Khera <40616383+akhera99@users.noreply.github.com> Date: Mon, 10 Feb 2025 13:09:56 -0800 Subject: [PATCH 203/305] Update PublishData.json (#77140) --- eng/config/PublishData.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/eng/config/PublishData.json b/eng/config/PublishData.json index 2059299b840fa..ee344aed72e29 100644 --- a/eng/config/PublishData.json +++ b/eng/config/PublishData.json @@ -253,6 +253,16 @@ "insertionTitlePrefix": "[d17.14 P1]", "insertionCreateDraftPR": false }, + "release/dev18.0": { + "nugetKind": [ + "Shipping", + "NonShipping" + ], + "vsBranch": "dev/monicaro/versioning", + "vsMajorVersion": 18, + "insertionTitlePrefix": "[d18.0 P1]", + "insertionCreateDraftPR": false + }, "main": { "nugetKind": [ "Shipping", From 91f5069a5b51f1b42ecf020f980288ed66944428 Mon Sep 17 00:00:00 2001 From: Fred Silberberg Date: Mon, 10 Feb 2025 16:38:48 -0500 Subject: [PATCH 204/305] Do not build Microsoft.CodeAnalysis.ExternalAccess.Copilot during source-build (#77136) This is only used by VS, and causes errors during source-build due to it targeting net8.0. --- .../Copilot/Microsoft.CodeAnalysis.ExternalAccess.Copilot.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Features/ExternalAccess/Copilot/Microsoft.CodeAnalysis.ExternalAccess.Copilot.csproj b/src/Features/ExternalAccess/Copilot/Microsoft.CodeAnalysis.ExternalAccess.Copilot.csproj index 8735cf01ed4cd..a4361980e5d15 100644 --- a/src/Features/ExternalAccess/Copilot/Microsoft.CodeAnalysis.ExternalAccess.Copilot.csproj +++ b/src/Features/ExternalAccess/Copilot/Microsoft.CodeAnalysis.ExternalAccess.Copilot.csproj @@ -5,6 +5,7 @@ Library Microsoft.CodeAnalysis.ExternalAccess.Copilot $(NetVSShared);net472 + true true From 494c5e0cac0d99a25db1e6eb99349dc1bb48cf7c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 10 Feb 2025 14:17:26 -0800 Subject: [PATCH 205/305] Update docs --- .../DiagnosticIncrementalAnalyzer.CompilationManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs index bf7d35d92cd52..8c218f1c9f159 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs @@ -18,8 +18,8 @@ namespace Microsoft.CodeAnalysis.Diagnostics; internal partial class DiagnosticAnalyzerService { /// - /// Cached data from a to the last instance created - /// for it. Note: the CompilationWithAnalyzersPair instance is dependent on the set of to the last instance + /// created for it. Note: the CompilationWithAnalyzersPair instance is dependent on the set of s passed along with the project. As such, we might not be able to use a prior cached /// value if the set of analyzers changes. In that case, a new instance will be created and will be cached for the /// next caller. From 8d93475ae96daefa768642c202e8b35cb1a3e247 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 10 Feb 2025 14:17:52 -0800 Subject: [PATCH 206/305] PR feedback --- .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 847f66675d9e6..0ceeff1400fa6 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -48,8 +48,8 @@ public async Task> ForceAnalyzeProjectAsync(Proje // Try to add the new computed data to the CWT. But use any existing value that another thread // might have beaten us to storing in it. #if NET - _projectToForceAnalysisData.TryAdd(projectState, box); - Contract.ThrowIfFalse(_projectToForceAnalysisData.TryGetValue(projectState, out box)); + if (!_projectToForceAnalysisData.TryAdd(projectState, box)) + Contract.ThrowIfFalse(_projectToForceAnalysisData.TryGetValue(projectState, out box)); #else box = _projectToForceAnalysisData.GetValue(projectState, _ => box); #endif From a070adda066350f4d61c6a503bffe3085f2024c0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 10 Feb 2025 14:20:36 -0800 Subject: [PATCH 207/305] fix remote side --- .../Services/DiagnosticAnalyzer/DiagnosticComputer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs index 3a767508ac893..6ff32cf4ac6e1 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs @@ -356,7 +356,8 @@ private async Task GetDiagnosticsAsync( } } - var skippedAnalyzersInfo = _project.GetSkippedAnalyzersInfo(_analyzerInfoCache); + var skippedAnalyzersInfo = _project.Solution.SolutionState.Analyzers.GetSkippedAnalyzersInfo( + _project.State, _analyzerInfoCache); return await AnalyzeAsync(compilationWithAnalyzers, analyzerToIdMap, projectAnalyzers, hostAnalyzers, skippedAnalyzersInfo, logPerformanceInfo, getTelemetryInfo, cancellationToken).ConfigureAwait(false); From ab3ae969334deed1b8f66fa54a4568253fe967fe Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Mon, 10 Feb 2025 14:31:11 -0800 Subject: [PATCH 208/305] Report LSP diagnostics with their severity. --- .../Protocol/Extensions/ProtocolConversions.Diagnostics.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/LanguageServer/Protocol/Extensions/ProtocolConversions.Diagnostics.cs b/src/LanguageServer/Protocol/Extensions/ProtocolConversions.Diagnostics.cs index 9d084b8076d00..c6f8461425fd4 100644 --- a/src/LanguageServer/Protocol/Extensions/ProtocolConversions.Diagnostics.cs +++ b/src/LanguageServer/Protocol/Extensions/ProtocolConversions.Diagnostics.cs @@ -213,8 +213,7 @@ private static LSP.DiagnosticSeverity ConvertDiagnosticSeverity(DiagnosticSeveri // Hidden is translated in ConvertTags to pass along appropriate _ms tags // that will hide the item in a client that knows about those tags. DiagnosticSeverity.Hidden => LSP.DiagnosticSeverity.Hint, - // VSCode shows information diagnostics as blue squiggles, and hint diagnostics as 3 dots. We prefer the latter rendering so we return hint diagnostics in vscode. - DiagnosticSeverity.Info => supportsVisualStudioExtensions ? LSP.DiagnosticSeverity.Information : LSP.DiagnosticSeverity.Hint, + DiagnosticSeverity.Info => LSP.DiagnosticSeverity.Information, DiagnosticSeverity.Warning => LSP.DiagnosticSeverity.Warning, DiagnosticSeverity.Error => LSP.DiagnosticSeverity.Error, _ => throw ExceptionUtilities.UnexpectedValue(severity), From 6b304767106ded2f209c98084e0e4d311f67971b Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Mon, 10 Feb 2025 16:28:18 -0800 Subject: [PATCH 209/305] Remove unneeded test --- .../ProtocolConversions.Diagnostics.cs | 4 ++-- .../Diagnostics/PullDiagnosticTests.cs | 24 ------------------- 2 files changed, 2 insertions(+), 26 deletions(-) diff --git a/src/LanguageServer/Protocol/Extensions/ProtocolConversions.Diagnostics.cs b/src/LanguageServer/Protocol/Extensions/ProtocolConversions.Diagnostics.cs index c6f8461425fd4..5c30d938549e2 100644 --- a/src/LanguageServer/Protocol/Extensions/ProtocolConversions.Diagnostics.cs +++ b/src/LanguageServer/Protocol/Extensions/ProtocolConversions.Diagnostics.cs @@ -107,7 +107,7 @@ private static LSP.VSDiagnostic CreateLspDiagnostic( Code = diagnosticData.Id, CodeDescription = ProtocolConversions.HelpLinkToCodeDescription(diagnosticData.GetValidHelpLinkUri()), Message = diagnosticData.Message, - Severity = ConvertDiagnosticSeverity(diagnosticData.Severity, supportsVisualStudioExtensions), + Severity = ConvertDiagnosticSeverity(diagnosticData.Severity), Tags = ConvertTags(diagnosticData, isLiveSource, potentialDuplicate), DiagnosticRank = ConvertRank(diagnosticData), Range = GetRange(diagnosticData.DataLocation) @@ -207,7 +207,7 @@ private static bool ShouldIncludeHiddenDiagnostic(DiagnosticData diagnosticData, return null; } - private static LSP.DiagnosticSeverity ConvertDiagnosticSeverity(DiagnosticSeverity severity, bool supportsVisualStudioExtensions) + private static LSP.DiagnosticSeverity ConvertDiagnosticSeverity(DiagnosticSeverity severity) => severity switch { // Hidden is translated in ConvertTags to pass along appropriate _ms tags diff --git a/src/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs b/src/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs index a840823f2adc5..bc9e70c7fd195 100644 --- a/src/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs @@ -870,30 +870,6 @@ public async Task TestInfoDiagnosticsAreReportedAsInformationInVS(bool mutatingL Assert.Equal(LSP.DiagnosticSeverity.Information, results.Single().Diagnostics.Single().Severity); } - [Theory, CombinatorialData] - public async Task TestInfoDiagnosticsAreReportedAsHintInVSCode(bool mutatingLspWorkspace) - { - var markup = -@"class A -{ - public A SomeA = new A(); -}"; - await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: false); - - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); - - await OpenDocumentAsync(testLspServer, document); - - var results = await RunGetDocumentPullDiagnosticsAsync( - testLspServer, document.GetURI(), useVSDiagnostics: false); - - Assert.Equal("IDE0090", results.Single().Diagnostics.Single().Code); - Assert.Equal(LSP.DiagnosticSeverity.Hint, results.Single().Diagnostics.Single().Severity); - } - #endregion #region Workspace Diagnostics From a3b817dcd0151766f015830997aca2fd98edfc9d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 10 Feb 2025 16:43:22 -0800 Subject: [PATCH 210/305] Use raw strings in tests --- .../Diagnostics/PullDiagnosticTests.cs | 711 ++++++++++-------- 1 file changed, 378 insertions(+), 333 deletions(-) diff --git a/src/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs b/src/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs index a840823f2adc5..1adebaac24d91 100644 --- a/src/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs @@ -35,8 +35,7 @@ public sealed class PullDiagnosticTests(ITestOutputHelper testOutputHelper) : Ab [Theory, CombinatorialData] public async Task TestNoDocumentDiagnosticsForClosedFilesWithFSAOff(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var markup = -@"class A {"; + var markup = @"class A {"; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); @@ -54,8 +53,7 @@ public async Task TestNoDocumentDiagnosticsForClosedFilesWithFSAOff(bool useVSDi [Theory, CombinatorialData] public async Task TestDocumentDiagnosticsForOpenFilesWithFSAOff(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var markup = -@"class A {"; + var markup = @"class A {"; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); // Calling GetTextBuffer will effectively open the file. @@ -75,8 +73,7 @@ public async Task TestDocumentDiagnosticsForOpenFilesWithFSAOff(bool useVSDiagno [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/fsharp/issues/15972")] public async Task TestDocumentDiagnosticsForOpenFilesWithFSAOff_Categories(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var markup = -@"class A : B {"; + var markup = @"class A : B {"; var additionalAnalyzers = new DiagnosticAnalyzer[] { new CSharpSyntaxAnalyzer(), new CSharpSemanticAnalyzer() }; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( @@ -157,12 +154,14 @@ public override void Initialize(AnalysisContext context) public async Task TestDocumentDiagnosticsHasVSExpandedMessage(bool mutatingLspWorkspace) { var markup = -@"internal class Program -{ - static void Main(string[] args) - { - } -}"; + """ + internal class Program + { + static void Main(string[] args) + { + } + } + """; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: true); // Calling GetTextBuffer will effectively open the file. @@ -182,8 +181,7 @@ static void Main(string[] args) [Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2050705")] public async Task TestDocumentDiagnosticsUsesNullForExpandedMessage(bool mutatingLspWorkspace) { - var markup = -@"class A {"; + var markup = @"class A {"; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: true); // Calling GetTextBuffer will effectively open the file. @@ -205,10 +203,11 @@ public async Task TestDocumentDiagnosticsUsesNullForExpandedMessage(bool mutatin public async Task TestDocumentTodoCommentsDiagnosticsForOpenFile_Category(bool mutatingLspWorkspace) { var markup = -@" -// todo: goo -class A { -}"; + """ + // todo: goo + class A { + } + """; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: true); // Calling GetTextBuffer will effectively open the file. @@ -228,8 +227,7 @@ class A { [Theory, CombinatorialData] public async Task TestDocumentDiagnosticsForOpenFilesIfDefaultAndFeatureFlagOn(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var markup = -@"class A {"; + var markup = @"class A {"; await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, GetInitializationOptions(BackgroundAnalysisScope.OpenFiles, CompilerDiagnosticsScope.OpenFiles, useVSDiagnostics)); @@ -245,8 +243,7 @@ public async Task TestDocumentDiagnosticsForOpenFilesIfDefaultAndFeatureFlagOn(b [Theory, CombinatorialData] public async Task TestDocumentDiagnosticsForRemovedDocument(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var markup = -@"class A {"; + var markup = @"class A {"; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); var workspace = testLspServer.TestWorkspace; @@ -278,8 +275,7 @@ public async Task TestDocumentDiagnosticsForRemovedDocument(bool useVSDiagnostic [Theory, CombinatorialData] public async Task TestNoChangeIfDocumentDiagnosticsCalledTwice(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var markup = -@"class A {"; + var markup = @"class A {"; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); // Calling GetTextBuffer will effectively open the file. @@ -305,8 +301,7 @@ public async Task TestNoChangeIfDocumentDiagnosticsCalledTwice(bool useVSDiagnos [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1481208")] public async Task TestDocumentDiagnosticsWhenGlobalStateChanges(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var markup = -@"class A {"; + var markup = @"class A {"; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); // Calling GetTextBuffer will effectively open the file. @@ -336,8 +331,7 @@ public async Task TestDocumentDiagnosticsWhenGlobalStateChanges(bool useVSDiagno [Theory, CombinatorialData] public async Task TestDocumentDiagnosticsRemovedAfterErrorIsFixed(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var markup = -@"class A {"; + var markup = @"class A {"; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); // Calling GetTextBuffer will effectively open the file. @@ -359,8 +353,7 @@ public async Task TestDocumentDiagnosticsRemovedAfterErrorIsFixed(bool useVSDiag [Theory, CombinatorialData] public async Task TestDocumentDiagnosticsRemainAfterErrorIsNotFixed(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var markup = -@"class A {"; + var markup = @"class A {"; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); // Calling GetTextBuffer will effectively open the file. @@ -388,8 +381,10 @@ public async Task TestDocumentDiagnosticsRemainAfterErrorIsNotFixed(bool useVSDi public async Task TestDocumentDiagnosticsAreNotMapped(bool useVSDiagnostics, bool mutatingLspWorkspace) { var markup = -@"#line 1 ""test.txt"" -class A {"; + """ + #line 1 "test.txt" + class A { + """; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); // Calling GetTextBuffer will effectively open the file. @@ -409,8 +404,7 @@ class A {"; [Theory, CombinatorialData] public async Task TestStreamingDocumentDiagnostics(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var markup = -@"class A {"; + var markup = @"class A {"; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); // Calling GetTextBuffer will effectively open the file. @@ -429,19 +423,23 @@ public async Task TestStreamingDocumentDiagnostics(bool useVSDiagnostics, bool m public async Task TestDocumentDiagnosticsForOpenFilesUsesActiveContext(bool useVSDiagnostics, bool mutatingLspWorkspace) { var documentText = -@"#if ONE -class A { -#endif -class B {"; + """ + #if ONE + class A { + #endif + class B { + """; var workspaceXml = -@$" - - {documentText} - - - {documentText} - -"; + $""" + + + {documentText} + + + {documentText} + + + """; await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); @@ -480,17 +478,18 @@ class B {"; [Theory, CombinatorialData] public async Task TestDocumentDiagnosticsHasSameIdentifierForLinkedFile(bool mutatingLspWorkspace) { - var documentText = -@"class A { err }"; + var documentText = @"class A { err }"; var workspaceXml = -@$" - - {documentText} - - - {documentText} - -"; + $""" + + + {documentText} + + + {documentText} + + + """; await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: false); @@ -525,26 +524,32 @@ static VSTextDocumentIdentifier GetVsTextDocumentIdentifier(Document document) public async Task TestDocumentDiagnosticsWithChangeInReferencedProject(bool useVSDiagnostics, bool mutatingLspWorkspace) { var markup1 = -@"namespace M -{ - class A : B { } -}"; + """ + namespace M + { + class A : B { } + } + """; var markup2 = -@"namespace M -{ - public class {|caret:|} { } -}"; + """ + namespace M + { + public class {|caret:|} { } + } + """; var workspaceXml = -@$" - - {markup1} - CSProj2 - - - {markup2} - -"; + $""" + + + {markup1} + CSProj2 + + + {markup2} + + + """; await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); var csproj1Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj1").Single().Documents.First(); @@ -572,25 +577,31 @@ public class {|caret:|} { } public async Task TestDocumentDiagnosticsWithChangeInNotReferencedProject(bool useVSDiagnostics, bool mutatingLspWorkspace) { var markup1 = -@"namespace M -{ - class A : B { } -}"; + """ + namespace M + { + class A : B { } + } + """; var markup2 = -@"namespace M -{ - public class {|caret:|} { } -}"; + """ + namespace M + { + public class {|caret:|} { } + } + """; var workspaceXml = -@$" - - {markup1} - - - {markup2} - -"; + $""" + + + {markup1} + + + {markup2} + + + """; await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); var csproj1Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj1").Single().Documents.First(); @@ -618,8 +629,7 @@ public class {|caret:|} { } [Theory, CombinatorialData] public async Task TestDocumentDiagnosticsFromRazorServer(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var markup = -@"class A {"; + var markup = @"class A {"; await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, GetInitializationOptions(BackgroundAnalysisScope.OpenFiles, CompilerDiagnosticsScope.OpenFiles, useVSDiagnostics, WellKnownLspServerKinds.RazorLspServer)); @@ -642,8 +652,7 @@ public async Task TestDocumentDiagnosticsFromRazorServer(bool useVSDiagnostics, [Theory, CombinatorialData] public async Task TestDocumentDiagnosticsFromLiveShareServer(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var markup = -@"class A {"; + var markup = @"class A {"; await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, GetInitializationOptions(BackgroundAnalysisScope.OpenFiles, CompilerDiagnosticsScope.OpenFiles, useVSDiagnostics, WellKnownLspServerKinds.LiveShareLspServer)); @@ -694,12 +703,13 @@ public async Task TestDocumentDiagnosticsIncludesSourceGeneratorDiagnostics(bool public async Task TestDocumentDiagnosticsWithFadingOptionOn(bool useVSDiagnostics, bool mutatingLspWorkspace) { var markup = -@" -{|first:using System.Linq; -using System.Threading;|} -class A -{ -}"; + """ + {|first:using System.Linq; + using System.Threading;|} + class A + { + } + """; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); var firstLocation = testLspServer.GetLocations("first").Single().Range; testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption(FadingOptions.FadeOutUnusedImports, LanguageNames.CSharp, true); @@ -733,12 +743,13 @@ class A public async Task TestDocumentDiagnosticsWithFadingOptionOff(bool useVSDiagnostics, bool mutatingLspWorkspace) { var markup = -@" -{|first:using System.Linq; -using System.Threading;|} -class A -{ -}"; + """ + {|first:using System.Linq; + using System.Threading;|} + class A + { + } + """; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); var firstLocation = testLspServer.GetLocations("first").Single().Range; testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption(FadingOptions.FadeOutUnusedImports, LanguageNames.CSharp, false); @@ -757,14 +768,16 @@ class A public async Task TestDocumentDiagnosticsWithNotConfigurableFading(bool useVSDiagnostics, bool mutatingLspWorkspace) { var markup = -@"class A -{ - void M() - { - _ = {|line:{|open:(|}1 + 2 +|} - 3 + 4{|close:)|}; - } -}"; + """ + class A + { + void M() + { + _ = {|line:{|open:(|}1 + 2 +|} + 3 + 4{|close:)|}; + } + } + """; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); var openLocation = testLspServer.GetLocations("open").Single().Range; var closeLocation = testLspServer.GetLocations("close").Single().Range; @@ -824,16 +837,17 @@ public async Task TestDocumentDiagnosticsForUnnecessarySuppressions(bool useVSDi [Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1824321")] public async Task TestDocumentDiagnosticsForSourceSuppressions(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var markup = @" -class C -{ - void M() - { -#pragma warning disable CS0168 // Variable is declared but never used - int x; -#pragma warning restore CS0168 // Variable is declared but never used - } -}"; + var markup = """ + class C + { + void M() + { + #pragma warning disable CS0168 // Variable is declared but never used + int x; + #pragma warning restore CS0168 // Variable is declared but never used + } + } + """; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); @@ -850,10 +864,12 @@ void M() public async Task TestInfoDiagnosticsAreReportedAsInformationInVS(bool mutatingLspWorkspace) { var markup = -@"class A -{ - public A SomeA = new A(); -}"; + """ + class A + { + public A SomeA = new A(); + } + """; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: true); // Calling GetTextBuffer will effectively open the file. @@ -874,10 +890,12 @@ public async Task TestInfoDiagnosticsAreReportedAsInformationInVS(bool mutatingL public async Task TestInfoDiagnosticsAreReportedAsHintInVSCode(bool mutatingLspWorkspace) { var markup = -@"class A -{ - public A SomeA = new A(); -}"; + """ + class A + { + public A SomeA = new A(); + } + """; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: false); // Calling GetTextBuffer will effectively open the file. @@ -901,8 +919,7 @@ public async Task TestInfoDiagnosticsAreReportedAsHintInVSCode(bool mutatingLspW [Theory, CombinatorialData] public async Task TestNoWorkspaceDiagnosticsForClosedFilesWithFSAOff(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var markup1 = -@"class A {"; + var markup1 = @"class A {"; var markup2 = ""; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); @@ -915,8 +932,7 @@ public async Task TestNoWorkspaceDiagnosticsForClosedFilesWithFSAOff(bool useVSD [Theory, CombinatorialData] public async Task TestWorkspaceDiagnosticsForClosedFilesWithFSAOn(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var markup1 = -@"class A {"; + var markup1 = @"class A {"; var markup2 = ""; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); @@ -932,8 +948,7 @@ public async Task TestWorkspaceDiagnosticsForClosedFilesWithFSAOn(bool useVSDiag [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/65967")] public async Task TestWorkspaceDiagnosticsForClosedFilesWithRunCodeAnalysisAndFSAOff(bool useVSDiagnostics, bool mutatingLspWorkspace, bool scopeRunCodeAnalysisToProject) { - var markup1 = -@"class A {"; + var markup1 = @"class A {"; var markup2 = ""; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); @@ -973,8 +988,7 @@ public async Task TestWorkspaceDiagnosticsForClosedFilesWithRunCodeAnalysisAndFS [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/65967")] public async Task TestWorkspaceDiagnosticsForClosedFilesWithRunCodeAnalysisFSAOn(bool useVSDiagnostics, bool mutatingLspWorkspace, bool scopeRunCodeAnalysisToProject) { - var markup1 = -@"class A {"; + var markup1 = @"class A {"; var markup2 = ""; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); @@ -1040,10 +1054,11 @@ public async Task SourceGeneratorFailures_FSA(bool useVSDiagnostics, bool mutati public async Task TestWorkspaceTodoForClosedFilesWithFSAOffAndTodoOff(bool mutatingLspWorkspace) { var markup1 = -@" -// todo: goo -class A { -}"; + """ + // todo: goo + class A { + } + """; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( [markup1], mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: true); @@ -1056,10 +1071,11 @@ class A { public async Task TestWorkspaceTodoForClosedFilesWithFSAOffAndTodoOn(bool mutatingLspWorkspace) { var markup1 = -@" -// todo: goo -class A { -}"; + """ + // todo: goo + class A { + } + """; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( [markup1], mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: true); @@ -1083,10 +1099,11 @@ public async Task TestWorkspaceTodoForClosedFilesWithFSAOffAndTodoOn_Priorities( { var rank = (VSDiagnosticRank)intRank; var markup1 = -@" -// todo: goo -class A { -}"; + """ + // todo: goo + class A { + } + """; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( [markup1], mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: true); @@ -1106,10 +1123,11 @@ class A { public async Task TestWorkspaceTodoForClosedFilesWithFSAOnAndTodoOff(bool mutatingLspWorkspace) { var markup1 = -@" -// todo: goo -class A { -}"; + """ + // todo: goo + class A { + } + """; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( [markup1], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics: true); @@ -1122,10 +1140,11 @@ class A { public async Task TestWorkspaceTodoForClosedFilesWithFSAOnAndTodoOn(bool mutatingLspWorkspace) { var markup1 = -@" -// todo: goo -class A { -}"; + """ + // todo: goo + class A { + } + """; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( [markup1], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics: true); @@ -1141,10 +1160,11 @@ class A { public async Task TestWorkspaceTodoAndDiagnosticForClosedFilesWithFSAOnAndTodoOn(bool mutatingLspWorkspace) { var markup1 = -@" -// todo: goo -class A { -"; + """ + // todo: goo + class A { + + """; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( [markup1], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics: true); @@ -1298,8 +1318,7 @@ static string Inspect(TestDiagnosticResult result) [Theory, CombinatorialData] public async Task TestNoWorkspaceDiagnosticsForClosedFilesWithFSAOffWithFileInProjectOpen(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var markup1 = -@"class A {"; + var markup1 = @"class A {"; var markup2 = ""; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); @@ -1356,19 +1375,20 @@ public async Task TestWorkspaceDiagnosticsDoesNotIncludeSourceGeneratorDiagnosti [Theory, CombinatorialData] public async Task TestNoWorkspaceDiagnosticsForClosedFilesInProjectsWithIncorrectLanguage(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var csharpMarkup = -@"class A {"; + var csharpMarkup = @"class A {"; var typeScriptMarkup = "???"; var workspaceXml = -@$" - - {csharpMarkup} - - - {typeScriptMarkup} - -"; + $""" + + + {csharpMarkup} + + + {typeScriptMarkup} + + + """; await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); @@ -1380,8 +1400,7 @@ public async Task TestNoWorkspaceDiagnosticsForClosedFilesInProjectsWithIncorrec [Theory, CombinatorialData] public async Task TestWorkspaceDiagnosticsForSourceGeneratedFiles(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var markup1 = -@"class A {"; + var markup1 = @"class A {"; var markup2 = ""; await using var testLspServer = await CreateTestLspServerAsync( markups: [], mutatingLspWorkspace, @@ -1404,8 +1423,7 @@ public async Task TestWorkspaceDiagnosticsForSourceGeneratedFiles(bool useVSDiag [Theory, CombinatorialData] public async Task TestWorkspaceDiagnosticsForRemovedDocument(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var markup1 = -@"class A {"; + var markup1 = @"class A {"; var markup2 = ""; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); @@ -1431,8 +1449,7 @@ public async Task TestWorkspaceDiagnosticsForRemovedDocument(bool useVSDiagnosti [Theory, CombinatorialData] public async Task TestNoChangeIfWorkspaceDiagnosticsCalledTwice(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var markup1 = -@"class A {"; + var markup1 = @"class A {"; var markup2 = ""; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); @@ -1453,8 +1470,7 @@ public async Task TestNoChangeIfWorkspaceDiagnosticsCalledTwice(bool useVSDiagno [Theory, CombinatorialData] public async Task TestWorkspaceDiagnosticsRemovedAfterErrorIsFixed(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var markup1 = -@"class A {"; + var markup1 = @"class A {"; var markup2 = ""; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); @@ -1480,8 +1496,7 @@ public async Task TestWorkspaceDiagnosticsRemovedAfterErrorIsFixed(bool useVSDia [Theory, CombinatorialData] public async Task TestWorkspaceDiagnosticsRemainAfterErrorIsNotFixed(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var markup1 = -@"class A {"; + var markup1 = @"class A {"; var markup2 = ""; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); @@ -1520,8 +1535,7 @@ public async Task TestWorkspaceDiagnosticsRemainAfterErrorIsNotFixed(bool useVSD [Theory, CombinatorialData] public async Task TestStreamingWorkspaceDiagnostics(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var markup1 = -@"class A {"; + var markup1 = @"class A {"; var markup2 = ""; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); @@ -1541,8 +1555,10 @@ public async Task TestStreamingWorkspaceDiagnostics(bool useVSDiagnostics, bool public async Task TestWorkspaceDiagnosticsAreNotMapped(bool useVSDiagnostics, bool mutatingLspWorkspace) { var markup1 = -@"#line 1 ""test.txt"" -class A {"; + """ + #line 1 "test.txt" + class A { + """; var markup2 = ""; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); @@ -1560,26 +1576,32 @@ class A {"; public async Task TestWorkspaceDiagnosticsWithChangeInReferencedProject(bool useVSDiagnostics, bool mutatingLspWorkspace) { var markup1 = -@"namespace M -{ - class A : B { } -}"; + """ + namespace M + { + class A : B { } + } + """; var markup2 = -@"namespace M -{ - public class {|caret:|} { } -}"; + """ + namespace M + { + public class {|caret:|} { } + } + """; var workspaceXml = -@$" - - {markup1} - CSProj2 - - - {markup2} - -"; + $""" + + + {markup1} + CSProj2 + + + {markup2} + + + """; await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); @@ -1619,41 +1641,49 @@ public class {|caret:|} { } public async Task TestWorkspaceDiagnosticsWithChangeInRecursiveReferencedProject(bool useVSDiagnostics, bool mutatingLspWorkspace) { var markup1 = -@"namespace M -{ - public class A : B - { - } -}"; + """ + namespace M + { + public class A : B + { + } + } + """; var markup2 = -@"namespace M -{ - public class B : C - { - } -}"; + """ + namespace M + { + public class B : C + { + } + } + """; var markup3 = -@"namespace M -{ - public class {|caret:|} - { - } -}"; + """ + namespace M + { + public class {|caret:|} + { + } + } + """; var workspaceXml = -@$" - - CSProj2 - {markup1} - - - CSProj3 - {markup2} - - - {markup3} - -"; + $""" + + + CSProj2 + {markup1} + + + CSProj3 + {markup2} + + + {markup3} + + + """; await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); var csproj3Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj3").Single().Documents.First(); @@ -1703,25 +1733,31 @@ public class {|caret:|} public async Task TestWorkspaceDiagnosticsWithChangeInNotReferencedProject(bool useVSDiagnostics, bool mutatingLspWorkspace) { var markup1 = -@"namespace M -{ - class A : B { } -}"; + """ + namespace M + { + class A : B { } + } + """; var markup2 = -@"namespace M -{ - public class {|caret:|} { } -}"; + """ + namespace M + { + public class {|caret:|} { } + } + """; var workspaceXml = -@$" - - {markup1} - - - {markup2} - -"; + $""" + + + {markup1} + + + {markup2} + + + """; await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); @@ -1753,40 +1789,43 @@ public class {|caret:|} { } AssertEx.Empty(results[0].Diagnostics); Assert.NotEqual(previousResultIds[2].resultId, results[0].ResultId); } -#if true -#else -#endif [Theory, CombinatorialData] public async Task TestWorkspaceDiagnosticsWithDependentProjectReloadedAndChanged(bool useVSDiagnostics, bool mutatingLspWorkspace) { var markup1 = -@"namespace M -{ - class A : B { } -}"; + """ + namespace M + { + class A : B { } + } + """; var markup2 = -@"namespace M -{ - public class B - { - public static void Do() - { - int unusedVariable; - } - } -}"; + """ + namespace M + { + public class B + { + public static void Do() + { + int unusedVariable; + } + } + } + """; var workspaceXml = -@$" - - {markup1} - CSProj2 - - - {markup2} - -"; + $""" + + + {markup1} + CSProj2 + + + {markup2} + + + """; await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); @@ -1824,26 +1863,32 @@ public static void Do() public async Task TestWorkspaceDiagnosticsWithDependentProjectReloadedUnchanged(bool useVSDiagnostics, bool mutatingLspWorkspace) { var markup1 = -@"namespace M -{ - class A : B { } -}"; + """ + namespace M + { + class A : B { } + } + """; var markup2 = -@"namespace M -{ - public class {|caret:|} { } -}"; + """ + namespace M + { + public class {|caret:|} { } + } + """; var workspaceXml = -@$" - - {markup1} - CSProj2 - - - {markup2} - -"; + $""" + + + {markup1} + CSProj2 + + + {markup2} + + + """; await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); @@ -1877,25 +1922,29 @@ public class {|caret:|} { } public async Task TestWorkspaceDiagnosticsOrderOfReferencedProjectsReloadedDoesNotMatter(bool useVSDiagnostics, bool mutatingLspWorkspace) { var markup1 = -@"namespace M -{ - class A : B { } -}"; + """ + namespace M + { + class A : B { } + } + """; var workspaceXml = -@$" - - {markup1} - CSProj2 - CSProj3 - - - - - - - -"; + $""" + + + {markup1} + CSProj2 + CSProj3 + + + + + + + + + """; await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); @@ -1928,17 +1977,18 @@ class A : B { } [Theory, CombinatorialData] public async Task TestWorkspaceDiagnosticsDoesNotThrowIfProjectWithoutFilePathExists(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var csharpMarkup = -@"class A {"; + var csharpMarkup = @"class A {"; var workspaceXml = -@$" - - {csharpMarkup} - - - - -"; + $""" + + + {csharpMarkup} + + + + + + """; await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); @@ -1953,8 +2003,7 @@ public async Task TestWorkspaceDiagnosticsDoesNotThrowIfProjectWithoutFilePathEx [Theory, CombinatorialData] public async Task TestWorkspaceDiagnosticsWaitsForLspTextChanges(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var markup1 = -@"class A {"; + var markup1 = @"class A {"; var markup2 = ""; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); @@ -1982,8 +2031,7 @@ public async Task TestWorkspaceDiagnosticsWaitsForLspTextChanges(bool useVSDiagn [Theory, CombinatorialData] public async Task TestWorkspaceDiagnosticsWaitsForLspSolutionChanges(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var markup1 = -@"class A {"; + var markup1 = @"class A {"; var markup2 = ""; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); @@ -2011,8 +2059,7 @@ public async Task TestWorkspaceDiagnosticsWaitsForLspSolutionChanges(bool useVSD [Theory(Skip = "https://github.com/dotnet/roslyn/issues/76503"), CombinatorialData] public async Task TestWorkspaceDiagnosticsWaitsForLspTextChangesWithMultipleSources(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var markup1 = -@"class A {"; + var markup1 = @"class A {"; var markup2 = ""; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); @@ -2070,8 +2117,7 @@ public async Task TestWorkspaceDiagnosticsWaitsForLspTextChangesWithMultipleSour [Theory, CombinatorialData] public async Task TestWorkspaceDiagnosticsForClosedFilesSwitchFSAFromOnToOff(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var markup1 = -@"class A {"; + var markup1 = @"class A {"; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( [markup1], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); @@ -2108,8 +2154,7 @@ public async Task TestWorkspaceDiagnosticsForClosedFilesSwitchFSAFromOnToOff(boo [Theory, CombinatorialData] public async Task TestWorkspaceDiagnosticsForClosedFilesSwitchFSAFromOffToOn(bool useVSDiagnostics, bool mutatingLspWorkspace) { - var markup1 = -@"class A {"; + var markup1 = @"class A {"; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( [markup1], mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); From a6155bb69829615791b44ce534ba2030275e166a Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Mon, 10 Feb 2025 17:44:14 -0800 Subject: [PATCH 211/305] Fix race during shutdown of the BuildHost server Our RPC mechanism to the build host is a simple one: we send JSON payloads that are formatted as a single textual line, similar to other methods. We did the writing to the build host via a TextWriter, and called Flush() at the end to ensure that everything is written to the other end, otherwise things might deadlock if we wait for the server to reply to something that's not coming. We recently switched the communication over to named pipes, which have a bit of a subtle problem here: with PipeStream, Flush() doesn't do anything, but it will check the pipe is still connected and throw if it's not. This had a problem during shutdown: we might write to the pipe, and the other side will see the shutdown request, respond, and close, before that Flush() call could ever happen. And once it did, we'd then throw an exception becase the other end did exactly what wanted it to! I could special-case the handling of pipes in the RPC code, but there's no reason to allow the use of arbitrary streams here. Instead, it's best to just update the tests to also use named pipes (which means we're better testing what we're doing in real code), and it also means we can dispense of flushing entirely. Unfortunately TextWriter gives no way to disable flushing that I can see, so I also dispose of TextWriter and just write to the PipeStream directly. Fix https://github.com/dotnet/roslyn/issues/77040 --- src/Workspaces/MSBuild/BuildHost/Program.cs | 2 +- .../MSBuild/BuildHost/Rpc/RpcServer.cs | 17 +++---- .../Core/MSBuild/BuildHostProcessManager.cs | 2 +- src/Workspaces/MSBuild/Core/Rpc/RpcClient.cs | 37 +++++++++------ src/Workspaces/MSBuild/Test/RpcTests.cs | 47 ++++++++++++++----- 5 files changed, 69 insertions(+), 36 deletions(-) diff --git a/src/Workspaces/MSBuild/BuildHost/Program.cs b/src/Workspaces/MSBuild/BuildHost/Program.cs index 48605a392742a..6f12720481727 100644 --- a/src/Workspaces/MSBuild/BuildHost/Program.cs +++ b/src/Workspaces/MSBuild/BuildHost/Program.cs @@ -52,7 +52,7 @@ internal static async Task Main(string[] args) var pipeServer = NamedPipeUtil.CreateServer(pipeName, PipeDirection.InOut); await pipeServer.WaitForConnectionAsync().ConfigureAwait(false); - var server = new RpcServer(sendingStream: pipeServer, receivingStream: pipeServer); + var server = new RpcServer(pipeServer); var targetObject = server.AddTarget(new BuildHost(logger, propertiesBuilder.ToImmutable(), binaryLogPath, server)); Contract.ThrowIfFalse(targetObject == 0, "The first object registered should have target 0, which is assumed by the client."); diff --git a/src/Workspaces/MSBuild/BuildHost/Rpc/RpcServer.cs b/src/Workspaces/MSBuild/BuildHost/Rpc/RpcServer.cs index a6a08b8ed3050..e161bc6fa0c2a 100644 --- a/src/Workspaces/MSBuild/BuildHost/Rpc/RpcServer.cs +++ b/src/Workspaces/MSBuild/BuildHost/Rpc/RpcServer.cs @@ -6,6 +6,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; +using System.IO.Pipes; using System.Reflection; using System.Threading; using System.Threading.Tasks; @@ -25,19 +26,19 @@ namespace Microsoft.CodeAnalysis.MSBuild; /// internal sealed class RpcServer { - private readonly TextWriter _sendingStream; + private readonly TextWriter _streamWriter; private readonly SemaphoreSlim _sendingStreamSemaphore = new SemaphoreSlim(initialCount: 1); - private readonly TextReader _receivingStream; + private readonly TextReader _streamReader; private readonly ConcurrentDictionary _rpcTargets = []; private volatile int _nextRpcTargetIndex = -1; // We'll start at -1 so the first value becomes zero private readonly CancellationTokenSource _shutdownTokenSource = new CancellationTokenSource(); - public RpcServer(Stream sendingStream, Stream receivingStream) + public RpcServer(PipeStream stream) { - _sendingStream = new StreamWriter(sendingStream, JsonSettings.StreamEncoding); - _receivingStream = new StreamReader(receivingStream, JsonSettings.StreamEncoding); + _streamWriter = new StreamWriter(stream, JsonSettings.StreamEncoding); + _streamReader = new StreamReader(stream, JsonSettings.StreamEncoding); } public int AddTarget(object rpcTarget) @@ -61,7 +62,7 @@ public async Task RunAsync() var runningTasks = new ConcurrentSet(); string? line; - while ((line = await _receivingStream.TryReadLineOrReturnNullIfCancelledAsync(_shutdownTokenSource.Token).ConfigureAwait(false)) != null) + while ((line = await _streamReader.TryReadLineOrReturnNullIfCancelledAsync(_shutdownTokenSource.Token).ConfigureAwait(false)) != null) { Request? request; @@ -173,8 +174,8 @@ private async Task ProcessRequestAsync(Request request) #endif using (await _sendingStreamSemaphore.DisposableWaitAsync().ConfigureAwait(false)) { - await _sendingStream.WriteLineAsync(responseJson).ConfigureAwait(false); - await _sendingStream.FlushAsync().ConfigureAwait(false); + await _streamWriter.WriteLineAsync(responseJson).ConfigureAwait(false); + await _streamWriter.FlushAsync().ConfigureAwait(false); } } diff --git a/src/Workspaces/MSBuild/Core/MSBuild/BuildHostProcessManager.cs b/src/Workspaces/MSBuild/Core/MSBuild/BuildHostProcessManager.cs index 1592705b0962a..057a90fafd4bb 100644 --- a/src/Workspaces/MSBuild/Core/MSBuild/BuildHostProcessManager.cs +++ b/src/Workspaces/MSBuild/Core/MSBuild/BuildHostProcessManager.cs @@ -389,7 +389,7 @@ public BuildHostProcess(Process process, string pipeName, ILoggerFactory? logger throw new Exception("Ownership of BuildHost pipe is incorrect."); } - _rpcClient = new RpcClient(sendingStream: pipeClient, receivingStream: pipeClient); + _rpcClient = new RpcClient(pipeClient); _rpcClient.Start(); _rpcClient.Disconnected += Process_Exited; BuildHost = new RemoteBuildHost(_rpcClient); diff --git a/src/Workspaces/MSBuild/Core/Rpc/RpcClient.cs b/src/Workspaces/MSBuild/Core/Rpc/RpcClient.cs index 794030581ecbe..cc93ceb6ba251 100644 --- a/src/Workspaces/MSBuild/Core/Rpc/RpcClient.cs +++ b/src/Workspaces/MSBuild/Core/Rpc/RpcClient.cs @@ -6,6 +6,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; +using System.IO.Pipes; using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json; @@ -24,19 +25,23 @@ namespace Microsoft.CodeAnalysis.MSBuild; /// internal sealed class RpcClient { - private readonly TextWriter _sendingStream; - private readonly SemaphoreSlim _sendingStreamSemaphore = new SemaphoreSlim(initialCount: 1); - private readonly TextReader _receivingStream; + private readonly PipeStream _stream; + + /// + /// A semaphore taken to synchronize all writes to . + /// + private readonly SemaphoreSlim _streamWritingSemaphore = new SemaphoreSlim(initialCount: 1); + private readonly TextReader _receivingStreamReader; private readonly ConcurrentDictionary, System.Type? expectedReturnType)> _outstandingRequests = []; private volatile int _nextRequestId = 0; private readonly CancellationTokenSource _shutdownTokenSource = new CancellationTokenSource(); - public RpcClient(Stream sendingStream, Stream receivingStream) + public RpcClient(PipeStream stream) { - _sendingStream = new StreamWriter(sendingStream, JsonSettings.StreamEncoding); - _receivingStream = new StreamReader(receivingStream, JsonSettings.StreamEncoding); + _stream = stream; + _receivingStreamReader = new StreamReader(stream, JsonSettings.StreamEncoding); } public event EventHandler? Disconnected; @@ -50,7 +55,7 @@ public void Start() try { string? line; - while ((line = await _receivingStream.TryReadLineOrReturnNullIfCancelledAsync(_shutdownTokenSource.Token).ConfigureAwait(false)) != null) + while ((line = await _receivingStreamReader.TryReadLineOrReturnNullIfCancelledAsync(_shutdownTokenSource.Token).ConfigureAwait(false)) != null) { Response? response; try @@ -153,20 +158,22 @@ public async Task InvokeAsync(int targetObject, string methodName, List p is not null ? JToken.FromObject(p) : JValue.CreateNull()) }; - var requestJson = JsonConvert.SerializeObject(request, JsonSettings.SingleLineSerializerSettings); + var requestJson = JsonConvert.SerializeObject(request, JsonSettings.SingleLineSerializerSettings) + Environment.NewLine; + var requestJsonBytes = JsonSettings.StreamEncoding.GetBytes(requestJson); try { // The only cancellation we support is cancelling before we are able to write the request to the stream; once it's been written // the other side will execute it to completion. Thus cancellationToken is checked here, but nowhere else. - using (await _sendingStreamSemaphore.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) + using (await _streamWritingSemaphore.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) { - await _sendingStream.WriteLineAsync(requestJson).ConfigureAwait(false); -#if NET8_0_OR_GREATER - await _sendingStream.FlushAsync(CancellationToken.None).ConfigureAwait(false); -#else - await _sendingStream.FlushAsync().ConfigureAwait(false); -#endif + // Write out the request to the stream. This previously used a TextWriter and called Flush(), but it was discovered that wasn't safe + // around PipeStream: the TextWriter might call Flush() on the underlying stream, and PipeStream's Flush() implementation doesn't do + // anything other than check if the pipe was disconnected. This created a race condition during us trying to shutdown the build host: + // we'd send a shutdown message, but if TextWriter calls PipeStream.Flush() at any point (either because we ask it to flush, or it were + // to decide it wants to flush), it might do so once the pipe has disconnected and we'll get an IOException -- even though the other + // process did exactly what we wanted it to! By just writing to the pipe directly we avoid any surprises here. + await _stream.WriteAsync(requestJsonBytes, 0, requestJsonBytes.Length, cancellationToken).ConfigureAwait(false); } } catch (OperationCanceledException) diff --git a/src/Workspaces/MSBuild/Test/RpcTests.cs b/src/Workspaces/MSBuild/Test/RpcTests.cs index fd0e2a55f18ae..e8828e147f266 100644 --- a/src/Workspaces/MSBuild/Test/RpcTests.cs +++ b/src/Workspaces/MSBuild/Test/RpcTests.cs @@ -4,9 +4,10 @@ using System; using System.Collections.Generic; +using System.IO.Pipes; using System.Threading; using System.Threading.Tasks; -using Nerdbank.Streams; +using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.MSBuild.UnitTests @@ -18,19 +19,30 @@ public class RpcTests { private class RpcPair : IAsyncDisposable { - private readonly SimplexStream _serverToClientStream; - private readonly SimplexStream _clientToServerStream; + private readonly PipeStream _clientStream; + private readonly PipeStream _serverStream; public RpcPair() { - // Using two simplex streams here to better model the fact standard in and standard out are uni-directional - _serverToClientStream = new SimplexStream(); - _clientToServerStream = new SimplexStream(); + // Create a pipe server and connected client; we use pipes here to match the same way we communicate with the build host in the + // product since pipes can act a bit different than other types of streams, and that can lead to subtle bugs. + var pipeName = Guid.NewGuid().ToString(); - Server = new RpcServer(sendingStream: _serverToClientStream, receivingStream: _clientToServerStream); - Client = new RpcClient(sendingStream: _clientToServerStream, receivingStream: _serverToClientStream); + var pipeServer = NamedPipeUtil.CreateServer(pipeName, PipeDirection.InOut); + var pipeServerConnectionTask = pipeServer.WaitForConnectionAsync(); + var pipeClient = NamedPipeUtil.CreateClient(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous); - ServerCompletion = Server.RunAsync(); + pipeClient.Connect(); + pipeServerConnectionTask.Wait(); + + _clientStream = pipeClient; + _serverStream = pipeServer; + + Server = new RpcServer(_serverStream); + Client = new RpcClient(_clientStream); + + // Start the server, and shut the pipe down once it's done to simulate real behavior + ServerCompletion = Server.RunAsync().ContinueWith(_ => _serverStream.Dispose(), TaskScheduler.Default); Client.Start(); } @@ -40,8 +52,8 @@ public RpcPair() public async ValueTask DisposeAsync() { - _serverToClientStream.CompleteWriting(); - _clientToServerStream.CompleteWriting(); + _clientStream.Dispose(); + _serverStream.Dispose(); await ServerCompletion; } @@ -203,6 +215,18 @@ public async Task CancelledTaskDoesNotLeakRequest() Assert.Equal(0, rpcPair.Client.GetTestAccessor().GetOutstandingRequestCount()); } + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/77040")] + public async Task RequestThatClosesServerDoesNotThrow() + { + await using var rpcPair = new RpcPair(); + rpcPair.Server.AddTarget(new ObjectWithMethodThatShutsDownServer(rpcPair.Server)); + + // Invoke the method, and ensure we don't get exceptions back due to the shutdown if the pipe closed too early. + // This is not guaranteed to catch any bug, since any bug is fundamentally a race, but it at least ensures we aren't always broken. + await rpcPair.Client.InvokeAsync(targetObject: 0, nameof(ObjectWithMethodThatShutsDownServer.Shutdown), [], CancellationToken.None); + } + #pragma warning disable CA1822 // Mark members as static private sealed class ObjectWithHelloMethod { public string Hello(string name) { return "Hello " + name; } } @@ -260,6 +284,7 @@ public void Complete(int index) } private sealed class ObjectWithThrowingMethod { public void ThrowException() { throw new Exception("Exception thrown by test method!"); } } + private sealed class ObjectWithMethodThatShutsDownServer(RpcServer server) { public void Shutdown() { server.Shutdown(); } } #pragma warning restore CA1822 // Mark members as static } From 90522a12de540ddf83645abcc320e59875d5f305 Mon Sep 17 00:00:00 2001 From: Ankita Khera <40616383+akhera99@users.noreply.github.com> Date: Mon, 10 Feb 2025 18:59:21 -0800 Subject: [PATCH 212/305] Add main to release/dev18.0 flow (#77148) * add main to release/dev18.0 flow * add workflow-dispatch --- .config/branch-merge.json | 9 +++++++++ .github/workflows/main-merge.yml | 18 ++++++++++++++++++ azure-pipelines-official.yml | 1 + 3 files changed, 28 insertions(+) create mode 100644 .config/branch-merge.json create mode 100644 .github/workflows/main-merge.yml diff --git a/.config/branch-merge.json b/.config/branch-merge.json new file mode 100644 index 0000000000000..0b1db28cc334a --- /dev/null +++ b/.config/branch-merge.json @@ -0,0 +1,9 @@ +{ + "merge-flow-configurations": { + // Merge any main changes to release/dev18.0. + "main": { + "MergeToBranch": "release/dev18.0", + "ExtraSwitches": "-QuietComments" + } + } +} \ No newline at end of file diff --git a/.github/workflows/main-merge.yml b/.github/workflows/main-merge.yml new file mode 100644 index 0000000000000..21dfaefe4cd4c --- /dev/null +++ b/.github/workflows/main-merge.yml @@ -0,0 +1,18 @@ +# Merges any changes from release/prerelease to main (e.g. servicing changes) + +name: Flow main to release/dev18.0 +on: + schedule: + # once a day at 13:00 UTC to cleanup old runs + - cron: '0 13 * * *' + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +jobs: + check-script: + uses: dotnet/arcade/.github/workflows/inter-branch-merge-base.yml@main + with: + configuration_file_path: '.config/branch-merge.json' \ No newline at end of file diff --git a/azure-pipelines-official.yml b/azure-pipelines-official.yml index 6237d65cacde8..dbdef3f0058d7 100644 --- a/azure-pipelines-official.yml +++ b/azure-pipelines-official.yml @@ -5,6 +5,7 @@ trigger: - main-vs-deps - release/dev16.*-vs-deps - release/dev17.* + - release/dev18.* - features/lsp_tools_host exclude: - release/dev17.0 From e46a163eb023056dbe777cb9df7fe6079b944430 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Mon, 10 Feb 2025 19:06:26 -0800 Subject: [PATCH 213/305] Fix workspace command execution --- .../AbstractLanguageServer.cs | 13 +++- .../Commands/ExecuteWorkspaceCommandTests.cs | 74 +++++++++++++++++++ 2 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 src/LanguageServer/ProtocolUnitTests/Commands/ExecuteWorkspaceCommandTests.cs diff --git a/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs b/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs index a375878201a7e..071edc9649eaa 100644 --- a/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs +++ b/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs @@ -27,6 +27,7 @@ internal abstract class AbstractLanguageServer /// private readonly Lazy> _queue; private readonly Lazy _lspServices; + private readonly Lazy _handlerProvider; public bool IsInitialized { get; private set; } @@ -67,6 +68,13 @@ protected AbstractLanguageServer( _jsonRpc.Disconnected += JsonRpc_Disconnected; _lspServices = new Lazy(() => ConstructLspServices()); _queue = new Lazy>(() => ConstructRequestExecutionQueue()); + _handlerProvider = new Lazy(() => + { + var lspServices = _lspServices.Value; + var handlerProvider = new HandlerProvider(lspServices, TypeRefResolver); + SetupRequestDispatcher(handlerProvider); + return handlerProvider; + }); } /// @@ -89,10 +97,7 @@ protected virtual AbstractHandlerProvider HandlerProvider { get { - var lspServices = _lspServices.Value; - var handlerProvider = new HandlerProvider(lspServices, TypeRefResolver); - SetupRequestDispatcher(handlerProvider); - return handlerProvider; + return _handlerProvider.Value; } } diff --git a/src/LanguageServer/ProtocolUnitTests/Commands/ExecuteWorkspaceCommandTests.cs b/src/LanguageServer/ProtocolUnitTests/Commands/ExecuteWorkspaceCommandTests.cs new file mode 100644 index 0000000000000..7b9c222145ab4 --- /dev/null +++ b/src/LanguageServer/ProtocolUnitTests/Commands/ExecuteWorkspaceCommandTests.cs @@ -0,0 +1,74 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Composition; +using System.Linq; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageServer.Handler; +using Microsoft.CodeAnalysis.LanguageServer.Handler.Commands; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.LanguageServer.Protocol; +using Roslyn.Test.Utilities; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Commands; +public class ExecuteWorkspaceCommandTests : AbstractLanguageServerProtocolTests +{ + protected override TestComposition Composition => base.Composition.AddParts( + typeof(TestWorkspaceCommandHandler)); + + public ExecuteWorkspaceCommandTests(ITestOutputHelper? testOutputHelper) : base(testOutputHelper) + { + } + + [Theory, CombinatorialData] + public async Task TestExecuteWorkspaceCommand(bool mutatingLspWorkspace) + { + await using var server = await CreateTestLspServerAsync("", mutatingLspWorkspace); + + var request = new ExecuteCommandParams() + { + Arguments = new object[] { JsonSerializer.Serialize(new TextDocumentIdentifier { Uri = ProtocolConversions.CreateAbsoluteUri(@"C:\someFile.cs") }) }, + Command = TestWorkspaceCommandHandler.CommandName + }; + var response = await server.ExecuteRequestAsync(Methods.WorkspaceExecuteCommandName, request, CancellationToken.None); + AssertEx.NotNull(response); + Assert.True((bool)response); + + } + + [ExportCSharpVisualBasicStatelessLspService(typeof(TestWorkspaceCommandHandler)), Shared, PartNotDiscoverable] + [Command(CommandName)] + internal class TestWorkspaceCommandHandler : AbstractExecuteWorkspaceCommandHandler + { + internal const string CommandName = nameof(TestWorkspaceCommandHandler); + + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public TestWorkspaceCommandHandler() + { + } + + public override string Command => CommandName; + + public override bool MutatesSolutionState => false; + + public override bool RequiresLSPSolution => true; + + public override TextDocumentIdentifier GetTextDocumentIdentifier(ExecuteCommandParams request) + { + return JsonSerializer.Deserialize((JsonElement)request.Arguments.First(), ProtocolConversions.LspJsonSerializerOptions)!; + } + + public override Task HandleRequestAsync(ExecuteCommandParams request, RequestContext context, CancellationToken cancellationToken) + { + return Task.FromResult(true); + } + } +} From 19c9b9eb00ae9afa7bedb10bd6be06bb41a880f6 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Tue, 11 Feb 2025 10:35:29 +0100 Subject: [PATCH 214/305] Detect data section string literal hash collisions (#77061) * Detect data section string literal hash collisions * Move check into GetOrAdd * Remove VB error * Remove test only option from equality * Truncate the string in the error message * Update the spec * Update the spec --- docs/features/string-literals-data-section.md | 18 ++++++++++- .../CSharp/Portable/CSharpResources.resx | 3 ++ .../CSharp/Portable/Errors/ErrorCode.cs | 2 ++ .../CSharp/Portable/Errors/ErrorFacts.cs | 4 ++- .../CSharp/Portable/Errors/MessageProvider.cs | 1 + .../Portable/xlf/CSharpResources.cs.xlf | 5 ++++ .../Portable/xlf/CSharpResources.de.xlf | 5 ++++ .../Portable/xlf/CSharpResources.es.xlf | 5 ++++ .../Portable/xlf/CSharpResources.fr.xlf | 5 ++++ .../Portable/xlf/CSharpResources.it.xlf | 5 ++++ .../Portable/xlf/CSharpResources.ja.xlf | 5 ++++ .../Portable/xlf/CSharpResources.ko.xlf | 5 ++++ .../Portable/xlf/CSharpResources.pl.xlf | 5 ++++ .../Portable/xlf/CSharpResources.pt-BR.xlf | 5 ++++ .../Portable/xlf/CSharpResources.ru.xlf | 5 ++++ .../Portable/xlf/CSharpResources.tr.xlf | 5 ++++ .../Portable/xlf/CSharpResources.zh-Hans.xlf | 5 ++++ .../Portable/xlf/CSharpResources.zh-Hant.xlf | 5 ++++ .../Test/Emit/Emit/EmitMetadataTests.cs | 21 +++++++++++++ .../Test/Syntax/Diagnostics/DiagnosticTest.cs | 1 + .../CodeAnalysisTest/Emit/EmitOptionsTests.cs | 3 +- .../CodeGen/PrivateImplementationDetails.cs | 30 +++++++++++++++---- .../Diagnostic/CommonMessageProvider.cs | 1 + .../Core/Portable/Emit/EmitOptions.cs | 2 ++ .../Test/Core/Mocks/TestMessageProvider.cs | 2 ++ .../VisualBasic/Portable/Errors/ErrorFacts.vb | 1 + .../Portable/Errors/MessageProvider.vb | 6 ++++ .../CSharpLspBuildOnlyDiagnostics.cs | 3 +- 28 files changed, 154 insertions(+), 9 deletions(-) diff --git a/docs/features/string-literals-data-section.md b/docs/features/string-literals-data-section.md index 133416a2cfa36..adda3e666eaf0 100644 --- a/docs/features/string-literals-data-section.md +++ b/docs/features/string-literals-data-section.md @@ -54,7 +54,7 @@ The utf8 string literal encoding emit strategy emits `ldsfld` of a field in a ge For every unique string literal, a unique internal static class is generated which: - has name composed of `` followed by a hex-encoded XXH128 hash of the string - (collisions [should not happen][xxh128] with XXH128 and so they aren't currently detected or reported, the behavior in that case is undefined), + (collisions [should not happen][xxh128] with XXH128, but if there are string literals which would result in the same XXH128 hash, a compile-time error is reported), - is nested in the `` type to avoid polluting the global namespace and to avoid having to enforce name uniqueness across modules, - has one internal static readonly `string` field which is initialized in a static constructor of the class, @@ -62,6 +62,7 @@ For every unique string literal, a unique internal static class is generated whi There is also an internal static readonly `.data` field generated into `` containing the actual bytes, similar to [u8 string literals][u8-literals] and [constant array initializers][constant-array-init]. +This field uses hex-encoded SHA-256 hash for its name and collisions are currently not reported by the compiler. These other scenarios might also reuse the data field, e.g., the following statements could all reuse the same data field: ```cs @@ -241,6 +242,9 @@ This fixup phase already exists in the compiler in `MetadataWriter.WriteInstruct It is called from `SerializeMethodBodies` which precedes `PopulateSystemTables` call, hence synthesizing the utf8 string classes in the former should be possible and they would be emitted in the latter. +Alternatively, we could collect string literals during binding, then before emit sort them by length and content (for determinism) +to find the ones that are over the threshold and should be emitted with this new strategy. + ### Statistics The compiler could emit an info diagnostic with useful statistics for customers to determine what threshold to set. @@ -302,6 +306,18 @@ The compiler should report a diagnostic when the feature is enabled together wit `[assembly: System.Runtime.CompilerServices.CompilationRelaxations(0)]`, i.e., string interning enabled, because that is incompatible with the feature. +### Avoiding hash collisions + +Instead of XXH128 for the type names and SHA-256 for the data field names, we could use index-based names. +- The compiler could assign names lazily based on metadata tokens which are deterministic. + If building on the current approach, that might require some refactoring, + because internal data structures in the compiler might not be ready for lazy names like that. + But it would be easier if combined with the first strategy suggested for [automatic threshold](#automatic-threshold) above, + where we would not synthesize the types until very late in the emit phase (during fixup of the metadata tokens). +- We could build on the second strategy suggested for [automatic threshold](#automatic-threshold) where we would collect string literals during binding + (and perhaps also constant arrays and u8 strings if we want to extend this support to them as well), + then before emit we would sort them by length and content and assign indices to them to be then used for the synthesized names. + [u8-literals]: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-11.0/utf8-string-literals [constant-array-init]: https://github.com/dotnet/roslyn/pull/24621 diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 13c398af16501..bdb7325e53256 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -8047,4 +8047,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ 'InterceptsLocationAttribute(string, int, int)' is not supported. Move to 'InterceptableLocation'-based generation of these attributes instead. (https://github.com/dotnet/roslyn/issues/72133) + + Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 83f128759bf41..53439e87d1b0f 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2360,6 +2360,8 @@ internal enum ErrorCode ERR_ImplicitlyTypedParamsParameter = 9272, ERR_VariableDeclarationNamedField = 9273, + ERR_DataSectionStringLiteralHashCollision = 9274, + // Note: you will need to do the following after adding errors: // 1) Update ErrorFacts.IsBuildOnlyDiagnostic (src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs) diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index f6e520514814b..c3df3adc794b8 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -637,7 +637,9 @@ or ErrorCode.ERR_InterceptableMethodMustBeOrdinary or ErrorCode.ERR_PossibleAsyncIteratorWithoutYield or ErrorCode.ERR_PossibleAsyncIteratorWithoutYieldOrAwait or ErrorCode.ERR_RefLocalAcrossAwait - // Update src\EditorFeatures\CSharp\LanguageServer\CSharpLspBuildOnlyDiagnostics.cs + or ErrorCode.ERR_DataSectionStringLiteralHashCollision + // Update src\Features\CSharp\Portable\Diagnostics\LanguageServer\CSharpLspBuildOnlyDiagnostics.cs + // and TestIsBuildOnlyDiagnostic in src\Compilers\CSharp\Test\Syntax\Diagnostics\DiagnosticTest.cs // whenever new values are added here. => true, diff --git a/src/Compilers/CSharp/Portable/Errors/MessageProvider.cs b/src/Compilers/CSharp/Portable/Errors/MessageProvider.cs index cdd1ca8b7f5f2..ba05a944c54bd 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageProvider.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageProvider.cs @@ -250,6 +250,7 @@ public override void ReportDuplicateMetadataReferenceWeak(DiagnosticBag diagnost public override int ERR_EncUpdateFailedMissingSymbol => (int)ErrorCode.ERR_EncUpdateFailedMissingSymbol; public override int ERR_InvalidDebugInfo => (int)ErrorCode.ERR_InvalidDebugInfo; public override int ERR_FunctionPointerTypesInAttributeNotSupported => (int)ErrorCode.ERR_FunctionPointerTypesInAttributeNotSupported; + public override int ERR_DataSectionStringLiteralHashCollision => (int)ErrorCode.ERR_DataSectionStringLiteralHashCollision; // Generators: public override int WRN_GeneratorFailedDuringInitialization => (int)ErrorCode.WRN_GeneratorFailedDuringInitialization; diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 9b514141b8ee4..5720459cb38ce 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -537,6 +537,11 @@ Kopírovací konstruktor {0} musí být veřejný nebo chráněný, protože záznam není zapečetěný. + + Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + + The name '{0}' does not match the corresponding 'Deconstruct' parameter '{1}'. Název {0} neodpovídá příslušnému parametru Deconstruct {1}. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 08914ed33405f..2ac6b42fb054d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -537,6 +537,11 @@ Der Kopierkonstruktor "{0}" muss öffentlich oder geschützt sein, weil der Datensatz nicht versiegelt ist. + + Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + + The name '{0}' does not match the corresponding 'Deconstruct' parameter '{1}'. Der Name "{0}" stimmt nicht mit dem entsprechenden Deconstruct-Parameter "{1}" überein. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 97fd9dabc3794..e776b15f4336c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -537,6 +537,11 @@ Un constructor de copia "{0}" debe ser público o estar protegido porque el registro no está sellado. + + Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + + The name '{0}' does not match the corresponding 'Deconstruct' parameter '{1}'. El nombre "{0}" no coincide con el parámetro de "Deconstruct" correspondiente, "{1}". diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index aed63cb49673e..ddceebdb38625 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -537,6 +537,11 @@ Un constructeur de copie '{0}' doit être public ou protégé, car l'enregistrement n'est pas sealed. + + Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + + The name '{0}' does not match the corresponding 'Deconstruct' parameter '{1}'. Le nom '{0}' ne correspond pas au paramètre 'Deconstruct' correspondant '{1}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index c688b9962573d..fd94a9ee799ce 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -537,6 +537,11 @@ Un costruttore di copia '{0}' deve essere pubblico o protetto perché il record non è sealed. + + Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + + The name '{0}' does not match the corresponding 'Deconstruct' parameter '{1}'. Il nome '{0}' non corrisponde al parametro '{1}' di 'Deconstruct' corrispondente. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index f005a99296f9a..65647430f1f04 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -537,6 +537,11 @@ コピー コンストラクター '{0}' は、レコードが sealed ではないため、public または protected にする必要があります。 + + Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + + The name '{0}' does not match the corresponding 'Deconstruct' parameter '{1}'. 名前 '{0}' は対応する 'Deconstruct' パラメーター '{1}' と一致しません。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 3d3a95f5953ff..b01d67981c2c0 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -537,6 +537,11 @@ 레코드가 봉인되지 않았으므로 복사 생성자 '{0}'이(가) 퍼블릭이거나 보호되어야 합니다. + + Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + + The name '{0}' does not match the corresponding 'Deconstruct' parameter '{1}'. '{0}' 이름이 해당 'Deconstruct' 매개 변수 '{1}'과(와) 일치하지 않습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 2e86434383d85..57dbceb0ec44e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -537,6 +537,11 @@ Konstruktor kopiujący „{0}” musi być publiczny lub chroniony, ponieważ rekord nie jest zapieczętowany. + + Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + + The name '{0}' does not match the corresponding 'Deconstruct' parameter '{1}'. Nazwa „{0}” nie jest zgodna z odpowiednim parametrem „Deconstruct” „{1}”. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 060041b7ff0b1..d0d94f1330ea7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -537,6 +537,11 @@ Um construtor de cópia '{0}' precisa ser público ou protegido porque o registro não está selado. + + Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + + The name '{0}' does not match the corresponding 'Deconstruct' parameter '{1}'. O nome '{0}' não corresponde ao parâmetro 'Deconstruct' '{1}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index c041828059022..2c1f011baab3d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -537,6 +537,11 @@ Конструктор копий "{0}" должен быть открытым или защищенным, так как запись не запечатана. + + Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + + The name '{0}' does not match the corresponding 'Deconstruct' parameter '{1}'. Имя "{0}" не соответствует указанному параметру "Deconstruct" "{1}". diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 5fb5ce580e40c..4e5bd6417ef0e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -537,6 +537,11 @@ Kayıt mühürlü olmadığından, '{0}' kopya oluşturucusu genel veya korumalı olmalıdır. + + Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + + The name '{0}' does not match the corresponding 'Deconstruct' parameter '{1}'. '{0}' adı ilgili '{1}' 'Deconstruct' parametresiyle eşleşmiyor. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index d338387f671e6..2ba44fcc0e1bd 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -537,6 +537,11 @@ 复制构造函数“{0}”必须是公共的或受保护的,因为该记录未密封。 + + Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + + The name '{0}' does not match the corresponding 'Deconstruct' parameter '{1}'. 名称“{0}”与相应 "Deconstruct" 参数“{1}”不匹配。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index f67ddd602bd43..4ae88a2fecd1e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -537,6 +537,11 @@ 因為記錄不是密封的,所以複製建構函式 '{0}' 必須是公用或受保護。 + + Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + + The name '{0}' does not match the corresponding 'Deconstruct' parameter '{1}'. 名稱 '{0}' 與對應的 'Deconstruct' 參數 '{1}' 不相符。 diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EmitMetadataTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EmitMetadataTests.cs index 126f4d6ed3bbd..c8577b654b471 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EmitMetadataTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EmitMetadataTests.cs @@ -3175,6 +3175,27 @@ .maxstack 1 """); } + [Fact] + public void DataSectionStringLiterals_HashCollision() + { + var emitOptions = new EmitOptions + { + // Take only the first byte of each string as its hash to simulate collisions. + TestOnly_DataToHexViaXxHash128 = static (data) => data[0].ToString(), + }; + var source = """ + System.Console.Write("a"); + System.Console.Write("b"); + System.Console.Write("aa"); + """; + CreateCompilation(source, + parseOptions: TestOptions.Regular.WithFeature("experimental-data-section-string-literals", "0")) + .VerifyEmitDiagnostics(emitOptions, + // (3,22): error CS9274: Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: a + // System.Console.Write("aa"); + Diagnostic(ErrorCode.ERR_DataSectionStringLiteralHashCollision, @"""aa""").WithArguments("a").WithLocation(3, 22)); + } + [Fact] public void DataSectionStringLiterals_SynthesizedTypes() { diff --git a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs index e2c7ddb41920c..0f68ba4c2475f 100644 --- a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs +++ b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs @@ -2991,6 +2991,7 @@ public void TestIsBuildOnlyDiagnostic() case ErrorCode.ERR_PossibleAsyncIteratorWithoutYield: case ErrorCode.ERR_PossibleAsyncIteratorWithoutYieldOrAwait: case ErrorCode.ERR_RefLocalAcrossAwait: + case ErrorCode.ERR_DataSectionStringLiteralHashCollision: Assert.True(isBuildOnly, $"Check failed for ErrorCode.{errorCode}"); break; diff --git a/src/Compilers/Core/CodeAnalysisTest/Emit/EmitOptionsTests.cs b/src/Compilers/Core/CodeAnalysisTest/Emit/EmitOptionsTests.cs index 17db9893fe02c..16fafd03acb16 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Emit/EmitOptionsTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Emit/EmitOptionsTests.cs @@ -92,7 +92,8 @@ public void TestFieldsForEqualsAndGetHashCode() nameof(EmitOptions.IncludePrivateMembers), nameof(EmitOptions.InstrumentationKinds), nameof(EmitOptions.DefaultSourceFileEncoding), - nameof(EmitOptions.FallbackSourceFileEncoding)); + nameof(EmitOptions.FallbackSourceFileEncoding), + nameof(EmitOptions.TestOnly_DataToHexViaXxHash128)); } [Fact] diff --git a/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs b/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs index 167f24f09be32..4866750134114 100644 --- a/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs +++ b/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs @@ -100,6 +100,9 @@ internal sealed class PrivateImplementationDetails : DefaultTypeDef, Cci.INamesp // data section string literal holders (key is the full string literal) private readonly ConcurrentDictionary _dataSectionStringLiteralTypes = new ConcurrentDictionary(); + // map of data section string literal generated type names ( + hash) to the full text + private readonly ConcurrentDictionary _dataSectionStringLiteralNames = new ConcurrentDictionary(); + private ImmutableArray _orderedNestedTypes; internal PrivateImplementationDetails( @@ -333,16 +336,28 @@ internal MappedField GetOrAddDataField(ImmutableArray data, ushort alignme } var @this = moduleBuilder.GetPrivateImplClass(syntaxNode, diagnostics); - return @this._dataSectionStringLiteralTypes.GetOrAdd(text, static (key, arg) => + return @this._dataSectionStringLiteralTypes.GetOrAdd(text, static (text, arg) => { - var (@this, data, diagnostics) = arg; + var (@this, data, syntaxNode, diagnostics) = arg; - string name = "" + DataToHexViaXxHash128(data); + string name = "" + @this.DataToHexViaXxHash128(data); MappedField dataField = @this.GetOrAddDataField(data, alignment: 1); Cci.IMethodDefinition bytesToStringHelper = @this.GetOrSynthesizeBytesToStringHelper(diagnostics); + var previousText = @this._dataSectionStringLiteralNames.GetOrAdd(name, text); + if (previousText != text) + { + // If there is a hash collision, we cannot fallback to normal string literal emit strategy + // because the selection of which literal would get which emit strategy would not be deterministic. + var messageProvider = @this.ModuleBuilder.CommonCompilation.MessageProvider; + diagnostics.Add(messageProvider.CreateDiagnostic( + messageProvider.ERR_DataSectionStringLiteralHashCollision, + syntaxNode.GetLocation(), + previousText[..Math.Min(previousText.Length, 500)])); + } + return new DataSectionStringType( name: name, containingType: @this, @@ -350,7 +365,7 @@ internal MappedField GetOrAddDataField(ImmutableArray data, ushort alignme bytesToStringHelper: bytesToStringHelper, diagnostics: diagnostics); }, - (@this, ImmutableCollectionsMarshal.AsImmutableArray(data), diagnostics)).Field; + (@this, ImmutableCollectionsMarshal.AsImmutableArray(data), syntaxNode, diagnostics)).Field; } /// @@ -548,8 +563,13 @@ private static string DataToHex(ImmutableArray data) return HashToHex(hash.AsSpan()); } - private static string DataToHexViaXxHash128(ImmutableArray data) + private string DataToHexViaXxHash128(ImmutableArray data) { + if (ModuleBuilder.EmitOptions.TestOnly_DataToHexViaXxHash128 is { } handler) + { + return handler(data); + } + Span hash = stackalloc byte[sizeof(ulong) * 2]; int bytesWritten = XxHash128.Hash(data.AsSpan(), hash); Debug.Assert(bytesWritten == hash.Length); diff --git a/src/Compilers/Core/Portable/Diagnostic/CommonMessageProvider.cs b/src/Compilers/Core/Portable/Diagnostic/CommonMessageProvider.cs index 03af6e1a38996..b662165fee496 100644 --- a/src/Compilers/Core/Portable/Diagnostic/CommonMessageProvider.cs +++ b/src/Compilers/Core/Portable/Diagnostic/CommonMessageProvider.cs @@ -244,6 +244,7 @@ public string GetIdForErrorCode(int errorCode) public abstract int ERR_EncUpdateFailedMissingSymbol { get; } public abstract int ERR_InvalidDebugInfo { get; } public abstract int ERR_FunctionPointerTypesInAttributeNotSupported { get; } + public abstract int ERR_DataSectionStringLiteralHashCollision { get; } // Generators: public abstract int WRN_GeneratorFailedDuringInitialization { get; } diff --git a/src/Compilers/Core/Portable/Emit/EmitOptions.cs b/src/Compilers/Core/Portable/Emit/EmitOptions.cs index 07e92a2f3371e..fefce27c268cc 100644 --- a/src/Compilers/Core/Portable/Emit/EmitOptions.cs +++ b/src/Compilers/Core/Portable/Emit/EmitOptions.cs @@ -120,6 +120,8 @@ public sealed class EmitOptions : IEquatable /// private bool _testOnly_AllowLocalStateTracing; + internal Func, string>? TestOnly_DataToHexViaXxHash128 { get; init; } + // 1.2 BACKCOMPAT OVERLOAD -- DO NOT TOUCH public EmitOptions( bool metadataOnly, diff --git a/src/Compilers/Test/Core/Mocks/TestMessageProvider.cs b/src/Compilers/Test/Core/Mocks/TestMessageProvider.cs index 008e2eb21e579..b3fa3ea6172e3 100644 --- a/src/Compilers/Test/Core/Mocks/TestMessageProvider.cs +++ b/src/Compilers/Test/Core/Mocks/TestMessageProvider.cs @@ -470,6 +470,8 @@ public override int ERR_InvalidDebugInfo public override int ERR_FunctionPointerTypesInAttributeNotSupported => throw new NotImplementedException(); + public override int ERR_DataSectionStringLiteralHashCollision => throw new NotImplementedException(); + public override int? WRN_ByValArraySizeConstRequired => throw new NotImplementedException(); } } diff --git a/src/Compilers/VisualBasic/Portable/Errors/ErrorFacts.vb b/src/Compilers/VisualBasic/Portable/Errors/ErrorFacts.vb index 0ceb29c88e20a..ca8fcd8f48918 100644 --- a/src/Compilers/VisualBasic/Portable/Errors/ErrorFacts.vb +++ b/src/Compilers/VisualBasic/Portable/Errors/ErrorFacts.vb @@ -21,6 +21,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ERRID.ERR_CannotGotoNonScopeBlocksWithClosure, ERRID.ERR_SymbolDefinedInAssembly ' Update src\Features\VisualBasic\Portable\Diagnostics\LanguageServer\VisualBasicLspBuildOnlyDiagnostics.vb + ' and TestIsBuildOnlyDiagnostic in src\Compilers\VisualBasic\Test\Semantic\Diagnostics\DiagnosticTests.vb ' whenever new values are added here. Return True Case ERRID.Void, diff --git a/src/Compilers/VisualBasic/Portable/Errors/MessageProvider.vb b/src/Compilers/VisualBasic/Portable/Errors/MessageProvider.vb index c600c952fafeb..0e573eaa217bd 100644 --- a/src/Compilers/VisualBasic/Portable/Errors/MessageProvider.vb +++ b/src/Compilers/VisualBasic/Portable/Errors/MessageProvider.vb @@ -594,6 +594,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Get End Property + Public Overrides ReadOnly Property ERR_DataSectionStringLiteralHashCollision As Integer + Get + Throw ExceptionUtilities.Unreachable + End Get + End Property + ' Generators Public Overrides ReadOnly Property WRN_GeneratorFailedDuringInitialization As Integer Get diff --git a/src/Features/CSharp/Portable/Diagnostics/LanguageServer/CSharpLspBuildOnlyDiagnostics.cs b/src/Features/CSharp/Portable/Diagnostics/LanguageServer/CSharpLspBuildOnlyDiagnostics.cs index 6bd117bf73410..c19cb644ff215 100644 --- a/src/Features/CSharp/Portable/Diagnostics/LanguageServer/CSharpLspBuildOnlyDiagnostics.cs +++ b/src/Features/CSharp/Portable/Diagnostics/LanguageServer/CSharpLspBuildOnlyDiagnostics.cs @@ -60,7 +60,8 @@ namespace Microsoft.CodeAnalysis.CSharp.LanguageServer; "CS9207", // ErrorCode.ERR_InterceptableMethodMustBeOrdinary "CS8419", // ErrorCode.ERR_PossibleAsyncIteratorWithoutYield "CS8420", // ErrorCode.ERR_PossibleAsyncIteratorWithoutYieldOrAwait - "CS9217" // ErrorCode.ERR_RefLocalAcrossAwait + "CS9217", // ErrorCode.ERR_RefLocalAcrossAwait + "CS9274" // ErrorCode.ERR_DataSectionStringLiteralHashCollision )] [Shared] internal sealed class CSharpLspBuildOnlyDiagnostics : ILspBuildOnlyDiagnostics From 0de48781eac6b1384b5484ba01f05c4d5c4b1ce1 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 11 Feb 2025 10:26:56 -0800 Subject: [PATCH 215/305] Update dependencies from https://github.com/dotnet/source-build-reference-packages build 20250130.4 (#76985) Microsoft.SourceBuild.Intermediate.source-build-reference-packages From Version 10.0.607901 -> To Version 10.0.608004 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 7d5a6a3201211..0e453f61917e1 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -8,9 +8,9 @@ - + https://github.com/dotnet/source-build-reference-packages - 3f926184cacc477e3ddccd1eb4f3ebcb9a2b81a5 + f18184030957ae8b02c6cb971ff2a58a06d2e7b1 From eeff8ea2ef70067c0013075edd0ccec82c829c7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Tue, 11 Feb 2025 10:45:55 -0800 Subject: [PATCH 216/305] Add Microsoft.CodeAnalysis.Contracts source package (#76997) * Add source package * Polyfills * Move ExceptionUtilities and Contracts to Microsoft.CodeAnalysis namespace * Move ExceptionUtilities to Contracts source packages * Move Contract to Contracts source packages * Move NonCopyable, NonDefaultable attributes to Microsoft.CodeAnalysis namespaces * Move NonCopyableAttribute and NonDefaultableAttribute to Contracts source package * Exclude Contract from core compiler --- Roslyn.sln | 29 ++++++++++++++++- eng/config/PublishData.json | 1 + eng/test-rebuild.ps1 | 3 ++ .../Microsoft.CodeAnalysis.CodeStyle.csproj | 1 + .../Tools/CodeStyleConfigFileGenerator.csproj | 3 +- ...ndInterpolatedStringArgumentPlaceholder.cs | 2 +- .../Model/ExpandedVarargsMethodReference.cs | 4 +-- .../Emitter/Model/NamedTypeReference.cs | 4 +-- .../Emitter/Model/ParameterTypeInformation.cs | 8 ++--- .../Portable/Emitter/Model/SymbolAdapter.cs | 4 +-- .../Emitter/Model/TypeMemberReference.cs | 4 +-- .../Portable/SymbolDisplay/ObjectDisplay.cs | 3 -- .../Test/Semantic/Semantics/OperatorTests.cs | 2 +- .../Microsoft.Build.Tasks.CodeAnalysis.csproj | 20 ++++-------- .../Core/Portable/CodeGen/ArrayMembers.cs | 4 +-- .../CodeGen/PrivateImplementationDetails.cs | 8 ++--- src/Compilers/Core/Portable/Emit/ErrorType.cs | 4 +-- .../Emit/NoPia/CommonEmbeddedMember.cs | 4 +-- .../Emit/NoPia/CommonEmbeddedParameter.cs | 4 +-- .../Portable/Emit/NoPia/CommonEmbeddedType.cs | 4 +-- .../Emit/NoPia/CommonEmbeddedTypeParameter.cs | 4 +-- .../Core/Portable/Emit/NoPia/VtblGap.cs | 4 +-- .../Core/Portable/FileSystem/FileUtilities.cs | 1 + .../InternalUtilities/EmptyComparer.cs | 3 +- .../InternalUtilities/ExceptionExtensions.cs | 20 ++++++++++++ .../Portable/InternalUtilities/FatalError.cs | 2 -- .../ReaderWriterLockSlimExtensions.cs | 1 + .../InternalUtilities/RoslynParallel.cs | 1 + .../SemaphoreSlimExtensions.cs | 1 + .../InternalUtilities/SingleInitNullable.cs | 1 + .../Portable/Microsoft.CodeAnalysis.csproj | 3 +- .../PEWriter/InheritedTypeParameter.cs | 7 ++-- .../Core/Portable/PEWriter/MetadataVisitor.cs | 6 ++-- .../Portable/PEWriter/MethodDefinitionBase.cs | 1 + .../PEWriter/ModifiedTypeReference.cs | 5 +-- .../PEWriter/ParameterDefinitionBase.cs | 5 +-- .../Core/Portable/PEWriter/RootModuleType.cs | 5 +-- .../Core/Portable/PEWriter/SequencePoint.cs | 1 + .../Core/Portable/SpecialTypeExtensions.cs | 2 +- ...soft.CodeAnalysis.Rebuild.UnitTests.csproj | 3 -- .../Test/Core/Metadata/MetadataReaderUtils.cs | 1 + .../Portable/Emit/NamedTypeReference.vb | 4 +-- .../Portable/Emit/ParameterTypeInformation.vb | 4 +-- .../Portable/Emit/SymbolAdapter.vb | 4 +-- .../Portable/Emit/TypeMemberReference.vb | 4 +-- ...ft.CodeAnalysis.Collections.Package.csproj | 10 +++--- .../Contracts}/CollectionBuilderAttribute.cs | 0 .../CompilerFeatureRequiredAttribute.cs | 0 .../Contract.InterpolatedStringHandlers.cs | 6 +++- .../Contracts}/Contract.cs | 6 +++- .../Contracts}/ExceptionUtilities.cs | 2 +- .../Contracts}/ExperimentalAttribute.cs | 0 .../Contracts}/IReadOnlySet.cs | 0 ...erpolatedStringHandlerArgumentAttribute.cs | 0 .../InterpolatedStringHandlerAttribute.cs | 0 .../Contracts}/IsExternalInit.cs | 0 ...soft.CodeAnalysis.Contracts.Package.csproj | 26 +++++++++++++++ ...Microsoft.CodeAnalysis.Contracts.projitems | 32 +++++++++++++++++++ .../Microsoft.CodeAnalysis.Contracts.shproj | 14 ++++++++ .../Contracts}/NonCopyableAttribute.cs | 11 +++---- .../Contracts}/NonDefaultableAttribute.cs | 11 +++---- .../Contracts}/NullableAttributes.cs | 0 .../Contracts}/RequiredMemberAttribute.cs | 0 .../SetsRequiredMembersAttribute.cs | 0 .../Threading/AsyncBatchingWorkQueue`0.cs | 1 - .../Threading/AsyncBatchingWorkQueue`1.cs | 1 - .../Threading/AsyncBatchingWorkQueue`2.cs | 1 - ...soft.CodeAnalysis.Threading.Package.csproj | 7 +--- .../Structure/CommentStructureTests.cs | 2 +- .../Json/CSharpJsonParserTests_NstTests.cs | 2 +- .../AbstractGoToCommandHandler`2.cs | 2 +- ...crosoft.CodeAnalysis.EditorFeatures.csproj | 1 - .../ChangeSignatureTestState.cs | 2 +- .../SignatureHelpControllerTests.vb | 2 +- ...lysis.EditorFeatures.Test.Utilities.csproj | 4 --- .../NavigateToTestAggregator.Callback.cs | 1 + .../NavigateTo/NavigateToTestAggregator.cs | 1 + .../ReplaceSpanResult.vb | 2 +- ...Analysis.VisualBasic.EditorFeatures.vbproj | 2 +- .../Structure/CommentStructureTests.vb | 2 +- ...osoft.CodeAnalysis.FunctionResolver.csproj | 7 +--- ...crosoft.CodeAnalysis.ResultProvider.csproj | 13 +------- .../NamespaceTypeDefinitionNoBase.cs | 4 +-- ...deAnalysis.ResultProvider.Utilities.csproj | 13 +------- ...LanguageServerIndexFormat.Generator.csproj | 1 - ...odeAnalysis.Features.Test.Utilities.csproj | 4 --- ...rosoft.CodeAnalysis.InteractiveHost.csproj | 8 +---- ...ageServerProtocol.Framework.Example.csproj | 4 +-- ...ageServerProtocol.Framework.Package.csproj | 5 +-- ...eServerProtocol.Framework.Shared.projitems | 1 - ...guageServer.Protocol.Test.Utilities.csproj | 4 --- ...odeAnalysis.LanguageServer.Protocol.csproj | 1 - ...s.LanguageServer.Protocol.UnitTests.csproj | 4 --- .../Reader/MockSymUnmanagedReader.cs | 2 +- .../BuildActionTelemetryTable.csproj | 7 ++-- ...t.CodeAnalysis.ExternalAccess.Razor.csproj | 4 --- ...ft.CodeAnalysis.ExternalAccess.Xaml.csproj | 7 ---- src/Tools/Replay/Replay.csproj | 6 ++-- .../BuildTask/SemanticSearch.BuildTask.csproj | 3 +- .../ProjectSystemShim/TestEvaluationData.cs | 2 +- .../DebuggerIntellisenseFilter.cs | 2 +- .../DocumentSymbolDataViewModelSorter.cs | 2 +- .../Entries/SimpleMessageEntry.cs | 2 +- .../AbstractOleCommandTarget.Query.cs | 2 +- .../AbstractOleCommandTarget.cs | 2 +- .../AbstractVsTextViewFilter.cs | 1 + .../VisualStudioUIContextActivationService.cs | 1 + .../MenuItemContainerTemplateSelector.cs | 2 +- .../VsInteractiveWindowProvider.cs | 1 + .../Core/Def/Interop/ComAggregate.cs | 2 +- .../Def/LanguageClient/LogHubLspLogger.cs | 2 +- ...tLanguageService`2.IVsLanguageDebugInfo.cs | 2 +- ...osoft.VisualStudio.LanguageServices.csproj | 3 -- .../Def/NavigationBar/NavigationBarClient.cs | 2 +- .../Core/Def/Options/FeatureFlagPersister.cs | 2 +- .../LocalUserRegistryOptionPersister.cs | 2 +- .../Options/VisualStudioOptionPersister.cs | 1 + .../VisualStudioOptionPersisterProvider.cs | 1 + .../Core/Def/PreviewPane/PreviewPane.xaml.cs | 1 + .../Def/ProjectSystem/FileChangeTracker.cs | 1 + .../Core/Def/ProjectSystem/InvisibleEditor.cs | 2 +- .../Legacy/SolutionEventsBatchScopeCreator.cs | 1 + .../Core/Def/RoslynActivityLogger.cs | 2 +- src/VisualStudio/Core/Def/RoslynPackage.cs | 1 + .../Core/Def/SolutionEventMonitor.cs | 2 +- .../StackTraceExplorerCommandHandler.cs | 1 + .../AbstractWorkspaceTelemetryService.cs | 2 +- .../VisualStudioWorkspaceTelemetryService.cs | 3 +- .../UnusedReferenceExtensions.cs | 1 + .../Def/Utilities/VsCodeWindowViewTracker.cs | 2 +- .../Core/Def/Venus/VenusCommandFilter.cs | 2 +- .../Style/EnumCodeStyleOptionViewModel.cs | 2 +- .../AnalyzerReferenceManager.cs | 1 + .../Roslyn.VisualStudio.Next.UnitTests.csproj | 4 --- .../UnifiedSettings/TestModel/Utilities.cs | 2 +- .../Test/ClassView/MockVsServiceProvider.vb | 2 +- ....CodeAnalysis.ExternalAccess.FSharp.csproj | 4 --- .../InProcess/AddParameterDialogInProcess.cs | 2 +- .../InProcess/AutomationElementHelper.cs | 1 + .../ChangeSignatureDialogInProcess.cs | 1 + .../CodeDefinitionWindowInProcess.cs | 2 +- .../InProcess/GenerateTypeDialogInProcess.cs | 1 + .../ITextViewWindowInProcessExtensions.cs | 2 +- .../InProcess/InlineRenameInProcess.cs | 1 + .../InProcess/InteractiveWindowInProcess.cs | 1 + .../InProcess/LocalsWindowInProcess.cs | 1 + .../InProcess/MessageBoxInProcess.cs | 2 +- .../MoveToNamespaceDialogInProcess.cs | 1 + .../InProcess/PickMembersDialogInProcess.cs | 2 +- .../InProcess/ScreenshotInProcess.cs | 1 + .../DiagnosticsWindow.cs | 2 +- .../Commands/CreateEventCommandHandler.cs | 3 +- .../OnAutoInsert/OnAutoInsertHandler.cs | 1 + .../OnTypeRename/OnTypeRenameHandler.cs | 1 + .../Microsoft.CodeAnalysis.Workspaces.csproj | 1 + ...eAnalysis.Workspaces.Test.Utilities.csproj | 3 -- ...alysis.Workspaces.MSBuild.BuildHost.csproj | 5 ++- ...oft.CodeAnalysis.Workspaces.MSBuild.csproj | 1 - .../Core/CompilerExtensions.projitems | 14 -------- .../Compiler/Core/Utilities/AsyncLazy`1.cs | 1 + .../EnumerableConditionalWeakTable.cs | 1 + .../Compiler/Core/Utilities/EventMap.cs | 1 + .../Core/Utilities/FixedSizeArrayBuilder.cs | 1 + .../Core/Utilities/NonReentrantLock.cs | 1 + .../Utilities/ReferenceCountedDisposable.cs | 2 +- .../Compiler/Core/Utilities/TaskExtensions.cs | 1 + .../Core/Utilities/ValueTaskExtensions.cs | 1 + .../VisualBasicCodeGenerationHelpers.vb | 2 +- 168 files changed, 312 insertions(+), 276 deletions(-) create mode 100644 src/Compilers/Core/Portable/InternalUtilities/ExceptionExtensions.cs rename src/{Compilers/Core/Portable/Syntax => Dependencies/Contracts}/CollectionBuilderAttribute.cs (100%) rename src/{Compilers/Core/Portable/InternalUtilities => Dependencies/Contracts}/CompilerFeatureRequiredAttribute.cs (100%) rename src/{Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities => Dependencies/Contracts}/Contract.InterpolatedStringHandlers.cs (96%) rename src/{Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities => Dependencies/Contracts}/Contract.cs (98%) rename src/{Compilers/Core/Portable/InternalUtilities => Dependencies/Contracts}/ExceptionUtilities.cs (98%) rename src/{Compilers/Core/Portable/InternalUtilities => Dependencies/Contracts}/ExperimentalAttribute.cs (100%) rename src/{Compilers/Core/Portable/InternalUtilities => Dependencies/Contracts}/IReadOnlySet.cs (100%) rename src/{Compilers/Core/Portable/InternalUtilities => Dependencies/Contracts}/InterpolatedStringHandlerArgumentAttribute.cs (100%) rename src/{Compilers/Core/Portable/InternalUtilities => Dependencies/Contracts}/InterpolatedStringHandlerAttribute.cs (100%) rename src/{Compilers/Core/Portable/InternalUtilities => Dependencies/Contracts}/IsExternalInit.cs (100%) create mode 100644 src/Dependencies/Contracts/Microsoft.CodeAnalysis.Contracts.Package.csproj create mode 100644 src/Dependencies/Contracts/Microsoft.CodeAnalysis.Contracts.projitems create mode 100644 src/Dependencies/Contracts/Microsoft.CodeAnalysis.Contracts.shproj rename src/{Compilers/Core/Portable/InternalUtilities => Dependencies/Contracts}/NonCopyableAttribute.cs (53%) rename src/{Compilers/Core/Portable/InternalUtilities => Dependencies/Contracts}/NonDefaultableAttribute.cs (53%) rename src/{Compilers/Core/Portable/InternalUtilities => Dependencies/Contracts}/NullableAttributes.cs (100%) rename src/{Compilers/Core/Portable/InternalUtilities => Dependencies/Contracts}/RequiredMemberAttribute.cs (100%) rename src/{Compilers/Core/Portable/InternalUtilities => Dependencies/Contracts}/SetsRequiredMembersAttribute.cs (100%) diff --git a/Roslyn.sln b/Roslyn.sln index 905f0de701071..5f4219bd48e7b 100644 --- a/Roslyn.sln +++ b/Roslyn.sln @@ -573,6 +573,10 @@ Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Microsoft.CodeAnalysis.Thre EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.Threading.Package", "src\Dependencies\Threading\Microsoft.CodeAnalysis.Threading.Package.csproj", "{2559DAF9-784E-4C29-E0E1-70204B1FD56E}" EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Microsoft.CodeAnalysis.Contracts", "src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.shproj", "{BD974609-C68B-4BE6-9682-EB132462B50D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.Contracts.Package", "src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.Package.csproj", "{A8D5CFFA-7F9E-C35B-4F19-D63F6EC1D5CA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1407,6 +1411,10 @@ Global {2559DAF9-784E-4C29-E0E1-70204B1FD56E}.Debug|Any CPU.Build.0 = Debug|Any CPU {2559DAF9-784E-4C29-E0E1-70204B1FD56E}.Release|Any CPU.ActiveCfg = Release|Any CPU {2559DAF9-784E-4C29-E0E1-70204B1FD56E}.Release|Any CPU.Build.0 = Release|Any CPU + {A8D5CFFA-7F9E-C35B-4F19-D63F6EC1D5CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A8D5CFFA-7F9E-C35B-4F19-D63F6EC1D5CA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A8D5CFFA-7F9E-C35B-4F19-D63F6EC1D5CA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A8D5CFFA-7F9E-C35B-4F19-D63F6EC1D5CA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1671,6 +1679,8 @@ Global {5D60CF30-28A9-9F0F-7610-D90E4A69AE73} = {58A2876A-618D-4AE6-A136-E44B42BBDE11} {967723E8-4FDD-447B-99F6-4F8C47CB5433} = {C2D1346B-9665-4150-B644-075CF1636BAA} {2559DAF9-784E-4C29-E0E1-70204B1FD56E} = {C2D1346B-9665-4150-B644-075CF1636BAA} + {BD974609-C68B-4BE6-9682-EB132462B50D} = {C2D1346B-9665-4150-B644-075CF1636BAA} + {A8D5CFFA-7F9E-C35B-4F19-D63F6EC1D5CA} = {C2D1346B-9665-4150-B644-075CF1636BAA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {604E6B91-7BC0-4126-AE07-D4D2FEFC3D29} @@ -1679,10 +1689,12 @@ Global src\Analyzers\VisualBasic\CodeFixes\VisualBasicCodeFixes.projitems*{0141285d-8f6c-42c7-baf3-3c0ccd61c716}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Workspace\VisualBasic\VisualBasicWorkspaceExtensions.projitems*{0141285d-8f6c-42c7-baf3-3c0ccd61c716}*SharedItemsImports = 5 src\Compilers\CSharp\csc\CscCommandLine.projitems*{0161e25c-918a-4dc8-9648-30fdcc8e31e9}*SharedItemsImports = 5 + src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{0c2e1633-1462-4712-88f4-a0c945bad3a8}*SharedItemsImports = 5 src\Analyzers\Core\CodeFixes\CodeFixes.projitems*{1b6c4a1a-413b-41fb-9f85-5c09118e541b}*SharedItemsImports = 13 src\Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 5 src\Dependencies\CodeAnalysis.Debugging\Microsoft.CodeAnalysis.Debugging.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 5 src\Dependencies\Collections\Microsoft.CodeAnalysis.Collections.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 5 + src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 5 src\Dependencies\PooledObjects\Microsoft.CodeAnalysis.PooledObjects.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Compiler\CSharp\CSharpCompilerExtensions.projitems*{21b239d0-d144-430f-a394-c066d58ee267}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Workspace\CSharp\CSharpWorkspaceExtensions.projitems*{21b239d0-d144-430f-a394-c066d58ee267}*SharedItemsImports = 5 @@ -1690,17 +1702,21 @@ Global src\Analyzers\VisualBasic\Analyzers\VisualBasicAnalyzers.projitems*{2531a8c4-97dd-47bc-a79c-b7846051e137}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Compiler\VisualBasic\VisualBasicCompilerExtensions.projitems*{2531a8c4-97dd-47bc-a79c-b7846051e137}*SharedItemsImports = 5 src\Dependencies\Collections\Microsoft.CodeAnalysis.Collections.projitems*{2559daf9-784e-4c29-e0e1-70204b1fd56e}*SharedItemsImports = 5 + src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{2559daf9-784e-4c29-e0e1-70204b1fd56e}*SharedItemsImports = 5 src\Dependencies\PooledObjects\Microsoft.CodeAnalysis.PooledObjects.projitems*{2559daf9-784e-4c29-e0e1-70204b1fd56e}*SharedItemsImports = 5 src\Analyzers\Core\Analyzers\Analyzers.projitems*{275812ee-dedb-4232-9439-91c9757d2ae4}*SharedItemsImports = 5 src\Dependencies\Collections\Microsoft.CodeAnalysis.Collections.projitems*{275812ee-dedb-4232-9439-91c9757d2ae4}*SharedItemsImports = 5 + src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{275812ee-dedb-4232-9439-91c9757d2ae4}*SharedItemsImports = 5 src\Dependencies\PooledObjects\Microsoft.CodeAnalysis.PooledObjects.projitems*{275812ee-dedb-4232-9439-91c9757d2ae4}*SharedItemsImports = 5 src\Dependencies\Threading\Microsoft.CodeAnalysis.Threading.projitems*{275812ee-dedb-4232-9439-91c9757d2ae4}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Compiler\Core\CompilerExtensions.projitems*{275812ee-dedb-4232-9439-91c9757d2ae4}*SharedItemsImports = 5 + src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{2801f82b-78ce-4bae-b06f-537574751e2e}*SharedItemsImports = 5 src\ExpressionEvaluator\VisualBasic\Source\ResultProvider\BasicResultProvider.projitems*{3140fe61-0856-4367-9aa3-8081b9a80e35}*SharedItemsImports = 13 src\ExpressionEvaluator\CSharp\Source\ResultProvider\CSharpResultProvider.projitems*{3140fe61-0856-4367-9aa3-8081b9a80e36}*SharedItemsImports = 13 src\Analyzers\CSharp\Analyzers\CSharpAnalyzers.projitems*{3973b09a-4fbf-44a5-8359-3d22ceb71f71}*SharedItemsImports = 5 src\Analyzers\CSharp\CodeFixes\CSharpCodeFixes.projitems*{3973b09a-4fbf-44a5-8359-3d22ceb71f71}*SharedItemsImports = 5 src\Compilers\CSharp\CSharpAnalyzerDriver\CSharpAnalyzerDriver.projitems*{3973b09a-4fbf-44a5-8359-3d22ceb71f71}*SharedItemsImports = 5 + src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{41ed1bfa-fdad-4fe4-8118-db23fb49b0b0}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Workspace\CSharp\CSharpWorkspaceExtensions.projitems*{438db8af-f3f0-4ed9-80b5-13fddd5b8787}*SharedItemsImports = 13 src\Compilers\CSharp\csc\CscCommandLine.projitems*{4b45ca0c-03a0-400f-b454-3d4bcb16af38}*SharedItemsImports = 5 src\Analyzers\CSharp\Tests\CSharpAnalyzers.UnitTests.projitems*{5018d049-5870-465a-889b-c742ce1e31cb}*SharedItemsImports = 5 @@ -1709,6 +1725,7 @@ Global src\Workspaces\SharedUtilitiesAndExtensions\Workspace\VisualBasic\VisualBasicWorkspaceExtensions.projitems*{57ca988d-f010-4bf2-9a2e-07d6dcd2ff2c}*SharedItemsImports = 5 src\Analyzers\CSharp\Tests\CSharpAnalyzers.UnitTests.projitems*{58969243-7f59-4236-93d0-c93b81f569b3}*SharedItemsImports = 13 src\Dependencies\Collections\Microsoft.CodeAnalysis.Collections.projitems*{5f8d2414-064a-4b3a-9b42-8e2a04246be5}*SharedItemsImports = 5 + src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{5f8d2414-064a-4b3a-9b42-8e2a04246be5}*SharedItemsImports = 5 src\Dependencies\PooledObjects\Microsoft.CodeAnalysis.PooledObjects.projitems*{5f8d2414-064a-4b3a-9b42-8e2a04246be5}*SharedItemsImports = 5 src\Dependencies\Threading\Microsoft.CodeAnalysis.Threading.projitems*{5f8d2414-064a-4b3a-9b42-8e2a04246be5}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Compiler\Core\CompilerExtensions.projitems*{5f8d2414-064a-4b3a-9b42-8e2a04246be5}*SharedItemsImports = 5 @@ -1719,11 +1736,14 @@ Global src\LanguageServer\Microsoft.CommonLanguageServerProtocol.Framework\Microsoft.CommonLanguageServerProtocol.Framework.Shared.projitems*{64eaded3-4b5d-4431-bbe5-a4aba1c38c00}*SharedItemsImports = 13 src\LanguageServer\Microsoft.CommonLanguageServerProtocol.Framework\Microsoft.CommonLanguageServerProtocol.Framework.Shared.projitems*{686bf57e-a6ff-467b-aab3-44de916a9772}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Compiler\CSharp\CSharpCompilerExtensions.projitems*{699fea05-aea7-403d-827e-53cf4e826955}*SharedItemsImports = 13 + src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{6fc8e6f5-659c-424d-aeb5-331b95883e29}*SharedItemsImports = 5 src\ExpressionEvaluator\VisualBasic\Source\ResultProvider\BasicResultProvider.projitems*{76242a2d-2600-49dd-8c15-fea07ecb1843}*SharedItemsImports = 5 src\Analyzers\Core\Analyzers\Analyzers.projitems*{76e96966-4780-4040-8197-bde2879516f4}*SharedItemsImports = 13 + src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{7ad4fe65-9a30-41a6-8004-aa8f89bcb7f3}*SharedItemsImports = 5 src\Analyzers\VisualBasic\Tests\VisualBasicAnalyzers.UnitTests.projitems*{7b7f4153-ae93-4908-b8f0-430871589f83}*SharedItemsImports = 13 src\Compilers\VisualBasic\vbc\VbcCommandLine.projitems*{810b02ad-2ea5-4422-88ac-b71b8ab0df0b}*SharedItemsImports = 13 src\Dependencies\Collections\Microsoft.CodeAnalysis.Collections.projitems*{8e2a252e-a140-45a6-a81a-2652996ea589}*SharedItemsImports = 5 + src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{8e2a252e-a140-45a6-a81a-2652996ea589}*SharedItemsImports = 5 src\Dependencies\Threading\Microsoft.CodeAnalysis.Threading.projitems*{8e2a252e-a140-45a6-a81a-2652996ea589}*SharedItemsImports = 5 src\Analyzers\VisualBasic\Analyzers\VisualBasicAnalyzers.projitems*{94faf461-2e74-4dbb-9813-6b2cde6f1880}*SharedItemsImports = 13 src\Compilers\Server\VBCSCompiler\VBCSCompilerCommandLine.projitems*{9508f118-f62e-4c16-a6f4-7c3b56e166ad}*SharedItemsImports = 5 @@ -1738,21 +1758,26 @@ Global src\Compilers\VisualBasic\BasicAnalyzerDriver\BasicAnalyzerDriver.projitems*{a1bcd0ce-6c2f-4f8c-9a48-d9d93928e26d}*SharedItemsImports = 5 src\Analyzers\CSharp\Analyzers\CSharpAnalyzers.projitems*{aa87bfed-089a-4096-b8d5-690bdc7d5b24}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Compiler\CSharp\CSharpCompilerExtensions.projitems*{aa87bfed-089a-4096-b8d5-690bdc7d5b24}*SharedItemsImports = 5 + src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{abdbac1e-350e-4dc3-bb45-3504404545ee}*SharedItemsImports = 5 src\ExpressionEvaluator\Core\Source\ResultProvider\ResultProvider.projitems*{abdbac1e-350e-4dc3-bb45-3504404545ee}*SharedItemsImports = 5 src\ExpressionEvaluator\VisualBasic\Source\ResultProvider\BasicResultProvider.projitems*{ace53515-482c-4c6a-e2d2-4242a687dfee}*SharedItemsImports = 5 src\Compilers\CSharp\csc\CscCommandLine.projitems*{b021ccbc-b2af-4560-af28-ed055f0ed696}*SharedItemsImports = 13 src\Dependencies\Collections\Microsoft.CodeAnalysis.Collections.projitems*{b1481d94-682e-46ec-adbe-a16eb46feee9}*SharedItemsImports = 5 + src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{b1481d94-682e-46ec-adbe-a16eb46feee9}*SharedItemsImports = 5 src\Dependencies\PooledObjects\Microsoft.CodeAnalysis.PooledObjects.projitems*{b1481d94-682e-46ec-adbe-a16eb46feee9}*SharedItemsImports = 5 src\Compilers\CSharp\CSharpAnalyzerDriver\CSharpAnalyzerDriver.projitems*{b501a547-c911-4a05-ac6e-274a50dff30e}*SharedItemsImports = 5 src\ExpressionEvaluator\Core\Source\ResultProvider\ResultProvider.projitems*{bb3ca047-5d00-48d4-b7d3-233c1265c065}*SharedItemsImports = 13 + src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{bd9539eb-aa5e-4e67-ac7f-97d7cbc4d0c9}*SharedItemsImports = 5 + src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{bd974609-c68b-4be6-9682-eb132462b50d}*SharedItemsImports = 13 src\ExpressionEvaluator\CSharp\Source\ResultProvider\CSharpResultProvider.projitems*{bf9dac1e-3a5e-4dc3-bb44-9a64e0d4e9d4}*SharedItemsImports = 5 src\Dependencies\PooledObjects\Microsoft.CodeAnalysis.PooledObjects.projitems*{c1930979-c824-496b-a630-70f5369a636f}*SharedItemsImports = 13 src\Workspaces\SharedUtilitiesAndExtensions\Compiler\VisualBasic\VisualBasicCompilerExtensions.projitems*{cec0dce7-8d52-45c3-9295-fc7b16bd2451}*SharedItemsImports = 13 src\Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems*{d0bc9be7-24f6-40ca-8dc6-fcb93bd44b34}*SharedItemsImports = 13 - src\LanguageServer\Microsoft.CommonLanguageServerProtocol.Framework.Shared\Microsoft.CommonLanguageServerProtocol.Framework.Shared.projitems*{d2589bce-4f2e-4113-b7e7-37392c0c5492}*SharedItemsImports = 5 + src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{d2589bce-4f2e-4113-b7e7-37392c0c5492}*SharedItemsImports = 5 src\Dependencies\CodeAnalysis.Debugging\Microsoft.CodeAnalysis.Debugging.projitems*{d73adf7d-2c1c-42ae-b2ab-edc9497e4b71}*SharedItemsImports = 13 src\Compilers\Server\VBCSCompiler\VBCSCompilerCommandLine.projitems*{d8ef0777-9d65-4849-a7d6-ac81e58e2317}*SharedItemsImports = 13 src\Analyzers\CSharp\CodeFixes\CSharpCodeFixes.projitems*{da973826-c985-4128-9948-0b445e638bdb}*SharedItemsImports = 13 + src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{db96c25f-39a9-4a6a-92bc-d1e42717308f}*SharedItemsImports = 5 src\Compilers\Server\VBCSCompiler\VBCSCompilerCommandLine.projitems*{dc8c78cc-b6fe-47bf-93b1-b65a1c67c08d}*SharedItemsImports = 5 src\Analyzers\VisualBasic\Tests\VisualBasicAnalyzers.UnitTests.projitems*{e512c6c1-f085-4ad7-b0d9-e8f1a0a2a510}*SharedItemsImports = 5 src\Compilers\VisualBasic\vbc\VbcCommandLine.projitems*{e58ee9d7-1239-4961-a0c1-f9ec3952c4c1}*SharedItemsImports = 5 @@ -1766,6 +1791,8 @@ Global src\Analyzers\Core\CodeFixes\CodeFixes.projitems*{edc68a0e-c68d-4a74-91b7-bf38ec909888}*SharedItemsImports = 5 src\Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems*{edc68a0e-c68d-4a74-91b7-bf38ec909888}*SharedItemsImports = 5 src\Dependencies\CodeAnalysis.Debugging\Microsoft.CodeAnalysis.Debugging.projitems*{edc68a0e-c68d-4a74-91b7-bf38ec909888}*SharedItemsImports = 5 + src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{fa0e905d-ec46-466d-b7b2-3b5557f9428c}*SharedItemsImports = 5 src\ExpressionEvaluator\Core\Source\ResultProvider\ResultProvider.projitems*{fa0e905d-ec46-466d-b7b2-3b5557f9428c}*SharedItemsImports = 5 + src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{fce88bbd-9bbd-4871-b9b0-de176d73a6b0}*SharedItemsImports = 5 EndGlobalSection EndGlobal diff --git a/eng/config/PublishData.json b/eng/config/PublishData.json index ee344aed72e29..8cb503ec64893 100644 --- a/eng/config/PublishData.json +++ b/eng/config/PublishData.json @@ -31,6 +31,7 @@ "Microsoft.Net.Compilers.Toolset.Arm64": "arcade", "Microsoft.Net.Compilers.Toolset.Framework": "arcade", "Microsoft.NETCore.Compilers": "arcade", + "Microsoft.CodeAnalysis.Contracts": "arcade", "Microsoft.CodeAnalysis.Debugging": "arcade", "Microsoft.CodeAnalysis.PooledObjects": "arcade", "Microsoft.CodeAnalysis.Collections": "arcade", diff --git a/eng/test-rebuild.ps1 b/eng/test-rebuild.ps1 index 294a34d7e776c..74017ea313302 100644 --- a/eng/test-rebuild.ps1 +++ b/eng/test-rebuild.ps1 @@ -57,6 +57,9 @@ try { # Rebuilds with other issues " --exclude net472\Microsoft.CodeAnalysis.EditorFeatures2.UnitTests.dll" + " --exclude net9.0\Microsoft.CodeAnalysis.Collections.Package.dll" + + " --exclude netstandard2.0\Microsoft.CodeAnalysis.Contracts.Package.dll" + + " --exclude net8.0\Microsoft.CodeAnalysis.Contracts.Package.dll" + + " --exclude net9.0\Microsoft.CodeAnalysis.Contracts.Package.dll" + " --exclude netcoreapp3.1\Microsoft.CodeAnalysis.Collections.Package.dll" + " --exclude netstandard2.0\Microsoft.CodeAnalysis.Collections.Package.dll" + " --exclude netstandard2.0\Microsoft.CodeAnalysis.Debugging.Package.dll" + diff --git a/src/CodeStyle/Core/Analyzers/Microsoft.CodeAnalysis.CodeStyle.csproj b/src/CodeStyle/Core/Analyzers/Microsoft.CodeAnalysis.CodeStyle.csproj index c2756b64e73a8..8860cb81eeda1 100644 --- a/src/CodeStyle/Core/Analyzers/Microsoft.CodeAnalysis.CodeStyle.csproj +++ b/src/CodeStyle/Core/Analyzers/Microsoft.CodeAnalysis.CodeStyle.csproj @@ -50,5 +50,6 @@ + \ No newline at end of file diff --git a/src/CodeStyle/Tools/CodeStyleConfigFileGenerator.csproj b/src/CodeStyle/Tools/CodeStyleConfigFileGenerator.csproj index 73a974e028331..0fd6810eb515b 100644 --- a/src/CodeStyle/Tools/CodeStyleConfigFileGenerator.csproj +++ b/src/CodeStyle/Tools/CodeStyleConfigFileGenerator.csproj @@ -8,13 +8,12 @@ - - + diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundInterpolatedStringArgumentPlaceholder.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundInterpolatedStringArgumentPlaceholder.cs index b1645affc7569..895ccef3c4f0b 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundInterpolatedStringArgumentPlaceholder.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundInterpolatedStringArgumentPlaceholder.cs @@ -10,6 +10,6 @@ internal partial class BoundInterpolatedStringArgumentPlaceholder public const int TrailingConstructorValidityParameter = -2; public const int UnspecifiedParameter = -3; - public sealed override bool IsEquivalentToThisReference => throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + public sealed override bool IsEquivalentToThisReference => throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/ExpandedVarargsMethodReference.cs b/src/Compilers/CSharp/Portable/Emitter/Model/ExpandedVarargsMethodReference.cs index 5902f478f2db2..c4e2e505af41a 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/ExpandedVarargsMethodReference.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/ExpandedVarargsMethodReference.cs @@ -225,13 +225,13 @@ private static void Append(PooledStringBuilder result, object value) public sealed override bool Equals(object obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeReference.cs b/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeReference.cs index da87c2a8b6779..0636eb1fa9634 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeReference.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeReference.cs @@ -168,13 +168,13 @@ Cci.IDefinition Cci.IReference.AsDefinition(EmitContext context) public sealed override bool Equals(object obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/ParameterTypeInformation.cs b/src/Compilers/CSharp/Portable/Emitter/Model/ParameterTypeInformation.cs index 973d8feaefb09..7022719476367 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/ParameterTypeInformation.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/ParameterTypeInformation.cs @@ -69,13 +69,13 @@ public override string ToString() public sealed override bool Equals(object obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } } @@ -120,13 +120,13 @@ ushort Cci.IParameterListEntry.Index public sealed override bool Equals(object obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/SymbolAdapter.cs b/src/Compilers/CSharp/Portable/Emitter/Model/SymbolAdapter.cs index afd34feab5b3c..44b552ef719cb 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/SymbolAdapter.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/SymbolAdapter.cs @@ -180,13 +180,13 @@ public sealed override string ToString() public sealed override bool Equals(object obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } [Conditional("DEBUG")] diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/TypeMemberReference.cs b/src/Compilers/CSharp/Portable/Emitter/Model/TypeMemberReference.cs index a6fccdc4eaa3d..e10acefabb91c 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/TypeMemberReference.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/TypeMemberReference.cs @@ -53,13 +53,13 @@ Cci.IDefinition Cci.IReference.AsDefinition(EmitContext context) public sealed override bool Equals(object obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/ObjectDisplay.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/ObjectDisplay.cs index 68aae1cb8193a..760ef38769c3f 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/ObjectDisplay.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/ObjectDisplay.cs @@ -5,12 +5,9 @@ #nullable disable using System; -using System.Diagnostics; using System.Globalization; using System.Reflection; -using System.Text; using Microsoft.CodeAnalysis.PooledObjects; -using ExceptionUtilities = Roslyn.Utilities.ExceptionUtilities; namespace Microsoft.CodeAnalysis.CSharp { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/OperatorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/OperatorTests.cs index 74966d84778a6..e22052dedc114 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/OperatorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/OperatorTests.cs @@ -8789,7 +8789,7 @@ struct TestStr Assert.Null(info1.Symbol); break; default: - throw Roslyn.Utilities.ExceptionUtilities.UnexpectedValue(i); + throw ExceptionUtilities.UnexpectedValue(i); } } } diff --git a/src/Compilers/Core/MSBuildTask/Microsoft.Build.Tasks.CodeAnalysis.csproj b/src/Compilers/Core/MSBuildTask/Microsoft.Build.Tasks.CodeAnalysis.csproj index d40e9c02f461d..1cc48b02c01b1 100644 --- a/src/Compilers/Core/MSBuildTask/Microsoft.Build.Tasks.CodeAnalysis.csproj +++ b/src/Compilers/Core/MSBuildTask/Microsoft.Build.Tasks.CodeAnalysis.csproj @@ -17,7 +17,7 @@ https://github.com/dotnet/arcade/issues/9305 --> false - + $(NoWarn);CA1819 @@ -53,14 +53,10 @@ - - - - @@ -80,9 +76,7 @@ - + PreserveNewest @@ -94,8 +88,7 @@ - + <_CompilerApiVersion>$([System.Version]::Parse($(VersionPrefix)).Major).$([System.Version]::Parse($(VersionPrefix)).Minor) @@ -108,9 +101,8 @@ - + + + diff --git a/src/Compilers/Core/Portable/CodeGen/ArrayMembers.cs b/src/Compilers/Core/Portable/CodeGen/ArrayMembers.cs index 5d4e4c662f8ff..39662c00c568e 100644 --- a/src/Compilers/Core/Portable/CodeGen/ArrayMembers.cs +++ b/src/Compilers/Core/Portable/CodeGen/ArrayMembers.cs @@ -365,13 +365,13 @@ public override string ToString() public sealed override bool Equals(object? obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs b/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs index 4866750134114..87b9df89e731c 100644 --- a/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs +++ b/src/Compilers/Core/Portable/CodeGen/PrivateImplementationDetails.cs @@ -917,13 +917,13 @@ public MetadataConstant Constant public sealed override bool Equals(object? obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } } @@ -1131,13 +1131,13 @@ public TypeDefinitionHandle TypeDef public sealed override bool Equals(object? obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/Core/Portable/Emit/ErrorType.cs b/src/Compilers/Core/Portable/Emit/ErrorType.cs index f7d2ef047a65e..1c844f2f4484b 100644 --- a/src/Compilers/Core/Portable/Emit/ErrorType.cs +++ b/src/Compilers/Core/Portable/Emit/ErrorType.cs @@ -193,13 +193,13 @@ string Cci.INamedEntity.Name public sealed override bool Equals(object obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } /// diff --git a/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedMember.cs b/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedMember.cs index f2ec63e12cd4f..114247c93730c 100644 --- a/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedMember.cs +++ b/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedMember.cs @@ -122,13 +122,13 @@ Cci.IDefinition Cci.IReference.AsDefinition(EmitContext context) public sealed override bool Equals(object obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedParameter.cs b/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedParameter.cs index b8c27026b7500..0f751bbb4cc5e 100644 --- a/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedParameter.cs +++ b/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedParameter.cs @@ -274,13 +274,13 @@ public override string ToString() public sealed override bool Equals(object obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedType.cs b/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedType.cs index d885927dfdd45..ea19ac97129ee 100644 --- a/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedType.cs +++ b/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedType.cs @@ -718,13 +718,13 @@ public override string ToString() public sealed override bool Equals(object obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedTypeParameter.cs b/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedTypeParameter.cs index 1898e994606ea..b7d19bd66f3cf 100644 --- a/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedTypeParameter.cs +++ b/src/Compilers/Core/Portable/Emit/NoPia/CommonEmbeddedTypeParameter.cs @@ -239,13 +239,13 @@ Cci.IMethodReference Cci.IGenericMethodParameterReference.DefiningMethod public sealed override bool Equals(object obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/Emit/NoPia/VtblGap.cs b/src/Compilers/Core/Portable/Emit/NoPia/VtblGap.cs index 696403f6dca06..12fe6d43652e7 100644 --- a/src/Compilers/Core/Portable/Emit/NoPia/VtblGap.cs +++ b/src/Compilers/Core/Portable/Emit/NoPia/VtblGap.cs @@ -259,13 +259,13 @@ Cci.ITypeReference Cci.ISignature.GetType(EmitContext context) public sealed override bool Equals(object obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/FileSystem/FileUtilities.cs b/src/Compilers/Core/Portable/FileSystem/FileUtilities.cs index 66cd96adace05..be2435b56dfd5 100644 --- a/src/Compilers/Core/Portable/FileSystem/FileUtilities.cs +++ b/src/Compilers/Core/Portable/FileSystem/FileUtilities.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.IO; using System.Threading.Tasks; +using Microsoft.CodeAnalysis; namespace Roslyn.Utilities { diff --git a/src/Compilers/Core/Portable/InternalUtilities/EmptyComparer.cs b/src/Compilers/Core/Portable/InternalUtilities/EmptyComparer.cs index 71e3a791ee95b..0f61efb45d893 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/EmptyComparer.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/EmptyComparer.cs @@ -3,8 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.CompilerServices; +using Microsoft.CodeAnalysis; namespace Roslyn.Utilities { diff --git a/src/Compilers/Core/Portable/InternalUtilities/ExceptionExtensions.cs b/src/Compilers/Core/Portable/InternalUtilities/ExceptionExtensions.cs new file mode 100644 index 0000000000000..92de8e4d05892 --- /dev/null +++ b/src/Compilers/Core/Portable/InternalUtilities/ExceptionExtensions.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Threading; + +namespace Microsoft.CodeAnalysis; + +internal static class ExceptionExtensions +{ + /// + /// Determine if an exception was an , and that the provided token caused the cancellation. + /// + /// The exception to test. + /// Checked to see if the provided token was cancelled. + /// if the exception was an and the token was canceled. + internal static bool IsCurrentOperationBeingCancelled(this Exception exception, CancellationToken cancellationToken) + => exception is OperationCanceledException && cancellationToken.IsCancellationRequested; +} diff --git a/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs b/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs index a620bf64f1de7..ba93aec54dd86 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs @@ -4,11 +4,9 @@ using System; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.CompilerServices; using System.Threading; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ErrorReporting { diff --git a/src/Compilers/Core/Portable/InternalUtilities/ReaderWriterLockSlimExtensions.cs b/src/Compilers/Core/Portable/InternalUtilities/ReaderWriterLockSlimExtensions.cs index d75c522a02f6c..0516e0d67c7fc 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/ReaderWriterLockSlimExtensions.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/ReaderWriterLockSlimExtensions.cs @@ -4,6 +4,7 @@ using System; using System.Threading; +using Microsoft.CodeAnalysis; namespace Roslyn.Utilities { diff --git a/src/Compilers/Core/Portable/InternalUtilities/RoslynParallel.cs b/src/Compilers/Core/Portable/InternalUtilities/RoslynParallel.cs index 58ea9a75a47f7..b0d035b8f5ce8 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/RoslynParallel.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/RoslynParallel.cs @@ -5,6 +5,7 @@ using System; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.ErrorReporting; namespace Roslyn.Utilities diff --git a/src/Compilers/Core/Portable/InternalUtilities/SemaphoreSlimExtensions.cs b/src/Compilers/Core/Portable/InternalUtilities/SemaphoreSlimExtensions.cs index e8e057a8b950d..27fa928fab65c 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/SemaphoreSlimExtensions.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/SemaphoreSlimExtensions.cs @@ -5,6 +5,7 @@ using System; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis; namespace Roslyn.Utilities { diff --git a/src/Compilers/Core/Portable/InternalUtilities/SingleInitNullable.cs b/src/Compilers/Core/Portable/InternalUtilities/SingleInitNullable.cs index 1e4633cee50bb..24299ce099285 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/SingleInitNullable.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/SingleInitNullable.cs @@ -4,6 +4,7 @@ using System; using System.Threading; +using Microsoft.CodeAnalysis; namespace Roslyn.Utilities; diff --git a/src/Compilers/Core/Portable/Microsoft.CodeAnalysis.csproj b/src/Compilers/Core/Portable/Microsoft.CodeAnalysis.csproj index dfe5d9e772867..cfa76200dd450 100644 --- a/src/Compilers/Core/Portable/Microsoft.CodeAnalysis.csproj +++ b/src/Compilers/Core/Portable/Microsoft.CodeAnalysis.csproj @@ -6,7 +6,7 @@ Microsoft.CodeAnalysis true $(NetRoslynSourceBuild);netstandard2.0 - $(DefineConstants);COMPILERCORE + $(DefineConstants);COMPILERCORE;MICROSOFT_CODEANALYSIS_CONTRACTS_NO_CONTRACT $(DefineConstants);MICROSOFT_CODEANALYSIS_POOLEDOBJECTS_NO_POOLED_DISPOSER @@ -119,4 +119,5 @@ + diff --git a/src/Compilers/Core/Portable/PEWriter/InheritedTypeParameter.cs b/src/Compilers/Core/Portable/PEWriter/InheritedTypeParameter.cs index a988641b3ca32..a90fc8b2212b8 100644 --- a/src/Compilers/Core/Portable/PEWriter/InheritedTypeParameter.cs +++ b/src/Compilers/Core/Portable/PEWriter/InheritedTypeParameter.cs @@ -2,10 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using System.Reflection.Metadata; -using Roslyn.Utilities; +using Microsoft.CodeAnalysis; using EmitContext = Microsoft.CodeAnalysis.Emit.EmitContext; namespace Microsoft.Cci @@ -307,13 +306,13 @@ public bool IsGenericTypeInstance public sealed override bool Equals(object? obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataVisitor.cs b/src/Compilers/Core/Portable/PEWriter/MetadataVisitor.cs index ccb46b02b74f8..f92843a23a2b2 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataVisitor.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataVisitor.cs @@ -3,11 +3,11 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Diagnostics; -using Roslyn.Utilities; using System.Collections.Immutable; -using Microsoft.CodeAnalysis.Emit; +using System.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeGen; +using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Emit.EditAndContinue; namespace Microsoft.Cci diff --git a/src/Compilers/Core/Portable/PEWriter/MethodDefinitionBase.cs b/src/Compilers/Core/Portable/PEWriter/MethodDefinitionBase.cs index 2ecd2e3d02edf..712124d888ce8 100644 --- a/src/Compilers/Core/Portable/PEWriter/MethodDefinitionBase.cs +++ b/src/Compilers/Core/Portable/PEWriter/MethodDefinitionBase.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Reflection; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeGen; using Microsoft.CodeAnalysis.Debugging; using Microsoft.CodeAnalysis.Emit; diff --git a/src/Compilers/Core/Portable/PEWriter/ModifiedTypeReference.cs b/src/Compilers/Core/Portable/PEWriter/ModifiedTypeReference.cs index 5d3d9b805dbe9..5dfc45e136099 100644 --- a/src/Compilers/Core/Portable/PEWriter/ModifiedTypeReference.cs +++ b/src/Compilers/Core/Portable/PEWriter/ModifiedTypeReference.cs @@ -6,6 +6,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Reflection.Metadata; +using Microsoft.CodeAnalysis; using Roslyn.Utilities; using EmitContext = Microsoft.CodeAnalysis.Emit.EmitContext; @@ -150,13 +151,13 @@ void IReference.Dispatch(MetadataVisitor visitor) public sealed override bool Equals(object? obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/PEWriter/ParameterDefinitionBase.cs b/src/Compilers/Core/Portable/PEWriter/ParameterDefinitionBase.cs index 1eceff4aa72e7..886b50dc0e8be 100644 --- a/src/Compilers/Core/Portable/PEWriter/ParameterDefinitionBase.cs +++ b/src/Compilers/Core/Portable/PEWriter/ParameterDefinitionBase.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Collections.Immutable; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeGen; using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Symbols; @@ -36,12 +37,12 @@ internal abstract class ParameterDefinitionBase : Cci.IParameterDefinition public sealed override bool Equals(object? obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/Core/Portable/PEWriter/RootModuleType.cs b/src/Compilers/Core/Portable/PEWriter/RootModuleType.cs index 9db1f14698f3a..e6f49eafe857a 100644 --- a/src/Compilers/Core/Portable/PEWriter/RootModuleType.cs +++ b/src/Compilers/Core/Portable/PEWriter/RootModuleType.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Reflection.Metadata; using System.Runtime.InteropServices; +using Microsoft.CodeAnalysis; using Roslyn.Utilities; using EmitContext = Microsoft.CodeAnalysis.Emit.EmitContext; @@ -329,13 +330,13 @@ IDefinition IReference.AsDefinition(EmitContext context) public sealed override bool Equals(object? obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/Compilers/Core/Portable/PEWriter/SequencePoint.cs b/src/Compilers/Core/Portable/PEWriter/SequencePoint.cs index fbe377f8b1775..57e996fd5a0e7 100644 --- a/src/Compilers/Core/Portable/PEWriter/SequencePoint.cs +++ b/src/Compilers/Core/Portable/PEWriter/SequencePoint.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis; using Roslyn.Utilities; namespace Microsoft.Cci diff --git a/src/Compilers/Core/Portable/SpecialTypeExtensions.cs b/src/Compilers/Core/Portable/SpecialTypeExtensions.cs index 93d075bf02263..bcca9e8a5250d 100644 --- a/src/Compilers/Core/Portable/SpecialTypeExtensions.cs +++ b/src/Compilers/Core/Portable/SpecialTypeExtensions.cs @@ -267,7 +267,7 @@ public static int VBForToShiftBits(this SpecialType specialType) case SpecialType.System_Int64: return 63; default: - throw Roslyn.Utilities.ExceptionUtilities.UnexpectedValue(specialType); + throw ExceptionUtilities.UnexpectedValue(specialType); } } diff --git a/src/Compilers/Core/RebuildTest/Microsoft.CodeAnalysis.Rebuild.UnitTests.csproj b/src/Compilers/Core/RebuildTest/Microsoft.CodeAnalysis.Rebuild.UnitTests.csproj index 52bbbb4a27ccb..22d08e4182578 100644 --- a/src/Compilers/Core/RebuildTest/Microsoft.CodeAnalysis.Rebuild.UnitTests.csproj +++ b/src/Compilers/Core/RebuildTest/Microsoft.CodeAnalysis.Rebuild.UnitTests.csproj @@ -7,9 +7,6 @@ true $(NetRoslyn);net472 - - - diff --git a/src/Compilers/Test/Core/Metadata/MetadataReaderUtils.cs b/src/Compilers/Test/Core/Metadata/MetadataReaderUtils.cs index 8cd21b15c022b..f017a680afbeb 100644 --- a/src/Compilers/Test/Core/Metadata/MetadataReaderUtils.cs +++ b/src/Compilers/Test/Core/Metadata/MetadataReaderUtils.cs @@ -13,6 +13,7 @@ using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Reflection.PortableExecutable; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Debugging; using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.PooledObjects; diff --git a/src/Compilers/VisualBasic/Portable/Emit/NamedTypeReference.vb b/src/Compilers/VisualBasic/Portable/Emit/NamedTypeReference.vb index 30f4422a89728..81e2608acb954 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/NamedTypeReference.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/NamedTypeReference.vb @@ -123,12 +123,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit Public NotOverridable Overrides Function Equals(obj As Object) As Boolean ' It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - Throw Roslyn.Utilities.ExceptionUtilities.Unreachable + Throw ExceptionUtilities.Unreachable End Function Public NotOverridable Overrides Function GetHashCode() As Integer ' It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - Throw Roslyn.Utilities.ExceptionUtilities.Unreachable + Throw ExceptionUtilities.Unreachable End Function End Class End Namespace diff --git a/src/Compilers/VisualBasic/Portable/Emit/ParameterTypeInformation.vb b/src/Compilers/VisualBasic/Portable/Emit/ParameterTypeInformation.vb index 4f3ea0aed9844..dadb28702f577 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/ParameterTypeInformation.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/ParameterTypeInformation.vb @@ -50,12 +50,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit Public Overrides Function Equals(obj As Object) As Boolean ' It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - Throw Roslyn.Utilities.ExceptionUtilities.Unreachable + Throw ExceptionUtilities.Unreachable End Function Public Overrides Function GetHashCode() As Integer ' It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - Throw Roslyn.Utilities.ExceptionUtilities.Unreachable + Throw ExceptionUtilities.Unreachable End Function End Class End Namespace diff --git a/src/Compilers/VisualBasic/Portable/Emit/SymbolAdapter.vb b/src/Compilers/VisualBasic/Portable/Emit/SymbolAdapter.vb index c7c9551f017da..24f1d61b76320 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/SymbolAdapter.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/SymbolAdapter.vb @@ -154,12 +154,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Public NotOverridable Overrides Function Equals(obj As Object) As Boolean ' It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - Throw Roslyn.Utilities.ExceptionUtilities.Unreachable + Throw ExceptionUtilities.Unreachable End Function Public NotOverridable Overrides Function GetHashCode() As Integer ' It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - Throw Roslyn.Utilities.ExceptionUtilities.Unreachable + Throw ExceptionUtilities.Unreachable End Function diff --git a/src/Compilers/VisualBasic/Portable/Emit/TypeMemberReference.vb b/src/Compilers/VisualBasic/Portable/Emit/TypeMemberReference.vb index e53016af2ca34..0071a89ee6041 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/TypeMemberReference.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/TypeMemberReference.vb @@ -42,12 +42,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit Public NotOverridable Overrides Function Equals(obj As Object) As Boolean ' It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - Throw Roslyn.Utilities.ExceptionUtilities.Unreachable + Throw ExceptionUtilities.Unreachable End Function Public NotOverridable Overrides Function GetHashCode() As Integer ' It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - Throw Roslyn.Utilities.ExceptionUtilities.Unreachable + Throw ExceptionUtilities.Unreachable End Function End Class End Namespace diff --git a/src/Dependencies/Collections/Microsoft.CodeAnalysis.Collections.Package.csproj b/src/Dependencies/Collections/Microsoft.CodeAnalysis.Collections.Package.csproj index cbcddc860d25f..e77a382a6ca5a 100644 --- a/src/Dependencies/Collections/Microsoft.CodeAnalysis.Collections.Package.csproj +++ b/src/Dependencies/Collections/Microsoft.CodeAnalysis.Collections.Package.csproj @@ -20,10 +20,6 @@ $(NoWarn);NU5128 - - - - @@ -46,11 +42,13 @@ - <_File Remove="@(_File)"/> + <_File Remove="@(_File)" /> <_File Include="$(MSBuildProjectDirectory)\**\*.resx" TargetDir="contentFiles/$(_LanguageDirName)/$(TargetFramework)" BuildAction="EmbeddedResource" /> <_File Include="$(MSBuildProjectDirectory)\**\*.xlf" TargetDir="contentFiles/$(_LanguageDirName)/$(TargetFramework)" BuildAction="None" /> - + + + \ No newline at end of file diff --git a/src/Compilers/Core/Portable/Syntax/CollectionBuilderAttribute.cs b/src/Dependencies/Contracts/CollectionBuilderAttribute.cs similarity index 100% rename from src/Compilers/Core/Portable/Syntax/CollectionBuilderAttribute.cs rename to src/Dependencies/Contracts/CollectionBuilderAttribute.cs diff --git a/src/Compilers/Core/Portable/InternalUtilities/CompilerFeatureRequiredAttribute.cs b/src/Dependencies/Contracts/CompilerFeatureRequiredAttribute.cs similarity index 100% rename from src/Compilers/Core/Portable/InternalUtilities/CompilerFeatureRequiredAttribute.cs rename to src/Dependencies/Contracts/CompilerFeatureRequiredAttribute.cs diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/Contract.InterpolatedStringHandlers.cs b/src/Dependencies/Contracts/Contract.InterpolatedStringHandlers.cs similarity index 96% rename from src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/Contract.InterpolatedStringHandlers.cs rename to src/Dependencies/Contracts/Contract.InterpolatedStringHandlers.cs index 84469add46cbf..628aa3457f573 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/Contract.InterpolatedStringHandlers.cs +++ b/src/Dependencies/Contracts/Contract.InterpolatedStringHandlers.cs @@ -2,12 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if !MICROSOFT_CODEANALYSIS_CONTRACTS_NO_CONTRACT + using System.Runtime.CompilerServices; using System.Text; #pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/58168 -namespace Roslyn.Utilities; +namespace Microsoft.CodeAnalysis; internal static partial class Contract { @@ -65,3 +67,5 @@ public ThrowIfNullInterpolatedStringHandler(int literalLength, int formattedCoun public string GetFormattedText() => _stringBuilder.ToString(); } } + +#endif diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/Contract.cs b/src/Dependencies/Contracts/Contract.cs similarity index 98% rename from src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/Contract.cs rename to src/Dependencies/Contracts/Contract.cs index db14883bd1652..489ffe5bea9de 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/Contract.cs +++ b/src/Dependencies/Contracts/Contract.cs @@ -2,13 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if !MICROSOFT_CODEANALYSIS_CONTRACTS_NO_CONTRACT + using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Runtime.CompilerServices; -namespace Roslyn.Utilities; +namespace Microsoft.CodeAnalysis; internal static partial class Contract { @@ -157,3 +159,5 @@ public static void Fail(string message = "Unexpected", [CallerLineNumber] int li throw new InvalidOperationException($"{message} - file {fileName} line {lineNumber}"); } } + +#endif diff --git a/src/Compilers/Core/Portable/InternalUtilities/ExceptionUtilities.cs b/src/Dependencies/Contracts/ExceptionUtilities.cs similarity index 98% rename from src/Compilers/Core/Portable/InternalUtilities/ExceptionUtilities.cs rename to src/Dependencies/Contracts/ExceptionUtilities.cs index e2c787f7f2eb4..983341521a3c3 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/ExceptionUtilities.cs +++ b/src/Dependencies/Contracts/ExceptionUtilities.cs @@ -7,7 +7,7 @@ using System.Runtime.CompilerServices; using System.Threading; -namespace Roslyn.Utilities +namespace Microsoft.CodeAnalysis { internal static class ExceptionUtilities { diff --git a/src/Compilers/Core/Portable/InternalUtilities/ExperimentalAttribute.cs b/src/Dependencies/Contracts/ExperimentalAttribute.cs similarity index 100% rename from src/Compilers/Core/Portable/InternalUtilities/ExperimentalAttribute.cs rename to src/Dependencies/Contracts/ExperimentalAttribute.cs diff --git a/src/Compilers/Core/Portable/InternalUtilities/IReadOnlySet.cs b/src/Dependencies/Contracts/IReadOnlySet.cs similarity index 100% rename from src/Compilers/Core/Portable/InternalUtilities/IReadOnlySet.cs rename to src/Dependencies/Contracts/IReadOnlySet.cs diff --git a/src/Compilers/Core/Portable/InternalUtilities/InterpolatedStringHandlerArgumentAttribute.cs b/src/Dependencies/Contracts/InterpolatedStringHandlerArgumentAttribute.cs similarity index 100% rename from src/Compilers/Core/Portable/InternalUtilities/InterpolatedStringHandlerArgumentAttribute.cs rename to src/Dependencies/Contracts/InterpolatedStringHandlerArgumentAttribute.cs diff --git a/src/Compilers/Core/Portable/InternalUtilities/InterpolatedStringHandlerAttribute.cs b/src/Dependencies/Contracts/InterpolatedStringHandlerAttribute.cs similarity index 100% rename from src/Compilers/Core/Portable/InternalUtilities/InterpolatedStringHandlerAttribute.cs rename to src/Dependencies/Contracts/InterpolatedStringHandlerAttribute.cs diff --git a/src/Compilers/Core/Portable/InternalUtilities/IsExternalInit.cs b/src/Dependencies/Contracts/IsExternalInit.cs similarity index 100% rename from src/Compilers/Core/Portable/InternalUtilities/IsExternalInit.cs rename to src/Dependencies/Contracts/IsExternalInit.cs diff --git a/src/Dependencies/Contracts/Microsoft.CodeAnalysis.Contracts.Package.csproj b/src/Dependencies/Contracts/Microsoft.CodeAnalysis.Contracts.Package.csproj new file mode 100644 index 0000000000000..d00764e8b32d7 --- /dev/null +++ b/src/Dependencies/Contracts/Microsoft.CodeAnalysis.Contracts.Package.csproj @@ -0,0 +1,26 @@ + + + + + $(NetRoslynSourceBuild);netstandard2.0 + false + false + none + false + true + + + true + true + Microsoft.CodeAnalysis.Contracts + false + + Package containing sources of Microsoft .NET Compiler Platform ("Roslyn") contract and polyfill types. + + + $(NoWarn);NU5128 + + + + + \ No newline at end of file diff --git a/src/Dependencies/Contracts/Microsoft.CodeAnalysis.Contracts.projitems b/src/Dependencies/Contracts/Microsoft.CodeAnalysis.Contracts.projitems new file mode 100644 index 0000000000000..b98b83ea55567 --- /dev/null +++ b/src/Dependencies/Contracts/Microsoft.CodeAnalysis.Contracts.projitems @@ -0,0 +1,32 @@ + + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + BD974609-C68B-4BE6-9682-EB132462B50D + + + Microsoft.CodeAnalysis.Contracts + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Dependencies/Contracts/Microsoft.CodeAnalysis.Contracts.shproj b/src/Dependencies/Contracts/Microsoft.CodeAnalysis.Contracts.shproj new file mode 100644 index 0000000000000..e6fb451a37453 --- /dev/null +++ b/src/Dependencies/Contracts/Microsoft.CodeAnalysis.Contracts.shproj @@ -0,0 +1,14 @@ + + + + + BD974609-C68B-4BE6-9682-EB132462B50D + 14.0 + + + + + + + + \ No newline at end of file diff --git a/src/Compilers/Core/Portable/InternalUtilities/NonCopyableAttribute.cs b/src/Dependencies/Contracts/NonCopyableAttribute.cs similarity index 53% rename from src/Compilers/Core/Portable/InternalUtilities/NonCopyableAttribute.cs rename to src/Dependencies/Contracts/NonCopyableAttribute.cs index edaee07bae0cb..a5efff996a18a 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/NonCopyableAttribute.cs +++ b/src/Dependencies/Contracts/NonCopyableAttribute.cs @@ -2,14 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; -namespace Roslyn.Utilities +namespace Microsoft.CodeAnalysis; + +[AttributeUsage(AttributeTargets.Struct | AttributeTargets.GenericParameter)] +internal sealed class NonCopyableAttribute : Attribute { - [AttributeUsage(AttributeTargets.Struct | AttributeTargets.GenericParameter)] - internal sealed class NonCopyableAttribute : Attribute - { - } } diff --git a/src/Compilers/Core/Portable/InternalUtilities/NonDefaultableAttribute.cs b/src/Dependencies/Contracts/NonDefaultableAttribute.cs similarity index 53% rename from src/Compilers/Core/Portable/InternalUtilities/NonDefaultableAttribute.cs rename to src/Dependencies/Contracts/NonDefaultableAttribute.cs index a0d14a00b4b5b..c89b6aed40f05 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/NonDefaultableAttribute.cs +++ b/src/Dependencies/Contracts/NonDefaultableAttribute.cs @@ -2,14 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; -namespace Roslyn.Utilities +namespace Microsoft.CodeAnalysis; + +[AttributeUsage(AttributeTargets.Struct | AttributeTargets.GenericParameter)] +internal sealed class NonDefaultableAttribute : Attribute { - [AttributeUsage(AttributeTargets.Struct | AttributeTargets.GenericParameter)] - internal sealed class NonDefaultableAttribute : Attribute - { - } } diff --git a/src/Compilers/Core/Portable/InternalUtilities/NullableAttributes.cs b/src/Dependencies/Contracts/NullableAttributes.cs similarity index 100% rename from src/Compilers/Core/Portable/InternalUtilities/NullableAttributes.cs rename to src/Dependencies/Contracts/NullableAttributes.cs diff --git a/src/Compilers/Core/Portable/InternalUtilities/RequiredMemberAttribute.cs b/src/Dependencies/Contracts/RequiredMemberAttribute.cs similarity index 100% rename from src/Compilers/Core/Portable/InternalUtilities/RequiredMemberAttribute.cs rename to src/Dependencies/Contracts/RequiredMemberAttribute.cs diff --git a/src/Compilers/Core/Portable/InternalUtilities/SetsRequiredMembersAttribute.cs b/src/Dependencies/Contracts/SetsRequiredMembersAttribute.cs similarity index 100% rename from src/Compilers/Core/Portable/InternalUtilities/SetsRequiredMembersAttribute.cs rename to src/Dependencies/Contracts/SetsRequiredMembersAttribute.cs diff --git a/src/Dependencies/Threading/AsyncBatchingWorkQueue`0.cs b/src/Dependencies/Threading/AsyncBatchingWorkQueue`0.cs index c6b81aff2e2e4..db94f6efbc8e7 100644 --- a/src/Dependencies/Threading/AsyncBatchingWorkQueue`0.cs +++ b/src/Dependencies/Threading/AsyncBatchingWorkQueue`0.cs @@ -8,7 +8,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Shared.TestHooks; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Threading; diff --git a/src/Dependencies/Threading/AsyncBatchingWorkQueue`1.cs b/src/Dependencies/Threading/AsyncBatchingWorkQueue`1.cs index a2f8d275e3409..a14aebc098cb9 100644 --- a/src/Dependencies/Threading/AsyncBatchingWorkQueue`1.cs +++ b/src/Dependencies/Threading/AsyncBatchingWorkQueue`1.cs @@ -8,7 +8,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Shared.TestHooks; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Threading; diff --git a/src/Dependencies/Threading/AsyncBatchingWorkQueue`2.cs b/src/Dependencies/Threading/AsyncBatchingWorkQueue`2.cs index 89b331ff3e1b7..4844a6f60c807 100644 --- a/src/Dependencies/Threading/AsyncBatchingWorkQueue`2.cs +++ b/src/Dependencies/Threading/AsyncBatchingWorkQueue`2.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.TestHooks; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Threading; diff --git a/src/Dependencies/Threading/Microsoft.CodeAnalysis.Threading.Package.csproj b/src/Dependencies/Threading/Microsoft.CodeAnalysis.Threading.Package.csproj index b25752edccacb..ab9f373d8fec6 100644 --- a/src/Dependencies/Threading/Microsoft.CodeAnalysis.Threading.Package.csproj +++ b/src/Dependencies/Threading/Microsoft.CodeAnalysis.Threading.Package.csproj @@ -21,13 +21,7 @@ - - - - - - @@ -35,4 +29,5 @@ + diff --git a/src/EditorFeatures/CSharpTest/Structure/CommentStructureTests.cs b/src/EditorFeatures/CSharpTest/Structure/CommentStructureTests.cs index 9071be18f4622..3fa9bc368bd14 100644 --- a/src/EditorFeatures/CSharpTest/Structure/CommentStructureTests.cs +++ b/src/EditorFeatures/CSharpTest/Structure/CommentStructureTests.cs @@ -47,7 +47,7 @@ internal override async Task> GetBlockSpansWorkerAsync return CreateCommentBlockSpan(token.TrailingTrivia); } - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } [Fact] diff --git a/src/EditorFeatures/CSharpTest2/EmbeddedLanguages/Json/CSharpJsonParserTests_NstTests.cs b/src/EditorFeatures/CSharpTest2/EmbeddedLanguages/Json/CSharpJsonParserTests_NstTests.cs index ff8ddadcd534f..d6af20208ea5e 100644 --- a/src/EditorFeatures/CSharpTest2/EmbeddedLanguages/Json/CSharpJsonParserTests_NstTests.cs +++ b/src/EditorFeatures/CSharpTest2/EmbeddedLanguages/Json/CSharpJsonParserTests_NstTests.cs @@ -16,7 +16,7 @@ private void TestNST( { var (_, tree, allChars) = JustParseTree(stringText, JsonOptions.Strict, conversionFailureOk: false); Assert.NotNull(tree); - Roslyn.Utilities.Contract.ThrowIfNull(tree); + Contract.ThrowIfNull(tree); var actualTree = TreeToText(tree!).Replace("\"", "\"\""); Assert.Equal(expected.Replace("\"", "\"\""), actualTree); diff --git a/src/EditorFeatures/Core/GoToDefinition/AbstractGoToCommandHandler`2.cs b/src/EditorFeatures/Core/GoToDefinition/AbstractGoToCommandHandler`2.cs index a93e35b1372d2..b8ce01e14c998 100644 --- a/src/EditorFeatures/Core/GoToDefinition/AbstractGoToCommandHandler`2.cs +++ b/src/EditorFeatures/Core/GoToDefinition/AbstractGoToCommandHandler`2.cs @@ -103,7 +103,7 @@ public bool ExecuteCommand(TCommandArgs args, CommandExecutionContext context) if (service == null) return false; - Roslyn.Utilities.Contract.ThrowIfNull(document); + Contract.ThrowIfNull(document); // cancel any prior find-refs that might be in progress. _cancellationTokenSource.Cancel(); diff --git a/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj b/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj index 2ffdb061e7ab2..ae74faa7622ea 100644 --- a/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj +++ b/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj @@ -34,7 +34,6 @@ - diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/ChangeSignature/ChangeSignatureTestState.cs b/src/EditorFeatures/DiagnosticsTestUtilities/ChangeSignature/ChangeSignatureTestState.cs index 827de1a7e388e..536fad3c1140f 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/ChangeSignature/ChangeSignatureTestState.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/ChangeSignature/ChangeSignatureTestState.cs @@ -85,7 +85,7 @@ public async Task GetParameterConfigurationAsync() return changeSignatureAnalyzedSucceedContext.ParameterConfiguration; } - throw Roslyn.Utilities.ExceptionUtilities.UnexpectedValue(((CannotChangeSignatureAnalyzedContext)context).CannotChangeSignatureReason.ToString()); + throw ExceptionUtilities.UnexpectedValue(((CannotChangeSignatureAnalyzedContext)context).CannotChangeSignatureReason.ToString()); } public void Dispose() diff --git a/src/EditorFeatures/Test2/IntelliSense/SignatureHelpControllerTests.vb b/src/EditorFeatures/Test2/IntelliSense/SignatureHelpControllerTests.vb index 2409e8479831e..66d9c617a1253 100644 --- a/src/EditorFeatures/Test2/IntelliSense/SignatureHelpControllerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/SignatureHelpControllerTests.vb @@ -113,7 +113,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Private Shared ReadOnly s_controllerMocksMap As New ConditionalWeakTable(Of Controller, ControllerMocks) Private Shared Function GetMocks(controller As Controller) As ControllerMocks Dim result As ControllerMocks = Nothing - Roslyn.Utilities.Contract.ThrowIfFalse(s_controllerMocksMap.TryGetValue(controller, result)) + Contract.ThrowIfFalse(s_controllerMocksMap.TryGetValue(controller, result)) Return result End Function diff --git a/src/EditorFeatures/TestUtilities/Microsoft.CodeAnalysis.EditorFeatures.Test.Utilities.csproj b/src/EditorFeatures/TestUtilities/Microsoft.CodeAnalysis.EditorFeatures.Test.Utilities.csproj index 540e000766193..0b19db7854e6a 100644 --- a/src/EditorFeatures/TestUtilities/Microsoft.CodeAnalysis.EditorFeatures.Test.Utilities.csproj +++ b/src/EditorFeatures/TestUtilities/Microsoft.CodeAnalysis.EditorFeatures.Test.Utilities.csproj @@ -10,10 +10,6 @@ false true - - - - diff --git a/src/EditorFeatures/TestUtilities/NavigateTo/NavigateToTestAggregator.Callback.cs b/src/EditorFeatures/TestUtilities/NavigateTo/NavigateToTestAggregator.Callback.cs index af0b25b4bef97..33a09ed7c6423 100644 --- a/src/EditorFeatures/TestUtilities/NavigateTo/NavigateToTestAggregator.Callback.cs +++ b/src/EditorFeatures/TestUtilities/NavigateTo/NavigateToTestAggregator.Callback.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.CodeAnalysis; using Microsoft.VisualStudio.Language.NavigateTo.Interfaces; using Roslyn.Utilities; diff --git a/src/EditorFeatures/TestUtilities/NavigateTo/NavigateToTestAggregator.cs b/src/EditorFeatures/TestUtilities/NavigateTo/NavigateToTestAggregator.cs index 3610e24b04132..b7be4d6a4249c 100644 --- a/src/EditorFeatures/TestUtilities/NavigateTo/NavigateToTestAggregator.cs +++ b/src/EditorFeatures/TestUtilities/NavigateTo/NavigateToTestAggregator.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.CodeAnalysis; using Microsoft.VisualStudio.Language.NavigateTo.Interfaces; using Moq; using Roslyn.Utilities; diff --git a/src/EditorFeatures/VisualBasic/EndConstructGeneration/ReplaceSpanResult.vb b/src/EditorFeatures/VisualBasic/EndConstructGeneration/ReplaceSpanResult.vb index 909b925f7be53..da7392e50945a 100644 --- a/src/EditorFeatures/VisualBasic/EndConstructGeneration/ReplaceSpanResult.vb +++ b/src/EditorFeatures/VisualBasic/EndConstructGeneration/ReplaceSpanResult.vb @@ -17,7 +17,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.EndConstructGeneration Private ReadOnly _newCaretPosition As Integer? Public Sub New(snapshotSpan As SnapshotSpan, replacementText As String, newCaretPosition As Integer?) - ThrowIfNull(replacementText) + Contract.ThrowIfNull(replacementText) _snapshotSpan = snapshotSpan _replacementText = replacementText diff --git a/src/EditorFeatures/VisualBasic/Microsoft.CodeAnalysis.VisualBasic.EditorFeatures.vbproj b/src/EditorFeatures/VisualBasic/Microsoft.CodeAnalysis.VisualBasic.EditorFeatures.vbproj index 6f4fcb44d0880..e823fb16776ab 100644 --- a/src/EditorFeatures/VisualBasic/Microsoft.CodeAnalysis.VisualBasic.EditorFeatures.vbproj +++ b/src/EditorFeatures/VisualBasic/Microsoft.CodeAnalysis.VisualBasic.EditorFeatures.vbproj @@ -29,7 +29,7 @@ - + diff --git a/src/EditorFeatures/VisualBasicTest/Structure/CommentStructureTests.vb b/src/EditorFeatures/VisualBasicTest/Structure/CommentStructureTests.vb index 65dcd079c2464..07000d1247ea7 100644 --- a/src/EditorFeatures/VisualBasicTest/Structure/CommentStructureTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Structure/CommentStructureTests.vb @@ -29,7 +29,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Outlining ElseIf token.TrailingTrivia.Contains(trivia) Then Return CreateCommentsRegions(token.TrailingTrivia) Else - Throw Roslyn.Utilities.ExceptionUtilities.Unreachable + Throw ExceptionUtilities.Unreachable End If End Function diff --git a/src/ExpressionEvaluator/Core/Source/FunctionResolver/Microsoft.CodeAnalysis.FunctionResolver.csproj b/src/ExpressionEvaluator/Core/Source/FunctionResolver/Microsoft.CodeAnalysis.FunctionResolver.csproj index 3271d4fbd6d4d..5744d70120de4 100644 --- a/src/ExpressionEvaluator/Core/Source/FunctionResolver/Microsoft.CodeAnalysis.FunctionResolver.csproj +++ b/src/ExpressionEvaluator/Core/Source/FunctionResolver/Microsoft.CodeAnalysis.FunctionResolver.csproj @@ -10,9 +10,6 @@ true - - Compiler\ExceptionUtilities.cs - Compiler\MetadataTypeCodeExtensions.cs @@ -25,9 +22,6 @@ Compiler\CommonGeneratedNames.cs - - Compiler\NullableAttributes.cs - Compiler\RoslynString.cs @@ -64,5 +58,6 @@ + diff --git a/src/ExpressionEvaluator/Core/Source/ResultProvider/Portable/Microsoft.CodeAnalysis.ResultProvider.csproj b/src/ExpressionEvaluator/Core/Source/ResultProvider/Portable/Microsoft.CodeAnalysis.ResultProvider.csproj index 4c83167c2a17a..c28f388bd2ddc 100644 --- a/src/ExpressionEvaluator/Core/Source/ResultProvider/Portable/Microsoft.CodeAnalysis.ResultProvider.csproj +++ b/src/ExpressionEvaluator/Core/Source/ResultProvider/Portable/Microsoft.CodeAnalysis.ResultProvider.csproj @@ -28,21 +28,9 @@ Compiler\InternalUtilities\EnumField.cs - - Compiler\InternalUtilities\ExceptionUtilities.cs - - - Compiler\InternalUtilities\NullableAttributes.cs - Compiler\InternalUtilities\ReflectionUtilities.cs - - Compilers\InternalUtilities\InterpolatedStringHandlerAttribute.cs - - - Compilers\InternalUtilities\InterpolatedStringHandlerArgumentAttribute.cs - Compiler\InternalUtilities\ObjectPool`1.cs @@ -90,4 +78,5 @@ + \ No newline at end of file diff --git a/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/NamespaceTypeDefinitionNoBase.cs b/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/NamespaceTypeDefinitionNoBase.cs index bd857b6fc3ffb..9adcc73fa102b 100644 --- a/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/NamespaceTypeDefinitionNoBase.cs +++ b/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/NamespaceTypeDefinitionNoBase.cs @@ -132,13 +132,13 @@ internal NamespaceTypeDefinitionNoBase(INamespaceTypeDefinition underlyingType) public sealed override bool Equals(object obj) { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } public sealed override int GetHashCode() { // It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used. - throw Roslyn.Utilities.ExceptionUtilities.Unreachable(); + throw ExceptionUtilities.Unreachable(); } } } diff --git a/src/ExpressionEvaluator/Core/Test/ResultProvider/Microsoft.CodeAnalysis.ResultProvider.Utilities.csproj b/src/ExpressionEvaluator/Core/Test/ResultProvider/Microsoft.CodeAnalysis.ResultProvider.Utilities.csproj index 7def48e1d75a6..a62c0965c6d4a 100644 --- a/src/ExpressionEvaluator/Core/Test/ResultProvider/Microsoft.CodeAnalysis.ResultProvider.Utilities.csproj +++ b/src/ExpressionEvaluator/Core/Test/ResultProvider/Microsoft.CodeAnalysis.ResultProvider.Utilities.csproj @@ -40,21 +40,9 @@ Compiler\InternalUtilities\EnumField.cs - - Compiler\InternalUtilities\ExceptionUtilities.cs - - - Compiler\InternalUtilities\NullableAttributes.cs - Compiler\InternalUtilities\ReflectionUtilities.cs - - Compilers\InternalUtilities\InterpolatedStringHandlerAttribute.cs - - - Compilers\InternalUtilities\InterpolatedStringHandlerArgumentAttribute.cs - Compiler\InternalUtilities\ObjectPool`1.cs @@ -94,4 +82,5 @@ + diff --git a/src/Features/Lsif/Generator/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.csproj b/src/Features/Lsif/Generator/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.csproj index 08a3b376f97de..1b6c512bb1cfb 100644 --- a/src/Features/Lsif/Generator/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.csproj +++ b/src/Features/Lsif/Generator/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.csproj @@ -55,7 +55,6 @@ - diff --git a/src/Features/TestUtilities/Microsoft.CodeAnalysis.Features.Test.Utilities.csproj b/src/Features/TestUtilities/Microsoft.CodeAnalysis.Features.Test.Utilities.csproj index 2aa8f604ab261..392d25293108e 100644 --- a/src/Features/TestUtilities/Microsoft.CodeAnalysis.Features.Test.Utilities.csproj +++ b/src/Features/TestUtilities/Microsoft.CodeAnalysis.Features.Test.Utilities.csproj @@ -10,10 +10,6 @@ true true - - - - diff --git a/src/Interactive/Host/Microsoft.CodeAnalysis.InteractiveHost.csproj b/src/Interactive/Host/Microsoft.CodeAnalysis.InteractiveHost.csproj index b68db02519f4d..778289fdaf640 100644 --- a/src/Interactive/Host/Microsoft.CodeAnalysis.InteractiveHost.csproj +++ b/src/Interactive/Host/Microsoft.CodeAnalysis.InteractiveHost.csproj @@ -36,18 +36,11 @@ - - - - - - - @@ -78,4 +71,5 @@ + \ No newline at end of file diff --git a/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/Microsoft.CommonLanguageServerProtocol.Framework.Example.csproj b/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/Microsoft.CommonLanguageServerProtocol.Framework.Example.csproj index 1e3c944072843..6b419a03f3fcf 100644 --- a/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/Microsoft.CommonLanguageServerProtocol.Framework.Example.csproj +++ b/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/Microsoft.CommonLanguageServerProtocol.Framework.Example.csproj @@ -21,7 +21,5 @@ - - - + diff --git a/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/Microsoft.CommonLanguageServerProtocol.Framework.Package.csproj b/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/Microsoft.CommonLanguageServerProtocol.Framework.Package.csproj index ce4ed2a289ace..432407fd46ce9 100644 --- a/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/Microsoft.CommonLanguageServerProtocol.Framework.Package.csproj +++ b/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/Microsoft.CommonLanguageServerProtocol.Framework.Package.csproj @@ -25,10 +25,6 @@ - - - - @@ -43,4 +39,5 @@ contentFiles\cs\$(TargetFramework)\ + diff --git a/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/Microsoft.CommonLanguageServerProtocol.Framework.Shared.projitems b/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/Microsoft.CommonLanguageServerProtocol.Framework.Shared.projitems index ef9c653643d6e..dd8785f1ae8f5 100644 --- a/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/Microsoft.CommonLanguageServerProtocol.Framework.Shared.projitems +++ b/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/Microsoft.CommonLanguageServerProtocol.Framework.Shared.projitems @@ -41,6 +41,5 @@ - \ No newline at end of file diff --git a/src/LanguageServer/Protocol.TestUtilities/Microsoft.CodeAnalysis.LanguageServer.Protocol.Test.Utilities.csproj b/src/LanguageServer/Protocol.TestUtilities/Microsoft.CodeAnalysis.LanguageServer.Protocol.Test.Utilities.csproj index 81266477a3850..910bce3e518ef 100644 --- a/src/LanguageServer/Protocol.TestUtilities/Microsoft.CodeAnalysis.LanguageServer.Protocol.Test.Utilities.csproj +++ b/src/LanguageServer/Protocol.TestUtilities/Microsoft.CodeAnalysis.LanguageServer.Protocol.Test.Utilities.csproj @@ -10,10 +10,6 @@ true true - - - - diff --git a/src/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj b/src/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj index 0f49abbd7a401..95155e412544a 100644 --- a/src/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj +++ b/src/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj @@ -14,7 +14,6 @@ - diff --git a/src/LanguageServer/ProtocolUnitTests/Microsoft.CodeAnalysis.LanguageServer.Protocol.UnitTests.csproj b/src/LanguageServer/ProtocolUnitTests/Microsoft.CodeAnalysis.LanguageServer.Protocol.UnitTests.csproj index b244b45d8789b..1cf6c995899f4 100644 --- a/src/LanguageServer/ProtocolUnitTests/Microsoft.CodeAnalysis.LanguageServer.Protocol.UnitTests.csproj +++ b/src/LanguageServer/ProtocolUnitTests/Microsoft.CodeAnalysis.LanguageServer.Protocol.UnitTests.csproj @@ -13,10 +13,6 @@ Always - - - - diff --git a/src/Test/PdbUtilities/Reader/MockSymUnmanagedReader.cs b/src/Test/PdbUtilities/Reader/MockSymUnmanagedReader.cs index b158a7f9a477d..dfc3608111351 100644 --- a/src/Test/PdbUtilities/Reader/MockSymUnmanagedReader.cs +++ b/src/Test/PdbUtilities/Reader/MockSymUnmanagedReader.cs @@ -11,7 +11,7 @@ using System.Runtime.InteropServices.ComTypes; using DSR::Microsoft.DiaSymReader; using Microsoft.CodeAnalysis.PooledObjects; -using Roslyn.Utilities; +using Microsoft.CodeAnalysis; using Xunit; namespace Roslyn.Test.Utilities diff --git a/src/Tools/BuildActionTelemetryTable/BuildActionTelemetryTable.csproj b/src/Tools/BuildActionTelemetryTable/BuildActionTelemetryTable.csproj index 783809b4edae9..76e418c1e9cd8 100644 --- a/src/Tools/BuildActionTelemetryTable/BuildActionTelemetryTable.csproj +++ b/src/Tools/BuildActionTelemetryTable/BuildActionTelemetryTable.csproj @@ -21,14 +21,11 @@ - - - - - + + diff --git a/src/Tools/ExternalAccess/Razor/Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj b/src/Tools/ExternalAccess/Razor/Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj index 6164b50074dd8..6beb60457471d 100644 --- a/src/Tools/ExternalAccess/Razor/Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj +++ b/src/Tools/ExternalAccess/Razor/Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj @@ -14,10 +14,6 @@ - - - - diff --git a/src/Tools/ExternalAccess/Xaml/Microsoft.CodeAnalysis.ExternalAccess.Xaml.csproj b/src/Tools/ExternalAccess/Xaml/Microsoft.CodeAnalysis.ExternalAccess.Xaml.csproj index 4310078bf9b32..d17ef579337c3 100644 --- a/src/Tools/ExternalAccess/Xaml/Microsoft.CodeAnalysis.ExternalAccess.Xaml.csproj +++ b/src/Tools/ExternalAccess/Xaml/Microsoft.CodeAnalysis.ExternalAccess.Xaml.csproj @@ -31,14 +31,7 @@ - - - - - - - diff --git a/src/Tools/Replay/Replay.csproj b/src/Tools/Replay/Replay.csproj index 0f52b65a3e8f4..fd3d130c7aca8 100644 --- a/src/Tools/Replay/Replay.csproj +++ b/src/Tools/Replay/Replay.csproj @@ -11,12 +11,8 @@ - - - - @@ -36,4 +32,6 @@ + + diff --git a/src/Tools/SemanticSearch/BuildTask/SemanticSearch.BuildTask.csproj b/src/Tools/SemanticSearch/BuildTask/SemanticSearch.BuildTask.csproj index 9e28511dab63f..f0184c57d0cd0 100644 --- a/src/Tools/SemanticSearch/BuildTask/SemanticSearch.BuildTask.csproj +++ b/src/Tools/SemanticSearch/BuildTask/SemanticSearch.BuildTask.csproj @@ -17,10 +17,9 @@ - - + diff --git a/src/VisualStudio/CSharp/Test/ProjectSystemShim/TestEvaluationData.cs b/src/VisualStudio/CSharp/Test/ProjectSystemShim/TestEvaluationData.cs index 3eea12d66da49..055f3bfdb87e4 100644 --- a/src/VisualStudio/CSharp/Test/ProjectSystemShim/TestEvaluationData.cs +++ b/src/VisualStudio/CSharp/Test/ProjectSystemShim/TestEvaluationData.cs @@ -3,8 +3,8 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; +using Microsoft.CodeAnalysis; using Microsoft.VisualStudio.LanguageServices.ProjectSystem; -using Roslyn.Utilities; namespace Roslyn.VisualStudio.CSharp.UnitTests.ProjectSystemShim; diff --git a/src/VisualStudio/Core/Def/DebuggerIntelliSense/DebuggerIntellisenseFilter.cs b/src/VisualStudio/Core/Def/DebuggerIntelliSense/DebuggerIntellisenseFilter.cs index 7a52ecb07cf53..1998be1c60a8c 100644 --- a/src/VisualStudio/Core/Def/DebuggerIntelliSense/DebuggerIntellisenseFilter.cs +++ b/src/VisualStudio/Core/Def/DebuggerIntelliSense/DebuggerIntellisenseFilter.cs @@ -7,6 +7,7 @@ using System; using System.Diagnostics; using System.Runtime.InteropServices; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.VisualStudio.ComponentModelHost; using Microsoft.VisualStudio.Editor; @@ -15,7 +16,6 @@ using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.TextManager.Interop; using Microsoft.VisualStudio.Utilities; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.DebuggerIntelliSense; diff --git a/src/VisualStudio/Core/Def/DocumentOutline/DocumentSymbolDataViewModelSorter.cs b/src/VisualStudio/Core/Def/DocumentOutline/DocumentSymbolDataViewModelSorter.cs index 351bcc84e61b8..db3bc907a9933 100644 --- a/src/VisualStudio/Core/Def/DocumentOutline/DocumentSymbolDataViewModelSorter.cs +++ b/src/VisualStudio/Core/Def/DocumentOutline/DocumentSymbolDataViewModelSorter.cs @@ -8,7 +8,7 @@ using System.Globalization; using System.Windows.Data; using System.Windows.Markup; -using Roslyn.Utilities; +using Microsoft.CodeAnalysis; namespace Microsoft.VisualStudio.LanguageServices.DocumentOutline; diff --git a/src/VisualStudio/Core/Def/FindReferences/Entries/SimpleMessageEntry.cs b/src/VisualStudio/Core/Def/FindReferences/Entries/SimpleMessageEntry.cs index d0e60da16ee5b..3265dc00db67a 100644 --- a/src/VisualStudio/Core/Def/FindReferences/Entries/SimpleMessageEntry.cs +++ b/src/VisualStudio/Core/Def/FindReferences/Entries/SimpleMessageEntry.cs @@ -4,9 +4,9 @@ using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Navigation; using Microsoft.VisualStudio.Shell.TableManager; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.FindUsages; diff --git a/src/VisualStudio/Core/Def/Implementation/AbstractOleCommandTarget.Query.cs b/src/VisualStudio/Core/Def/Implementation/AbstractOleCommandTarget.Query.cs index 3d7ae6167f9e0..5ecfbc0281b4a 100644 --- a/src/VisualStudio/Core/Def/Implementation/AbstractOleCommandTarget.Query.cs +++ b/src/VisualStudio/Core/Def/Implementation/AbstractOleCommandTarget.Query.cs @@ -5,8 +5,8 @@ #nullable disable using System; +using Microsoft.CodeAnalysis; using Microsoft.VisualStudio.OLE.Interop; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation; diff --git a/src/VisualStudio/Core/Def/Implementation/AbstractOleCommandTarget.cs b/src/VisualStudio/Core/Def/Implementation/AbstractOleCommandTarget.cs index 937c7342b8289..47fca7fc4bda2 100644 --- a/src/VisualStudio/Core/Def/Implementation/AbstractOleCommandTarget.cs +++ b/src/VisualStudio/Core/Def/Implementation/AbstractOleCommandTarget.cs @@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.VisualStudio.ComponentModelHost; @@ -12,7 +13,6 @@ using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.TextManager.Interop; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation; diff --git a/src/VisualStudio/Core/Def/Implementation/AbstractVsTextViewFilter.cs b/src/VisualStudio/Core/Def/Implementation/AbstractVsTextViewFilter.cs index c76b3b9699c59..70eb12b511530 100644 --- a/src/VisualStudio/Core/Def/Implementation/AbstractVsTextViewFilter.cs +++ b/src/VisualStudio/Core/Def/Implementation/AbstractVsTextViewFilter.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.BraceMatching; using Microsoft.CodeAnalysis.Debugging; using Microsoft.CodeAnalysis.Editor; diff --git a/src/VisualStudio/Core/Def/Implementation/VisualStudioUIContextActivationService.cs b/src/VisualStudio/Core/Def/Implementation/VisualStudioUIContextActivationService.cs index 41b73f8a55c5c..5d14623779347 100644 --- a/src/VisualStudio/Core/Def/Implementation/VisualStudioUIContextActivationService.cs +++ b/src/VisualStudio/Core/Def/Implementation/VisualStudioUIContextActivationService.cs @@ -4,6 +4,7 @@ using System; using System.Composition; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.VisualStudio.Shell; diff --git a/src/VisualStudio/Core/Def/InheritanceMargin/MarginGlyph/MenuItemContainerTemplateSelector.cs b/src/VisualStudio/Core/Def/InheritanceMargin/MarginGlyph/MenuItemContainerTemplateSelector.cs index ea160fee7fdee..ef414babf9f31 100644 --- a/src/VisualStudio/Core/Def/InheritanceMargin/MarginGlyph/MenuItemContainerTemplateSelector.cs +++ b/src/VisualStudio/Core/Def/InheritanceMargin/MarginGlyph/MenuItemContainerTemplateSelector.cs @@ -4,7 +4,7 @@ using System.Windows; using System.Windows.Controls; -using Roslyn.Utilities; +using Microsoft.CodeAnalysis; namespace Microsoft.VisualStudio.LanguageServices.Implementation.InheritanceMargin.MarginGlyph; diff --git a/src/VisualStudio/Core/Def/Interactive/VsInteractiveWindowProvider.cs b/src/VisualStudio/Core/Def/Interactive/VsInteractiveWindowProvider.cs index bd38aac5f3580..9b4fe70571d2a 100644 --- a/src/VisualStudio/Core/Def/Interactive/VsInteractiveWindowProvider.cs +++ b/src/VisualStudio/Core/Def/Interactive/VsInteractiveWindowProvider.cs @@ -12,6 +12,7 @@ using System.Diagnostics; using System.Linq; using InteractiveHost::Microsoft.CodeAnalysis.Interactive; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Interactive; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.VisualStudio.InteractiveWindow.Commands; diff --git a/src/VisualStudio/Core/Def/Interop/ComAggregate.cs b/src/VisualStudio/Core/Def/Interop/ComAggregate.cs index 463116bded3f7..86c23d4e7d16e 100644 --- a/src/VisualStudio/Core/Def/Interop/ComAggregate.cs +++ b/src/VisualStudio/Core/Def/Interop/ComAggregate.cs @@ -4,7 +4,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; -using Roslyn.Utilities; +using Microsoft.CodeAnalysis; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Interop; diff --git a/src/VisualStudio/Core/Def/LanguageClient/LogHubLspLogger.cs b/src/VisualStudio/Core/Def/LanguageClient/LogHubLspLogger.cs index 4aacd546fca84..85206d8e1ab58 100644 --- a/src/VisualStudio/Core/Def/LanguageClient/LogHubLspLogger.cs +++ b/src/VisualStudio/Core/Def/LanguageClient/LogHubLspLogger.cs @@ -4,10 +4,10 @@ using System; using System.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CommonLanguageServerProtocol.Framework; using Microsoft.VisualStudio.LogHub; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.LanguageClient; diff --git a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageDebugInfo.cs b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageDebugInfo.cs index 9820e02d3087e..8cd5493da8635 100644 --- a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageDebugInfo.cs +++ b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageDebugInfo.cs @@ -5,9 +5,9 @@ #nullable disable using System; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities; -using Roslyn.Utilities; using IVsEnumBSTR = Microsoft.VisualStudio.TextManager.Interop.IVsEnumBSTR; using IVsTextBuffer = Microsoft.VisualStudio.TextManager.Interop.IVsTextBuffer; using TextSpan = Microsoft.VisualStudio.TextManager.Interop.TextSpan; diff --git a/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj b/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj index 007c55fa569e3..6bb7c7aca2d1f 100644 --- a/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj +++ b/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj @@ -51,9 +51,6 @@ - - - diff --git a/src/VisualStudio/Core/Def/NavigationBar/NavigationBarClient.cs b/src/VisualStudio/Core/Def/NavigationBar/NavigationBarClient.cs index 8597fe3e621cd..ce1819728e7cc 100644 --- a/src/VisualStudio/Core/Def/NavigationBar/NavigationBarClient.cs +++ b/src/VisualStudio/Core/Def/NavigationBar/NavigationBarClient.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Editor.Wpf; @@ -17,7 +18,6 @@ using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.TextManager.Interop; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.NavigationBar; diff --git a/src/VisualStudio/Core/Def/Options/FeatureFlagPersister.cs b/src/VisualStudio/Core/Def/Options/FeatureFlagPersister.cs index a15b51d241d7d..66621e6fd58fb 100644 --- a/src/VisualStudio/Core/Def/Options/FeatureFlagPersister.cs +++ b/src/VisualStudio/Core/Def/Options/FeatureFlagPersister.cs @@ -4,10 +4,10 @@ using System; using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Options; using Microsoft.Internal.VisualStudio.Shell.Interop; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Options; diff --git a/src/VisualStudio/Core/Def/Options/LocalUserRegistryOptionPersister.cs b/src/VisualStudio/Core/Def/Options/LocalUserRegistryOptionPersister.cs index 1e6dc3b3e4d9e..8387e52ae155a 100644 --- a/src/VisualStudio/Core/Def/Options/LocalUserRegistryOptionPersister.cs +++ b/src/VisualStudio/Core/Def/Options/LocalUserRegistryOptionPersister.cs @@ -4,10 +4,10 @@ using System; using System.Runtime.InteropServices; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Options; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.Win32; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Options; diff --git a/src/VisualStudio/Core/Def/Options/VisualStudioOptionPersister.cs b/src/VisualStudio/Core/Def/Options/VisualStudioOptionPersister.cs index 4d5deb8bbcd9c..532b178e16347 100644 --- a/src/VisualStudio/Core/Def/Options/VisualStudioOptionPersister.cs +++ b/src/VisualStudio/Core/Def/Options/VisualStudioOptionPersister.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Threading.Tasks; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Options; using Roslyn.Utilities; diff --git a/src/VisualStudio/Core/Def/Options/VisualStudioOptionPersisterProvider.cs b/src/VisualStudio/Core/Def/Options/VisualStudioOptionPersisterProvider.cs index b7a78d2196971..4827ef4fcb0ae 100644 --- a/src/VisualStudio/Core/Def/Options/VisualStudioOptionPersisterProvider.cs +++ b/src/VisualStudio/Core/Def/Options/VisualStudioOptionPersisterProvider.cs @@ -8,6 +8,7 @@ using System.ComponentModel.Composition; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host.Mef; diff --git a/src/VisualStudio/Core/Def/PreviewPane/PreviewPane.xaml.cs b/src/VisualStudio/Core/Def/PreviewPane/PreviewPane.xaml.cs index 764c706bcb21e..d0fbc450c5737 100644 --- a/src/VisualStudio/Core/Def/PreviewPane/PreviewPane.xaml.cs +++ b/src/VisualStudio/Core/Def/PreviewPane/PreviewPane.xaml.cs @@ -12,6 +12,7 @@ using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Navigation; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics.Log; using Microsoft.CodeAnalysis.Editor.Implementation.Preview; using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities; diff --git a/src/VisualStudio/Core/Def/ProjectSystem/FileChangeTracker.cs b/src/VisualStudio/Core/Def/ProjectSystem/FileChangeTracker.cs index 34db1dfbb8256..ad0eaf96c4078 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/FileChangeTracker.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/FileChangeTracker.cs @@ -13,6 +13,7 @@ using IVsAsyncFileChangeEx2 = Microsoft.VisualStudio.Shell.IVsAsyncFileChangeEx2; using Microsoft.VisualStudio.Shell.Interop; using Roslyn.Utilities; +using Microsoft.CodeAnalysis; namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; diff --git a/src/VisualStudio/Core/Def/ProjectSystem/InvisibleEditor.cs b/src/VisualStudio/Core/Def/ProjectSystem/InvisibleEditor.cs index 4a7e5a4639726..34a7c77ccc94a 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/InvisibleEditor.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/InvisibleEditor.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; using System.Runtime.InteropServices; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.ErrorReporting; @@ -14,7 +15,6 @@ using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.TextManager.Interop; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; diff --git a/src/VisualStudio/Core/Def/ProjectSystem/Legacy/SolutionEventsBatchScopeCreator.cs b/src/VisualStudio/Core/Def/ProjectSystem/Legacy/SolutionEventsBatchScopeCreator.cs index 9a78bcbae6dbf..df4ea8ce29de6 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/Legacy/SolutionEventsBatchScopeCreator.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/Legacy/SolutionEventsBatchScopeCreator.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.ComponentModel.Composition; using System.Linq; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host.Mef; diff --git a/src/VisualStudio/Core/Def/RoslynActivityLogger.cs b/src/VisualStudio/Core/Def/RoslynActivityLogger.cs index c8aa1ca25a30f..307b9cdc4c1d5 100644 --- a/src/VisualStudio/Core/Def/RoslynActivityLogger.cs +++ b/src/VisualStudio/Core/Def/RoslynActivityLogger.cs @@ -6,8 +6,8 @@ using System.Diagnostics; using System.Threading; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Internal.Log; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices; diff --git a/src/VisualStudio/Core/Def/RoslynPackage.cs b/src/VisualStudio/Core/Def/RoslynPackage.cs index d0980fe34b761..9baed617356bc 100644 --- a/src/VisualStudio/Core/Def/RoslynPackage.cs +++ b/src/VisualStudio/Core/Def/RoslynPackage.cs @@ -9,6 +9,7 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.ColorSchemes; using Microsoft.CodeAnalysis.Common; using Microsoft.CodeAnalysis.EditAndContinue; diff --git a/src/VisualStudio/Core/Def/SolutionEventMonitor.cs b/src/VisualStudio/Core/Def/SolutionEventMonitor.cs index 18d960b371a7e..5011b4542c63d 100644 --- a/src/VisualStudio/Core/Def/SolutionEventMonitor.cs +++ b/src/VisualStudio/Core/Def/SolutionEventMonitor.cs @@ -4,9 +4,9 @@ using System; using System.Collections.Generic; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Notification; using Microsoft.VisualStudio.Shell; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices; diff --git a/src/VisualStudio/Core/Def/StackTraceExplorer/StackTraceExplorerCommandHandler.cs b/src/VisualStudio/Core/Def/StackTraceExplorer/StackTraceExplorerCommandHandler.cs index 610808ca0e25a..ea22f4b2be2a4 100644 --- a/src/VisualStudio/Core/Def/StackTraceExplorer/StackTraceExplorerCommandHandler.cs +++ b/src/VisualStudio/Core/Def/StackTraceExplorer/StackTraceExplorerCommandHandler.cs @@ -5,6 +5,7 @@ using System; using System.ComponentModel.Design; using System.Linq; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Options; diff --git a/src/VisualStudio/Core/Def/Telemetry/AbstractWorkspaceTelemetryService.cs b/src/VisualStudio/Core/Def/Telemetry/AbstractWorkspaceTelemetryService.cs index cb1ef8c812cbd..7921adfd5ddae 100644 --- a/src/VisualStudio/Core/Def/Telemetry/AbstractWorkspaceTelemetryService.cs +++ b/src/VisualStudio/Core/Def/Telemetry/AbstractWorkspaceTelemetryService.cs @@ -5,11 +5,11 @@ using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Telemetry; using Microsoft.VisualStudio.Telemetry; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Telemetry; diff --git a/src/VisualStudio/Core/Def/Telemetry/VisualStudioWorkspaceTelemetryService.cs b/src/VisualStudio/Core/Def/Telemetry/VisualStudioWorkspaceTelemetryService.cs index 3b3a85b6b5fe4..2fa4f7ad2c105 100644 --- a/src/VisualStudio/Core/Def/Telemetry/VisualStudioWorkspaceTelemetryService.cs +++ b/src/VisualStudio/Core/Def/Telemetry/VisualStudioWorkspaceTelemetryService.cs @@ -6,16 +6,15 @@ using System.Composition; using System.Diagnostics; using System.Threading.Tasks; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Telemetry; using Microsoft.VisualStudio.Telemetry; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Telemetry; diff --git a/src/VisualStudio/Core/Def/UnusedReferences/UnusedReferenceExtensions.cs b/src/VisualStudio/Core/Def/UnusedReferences/UnusedReferenceExtensions.cs index 5c6251c350a89..66165dfc70e97 100644 --- a/src/VisualStudio/Core/Def/UnusedReferences/UnusedReferenceExtensions.cs +++ b/src/VisualStudio/Core/Def/UnusedReferences/UnusedReferenceExtensions.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.UnusedReferences; using Microsoft.VisualStudio.LanguageServices.ExternalAccess.ProjectSystem.Api; using Roslyn.Utilities; diff --git a/src/VisualStudio/Core/Def/Utilities/VsCodeWindowViewTracker.cs b/src/VisualStudio/Core/Def/Utilities/VsCodeWindowViewTracker.cs index 5f98a5145219a..23c46561cc095 100644 --- a/src/VisualStudio/Core/Def/Utilities/VsCodeWindowViewTracker.cs +++ b/src/VisualStudio/Core/Def/Utilities/VsCodeWindowViewTracker.cs @@ -6,13 +6,13 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.LanguageServices.Implementation; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.TextManager.Interop; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Utilities; diff --git a/src/VisualStudio/Core/Def/Venus/VenusCommandFilter.cs b/src/VisualStudio/Core/Def/Venus/VenusCommandFilter.cs index 4749f29b355ae..3a14dc0bf351d 100644 --- a/src/VisualStudio/Core/Def/Venus/VenusCommandFilter.cs +++ b/src/VisualStudio/Core/Def/Venus/VenusCommandFilter.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Linq; using System.Threading.Tasks; +using Microsoft.CodeAnalysis; using Microsoft.VisualStudio.ComponentModelHost; using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.LanguageServices.Implementation.Extensions; @@ -14,7 +15,6 @@ using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.TextManager.Interop; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Venus; diff --git a/src/VisualStudio/Core/Impl/Options/Style/EnumCodeStyleOptionViewModel.cs b/src/VisualStudio/Core/Impl/Options/Style/EnumCodeStyleOptionViewModel.cs index b8156f7ce1f45..2943777c92018 100644 --- a/src/VisualStudio/Core/Impl/Options/Style/EnumCodeStyleOptionViewModel.cs +++ b/src/VisualStudio/Core/Impl/Options/Style/EnumCodeStyleOptionViewModel.cs @@ -8,9 +8,9 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Options; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Options { diff --git a/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerReferenceManager.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerReferenceManager.cs index 453f8f4ed10d1..d1e4d14816e2f 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerReferenceManager.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerReferenceManager.cs @@ -4,6 +4,7 @@ using System; using System.ComponentModel.Composition; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; using Microsoft.VisualStudio.Shell; diff --git a/src/VisualStudio/Core/Test.Next/Roslyn.VisualStudio.Next.UnitTests.csproj b/src/VisualStudio/Core/Test.Next/Roslyn.VisualStudio.Next.UnitTests.csproj index ffd478efc1217..3573c500702b9 100644 --- a/src/VisualStudio/Core/Test.Next/Roslyn.VisualStudio.Next.UnitTests.csproj +++ b/src/VisualStudio/Core/Test.Next/Roslyn.VisualStudio.Next.UnitTests.csproj @@ -71,8 +71,4 @@ - - - - \ No newline at end of file diff --git a/src/VisualStudio/Core/Test.Next/UnifiedSettings/TestModel/Utilities.cs b/src/VisualStudio/Core/Test.Next/UnifiedSettings/TestModel/Utilities.cs index 01a816265e1c2..1287a12ab69bc 100644 --- a/src/VisualStudio/Core/Test.Next/UnifiedSettings/TestModel/Utilities.cs +++ b/src/VisualStudio/Core/Test.Next/UnifiedSettings/TestModel/Utilities.cs @@ -4,9 +4,9 @@ using System; using System.Globalization; +using Microsoft.CodeAnalysis; using Microsoft.VisualStudio.LanguageServices; using Microsoft.VisualStudio.LanguageServices.CSharp; -using Roslyn.Utilities; using CSharpPackage = Microsoft.VisualStudio.LanguageServices.CSharp.VSPackage; using VisualBasicPackage = Microsoft.VisualStudio.LanguageServices.VisualBasic.VSPackage; diff --git a/src/VisualStudio/Core/Test/ClassView/MockVsServiceProvider.vb b/src/VisualStudio/Core/Test/ClassView/MockVsServiceProvider.vb index 10b5a08695ac7..0921df8013f73 100644 --- a/src/VisualStudio/Core/Test/ClassView/MockVsServiceProvider.vb +++ b/src/VisualStudio/Core/Test/ClassView/MockVsServiceProvider.vb @@ -2,9 +2,9 @@ ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. +Imports Microsoft.CodeAnalysis Imports Microsoft.VisualStudio.Shell Imports Microsoft.VisualStudio.Shell.Interop -Imports Roslyn.Utilities Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ClassView Friend Class MockServiceProvider diff --git a/src/VisualStudio/ExternalAccess/FSharp/Microsoft.CodeAnalysis.ExternalAccess.FSharp.csproj b/src/VisualStudio/ExternalAccess/FSharp/Microsoft.CodeAnalysis.ExternalAccess.FSharp.csproj index d5e722268a4da..e041981e98b54 100644 --- a/src/VisualStudio/ExternalAccess/FSharp/Microsoft.CodeAnalysis.ExternalAccess.FSharp.csproj +++ b/src/VisualStudio/ExternalAccess/FSharp/Microsoft.CodeAnalysis.ExternalAccess.FSharp.csproj @@ -15,10 +15,6 @@ - - - - + From c53e1e0aeef0574e4dcdf0ee3a692a02072a4b70 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Tue, 11 Feb 2025 17:18:38 -0800 Subject: [PATCH 223/305] Multi-target LSP tests to run against .NET9 --- .../VSTypeScriptHandlerTests.cs | 10 +- .../Workspaces/EditorTestWorkspace.cs | 22 +--- ...odeAnalysis.Features.Test.Utilities.csproj | 1 + ...rverProtocolTests.InitializationOptions.cs | 0 .../AbstractLanguageServerProtocolTests.cs | 71 +++++++---- .../AbstractLspBuildOnlyDiagnosticsTests.cs | 2 +- .../LanguageServer/TestOutputLspLogger.cs | 0 .../TestWorkspaceRegistrationService.cs | 0 ...guageServer.Protocol.Test.Utilities.csproj | 1 + .../Workspaces/LspTestWorkspace.cs | 1 + .../TestDocumentTrackingService.cs | 2 +- .../TestStaticSourceTextContainer.cs | 22 ++++ .../CodeActions/CodeActionResolveTests.cs | 114 +++++------------- .../CodeActions/CodeActionsTests.cs | 14 +-- .../CodeActions/RunCodeActionsTests.cs | 2 +- ...ngeConfigurationNotificationHandlerTest.cs | 2 +- .../Diagnostics/PullDiagnosticTests.cs | 87 +++---------- ...s.LanguageServer.Protocol.UnitTests.csproj | 10 +- .../Options/LspOptionsTests.cs | 2 - .../GetTextDocumentWithContextHandlerTests.cs | 8 +- .../FindAllReferencesHandlerFeaturesTests.cs | 5 +- .../ProtocolUnitTests/Rename/RenameTests.cs | 10 +- .../SpellCheck/SpellCheckTests.cs | 27 +---- .../Workspaces/LspWorkspaceManagerTests.cs | 9 +- .../SourceGeneratedDocumentTests.cs | 2 +- 25 files changed, 164 insertions(+), 260 deletions(-) rename src/{LanguageServer/ProtocolUnitTests => EditorFeatures/Test/LanguageServer}/VSTypeScriptHandlerTests.cs (94%) rename src/{EditorFeatures/TestUtilities => LanguageServer/Protocol.TestUtilities}/LanguageServer/AbstractLanguageServerProtocolTests.InitializationOptions.cs (100%) rename src/{EditorFeatures/TestUtilities => LanguageServer/Protocol.TestUtilities}/LanguageServer/AbstractLanguageServerProtocolTests.cs (93%) rename src/{EditorFeatures/TestUtilities => LanguageServer/Protocol.TestUtilities}/LanguageServer/AbstractLspBuildOnlyDiagnosticsTests.cs (97%) rename src/{EditorFeatures/TestUtilities => LanguageServer/Protocol.TestUtilities}/LanguageServer/TestOutputLspLogger.cs (100%) rename src/{EditorFeatures/TestUtilities => LanguageServer/Protocol.TestUtilities}/LanguageServer/TestWorkspaceRegistrationService.cs (100%) rename src/{EditorFeatures/TestUtilities/DocumentTracking => LanguageServer/Protocol.TestUtilities/Workspaces}/TestDocumentTrackingService.cs (95%) create mode 100644 src/LanguageServer/Protocol.TestUtilities/Workspaces/TestStaticSourceTextContainer.cs diff --git a/src/LanguageServer/ProtocolUnitTests/VSTypeScriptHandlerTests.cs b/src/EditorFeatures/Test/LanguageServer/VSTypeScriptHandlerTests.cs similarity index 94% rename from src/LanguageServer/ProtocolUnitTests/VSTypeScriptHandlerTests.cs rename to src/EditorFeatures/Test/LanguageServer/VSTypeScriptHandlerTests.cs index 043c8670f2733..a682b262b593b 100644 --- a/src/LanguageServer/ProtocolUnitTests/VSTypeScriptHandlerTests.cs +++ b/src/EditorFeatures/Test/LanguageServer/VSTypeScriptHandlerTests.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Immutable; using System.Composition; using System.IO; using System.Linq; @@ -21,15 +20,18 @@ using StreamJsonRpc; using Xunit; using Xunit.Abstractions; +using Microsoft.CodeAnalysis.LanguageServer; -namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests; +namespace Microsoft.CodeAnalysis.Editor.UnitTests.LanguageServer; public class VSTypeScriptHandlerTests : AbstractLanguageServerProtocolTests { public VSTypeScriptHandlerTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { } - protected override TestComposition Composition => base.Composition.AddParts(typeof(TypeScriptHandlerFactory)); + protected override TestComposition Composition => EditorTestCompositions.LanguageServerProtocolEditorFeatures + .AddParts(typeof(TypeScriptHandlerFactory)) + .AddParts(typeof(TestWorkspaceRegistrationService)); [Fact] public async Task TestExternalAccessTypeScriptHandlerInvoked() @@ -102,7 +104,7 @@ private async Task CreateTsTestLspServerAsync(string workspaceXml return await TestLspServer.CreateAsync(testWorkspace, new ClientCapabilities(), languageServerTarget, clientStream); } - private static RoslynLanguageServer CreateLanguageServer(Stream inputStream, Stream outputStream, EditorTestWorkspace workspace) + private static RoslynLanguageServer CreateLanguageServer(Stream inputStream, Stream outputStream, LspTestWorkspace workspace) { var capabilitiesProvider = workspace.ExportProvider.GetExportedValue(); var servicesProvider = workspace.ExportProvider.GetExportedValue(); diff --git a/src/EditorFeatures/TestUtilities/Workspaces/EditorTestWorkspace.cs b/src/EditorFeatures/TestUtilities/Workspaces/EditorTestWorkspace.cs index 291cc01aced8b..bb234f12cb3f0 100644 --- a/src/EditorFeatures/TestUtilities/Workspaces/EditorTestWorkspace.cs +++ b/src/EditorFeatures/TestUtilities/Workspaces/EditorTestWorkspace.cs @@ -31,12 +31,11 @@ namespace Microsoft.CodeAnalysis.Test.Utilities; -public partial class EditorTestWorkspace : TestWorkspace, ILspWorkspace +public partial class EditorTestWorkspace : TestWorkspace { private const string ReferencesOnDiskAttributeName = "ReferencesOnDisk"; private readonly Dictionary _createdTextBuffers = []; - private readonly bool _supportsLspMutation; internal EditorTestWorkspace( TestComposition? composition = null, @@ -44,8 +43,7 @@ internal EditorTestWorkspace( Guid solutionTelemetryId = default, bool disablePartialSolutions = true, bool ignoreUnchangeableDocumentsWhenApplyingChanges = true, - WorkspaceConfigurationOptions? configurationOptions = null, - bool supportsLspMutation = false) + WorkspaceConfigurationOptions? configurationOptions = null) : base(composition ?? EditorTestCompositions.EditorFeatures, workspaceKind, solutionTelemetryId, @@ -53,22 +51,6 @@ internal EditorTestWorkspace( ignoreUnchangeableDocumentsWhenApplyingChanges, configurationOptions) { - _supportsLspMutation = supportsLspMutation; - } - - bool ILspWorkspace.SupportsMutation => _supportsLspMutation; - - ValueTask ILspWorkspace.UpdateTextIfPresentAsync(DocumentId documentId, SourceText sourceText, CancellationToken cancellationToken) - { - Contract.ThrowIfFalse(_supportsLspMutation); - OnDocumentTextChanged(documentId, sourceText, PreservationMode.PreserveIdentity, requireDocumentPresent: false); - return ValueTaskFactory.CompletedTask; - } - - internal override ValueTask TryOnDocumentClosedAsync(DocumentId documentId, CancellationToken cancellationToken) - { - Contract.ThrowIfFalse(_supportsLspMutation); - return base.TryOnDocumentClosedAsync(documentId, cancellationToken); } private protected override EditorTestHostDocument CreateDocument( diff --git a/src/Features/TestUtilities/Microsoft.CodeAnalysis.Features.Test.Utilities.csproj b/src/Features/TestUtilities/Microsoft.CodeAnalysis.Features.Test.Utilities.csproj index 392d25293108e..766f43532bf19 100644 --- a/src/Features/TestUtilities/Microsoft.CodeAnalysis.Features.Test.Utilities.csproj +++ b/src/Features/TestUtilities/Microsoft.CodeAnalysis.Features.Test.Utilities.csproj @@ -33,6 +33,7 @@ + diff --git a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.InitializationOptions.cs b/src/LanguageServer/Protocol.TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.InitializationOptions.cs similarity index 100% rename from src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.InitializationOptions.cs rename to src/LanguageServer/Protocol.TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.InitializationOptions.cs diff --git a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs b/src/LanguageServer/Protocol.TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs similarity index 93% rename from src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs rename to src/LanguageServer/Protocol.TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs index 29071e6c24179..9f0b68c393716 100644 --- a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs +++ b/src/LanguageServer/Protocol.TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs @@ -15,9 +15,6 @@ using System.Xml.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Editor.Shared.Extensions; -using Microsoft.CodeAnalysis.Editor.Test; -using Microsoft.CodeAnalysis.Editor.UnitTests; using Microsoft.CodeAnalysis.Extensions; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageServer; @@ -51,10 +48,6 @@ protected AbstractLanguageServerProtocolTests(ITestOutputHelper? testOutputHelpe TestOutputLspLogger = testOutputHelper != null ? new TestOutputLspLogger(testOutputHelper) : NoOpLspLogger.Instance; } - protected static readonly TestComposition EditorFeaturesLspComposition = EditorTestCompositions.LanguageServerProtocolEditorFeatures - .AddParts(typeof(TestDocumentTrackingService)) - .AddParts(typeof(TestWorkspaceRegistrationService)); - protected static readonly TestComposition FeaturesLspComposition = LspTestCompositions.LanguageServerProtocol .AddParts(typeof(TestDocumentTrackingService)) .AddParts(typeof(TestWorkspaceRegistrationService)); @@ -108,7 +101,7 @@ private protected class OrderLocations : Comparer public override int Compare(LSP.Location? x, LSP.Location? y) => CompareLocations(x, y); } - protected virtual TestComposition Composition => EditorFeaturesLspComposition; + protected virtual TestComposition Composition => FeaturesLspComposition; private protected virtual TestAnalyzerReferenceByLanguage CreateTestAnalyzersReference() => new(DiagnosticExtensions.GetCompilerDiagnosticAnalyzersMap()); @@ -180,7 +173,7 @@ private protected static string ApplyTextEdits(LSP.TextEdit[]? edits, SourceText internal static LSP.SymbolInformation CreateSymbolInformation(LSP.SymbolKind kind, string name, LSP.Location location, Glyph glyph, string? containerName = null) { - var imageId = glyph.GetImageId(); + var (guid, id) = glyph.GetVsImageData(); #pragma warning disable CS0618 // SymbolInformation is obsolete, need to switch to DocumentSymbol/WorkspaceSymbol var info = new LSP.VSSymbolInformation() @@ -188,7 +181,7 @@ internal static LSP.SymbolInformation CreateSymbolInformation(LSP.SymbolKind kin Kind = kind, Name = name, Location = location, - Icon = new LSP.VSImageId { Guid = imageId.Guid, Id = imageId.Id }, + Icon = new LSP.VSImageId { Guid = guid, Id = id }, }; if (containerName != null) @@ -279,7 +272,7 @@ private protected static LSP.CompletionParams CreateCompletionParams( }; if (tags != null) - item.Icon = tags.ToImmutableArray().GetFirstGlyph().GetImageElement().ToLSPImageElement(); + item.Icon = new(tags.ToImmutableArray().GetFirstGlyph().ToLSPImageId()); if (commitCharacters != null) item.CommitCharacters = [.. commitCharacters.Value.Select(c => c.ToString())]; @@ -321,13 +314,13 @@ private protected Task CreateTestLspServerAsync( var workspace = CreateWorkspace(lspOptions, workspaceKind: null, mutatingLspWorkspace, composition); workspace.InitializeDocuments( - TestWorkspace.CreateWorkspaceElement(languageName, files: markups, fileContainingFolders: lspOptions.DocumentFileContainingFolders, sourceGeneratedFiles: lspOptions.SourceGeneratedMarkups, commonReferences: commonReferences), + LspTestWorkspace.CreateWorkspaceElement(languageName, files: markups, fileContainingFolders: lspOptions.DocumentFileContainingFolders, sourceGeneratedFiles: lspOptions.SourceGeneratedMarkups, commonReferences: commonReferences), openDocuments: false); return CreateTestLspServerAsync(workspace, lspOptions, languageName); } - private async Task CreateTestLspServerAsync(EditorTestWorkspace workspace, InitializationOptions initializationOptions, string languageName) + private async Task CreateTestLspServerAsync(LspTestWorkspace workspace, InitializationOptions initializationOptions, string languageName) { var solution = workspace.CurrentSolution; @@ -374,10 +367,10 @@ private protected async Task CreateXmlTestLspServerAsync( return await TestLspServer.CreateAsync(workspace, lspOptions, TestOutputLspLogger); } - internal EditorTestWorkspace CreateWorkspace( + internal LspTestWorkspace CreateWorkspace( InitializationOptions? options, string? workspaceKind, bool mutatingLspWorkspace, TestComposition? composition = null) { - var workspace = new EditorTestWorkspace( + var workspace = new LspTestWorkspace( composition ?? Composition, workspaceKind, configurationOptions: new WorkspaceConfigurationOptions(ValidateCompilationTrackerStates: true), supportsLspMutation: mutatingLspWorkspace); options?.OptionUpdater?.Invoke(workspace.GetService()); @@ -390,13 +383,13 @@ internal EditorTestWorkspace CreateWorkspace( /// Waits for the async operations on the workspace to complete. /// This ensures that events like workspace registration / workspace changes are processed by the time we exit this method. /// - protected static async Task WaitForWorkspaceOperationsAsync(EditorTestWorkspace workspace) + protected static async Task WaitForWorkspaceOperationsAsync(TestWorkspace workspace) { var workspaceWaiter = GetWorkspaceWaiter(workspace); await workspaceWaiter.ExpeditedWaitAsync(); } - private static IAsynchronousOperationWaiter GetWorkspaceWaiter(EditorTestWorkspace workspace) + private static IAsynchronousOperationWaiter GetWorkspaceWaiter(TestWorkspace workspace) { var operations = workspace.ExportProvider.GetExportedValue(); return operations.GetWaiter(FeatureAttribute.Workspace); @@ -419,7 +412,7 @@ protected static void AddMappedDocument(Workspace workspace, string markup) workspace.TryApplyChanges(newSolution); } - protected static async Task AddGeneratorAsync(ISourceGenerator generator, EditorTestWorkspace workspace) + protected static async Task AddGeneratorAsync(ISourceGenerator generator, LspTestWorkspace workspace) { var analyzerReference = new TestGeneratorReference(generator); @@ -433,7 +426,7 @@ protected static async Task AddGeneratorAsync(ISourceGenerato return analyzerReference; } - protected static async Task RemoveGeneratorAsync(AnalyzerReference reference, EditorTestWorkspace workspace) + protected static async Task RemoveGeneratorAsync(AnalyzerReference reference, LspTestWorkspace workspace) { var solution = workspace.CurrentSolution .Projects.Single() @@ -444,7 +437,7 @@ protected static async Task RemoveGeneratorAsync(AnalyzerReference reference, Ed await WaitForWorkspaceOperationsAsync(workspace); } - internal static async Task>> GetAnnotatedLocationsAsync(EditorTestWorkspace workspace, Solution solution) + internal static async Task>> GetAnnotatedLocationsAsync(LspTestWorkspace workspace, Solution solution) { var locations = new Dictionary>(); foreach (var testDocument in workspace.Documents) @@ -533,7 +526,7 @@ private static LSP.DidCloseTextDocumentParams CreateDidCloseTextDocumentParams(U internal sealed class TestLspServer : IAsyncDisposable { - public readonly EditorTestWorkspace TestWorkspace; + public readonly LspTestWorkspace TestWorkspace; private readonly Dictionary> _locations; private readonly JsonRpc _clientRpc; private readonly ICodeAnalysisDiagnosticAnalyzerService _codeAnalysisService; @@ -543,7 +536,7 @@ internal sealed class TestLspServer : IAsyncDisposable public LSP.ClientCapabilities ClientCapabilities { get; } private TestLspServer( - EditorTestWorkspace testWorkspace, + LspTestWorkspace testWorkspace, Dictionary> locations, LSP.ClientCapabilities clientCapabilities, RoslynLanguageServer target, @@ -579,7 +572,7 @@ private void InitializeClientRpc() Assert.False(workspaceWaiter.HasPendingWork); } - internal static async Task CreateAsync(EditorTestWorkspace testWorkspace, InitializationOptions initializationOptions, AbstractLspLogger logger) + internal static async Task CreateAsync(LspTestWorkspace testWorkspace, InitializationOptions initializationOptions, AbstractLspLogger logger) { // Important: We must wait for workspace creation operations to finish. // Otherwise we could have a race where workspace change events triggered by creation are changing the state @@ -610,7 +603,7 @@ internal static async Task CreateAsync(EditorTestWorkspace testWo return server; } - internal static async Task CreateAsync(EditorTestWorkspace testWorkspace, LSP.ClientCapabilities clientCapabilities, RoslynLanguageServer target, Stream clientStream) + internal static async Task CreateAsync(LspTestWorkspace testWorkspace, LSP.ClientCapabilities clientCapabilities, RoslynLanguageServer target, Stream clientStream) { var locations = await GetAnnotatedLocationsAsync(testWorkspace, testWorkspace.CurrentSolution); var server = new TestLspServer(testWorkspace, locations, clientCapabilities, target, clientStream); @@ -625,7 +618,7 @@ internal static async Task CreateAsync(EditorTestWorkspace testWo return server; } - private static RoslynLanguageServer CreateLanguageServer(Stream inputStream, Stream outputStream, EditorTestWorkspace workspace, WellKnownLspServerKinds serverKind, AbstractLspLogger logger) + private static RoslynLanguageServer CreateLanguageServer(Stream inputStream, Stream outputStream, LspTestWorkspace workspace, WellKnownLspServerKinds serverKind, AbstractLspLogger logger) { var capabilitiesProvider = workspace.ExportProvider.GetExportedValue(); var factory = workspace.ExportProvider.GetExportedValue(); @@ -698,6 +691,34 @@ public async Task OpenDocumentAsync(Uri documentUri, string? text = null, string await ExecuteRequestAsync(LSP.Methods.TextDocumentDidOpenName, didOpenParams, CancellationToken.None); } + /// + /// Opens a document in the workspace only, and waits for workspace operations. + /// Use if the document should be opened in LSP"/> + /// + public async Task OpenDocumentInWorkspaceAsync(DocumentId documentId, bool openAllLinkedDocuments, SourceText? text = null) + { + var document = TestWorkspace.CurrentSolution.GetDocument(documentId); + Contract.ThrowIfNull(document); + + if (text is null) + text = await TestWorkspace.CurrentSolution.GetDocument(documentId)!.GetTextAsync(CancellationToken.None); + + List linkedDocuments = [documentId]; + if (openAllLinkedDocuments) + { + linkedDocuments.AddRange(document.GetLinkedDocumentIds()); + } + + var container = new TestStaticSourceTextContainer(text); + + foreach (var documentIdToOpen in linkedDocuments) + { + TestWorkspace.OnDocumentOpened(documentIdToOpen, container); + } + + await WaitForWorkspaceOperationsAsync(TestWorkspace); + } + public Task ReplaceTextAsync(Uri documentUri, params (LSP.Range Range, string Text)[] changes) { var didChangeParams = CreateDidChangeTextDocumentParams( diff --git a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLspBuildOnlyDiagnosticsTests.cs b/src/LanguageServer/Protocol.TestUtilities/LanguageServer/AbstractLspBuildOnlyDiagnosticsTests.cs similarity index 97% rename from src/EditorFeatures/TestUtilities/LanguageServer/AbstractLspBuildOnlyDiagnosticsTests.cs rename to src/LanguageServer/Protocol.TestUtilities/LanguageServer/AbstractLspBuildOnlyDiagnosticsTests.cs index 30726fc3aaa9b..0b09f4041b37e 100644 --- a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLspBuildOnlyDiagnosticsTests.cs +++ b/src/LanguageServer/Protocol.TestUtilities/LanguageServer/AbstractLspBuildOnlyDiagnosticsTests.cs @@ -22,7 +22,7 @@ public abstract class AbstractLspBuildOnlyDiagnosticsTests [Fact] public void TestExportedDiagnosticIds() { - var attribute = this.LspBuildOnlyDiagnosticsType.GetCustomAttribute(); + var attribute = this.LspBuildOnlyDiagnosticsType.GetCustomAttribute()!; var actualDiagnosticCodes = attribute.BuildOnlyDiagnostics; var missing = ExpectedDiagnosticCodes.Except(actualDiagnosticCodes).OrderBy(k => k).ToList(); diff --git a/src/EditorFeatures/TestUtilities/LanguageServer/TestOutputLspLogger.cs b/src/LanguageServer/Protocol.TestUtilities/LanguageServer/TestOutputLspLogger.cs similarity index 100% rename from src/EditorFeatures/TestUtilities/LanguageServer/TestOutputLspLogger.cs rename to src/LanguageServer/Protocol.TestUtilities/LanguageServer/TestOutputLspLogger.cs diff --git a/src/EditorFeatures/TestUtilities/LanguageServer/TestWorkspaceRegistrationService.cs b/src/LanguageServer/Protocol.TestUtilities/LanguageServer/TestWorkspaceRegistrationService.cs similarity index 100% rename from src/EditorFeatures/TestUtilities/LanguageServer/TestWorkspaceRegistrationService.cs rename to src/LanguageServer/Protocol.TestUtilities/LanguageServer/TestWorkspaceRegistrationService.cs diff --git a/src/LanguageServer/Protocol.TestUtilities/Microsoft.CodeAnalysis.LanguageServer.Protocol.Test.Utilities.csproj b/src/LanguageServer/Protocol.TestUtilities/Microsoft.CodeAnalysis.LanguageServer.Protocol.Test.Utilities.csproj index 910bce3e518ef..4dfef48693c83 100644 --- a/src/LanguageServer/Protocol.TestUtilities/Microsoft.CodeAnalysis.LanguageServer.Protocol.Test.Utilities.csproj +++ b/src/LanguageServer/Protocol.TestUtilities/Microsoft.CodeAnalysis.LanguageServer.Protocol.Test.Utilities.csproj @@ -16,5 +16,6 @@ + \ No newline at end of file diff --git a/src/LanguageServer/Protocol.TestUtilities/Workspaces/LspTestWorkspace.cs b/src/LanguageServer/Protocol.TestUtilities/Workspaces/LspTestWorkspace.cs index 96830c508dd90..5695456e45a13 100644 --- a/src/LanguageServer/Protocol.TestUtilities/Workspaces/LspTestWorkspace.cs +++ b/src/LanguageServer/Protocol.TestUtilities/Workspaces/LspTestWorkspace.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageServer; +using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; diff --git a/src/EditorFeatures/TestUtilities/DocumentTracking/TestDocumentTrackingService.cs b/src/LanguageServer/Protocol.TestUtilities/Workspaces/TestDocumentTrackingService.cs similarity index 95% rename from src/EditorFeatures/TestUtilities/DocumentTracking/TestDocumentTrackingService.cs rename to src/LanguageServer/Protocol.TestUtilities/Workspaces/TestDocumentTrackingService.cs index ec01c8c69affc..529f126e62ef1 100644 --- a/src/EditorFeatures/TestUtilities/DocumentTracking/TestDocumentTrackingService.cs +++ b/src/LanguageServer/Protocol.TestUtilities/Workspaces/TestDocumentTrackingService.cs @@ -7,7 +7,7 @@ using System.Composition; using Microsoft.CodeAnalysis.Host.Mef; -namespace Microsoft.CodeAnalysis.Editor.Test; +namespace Microsoft.CodeAnalysis.Test.Utilities; [ExportWorkspaceService(typeof(IDocumentTrackingService), ServiceLayer.Test), Shared, PartNotDiscoverable] [method: ImportingConstructor] diff --git a/src/LanguageServer/Protocol.TestUtilities/Workspaces/TestStaticSourceTextContainer.cs b/src/LanguageServer/Protocol.TestUtilities/Workspaces/TestStaticSourceTextContainer.cs new file mode 100644 index 0000000000000..40b0f2bd9a904 --- /dev/null +++ b/src/LanguageServer/Protocol.TestUtilities/Workspaces/TestStaticSourceTextContainer.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Test.Utilities; + +/// +/// Various tests often need a source text container to simulate workspace OnDocumentOpened calls. +/// +internal class TestStaticSourceTextContainer(SourceText text) : SourceTextContainer +{ + public override SourceText CurrentText => text; + + public override event EventHandler TextChanged + { + add { } + remove { } + } +} diff --git a/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionResolveTests.cs b/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionResolveTests.cs index 115998c9c86ca..8acf17687cc4b 100644 --- a/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionResolveTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionResolveTests.cs @@ -26,7 +26,7 @@ public CodeActionResolveTests(ITestOutputHelper testOutputHelper) : base(testOut { } - [WpfTheory, CombinatorialData] + [Theory, CombinatorialData] public async Task TestCodeActionResolveHandlerAsync(bool mutatingLspWorkspace) { var initialMarkup = @@ -77,7 +77,7 @@ void M() AssertJsonEquals(expectedResolvedAction, actualResolvedAction); } - [WpfTheory, CombinatorialData] + [Theory, CombinatorialData] public async Task TestCodeActionResolveHandlerAsync_NestedAction(bool mutatingLspWorkspace) { var initialMarkup = @@ -138,7 +138,7 @@ void M() AssertJsonEquals(expectedResolvedAction, actualResolvedAction); } - [WpfTheory, CombinatorialData] + [Theory, CombinatorialData] public async Task TestRename(bool mutatingLspWorkspace) { var markUp = @" @@ -198,23 +198,35 @@ class {|caret:ABC|} AssertJsonEquals(expectedCodeAction, actualResolvedAction); } - [WpfTheory, CombinatorialData] + [Theory, CombinatorialData] public async Task TestLinkedDocuments(bool mutatingLspWorkspace) { - var xmlWorkspace = @" + var originalMarkup = """ + class C + { + public static readonly int {|caret:_value|} = 10; + } + """; + var xmlWorkspace = $""" - class C -{ - public static readonly int {|caret:_value|} = 10; -} - + {originalMarkup} -"; + """; + + var expectedText = """ + class C + { + private static readonly int value = 10; + + public static int Value => value; + } + """; + await using var testLspServer = await CreateXmlTestLspServerAsync(xmlWorkspace, mutatingLspWorkspace); var titlePath = new string[] { string.Format(FeaturesResources.Encapsulate_field_colon_0_and_use_property, "_value") }; var unresolvedCodeAction = CodeActionsTests.CreateCodeAction( @@ -230,78 +242,18 @@ public async Task TestLinkedDocuments(bool mutatingLspWorkspace) diagnostics: null); var actualResolvedAction = await RunGetCodeActionResolveAsync(testLspServer, unresolvedCodeAction); - var edits = new SumType[] - { - new TextEdit() - { - NewText = "private", - Range = new LSP.Range() - { - Start = new Position - { - Line = 2, - Character = 4 - }, - End = new Position - { - Line = 2, - Character = 10 - } - } - }, - new TextEdit - { - NewText = string.Empty, - Range = new LSP.Range - { - Start = new Position - { - Line = 2, - Character = 31 - }, - End = new Position - { - Line = 2, - Character = 32 - } - } - }, - new TextEdit - { - NewText = @" - public static int Value => value;", - Range = new LSP.Range - { - Start = new Position - { - Line = 2, - Character = 43 - }, - End = new Position - { - Line = 2, - Character = 43 - } - } - } - }; - var expectedCodeAction = CodeActionsTests.CreateCodeAction( - title: string.Format(FeaturesResources.Encapsulate_field_colon_0_and_use_property, "_value"), - kind: CodeActionKind.Refactor, - children: [], - data: CreateCodeActionResolveData( - string.Format(FeaturesResources.Encapsulate_field_colon_0_and_use_property, "_value"), - testLspServer.GetLocations("caret").Single(), titlePath), - priority: VSInternalPriorityLevel.Normal, - groupName: "Roslyn2", - applicableRange: new LSP.Range { Start = new Position { Line = 2, Character = 33 }, End = new Position { Line = 39, Character = 2 } }, - diagnostics: null, - edit: GenerateWorkspaceEdit(testLspServer.GetLocations("caret"), edits)); - AssertJsonEquals(expectedCodeAction, actualResolvedAction); + AssertEx.NotNull(actualResolvedAction.Edit); + var textDocumentEdit = (LSP.TextDocumentEdit[])actualResolvedAction.Edit.DocumentChanges.Value; + Assert.Single(textDocumentEdit); + var originalText = await testLspServer.GetDocumentTextAsync(textDocumentEdit[0].TextDocument.Uri); + var edits = textDocumentEdit[0].Edits.Select(e => (LSP.TextEdit)e.Value).ToArray(); + var updatedText = ApplyTextEdits(edits, originalText); + Assert.Equal(expectedText, updatedText); + } - [WpfTheory, CombinatorialData] + [Theory, CombinatorialData] public async Task TestMoveTypeToDifferentFile(bool mutatingLspWorkspace) { var markUp = @" @@ -423,7 +375,7 @@ class BCD AssertJsonEquals(expectedCodeAction, actualResolvedAction); } - [WpfTheory, CombinatorialData] + [Theory, CombinatorialData] public async Task TestMoveTypeToDifferentFileInDirectory(bool mutatingLspWorkspace) { var markup = diff --git a/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs b/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs index e62cd3c664ca5..dfda6924f09de 100644 --- a/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs @@ -20,7 +20,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.CodeActions; public class CodeActionsTests(ITestOutputHelper testOutputHelper) : AbstractLanguageServerProtocolTests(testOutputHelper) { - [WpfTheory, CombinatorialData] + [Theory, CombinatorialData] public async Task TestCodeActionHandlerAsync(bool mutatingLspWorkspace) { var markup = @@ -57,7 +57,7 @@ void M() AssertJsonEquals(expected, useImplicitType); } - [WpfTheory, CombinatorialData] + [Theory, CombinatorialData] public async Task TestCodeActionHandlerAsync_NestedAction(bool mutatingLspWorkspace) { var markup = @@ -96,7 +96,7 @@ void M() AssertJsonEquals(expected, introduceConstant); } - [WpfTheory, CombinatorialData] + [Theory, CombinatorialData] public async Task TestCodeActionHasCorrectDiagnostics(bool mutatingLspWorkspace) { var markup = @@ -138,7 +138,7 @@ void M() Assert.Equal(AddImportDiagnosticIds.CS0103, addImport.Diagnostics.Single().Code!.Value); } - [WpfTheory, CombinatorialData] + [Theory, CombinatorialData] public async Task TestNoSuppressionFixerInStandardLSP(bool mutatingLspWorkspace) { var markup = """ @@ -175,7 +175,7 @@ class ABC Assert.Equal("Make method synchronous", results[0].Title); } - [WpfTheory, CombinatorialData] + [Theory, CombinatorialData] public async Task TestStandardLspNestedCodeAction(bool mutatingLspWorkspace) { var markup = """ @@ -221,7 +221,7 @@ private void XYZ() Assert.NotNull(inline.Command); } - [WpfTheory, CombinatorialData] + [Theory, CombinatorialData] public async Task TestStandardLspNestedFixAllCodeAction(bool mutatingLspWorkspace) { var markup = """ @@ -266,7 +266,7 @@ class ABC Assert.Equal("Fix All: in Source", data.NestedCodeActions!.Value[1].Title); } - [WpfTheory, CombinatorialData] + [Theory, CombinatorialData] public async Task TestStandardLspNestedResolveTopLevelCodeAction(bool mutatingLspWorkspace) { var markup = """ diff --git a/src/LanguageServer/ProtocolUnitTests/CodeActions/RunCodeActionsTests.cs b/src/LanguageServer/ProtocolUnitTests/CodeActions/RunCodeActionsTests.cs index 80bd47e9d2207..741476e85769d 100644 --- a/src/LanguageServer/ProtocolUnitTests/CodeActions/RunCodeActionsTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/CodeActions/RunCodeActionsTests.cs @@ -24,7 +24,7 @@ public RunCodeActionsTests(ITestOutputHelper testOutputHelper) : base(testOutput { } - [WpfTheory(Skip = "https://github.com/dotnet/roslyn/issues/65303"), CombinatorialData] + [Theory(Skip = "https://github.com/dotnet/roslyn/issues/65303"), CombinatorialData] public async Task TestRunCodeActions(bool mutatingLspWorkspace) { var markup = diff --git a/src/LanguageServer/ProtocolUnitTests/Configuration/DidChangeConfigurationNotificationHandlerTest.cs b/src/LanguageServer/ProtocolUnitTests/Configuration/DidChangeConfigurationNotificationHandlerTest.cs index 12c2b718c5e91..cf7ac74550ea5 100644 --- a/src/LanguageServer/ProtocolUnitTests/Configuration/DidChangeConfigurationNotificationHandlerTest.cs +++ b/src/LanguageServer/ProtocolUnitTests/Configuration/DidChangeConfigurationNotificationHandlerTest.cs @@ -156,7 +156,7 @@ public void VerifyLspClientOptionNames() string.Join(Environment.NewLine, actualNames)); } - private static void VerifyValuesInServer(EditorTestWorkspace workspace, List expectedValues) + private static void VerifyValuesInServer(LspTestWorkspace workspace, List expectedValues) { var globalOptionService = workspace.GetService(); var supportedOptions = DidChangeConfigurationNotificationHandler.SupportedOptions; diff --git a/src/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs b/src/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs index 07731ea745e5e..ee9b851466ee1 100644 --- a/src/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs @@ -56,9 +56,6 @@ public async Task TestDocumentDiagnosticsForOpenFilesWithFSAOff(bool useVSDiagno var markup = @"class A {"; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); await OpenDocumentAsync(testLspServer, document); @@ -79,9 +76,6 @@ public async Task TestDocumentDiagnosticsForOpenFilesWithFSAOff_Categories(bool await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics, additionalAnalyzers: additionalAnalyzers); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); await OpenDocumentAsync(testLspServer, document); @@ -164,9 +158,6 @@ static void Main(string[] args) """; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: true); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); await OpenDocumentAsync(testLspServer, document); @@ -184,9 +175,6 @@ public async Task TestDocumentDiagnosticsUsesNullForExpandedMessage(bool mutatin var markup = @"class A {"; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: true); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); await OpenDocumentAsync(testLspServer, document); @@ -210,9 +198,6 @@ class A { """; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: true); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); await OpenDocumentAsync(testLspServer, document); @@ -231,8 +216,6 @@ public async Task TestDocumentDiagnosticsForOpenFilesIfDefaultAndFeatureFlagOn(b await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, GetInitializationOptions(BackgroundAnalysisScope.OpenFiles, CompilerDiagnosticsScope.OpenFiles, useVSDiagnostics)); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); await OpenDocumentAsync(testLspServer, document); @@ -247,9 +230,6 @@ public async Task TestDocumentDiagnosticsForRemovedDocument(bool useVSDiagnostic await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); var workspace = testLspServer.TestWorkspace; - // Calling GetTextBuffer will effectively open the file. - workspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); // Get the diagnostics for the solution containing the doc. @@ -278,9 +258,6 @@ public async Task TestNoChangeIfDocumentDiagnosticsCalledTwice(bool useVSDiagnos var markup = @"class A {"; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); await OpenDocumentAsync(testLspServer, document); @@ -304,9 +281,6 @@ public async Task TestDocumentDiagnosticsWhenGlobalStateChanges(bool useVSDiagno var markup = @"class A {"; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); await OpenDocumentAsync(testLspServer, document); @@ -334,17 +308,15 @@ public async Task TestDocumentDiagnosticsRemovedAfterErrorIsFixed(bool useVSDiag var markup = @"class A {"; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - // Calling GetTextBuffer will effectively open the file. - var buffer = testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + var text = await document.GetTextAsync(); await OpenDocumentAsync(testLspServer, document); var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics); Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); - await InsertTextAsync(testLspServer, document, buffer.CurrentSnapshot.Length, "}"); + await InsertTextAsync(testLspServer, document, text.Length, "}"); results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics, results.Single().ResultId); AssertEx.Empty(results[0].Diagnostics); @@ -356,17 +328,14 @@ public async Task TestDocumentDiagnosticsRemainAfterErrorIsNotFixed(bool useVSDi var markup = @"class A {"; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - // Calling GetTextBuffer will effectively open the file. - var buffer = testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + var text = await document.GetTextAsync(); await OpenDocumentAsync(testLspServer, document); var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics); Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); Assert.Equal(new Position { Line = 0, Character = 9 }, results[0].Diagnostics.Single().Range.Start); - buffer.Insert(0, " "); await InsertTextAsync(testLspServer, document, position: 0, text: " "); results = await RunGetDocumentPullDiagnosticsAsync( @@ -387,9 +356,6 @@ class A { """; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); await OpenDocumentAsync(testLspServer, document); @@ -407,9 +373,6 @@ public async Task TestStreamingDocumentDiagnostics(bool useVSDiagnostics, bool m var markup = @"class A {"; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); await OpenDocumentAsync(testLspServer, document); @@ -449,8 +412,11 @@ class B { // Open either of the documents via LSP, we're tracking the URI and text. await OpenDocumentAsync(testLspServer, csproj1Document); - // This opens all documents in the workspace and ensures buffers are created. - testLspServer.TestWorkspace.GetTestDocument(csproj1Document.Id)!.GetTextBuffer(); + // If we don't have a mutating workspace, we need to manually open all linked documents in the workspace (otherwise updating the context will not succeed). + if (!mutatingLspWorkspace) + { + await testLspServer.OpenDocumentInWorkspaceAsync(csproj2Document.Id, openAllLinkedDocuments: true); + } // Set CSProj2 as the active context and get diagnostics. testLspServer.TestWorkspace.SetDocumentContext(csproj2Document.Id); @@ -634,9 +600,6 @@ public async Task TestDocumentDiagnosticsFromRazorServer(bool useVSDiagnostics, await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, GetInitializationOptions(BackgroundAnalysisScope.OpenFiles, CompilerDiagnosticsScope.OpenFiles, useVSDiagnostics, WellKnownLspServerKinds.RazorLspServer)); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); await OpenDocumentAsync(testLspServer, document); @@ -657,9 +620,6 @@ public async Task TestDocumentDiagnosticsFromLiveShareServer(bool useVSDiagnosti await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, GetInitializationOptions(BackgroundAnalysisScope.OpenFiles, CompilerDiagnosticsScope.OpenFiles, useVSDiagnostics, WellKnownLspServerKinds.LiveShareLspServer)); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); await OpenDocumentAsync(testLspServer, document); @@ -679,9 +639,6 @@ public async Task TestDocumentDiagnosticsIncludesSourceGeneratorDiagnostics(bool await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); var generator = new DiagnosticProducingGenerator(context => Location.Create(context.Compilation.SyntaxTrees.Single(), new TextSpan(0, 10))); @@ -872,9 +829,6 @@ class A """; await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, mutatingLspWorkspace, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics: true); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); await OpenDocumentAsync(testLspServer, document); @@ -941,8 +895,7 @@ public async Task TestWorkspaceDiagnosticsForClosedFilesWithRunCodeAnalysisAndFS // Now fix the compiler error, but don't re-execute code analysis. // Verify that we still get the workspace diagnostics from the prior snapshot on which code analysis was executed. - var buffer = testLspServer.TestWorkspace.Documents.First().GetTextBuffer(); - buffer.Insert(buffer.CurrentSnapshot.Length, "}"); + await InsertInClosedDocumentAsync(testLspServer, testLspServer.TestWorkspace.Documents.First().Id, "}"); var results2 = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: CreateDiagnosticParamsFromPreviousReports(results)); @@ -982,8 +935,7 @@ public async Task TestWorkspaceDiagnosticsForClosedFilesWithRunCodeAnalysisFSAOn // Now fix the compiler error, but don't rerun code analysis. // Verify that we get up-to-date workspace diagnostics, i.e. no compiler errors, from the current snapshot because FSA is enabled. - var buffer = testLspServer.TestWorkspace.Documents.First().GetTextBuffer(); - buffer.Insert(buffer.CurrentSnapshot.Length, "}"); + await InsertInClosedDocumentAsync(testLspServer, testLspServer.TestWorkspace.Documents.First().Id, "}"); var results2 = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: CreateDiagnosticParamsFromPreviousReports(results)); @@ -1456,8 +1408,7 @@ public async Task TestWorkspaceDiagnosticsRemovedAfterErrorIsFixed(bool useVSDia AssertEx.Empty(results[1].Diagnostics); AssertEx.Empty(results[2].Diagnostics); - var buffer = testLspServer.TestWorkspace.Documents.First().GetTextBuffer(); - buffer.Insert(buffer.CurrentSnapshot.Length, "}"); + await InsertInClosedDocumentAsync(testLspServer, testLspServer.TestWorkspace.Documents.First().Id, "}"); var results2 = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: CreateDiagnosticParamsFromPreviousReports(results)); @@ -1484,17 +1435,11 @@ public async Task TestWorkspaceDiagnosticsRemainAfterErrorIsNotFixed(bool useVSD AssertEx.Empty(results[1].Diagnostics); AssertEx.Empty(results[2].Diagnostics); - var buffer = testLspServer.TestWorkspace.Documents.First().GetTextBuffer(); - buffer.Insert(0, " "); + await InsertInClosedDocumentAsync(testLspServer, testLspServer.TestWorkspace.Documents.First().Id, " ", position: 0); var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.First(); var text = await document.GetTextAsync(); - // Hacky, but we need to close the document manually since editing the text-buffer will open it in the - // test-workspace. - testLspServer.TestWorkspace.OnDocumentClosed( - document.Id, TextLoader.From(TextAndVersion.Create(text, VersionStamp.Create()))); - var results2 = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); Assert.Equal("CS1513", results2[0].Diagnostics.Single().Code); @@ -2147,5 +2092,13 @@ public async Task TestWorkspaceDiagnosticsForClosedFilesSwitchFSAFromOffToOn(boo AssertEx.Empty(results[1].Diagnostics); } + internal static async Task InsertInClosedDocumentAsync(TestLspServer testLspServer, DocumentId documentId, string textToInsert, int? position = null) + { + var text = await testLspServer.GetCurrentSolution().GetDocument(documentId)!.GetTextAsync(CancellationToken.None); + position ??= text.Length; + text = text.WithChanges(new TextChange(new TextSpan(position.Value, 0), textToInsert)); + await testLspServer.TestWorkspace.ChangeDocumentAsync(documentId, text); + } + #endregion } diff --git a/src/LanguageServer/ProtocolUnitTests/Microsoft.CodeAnalysis.LanguageServer.Protocol.UnitTests.csproj b/src/LanguageServer/ProtocolUnitTests/Microsoft.CodeAnalysis.LanguageServer.Protocol.UnitTests.csproj index 1cf6c995899f4..301c552aa7836 100644 --- a/src/LanguageServer/ProtocolUnitTests/Microsoft.CodeAnalysis.LanguageServer.Protocol.UnitTests.csproj +++ b/src/LanguageServer/ProtocolUnitTests/Microsoft.CodeAnalysis.LanguageServer.Protocol.UnitTests.csproj @@ -3,7 +3,7 @@ - net472 + $(NetVSCode);net472 Library Microsoft.CodeAnalysis.LanguageServer.UnitTests UnitTest @@ -23,13 +23,6 @@ - - - - - - - @@ -43,6 +36,7 @@ + diff --git a/src/LanguageServer/ProtocolUnitTests/Options/LspOptionsTests.cs b/src/LanguageServer/ProtocolUnitTests/Options/LspOptionsTests.cs index f85284982a6ef..22f7e85fe4175 100644 --- a/src/LanguageServer/ProtocolUnitTests/Options/LspOptionsTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Options/LspOptionsTests.cs @@ -6,8 +6,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.AddImport; using Microsoft.CodeAnalysis.CodeGeneration; -using Microsoft.CodeAnalysis.CodeStyle; -using Microsoft.CodeAnalysis.Editor.Test; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Test.Utilities; diff --git a/src/LanguageServer/ProtocolUnitTests/ProjectContext/GetTextDocumentWithContextHandlerTests.cs b/src/LanguageServer/ProtocolUnitTests/ProjectContext/GetTextDocumentWithContextHandlerTests.cs index 44befe94053bb..a255360e48280 100644 --- a/src/LanguageServer/ProtocolUnitTests/ProjectContext/GetTextDocumentWithContextHandlerTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/ProjectContext/GetTextDocumentWithContextHandlerTests.cs @@ -81,11 +81,9 @@ public async Task SwitchingContextsChangesDefaultContext(bool mutatingLspWorkspa await using var testLspServer = await CreateXmlTestLspServerAsync(workspaceXml, mutatingLspWorkspace); - // Ensure the documents are open so we can change contexts - foreach (var document in testLspServer.TestWorkspace.Documents) - { - _ = document.GetOpenTextContainer(); - } + // Ensure all the linked documents are open so we can change contexts + var document = testLspServer.TestWorkspace.Documents.First(); + await testLspServer.OpenDocumentInWorkspaceAsync(document.Id, openAllLinkedDocuments: true); var documentUri = testLspServer.GetLocations("caret").Single().Uri; diff --git a/src/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerFeaturesTests.cs b/src/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerFeaturesTests.cs index 8fda99724efe7..d57d43540f495 100644 --- a/src/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerFeaturesTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerFeaturesTests.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Editor.Test; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; @@ -70,8 +69,8 @@ class SomeClass{{i}} } """; - var testDocument = new EditorTestHostDocument(text: source, displayName: @$"C:\SomeFile{i}.cs", exportProvider: testLspServer.TestWorkspace.ExportProvider, filePath: @$"C:\SomeFile{i}.cs"); - testLspServer.TestWorkspace.AddTestProject(new EditorTestHostProject(testLspServer.TestWorkspace, documents: [testDocument])); + var testDocument = new TestHostDocument(text: source, displayName: @$"C:\SomeFile{i}.cs", exportProvider: testLspServer.TestWorkspace.ExportProvider, filePath: @$"C:\SomeFile{i}.cs"); + testLspServer.TestWorkspace.AddTestProject(new TestHostProject(testLspServer.TestWorkspace, documents: [testDocument])); } await WaitForWorkspaceOperationsAsync(testLspServer.TestWorkspace); diff --git a/src/LanguageServer/ProtocolUnitTests/Rename/RenameTests.cs b/src/LanguageServer/ProtocolUnitTests/Rename/RenameTests.cs index 68c71fadb2528..870cbd83c119b 100644 --- a/src/LanguageServer/ProtocolUnitTests/Rename/RenameTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Rename/RenameTests.cs @@ -22,7 +22,7 @@ public RenameTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { } - [WpfTheory, CombinatorialData] + [Theory, CombinatorialData] public async Task TestRenameAsync(bool mutatingLspWorkspace) { var markup = @@ -45,7 +45,7 @@ void M2() AssertJsonEquals(expectedEdits, ((TextDocumentEdit[])results.DocumentChanges).First().Edits); } - [WpfTheory, CombinatorialData] + [Theory, CombinatorialData] public async Task TestRename_InvalidIdentifierAsync(bool mutatingLspWorkspace) { var markup = @@ -67,7 +67,7 @@ void M2() Assert.Null(results); } - [WpfTheory, CombinatorialData] + [Theory, CombinatorialData] public async Task TestRename_WithLinkedFilesAsync(bool mutatingLspWorkspace) { var markup = @@ -101,7 +101,7 @@ void M2() AssertJsonEquals(expectedEdits, ((TextDocumentEdit[])results.DocumentChanges).First().Edits); } - [WpfTheory, CombinatorialData] + [Theory, CombinatorialData] public async Task TestRename_WithLinkedFilesAndPreprocessorAsync(bool mutatingLspWorkspace) { var markup = @@ -147,7 +147,7 @@ void M4() AssertJsonEquals(expectedEdits, ((TextDocumentEdit[])results.DocumentChanges).First().Edits); } - [WpfTheory, CombinatorialData] + [Theory, CombinatorialData] public async Task TestRename_WithMappedFileAsync(bool mutatingLspWorkspace) { var markup = diff --git a/src/LanguageServer/ProtocolUnitTests/SpellCheck/SpellCheckTests.cs b/src/LanguageServer/ProtocolUnitTests/SpellCheck/SpellCheckTests.cs index f9eaf873a3a5f..aad9886aeadb5 100644 --- a/src/LanguageServer/ProtocolUnitTests/SpellCheck/SpellCheckTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/SpellCheck/SpellCheckTests.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageServer.Handler; +using Microsoft.CodeAnalysis.LanguageServer.UnitTests.Diagnostics; using Microsoft.CodeAnalysis.Text; using Roslyn.LanguageServer.Protocol; using Roslyn.Test.Utilities; @@ -48,9 +49,7 @@ public async Task TestDocumentResultsForOpenFiles(bool mutatingLspWorkspace) }"; await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); - // Calling GetTextBuffer will effectively open the file. var testDocument = testLspServer.TestWorkspace.Documents.Single(); - testDocument.GetTextBuffer(); var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); @@ -81,7 +80,6 @@ class {|Identifier:A{{v}}|} """)); await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); - // Calling GetTextBuffer will effectively open the file. var testDocument = testLspServer.TestWorkspace.Documents.Single(); var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); @@ -114,9 +112,6 @@ public async Task TestDocumentResultsForRemovedDocument(bool mutatingLspWorkspac await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); var workspace = testLspServer.TestWorkspace; - // Calling GetTextBuffer will effectively open the file. - workspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); // Get the diagnostics for the solution containing the doc. @@ -153,9 +148,6 @@ public async Task TestNoChangeIfDocumentResultsCalledTwice(bool mutatingLspWorks }"; await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); await OpenDocumentAsync(testLspServer, document); @@ -189,9 +181,6 @@ public async Task TestDocumentResultChangedAfterEntityAdded(bool mutatingLspWork "; await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); - // Calling GetTextBuffer will effectively open the file. - var buffer = testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); await OpenDocumentAsync(testLspServer, document); @@ -206,7 +195,7 @@ public async Task TestDocumentResultChangedAfterEntityAdded(bool mutatingLspWork Ranges = GetRanges(testLspServer.TestWorkspace.Documents.Single().AnnotatedSpans), }); - await InsertTextAsync(testLspServer, document, buffer.CurrentSnapshot.Length, "// comment"); + await InsertTextAsync(testLspServer, document, sourceText.Length, "// comment"); var (_, lspSolution) = await testLspServer.GetManager().GetLspSolutionInfoAsync(CancellationToken.None).ConfigureAwait(false); document = lspSolution!.Projects.Single().Documents.Single(); @@ -237,9 +226,6 @@ public async Task TestDocumentResultIdSameAfterIrrelevantEdit(bool mutatingLspWo }"; await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); - // Calling GetTextBuffer will effectively open the file. - var buffer = testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); await OpenDocumentAsync(testLspServer, document); @@ -277,9 +263,6 @@ class {|Identifier:A|} }"; await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); await OpenDocumentAsync(testLspServer, document); @@ -304,9 +287,6 @@ public async Task TestStreamingDocumentDiagnostics(bool mutatingLspWorkspace) }"; await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); - // Calling GetTextBuffer will effectively open the file. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); - var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); await OpenDocumentAsync(testLspServer, document); @@ -497,8 +477,7 @@ public async Task TestWorkspaceResultUpdatedAfterEdit(bool mutatingLspWorkspace) }); AssertEx.Empty(results[1].Ranges); - var buffer = testLspServer.TestWorkspace.Documents.First().GetTextBuffer(); - buffer.Insert(buffer.CurrentSnapshot.Length, "// comment"); + await PullDiagnosticTests.InsertInClosedDocumentAsync(testLspServer, document.Id, "// comment"); var results2 = await RunGetWorkspaceSpellCheckSpansAsync(testLspServer, previousResults: CreateParamsFromPreviousReports(results)); diff --git a/src/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceManagerTests.cs b/src/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceManagerTests.cs index 5866d332036e1..8226be4f8c8b4 100644 --- a/src/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceManagerTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceManagerTests.cs @@ -298,7 +298,7 @@ public async Task TestUsesRegisteredHostWorkspace(bool mutatingLspWorkspace) // Verify 1 workspace registered to start with. Assert.True(IsWorkspaceRegistered(testLspServer.TestWorkspace, testLspServer)); - using var testWorkspaceTwo = EditorTestWorkspace.Create( + using var testWorkspaceTwo = LspTestWorkspace.Create( XElement.Parse(secondWorkspaceXml), workspaceKind: "OtherWorkspaceKind", composition: testLspServer.TestWorkspace.Composition); @@ -512,14 +512,15 @@ public async Task TestSeparateWorkspaceManagerPerServerAsync(bool mutatingLspWor } [Theory, CombinatorialData] - public async Task TestDoesNotForkWhenDocumentTextBufferOpenedAsync(bool mutatingLspWorkspace) + public async Task TestDoesNotForkWhenDocumentOpenedInWorkspaceAsync(bool mutatingLspWorkspace) { var markup = "Text"; await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); + var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); var documentUri = testLspServer.GetCurrentSolution().Projects.First().Documents.First().GetURI(); - // Calling get text buffer opens the document in the workspace. - testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); + // Open document in the workspace. + await testLspServer.OpenDocumentInWorkspaceAsync(document.Id, openAllLinkedDocuments: false); await testLspServer.OpenDocumentAsync(documentUri, "Text"); diff --git a/src/LanguageServer/ProtocolUnitTests/Workspaces/SourceGeneratedDocumentTests.cs b/src/LanguageServer/ProtocolUnitTests/Workspaces/SourceGeneratedDocumentTests.cs index 1d6ae9f07598d..88d6e061debe9 100644 --- a/src/LanguageServer/ProtocolUnitTests/Workspaces/SourceGeneratedDocumentTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Workspaces/SourceGeneratedDocumentTests.cs @@ -281,7 +281,7 @@ public async Task TestReturnsNullForRemovedOpenedGeneratedFile(bool mutatingLspW Assert.Null(secondRequest.Text); } - private static async Task WaitForSourceGeneratorsAsync(EditorTestWorkspace workspace) + private static async Task WaitForSourceGeneratorsAsync(LspTestWorkspace workspace) { var operations = workspace.ExportProvider.GetExportedValue(); await operations.WaitAllAsync(workspace, [FeatureAttribute.Workspace, FeatureAttribute.SourceGenerators]); From c3fc118219d4bf572785dd6cb71fde2f6abb909f Mon Sep 17 00:00:00 2001 From: David Barbet Date: Tue, 11 Feb 2025 18:03:50 -0800 Subject: [PATCH 224/305] cleanup warnings in pull diags tests --- .../Diagnostics/PullDiagnosticTests.cs | 208 +++++++++--------- 1 file changed, 106 insertions(+), 102 deletions(-) diff --git a/src/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs b/src/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs index ee9b851466ee1..7b9becddaa4dd 100644 --- a/src/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs @@ -63,8 +63,8 @@ public async Task TestDocumentDiagnosticsForOpenFilesWithFSAOff(bool useVSDiagno var results = await RunGetDocumentPullDiagnosticsAsync( testLspServer, document.GetURI(), useVSDiagnostics); - Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); - Assert.NotNull(results.Single().Diagnostics.Single().CodeDescription!.Href); + Assert.Equal("CS1513", results.Single().Diagnostics!.Single().Code); + Assert.NotNull(results.Single().Diagnostics!.Single().CodeDescription!.Href); } [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/fsharp/issues/15972")] @@ -86,8 +86,8 @@ public async Task TestDocumentDiagnosticsForOpenFilesWithFSAOff_Categories(bool var semanticResults = await RunGetDocumentPullDiagnosticsAsync( testLspServer, document.GetURI(), useVSDiagnostics, category: PullDiagnosticCategories.DocumentCompilerSemantic); - Assert.Equal("CS1513", syntaxResults.Single().Diagnostics.Single().Code); - Assert.Equal("CS0246", semanticResults.Single().Diagnostics.Single().Code); + Assert.Equal("CS1513", syntaxResults.Single().Diagnostics!.Single().Code); + Assert.Equal("CS0246", semanticResults.Single().Diagnostics!.Single().Code); var syntaxResults2 = await RunGetDocumentPullDiagnosticsAsync( testLspServer, document.GetURI(), useVSDiagnostics, previousResultId: syntaxResults.Single().ResultId, category: PullDiagnosticCategories.DocumentCompilerSyntax); @@ -103,8 +103,8 @@ public async Task TestDocumentDiagnosticsForOpenFilesWithFSAOff_Categories(bool var semanticAnalyzerResults = await RunGetDocumentPullDiagnosticsAsync( testLspServer, document.GetURI(), useVSDiagnostics, category: PullDiagnosticCategories.DocumentAnalyzerSemantic); - Assert.Equal(CSharpSyntaxAnalyzer.RuleId, syntaxAnalyzerResults.Single().Diagnostics.Single().Code); - Assert.Equal(CSharpSemanticAnalyzer.RuleId, semanticAnalyzerResults.Single().Diagnostics.Single().Code); + Assert.Equal(CSharpSyntaxAnalyzer.RuleId, syntaxAnalyzerResults.Single().Diagnostics!.Single().Code); + Assert.Equal(CSharpSemanticAnalyzer.RuleId, semanticAnalyzerResults.Single().Diagnostics!.Single().Code); var syntaxAnalyzerResults2 = await RunGetDocumentPullDiagnosticsAsync( testLspServer, document.GetURI(), useVSDiagnostics, previousResultId: syntaxAnalyzerResults.Single().ResultId, category: PullDiagnosticCategories.DocumentAnalyzerSyntax); @@ -164,8 +164,8 @@ static void Main(string[] args) var results = await RunGetDocumentPullDiagnosticsAsync( testLspServer, document.GetURI(), useVSDiagnostics: true); - Assert.Equal("IDE0060", results.Single().Diagnostics.Single().Code); - var vsDiagnostic = (VSDiagnostic)results.Single().Diagnostics.Single(); + Assert.Equal("IDE0060", results.Single().Diagnostics!.Single().Code); + var vsDiagnostic = (VSDiagnostic)results.Single().Diagnostics!.Single(); Assert.Equal(vsDiagnostic.ExpandedMessage, AnalyzersResources.Avoid_unused_parameters_in_your_code_If_the_parameter_cannot_be_removed_then_change_its_name_so_it_starts_with_an_underscore_and_is_optionally_followed_by_an_integer_such_as__comma__1_comma__2_etc_These_are_treated_as_special_discard_symbol_names); } @@ -182,8 +182,8 @@ public async Task TestDocumentDiagnosticsUsesNullForExpandedMessage(bool mutatin var results = await RunGetDocumentPullDiagnosticsAsync( testLspServer, document.GetURI(), useVSDiagnostics: true); - Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); - var vsDiagnostic = (VSDiagnostic)results.Single().Diagnostics.Single(); + Assert.Equal("CS1513", results.Single().Diagnostics!.Single().Code); + var vsDiagnostic = (VSDiagnostic)results.Single().Diagnostics!.Single(); Assert.Null(vsDiagnostic.ExpandedMessage); } @@ -205,8 +205,8 @@ class A { var results = await RunGetDocumentPullDiagnosticsAsync( testLspServer, document.GetURI(), useVSDiagnostics: true, category: PullDiagnosticCategories.Task); - Assert.Equal("TODO", results.Single().Diagnostics.Single().Code); - Assert.Equal("todo: goo", results.Single().Diagnostics.Single().Message); + Assert.Equal("TODO", results.Single().Diagnostics!.Single().Code); + Assert.Equal("todo: goo", results.Single().Diagnostics!.Single().Message); } [Theory, CombinatorialData] @@ -220,7 +220,7 @@ public async Task TestDocumentDiagnosticsForOpenFilesIfDefaultAndFeatureFlagOn(b await OpenDocumentAsync(testLspServer, document); var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics); - Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); + Assert.Equal("CS1513", results.Single().Diagnostics!.Single().Code); } [Theory, CombinatorialData] @@ -239,7 +239,7 @@ public async Task TestDocumentDiagnosticsForRemovedDocument(bool useVSDiagnostic var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics).ConfigureAwait(false); - Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); + Assert.Equal("CS1513", results.Single().Diagnostics!.Single().Code); // Now remove the doc. workspace.OnDocumentRemoved(workspace.Documents.Single().Id); @@ -264,7 +264,7 @@ public async Task TestNoChangeIfDocumentDiagnosticsCalledTwice(bool useVSDiagnos var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics); - Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); + Assert.Equal("CS1513", results.Single().Diagnostics!.Single().Code); var resultId = results.Single().ResultId; results = await RunGetDocumentPullDiagnosticsAsync( @@ -287,7 +287,7 @@ public async Task TestDocumentDiagnosticsWhenGlobalStateChanges(bool useVSDiagno var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics); - Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); + Assert.Equal("CS1513", results.Single().Diagnostics!.Single().Code); var resultId = results.Single().ResultId; @@ -314,7 +314,7 @@ public async Task TestDocumentDiagnosticsRemovedAfterErrorIsFixed(bool useVSDiag await OpenDocumentAsync(testLspServer, document); var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); + Assert.Equal("CS1513", results[0].Diagnostics!.Single().Code); await InsertTextAsync(testLspServer, document, text.Length, "}"); @@ -333,8 +333,8 @@ public async Task TestDocumentDiagnosticsRemainAfterErrorIsNotFixed(bool useVSDi await OpenDocumentAsync(testLspServer, document); var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); - Assert.Equal(new Position { Line = 0, Character = 9 }, results[0].Diagnostics.Single().Range.Start); + Assert.Equal("CS1513", results[0].Diagnostics!.Single().Code); + Assert.Equal(new Position { Line = 0, Character = 9 }, results[0].Diagnostics!.Single().Range.Start); await InsertTextAsync(testLspServer, document, position: 0, text: " "); @@ -342,8 +342,8 @@ public async Task TestDocumentDiagnosticsRemainAfterErrorIsNotFixed(bool useVSDi testLspServer, document.GetURI(), useVSDiagnostics, previousResultId: results[0].ResultId); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); - Assert.Equal(new Position { Line = 0, Character = 10 }, results[0].Diagnostics.Single().Range.Start); + Assert.Equal("CS1513", results[0].Diagnostics!.Single().Code); + Assert.Equal(new Position { Line = 0, Character = 10 }, results[0].Diagnostics!.Single().Range.Start); } [Theory, CombinatorialData] @@ -363,8 +363,8 @@ class A { var results = await RunGetDocumentPullDiagnosticsAsync( testLspServer, document.GetURI(), useVSDiagnostics); - Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); - Assert.Equal(1, results.Single().Diagnostics.Single().Range.Start.Line); + Assert.Equal("CS1513", results.Single().Diagnostics!.Single().Code); + Assert.Equal(1, results.Single().Diagnostics!.Single().Range.Start.Line); } [Theory, CombinatorialData] @@ -379,7 +379,7 @@ public async Task TestStreamingDocumentDiagnostics(bool useVSDiagnostics, bool m var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics, useProgress: true); - Assert.Equal("CS1513", results!.Single().Diagnostics.Single().Code); + Assert.Equal("CS1513", results!.Single().Diagnostics!.Single().Code); } [Theory, CombinatorialData] @@ -421,12 +421,12 @@ class B { // Set CSProj2 as the active context and get diagnostics. testLspServer.TestWorkspace.SetDocumentContext(csproj2Document.Id); var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, csproj2Document.GetURI(), useVSDiagnostics); - Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); + Assert.Equal("CS1513", results.Single().Diagnostics!.Single().Code); if (useVSDiagnostics) { // Only VSDiagnostics will have the project. - var vsDiagnostic = (LSP.VSDiagnostic)results.Single().Diagnostics.Single(); - Assert.Equal("CSProj2", vsDiagnostic.Projects.Single().ProjectName); + var vsDiagnostic = (LSP.VSDiagnostic)results.Single().Diagnostics!.Single(); + Assert.Equal("CSProj2", vsDiagnostic.Projects!.Single().ProjectName); } // Set CSProj1 as the active context and get diagnostics. @@ -437,7 +437,7 @@ class B { if (useVSDiagnostics) { - AssertEx.All(results.Single().Diagnostics, d => Assert.Equal("CSProj1", ((VSDiagnostic)d).Projects.Single().ProjectName)); + AssertEx.All(results.Single().Diagnostics, d => Assert.Equal("CSProj1", ((VSDiagnostic)d).Projects!.Single().ProjectName)); } } @@ -466,9 +466,9 @@ public async Task TestDocumentDiagnosticsHasSameIdentifierForLinkedFile(bool mut await OpenDocumentAsync(testLspServer, csproj1Document); var csproj1Results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, GetVsTextDocumentIdentifier(csproj1Document), useVSDiagnostics: true); - var csproj1Diagnostic = (VSDiagnostic)csproj1Results.Single().Diagnostics.Single(); + var csproj1Diagnostic = (VSDiagnostic)csproj1Results.Single().Diagnostics!.Single(); var csproj2Results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, GetVsTextDocumentIdentifier(csproj2Document), useVSDiagnostics: true); - var csproj2Diagnostic = (VSDiagnostic)csproj2Results.Single().Diagnostics.Single(); + var csproj2Diagnostic = (VSDiagnostic)csproj2Results.Single().Diagnostics!.Single(); Assert.Equal(csproj1Diagnostic.Identifier, csproj2Diagnostic.Identifier); static VSTextDocumentIdentifier GetVsTextDocumentIdentifier(Document document) @@ -527,7 +527,7 @@ public class {|caret:|} { } // Verify we a diagnostic in A.cs since B does not exist. var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, csproj1Document.GetURI(), useVSDiagnostics); Assert.Single(results); - Assert.Equal("CS0246", results.Single().Diagnostics.Single().Code); + Assert.Equal("CS0246", results.Single().Diagnostics!.Single().Code); // Insert B into B.cs and verify that the error in A.cs is now gone. var locationToReplace = testLspServer.GetLocations("caret").Single().Range; @@ -579,7 +579,7 @@ public class {|caret:|} { } // Verify we get a diagnostic in A since the class B does not exist. var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, csproj1Document.GetURI(), useVSDiagnostics); Assert.Single(results); - Assert.Equal("CS0246", results.Single().Diagnostics.Single().Code); + Assert.Equal("CS0246", results.Single().Diagnostics!.Single().Code); // Add B to CSProj2 and verify that we get an unchanged result (still has diagnostic) for A.cs // since CSProj1 does not reference CSProj2 @@ -608,8 +608,8 @@ public async Task TestDocumentDiagnosticsFromRazorServer(bool useVSDiagnostics, testLspServer, document.GetURI(), useVSDiagnostics); // Assert that we have diagnostics even though the option is set to push. - Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); - Assert.NotNull(results.Single().Diagnostics.Single().CodeDescription!.Href); + Assert.Equal("CS1513", results.Single().Diagnostics!.Single().Code); + Assert.NotNull(results.Single().Diagnostics!.Single().CodeDescription!.Href); } [Theory, CombinatorialData] @@ -628,8 +628,8 @@ public async Task TestDocumentDiagnosticsFromLiveShareServer(bool useVSDiagnosti testLspServer, document.GetURI(), useVSDiagnostics); // Assert that we have diagnostics even though the option is set to push. - Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); - Assert.NotNull(results.Single().Diagnostics.Single().CodeDescription!.Href); + Assert.Equal("CS1513", results.Single().Diagnostics!.Single().Code); + Assert.NotNull(results.Single().Diagnostics!.Single().CodeDescription!.Href); } [Theory, CombinatorialData] @@ -764,14 +764,14 @@ void M() else { // There should be one unnecessary diagnostic. - Assert.True(results.Single().Diagnostics.Single().Tags!.Contains(DiagnosticTag.Unnecessary)); - Assert.Equal(lineLocation, results.Single().Diagnostics.Single().Range); + Assert.True(results.Single().Diagnostics!.Single().Tags!.Contains(DiagnosticTag.Unnecessary)); + Assert.Equal(lineLocation, results.Single().Diagnostics!.Single().Range); // There should be an additional location for the open paren. - Assert.Equal(openLocation, results.Single().Diagnostics.Single().RelatedInformation![0].Location.Range); + Assert.Equal(openLocation, results.Single().Diagnostics!.Single().RelatedInformation![0].Location.Range); // There should be an additional location for the close paren. - Assert.Equal(closeLocation, results.Single().Diagnostics.Single().RelatedInformation![1].Location.Range); + Assert.Equal(closeLocation, results.Single().Diagnostics!.Single().RelatedInformation![1].Location.Range); } } @@ -788,7 +788,7 @@ public async Task TestDocumentDiagnosticsForUnnecessarySuppressions(bool useVSDi var results = await RunGetDocumentPullDiagnosticsAsync( testLspServer, document.GetURI(), useVSDiagnostics); - Assert.Equal(IDEDiagnosticIds.RemoveUnnecessarySuppressionDiagnosticId, results.Single().Diagnostics.Single().Code); + Assert.Equal(IDEDiagnosticIds.RemoveUnnecessarySuppressionDiagnosticId, results.Single().Diagnostics!.Single().Code); } [Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1824321")] @@ -836,8 +836,8 @@ class A var results = await RunGetDocumentPullDiagnosticsAsync( testLspServer, document.GetURI(), useVSDiagnostics: true); - Assert.Equal("IDE0090", results.Single().Diagnostics.Single().Code); - Assert.Equal(LSP.DiagnosticSeverity.Information, results.Single().Diagnostics.Single().Severity); + Assert.Equal("IDE0090", results.Single().Diagnostics!.Single().Code); + Assert.Equal(LSP.DiagnosticSeverity.Information, results.Single().Diagnostics!.Single().Severity); } #endregion @@ -868,7 +868,7 @@ public async Task TestWorkspaceDiagnosticsForClosedFilesWithFSAOn(bool useVSDiag var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); Assert.Equal(3, results.Length); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); + Assert.Equal("CS1513", results[0].Diagnostics!.Single().Code); AssertEx.Empty(results[1].Diagnostics); AssertEx.Empty(results[2].Diagnostics); } @@ -887,9 +887,9 @@ public async Task TestWorkspaceDiagnosticsForClosedFilesWithRunCodeAnalysisAndFS var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); Assert.Equal(3, results.Length); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); + Assert.Equal("CS1513", results[0].Diagnostics!.Single().Code); // this should be considered a build-error, since it was produced by the last code-analysis run. - Assert.Contains(VSDiagnosticTags.BuildError, results[0].Diagnostics.Single().Tags!); + Assert.Contains(VSDiagnosticTags.BuildError, results[0].Diagnostics!.Single().Tags!); AssertEx.Empty(results[1].Diagnostics); AssertEx.Empty(results[2].Diagnostics); @@ -927,9 +927,9 @@ public async Task TestWorkspaceDiagnosticsForClosedFilesWithRunCodeAnalysisFSAOn var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); Assert.Equal(3, results.Length); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); + Assert.Equal("CS1513", results[0].Diagnostics!.Single().Code); // this should *not* be considered a build-error, since it was produced by the live workspace results. - Assert.DoesNotContain(VSDiagnosticTags.BuildError, results[0].Diagnostics.Single().Tags!); + Assert.DoesNotContain(VSDiagnosticTags.BuildError, results[0].Diagnostics!.Single().Tags!); AssertEx.Empty(results[1].Diagnostics); AssertEx.Empty(results[2].Diagnostics); @@ -973,7 +973,7 @@ public async Task SourceGeneratorFailures_FSA(bool useVSDiagnostics, bool mutati Assert.Equal(2, results.Length); AssertEx.Empty(results[0].Diagnostics); - Assert.True(results[1].Diagnostics.Single().Message.Contains("Source generator failed")); + Assert.True(results[1].Diagnostics!.Single().Message.Contains("Source generator failed")); } [Theory, CombinatorialData] @@ -1008,9 +1008,9 @@ class A { var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics: true, includeTaskListItems: true, category: PullDiagnosticCategories.Task); Assert.Equal(1, results.Length); - Assert.Equal("TODO", results[0].Diagnostics.Single().Code); - Assert.Equal("todo: goo", results[0].Diagnostics.Single().Message); - Assert.Equal(VSDiagnosticRank.Default, ((VSDiagnostic)results[0].Diagnostics.Single()).DiagnosticRank); + Assert.Equal("TODO", results[0].Diagnostics!.Single().Code); + Assert.Equal("todo: goo", results[0].Diagnostics!.Single().Message); + Assert.Equal(VSDiagnosticRank.Default, ((VSDiagnostic)results[0].Diagnostics!.Single()).DiagnosticRank); } [Theory] @@ -1040,9 +1040,9 @@ class A { var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics: true, includeTaskListItems: true, category: PullDiagnosticCategories.Task); Assert.Equal(1, results.Length); - Assert.Equal("TODO", results[0].Diagnostics.Single().Code); - Assert.Equal("todo: goo", results[0].Diagnostics.Single().Message); - Assert.Equal(rank, ((VSDiagnostic)results[0].Diagnostics.Single()).DiagnosticRank); + Assert.Equal("TODO", results[0].Diagnostics!.Single().Code); + Assert.Equal("todo: goo", results[0].Diagnostics!.Single().Message); + Assert.Equal(rank, ((VSDiagnostic)results[0].Diagnostics!.Single()).DiagnosticRank); } [Theory, CombinatorialData] @@ -1078,8 +1078,8 @@ class A { Assert.Equal(1, results.Length); - Assert.Equal("TODO", results[0].Diagnostics.Single().Code); - Assert.Equal("todo: goo", results[0].Diagnostics.Single().Message); + Assert.Equal("TODO", results[0].Diagnostics!.Single().Code); + Assert.Equal("todo: goo", results[0].Diagnostics!.Single().Message); } [Theory, CombinatorialData] @@ -1222,23 +1222,27 @@ public async Task EditAndContinue(bool useVSDiagnostics, bool mutatingLspWorkspa AssertEx.Equal([], workspaceResults3.Select(Inspect)); static DiagnosticData CreateDiagnostic(string id, Document? document = null, Project? project = null) - => new( - id, - category: "EditAndContinue", - message: "test message", - severity: DiagnosticSeverity.Error, - defaultSeverity: DiagnosticSeverity.Error, - isEnabledByDefault: true, - warningLevel: 0, - projectId: project?.Id, - customTags: [], - properties: ImmutableDictionary.Empty, - location: new DiagnosticDataLocation(new FileLinePositionSpan("file", span: default), document?.Id), - additionalLocations: [], - language: (project ?? document!.Project).Language); + { + return new( + id, + category: "EditAndContinue", + message: "test message", + severity: DiagnosticSeverity.Error, + defaultSeverity: DiagnosticSeverity.Error, + isEnabledByDefault: true, + warningLevel: 0, + projectId: project?.Id, + customTags: [], + properties: ImmutableDictionary.Empty, + location: new DiagnosticDataLocation(new FileLinePositionSpan("file", span: default), document?.Id), + additionalLocations: [], + language: (project ?? document!.Project).Language); + } static string Inspect(TestDiagnosticResult result) - => $"{result.TextDocument.Uri} -> [{string.Join(",", result.Diagnostics?.Select(d => d.Code?.Value) ?? [])}]"; + { + return $"{result.TextDocument.Uri} -> [{string.Join(",", result.Diagnostics?.Select(d => d.Code?.Value) ?? [])}]"; + } } [Theory, CombinatorialData] @@ -1274,7 +1278,7 @@ public async Task TestWorkspaceDiagnosticsIncludesSourceGeneratorDiagnosticsClos var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - Assert.Equal(DiagnosticProducingGenerator.Descriptor.Id, results[0].Diagnostics.Single().Code); + Assert.Equal(DiagnosticProducingGenerator.Descriptor.Id, results[0].Diagnostics!.Single().Code); AssertEx.Empty(results[1].Diagnostics); } @@ -1342,7 +1346,7 @@ public async Task TestWorkspaceDiagnosticsForSourceGeneratedFiles(bool useVSDiag Assert.Equal(3, results.Length); // Since we sorted above by URI the first result is the project. AssertEx.Empty(results[0].Diagnostics); - Assert.Equal("CS1513", results[1].Diagnostics.Single().Code); + Assert.Equal("CS1513", results[1].Diagnostics!.Single().Code); AssertEx.Empty(results[2].Diagnostics); } @@ -1357,7 +1361,7 @@ public async Task TestWorkspaceDiagnosticsForRemovedDocument(bool useVSDiagnosti var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); Assert.Equal(3, results.Length); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); + Assert.Equal("CS1513", results[0].Diagnostics!.Single().Code); AssertEx.Empty(results[1].Diagnostics); AssertEx.Empty(results[2].Diagnostics); @@ -1383,7 +1387,7 @@ public async Task TestNoChangeIfWorkspaceDiagnosticsCalledTwice(bool useVSDiagno var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); Assert.Equal(3, results.Length); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); + Assert.Equal("CS1513", results[0].Diagnostics!.Single().Code); AssertEx.Empty(results[1].Diagnostics); AssertEx.Empty(results[2].Diagnostics); @@ -1404,7 +1408,7 @@ public async Task TestWorkspaceDiagnosticsRemovedAfterErrorIsFixed(bool useVSDia var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); Assert.Equal(3, results.Length); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); + Assert.Equal("CS1513", results[0].Diagnostics!.Single().Code); AssertEx.Empty(results[1].Diagnostics); AssertEx.Empty(results[2].Diagnostics); @@ -1429,8 +1433,8 @@ public async Task TestWorkspaceDiagnosticsRemainAfterErrorIsNotFixed(bool useVSD var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); Assert.Equal(3, results.Length); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); - Assert.Equal(new Position { Line = 0, Character = 9 }, results[0].Diagnostics.Single().Range.Start); + Assert.Equal("CS1513", results[0].Diagnostics!.Single().Code); + Assert.Equal(new Position { Line = 0, Character = 9 }, results[0].Diagnostics!.Single().Range.Start); AssertEx.Empty(results[1].Diagnostics); AssertEx.Empty(results[2].Diagnostics); @@ -1442,8 +1446,8 @@ public async Task TestWorkspaceDiagnosticsRemainAfterErrorIsNotFixed(bool useVSD var results2 = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - Assert.Equal("CS1513", results2[0].Diagnostics.Single().Code); - Assert.Equal(new Position { Line = 0, Character = 10 }, results2[0].Diagnostics.Single().Range.Start); + Assert.Equal("CS1513", results2[0].Diagnostics!.Single().Code); + Assert.Equal(new Position { Line = 0, Character = 10 }, results2[0].Diagnostics!.Single().Range.Start); AssertEx.Empty(results2[1].Diagnostics); Assert.NotEqual(results[1].ResultId, results2[1].ResultId); @@ -1462,8 +1466,8 @@ public async Task TestStreamingWorkspaceDiagnostics(bool useVSDiagnostics, bool var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); Assert.Equal(3, results.Length); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); - Assert.Equal(new Position { Line = 0, Character = 9 }, results[0].Diagnostics.Single().Range.Start); + Assert.Equal("CS1513", results[0].Diagnostics!.Single().Code); + Assert.Equal(new Position { Line = 0, Character = 9 }, results[0].Diagnostics!.Single().Range.Start); results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, useProgress: true); @@ -1485,8 +1489,8 @@ class A { var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); Assert.Equal(3, results.Length); Assert.Equal(ProtocolConversions.CreateAbsoluteUri(@"C:\test1.cs"), results[0].TextDocument!.Uri); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); - Assert.Equal(1, results[0].Diagnostics.Single().Range.Start.Line); + Assert.Equal("CS1513", results[0].Diagnostics!.Single().Code); + Assert.Equal(1, results[0].Diagnostics!.Single().Range.Start.Line); AssertEx.Empty(results[1].Diagnostics); AssertEx.Empty(results[2].Diagnostics); } @@ -1530,8 +1534,8 @@ public class {|caret:|} { } var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); AssertEx.NotNull(results); Assert.Equal(4, results.Length); - Assert.Equal("CS0246", results[0].Diagnostics.Single().Code); - Assert.Equal("CS1001", results[2].Diagnostics.Single().Code); + Assert.Equal("CS0246", results[0].Diagnostics!.Single().Code); + Assert.Equal("CS1001", results[2].Diagnostics!.Single().Code); // Insert B into B.cs via the workspace. var caretLocation = testLspServer.GetLocations("caret").First().Range; @@ -1612,13 +1616,13 @@ public class {|caret:|} AssertEx.NotNull(results); Assert.Equal(6, results.Length); // Type C does not exist. - Assert.Equal("CS0246", results[0].Diagnostics.Single().Code); + Assert.Equal("CS0246", results[0].Diagnostics!.Single().Code); AssertEx.Empty(results[1].Diagnostics); // Type C does not exist. - Assert.Equal("CS0246", results[2].Diagnostics.Single().Code); + Assert.Equal("CS0246", results[2].Diagnostics!.Single().Code); AssertEx.Empty(results[3].Diagnostics); // Syntax error missing identifier. - Assert.Equal("CS1001", results[4].Diagnostics.Single().Code); + Assert.Equal("CS1001", results[4].Diagnostics!.Single().Code); AssertEx.Empty(results[5].Diagnostics); // Insert C into C.cs via the workspace. @@ -1636,7 +1640,7 @@ public class {|caret:|} Assert.Equal(3, results.Length); // A.cs should report CS0012 indicating that C is not directly referenced. - Assert.Equal("CS0012", results[0].Diagnostics.Single().Code); + Assert.Equal("CS0012", results[0].Diagnostics!.Single().Code); Assert.NotEqual(previousResultIds[0].resultId, results[0].ResultId); // B.cs should no longer have a diagnostic since C exists. @@ -1686,9 +1690,9 @@ public class {|caret:|} { } var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); AssertEx.NotNull(results); Assert.Equal(4, results.Length); - Assert.Equal("CS0246", results[0].Diagnostics.Single().Code); + Assert.Equal("CS0246", results[0].Diagnostics!.Single().Code); AssertEx.Empty(results[1].Diagnostics); - Assert.Equal("CS1001", results[2].Diagnostics.Single().Code); + Assert.Equal("CS1001", results[2].Diagnostics!.Single().Code); AssertEx.Empty(results[3].Diagnostics); // Insert B into B.cs via the workspace. @@ -1755,8 +1759,8 @@ public static void Do() AssertEx.NotNull(results); Assert.Equal(4, results.Length); AssertEx.Empty(results[0].Diagnostics); - Assert.Equal("CS0168", results[2].Diagnostics.Single().Code); - Assert.Equal(LSP.DiagnosticSeverity.Warning, results[2].Diagnostics.Single().Severity); + Assert.Equal("CS0168", results[2].Diagnostics!.Single().Code); + Assert.Equal(LSP.DiagnosticSeverity.Warning, results[2].Diagnostics!.Single().Severity); // Change and reload the project via the workspace. var projectInfo = testLspServer.TestWorkspace.Projects.Where(p => p.AssemblyName == "CSProj2").Single().ToProjectInfo(); @@ -1774,8 +1778,8 @@ public static void Do() // We should get a single report back for B.cs now that the diagnostic has been promoted to an error. // The diagnostics in A.cs did not change and so are not reported again. Assert.Equal(1, results.Length); - Assert.Equal("CS0168", results[0].Diagnostics.Single().Code); - Assert.Equal(LSP.DiagnosticSeverity.Error, results[0].Diagnostics.Single().Severity); + Assert.Equal("CS0168", results[0].Diagnostics!.Single().Code); + Assert.Equal(LSP.DiagnosticSeverity.Error, results[0].Diagnostics!.Single().Severity); } [Theory, CombinatorialData] @@ -1817,8 +1821,8 @@ public class {|caret:|} { } var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); AssertEx.NotNull(results); Assert.Equal(4, results.Length); - Assert.Equal("CS0246", results[0].Diagnostics.Single().Code); - Assert.Equal("CS1001", results[2].Diagnostics.Single().Code); + Assert.Equal("CS0246", results[0].Diagnostics!.Single().Code); + Assert.Equal("CS1001", results[2].Diagnostics!.Single().Code); // Reload the project via the workspace. var projectInfo = testLspServer.TestWorkspace.Projects.Where(p => p.AssemblyName == "CSProj2").Single().ToProjectInfo(); @@ -1873,7 +1877,7 @@ class A : B { } var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); AssertEx.NotNull(results); Assert.Equal(6, results.Length); - Assert.Equal("CS0246", results[0].Diagnostics.Single().Code); + Assert.Equal("CS0246", results[0].Diagnostics!.Single().Code); // Reload the project via the workspace. var projectInfo = testLspServer.TestWorkspace.Projects.Where(p => p.AssemblyName == "CSProj2").Single().ToProjectInfo(); @@ -2043,7 +2047,7 @@ public async Task TestWorkspaceDiagnosticsForClosedFilesSwitchFSAFromOnToOff(boo var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); Assert.Equal(2, results.Length); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); + Assert.Equal("CS1513", results[0].Diagnostics!.Single().Code); AssertEx.Empty(results[1].Diagnostics); var options = testLspServer.TestWorkspace.ExportProvider.GetExportedValue(); @@ -2088,7 +2092,7 @@ public async Task TestWorkspaceDiagnosticsForClosedFilesSwitchFSAFromOffToOn(boo results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: CreateDiagnosticParamsFromPreviousReports(results)); Assert.Equal(2, results.Length); - Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); + Assert.Equal("CS1513", results[0].Diagnostics!.Single().Code); AssertEx.Empty(results[1].Diagnostics); } From 9cfab2d5a5ffccba1fa937402044182778e70ae3 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Tue, 11 Feb 2025 18:12:49 -0800 Subject: [PATCH 225/305] cleanup more warnings --- .../CodeActions/CodeActionsTests.cs | 12 ++++---- .../CodeLens/CSharpCodeLensTests.cs | 29 +++++-------------- .../Commands/ExecuteWorkspaceCommandTests.cs | 2 +- ...ngeConfigurationNotificationHandlerTest.cs | 2 +- .../AbstractPullDiagnosticTestsBase.cs | 3 +- .../AdditionalFileDiagnosticsTests.cs | 12 ++++---- .../Diagnostics/DiagnosticsPullCacheTests.cs | 6 ++-- .../Diagnostics/NonLocalDiagnosticTests.cs | 2 +- .../WorkspaceProjectDiagnosticsTests.cs | 4 +-- .../InlayHint/CSharpInlayHintTests.cs | 4 ++- .../ProtocolUnitTests/MapCode/MapCodeTests.cs | 4 ++- .../LspMetadataAsSourceWorkspaceTests.cs | 2 ++ .../LspMiscellaneousFilesWorkspaceTests.cs | 1 + .../RelatedDocuments/RelatedDocumentsTests.cs | 6 ++-- .../Symbols/WorkspaceSymbolsTests.cs | 2 +- 15 files changed, 42 insertions(+), 49 deletions(-) diff --git a/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs b/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs index dfda6924f09de..e04e6459b4a65 100644 --- a/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs @@ -90,7 +90,7 @@ void M() var results = await RunGetCodeActionsAsync(testLspServer, CreateCodeActionParams(caretLocation)); var topLevelAction = Assert.Single(results, action => action.Title == titlePath[0]); - var introduceConstant = topLevelAction.Children.FirstOrDefault( + var introduceConstant = topLevelAction.Children!.FirstOrDefault( r => JsonSerializer.Deserialize((JsonElement)r.Data!, ProtocolConversions.LspJsonSerializerOptions)!.UniqueIdentifier == titlePath[1]); AssertJsonEquals(expected, introduceConstant); @@ -134,7 +134,7 @@ void M() var results = await RunGetCodeActionsAsync(testLspServer, codeActionParams); var addImport = results.FirstOrDefault(r => r.Title.Contains($"using System.Threading.Tasks")); - Assert.Equal(1, addImport.Diagnostics!.Length); + Assert.Equal(1, addImport!.Diagnostics!.Length); Assert.Equal(AddImportDiagnosticIds.CS0103, addImport.Diagnostics.Single().Code!.Value); } @@ -204,7 +204,7 @@ private void XYZ() var results = await RunGetCodeActionsAsync(testLspServer, codeActionParams); var inline = results.FirstOrDefault(r => r.Title.Contains($"Inline 'A()'")); - var data = GetCodeActionResolveData(inline); + var data = GetCodeActionResolveData(inline!); Assert.NotNull(data); // Asserts that there are NestedActions on Inline @@ -218,7 +218,7 @@ private void XYZ() Assert.Equal("Inline and keep 'A()'", nestedActionData!.CodeActionPath[1]); // Asserts that there is a Command present on an action with nested actions - Assert.NotNull(inline.Command); + Assert.NotNull(inline?.Command); } [Theory, CombinatorialData] @@ -296,7 +296,7 @@ private void XYZ() var results = await RunGetCodeActionsAsync(testLspServer, codeActionParams); // Assert that nested code actions aren't enumerated. var inline = results.FirstOrDefault(r => r.Title.Contains($"Inline 'A()'")); - var resolvedAction = await RunGetCodeActionResolveAsync(testLspServer, inline); + var resolvedAction = await RunGetCodeActionResolveAsync(testLspServer, inline!); Assert.Null(resolvedAction.Edit); } @@ -306,7 +306,7 @@ private static async Task RunGetCodeActionsAsync( { var result = await testLspServer.ExecuteRequestAsync( LSP.Methods.TextDocumentCodeActionName, codeActionParams, CancellationToken.None); - return [.. result.Cast()]; + return [.. result!.Cast()]; } private static async Task RunGetCodeActionResolveAsync( diff --git a/src/LanguageServer/ProtocolUnitTests/CodeLens/CSharpCodeLensTests.cs b/src/LanguageServer/ProtocolUnitTests/CodeLens/CSharpCodeLensTests.cs index 37ff9c71e2789..1a9156b341aeb 100644 --- a/src/LanguageServer/ProtocolUnitTests/CodeLens/CSharpCodeLensTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/CodeLens/CSharpCodeLensTests.cs @@ -386,8 +386,8 @@ void UseM() }; var actualCodeLenses = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentCodeLensName, codeLensParamsDoc1, CancellationToken.None); - var firstCodeLens = actualCodeLenses.First(); - var data = JsonSerializer.Deserialize(firstCodeLens.Data!.ToString(), ProtocolConversions.LspJsonSerializerOptions); + var firstCodeLens = actualCodeLenses!.First(); + var data = JsonSerializer.Deserialize(firstCodeLens.Data!.ToString()!, ProtocolConversions.LspJsonSerializerOptions); AssertEx.NotNull(data); // Update the document so the syntax version changes @@ -412,10 +412,7 @@ void M(A a) await using var testLspServer = await CreateTestLspServerAsync(markup, lspMutatingWorkspace, new InitializationOptions { ClientCapabilities = CapabilitiesWithVSExtensions, - OptionUpdater = (globalOptions) => - { - globalOptions.SetGlobalOption(LspOptionsStorage.LspEnableReferencesCodeLens, LanguageNames.CSharp, false); - } + OptionUpdater = (globalOptions) => globalOptions.SetGlobalOption(LspOptionsStorage.LspEnableReferencesCodeLens, LanguageNames.CSharp, false) }); var actualCodeLenses = await GetCodeLensAsync(testLspServer); AssertEx.Empty(actualCodeLenses); @@ -446,10 +443,7 @@ class A await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, new InitializationOptions { ClientCapabilities = CapabilitiesWithVSExtensions, - OptionUpdater = (globalOptions) => - { - globalOptions.SetGlobalOption(LspOptionsStorage.LspUsingDevkitFeatures, false); - } + OptionUpdater = (globalOptions) => globalOptions.SetGlobalOption(LspOptionsStorage.LspUsingDevkitFeatures, false) }); await VerifyTestCodeLensAsync(testLspServer, FeaturesResources.Run_Test, FeaturesResources.Debug_Test); } @@ -479,10 +473,7 @@ public void M() await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, new InitializationOptions { ClientCapabilities = CapabilitiesWithVSExtensions, - OptionUpdater = (globalOptions) => - { - globalOptions.SetGlobalOption(LspOptionsStorage.LspUsingDevkitFeatures, false); - } + OptionUpdater = (globalOptions) => globalOptions.SetGlobalOption(LspOptionsStorage.LspUsingDevkitFeatures, false) }); await VerifyTestCodeLensAsync(testLspServer, FeaturesResources.Run_All_Tests, FeaturesResources.Debug_All_Tests); } @@ -512,10 +503,7 @@ class A await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, new InitializationOptions { ClientCapabilities = CapabilitiesWithVSExtensions, - OptionUpdater = (globalOptions) => - { - globalOptions.SetGlobalOption(LspOptionsStorage.LspUsingDevkitFeatures, true); - } + OptionUpdater = (globalOptions) => globalOptions.SetGlobalOption(LspOptionsStorage.LspUsingDevkitFeatures, true) }); await VerifyTestCodeLensMissingAsync(testLspServer); } @@ -545,10 +533,7 @@ class A await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, new InitializationOptions { ClientCapabilities = CapabilitiesWithVSExtensions, - OptionUpdater = (globalOptions) => - { - globalOptions.SetGlobalOption(LspOptionsStorage.LspEnableTestsCodeLens, LanguageNames.CSharp, false); - } + OptionUpdater = (globalOptions) => globalOptions.SetGlobalOption(LspOptionsStorage.LspEnableTestsCodeLens, LanguageNames.CSharp, false) }); await VerifyTestCodeLensMissingAsync(testLspServer); } diff --git a/src/LanguageServer/ProtocolUnitTests/Commands/ExecuteWorkspaceCommandTests.cs b/src/LanguageServer/ProtocolUnitTests/Commands/ExecuteWorkspaceCommandTests.cs index 7b9c222145ab4..5e8094d8274c8 100644 --- a/src/LanguageServer/ProtocolUnitTests/Commands/ExecuteWorkspaceCommandTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Commands/ExecuteWorkspaceCommandTests.cs @@ -63,7 +63,7 @@ public TestWorkspaceCommandHandler() public override TextDocumentIdentifier GetTextDocumentIdentifier(ExecuteCommandParams request) { - return JsonSerializer.Deserialize((JsonElement)request.Arguments.First(), ProtocolConversions.LspJsonSerializerOptions)!; + return JsonSerializer.Deserialize((JsonElement)request.Arguments!.First(), ProtocolConversions.LspJsonSerializerOptions)!; } public override Task HandleRequestAsync(ExecuteCommandParams request, RequestContext context, CancellationToken cancellationToken) diff --git a/src/LanguageServer/ProtocolUnitTests/Configuration/DidChangeConfigurationNotificationHandlerTest.cs b/src/LanguageServer/ProtocolUnitTests/Configuration/DidChangeConfigurationNotificationHandlerTest.cs index cf7ac74550ea5..04875a010bc6f 100644 --- a/src/LanguageServer/ProtocolUnitTests/Configuration/DidChangeConfigurationNotificationHandlerTest.cs +++ b/src/LanguageServer/ProtocolUnitTests/Configuration/DidChangeConfigurationNotificationHandlerTest.cs @@ -244,7 +244,7 @@ private static string ConvertToString(object? value) => value switch { null => "null", - _ => value.ToString() + _ => value.ToString()! }; private static string GenerateNonDefaultValue(IOption2 option) diff --git a/src/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs b/src/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs index 094dec54538ad..d13000fa95ea2 100644 --- a/src/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs +++ b/src/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs @@ -279,7 +279,8 @@ private protected static async Task> RunGet if (useProgress) { Assert.Null(diagnostics); - diagnostics = progress!.Value.GetValues().Single().First; + AssertEx.NotNull(progress); + diagnostics = progress.Value.GetValues()!.Single().First; } if (diagnostics == null) diff --git a/src/LanguageServer/ProtocolUnitTests/Diagnostics/AdditionalFileDiagnosticsTests.cs b/src/LanguageServer/ProtocolUnitTests/Diagnostics/AdditionalFileDiagnosticsTests.cs index 140598171eaa0..1fcbd5de786d4 100644 --- a/src/LanguageServer/ProtocolUnitTests/Diagnostics/AdditionalFileDiagnosticsTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Diagnostics/AdditionalFileDiagnosticsTests.cs @@ -40,7 +40,7 @@ public async Task TestWorkspaceDiagnosticsReportsAdditionalFileDiagnostic(bool u @"C:\C.cs: []", @$"C:\Test.txt: [{MockAdditionalFileDiagnosticAnalyzer.Id}]", @"C:\CSProj1.csproj: []" - ], results.Select(r => $"{r.Uri.LocalPath}: [{string.Join(", ", r.Diagnostics.Select(d => d.Code?.Value?.ToString()))}]")); + ], results.Select(r => $"{r.Uri.LocalPath}: [{string.Join(", ", r.Diagnostics!.Select(d => d.Code?.Value?.ToString()))}]")); // Asking again should give us back an unchanged diagnostic. var results2 = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, previousResults: CreateDiagnosticParamsFromPreviousReports(results)); @@ -64,7 +64,7 @@ public async Task TestWorkspaceDiagnosticsWithRemovedAdditionalFile(bool useVSDi Assert.Equal(3, results.Length); AssertEx.Empty(results[0].Diagnostics); - Assert.Equal(MockAdditionalFileDiagnosticAnalyzer.Id, results[1].Diagnostics.Single().Code); + Assert.Equal(MockAdditionalFileDiagnosticAnalyzer.Id, results[1].Diagnostics!.Single().Code); Assert.Equal(@"C:\Test.txt", results[1].Uri.LocalPath); AssertEx.Empty(results[2].Diagnostics); @@ -100,12 +100,12 @@ public async Task TestWorkspaceDiagnosticsWithAdditionalFileInMultipleProjects(b var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics: true); Assert.Equal(6, results.Length); - Assert.Equal(MockAdditionalFileDiagnosticAnalyzer.Id, results[1].Diagnostics.Single().Code); + Assert.Equal(MockAdditionalFileDiagnosticAnalyzer.Id, results[1].Diagnostics!.Single().Code); Assert.Equal(@"C:\Test.txt", results[1].Uri.LocalPath); - Assert.Equal("CSProj1", ((LSP.VSDiagnostic)results[1].Diagnostics.Single()).Projects.First().ProjectName); - Assert.Equal(MockAdditionalFileDiagnosticAnalyzer.Id, results[4].Diagnostics.Single().Code); + Assert.Equal("CSProj1", ((LSP.VSDiagnostic)results[1].Diagnostics!.Single()).Projects!.First().ProjectName); + Assert.Equal(MockAdditionalFileDiagnosticAnalyzer.Id, results[4].Diagnostics!.Single().Code); Assert.Equal(@"C:\Test.txt", results[4].Uri.LocalPath); - Assert.Equal("CSProj2", ((LSP.VSDiagnostic)results[4].Diagnostics.Single()).Projects.First().ProjectName); + Assert.Equal("CSProj2", ((LSP.VSDiagnostic)results[4].Diagnostics!.Single()).Projects!.First().ProjectName); // Asking again should give us back an unchanged diagnostic. var results2 = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics: true, previousResults: CreateDiagnosticParamsFromPreviousReports(results)); diff --git a/src/LanguageServer/ProtocolUnitTests/Diagnostics/DiagnosticsPullCacheTests.cs b/src/LanguageServer/ProtocolUnitTests/Diagnostics/DiagnosticsPullCacheTests.cs index 0fc045014346d..4d83a1ccaa0a9 100644 --- a/src/LanguageServer/ProtocolUnitTests/Diagnostics/DiagnosticsPullCacheTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Diagnostics/DiagnosticsPullCacheTests.cs @@ -35,7 +35,7 @@ public async Task TestDocumentDiagnosticsCallsDiagnosticSourceWhenVersionChanges await OpenDocumentAsync(testLspServer, document); var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics); - Assert.Equal(TestDiagnosticSource.Id, results[0].Diagnostics.Single().Code); + Assert.Equal(TestDiagnosticSource.Id, results[0].Diagnostics!.Single().Code); Assert.Equal(1, testProvider.DiagnosticsRequestedCount); // Make a change that modifies the versions we use to cache. @@ -65,7 +65,7 @@ public async Task TestDocumentDiagnosticsCallsDiagnosticSourceWhenGlobalVersionC await OpenDocumentAsync(testLspServer, document); var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics); - Assert.Equal(TestDiagnosticSource.Id, results[0].Diagnostics.Single().Code); + Assert.Equal(TestDiagnosticSource.Id, results[0].Diagnostics!.Single().Code); Assert.Equal(1, testProvider.DiagnosticsRequestedCount); // Make a global version change @@ -96,7 +96,7 @@ public async Task TestDocumentDiagnosticsDoesNotCallDiagnosticSourceWhenVersionS await OpenDocumentAsync(testLspServer, document); var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics); - Assert.Equal(TestDiagnosticSource.Id, results[0].Diagnostics.Single().Code); + Assert.Equal(TestDiagnosticSource.Id, results[0].Diagnostics!.Single().Code); Assert.Equal(1, testProvider.DiagnosticsRequestedCount); // Make another request without modifying anything and assert we did not re-calculate anything. diff --git a/src/LanguageServer/ProtocolUnitTests/Diagnostics/NonLocalDiagnosticTests.cs b/src/LanguageServer/ProtocolUnitTests/Diagnostics/NonLocalDiagnosticTests.cs index c1379550ec811..a728c2f2a568e 100644 --- a/src/LanguageServer/ProtocolUnitTests/Diagnostics/NonLocalDiagnosticTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Diagnostics/NonLocalDiagnosticTests.cs @@ -45,7 +45,7 @@ internal async Task TestNonLocalDocumentDiagnosticsAreReportedWhenFSAEnabled(boo { Assert.Equal(1, results.Length); Assert.Equal(2, results[0].Diagnostics?.Length); - var orderedDiagnostics = results[0].Diagnostics.OrderBy(d => d.Code!.Value.Value).ToList(); + var orderedDiagnostics = results[0].Diagnostics!.OrderBy(d => d.Code!.Value.Value).ToList(); Assert.Equal(NonLocalDiagnosticsAnalyzer.NonLocalDescriptor.Id, orderedDiagnostics[0].Code); Assert.Equal(NonLocalDiagnosticsAnalyzer.CompilationEndDescriptor.Id, orderedDiagnostics[1].Code); Assert.Equal(document.GetURI(), results[0].Uri); diff --git a/src/LanguageServer/ProtocolUnitTests/Diagnostics/WorkspaceProjectDiagnosticsTests.cs b/src/LanguageServer/ProtocolUnitTests/Diagnostics/WorkspaceProjectDiagnosticsTests.cs index ce607c6a9fc25..93cf0d63cc83f 100644 --- a/src/LanguageServer/ProtocolUnitTests/Diagnostics/WorkspaceProjectDiagnosticsTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Diagnostics/WorkspaceProjectDiagnosticsTests.cs @@ -29,7 +29,7 @@ public async Task TestWorkspaceDiagnosticsReportsProjectDiagnostic(bool useVSDia Assert.Equal(2, results.Length); AssertEx.Empty(results[0].Diagnostics); - Assert.Equal(MockProjectDiagnosticAnalyzer.Id, results[1].Diagnostics.Single().Code); + Assert.Equal(MockProjectDiagnosticAnalyzer.Id, results[1].Diagnostics!.Single().Code); Assert.Equal(ProtocolConversions.CreateAbsoluteUri(testLspServer.GetCurrentSolution().Projects.First().FilePath!), results[1].Uri); // Asking again should give us back an unchanged diagnostic. @@ -46,7 +46,7 @@ public async Task TestWorkspaceDiagnosticsWithRemovedProject(bool useVSDiagnosti Assert.Equal(2, results.Length); AssertEx.Empty(results[0].Diagnostics); - Assert.Equal(MockProjectDiagnosticAnalyzer.Id, results[1].Diagnostics.Single().Code); + Assert.Equal(MockProjectDiagnosticAnalyzer.Id, results[1].Diagnostics!.Single().Code); Assert.Equal(ProtocolConversions.CreateAbsoluteUri(testLspServer.GetCurrentSolution().Projects.First().FilePath!), results[1].Uri); var initialSolution = testLspServer.GetCurrentSolution(); diff --git a/src/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs b/src/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs index 5968d8f97ca07..5963b91d09b83 100644 --- a/src/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs @@ -130,8 +130,9 @@ void M() }; var actualInlayHints = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentInlayHintName, inlayHintParams, CancellationToken.None); + AssertEx.NotNull(actualInlayHints); var firstInlayHint = actualInlayHints.First(); - var data = JsonSerializer.Deserialize(firstInlayHint.Data!.ToString(), ProtocolConversions.LspJsonSerializerOptions); + var data = JsonSerializer.Deserialize(firstInlayHint.Data!.ToString()!, ProtocolConversions.LspJsonSerializerOptions); AssertEx.NotNull(data); var firstResultId = data.ResultId; @@ -143,6 +144,7 @@ void M() await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentInlayHintName, inlayHintParams, CancellationToken.None); await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentInlayHintName, inlayHintParams, CancellationToken.None); var lastInlayHints = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentInlayHintName, inlayHintParams, CancellationToken.None); + AssertEx.NotNull(lastInlayHints); Assert.True(lastInlayHints.Any()); // Assert that the first result id is no longer in the cache. diff --git a/src/LanguageServer/ProtocolUnitTests/MapCode/MapCodeTests.cs b/src/LanguageServer/ProtocolUnitTests/MapCode/MapCodeTests.cs index 0aaf597dec428..2947fd4a7dcf1 100644 --- a/src/LanguageServer/ProtocolUnitTests/MapCode/MapCodeTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/MapCode/MapCodeTests.cs @@ -112,7 +112,7 @@ static void Main(string[] args) var results = await testLspServer.ExecuteRequestAsync(VSInternalMethods.WorkspaceMapCodeName, mapCodeParams, CancellationToken.None); AssertEx.NotNull(results); - TextEdit[] edits; + TextEdit[]? edits; if (supportDocumentChanges) { Assert.Null(results.Changes); @@ -131,6 +131,8 @@ static void Main(string[] args) Assert.True(results.Changes!.TryGetValue(ProtocolConversions.GetDocumentFilePathFromUri(documentUri), out edits)); } + AssertEx.NotNull(edits); + var documentText = await document.GetTextAsync(); var actualText = ApplyTextEdits(edits, documentText); Assert.Equal(expected, actualText); diff --git a/src/LanguageServer/ProtocolUnitTests/Metadata/LspMetadataAsSourceWorkspaceTests.cs b/src/LanguageServer/ProtocolUnitTests/Metadata/LspMetadataAsSourceWorkspaceTests.cs index 36e37923ab780..733d6c9306bd8 100644 --- a/src/LanguageServer/ProtocolUnitTests/Metadata/LspMetadataAsSourceWorkspaceTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Metadata/LspMetadataAsSourceWorkspaceTests.cs @@ -43,6 +43,7 @@ void M() var location = testLspServer.GetLocations("definition").Single(); var definition = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentDefinitionName, CreateTextDocumentPositionParams(location), CancellationToken.None); + AssertEx.NotNull(definition); // Open the metadata file and verify it gets added to the metadata workspace. await testLspServer.OpenDocumentAsync(definition.Single().Uri, text: string.Empty).ConfigureAwait(false); @@ -89,6 +90,7 @@ public static void WriteLine(string value) {} var location = testLspServer.GetLocations("definition").Single(); var definition = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentDefinitionName, CreateTextDocumentPositionParams(location), CancellationToken.None); + AssertEx.NotNull(definition); // Open the metadata file and verify it gets added to the metadata workspace. // We don't have the real metadata source, so just populate it with our fake metadata source. diff --git a/src/LanguageServer/ProtocolUnitTests/Miscellaneous/LspMiscellaneousFilesWorkspaceTests.cs b/src/LanguageServer/ProtocolUnitTests/Miscellaneous/LspMiscellaneousFilesWorkspaceTests.cs index be2a76a84c976..5299ad3bec399 100644 --- a/src/LanguageServer/ProtocolUnitTests/Miscellaneous/LspMiscellaneousFilesWorkspaceTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Miscellaneous/LspMiscellaneousFilesWorkspaceTests.cs @@ -286,6 +286,7 @@ void M() Assert.Equal(LanguageNames.CSharp, miscDoc.Project.Language); // Verify GTD request succeeded. + AssertEx.NotNull(result); Assert.Equal(0, result.Single().Range.Start.Line); Assert.Equal(6, result.Single().Range.Start.Character); Assert.Equal(0, result.Single().Range.End.Line); diff --git a/src/LanguageServer/ProtocolUnitTests/RelatedDocuments/RelatedDocumentsTests.cs b/src/LanguageServer/ProtocolUnitTests/RelatedDocuments/RelatedDocumentsTests.cs index 979a37c00517c..d4965c90a9321 100644 --- a/src/LanguageServer/ProtocolUnitTests/RelatedDocuments/RelatedDocumentsTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/RelatedDocuments/RelatedDocumentsTests.cs @@ -95,7 +95,7 @@ class Y useProgress: useProgress); Assert.Equal(1, results.Length); - Assert.Equal(project.Documents.Last().FilePath, results[0].FilePaths.Single()); + Assert.Equal(project.Documents.Last().FilePath, results[0].FilePaths!.Single()); } [Theory, CombinatorialData] @@ -129,8 +129,8 @@ class Z project.Documents.First().GetURI(), useProgress: useProgress); - Assert.Equal(2, results.SelectMany(r => r.FilePaths).Count()); - AssertEx.SetEqual([.. project.Documents.Skip(1).Select(d => d.FilePath)], results.SelectMany(r => r.FilePaths)); + Assert.Equal(2, results.SelectMany(r => r.FilePaths!).Count()); + AssertEx.SetEqual([.. project.Documents.Skip(1).Select(d => d.FilePath)], results.SelectMany(r => r.FilePaths!)); } [Theory, CombinatorialData] diff --git a/src/LanguageServer/ProtocolUnitTests/Symbols/WorkspaceSymbolsTests.cs b/src/LanguageServer/ProtocolUnitTests/Symbols/WorkspaceSymbolsTests.cs index 3a90b5d341ab6..213721f2f4ea1 100644 --- a/src/LanguageServer/ProtocolUnitTests/Symbols/WorkspaceSymbolsTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Symbols/WorkspaceSymbolsTests.cs @@ -21,7 +21,7 @@ public class WorkspaceSymbolsTests(ITestOutputHelper testOutputHelper) : AbstractLanguageServerProtocolTests(testOutputHelper) { private static void AssertSetEquals(LSP.SymbolInformation[] expected, LSP.SymbolInformation[]? results) - => Assert.True(expected.ToHashSet().SetEquals(results)); + => Assert.True(expected.ToHashSet().SetEquals(results!)); private Task CreateTestLspServerAsync(string markup, bool mutatingLspWorkspace) => CreateTestLspServerAsync(markup, mutatingLspWorkspace, composition: Composition.AddParts(typeof(TestWorkspaceNavigateToSearchHostService))); From fecfec94a2b0299dd50f4b0c076dcd69fd756654 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Wed, 12 Feb 2025 05:01:48 +0000 Subject: [PATCH 226/305] Update dependencies from https://github.com/dotnet/arcade build 20250211.5 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.XliffTasks From Version 9.0.0-beta.25077.4 -> To Version 9.0.0-beta.25111.5 --- eng/Version.Details.xml | 16 ++++++++-------- global.json | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 0e453f61917e1..cfde01e9f251b 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -122,19 +122,19 @@ - + https://github.com/dotnet/arcade - bac7e1caea791275b7c3ccb4cb75fd6a04a26618 + 5da211e1c42254cb35e7ef3d5a8428fb24853169 - + https://github.com/dotnet/arcade - bac7e1caea791275b7c3ccb4cb75fd6a04a26618 + 5da211e1c42254cb35e7ef3d5a8428fb24853169 - + https://github.com/dotnet/arcade - bac7e1caea791275b7c3ccb4cb75fd6a04a26618 + 5da211e1c42254cb35e7ef3d5a8428fb24853169 https://github.com/dotnet/symreader @@ -150,9 +150,9 @@ https://github.com/dotnet/roslyn 5d10d428050c0d6afef30a072c4ae68776621877 - + https://github.com/dotnet/arcade - bac7e1caea791275b7c3ccb4cb75fd6a04a26618 + 5da211e1c42254cb35e7ef3d5a8428fb24853169 https://github.com/dotnet/roslyn-analyzers diff --git a/global.json b/global.json index b8003a71e69c1..ef8ab9a2d7469 100644 --- a/global.json +++ b/global.json @@ -1,18 +1,18 @@ { "sdk": { - "version": "9.0.102", + "version": "9.0.103", "allowPrerelease": false, "rollForward": "patch" }, "tools": { - "dotnet": "9.0.102", + "dotnet": "9.0.103", "vs": { "version": "17.8.0" } }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.25077.4", - "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.25077.4", + "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.25111.5", + "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.25111.5", "Microsoft.Build.Traversal": "3.4.0" } } From c8a2a0df4245902ff86a408fbc1fc7f2136f6139 Mon Sep 17 00:00:00 2001 From: "Nikola Milosavljevic (CLR) false" Date: Tue, 11 Feb 2025 23:44:46 -0800 Subject: [PATCH 227/305] Enable tests in VMR --- eng/Directory.Packages.props | 4 ++-- eng/Versions.props | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/eng/Directory.Packages.props b/eng/Directory.Packages.props index e28bb02fdaa1c..2d5be90f1cced 100644 --- a/eng/Directory.Packages.props +++ b/eng/Directory.Packages.props @@ -142,8 +142,8 @@ --> - - + + true + + + 17.5.0 + From 3f2e5e2ba7145ae675e008e82069168c0496cc66 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Wed, 12 Feb 2025 10:33:30 +0100 Subject: [PATCH 228/305] Add analyzer redirecting API (#74820) * Add IAnalyzerAssemblyResolver public API * Load analyzers directly in ServiceHub * Handle missing assembly on disk * Redirect path only for serialization * Redirect full path on creation * Add separate redirector API * Remove superfluous methods * Report caught exceptions * Make the API RestrictedInternalsVisibleTo * Remove unnecessary resolver modification * Fixup after merge * Remove redirecting from AnalyzerFileReference * Redirect analyzers in ProjectSystemProject * Expand doc comment * Improve code --- .../AnalyzerFileReference.cs | 3 ++ .../Core/Portable/PublicAPI.Unshipped.txt | 1 + .../LanguageServerWorkspaceFactory.cs | 3 +- .../VisualStudioProjectFactory.cs | 6 ++- .../AnalyzerReferenceTests.vb | 44 +++++++++++++++++-- .../Microsoft.CodeAnalysis.Workspaces.csproj | 1 + .../IAnalyzerAssemblyRedirector.cs | 33 ++++++++++++++ .../ProjectSystem/ProjectSystemProject.cs | 35 +++++++++++++++ .../ProjectSystemProjectHostInfo.cs | 4 +- 9 files changed, 124 insertions(+), 6 deletions(-) create mode 100644 src/Workspaces/Core/Portable/Workspace/ProjectSystem/IAnalyzerAssemblyRedirector.cs diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerFileReference.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerFileReference.cs index fbbd8f4553645..2f1eafe06b16d 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerFileReference.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerFileReference.cs @@ -106,6 +106,9 @@ public bool Equals(AnalyzerReference? other) public override int GetHashCode() => Hash.Combine(RuntimeHelpers.GetHashCode(_assemblyLoader), FullPath.GetHashCode()); + public override string ToString() + => $"{nameof(AnalyzerFileReference)}({nameof(FullPath)} = {FullPath})"; + public override ImmutableArray GetAnalyzersForAllLanguages() { // This API returns duplicates of analyzers that support multiple languages. diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 5ed8ac837d3b4..da1af91a7e677 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -18,6 +18,7 @@ Microsoft.CodeAnalysis.IPropertySymbol.PartialImplementationPart.get -> Microsof Microsoft.CodeAnalysis.IPropertySymbol.IsPartialDefinition.get -> bool Microsoft.CodeAnalysis.ITypeParameterSymbol.AllowsRefLikeType.get -> bool Microsoft.CodeAnalysis.RuntimeCapability.ByRefLikeGenerics = 8 -> Microsoft.CodeAnalysis.RuntimeCapability +override Microsoft.CodeAnalysis.Diagnostics.AnalyzerFileReference.ToString() -> string! static Microsoft.CodeAnalysis.GeneratorExtensions.AsIncrementalGenerator(this Microsoft.CodeAnalysis.ISourceGenerator! sourceGenerator) -> Microsoft.CodeAnalysis.IIncrementalGenerator! static Microsoft.CodeAnalysis.GeneratorExtensions.GetGeneratorType(this Microsoft.CodeAnalysis.IIncrementalGenerator! generator) -> System.Type! Microsoft.CodeAnalysis.Compilation.CreatePreprocessingSymbol(string! name) -> Microsoft.CodeAnalysis.IPreprocessingSymbol! diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerWorkspaceFactory.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerWorkspaceFactory.cs index 9822e7020a5ab..f62d05679f3a6 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerWorkspaceFactory.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerWorkspaceFactory.cs @@ -43,7 +43,8 @@ public LanguageServerWorkspaceFactory( var razorSourceGenerator = serverConfigurationFactory?.ServerConfiguration?.RazorSourceGenerator; ProjectSystemHostInfo = new ProjectSystemHostInfo( DynamicFileInfoProviders: [.. dynamicFileInfoProviders], - new HostDiagnosticAnalyzerProvider(razorSourceGenerator)); + new HostDiagnosticAnalyzerProvider(razorSourceGenerator), + AnalyzerAssemblyRedirectors: []); TargetFrameworkManager = projectTargetFrameworkManager; } diff --git a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProjectFactory.cs b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProjectFactory.cs index 620d493be4cef..92da315daac29 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProjectFactory.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProjectFactory.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Workspaces.AnalyzerRedirecting; using Microsoft.CodeAnalysis.Workspaces.ProjectSystem; using Microsoft.VisualStudio.LanguageServices.ExternalAccess.VSTypeScript.Api; using Microsoft.VisualStudio.LanguageServices.Implementation.Diagnostics; @@ -33,6 +34,7 @@ internal sealed class VisualStudioProjectFactory : IVsTypeScriptVisualStudioProj private readonly VisualStudioWorkspaceImpl _visualStudioWorkspaceImpl; private readonly ImmutableArray> _dynamicFileInfoProviders; private readonly IVisualStudioDiagnosticAnalyzerProviderFactory _vsixAnalyzerProviderFactory; + private readonly ImmutableArray _analyzerAssemblyRedirectors; private readonly IVsService _solution2; [ImportingConstructor] @@ -42,12 +44,14 @@ public VisualStudioProjectFactory( VisualStudioWorkspaceImpl visualStudioWorkspaceImpl, [ImportMany] IEnumerable> fileInfoProviders, IVisualStudioDiagnosticAnalyzerProviderFactory vsixAnalyzerProviderFactory, + [ImportMany] IEnumerable analyzerAssemblyRedirectors, IVsService solution2) { _threadingContext = threadingContext; _visualStudioWorkspaceImpl = visualStudioWorkspaceImpl; _dynamicFileInfoProviders = fileInfoProviders.AsImmutableOrEmpty(); _vsixAnalyzerProviderFactory = vsixAnalyzerProviderFactory; + _analyzerAssemblyRedirectors = analyzerAssemblyRedirectors.AsImmutableOrEmpty(); _solution2 = solution2; } @@ -93,7 +97,7 @@ public async Task CreateAndAddToWorkspaceAsync( _visualStudioWorkspaceImpl.ProjectSystemProjectFactory.SolutionPath = solutionFilePath; _visualStudioWorkspaceImpl.ProjectSystemProjectFactory.SolutionTelemetryId = GetSolutionSessionId(); - var hostInfo = new ProjectSystemHostInfo(_dynamicFileInfoProviders, vsixAnalyzerProvider); + var hostInfo = new ProjectSystemHostInfo(_dynamicFileInfoProviders, vsixAnalyzerProvider, _analyzerAssemblyRedirectors); var project = await _visualStudioWorkspaceImpl.ProjectSystemProjectFactory.CreateAndAddToWorkspaceAsync(projectSystemName, language, creationInfo, hostInfo); _visualStudioWorkspaceImpl.AddProjectToInternalMaps(project, creationInfo.Hierarchy, creationInfo.ProjectGuid, projectSystemName); diff --git a/src/VisualStudio/Core/Test/ProjectSystemShim/VisualStudioProjectTests/AnalyzerReferenceTests.vb b/src/VisualStudio/Core/Test/ProjectSystemShim/VisualStudioProjectTests/AnalyzerReferenceTests.vb index 6d9f10df1256e..c0d0b85ee28c5 100644 --- a/src/VisualStudio/Core/Test/ProjectSystemShim/VisualStudioProjectTests/AnalyzerReferenceTests.vb +++ b/src/VisualStudio/Core/Test/ProjectSystemShim/VisualStudioProjectTests/AnalyzerReferenceTests.vb @@ -2,13 +2,13 @@ ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. -Imports System.Collections.Immutable +Imports System.ComponentModel.Composition Imports System.IO Imports System.Threading Imports Microsoft.CodeAnalysis -Imports Microsoft.CodeAnalysis.Diagnostics -Imports Microsoft.CodeAnalysis.[Shared].TestHooks +Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.Test.Utilities +Imports Microsoft.CodeAnalysis.Workspaces.AnalyzerRedirecting Imports Microsoft.VisualStudio.LanguageServices.Implementation.Diagnostics Imports Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Imports Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Framework @@ -285,5 +285,43 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim Assert.False(project.HasSdkCodeStyleAnalyzers) End Using End Function + + + Public Async Function RedirectedAnalyzers_CSharp() As Task + Using environment = New TestEnvironment(GetType(Redirector)) + Dim project = Await environment.ProjectFactory.CreateAndAddToWorkspaceAsync( + "Project", LanguageNames.CSharp, CancellationToken.None) + + ' Add analyzers + project.AddAnalyzerReference(Path.Combine(TempRoot.Root, "Sdks", "Microsoft.NET.Sdk", "analyzers", "Microsoft.CodeAnalysis.NetAnalyzers.dll")) + project.AddAnalyzerReference(Path.Combine(TempRoot.Root, "Sdks", "Microsoft.NET.Sdk", "analyzers", "Microsoft.CodeAnalysis.CSharp.NetAnalyzers.dll")) + project.AddAnalyzerReference(Path.Combine(TempRoot.Root, "Dir", "File.dll")) + + ' Ensure the SDK ones are redirected + AssertEx.Equal( + { + Path.Combine(TempRoot.Root, "Sdks", "Microsoft.NET.Sdk", "analyzers", "Microsoft.CodeAnalysis.NetAnalyzers.redirected.dll"), + Path.Combine(TempRoot.Root, "Sdks", "Microsoft.NET.Sdk", "analyzers", "Microsoft.CodeAnalysis.CSharp.NetAnalyzers.redirected.dll"), + Path.Combine(TempRoot.Root, "Dir", "File.dll") + }, environment.Workspace.CurrentSolution.Projects.Single().AnalyzerReferences.Select(Function(r) r.FullPath)) + End Using + End Function + + + Private Class Redirector + Implements IAnalyzerAssemblyRedirector + + + Public Sub New() + End Sub + + Public Function RedirectPath(fullPath As String) As String Implements IAnalyzerAssemblyRedirector.RedirectPath + If fullPath.Contains("Microsoft.NET.Sdk") Then + Return Path.ChangeExtension(fullPath, ".redirected.dll") + End If + + Return Nothing + End Function + End Class End Class End Namespace diff --git a/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj index 7b02585ba1d32..4196dfa0a75af 100644 --- a/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj @@ -146,6 +146,7 @@ + diff --git a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/IAnalyzerAssemblyRedirector.cs b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/IAnalyzerAssemblyRedirector.cs new file mode 100644 index 0000000000000..c1881e5ccc073 --- /dev/null +++ b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/IAnalyzerAssemblyRedirector.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.Workspaces.AnalyzerRedirecting; + +/// +/// Any MEF component implementing this interface will be used to redirect analyzer assemblies. +/// +/// +/// The redirected path is passed to the compiler where it is processed in the standard way, +/// e.g., the redirected assembly is shadow copied before it's loaded +/// (this could be improved in the future since shadow copying redirected assemblies is usually unnecessary). +/// +internal interface IAnalyzerAssemblyRedirector +{ + /// + /// Original full path of the analyzer assembly. + /// + /// + /// The redirected full path of the analyzer assembly + /// or if this instance cannot redirect the given assembly. + /// + /// + /// + /// If two redirectors return different paths for the same assembly, no redirection will be performed. + /// + /// + /// No thread switching inside this method is allowed. + /// + /// + string? RedirectPath(string fullPath); +} diff --git a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProject.cs b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProject.cs index 11133139e4f0b..fdf1663a2a0b5 100644 --- a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProject.cs +++ b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProject.cs @@ -14,6 +14,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.PooledObjects; @@ -1127,9 +1128,43 @@ private OneOrMany GetMappedAnalyzerPaths(string fullPath) return GetMappedRazorSourceGenerator(fullPath); } + if (TryRedirectAnalyzerAssembly(fullPath) is { } redirectedPath) + { + return OneOrMany.Create(redirectedPath); + } + return OneOrMany.Create(fullPath); } + private string? TryRedirectAnalyzerAssembly(string fullPath) + { + string? redirectedPath = null; + + foreach (var redirector in _hostInfo.AnalyzerAssemblyRedirectors) + { + try + { + if (redirector.RedirectPath(fullPath) is { } currentlyRedirectedPath) + { + if (redirectedPath == null) + { + redirectedPath = currentlyRedirectedPath; + } + else if (redirectedPath != currentlyRedirectedPath) + { + throw new InvalidOperationException($"Multiple redirectors disagree on the path to redirect '{fullPath}' to ('{redirectedPath}' vs '{currentlyRedirectedPath}')."); + } + } + } + catch (Exception ex) when (FatalError.ReportAndCatch(ex, ErrorSeverity.General)) + { + // Ignore if the external redirector throws. + } + } + + return redirectedPath; + } + private static readonly string s_csharpCodeStyleAnalyzerSdkDirectory = CreateDirectoryPathFragment("Sdks", "Microsoft.NET.Sdk", "codestyle", "cs"); private static readonly string s_visualBasicCodeStyleAnalyzerSdkDirectory = CreateDirectoryPathFragment("Sdks", "Microsoft.NET.Sdk", "codestyle", "vb"); diff --git a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectHostInfo.cs b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectHostInfo.cs index fc3f0a58ef18a..5bb5f96513091 100644 --- a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectHostInfo.cs +++ b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectHostInfo.cs @@ -6,9 +6,11 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Workspaces.AnalyzerRedirecting; namespace Microsoft.CodeAnalysis.Workspaces.ProjectSystem; internal record ProjectSystemHostInfo( ImmutableArray> DynamicFileInfoProviders, - IHostDiagnosticAnalyzerProvider HostDiagnosticAnalyzerProvider); + IHostDiagnosticAnalyzerProvider HostDiagnosticAnalyzerProvider, + ImmutableArray AnalyzerAssemblyRedirectors); From f252374fd5b6fb2c1e7cd223da9c4f28562160d8 Mon Sep 17 00:00:00 2001 From: "Nikola Milosavljevic (CLR) false" Date: Wed, 12 Feb 2025 07:50:54 -0800 Subject: [PATCH 229/305] Remove the unused property --- eng/Directory.Packages.props | 1 - 1 file changed, 1 deletion(-) diff --git a/eng/Directory.Packages.props b/eng/Directory.Packages.props index 2d5be90f1cced..19220d0303328 100644 --- a/eng/Directory.Packages.props +++ b/eng/Directory.Packages.props @@ -12,7 +12,6 @@ 9.0.0-rc.2.24462.10 6.0.0-rtm.21518.12 7.0.0-alpha.1.22060.1 - <_MicrosoftTestPlatformVersion>17.5.0