diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md index 470853cd3c8f8..cbf8984cc876c 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md @@ -35,7 +35,7 @@ public class C ***Introduced in Visual Studio 2022 version 17.10*** *Conversion* of a collection expression to a `struct` or `class` that implements `System.Collections.IEnumerable` and *does not* have a strongly-typed `GetEnumerator()` -requires the elements in the collection expression are implicitly convertible to the `object`. +requires that the elements in the collection expression are implicitly convertible to `object`. Previously, the elements of a collection expression targeting an `IEnumerable` implementation were assumed to be convertible to `object`, and converted only when binding to the applicable `Add` method. This additional requirement means that collection expression conversions to `IEnumerable` implementations are treated consistently with other target types where the elements in the collection expression must be implicitly convertible to the *iteration type* of the target type. diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md index ce0ac3ee11fe5..3ebc24cd2fe0a 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md @@ -157,3 +157,30 @@ class C ``` A workaround is to use explicit delegate types instead of relying on `var` inference in those cases. + +## `dotnet_style_require_accessibility_modifiers` now consistently applies to interface members + +PR: https://github.com/dotnet/roslyn/pull/76324 + +Prior to this change, the analyzer for dotnet_style_require_accessibility_modifiers would simply ignore interface +members. This was because C# initially disallowed modifiers for interface members entirely, having them always +be public. + +Later versions of the language relaxed this restriction, allowing users to provide accessibility modifiers on +interface members, including a redundant `public` modifier. + +The analyzer was updated to now enforce the value for this option on interface members as well. The meaning +of the value is as follows: + +1. `never`. The analyzer does no analysis. Redundant modifiers are allowed on all members. +2. `always`. Redundant modifiers are always required on all members (including interface members). For example: + a `private` modifier on a class member, and a `public` modifier on an interface member. This is the option to + use if you feel that all members no matter what should state their accessibility explicitly. +4. `for_non_interface_members`. Redundant modifiers are required on all members *that are not* part of an interface, + but disallowed for interface members. For example: `private` will be required on private class members. However, + a public interface member will not be allowed to have redundant `public` modifiers. This matches the standard + modifier approach present prior to the language allowing modifiers on interface members. +5. `omit_if_default`. Redundant modifiers are disallowed. For example a private class member will be disallowed from + using `private`, and a public interface member will be disallowed from using `public`. This is the option to use + if you feel that restating the accessibility when it matches what the language chooses by default is redundant and + should be disalloed. 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/eng/config/PublishData.json b/eng/config/PublishData.json index 27e271cf33f5d..da865cfedad8c 100644 --- a/eng/config/PublishData.json +++ b/eng/config/PublishData.json @@ -263,7 +263,7 @@ "vsBranch": "dev/monicaro/versioning", "vsMajorVersion": 18, "insertionTitlePrefix": "[d18.0 P1]", - "insertionCreateDraftPR": false + "insertionCreateDraftPR": true }, "main": { "nugetKind": [ 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" } } diff --git a/src/Analyzers/CSharp/Analyzers/AddAccessibilityModifiers/CSharpAddAccessibilityModifiers.cs b/src/Analyzers/CSharp/Analyzers/AddAccessibilityModifiers/CSharpAddAccessibilityModifiers.cs index af2330e283b2e..0e3b9dbb57afb 100644 --- a/src/Analyzers/CSharp/Analyzers/AddAccessibilityModifiers/CSharpAddAccessibilityModifiers.cs +++ b/src/Analyzers/CSharp/Analyzers/AddAccessibilityModifiers/CSharpAddAccessibilityModifiers.cs @@ -2,7 +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 Microsoft.CodeAnalysis.AddAccessibilityModifiers; +using Microsoft.CodeAnalysis.AddOrRemoveAccessibilityModifiers; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -10,13 +10,13 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.CSharp.AddAccessibilityModifiers; +namespace Microsoft.CodeAnalysis.CSharp.AddOrRemoveAccessibilityModifiers; -internal class CSharpAddAccessibilityModifiers : AbstractAddAccessibilityModifiers +internal class CSharpAddOrRemoveAccessibilityModifiers : AbstractAddOrRemoveAccessibilityModifiers { - public static readonly CSharpAddAccessibilityModifiers Instance = new(); + public static readonly CSharpAddOrRemoveAccessibilityModifiers Instance = new(); - protected CSharpAddAccessibilityModifiers() + protected CSharpAddOrRemoveAccessibilityModifiers() { } diff --git a/src/Analyzers/CSharp/Analyzers/AddAccessibilityModifiers/CSharpAddAccessibilityModifiersDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/AddAccessibilityModifiers/CSharpAddAccessibilityModifiersDiagnosticAnalyzer.cs index 8f8622d108529..4b8e4c21c254e 100644 --- a/src/Analyzers/CSharp/Analyzers/AddAccessibilityModifiers/CSharpAddAccessibilityModifiersDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/AddAccessibilityModifiers/CSharpAddAccessibilityModifiersDiagnosticAnalyzer.cs @@ -2,21 +2,22 @@ // 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 Microsoft.CodeAnalysis.AddAccessibilityModifiers; +using Microsoft.CodeAnalysis.AddOrRemoveAccessibilityModifiers; using Microsoft.CodeAnalysis.CodeStyle; -using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.LanguageService; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.LanguageService; -namespace Microsoft.CodeAnalysis.CSharp.AddAccessibilityModifiers; +namespace Microsoft.CodeAnalysis.CSharp.AddOrRemoveAccessibilityModifiers; [DiagnosticAnalyzer(LanguageNames.CSharp)] -internal class CSharpAddAccessibilityModifiersDiagnosticAnalyzer - : AbstractAddAccessibilityModifiersDiagnosticAnalyzer +internal sealed class CSharpAddOrRemoveAccessibilityModifiersDiagnosticAnalyzer + : AbstractAddOrRemoveAccessibilityModifiersDiagnosticAnalyzer { + protected override IAccessibilityFacts AccessibilityFacts => CSharpAccessibilityFacts.Instance; + protected override IAddOrRemoveAccessibilityModifiers AddOrRemoveAccessibilityModifiers => CSharpAddOrRemoveAccessibilityModifiers.Instance; + protected override void ProcessCompilationUnit( SyntaxTreeAnalysisContext context, CodeStyleOption2 option, CompilationUnitSyntax compilationUnit) @@ -47,20 +48,6 @@ private void ProcessMemberDeclaration( if (member is TypeDeclarationSyntax typeDeclaration) ProcessMembers(context, option, typeDeclaration.Members); - if (!CSharpAddAccessibilityModifiers.Instance.ShouldUpdateAccessibilityModifier( - CSharpAccessibilityFacts.Instance, member, option.Value, out var name, out var modifiersAdded)) - { - return; - } - - // Have an issue to flag, either add or remove. Report issue to user. - var additionalLocations = ImmutableArray.Create(member.GetLocation()); - context.ReportDiagnostic(DiagnosticHelper.Create( - Descriptor, - name.GetLocation(), - option.Notification, - context.Options, - additionalLocations: additionalLocations, - modifiersAdded ? ModifiersAddedProperties : null)); + CheckMemberAndReportDiagnostic(context, option, member); } } diff --git a/src/Analyzers/CSharp/CodeFixes/AddAccessibilityModifiers/CSharpAddAccessibilityModifiersCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/AddAccessibilityModifiers/CSharpAddAccessibilityModifiersCodeFixProvider.cs index 57ae84194335c..175541ff0e3ed 100644 --- a/src/Analyzers/CSharp/CodeFixes/AddAccessibilityModifiers/CSharpAddAccessibilityModifiersCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/AddAccessibilityModifiers/CSharpAddAccessibilityModifiersCodeFixProvider.cs @@ -4,16 +4,16 @@ using System.Composition; using System.Diagnostics.CodeAnalysis; -using Microsoft.CodeAnalysis.AddAccessibilityModifiers; +using Microsoft.CodeAnalysis.AddOrRemoveAccessibilityModifiers; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace Microsoft.CodeAnalysis.CSharp.AddAccessibilityModifiers; +namespace Microsoft.CodeAnalysis.CSharp.AddOrRemoveAccessibilityModifiers; -[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.AddAccessibilityModifiers), Shared] +[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.AddOrRemoveAccessibilityModifiers), Shared] [method: ImportingConstructor] [method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] -internal sealed class CSharpAddAccessibilityModifiersCodeFixProvider() : AbstractAddAccessibilityModifiersCodeFixProvider +internal sealed class CSharpAddOrRemoveAccessibilityModifiersCodeFixProvider() : AbstractAddOrRemoveAccessibilityModifiersCodeFixProvider { protected override SyntaxNode MapToDeclarator(SyntaxNode node) { diff --git a/src/Analyzers/CSharp/CodeFixes/AddAccessibilityModifiers/CSharpAddAccessibilityModifiersService.cs b/src/Analyzers/CSharp/CodeFixes/AddAccessibilityModifiers/CSharpAddAccessibilityModifiersService.cs index 62b52a9e97e5f..78cc6bf67726d 100644 --- a/src/Analyzers/CSharp/CodeFixes/AddAccessibilityModifiers/CSharpAddAccessibilityModifiersService.cs +++ b/src/Analyzers/CSharp/CodeFixes/AddAccessibilityModifiers/CSharpAddAccessibilityModifiersService.cs @@ -4,12 +4,12 @@ using System; using System.Composition; -using Microsoft.CodeAnalysis.AddAccessibilityModifiers; +using Microsoft.CodeAnalysis.AddOrRemoveAccessibilityModifiers; using Microsoft.CodeAnalysis.Host.Mef; -namespace Microsoft.CodeAnalysis.CSharp.AddAccessibilityModifiers; +namespace Microsoft.CodeAnalysis.CSharp.AddOrRemoveAccessibilityModifiers; -[ExportLanguageService(typeof(IAddAccessibilityModifiersService), LanguageNames.CSharp), Shared] +[ExportLanguageService(typeof(IAddOrRemoveAccessibilityModifiersService), LanguageNames.CSharp), Shared] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal sealed class CSharpAddAccessibilityModifiersService() : CSharpAddAccessibilityModifiers, IAddAccessibilityModifiersService; +internal sealed class CSharpAddOrRemoveAccessibilityModifiersService() : CSharpAddOrRemoveAccessibilityModifiers, IAddOrRemoveAccessibilityModifiersService; diff --git a/src/Analyzers/CSharp/Tests/AddAccessibilityModifiers/AddAccessibilityModifiersFixAllTests.cs b/src/Analyzers/CSharp/Tests/AddAccessibilityModifiers/AddAccessibilityModifiersFixAllTests.cs index 8de7bf2451e7d..c85f257ba9dad 100644 --- a/src/Analyzers/CSharp/Tests/AddAccessibilityModifiers/AddAccessibilityModifiersFixAllTests.cs +++ b/src/Analyzers/CSharp/Tests/AddAccessibilityModifiers/AddAccessibilityModifiersFixAllTests.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp.AddAccessibilityModifiers; +using Microsoft.CodeAnalysis.CSharp.AddOrRemoveAccessibilityModifiers; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics; using Microsoft.CodeAnalysis.Test.Utilities; @@ -14,18 +14,18 @@ using Xunit; using Xunit.Abstractions; -namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.AddAccessibilityModifiers; +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.AddOrRemoveAccessibilityModifiers; -[Trait(Traits.Feature, Traits.Features.CodeActionsAddAccessibilityModifiers)] -public class AddAccessibilityModifiersFixAllTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_NoEditor +[Trait(Traits.Feature, Traits.Features.CodeActionsAddOrRemoveAccessibilityModifiers)] +public class AddOrRemoveAccessibilityModifiersFixAllTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_NoEditor { - public AddAccessibilityModifiersFixAllTests(ITestOutputHelper logger) + public AddOrRemoveAccessibilityModifiersFixAllTests(ITestOutputHelper logger) : base(logger) { } internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) - => (new CSharpAddAccessibilityModifiersDiagnosticAnalyzer(), new CSharpAddAccessibilityModifiersCodeFixProvider()); + => (new CSharpAddOrRemoveAccessibilityModifiersDiagnosticAnalyzer(), new CSharpAddOrRemoveAccessibilityModifiersCodeFixProvider()); [Fact, WorkItem("https://github.com/dotnet/vscode-csharp/issues/6611")] [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] diff --git a/src/Analyzers/CSharp/Tests/AddAccessibilityModifiers/AddAccessibilityModifiersTests.cs b/src/Analyzers/CSharp/Tests/AddAccessibilityModifiers/AddAccessibilityModifiersTests.cs index 1363fd069e220..e453663e93c48 100644 --- a/src/Analyzers/CSharp/Tests/AddAccessibilityModifiers/AddAccessibilityModifiersTests.cs +++ b/src/Analyzers/CSharp/Tests/AddAccessibilityModifiers/AddAccessibilityModifiersTests.cs @@ -5,20 +5,20 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.AddAccessibilityModifiers; +using Microsoft.CodeAnalysis.CSharp.AddOrRemoveAccessibilityModifiers; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; -namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.AddAccessibilityModifiers; +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.AddOrRemoveAccessibilityModifiers; using VerifyCS = CSharpCodeFixVerifier< - CSharpAddAccessibilityModifiersDiagnosticAnalyzer, - CSharpAddAccessibilityModifiersCodeFixProvider>; + CSharpAddOrRemoveAccessibilityModifiersDiagnosticAnalyzer, + CSharpAddOrRemoveAccessibilityModifiersCodeFixProvider>; -[Trait(Traits.Feature, Traits.Features.CodeActionsAddAccessibilityModifiers)] -public sealed class AddAccessibilityModifiersTests +[Trait(Traits.Feature, Traits.Features.CodeActionsAddOrRemoveAccessibilityModifiers)] +public sealed class AddOrRemoveAccessibilityModifiersTests { [Theory, CombinatorialData] public void TestStandardProperty(AnalyzerProperty property) diff --git a/src/Analyzers/Core/Analyzers/AddAccessibilityModifiers/AbstractAddAccessibilityModifiers.cs b/src/Analyzers/Core/Analyzers/AddAccessibilityModifiers/AbstractAddAccessibilityModifiers.cs index 334f0d2197a5a..831befee99011 100644 --- a/src/Analyzers/Core/Analyzers/AddAccessibilityModifiers/AbstractAddAccessibilityModifiers.cs +++ b/src/Analyzers/Core/Analyzers/AddAccessibilityModifiers/AbstractAddAccessibilityModifiers.cs @@ -5,9 +5,9 @@ using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.LanguageService; -namespace Microsoft.CodeAnalysis.AddAccessibilityModifiers; +namespace Microsoft.CodeAnalysis.AddOrRemoveAccessibilityModifiers; -internal abstract class AbstractAddAccessibilityModifiers : IAddAccessibilityModifiers +internal abstract class AbstractAddOrRemoveAccessibilityModifiers : IAddOrRemoveAccessibilityModifiers where TMemberDeclarationSyntax : SyntaxNode { public bool ShouldUpdateAccessibilityModifier( diff --git a/src/Analyzers/Core/Analyzers/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersDiagnosticAnalyzer.cs index c8e86b5a7b53e..372ae5d630a92 100644 --- a/src/Analyzers/Core/Analyzers/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersDiagnosticAnalyzer.cs @@ -5,20 +5,33 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.LanguageService; -namespace Microsoft.CodeAnalysis.AddAccessibilityModifiers; +namespace Microsoft.CodeAnalysis.AddOrRemoveAccessibilityModifiers; -internal abstract class AbstractAddAccessibilityModifiersDiagnosticAnalyzer() +internal abstract class AbstractAddOrRemoveAccessibilityModifiersDiagnosticAnalyzer() : AbstractBuiltInCodeStyleDiagnosticAnalyzer( - IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId, - EnforceOnBuildValues.AddAccessibilityModifiers, + IDEDiagnosticIds.AddOrRemoveAccessibilityModifiersDiagnosticId, + EnforceOnBuildValues.AddOrRemoveAccessibilityModifiers, CodeStyleOptions2.AccessibilityModifiersRequired, new LocalizableResourceString(nameof(AnalyzersResources.Add_accessibility_modifiers), AnalyzersResources.ResourceManager, typeof(AnalyzersResources)), new LocalizableResourceString(nameof(AnalyzersResources.Accessibility_modifiers_required), AnalyzersResources.ResourceManager, typeof(AnalyzersResources))) where TCompilationUnitSyntax : SyntaxNode { + protected abstract IAccessibilityFacts AccessibilityFacts { get; } + protected abstract IAddOrRemoveAccessibilityModifiers AddOrRemoveAccessibilityModifiers { get; } + + protected abstract void ProcessCompilationUnit(SyntaxTreeAnalysisContext context, CodeStyleOption2 option, TCompilationUnitSyntax compilationUnitSyntax); + + protected readonly DiagnosticDescriptor ModifierRemovedDescriptor = CreateDescriptorWithId( + IDEDiagnosticIds.AddOrRemoveAccessibilityModifiersDiagnosticId, + EnforceOnBuildValues.AddOrRemoveAccessibilityModifiers, + hasAnyCodeStyleOption: true, + new LocalizableResourceString(nameof(AnalyzersResources.Remove_accessibility_modifiers), AnalyzersResources.ResourceManager, typeof(AnalyzersResources)), + new LocalizableResourceString(nameof(AnalyzersResources.Accessibility_modifiers_unnecessary), AnalyzersResources.ResourceManager, typeof(AnalyzersResources))); + protected static readonly ImmutableDictionary ModifiersAddedProperties = ImmutableDictionary.Empty.Add( - AddAccessibilityModifiersConstants.ModifiersAdded, AddAccessibilityModifiersConstants.ModifiersAdded); + AddOrRemoveAccessibilityModifiersConstants.ModifiersAdded, AddOrRemoveAccessibilityModifiersConstants.ModifiersAdded); public sealed override DiagnosticAnalyzerCategory GetAnalyzerCategory() => DiagnosticAnalyzerCategory.SyntaxTreeWithoutSemanticsAnalysis; @@ -39,5 +52,25 @@ private void AnalyzeTree(SyntaxTreeAnalysisContext context, CompilationOptions c ProcessCompilationUnit(context, option, (TCompilationUnitSyntax)context.Tree.GetRoot(context.CancellationToken)); } - protected abstract void ProcessCompilationUnit(SyntaxTreeAnalysisContext context, CodeStyleOption2 option, TCompilationUnitSyntax compilationUnitSyntax); + protected void CheckMemberAndReportDiagnostic( + SyntaxTreeAnalysisContext context, + CodeStyleOption2 option, + SyntaxNode member) + { + if (!this.AddOrRemoveAccessibilityModifiers.ShouldUpdateAccessibilityModifier( + this.AccessibilityFacts, member, option.Value, out var name, out var modifiersAdded)) + { + return; + } + + // Have an issue to flag, either add or remove. Report issue to user. + var additionalLocations = ImmutableArray.Create(member.GetLocation()); + context.ReportDiagnostic(DiagnosticHelper.Create( + modifiersAdded ? Descriptor : ModifierRemovedDescriptor, + name.GetLocation(), + option.Notification, + context.Options, + additionalLocations: additionalLocations, + modifiersAdded ? ModifiersAddedProperties : null)); + } } diff --git a/src/Analyzers/Core/Analyzers/AddAccessibilityModifiers/IAddAccessibilityModifiersService.cs b/src/Analyzers/Core/Analyzers/AddAccessibilityModifiers/IAddAccessibilityModifiersService.cs index d3847ccc77d66..963cb75dbc551 100644 --- a/src/Analyzers/Core/Analyzers/AddAccessibilityModifiers/IAddAccessibilityModifiersService.cs +++ b/src/Analyzers/Core/Analyzers/AddAccessibilityModifiers/IAddAccessibilityModifiersService.cs @@ -5,14 +5,14 @@ using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.LanguageService; -namespace Microsoft.CodeAnalysis.AddAccessibilityModifiers; +namespace Microsoft.CodeAnalysis.AddOrRemoveAccessibilityModifiers; -internal static partial class AddAccessibilityModifiersConstants +internal static partial class AddOrRemoveAccessibilityModifiersConstants { public const string ModifiersAdded = nameof(ModifiersAdded); } -internal interface IAddAccessibilityModifiers +internal interface IAddOrRemoveAccessibilityModifiers { bool ShouldUpdateAccessibilityModifier( IAccessibilityFacts accessibilityFacts, diff --git a/src/Analyzers/Core/Analyzers/AnalyzersResources.resx b/src/Analyzers/Core/Analyzers/AnalyzersResources.resx index 34ab80d6e554e..4223c2a2f8317 100644 --- a/src/Analyzers/Core/Analyzers/AnalyzersResources.resx +++ b/src/Analyzers/Core/Analyzers/AnalyzersResources.resx @@ -406,4 +406,7 @@ Implement through '{0}' + + Accessibility modifiers unnecessary + \ No newline at end of file diff --git a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs index 8f49df52e2dfb..6dc7526db970f 100644 --- a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs +++ b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs @@ -14,7 +14,7 @@ internal static class EnforceOnBuildValues public const EnforceOnBuild UseExplicitType = /*IDE0008*/ EnforceOnBuild.HighlyRecommended; public const EnforceOnBuild AddBraces = /*IDE0011*/ EnforceOnBuild.HighlyRecommended; public const EnforceOnBuild OrderModifiers = /*IDE0036*/ EnforceOnBuild.HighlyRecommended; - public const EnforceOnBuild AddAccessibilityModifiers = /*IDE0040*/ EnforceOnBuild.HighlyRecommended; + public const EnforceOnBuild AddOrRemoveAccessibilityModifiers = /*IDE0040*/ EnforceOnBuild.HighlyRecommended; public const EnforceOnBuild ValidateFormatString = /*IDE0043*/ EnforceOnBuild.HighlyRecommended; public const EnforceOnBuild MakeFieldReadonly = /*IDE0044*/ EnforceOnBuild.HighlyRecommended; public const EnforceOnBuild RemoveUnusedMembers = /*IDE0051*/ EnforceOnBuild.HighlyRecommended; diff --git a/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs b/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs index 0332f81299acd..66cbc65fec400 100644 --- a/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs +++ b/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs @@ -63,7 +63,7 @@ internal static class IDEDiagnosticIds public const string UseLocalFunctionDiagnosticId = "IDE0039"; - public const string AddAccessibilityModifiersDiagnosticId = "IDE0040"; + public const string AddOrRemoveAccessibilityModifiersDiagnosticId = "IDE0040"; public const string UseIsNullCheckDiagnosticId = "IDE0041"; diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.cs.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.cs.xlf index d0248fe8779b0..26496c2b00074 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.cs.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.cs.xlf @@ -17,6 +17,11 @@ Vyžadují se modifikátory dostupnosti. + + Accessibility modifiers unnecessary + Accessibility modifiers unnecessary + + Add accessibility modifiers Přidat Modifikátory dostupnosti diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.de.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.de.xlf index c486b28641859..5a52eac44aa90 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.de.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.de.xlf @@ -17,6 +17,11 @@ Zugriffsmodifizierer erforderlich + + Accessibility modifiers unnecessary + Accessibility modifiers unnecessary + + Add accessibility modifiers Zugriffsmodifizierer hinzufügen diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.es.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.es.xlf index bbda0137154b4..ecd358040dcbf 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.es.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.es.xlf @@ -17,6 +17,11 @@ Modificadores de accesibilidad requeridos + + Accessibility modifiers unnecessary + Accessibility modifiers unnecessary + + Add accessibility modifiers Agregar modificadores de accesibilidad diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.fr.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.fr.xlf index 62df8a0e61ef6..34cc6b9e5dee0 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.fr.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.fr.xlf @@ -17,6 +17,11 @@ Modificateurs d'accessibilité obligatoires + + Accessibility modifiers unnecessary + Accessibility modifiers unnecessary + + Add accessibility modifiers Ajouter des modificateurs d'accessibilité diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.it.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.it.xlf index c209161779bf2..853e44033183e 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.it.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.it.xlf @@ -17,6 +17,11 @@ Modificatori di accessibilità obbligatori + + Accessibility modifiers unnecessary + Accessibility modifiers unnecessary + + Add accessibility modifiers Aggiungi i modificatori di accessibilità diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ja.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ja.xlf index 5722141523a15..92f825cce91a0 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ja.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ja.xlf @@ -17,6 +17,11 @@ アクセシビリティ修飾子が必要です + + Accessibility modifiers unnecessary + Accessibility modifiers unnecessary + + Add accessibility modifiers アクセシビリティ修飾子を追加します diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ko.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ko.xlf index 5e9e27116dab6..086699fda54a3 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ko.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ko.xlf @@ -17,6 +17,11 @@ 접근성 한정자 필요 + + Accessibility modifiers unnecessary + Accessibility modifiers unnecessary + + Add accessibility modifiers 접근성 한정자 추가 diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.pl.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.pl.xlf index dbd0d8a7e6498..84c9dda0cda65 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.pl.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.pl.xlf @@ -17,6 +17,11 @@ Wymagane modyfikatory dostępności + + Accessibility modifiers unnecessary + Accessibility modifiers unnecessary + + Add accessibility modifiers Dodaj modyfikatory dostępności diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.pt-BR.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.pt-BR.xlf index 51e2a0e7acd64..704936e6e3794 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.pt-BR.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.pt-BR.xlf @@ -17,6 +17,11 @@ Modificadores de acessibilidade necessários + + Accessibility modifiers unnecessary + Accessibility modifiers unnecessary + + Add accessibility modifiers Adicionar modificadores de acessibilidade diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ru.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ru.xlf index 1e0cea3951856..16a067d61c1ab 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ru.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ru.xlf @@ -17,6 +17,11 @@ Требуются модификаторы доступа + + Accessibility modifiers unnecessary + Accessibility modifiers unnecessary + + Add accessibility modifiers Добавить модификаторы доступа diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.tr.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.tr.xlf index 5ffcc4f3c7fc8..af0aedeeadaf3 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.tr.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.tr.xlf @@ -17,6 +17,11 @@ Erişilebilirlik değiştiricileri gerekli + + Accessibility modifiers unnecessary + Accessibility modifiers unnecessary + + Add accessibility modifiers Erişilebilirlik değiştiricileri Ekle diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.zh-Hans.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.zh-Hans.xlf index 6d60cb626c2ce..2f89565cd05a3 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.zh-Hans.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.zh-Hans.xlf @@ -17,6 +17,11 @@ 需要可访问性修饰符 + + Accessibility modifiers unnecessary + Accessibility modifiers unnecessary + + Add accessibility modifiers 添加可访问性修饰符 diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.zh-Hant.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.zh-Hant.xlf index 968c91af7e239..51f4235f1d10d 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.zh-Hant.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.zh-Hant.xlf @@ -17,6 +17,11 @@ 協助工具修飾元為必要項 + + Accessibility modifiers unnecessary + Accessibility modifiers unnecessary + + Add accessibility modifiers 新增協助工具修飾元 diff --git a/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersCodeFixProvider.cs index 275be2ee50cb0..feccbc95b2600 100644 --- a/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersCodeFixProvider.cs @@ -14,14 +14,14 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.AddAccessibilityModifiers; +namespace Microsoft.CodeAnalysis.AddOrRemoveAccessibilityModifiers; -internal abstract class AbstractAddAccessibilityModifiersCodeFixProvider : SyntaxEditorBasedCodeFixProvider +internal abstract class AbstractAddOrRemoveAccessibilityModifiersCodeFixProvider : SyntaxEditorBasedCodeFixProvider { protected abstract SyntaxNode MapToDeclarator(SyntaxNode declaration); public sealed override ImmutableArray FixableDiagnosticIds - => [IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId]; + => [IDEDiagnosticIds.AddOrRemoveAccessibilityModifiersDiagnosticId]; public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) { @@ -31,7 +31,7 @@ public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) ? CodeActionPriority.Low : CodeActionPriority.Default; - var (title, key) = diagnostic.Properties.ContainsKey(AddAccessibilityModifiersConstants.ModifiersAdded) + var (title, key) = diagnostic.Properties.ContainsKey(AddOrRemoveAccessibilityModifiersConstants.ModifiersAdded) ? (AnalyzersResources.Add_accessibility_modifiers, nameof(AnalyzersResources.Add_accessibility_modifiers)) : (AnalyzersResources.Remove_accessibility_modifiers, nameof(AnalyzersResources.Remove_accessibility_modifiers)); @@ -52,7 +52,7 @@ protected sealed override async Task FixAllAsync( var declarator = MapToDeclarator(declaration); var symbol = semanticModel.GetDeclaredSymbol(declarator, cancellationToken); Contract.ThrowIfNull(symbol); - AddAccessibilityModifiersHelpers.UpdateDeclaration(editor, symbol, declaration); + AddOrRemoveAccessibilityModifiersHelpers.UpdateDeclaration(editor, symbol, declaration); } } } diff --git a/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/AddAccessibilityModifiersHelpers.cs b/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/AddAccessibilityModifiersHelpers.cs index 1604b608eb4eb..e8bb74983cb44 100644 --- a/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/AddAccessibilityModifiersHelpers.cs +++ b/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/AddAccessibilityModifiersHelpers.cs @@ -7,9 +7,9 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.AddAccessibilityModifiers; +namespace Microsoft.CodeAnalysis.AddOrRemoveAccessibilityModifiers; -internal static partial class AddAccessibilityModifiersHelpers +internal static partial class AddOrRemoveAccessibilityModifiersHelpers { public static void UpdateDeclaration( SyntaxEditor editor, ISymbol symbol, SyntaxNode declaration) diff --git a/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/IAddAccessibilityModifiersService.cs b/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/IAddAccessibilityModifiersService.cs index 21dce6e78ca36..b2d3885afcfb5 100644 --- a/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/IAddAccessibilityModifiersService.cs +++ b/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/IAddAccessibilityModifiersService.cs @@ -4,8 +4,8 @@ using Microsoft.CodeAnalysis.Host; -namespace Microsoft.CodeAnalysis.AddAccessibilityModifiers; +namespace Microsoft.CodeAnalysis.AddOrRemoveAccessibilityModifiers; -internal interface IAddAccessibilityModifiersService : IAddAccessibilityModifiers, ILanguageService +internal interface IAddOrRemoveAccessibilityModifiersService : IAddOrRemoveAccessibilityModifiers, ILanguageService { } diff --git a/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs b/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs index dc931b6b22c5f..b25199c960ec8 100644 --- a/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs +++ b/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs @@ -6,7 +6,7 @@ namespace Microsoft.CodeAnalysis.CodeFixes; internal static class PredefinedCodeFixProviderNames { - public const string AddAccessibilityModifiers = nameof(AddAccessibilityModifiers); + public const string AddOrRemoveAccessibilityModifiers = nameof(AddOrRemoveAccessibilityModifiers); public const string AddAnonymousTypeMemberName = nameof(AddAnonymousTypeMemberName); public const string AddAsync = nameof(AddAsync); public const string AddBraces = nameof(AddBraces); diff --git a/src/Analyzers/VisualBasic/Analyzers/AddAccessibilityModifiers/VisualBasicAddAccessibilityModifiers.vb b/src/Analyzers/VisualBasic/Analyzers/AddAccessibilityModifiers/VisualBasicAddAccessibilityModifiers.vb index 1d7e64bd70632..bd1cc9b6a46f6 100644 --- a/src/Analyzers/VisualBasic/Analyzers/AddAccessibilityModifiers/VisualBasicAddAccessibilityModifiers.vb +++ b/src/Analyzers/VisualBasic/Analyzers/AddAccessibilityModifiers/VisualBasicAddAccessibilityModifiers.vb @@ -2,15 +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. -Imports Microsoft.CodeAnalysis.AddAccessibilityModifiers +Imports Microsoft.CodeAnalysis.AddOrRemoveAccessibilityModifiers Imports Microsoft.CodeAnalysis.CodeStyle Imports Microsoft.CodeAnalysis.VisualBasic.Syntax -Namespace Microsoft.CodeAnalysis.VisualBasic.AddAccessibilityModifiers - Friend Class VisualBasicAddAccessibilityModifiers - Inherits AbstractAddAccessibilityModifiers(Of StatementSyntax) +Namespace Microsoft.CodeAnalysis.VisualBasic.AddOrRemoveAccessibilityModifiers + Friend Class VisualBasicAddOrRemoveAccessibilityModifiers + Inherits AbstractAddOrRemoveAccessibilityModifiers(Of StatementSyntax) - Public Shared ReadOnly Instance As New VisualBasicAddAccessibilityModifiers() + Public Shared ReadOnly Instance As New VisualBasicAddOrRemoveAccessibilityModifiers() Protected Sub New() End Sub diff --git a/src/Analyzers/VisualBasic/Analyzers/AddAccessibilityModifiers/VisualBasicAddAccessibilityModifiersDiagnosticAnalyzer.vb b/src/Analyzers/VisualBasic/Analyzers/AddAccessibilityModifiers/VisualBasicAddAccessibilityModifiersDiagnosticAnalyzer.vb index 0781477ba3750..ddae933bc7667 100644 --- a/src/Analyzers/VisualBasic/Analyzers/AddAccessibilityModifiers/VisualBasicAddAccessibilityModifiersDiagnosticAnalyzer.vb +++ b/src/Analyzers/VisualBasic/Analyzers/AddAccessibilityModifiers/VisualBasicAddAccessibilityModifiersDiagnosticAnalyzer.vb @@ -2,17 +2,20 @@ ' 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 Microsoft.CodeAnalysis.AddAccessibilityModifiers +Imports Microsoft.CodeAnalysis.AddOrRemoveAccessibilityModifiers Imports Microsoft.CodeAnalysis.CodeStyle Imports Microsoft.CodeAnalysis.Diagnostics -Imports Microsoft.CodeAnalysis.VisualBasic.Syntax +Imports Microsoft.CodeAnalysis.LanguageService Imports Microsoft.CodeAnalysis.VisualBasic.LanguageService +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax -Namespace Microsoft.CodeAnalysis.VisualBasic.AddAccessibilityModifiers +Namespace Microsoft.CodeAnalysis.VisualBasic.AddOrRemoveAccessibilityModifiers - Friend Class VisualBasicAddAccessibilityModifiersDiagnosticAnalyzer - Inherits AbstractAddAccessibilityModifiersDiagnosticAnalyzer(Of CompilationUnitSyntax) + Friend NotInheritable Class VisualBasicAddOrRemoveAccessibilityModifiersDiagnosticAnalyzer + Inherits AbstractAddOrRemoveAccessibilityModifiersDiagnosticAnalyzer(Of CompilationUnitSyntax) + + Protected Overrides ReadOnly Property AccessibilityFacts As IAccessibilityFacts = VisualBasicAccessibilityFacts.Instance + Protected Overrides ReadOnly Property AddOrRemoveAccessibilityModifiers As IAddOrRemoveAccessibilityModifiers = VisualBasicAddOrRemoveAccessibilityModifiers.Instance Protected Overrides Sub ProcessCompilationUnit( context As SyntaxTreeAnalysisContext, @@ -55,21 +58,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.AddAccessibilityModifiers ProcessMembers(context, [option], typeBlock.Members) End If - Dim name As SyntaxToken = Nothing - Dim modifiersAdded As Boolean = False - If Not VisualBasicAddAccessibilityModifiers.Instance.ShouldUpdateAccessibilityModifier(VisualBasicAccessibilityFacts.Instance, member, [option].Value, name, modifiersAdded) Then - Return - End If - - ' Have an issue to flag, either add or remove. Report issue to user. - Dim additionalLocations = ImmutableArray.Create(member.GetLocation()) - context.ReportDiagnostic(DiagnosticHelper.Create( - Descriptor, - name.GetLocation(), - [option].Notification, - context.Options, - additionalLocations:=additionalLocations, - If(modifiersAdded, ModifiersAddedProperties, Nothing))) + CheckMemberAndReportDiagnostic(context, [option], member) End Sub End Class End Namespace diff --git a/src/Analyzers/VisualBasic/CodeFixes/AddAccessibilityModifiers/VisualBasicAddAccessibilityModifiersCodeFixProvider.vb b/src/Analyzers/VisualBasic/CodeFixes/AddAccessibilityModifiers/VisualBasicAddAccessibilityModifiersCodeFixProvider.vb index 3d1f470f2b079..8da0ee4ebfad7 100644 --- a/src/Analyzers/VisualBasic/CodeFixes/AddAccessibilityModifiers/VisualBasicAddAccessibilityModifiersCodeFixProvider.vb +++ b/src/Analyzers/VisualBasic/CodeFixes/AddAccessibilityModifiers/VisualBasicAddAccessibilityModifiersCodeFixProvider.vb @@ -3,15 +3,15 @@ ' See the LICENSE file in the project root for more information. Imports System.Composition -Imports Microsoft.CodeAnalysis.AddAccessibilityModifiers +Imports Microsoft.CodeAnalysis.AddOrRemoveAccessibilityModifiers Imports Microsoft.CodeAnalysis.CodeFixes Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.VisualBasic.Syntax -Namespace Microsoft.CodeAnalysis.VisualBasic.AddAccessibilityModifiers - - Friend Class VisualBasicAddAccessibilityModifiersCodeFixProvider - Inherits AbstractAddAccessibilityModifiersCodeFixProvider +Namespace Microsoft.CodeAnalysis.VisualBasic.AddOrRemoveAccessibilityModifiers + + Friend NotInheritable Class VisualBasicAddOrRemoveAccessibilityModifiersCodeFixProvider + Inherits AbstractAddOrRemoveAccessibilityModifiersCodeFixProvider diff --git a/src/Analyzers/VisualBasic/CodeFixes/AddAccessibilityModifiers/VisualBasicAddAccessibilityModifiersService.vb b/src/Analyzers/VisualBasic/CodeFixes/AddAccessibilityModifiers/VisualBasicAddAccessibilityModifiersService.vb index 3993b1e957f2f..e49521fef02f2 100644 --- a/src/Analyzers/VisualBasic/CodeFixes/AddAccessibilityModifiers/VisualBasicAddAccessibilityModifiersService.vb +++ b/src/Analyzers/VisualBasic/CodeFixes/AddAccessibilityModifiers/VisualBasicAddAccessibilityModifiersService.vb @@ -3,14 +3,14 @@ ' See the LICENSE file in the project root for more information. Imports System.Composition -Imports Microsoft.CodeAnalysis.AddAccessibilityModifiers +Imports Microsoft.CodeAnalysis.AddOrRemoveAccessibilityModifiers Imports Microsoft.CodeAnalysis.Host.Mef -Namespace Microsoft.CodeAnalysis.VisualBasic.AddAccessibilityModifiers - - Friend Class VisualBasicAddAccessibilityModifiersService - Inherits VisualBasicAddAccessibilityModifiers - Implements IAddAccessibilityModifiersService +Namespace Microsoft.CodeAnalysis.VisualBasic.AddOrRemoveAccessibilityModifiers + + Friend NotInheritable Class VisualBasicAddOrRemoveAccessibilityModifiersService + Inherits VisualBasicAddOrRemoveAccessibilityModifiers + Implements IAddOrRemoveAccessibilityModifiersService diff --git a/src/Analyzers/VisualBasic/Tests/AddAccessibilityModifiers/AddAccessibilityModifiersTests.vb b/src/Analyzers/VisualBasic/Tests/AddAccessibilityModifiers/AddAccessibilityModifiersTests.vb index 5e2229ddb255a..f0fe2225926a7 100644 --- a/src/Analyzers/VisualBasic/Tests/AddAccessibilityModifiers/AddAccessibilityModifiersTests.vb +++ b/src/Analyzers/VisualBasic/Tests/AddAccessibilityModifiers/AddAccessibilityModifiersTests.vb @@ -5,13 +5,13 @@ Imports Microsoft.CodeAnalysis.CodeStyle Imports Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions Imports VerifyVB = Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions.VisualBasicCodeFixVerifier(Of - Microsoft.CodeAnalysis.VisualBasic.AddAccessibilityModifiers.VisualBasicAddAccessibilityModifiersDiagnosticAnalyzer, - Microsoft.CodeAnalysis.VisualBasic.AddAccessibilityModifiers.VisualBasicAddAccessibilityModifiersCodeFixProvider) + Microsoft.CodeAnalysis.VisualBasic.AddOrRemoveAccessibilityModifiers.VisualBasicAddOrRemoveAccessibilityModifiersDiagnosticAnalyzer, + Microsoft.CodeAnalysis.VisualBasic.AddOrRemoveAccessibilityModifiers.VisualBasicAddOrRemoveAccessibilityModifiersCodeFixProvider) -Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.AddAccessibilityModifiers +Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.AddOrRemoveAccessibilityModifiers - - Public Class AddAccessibilityModifiersTests + + Public Class AddOrRemoveAccessibilityModifiersTests Public Sub TestStandardProperty([property] As AnalyzerProperty) VerifyVB.VerifyStandardProperty([property]) diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 12f19f4de2626..078840f3dd553 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -5050,25 +5050,26 @@ private void ParseVariableDeclarators( } else if (this.CurrentToken.Kind == SyntaxKind.CommaToken) { - // If we see `for (int i = 0, j < ...` then we do not want to consume j as the next declarator. + // If we see `for (int i = 0, i < ...` then we do not want to consume the second 'i' as the next declarator as it + // is more likely that the user meant to write `for (int i = 0; i < ...` instead and accidentally + // used a comma instead of a semicolon. // - // Legal forms here are `for (int i = 0, j; ...` or `for (int i = 0, j = ...` or `for (int i = 0, j)`. + // Note: the legal forms that we must keep parsing as a variable declarator are: // - // We also accept: `for (int i = 0, ;` as that's likely an intermediary state prior to writing the next - // variable. + // for (int i = 0, j, k; ... // identifier comma + // for (int i = 0, j = ... // identifier equals + // for (int i = 0, j; ... // identifier semicolon // - // Anything else we'll treat as as more likely to be the following conditional. + // We also accept: `for (int i = 0, ;` as that's likely an intermediary state prior to writing the + // next variable. Anything else we'll treat as as more likely to be the following conditional. if (flags.HasFlag(VariableFlags.ForStatement) && this.PeekToken(1).Kind != SyntaxKind.SemicolonToken) { - // `int i = 0, ...` where what follows is not an identifier. Don't treat this as the start of a - // second variable. - if (!IsTrueIdentifier(this.PeekToken(1))) - break; + var isLegalVariableDeclaratorStart = + IsTrueIdentifier(this.PeekToken(1)) && + this.PeekToken(2).Kind is SyntaxKind.CommaToken or SyntaxKind.EqualsToken or SyntaxKind.SemicolonToken; - // `int i = 0, j ...` where what follows is not something that continues a variable declaration. - // In this case, treat that `j` as the start of the condition expression instead. - if (this.PeekToken(2).Kind is not (SyntaxKind.SemicolonToken or SyntaxKind.EqualsToken or SyntaxKind.CloseParenToken)) + if (!isLegalVariableDeclaratorStart) break; } diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt index 0ebff1a371642..4fbb99123b487 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -9,6 +9,7 @@ override abstract Microsoft.CodeAnalysis.CSharp.InterceptableLocation.Equals(obj override abstract Microsoft.CodeAnalysis.CSharp.InterceptableLocation.GetHashCode() -> int static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetInterceptableLocation(this Microsoft.CodeAnalysis.SemanticModel? semanticModel, Microsoft.CodeAnalysis.CSharp.Syntax.InvocationExpressionSyntax! node, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.CSharp.InterceptableLocation? static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetInterceptsLocationAttributeSyntax(this Microsoft.CodeAnalysis.CSharp.InterceptableLocation! location) -> string! +abstract Microsoft.CodeAnalysis.CSharp.InterceptableLocation.Equals(Microsoft.CodeAnalysis.CSharp.InterceptableLocation? other) -> bool [RSEXPERIMENTAL003]Microsoft.CodeAnalysis.CSharp.SyntaxTokenParser [RSEXPERIMENTAL003]Microsoft.CodeAnalysis.CSharp.SyntaxTokenParser.Dispose() -> void [RSEXPERIMENTAL003]Microsoft.CodeAnalysis.CSharp.SyntaxTokenParser.ParseLeadingTrivia() -> Microsoft.CodeAnalysis.CSharp.SyntaxTokenParser.Result diff --git a/src/Compilers/CSharp/Portable/Utilities/InterceptableLocation.cs b/src/Compilers/CSharp/Portable/Utilities/InterceptableLocation.cs index 85c76ab47e0e4..049f6878cb7dc 100644 --- a/src/Compilers/CSharp/Portable/Utilities/InterceptableLocation.cs +++ b/src/Compilers/CSharp/Portable/Utilities/InterceptableLocation.cs @@ -18,7 +18,7 @@ namespace Microsoft.CodeAnalysis.CSharp; /// Denotes an interceptable call. Used by source generators to generate '[InterceptsLocation]' attributes. /// /// -public abstract class InterceptableLocation +public abstract class InterceptableLocation : IEquatable { private protected InterceptableLocation() { } @@ -40,6 +40,8 @@ private protected InterceptableLocation() { } public abstract override bool Equals(object? obj); public abstract override int GetHashCode(); + + public abstract bool Equals(InterceptableLocation? other); } #pragma warning disable RSEXPERIMENTAL002 // internal usage of experimental API @@ -167,12 +169,7 @@ public override bool Equals(object? obj) if ((object)this == obj) return true; - return obj is InterceptableLocation1 other - && _checksum.SequenceEqual(other._checksum) - && _path == other._path - && _position == other._position - && _lineNumberOneIndexed == other._lineNumberOneIndexed - && _characterNumberOneIndexed == other._characterNumberOneIndexed; + return obj is InterceptableLocation other && Equals(other); } public override int GetHashCode() @@ -183,4 +180,14 @@ public override int GetHashCode() BinaryPrimitives.ReadInt32LittleEndian(_checksum.AsSpan()), _position); } + + public override bool Equals(InterceptableLocation? obj) + { + return obj is InterceptableLocation1 other + && _checksum.SequenceEqual(other._checksum) + && _path == other._path + && _position == other._position + && _lineNumberOneIndexed == other._lineNumberOneIndexed + && _characterNumberOneIndexed == other._characterNumberOneIndexed; + } } diff --git a/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs b/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs index add91eb5d5684..a234d69049153 100644 --- a/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs @@ -2164,6 +2164,121 @@ class C { SymbolDisplayPartKind.Keyword); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77219")] + public void TestPropertyBackingField_Minimal_01() + { + var text = @" +#nullable enable +class C { + string P { get; set; } } +"; + + Func findSymbol = global => + global.GetTypeMembers("C", 0).Single(). + GetMembers("

k__BackingField").Single(); + + var format = new SymbolDisplayFormat( + memberOptions: + SymbolDisplayMemberOptions.IncludeExplicitInterface); + + TestSymbolDescription( + text, + findSymbol, + format, + "P.field", + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Keyword); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77219")] + public void TestPropertyBackingField_Minimal_02() + { + var text = @" +#nullable enable + +interface I { + string P { get; set; } +} + +class C : I { + string I.P { get; set; } } +"; + + Func findSymbol = global => + global.GetTypeMembers("C", 0).Single(). + GetMembers("k__BackingField").Single(); + + var format = new SymbolDisplayFormat( + memberOptions: + SymbolDisplayMemberOptions.IncludeExplicitInterface); + + TestSymbolDescription( + text, + findSymbol, + format, + "I.P.field", + SymbolDisplayPartKind.InterfaceName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Keyword); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77219")] + public void TestPropertyBackingField_Minimal_03() + { + var text = @" +#nullable enable + +interface I { + string P { get; set; } +} + +class C : I { + string I.P { get; set; } } +"; + + Func findSymbol = global => + global.GetTypeMembers("C", 0).Single(). + GetMembers("k__BackingField").Single(); + + var format = new SymbolDisplayFormat(); + + TestSymbolDescription( + text, + findSymbol, + format, + "P.field", + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Keyword); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77219")] + public void TestOrdinaryField_Minimal() + { + var text = @" +#nullable enable + +class C { + string F; +"; + + Func findSymbol = global => + global.GetTypeMembers("C", 0).Single(). + GetMembers("F").Single(); + + var format = new SymbolDisplayFormat(); + + TestSymbolDescription( + text, + findSymbol, + format, + "F", + SymbolDisplayPartKind.FieldName); + } + [Fact] public void TestPropertyBackingFieldFromCompilationReference() { diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ForStatementParsingTest.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ForStatementParsingTest.cs index 75dcb989303d4..9edfa81c9a381 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ForStatementParsingTest.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ForStatementParsingTest.cs @@ -213,12 +213,9 @@ public void TestCommaSeparators3() public void TestCommaSeparators4() { UsingStatement("for (int i = 0, i) ;", - // (1,18): error CS1002: ; expected - // for (int i = 0, i) ; - Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(1, 18), - // (1,18): error CS1525: Invalid expression term ')' + // (1,15): error CS1002: ; expected // for (int i = 0, i) ; - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(1, 18), + Diagnostic(ErrorCode.ERR_SemicolonExpected, ",").WithLocation(1, 15), // (1,18): error CS1002: ; expected // for (int i = 0, i) ; Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(1, 18)); @@ -245,16 +242,11 @@ public void TestCommaSeparators4() } } } - N(SyntaxKind.CommaToken); - N(SyntaxKind.VariableDeclarator); - { - N(SyntaxKind.IdentifierToken, "i"); - } } M(SyntaxKind.SemicolonToken); - M(SyntaxKind.IdentifierName); + N(SyntaxKind.IdentifierName); { - M(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "i"); } M(SyntaxKind.SemicolonToken); N(SyntaxKind.CloseParenToken); @@ -448,4 +440,4461 @@ public void TestVariableDeclaratorVersusCondition1() } EOF(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77160")] + public void TestMultipleDeclaratorsWithInitializers1() + { + UsingStatement(""" + for (int offset = 0, c1, c2; offset < length;) + { + } + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "offset"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "c1"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "c2"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "offset"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "length"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77160")] + public void TestMultipleDeclaratorsWithInitializers2() + { + UsingStatement(""" + for (int offset = 0, c1 = 1, c2; offset < length;) + { + } + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "offset"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "c1"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "c2"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "offset"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "length"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77160")] + public void TestMultipleDeclaratorsWithInitializers3() + { + UsingStatement(""" + for (int offset = 0, c1, c2 = 1; offset < length;) + { + } + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "offset"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "c1"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "c2"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "offset"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "length"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77160")] + public void TestMultipleDeclaratorsWithInitializers4() + { + UsingStatement(""" + for (int offset = 0, c1,; offset < length;) + { + } + """, + // (1,25): error CS1001: Identifier expected + // for (int offset = 0, c1,; offset < length;) + Diagnostic(ErrorCode.ERR_IdentifierExpected, ";").WithLocation(1, 25)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "offset"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "c1"); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "offset"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "length"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77160")] + public void TestMultipleDeclaratorsWithInitializers5() + { + UsingStatement(""" + for (int offset = 0, c1, c2,; offset < length;) + { + } + """, + // (1,29): error CS1001: Identifier expected + // for (int offset = 0, c1, c2,; offset < length;) + Diagnostic(ErrorCode.ERR_IdentifierExpected, ";").WithLocation(1, 29)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "offset"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "c1"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "c2"); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "offset"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "length"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77160")] + public void TestMultipleDeclaratorsWithInitializers6() + { + UsingStatement(""" + for (int offset = 0, c1 = ,; offset < length;) + { + } + """, + // (1,27): error CS1525: Invalid expression term ',' + // for (int offset = 0, c1 = ,; offset < length;) + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ",").WithArguments(",").WithLocation(1, 27), + // (1,28): error CS1001: Identifier expected + // for (int offset = 0, c1 = ,; offset < length;) + Diagnostic(ErrorCode.ERR_IdentifierExpected, ";").WithLocation(1, 28)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "offset"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "c1"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "offset"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "length"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77160")] + public void TestMultipleDeclaratorsWithInitializers7() + { + UsingStatement(""" + for (int offset = 0, c1 = , c2; offset < length;) + { + } + """, + // (1,27): error CS1525: Invalid expression term ',' + // for (int offset = 0, c1 = , c2; offset < length;) + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ",").WithArguments(",").WithLocation(1, 27)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "offset"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "c1"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "c2"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "offset"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "length"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77160")] + public void TestMultipleDeclaratorsWithExpression1() + { + UsingStatement(""" + for (Console.WriteLine("Blah"); true;) + { + } + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Console"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "WriteLine"); + } + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.StringLiteralExpression); + { + N(SyntaxKind.StringLiteralToken, "\"Blah\""); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.TrueLiteralExpression); + { + N(SyntaxKind.TrueKeyword); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void TestIncompleteInitializer1() + { + UsingStatement(""" + for (MyType m = new() { A = 1,; true; m++) + """, + // (1,31): error CS1525: Invalid expression term ';' + // for (MyType m = new() { A = 1,; true; m++) + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ";").WithArguments(";").WithLocation(1, 31), + // (1,31): error CS1003: Syntax error, ',' expected + // for (MyType m = new() { A = 1,; true; m++) + Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(",").WithLocation(1, 31), + // (1,37): error CS1003: Syntax error, ',' expected + // for (MyType m = new() { A = 1,; true; m++) + Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(",").WithLocation(1, 37), + // (1,42): error CS1513: } expected + // for (MyType m = new() { A = 1,; true; m++) + Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(1, 42), + // (1,42): error CS1002: ; expected + // for (MyType m = new() { A = 1,; true; m++) + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(1, 42), + // (1,42): error CS1525: Invalid expression term ')' + // for (MyType m = new() { A = 1,; true; m++) + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(1, 42), + // (1,42): error CS1002: ; expected + // for (MyType m = new() { A = 1,; true; m++) + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(1, 42), + // (1,43): error CS1733: Expected expression + // for (MyType m = new() { A = 1,; true; m++) + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 43), + // (1,43): error CS1002: ; expected + // for (MyType m = new() { A = 1,; true; m++) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 43)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "MyType"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "m"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.ImplicitObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ObjectInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.TrueLiteralExpression); + { + N(SyntaxKind.TrueKeyword); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.PostIncrementExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "m"); + } + N(SyntaxKind.PlusPlusToken); + } + M(SyntaxKind.CloseBraceToken); + } + } + } + } + } + M(SyntaxKind.SemicolonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestIncompleteInitializer2() + { + UsingStatement(""" + for (MyType m = new() { A = 1, B; true; m++) + """, + // (1,33): error CS1003: Syntax error, ',' expected + // for (MyType m = new() { A = 1, B; true; m++) + Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(",").WithLocation(1, 33), + // (1,39): error CS1003: Syntax error, ',' expected + // for (MyType m = new() { A = 1, B; true; m++) + Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(",").WithLocation(1, 39), + // (1,44): error CS1513: } expected + // for (MyType m = new() { A = 1, B; true; m++) + Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(1, 44), + // (1,44): error CS1002: ; expected + // for (MyType m = new() { A = 1, B; true; m++) + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(1, 44), + // (1,44): error CS1525: Invalid expression term ')' + // for (MyType m = new() { A = 1, B; true; m++) + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(1, 44), + // (1,44): error CS1002: ; expected + // for (MyType m = new() { A = 1, B; true; m++) + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(1, 44), + // (1,45): error CS1733: Expected expression + // for (MyType m = new() { A = 1, B; true; m++) + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 45), + // (1,45): error CS1002: ; expected + // for (MyType m = new() { A = 1, B; true; m++) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 45)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "MyType"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "m"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.ImplicitObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ObjectInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.TrueLiteralExpression); + { + N(SyntaxKind.TrueKeyword); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.PostIncrementExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "m"); + } + N(SyntaxKind.PlusPlusToken); + } + M(SyntaxKind.CloseBraceToken); + } + } + } + } + } + M(SyntaxKind.SemicolonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestIncompleteInitializer3() + { + UsingStatement(""" + for (MyType m = new() { A = 1, B, ; true; m++) + """, + // (1,35): error CS1525: Invalid expression term ';' + // for (MyType m = new() { A = 1, B, ; true; m++) + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ";").WithArguments(";").WithLocation(1, 35), + // (1,35): error CS1003: Syntax error, ',' expected + // for (MyType m = new() { A = 1, B, ; true; m++) + Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(",").WithLocation(1, 35), + // (1,41): error CS1003: Syntax error, ',' expected + // for (MyType m = new() { A = 1, B, ; true; m++) + Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(",").WithLocation(1, 41), + // (1,46): error CS1513: } expected + // for (MyType m = new() { A = 1, B, ; true; m++) + Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(1, 46), + // (1,46): error CS1002: ; expected + // for (MyType m = new() { A = 1, B, ; true; m++) + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(1, 46), + // (1,46): error CS1525: Invalid expression term ')' + // for (MyType m = new() { A = 1, B, ; true; m++) + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(1, 46), + // (1,46): error CS1002: ; expected + // for (MyType m = new() { A = 1, B, ; true; m++) + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(1, 46), + // (1,47): error CS1733: Expected expression + // for (MyType m = new() { A = 1, B, ; true; m++) + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 47), + // (1,47): error CS1002: ; expected + // for (MyType m = new() { A = 1, B, ; true; m++) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 47)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "MyType"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "m"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.ImplicitObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ObjectInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.TrueLiteralExpression); + { + N(SyntaxKind.TrueKeyword); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.PostIncrementExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "m"); + } + N(SyntaxKind.PlusPlusToken); + } + M(SyntaxKind.CloseBraceToken); + } + } + } + } + } + M(SyntaxKind.SemicolonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestIncompleteInitializer4() + { + UsingStatement(""" + for (MyType m = new() { A = 1, B = ; true; m++) + """, + // (1,36): error CS1525: Invalid expression term ';' + // for (MyType m = new() { A = 1, B = ; true; m++) + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ";").WithArguments(";").WithLocation(1, 36), + // (1,36): error CS1003: Syntax error, ',' expected + // for (MyType m = new() { A = 1, B = ; true; m++) + Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(",").WithLocation(1, 36), + // (1,42): error CS1003: Syntax error, ',' expected + // for (MyType m = new() { A = 1, B = ; true; m++) + Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(",").WithLocation(1, 42), + // (1,47): error CS1513: } expected + // for (MyType m = new() { A = 1, B = ; true; m++) + Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(1, 47), + // (1,47): error CS1002: ; expected + // for (MyType m = new() { A = 1, B = ; true; m++) + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(1, 47), + // (1,47): error CS1525: Invalid expression term ')' + // for (MyType m = new() { A = 1, B = ; true; m++) + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(1, 47), + // (1,47): error CS1002: ; expected + // for (MyType m = new() { A = 1, B = ; true; m++) + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(1, 47), + // (1,48): error CS1733: Expected expression + // for (MyType m = new() { A = 1, B = ; true; m++) + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 48), + // (1,48): error CS1002: ; expected + // for (MyType m = new() { A = 1, B = ; true; m++) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 48)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "MyType"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "m"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.ImplicitObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ObjectInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + N(SyntaxKind.EqualsToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.TrueLiteralExpression); + { + N(SyntaxKind.TrueKeyword); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.PostIncrementExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "m"); + } + N(SyntaxKind.PlusPlusToken); + } + M(SyntaxKind.CloseBraceToken); + } + } + } + } + } + M(SyntaxKind.SemicolonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestIncompleteInitializer5() + { + UsingStatement(""" + for (MyType m = new() { A = 1, B = ,; true; m++) + """, + // (1,36): error CS1525: Invalid expression term ',' + // for (MyType m = new() { A = 1, B = ,; true; m++) + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ",").WithArguments(",").WithLocation(1, 36), + // (1,37): error CS1525: Invalid expression term ';' + // for (MyType m = new() { A = 1, B = ,; true; m++) + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ";").WithArguments(";").WithLocation(1, 37), + // (1,37): error CS1003: Syntax error, ',' expected + // for (MyType m = new() { A = 1, B = ,; true; m++) + Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(",").WithLocation(1, 37), + // (1,43): error CS1003: Syntax error, ',' expected + // for (MyType m = new() { A = 1, B = ,; true; m++) + Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(",").WithLocation(1, 43), + // (1,48): error CS1513: } expected + // for (MyType m = new() { A = 1, B = ,; true; m++) + Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(1, 48), + // (1,48): error CS1002: ; expected + // for (MyType m = new() { A = 1, B = ,; true; m++) + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(1, 48), + // (1,48): error CS1525: Invalid expression term ')' + // for (MyType m = new() { A = 1, B = ,; true; m++) + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(1, 48), + // (1,48): error CS1002: ; expected + // for (MyType m = new() { A = 1, B = ,; true; m++) + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(1, 48), + // (1,49): error CS1733: Expected expression + // for (MyType m = new() { A = 1, B = ,; true; m++) + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 49), + // (1,49): error CS1002: ; expected + // for (MyType m = new() { A = 1, B = ,; true; m++) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 49)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "MyType"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "m"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.ImplicitObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ObjectInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + N(SyntaxKind.EqualsToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.TrueLiteralExpression); + { + N(SyntaxKind.TrueKeyword); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.PostIncrementExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "m"); + } + N(SyntaxKind.PlusPlusToken); + } + M(SyntaxKind.CloseBraceToken); + } + } + } + } + } + M(SyntaxKind.SemicolonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestIncompleteInitializer6() + { + UsingStatement(""" + for (MyType m = new() { A = 1, B = 1,; true; m++) + """, + // (1,38): error CS1525: Invalid expression term ';' + // for (MyType m = new() { A = 1, B = 1,; true; m++) + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ";").WithArguments(";").WithLocation(1, 38), + // (1,38): error CS1003: Syntax error, ',' expected + // for (MyType m = new() { A = 1, B = 1,; true; m++) + Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(",").WithLocation(1, 38), + // (1,44): error CS1003: Syntax error, ',' expected + // for (MyType m = new() { A = 1, B = 1,; true; m++) + Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(",").WithLocation(1, 44), + // (1,49): error CS1513: } expected + // for (MyType m = new() { A = 1, B = 1,; true; m++) + Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(1, 49), + // (1,49): error CS1002: ; expected + // for (MyType m = new() { A = 1, B = 1,; true; m++) + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(1, 49), + // (1,49): error CS1525: Invalid expression term ')' + // for (MyType m = new() { A = 1, B = 1,; true; m++) + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(1, 49), + // (1,49): error CS1002: ; expected + // for (MyType m = new() { A = 1, B = 1,; true; m++) + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(1, 49), + // (1,50): error CS1733: Expected expression + // for (MyType m = new() { A = 1, B = 1,; true; m++) + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 50), + // (1,50): error CS1002: ; expected + // for (MyType m = new() { A = 1, B = 1,; true; m++) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 50)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "MyType"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "m"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.ImplicitObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ObjectInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.TrueLiteralExpression); + { + N(SyntaxKind.TrueKeyword); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.PostIncrementExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "m"); + } + N(SyntaxKind.PlusPlusToken); + } + M(SyntaxKind.CloseBraceToken); + } + } + } + } + } + M(SyntaxKind.SemicolonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + [Fact] + public void TestIncompleteWith1() + { + UsingStatement(""" + for (MyType m = x with { A = 1,; true; m++) + """, + // (1,32): error CS1513: } expected + // for (MyType m = x with { A = 1,; true; m++) + Diagnostic(ErrorCode.ERR_RbraceExpected, ";").WithLocation(1, 32), + // (1,44): error CS1733: Expected expression + // for (MyType m = x with { A = 1,; true; m++) + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 44), + // (1,44): error CS1002: ; expected + // for (MyType m = x with { A = 1,; true; m++) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 44)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "MyType"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "m"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.WithExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.WithKeyword); + N(SyntaxKind.WithInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.CloseBraceToken); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.TrueLiteralExpression); + { + N(SyntaxKind.TrueKeyword); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.PostIncrementExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "m"); + } + N(SyntaxKind.PlusPlusToken); + } + N(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestIncompleteWith2() + { + UsingStatement(""" + for (MyType m = x with { A = 1, B; true; m++) + """, + // (1,34): error CS1513: } expected + // for (MyType m = x with { A = 1, B; true; m++) + Diagnostic(ErrorCode.ERR_RbraceExpected, ";").WithLocation(1, 34), + // (1,46): error CS1733: Expected expression + // for (MyType m = x with { A = 1, B; true; m++) + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 46), + // (1,46): error CS1002: ; expected + // for (MyType m = x with { A = 1, B; true; m++) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 46)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "MyType"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "m"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.WithExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.WithKeyword); + N(SyntaxKind.WithInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + M(SyntaxKind.CloseBraceToken); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.TrueLiteralExpression); + { + N(SyntaxKind.TrueKeyword); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.PostIncrementExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "m"); + } + N(SyntaxKind.PlusPlusToken); + } + N(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestIncompleteWith3() + { + UsingStatement(""" + for (MyType m = x with { A = 1, B, ; true; m++) + """, + // (1,36): error CS1513: } expected + // for (MyType m = x with { A = 1, B, ; true; m++) + Diagnostic(ErrorCode.ERR_RbraceExpected, ";").WithLocation(1, 36), + // (1,48): error CS1733: Expected expression + // for (MyType m = x with { A = 1, B, ; true; m++) + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 48), + // (1,48): error CS1002: ; expected + // for (MyType m = x with { A = 1, B, ; true; m++) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 48)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "MyType"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "m"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.WithExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.WithKeyword); + N(SyntaxKind.WithInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.CloseBraceToken); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.TrueLiteralExpression); + { + N(SyntaxKind.TrueKeyword); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.PostIncrementExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "m"); + } + N(SyntaxKind.PlusPlusToken); + } + N(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestIncompleteWith4() + { + UsingStatement(""" + for (MyType m = x with { A = 1, B = ; true; m++) + """, + // (1,37): error CS1525: Invalid expression term ';' + // for (MyType m = x with { A = 1, B = ; true; m++) + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ";").WithArguments(";").WithLocation(1, 37), + // (1,37): error CS1513: } expected + // for (MyType m = x with { A = 1, B = ; true; m++) + Diagnostic(ErrorCode.ERR_RbraceExpected, ";").WithLocation(1, 37), + // (1,49): error CS1733: Expected expression + // for (MyType m = x with { A = 1, B = ; true; m++) + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 49), + // (1,49): error CS1002: ; expected + // for (MyType m = x with { A = 1, B = ; true; m++) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 49)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "MyType"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "m"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.WithExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.WithKeyword); + N(SyntaxKind.WithInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + N(SyntaxKind.EqualsToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.CloseBraceToken); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.TrueLiteralExpression); + { + N(SyntaxKind.TrueKeyword); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.PostIncrementExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "m"); + } + N(SyntaxKind.PlusPlusToken); + } + N(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestIncompleteWith5() + { + UsingStatement(""" + for (MyType m = x with { A = 1, B = ,; true; m++) + """, + // (1,37): error CS1525: Invalid expression term ',' + // for (MyType m = x with { A = 1, B = ,; true; m++) + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ",").WithArguments(",").WithLocation(1, 37), + // (1,38): error CS1513: } expected + // for (MyType m = x with { A = 1, B = ,; true; m++) + Diagnostic(ErrorCode.ERR_RbraceExpected, ";").WithLocation(1, 38), + // (1,50): error CS1733: Expected expression + // for (MyType m = x with { A = 1, B = ,; true; m++) + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 50), + // (1,50): error CS1002: ; expected + // for (MyType m = x with { A = 1, B = ,; true; m++) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 50)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "MyType"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "m"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.WithExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.WithKeyword); + N(SyntaxKind.WithInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + N(SyntaxKind.EqualsToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.CloseBraceToken); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.TrueLiteralExpression); + { + N(SyntaxKind.TrueKeyword); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.PostIncrementExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "m"); + } + N(SyntaxKind.PlusPlusToken); + } + N(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestIncompleteWith6() + { + UsingStatement(""" + for (MyType m = x with { A = 1, B = 1,; true; m++) + """, + // (1,39): error CS1513: } expected + // for (MyType m = x with { A = 1, B = 1,; true; m++) + Diagnostic(ErrorCode.ERR_RbraceExpected, ";").WithLocation(1, 39), + // (1,51): error CS1733: Expected expression + // for (MyType m = x with { A = 1, B = 1,; true; m++) + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 51), + // (1,51): error CS1002: ; expected + // for (MyType m = x with { A = 1, B = 1,; true; m++) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 51)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "MyType"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "m"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.WithExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.WithKeyword); + N(SyntaxKind.WithInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.CloseBraceToken); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.TrueLiteralExpression); + { + N(SyntaxKind.TrueKeyword); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.PostIncrementExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "m"); + } + N(SyntaxKind.PlusPlusToken); + } + N(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_AnonymousFunction() + { + UsingStatement(""" + for (delegate() {};delegate() {};delegate() {}); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.AnonymousMethodExpression); + { + N(SyntaxKind.DelegateKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.AnonymousMethodExpression); + { + N(SyntaxKind.DelegateKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.AnonymousMethodExpression); + { + N(SyntaxKind.DelegateKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_AnonymousObjectCreation() + { + UsingStatement(""" + for (new();new();new()); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.ImplicitObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.ImplicitObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.ImplicitObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_ArrayCreation() + { + UsingStatement(""" + for (new int[] { };new int[] { };new int[] { }); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.ArrayCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.ArrayInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.ArrayCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.ArrayInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.ArrayCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.ArrayInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_Assignment1() + { + UsingStatement(""" + for (a=1;a=1;a=1); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_Assignment2() + { + UsingStatement(""" + for (a+=1;a+=1;a+=1); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.AddAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.PlusEqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.AddAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.PlusEqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.AddAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.PlusEqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_Cast() + { + UsingStatement(""" + for ((int)0;(int)0;(int)0); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CastExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CastExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CastExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_Checked() + { + UsingStatement(""" + for (checked(0);checked(0);checked(0)); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CheckedExpression); + { + N(SyntaxKind.CheckedKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CheckedExpression); + { + N(SyntaxKind.CheckedKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CheckedExpression); + { + N(SyntaxKind.CheckedKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_Collection() + { + UsingStatement(""" + for ([];[];[]); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CollectionExpression); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.CloseBracketToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CollectionExpression); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.CloseBracketToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CollectionExpression); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.CloseBracketToken); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_ConditionalAccess() + { + UsingStatement(""" + for (a?.b;a?.b;a?.b); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.ConditionalAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.MemberBindingExpression); + { + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.ConditionalAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.MemberBindingExpression); + { + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.ConditionalAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.MemberBindingExpression); + { + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_DefaultExpression1() + { + UsingStatement(""" + for (default(int);default(int);default(int)); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.DefaultExpression); + { + N(SyntaxKind.DefaultKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.DefaultExpression); + { + N(SyntaxKind.DefaultKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.DefaultExpression); + { + N(SyntaxKind.DefaultKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_DefaultExpression2() + { + UsingStatement(""" + for (default;default;default); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.DefaultLiteralExpression); + { + N(SyntaxKind.DefaultKeyword); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.DefaultLiteralExpression); + { + N(SyntaxKind.DefaultKeyword); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.DefaultLiteralExpression); + { + N(SyntaxKind.DefaultKeyword); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_ElementAccess() + { + UsingStatement(""" + for (a[0];a[0];a[0]); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.ElementAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.ElementAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.ElementAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_ImplicitArrayCreation() + { + UsingStatement(""" + for (new[]{};new[]{};new[]{}); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.ImplicitArrayCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.CloseBracketToken); + N(SyntaxKind.ArrayInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.ImplicitArrayCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.CloseBracketToken); + N(SyntaxKind.ArrayInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.ImplicitArrayCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.CloseBracketToken); + N(SyntaxKind.ArrayInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_InterpolatedString() + { + UsingStatement(""" + for ($"";$"";$""); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.InterpolatedStringExpression); + { + N(SyntaxKind.InterpolatedStringStartToken); + N(SyntaxKind.InterpolatedStringEndToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.InterpolatedStringExpression); + { + N(SyntaxKind.InterpolatedStringStartToken); + N(SyntaxKind.InterpolatedStringEndToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.InterpolatedStringExpression); + { + N(SyntaxKind.InterpolatedStringStartToken); + N(SyntaxKind.InterpolatedStringEndToken); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_Invocation() + { + UsingStatement(""" + for (a();a();a()); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_IsPattern() + { + UsingStatement(""" + for (a is B b;a is B b;a is B b); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_Literal() + { + UsingStatement(""" + for (true;true;true); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TrueLiteralExpression); + { + N(SyntaxKind.TrueKeyword); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.TrueLiteralExpression); + { + N(SyntaxKind.TrueKeyword); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.TrueLiteralExpression); + { + N(SyntaxKind.TrueKeyword); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_MemberAccess() + { + UsingStatement(""" + for (a.b;a.b;a.b); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_Parenthesized() + { + UsingStatement(""" + for ((a);(a);(a)); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.ParenthesizedExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.ParenthesizedExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.ParenthesizedExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_Postfix() + { + UsingStatement(""" + for (a++;a++;a++); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.PostIncrementExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.PlusPlusToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.PostIncrementExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.PlusPlusToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.PostIncrementExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.PlusPlusToken); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_ObjectCreation1() + { + UsingStatement(""" + for (new A();new A();new A()); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.ObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.ObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.ObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_ObjectCreation2() + { + UsingStatement(""" + for (new A() { };new A() { };new A() { }); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.ObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ObjectInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.ObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ObjectInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.ObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ObjectInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_ObjectCreation3() + { + UsingStatement(""" + for (new A { };new A { };new A { }); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.ObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.ObjectInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.ObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.ObjectInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.ObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.ObjectInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_Prefix() + { + UsingStatement(""" + for (++a;++a;++a); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.PreIncrementExpression); + { + N(SyntaxKind.PlusPlusToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.PreIncrementExpression); + { + N(SyntaxKind.PlusPlusToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.PreIncrementExpression); + { + N(SyntaxKind.PlusPlusToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_Query() + { + UsingStatement(""" + for (from a in b select c;from a in b select c;from a in b select c); + """, + // (1,1): error CS1073: Unexpected token 'from' + // for (from a in b select c;from a in b select c;from a in b select c); + Diagnostic(ErrorCode.ERR_UnexpectedToken, "for (from a in b select c;").WithArguments("from").WithLocation(1, 1), + // (1,1): error CS1003: Syntax error, 'foreach' expected + // for (from a in b select c;from a in b select c;from a in b select c); + Diagnostic(ErrorCode.ERR_SyntaxError, "for").WithArguments("foreach").WithLocation(1, 1), + // (1,18): error CS1026: ) expected + // for (from a in b select c;from a in b select c;from a in b select c); + Diagnostic(ErrorCode.ERR_CloseParenExpected, "select").WithLocation(1, 18)); + + N(SyntaxKind.ForEachStatement); + { + M(SyntaxKind.ForEachKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "from"); + } + N(SyntaxKind.IdentifierToken, "a"); + N(SyntaxKind.InKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + M(SyntaxKind.CloseParenToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "select"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "c"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_Range1() + { + UsingStatement(""" + for (..;..;..); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.RangeExpression); + { + N(SyntaxKind.DotDotToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.RangeExpression); + { + N(SyntaxKind.DotDotToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.RangeExpression); + { + N(SyntaxKind.DotDotToken); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_Range2() + { + UsingStatement(""" + for (a..;a..;a..); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.RangeExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.DotDotToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.RangeExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.DotDotToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.RangeExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.DotDotToken); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_Range3() + { + UsingStatement(""" + for (..a;..a;..a); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.RangeExpression); + { + N(SyntaxKind.DotDotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.RangeExpression); + { + N(SyntaxKind.DotDotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.RangeExpression); + { + N(SyntaxKind.DotDotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_Range4() + { + UsingStatement(""" + for (a..a;a..a;a..a); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.RangeExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.DotDotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.RangeExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.DotDotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.RangeExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.DotDotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_Ref1() + { + UsingStatement(""" + for (ref a; ref a; ref a); + """, + // (1,11): error CS1001: Identifier expected + // for (ref a; ref a; ref a); + Diagnostic(ErrorCode.ERR_IdentifierExpected, ";").WithLocation(1, 11), + // (1,13): error CS1525: Invalid expression term 'ref' + // for (ref a; ref a; ref a); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref a").WithArguments("ref").WithLocation(1, 13), + // (1,20): error CS1525: Invalid expression term 'ref' + // for (ref a; ref a; ref a); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref a").WithArguments("ref").WithLocation(1, 20)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_Ref2() + { + UsingStatement(""" + for (ref int a; ref a; ref a); + """, + // (1,17): error CS1525: Invalid expression term 'ref' + // for (ref int a; ref a; ref a); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref a").WithArguments("ref").WithLocation(1, 17), + // (1,24): error CS1525: Invalid expression term 'ref' + // for (ref int a; ref a; ref a); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref a").WithArguments("ref").WithLocation(1, 24)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_Sizeof() + { + UsingStatement(""" + for (sizeof(a);sizeof(a);sizeof(a)); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.SizeOfExpression); + { + N(SyntaxKind.SizeOfKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.SizeOfExpression); + { + N(SyntaxKind.SizeOfKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.SizeOfExpression); + { + N(SyntaxKind.SizeOfKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_Switch() + { + UsingStatement(""" + for (a switch {};a switch {};a switch {}); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.SwitchExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.SwitchExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.SwitchExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_Throw() + { + UsingStatement(""" + for (throw a;throw a;throw a); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.ThrowExpression); + { + N(SyntaxKind.ThrowKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.ThrowExpression); + { + N(SyntaxKind.ThrowKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.ThrowExpression); + { + N(SyntaxKind.ThrowKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_Tuple() + { + UsingStatement(""" + for ((a, b);(a, b);(a, b)); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_Typeof() + { + UsingStatement(""" + for (typeof(int);typeof(int);typeof(int)); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TypeOfExpression); + { + N(SyntaxKind.TypeOfKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.TypeOfExpression); + { + N(SyntaxKind.TypeOfKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.TypeOfExpression); + { + N(SyntaxKind.TypeOfKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_With1() + { + UsingStatement(""" + for (a with { }; a with { }; a with { }) + { + } + """, + // (1,1): error CS1073: Unexpected token ';' + // for (a with { }; a with { }; a with { }) + Diagnostic(ErrorCode.ERR_UnexpectedToken, "for (a with { }").WithArguments(";").WithLocation(1, 1), + // (1,13): error CS1002: ; expected + // for (a with { }; a with { }; a with { }) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "{").WithLocation(1, 13), + // (1,13): error CS1525: Invalid expression term '{' + // for (a with { }; a with { }; a with { }) + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "{").WithArguments("{").WithLocation(1, 13), + // (1,13): error CS1002: ; expected + // for (a with { }; a with { }; a with { }) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "{").WithLocation(1, 13), + // (1,13): error CS1026: ) expected + // for (a with { }; a with { }; a with { }) + Diagnostic(ErrorCode.ERR_CloseParenExpected, "{").WithLocation(1, 13)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "with"); + } + } + M(SyntaxKind.SemicolonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + M(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void TestVariousExpressions_With2() + { + UsingStatement(""" + for (; a with { }; a with { }) + { + } + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.WithExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.WithKeyword); + N(SyntaxKind.WithInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.WithExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.WithKeyword); + N(SyntaxKind.WithInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void TestComplexInitializer1() + { + UsingStatement(""" + for (;;); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestComplexInitializer2() + { + UsingStatement(""" + for (int i;;); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "i"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestComplexInitializer3() + { + UsingStatement(""" + for (int i, j, k;;); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "i"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "j"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "k"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestComplexInitializer4() + { + UsingStatement(""" + for (int i = 0;;); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "i"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestComplexInitializer5() + { + UsingStatement(""" + for (A b;;); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestComplexInitializer6() + { + UsingStatement(""" + for (A b, c, d;;); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "d"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestComplexInitializer7() + { + UsingStatement(""" + for (A b = null, c, d = null;;); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "b"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "d"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestComplexInitializer8() + { + UsingStatement(""" + for (A b = c switch { A => x, _ => y };;); + """); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "b"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SwitchExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchExpressionArm); + { + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.SwitchExpressionArm); + { + N(SyntaxKind.DiscardPattern); + { + N(SyntaxKind.UnderscoreToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestComplexInitializer9() + { + UsingStatement(""" + for (int i =;;); + """, + // (1,13): error CS1525: Invalid expression term ';' + // for (int i =;;); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ";").WithArguments(";").WithLocation(1, 13)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "i"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestComplexInitializer10() + { + UsingStatement(""" + for (int i = 0, j =;;); + """, + // (1,20): error CS1525: Invalid expression term ';' + // for (int i = 0, j =;;); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ";").WithArguments(";").WithLocation(1, 20)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "i"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "j"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs index 0e8e91913e027..0528bc9c60098 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs @@ -844,7 +844,7 @@ internal static AnalyzerDriver CreateAndAttachToCompilation( { AnalyzerDriver analyzerDriver = compilation.CreateAnalyzerDriver(analyzers, analyzerManager, severityFilter); newCompilation = compilation - .WithSemanticModelProvider(new CachingSemanticModelProvider()) + .WithSemanticModelProvider(CachingSemanticModelProvider.Instance) .WithEventQueue(new AsyncQueue()); var categorizeDiagnostics = false; diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/CachingSemanticModelProvider.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/CachingSemanticModelProvider.cs index 573045bbf5733..4177c37847907 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/CachingSemanticModelProvider.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/CachingSemanticModelProvider.cs @@ -23,22 +23,26 @@ namespace Microsoft.CodeAnalysis.Diagnostics /// internal sealed class CachingSemanticModelProvider : SemanticModelProvider { + // Provide access to CachingSemanticModelProvider through a singleton. The inner CWT is static + // to avoid leak potential -- see https://github.com/dotnet/runtime/issues/12255. + // CachingSemanticModelProvider.s_providerCache -> PerCompilationProvider -> Compilation -> CachingSemanticModelProvider + public static CachingSemanticModelProvider Instance { get; } = new CachingSemanticModelProvider(); + private static readonly ConditionalWeakTable.CreateValueCallback s_createProviderCallback = new ConditionalWeakTable.CreateValueCallback(compilation => new PerCompilationProvider(compilation)); - private readonly ConditionalWeakTable _providerCache; + private static readonly ConditionalWeakTable s_providerCache = new ConditionalWeakTable(); - public CachingSemanticModelProvider() + private CachingSemanticModelProvider() { - _providerCache = new ConditionalWeakTable(); } public override SemanticModel GetSemanticModel(SyntaxTree tree, Compilation compilation, SemanticModelOptions options = default) - => _providerCache.GetValue(compilation, s_createProviderCallback).GetSemanticModel(tree, options); + => s_providerCache.GetValue(compilation, s_createProviderCallback).GetSemanticModel(tree, options); internal void ClearCache(SyntaxTree tree, Compilation compilation) { - if (_providerCache.TryGetValue(compilation, out var provider)) + if (s_providerCache.TryGetValue(compilation, out var provider)) { provider.ClearCachedSemanticModel(tree); } @@ -46,7 +50,7 @@ internal void ClearCache(SyntaxTree tree, Compilation compilation) internal void ClearCache(Compilation compilation) { - _providerCache.Remove(compilation); + s_providerCache.Remove(compilation); } private sealed class PerCompilationProvider diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs index 45e7418d5887a..67c47debe0336 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs @@ -97,7 +97,7 @@ public CompilationWithAnalyzers(Compilation compilation, ImmutableArray()); _compilation = compilation; _analyzers = analyzers; @@ -723,7 +723,7 @@ private async Task ComputeAnalyzerDiagnosticsAsync(AnalysisScope? analysisScope, // subsequently discard this compilation. var compilation = analysisScope.IsSingleFileAnalysisForCompilerAnalyzer ? _compilation - : _compilation.WithSemanticModelProvider(new CachingSemanticModelProvider()).WithEventQueue(new AsyncQueue()); + : _compilation.WithSemanticModelProvider(CachingSemanticModelProvider.Instance).WithEventQueue(new AsyncQueue()); // Get the analyzer driver to execute analysis. using var driver = await CreateAndInitializeDriverAsync(compilation, _analysisOptions, analysisScope, _suppressors, categorizeDiagnostics: true, cancellationToken).ConfigureAwait(false); @@ -1188,7 +1188,7 @@ private static IEnumerable GetEffectiveDiagnosticsImpl(ImmutableArra if (compilation.SemanticModelProvider == null) { - compilation = compilation.WithSemanticModelProvider(new CachingSemanticModelProvider()); + compilation = compilation.WithSemanticModelProvider(CachingSemanticModelProvider.Instance); } var suppressMessageState = new SuppressMessageAttributeState(compilation); diff --git a/src/Compilers/Test/Core/Traits/Traits.cs b/src/Compilers/Test/Core/Traits/Traits.cs index f15d2ed986d08..11d5b56217166 100644 --- a/src/Compilers/Test/Core/Traits/Traits.cs +++ b/src/Compilers/Test/Core/Traits/Traits.cs @@ -37,7 +37,7 @@ public static class Features public const string ChangeSignature = nameof(ChangeSignature); public const string ClassView = nameof(ClassView); public const string Classification = nameof(Classification); - public const string CodeActionsAddAccessibilityModifiers = "CodeActions.AddAccessibilityModifiers"; + public const string CodeActionsAddOrRemoveAccessibilityModifiers = "CodeActions.AddOrRemoveAccessibilityModifiers"; public const string CodeActionsAddAnonymousTypeMemberName = "CodeActions.AddAnonymousTypeMemberName"; public const string CodeActionsAddAwait = "CodeActions.AddAwait"; public const string CodeActionsAddBraces = "CodeActions.AddBraces"; diff --git a/src/EditorFeatures/CSharp/CodeCleanup/CSharpCodeCleanupService.cs b/src/EditorFeatures/CSharp/CodeCleanup/CSharpCodeCleanupService.cs index 083c1ca0978b4..f00c77eb4d622 100644 --- a/src/EditorFeatures/CSharp/CodeCleanup/CSharpCodeCleanupService.cs +++ b/src/EditorFeatures/CSharp/CodeCleanup/CSharpCodeCleanupService.cs @@ -36,7 +36,7 @@ internal class CSharpCodeCleanupService(ICodeFixService codeFixService, IDiagnos IDEDiagnosticIds.RemoveUnnecessaryParenthesesDiagnosticId, IDEDiagnosticIds.AddRequiredParenthesesDiagnosticId), new DiagnosticSet(AnalyzersResources.Add_accessibility_modifiers, - IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId), + IDEDiagnosticIds.AddOrRemoveAccessibilityModifiersDiagnosticId), new DiagnosticSet(FeaturesResources.Apply_coalesce_expression_preferences, IDEDiagnosticIds.UseCoalesceExpressionForTernaryConditionalCheckDiagnosticId), new DiagnosticSet(FeaturesResources.Apply_object_collection_initialization_preferences, diff --git a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler.cs b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler.cs index 1fa853da29253..877e0a9c72a7c 100644 --- a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler.cs +++ b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler.cs @@ -20,18 +20,14 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.RawStringLiteral; [Order(After = nameof(SplitStringLiteralCommandHandler))] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal partial class RawStringLiteralCommandHandler( +internal sealed partial class RawStringLiteralCommandHandler( ITextUndoHistoryRegistry undoHistoryRegistry, - IGlobalOptionService globalOptions, IEditorOperationsFactoryService editorOperationsFactoryService, - EditorOptionsService editorOptionsService, - IIndentationManagerService indentationManager) + EditorOptionsService editorOptionsService) { private readonly ITextUndoHistoryRegistry _undoHistoryRegistry = undoHistoryRegistry; - private readonly IGlobalOptionService _globalOptions = globalOptions; private readonly IEditorOperationsFactoryService _editorOperationsFactoryService = editorOperationsFactoryService; private readonly EditorOptionsService _editorOptionsService = editorOptionsService; - private readonly IIndentationManagerService _indentationManager = indentationManager; public string DisplayName => CSharpEditorResources.Split_raw_string; } diff --git a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs index 22c871fa7b1b8..6170f34428627 100644 --- a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs +++ b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs @@ -2,8 +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.Diagnostics; using System.Linq; -using System.Threading; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; @@ -14,7 +14,6 @@ using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Commanding; using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Editor.Commanding.Commands; using Roslyn.Utilities; @@ -31,6 +30,8 @@ public CommandState GetCommandState(ReturnKeyCommandArgs args) /// public bool ExecuteCommand(ReturnKeyCommandArgs args, CommandExecutionContext context) { + var cancellationToken = context.OperationContext.UserCancellationToken; + var textView = args.TextView; var subjectBuffer = args.SubjectBuffer; var spans = textView.Selection.GetSnapshotSpansOnBuffer(subjectBuffer); @@ -51,8 +52,6 @@ public bool ExecuteCommand(ReturnKeyCommandArgs args, CommandExecutionContext co if (position >= currentSnapshot.Length) return false; - var cancellationToken = context.OperationContext.UserCancellationToken; - var document = currentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); if (document == null) return false; @@ -77,6 +76,10 @@ bool ExecuteReturnCommandBeforeQuoteCharacter() quotesAfter++; } + // We must have at least one following quote, as we only got into ExecuteReturnCommandBeforeQuoteCharacter + // if there was a quote character in front of it. + Debug.Assert(quotesAfter > 0); + for (var i = position - 1; i >= 0; i--) { if (currentSnapshot[i] != '"') @@ -110,6 +113,14 @@ SyntaxKind.InterpolatedSingleLineRawStringStartToken or return false; } + if (!isEmpty) + { + // in the non empty case (e.g. `"""goo$$"""`) we have to make sure sure that the caret is before the + // final quotes, not the initial ones. + if (token.Span.End - quotesAfter != position) + return false; + } + return MakeEdit(parsedDocument, expression, isEmpty); } diff --git a/src/EditorFeatures/CSharpTest/CodeActions/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs index ed9be9de37571..aa38c36ea5655 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs @@ -4400,4 +4400,19 @@ public override int GetHashCode() LanguageVersion = LanguageVersion.Default, }.RunAsync(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76916")] + public async Task TestMissingWithPrimaryConstructorAndNoFields() + { + await new VerifyCS.Test + { + TestCode = """ + class C(int a) + { + [||] + } + """, + LanguageVersion = LanguageVersion.CSharp12, + }.RunAsync(); + } } diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs index 898774278f37c..199cf2081e86b 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.UnitTests; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Serialization; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Test.Utilities; @@ -775,6 +776,7 @@ private static async Task TestNuGetAndVsixAnalyzerCoreAsync( if (nugetAnalyzerReferences.Count > 0) { project = project.WithAnalyzerReferences([new AnalyzerImageReference([.. nugetAnalyzerReferences])]); + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences); } var document = project.Documents.Single(); diff --git a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs index f4c7b4efab924..8e6fd350f3e08 100644 --- a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs +++ b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs @@ -7998,6 +7998,44 @@ void N() NullabilityAnalysis(string.Format(FeaturesResources._0_is_not_null_here, "s"))); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77219")] + public async Task NullableBackingFieldThatIsMaybeNull() + { + await TestWithOptionsAsync(TestOptions.RegularPreview, + """ + #nullable enable + + class X + { + string? P + { + get => $$field; + } + } + """, + MainDescription($"({FeaturesResources.field}) string? X.P.field"), + NullabilityAnalysis(string.Format(FeaturesResources._0_may_be_null_here, "P.field"))); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77219")] + public async Task NullableBackingFieldThatIsNotNull() + { + await TestWithOptionsAsync(TestOptions.RegularPreview, + """ + #nullable enable + + class X + { + string P + { + get => $$field; + } = "a"; + } + """, + MainDescription($"({FeaturesResources.field}) string X.P.field"), + NullabilityAnalysis(string.Format(FeaturesResources._0_is_not_null_here, "P.field"))); + } + [Fact] public async Task NullablePropertyThatIsMaybeNull() { diff --git a/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs b/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs index c91609f2d7a65..7a65e619c7374 100644 --- a/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs +++ b/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.RawStringLiteral; [UseExportProvider] -public class RawStringLiteralCommandHandlerTests +public sealed class RawStringLiteralCommandHandlerTests { internal sealed class RawStringLiteralTestState : AbstractCommandHandlerTestState { @@ -520,6 +520,48 @@ public void TestReturnWithinEndQuotesInMultilineRawString() testState.SendReturn(handled: false); } + [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/76773")] + public void TestReturnPriorToStartingQuotes1() + { + using var testState = RawStringLiteralTestState.CreateTestState( + """" + var v = Goo($$""" + bar); + """ + """"); + + // Should not handle this as we're not inside the raw string. + testState.SendReturn(handled: false); + } + + [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/76773")] + public void TestReturnPriorToStartingQuotes2() + { + using var testState = RawStringLiteralTestState.CreateTestState( + """" + var v = Goo("$$"" + bar); + """ + """"); + + // Should not handle this as we're not inside the raw string. + testState.SendReturn(handled: false); + } + + [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/76773")] + public void TestReturnPriorToStartingQuotes3() + { + using var testState = RawStringLiteralTestState.CreateTestState( + """" + var v = Goo(""$$" + bar); + """ + """"); + + // Should not handle this as we're not inside the raw string. + testState.SendReturn(handled: false); + } + #endregion #region generate initial empty raw string diff --git a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs index f449f7978a0af..149dd06940b05 100644 --- a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs +++ b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs @@ -18,6 +18,7 @@ using Microsoft.CodeAnalysis.Extensions; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Serialization; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Test.Utilities; @@ -429,7 +430,8 @@ public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) } } - private class MockAnalyzerReference : AnalyzerReference, ICodeFixProviderFactory + private class MockAnalyzerReference + : AnalyzerReference, ICodeFixProviderFactory, SerializerService.TestAccessor.IAnalyzerReferenceWithGuid { public readonly ImmutableArray Fixers; public readonly ImmutableArray Analyzers; @@ -490,6 +492,8 @@ public override object Id } } + public Guid Guid { get; } = Guid.NewGuid(); + public override ImmutableArray GetAnalyzers(string language) => Analyzers; diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index 6b141120ee330..db5836abe05f2 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -17,6 +17,7 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote.Diagnostics; using Microsoft.CodeAnalysis.Remote.Testing; +using Microsoft.CodeAnalysis.Serialization; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.SolutionCrawler; @@ -435,6 +436,7 @@ internal async Task TestAdditionalFileAnalyzer(bool registerFromInitialize, bool project = project.AddAdditionalDocument(name: "dummy2.txt", text: "Additional File2 Text", filePath: "dummy2.txt").Project; } + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences); var applied = workspace.TryApplyChanges(project.Solution); Assert.True(applied); diff --git a/src/LanguageServer/ProtocolUnitTests/VSTypeScriptHandlerTests.cs b/src/EditorFeatures/Test/LanguageServer/VSTypeScriptHandlerTests.cs similarity index 65% rename from src/LanguageServer/ProtocolUnitTests/VSTypeScriptHandlerTests.cs rename to src/EditorFeatures/Test/LanguageServer/VSTypeScriptHandlerTests.cs index 043c8670f2733..ddc84b2b89489 100644 --- a/src/LanguageServer/ProtocolUnitTests/VSTypeScriptHandlerTests.cs +++ b/src/EditorFeatures/Test/LanguageServer/VSTypeScriptHandlerTests.cs @@ -3,33 +3,39 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Immutable; +using System.Collections.Generic; using System.Composition; using System.IO; using System.Linq; +using System.ServiceModel.Syndication; using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; using Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript; using Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Test.Utilities; -using Roslyn.LanguageServer.Protocol; +using Microsoft.CommonLanguageServerProtocol.Framework; using Nerdbank.Streams; +using Roslyn.LanguageServer.Protocol; using Roslyn.Test.Utilities; using StreamJsonRpc; using Xunit; using Xunit.Abstractions; -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() @@ -88,43 +94,50 @@ public async Task TestGetSimplifierOptionsOnTypeScriptDocument() Assert.Same(SimplifierOptions.CommonDefaults, simplifierOptions); } - private async Task CreateTsTestLspServerAsync(string workspaceXml, InitializationOptions? options = null) + private async Task CreateTsTestLspServerAsync(string workspaceXml, InitializationOptions? options = null) { - var (clientStream, serverStream) = FullDuplexStream.CreatePair(); - var testWorkspace = CreateWorkspace(options, mutatingLspWorkspace: false, workspaceKind: null); testWorkspace.InitializeDocuments(XElement.Parse(workspaceXml), openDocuments: false); - // Ensure workspace operations are completed so we don't get unexpected workspace changes while running. - await WaitForWorkspaceOperationsAsync(testWorkspace); - var languageServerTarget = CreateLanguageServer(serverStream, serverStream, testWorkspace); - - return await TestLspServer.CreateAsync(testWorkspace, new ClientCapabilities(), languageServerTarget, clientStream); + return await VSTypeScriptTestLspServer.CreateAsync(testWorkspace, new InitializationOptions(), TestOutputLspLogger); } - private static RoslynLanguageServer CreateLanguageServer(Stream inputStream, Stream outputStream, EditorTestWorkspace workspace) + private class VSTypeScriptTestLspServer : AbstractTestLspServer { - var capabilitiesProvider = workspace.ExportProvider.GetExportedValue(); - var servicesProvider = workspace.ExportProvider.GetExportedValue(); - - var messageFormatter = RoslynLanguageServer.CreateJsonMessageFormatter(); - var jsonRpc = new JsonRpc(new HeaderDelimitedMessageHandler(outputStream, inputStream, messageFormatter)) + public VSTypeScriptTestLspServer(LspTestWorkspace testWorkspace, Dictionary> locations, InitializationOptions options, AbstractLspLogger logger) : base(testWorkspace, locations, options, logger) { - ExceptionStrategy = ExceptionProcessing.ISerializable, - }; - - var logger = NoOpLspLogger.Instance; + } - var languageServer = new RoslynLanguageServer( - servicesProvider, jsonRpc, messageFormatter.JsonSerializerOptions, - capabilitiesProvider, - logger, - workspace.Services.HostServices, - [InternalLanguageNames.TypeScript], - WellKnownLspServerKinds.RoslynTypeScriptLspServer); + protected override RoslynLanguageServer CreateLanguageServer(Stream inputStream, Stream outputStream, WellKnownLspServerKinds serverKind, AbstractLspLogger logger) + { + var capabilitiesProvider = TestWorkspace.ExportProvider.GetExportedValue(); + var servicesProvider = TestWorkspace.ExportProvider.GetExportedValue(); + + var messageFormatter = RoslynLanguageServer.CreateJsonMessageFormatter(); + var jsonRpc = new JsonRpc(new HeaderDelimitedMessageHandler(outputStream, inputStream, messageFormatter)) + { + ExceptionStrategy = ExceptionProcessing.ISerializable, + }; + + var languageServer = new RoslynLanguageServer( + servicesProvider, jsonRpc, messageFormatter.JsonSerializerOptions, + capabilitiesProvider, + logger, + TestWorkspace.Services.HostServices, + [InternalLanguageNames.TypeScript], + WellKnownLspServerKinds.RoslynTypeScriptLspServer); + + jsonRpc.StartListening(); + return languageServer; + } - jsonRpc.StartListening(); - return languageServer; + public static async Task CreateAsync(LspTestWorkspace testWorkspace, InitializationOptions options, AbstractLspLogger logger) + { + var locations = await GetAnnotatedLocationsAsync(testWorkspace, testWorkspace.CurrentSolution); + var server = new VSTypeScriptTestLspServer(testWorkspace, locations, options, logger); + await server.InitializeAsync(); + return server; + } } internal record TSRequest(Uri Document, string Project); diff --git a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb index c52637d2bcb8e..9913c17cfea6b 100644 --- a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb @@ -13,6 +13,7 @@ Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.Diagnostics.CSharp Imports Microsoft.CodeAnalysis.Editor.UnitTests Imports Microsoft.CodeAnalysis.Host.Mef +Imports Microsoft.CodeAnalysis.Serialization Imports Microsoft.CodeAnalysis.SolutionCrawler Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.UnitTests.Diagnostics @@ -104,6 +105,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim projectAnalyzerReference1 = New AnalyzerImageReference(projectAnalyzers1, display:=NameOf(projectAnalyzers1)) Dim projectAnalyzerReferences1 = ImmutableArray.Create(Of AnalyzerReference)(projectAnalyzerReference1) project = project.WithAnalyzerReferences(projectAnalyzerReferences1) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) ' Verify available diagnostic descriptors/analyzers descriptorsMap = hostAnalyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) @@ -119,6 +121,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim projectAnalyzers2 = ImmutableArray.Create(Of DiagnosticAnalyzer)(projectDiagnosticAnalyzer2) Dim projectAnalyzerReference2 = New AnalyzerImageReference(projectAnalyzers2, display:=NameOf(projectAnalyzers2)) project = project.AddAnalyzerReference(projectAnalyzerReference2) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) ' Verify available diagnostic descriptors/analyzers descriptorsMap = hostAnalyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) @@ -332,10 +335,12 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim p1 = solution.Projects.Single(Function(p) p.Language = LanguageNames.CSharp) p1 = p1.WithAnalyzerReferences(SpecializedCollections.SingletonCollection(New AnalyzerImageReference(ImmutableArray.Create(analyzer1)))) solution = p1.Solution + SerializerService.TestAccessor.AddAnalyzerImageReferences(p1.AnalyzerReferences) Dim p2 = solution.Projects.Single(Function(p) p.Language = LanguageNames.VisualBasic) p2 = p2.WithAnalyzerReferences(SpecializedCollections.SingletonCollection(New AnalyzerImageReference(ImmutableArray.Create(analyzer2)))) solution = p2.Solution + SerializerService.TestAccessor.AddAnalyzerImageReferences(p2.AnalyzerReferences) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() @@ -478,6 +483,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim analyzer = New ThrowsExceptionAnalyzer Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() @@ -512,6 +518,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim analyzer = New CodeBlockStartedAnalyzer(Of Microsoft.CodeAnalysis.CSharp.SyntaxKind) Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() @@ -589,6 +596,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim analyzer = New OperationAnalyzer(actionKind) Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) Assert.Equal(1, descriptorsMap.Count) @@ -622,6 +630,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim analyzer = New CodeBlockEndedAnalyzer() Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() @@ -655,6 +664,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim analyzer = New CodeBlockStartedAndEndedAnalyzer(Of Microsoft.CodeAnalysis.CSharp.SyntaxKind) Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() @@ -730,6 +740,7 @@ class AnonymousFunctions Dim analyzer = New CodeBlockStartedAndEndedAnalyzer(Of Microsoft.CodeAnalysis.CSharp.SyntaxKind) Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() @@ -765,6 +776,7 @@ class AnonymousFunctions Dim analyzer = New CompilationEndedAnalyzer Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() @@ -825,6 +837,7 @@ class AnonymousFunctions Dim analyzer = New StatefulCompilationAnalyzer Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) Dim projectDiagnostics = Await DiagnosticProviderTestUtilities.GetProjectDiagnosticsAsync(workspace, project) Assert.Equal(1, projectDiagnostics.Count()) @@ -850,6 +863,7 @@ class AnonymousFunctions Dim analyzer = New StatefulCompilationAnalyzer Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) ' Make couple of dummy invocations to GetDocumentDiagnostics. Dim document = project.Documents.Single() @@ -889,6 +903,7 @@ class AnonymousFunctions Dim analyzer = New StatefulCompilationAnalyzer Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) Dim projectDiagnostics = Await DiagnosticProviderTestUtilities.GetProjectDiagnosticsAsync(workspace, project) @@ -924,6 +939,8 @@ class AnonymousFunctions Dim analyzer = New NamedTypeAnalyzer Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) + Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() @@ -962,6 +979,7 @@ class AnonymousFunctions Dim analyzer = New PartialTypeDiagnosticAnalyzer(indexOfDeclToReportDiagnostic:=1) Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() @@ -1017,6 +1035,7 @@ class AnonymousFunctions ' Test partial type diagnostic reported on all source files. Dim analyzerReference = New AnalyzerImageReference(analyzers) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() @@ -1068,6 +1087,7 @@ public class B Dim analyzer As DiagnosticAnalyzer = New CodeBlockOrSyntaxNodeAnalyzer(isCodeBlockAnalyzer:=True) Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(analyzer)) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() @@ -1110,6 +1130,7 @@ public class B Dim analyzer As DiagnosticAnalyzer = New CodeBlockOrSyntaxNodeAnalyzer(isCodeBlockAnalyzer:=False) Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(analyzer)) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() @@ -1152,6 +1173,7 @@ public class B Dim analyzer As DiagnosticAnalyzer = New MethodSymbolAnalyzer Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(analyzer)) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() @@ -1202,6 +1224,7 @@ End Class Dim analyzer = New MustOverrideMethodAnalyzer() Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() @@ -1264,6 +1287,7 @@ public class B Dim analyzer = New FieldDeclarationAnalyzer() Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() @@ -1310,6 +1334,7 @@ public class B Dim analyzer = New FieldDeclarationAnalyzer() Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() @@ -1375,6 +1400,7 @@ public class B Dim analyzer = New CompilationAnalyzerWithAnalyzerOptions() Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) ' Add additional document Dim additionalDocText = "First" @@ -1932,6 +1958,7 @@ End Class Dim analyzer = New CodeBlockActionAnalyzer(onlyStatelessAction) Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() @@ -1991,6 +2018,7 @@ namespace ConsoleApplication1 ' Add analyzer Dim analyzerReference = New AnalyzerImageReference(analyzers.ToImmutableArray()) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() @@ -2057,6 +2085,7 @@ class MyClass Dim analyzer = New AnalyzerWithNoSupportedDiagnostics() Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() @@ -2095,6 +2124,7 @@ class MyClass Dim analyzer = New CompilationAnalyzerWithSeverity(DiagnosticSeverity.Hidden, configurable:=False) Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() @@ -2148,6 +2178,7 @@ class C Dim analyzer = DiagnosticExtensions.GetCompilerDiagnosticAnalyzer(LanguageNames.CSharp) Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(analyzer)) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) ' Get span to analyze Dim document = project.Documents.Single() @@ -2194,6 +2225,7 @@ class C Dim analyzer = New EnsureNoMergedNamespaceSymbolAnalyzer() Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() @@ -2238,6 +2270,7 @@ class MyClass Dim compilerAnalyzer = New CSharpCompilerDiagnosticAnalyzer() Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(compilerAnalyzer, syntaxAnalyzer, semanticAnalyzer)) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() @@ -2307,6 +2340,7 @@ class MyClass Dim compilerAnalyzer = New CSharpCompilerDiagnosticAnalyzer() Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(compilerAnalyzer, syntaxAnalyzer, semanticAnalyzer)) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() @@ -2396,6 +2430,7 @@ public class C Dim analyzer = New AllActionsAnalyzer() Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)) project = project.AddAnalyzerReference(analyzerReference) + SerializerService.TestAccessor.AddAnalyzerImageReferences(project.AnalyzerReferences) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) Dim diagnosticService = workspace.GetService(Of IDiagnosticAnalyzerService)() 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/EditorFeatures/VisualBasic/CodeCleanup/VisualBasicCodeCleanupService.vb b/src/EditorFeatures/VisualBasic/CodeCleanup/VisualBasicCodeCleanupService.vb index fee5d7f6359b2..c23580a6824f3 100644 --- a/src/EditorFeatures/VisualBasic/CodeCleanup/VisualBasicCodeCleanupService.vb +++ b/src/EditorFeatures/VisualBasic/CodeCleanup/VisualBasicCodeCleanupService.vb @@ -30,7 +30,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeCleanup New DiagnosticSet(FeaturesResources.Apply_parentheses_preferences, IDEDiagnosticIds.RemoveUnnecessaryParenthesesDiagnosticId, IDEDiagnosticIds.AddRequiredParenthesesDiagnosticId), New DiagnosticSet(AnalyzersResources.Add_accessibility_modifiers, - IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId), + IDEDiagnosticIds.AddOrRemoveAccessibilityModifiersDiagnosticId), New DiagnosticSet(FeaturesResources.Apply_coalesce_expression_preferences, IDEDiagnosticIds.UseCoalesceExpressionForTernaryConditionalCheckDiagnosticId), New DiagnosticSet(FeaturesResources.Apply_object_collection_initialization_preferences, diff --git a/src/EditorFeatures/VisualBasicTest/Formatting/CodeCleanUpTests.vb b/src/EditorFeatures/VisualBasicTest/Formatting/CodeCleanUpTests.vb index e2eff36a2a1c4..123c74f17b5b5 100644 --- a/src/EditorFeatures/VisualBasicTest/Formatting/CodeCleanUpTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Formatting/CodeCleanUpTests.vb @@ -215,7 +215,7 @@ End Class End Function - Public Function VisualBasicAddAccessibilityModifiers() As Task + Public Function VisualBasicAddOrRemoveAccessibilityModifiers() As Task Dim code As String = "Class Program Public Shared Sub Method() Console.WriteLine(""Hello"") diff --git a/src/Features/CSharp/Portable/CodeRefactorings/EnableNullable/EnableNullableCodeRefactoringProvider.FixAllProvider.cs b/src/Features/CSharp/Portable/CodeRefactorings/EnableNullable/EnableNullableCodeRefactoringProvider.FixAllProvider.cs index 238b2127dab7c..284bc6945b647 100644 --- a/src/Features/CSharp/Portable/CodeRefactorings/EnableNullable/EnableNullableCodeRefactoringProvider.FixAllProvider.cs +++ b/src/Features/CSharp/Portable/CodeRefactorings/EnableNullable/EnableNullableCodeRefactoringProvider.FixAllProvider.cs @@ -61,14 +61,11 @@ private sealed class FixAllCodeAction(Func, CancellationToken, Task> _createChangedSolution = createChangedSolution; - protected override async Task> ComputePreviewOperationsAsync(CancellationToken cancellationToken) + protected override async Task> ComputeOperationsAsync(IProgress progress, CancellationToken cancellationToken) { var changedSolution = await _createChangedSolution( - CodeActionPurpose.Preview, CodeAnalysisProgress.None, cancellationToken).ConfigureAwait(false); - if (changedSolution is null) - return []; - - return new CodeActionOperation[] { new ApplyChangesOperation(changedSolution) }; + CodeActionPurpose.Preview, progress, cancellationToken).ConfigureAwait(false); + return changedSolution is null ? [] : [new ApplyChangesOperation(changedSolution)]; } } } diff --git a/src/Features/CSharp/Portable/CodeRefactorings/MoveType/CSharpMoveTypeService.cs b/src/Features/CSharp/Portable/CodeRefactorings/MoveType/CSharpMoveTypeService.cs index d59700a5386cd..429d77ba862fe 100644 --- a/src/Features/CSharp/Portable/CodeRefactorings/MoveType/CSharpMoveTypeService.cs +++ b/src/Features/CSharp/Portable/CodeRefactorings/MoveType/CSharpMoveTypeService.cs @@ -20,6 +20,9 @@ namespace Microsoft.CodeAnalysis.CSharp.CodeRefactorings.MoveType; internal sealed class CSharpMoveTypeService() : AbstractMoveTypeService { + protected override (string name, int arity) GetSymbolNameAndArity(BaseTypeDeclarationSyntax syntax) + => (syntax.Identifier.ValueText, syntax is TypeDeclarationSyntax { TypeParameterList.Parameters.Count: var arity } ? arity : 0); + protected override bool IsMemberDeclaration(SyntaxNode syntaxNode) => syntaxNode is MemberDeclarationSyntax; diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/SnippetCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/SnippetCompletionProvider.cs index 6df55c5e6ee55..a7b7a5ce93764 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/SnippetCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/SnippetCompletionProvider.cs @@ -51,6 +51,7 @@ internal sealed class SnippetCompletionProvider : LSPCompletionProvider CSharpSnippetIdentifiers.StaticIntMain, CSharpSnippetIdentifiers.Struct, CSharpSnippetIdentifiers.StaticVoidMain, + CSharpSnippetIdentifiers.Using, CSharpSnippetIdentifiers.While ]; diff --git a/src/Features/CSharp/Portable/ConvertToInterpolatedString/CSharpConvertPlaceholderToInterpolatedStringRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertToInterpolatedString/CSharpConvertPlaceholderToInterpolatedStringRefactoringProvider.cs index 60a3d2ce75ce8..f28948a0c3fbc 100644 --- a/src/Features/CSharp/Portable/ConvertToInterpolatedString/CSharpConvertPlaceholderToInterpolatedStringRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertToInterpolatedString/CSharpConvertPlaceholderToInterpolatedStringRefactoringProvider.cs @@ -11,7 +11,9 @@ namespace Microsoft.CodeAnalysis.CSharp.ConvertToInterpolatedString; [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.ConvertPlaceholderToInterpolatedString), Shared] -internal sealed partial class CSharpConvertPlaceholderToInterpolatedStringRefactoringProvider : +[method: ImportingConstructor] +[method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] +internal sealed partial class CSharpConvertPlaceholderToInterpolatedStringRefactoringProvider() : AbstractConvertPlaceholderToInterpolatedStringRefactoringProvider< ExpressionSyntax, LiteralExpressionSyntax, @@ -21,12 +23,6 @@ internal sealed partial class CSharpConvertPlaceholderToInterpolatedStringRefact ArgumentListSyntax, InterpolationSyntax> { - [ImportingConstructor] - [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] - public CSharpConvertPlaceholderToInterpolatedStringRefactoringProvider() - { - } - protected override ExpressionSyntax ParseExpression(string text) => SyntaxFactory.ParseExpression(text); } diff --git a/src/Features/CSharp/Portable/Formatting/CSharpAccessibilityModifiersNewDocumentFormattingProvider.cs b/src/Features/CSharp/Portable/Formatting/CSharpAccessibilityModifiersNewDocumentFormattingProvider.cs index e43d6fbe50178..212256cebcce8 100644 --- a/src/Features/CSharp/Portable/Formatting/CSharpAccessibilityModifiersNewDocumentFormattingProvider.cs +++ b/src/Features/CSharp/Portable/Formatting/CSharpAccessibilityModifiersNewDocumentFormattingProvider.cs @@ -7,7 +7,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.AddAccessibilityModifiers; +using Microsoft.CodeAnalysis.AddOrRemoveAccessibilityModifiers; using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.LanguageService; @@ -41,7 +41,7 @@ public async Task FormatNewDocumentAsync(Document document, Document? var typeDeclarations = root.DescendantNodes().Where(node => syntaxFacts.IsTypeDeclaration(node)); var editor = new SyntaxEditor(root, document.Project.Solution.Services); - var service = document.GetRequiredLanguageService(); + var service = document.GetRequiredLanguageService(); foreach (var declaration in typeDeclarations) { @@ -69,7 +69,7 @@ public async Task FormatNewDocumentAsync(Document document, Document? if (type == null) continue; - AddAccessibilityModifiersHelpers.UpdateDeclaration(editor, type, declaration); + AddOrRemoveAccessibilityModifiersHelpers.UpdateDeclaration(editor, type, declaration); } return document.WithSyntaxRoot(editor.GetChangedRoot()); diff --git a/src/Features/CSharp/Portable/Snippets/CSharpSnippetIdentifiers.cs b/src/Features/CSharp/Portable/Snippets/CSharpSnippetIdentifiers.cs index 406a380e2ca8a..0631779f5892a 100644 --- a/src/Features/CSharp/Portable/Snippets/CSharpSnippetIdentifiers.cs +++ b/src/Features/CSharp/Portable/Snippets/CSharpSnippetIdentifiers.cs @@ -25,5 +25,6 @@ internal static class CSharpSnippetIdentifiers public const string StaticIntMain = "sim"; public const string Struct = "struct"; public const string StaticVoidMain = "svm"; + public const string Using = "using"; public const string While = "while"; } diff --git a/src/Features/CSharp/Portable/Snippets/CSharpUsingSnippetProvider.cs b/src/Features/CSharp/Portable/Snippets/CSharpUsingSnippetProvider.cs new file mode 100644 index 0000000000000..65f148c6f587e --- /dev/null +++ b/src/Features/CSharp/Portable/Snippets/CSharpUsingSnippetProvider.cs @@ -0,0 +1,46 @@ +// 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.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.Snippets; +using Microsoft.CodeAnalysis.Snippets.SnippetProviders; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.CSharp.Snippets; + +[ExportSnippetProvider(nameof(ISnippetProvider), LanguageNames.CSharp), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CSharpUsingSnippetProvider() : AbstractUsingSnippetProvider +{ + public override string Identifier => CSharpSnippetIdentifiers.Using; + + public override string Description => CSharpFeaturesResources.using_statement; + + protected override ImmutableArray GetPlaceHolderLocationsList(UsingStatementSyntax node, ISyntaxFacts syntaxFacts, CancellationToken cancellationToken) + { + var expression = node.Expression!; + return [new SnippetPlaceholder(expression.ToString(), expression.SpanStart)]; + } + + protected override int GetTargetCaretPosition(UsingStatementSyntax usingStatement, SourceText sourceText) + => CSharpSnippetHelpers.GetTargetCaretPositionInBlock( + usingStatement, + static s => (BlockSyntax)s.Statement, + sourceText); + + protected override Task AddIndentationToDocumentAsync(Document document, UsingStatementSyntax usingStatement, CancellationToken cancellationToken) + => CSharpSnippetHelpers.AddBlockIndentationToDocumentAsync( + document, + usingStatement, + static s => (BlockSyntax)s.Statement, + cancellationToken); +} diff --git a/src/Features/CSharpTest/ConvertToInterpolatedString/ConvertPlaceholderToInterpolatedStringTests.cs b/src/Features/CSharpTest/ConvertToInterpolatedString/ConvertPlaceholderToInterpolatedStringTests.cs index f91442f8ca011..394d5834bb4e9 100644 --- a/src/Features/CSharpTest/ConvertToInterpolatedString/ConvertPlaceholderToInterpolatedStringTests.cs +++ b/src/Features/CSharpTest/ConvertToInterpolatedString/ConvertPlaceholderToInterpolatedStringTests.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeRefactorings; @@ -15,12 +16,12 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertToInterpolatedString; [Trait(Traits.Feature, Traits.Features.CodeActionsConvertToInterpolatedString)] -public class ConvertPlaceholderToInterpolatedStringTests : AbstractCSharpCodeActionTest_NoEditor +public sealed class ConvertPlaceholderToInterpolatedStringTests : AbstractCSharpCodeActionTest_NoEditor { protected override CodeRefactoringProvider CreateCodeRefactoringProvider(TestWorkspace workspace, TestParameters parameters) => new CSharpConvertPlaceholderToInterpolatedStringRefactoringProvider(); - private static readonly string[] CompositeFormattedMethods = + private static readonly ImmutableArray CompositeFormattedMethods = [ "Console.Write", "Console.WriteLine", @@ -81,8 +82,7 @@ static string MakeFormattedParameters(int numberOfParameters) } } - [Theory] - [MemberData(nameof(InvocationData))] + [Theory, MemberData(nameof(InvocationData))] public async Task TestInvocationSubstitution(string before, string after) { await TestInRegularAndScriptAsync( @@ -1187,4 +1187,37 @@ void M() } """); } + + [Theory, MemberData(nameof(InvocationData))] + [WorkItem("https://github.com/dotnet/roslyn/issues/68469")] + public async Task TestInvocationSubstitution_FixAll(string before, string after) + { + await TestInRegularAndScriptAsync( + $$""" + using System; + using System.Diagnostics; + + class T + { + void M() + { + {|FixAllInDocument:{{before}}|}; + {{before}}; + } + } + """, + $$""" + using System; + using System.Diagnostics; + + class T + { + void M() + { + {{after}}; + {{after}}; + } + } + """); + } } diff --git a/src/Features/CSharpTest/Snippets/CSharpUsingSnippetProviderTests.cs b/src/Features/CSharpTest/Snippets/CSharpUsingSnippetProviderTests.cs new file mode 100644 index 0000000000000..3cc449347bab4 --- /dev/null +++ b/src/Features/CSharpTest/Snippets/CSharpUsingSnippetProviderTests.cs @@ -0,0 +1,202 @@ +// 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.Tasks; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Snippets; + +[Trait(Traits.Feature, Traits.Features.Snippets)] +public sealed class CSharpUsingSnippetProviderTests : AbstractCSharpSnippetProviderTests +{ + protected override string SnippetIdentifier => "using"; + + [Fact] + public async Task InsertUsingSnippetInMethodTest() + { + await VerifySnippetAsync(""" + class Program + { + public void Method() + { + $$ + } + } + """, """ + class Program + { + public void Method() + { + using ({|0:resource|}) + { + $$ + } + } + } + """); + } + + [Fact] + public async Task InsertUsingSnippetInGlobalContextTest() + { + await VerifySnippetAsync(""" + $$ + """, """ + using ({|0:resource|}) + { + $$ + } + """); + } + + [Fact] + public async Task NoUsingSnippetInBusingNamespaceTest() + { + await VerifySnippetIsAbsentAsync(""" + namespace Namespace + { + $$ + } + """); + } + + [Fact] + public async Task NoUsingSnippetInFileScopedNamespaceTest() + { + await VerifySnippetIsAbsentAsync(""" + namespace Namespace; + $$ + """); + } + + [Fact] + public async Task InsertUsingSnippetInConstructorTest() + { + await VerifySnippetAsync(""" + class Program + { + public Program() + { + $$ + } + } + """, """ + class Program + { + public Program() + { + using ({|0:resource|}) + { + $$ + } + } + } + """); + } + + [Fact] + public async Task NoUsingSnippetInTypeBodyTest() + { + await VerifySnippetIsAbsentAsync(""" + class Program + { + $$ + } + """); + } + + [Fact] + public async Task InsertUsingSnippetInLocalFunctionTest() + { + await VerifySnippetAsync(""" + class Program + { + public void Method() + { + void LocalFunction() + { + $$ + } + } + } + """, """ + class Program + { + public void Method() + { + void LocalFunction() + { + using ({|0:resource|}) + { + $$ + } + } + } + } + """); + } + + [Fact] + public async Task InsertUsingSnippetInAnonymousFunctionTest() + { + await VerifySnippetAsync(""" + class Program + { + public void Method() + { + var action = delegate() + { + $$ + }; + } + } + """, """ + class Program + { + public void Method() + { + var action = delegate() + { + using ({|0:resource|}) + { + $$ + } + }; + } + } + """); + } + + [Fact] + public async Task InsertUsingSnippetInParenthesizedLambdaExpressionTest() + { + await VerifySnippetAsync(""" + class Program + { + public void Method() + { + var action = () => + { + $$ + }; + } + } + """, """ + class Program + { + public void Method() + { + var action = () => + { + using ({|0:resource|}) + { + $$ + } + }; + } + } + """); + } +} diff --git a/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.Editor.cs b/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.Editor.cs index bbdd9444b28fa..35aede8ef24c6 100644 --- a/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.Editor.cs +++ b/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.Editor.cs @@ -2,13 +2,10 @@ // 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.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeRefactorings.MoveType; @@ -19,15 +16,16 @@ internal abstract partial class AbstractMoveTypeService private abstract class Editor( TService service, - State state, + SemanticDocument semanticDocument, + TTypeDeclarationSyntax typeDeclaration, string fileName, CancellationToken cancellationToken) { - protected State State { get; } = state; protected TService Service { get; } = service; + protected SemanticDocument SemanticDocument { get; } = semanticDocument; + protected TTypeDeclarationSyntax TypeDeclaration { get; } = typeDeclaration; protected string FileName { get; } = fileName; protected CancellationToken CancellationToken { get; } = cancellationToken; - protected SemanticDocument SemanticDocument => State.SemanticDocument; ///

/// Operations performed by CodeAction. @@ -35,27 +33,21 @@ private abstract class Editor( public virtual async Task> GetOperationsAsync() { var solution = await GetModifiedSolutionAsync().ConfigureAwait(false); - - if (solution == null) - { - return []; - } - - return [new ApplyChangesOperation(solution)]; + return solution == null ? [] : [new ApplyChangesOperation(solution)]; } /// /// Incremental solution edits that correlate to code operations /// - public abstract Task GetModifiedSolutionAsync(); + public abstract Task GetModifiedSolutionAsync(); - public static Editor GetEditor(MoveTypeOperationKind operationKind, TService service, State state, string fileName, CancellationToken cancellationToken) + public static Editor GetEditor(MoveTypeOperationKind operationKind, TService service, SemanticDocument document, TTypeDeclarationSyntax typeDeclaration, string fileName, CancellationToken cancellationToken) => operationKind switch { - MoveTypeOperationKind.MoveType => new MoveTypeEditor(service, state, fileName, cancellationToken), - MoveTypeOperationKind.RenameType => new RenameTypeEditor(service, state, fileName, cancellationToken), - MoveTypeOperationKind.RenameFile => new RenameFileEditor(service, state, fileName, cancellationToken), - MoveTypeOperationKind.MoveTypeNamespaceScope => new MoveTypeNamespaceScopeEditor(service, state, fileName, cancellationToken), + MoveTypeOperationKind.MoveType => new MoveTypeEditor(service, document, typeDeclaration, fileName, cancellationToken), + MoveTypeOperationKind.RenameType => new RenameTypeEditor(service, document, typeDeclaration, fileName, cancellationToken), + MoveTypeOperationKind.RenameFile => new RenameFileEditor(service, document, typeDeclaration, fileName, cancellationToken), + MoveTypeOperationKind.MoveTypeNamespaceScope => new MoveTypeNamespaceScopeEditor(service, document, typeDeclaration, fileName, cancellationToken), _ => throw ExceptionUtilities.UnexpectedValue(operationKind), }; } diff --git a/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.MoveTypeCodeAction.cs b/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.MoveTypeCodeAction.cs index 0cc0ababd3d02..c279936be45a9 100644 --- a/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.MoveTypeCodeAction.cs +++ b/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.MoveTypeCodeAction.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; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeRefactorings.MoveType; @@ -17,41 +14,43 @@ internal abstract partial class AbstractMoveTypeService _operationKind switch { MoveTypeOperationKind.MoveType => string.Format(FeaturesResources.Move_type_to_0, _fileName), - MoveTypeOperationKind.RenameType => string.Format(FeaturesResources.Rename_type_to_0, _state.DocumentNameWithoutExtension), + MoveTypeOperationKind.RenameType => string.Format(FeaturesResources.Rename_type_to_0, GetDocumentNameWithoutExtension(_document)), MoveTypeOperationKind.RenameFile => string.Format(FeaturesResources.Rename_file_to_0, _fileName), MoveTypeOperationKind.MoveTypeNamespaceScope => string.Empty, _ => throw ExceptionUtilities.UnexpectedValue(_operationKind), }; - public override string Title => _title; + public override string Title { get; } protected override async Task> ComputeOperationsAsync( IProgress progress, CancellationToken cancellationToken) { - var editor = Editor.GetEditor(_operationKind, _service, _state, _fileName, cancellationToken); + var editor = Editor.GetEditor(_operationKind, _service, _document, _typeDeclaration, _fileName, cancellationToken); return await editor.GetOperationsAsync().ConfigureAwait(false); } } diff --git a/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.MoveTypeEditor.cs b/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.MoveTypeEditor.cs index f904672a73af1..e4fc8389b3993 100644 --- a/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.MoveTypeEditor.cs +++ b/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.MoveTypeEditor.cs @@ -25,9 +25,10 @@ internal abstract partial class AbstractMoveTypeService /// Given a document and a type contained in it, moves the type @@ -42,10 +43,10 @@ private sealed class MoveTypeEditor( /// 3. Add this forked document to the solution. /// 4. Finally, update the original document and remove the type from it. /// - public override async Task GetModifiedSolutionAsync() + public override async Task GetModifiedSolutionAsync() { // Fork, update and add as new document. - var projectToBeUpdated = SemanticDocument.Document.Project; + var projectToBeUpdated = SemanticDocument.Project; var newDocumentId = DocumentId.CreateNewId(projectToBeUpdated.Id, FileName); // We do this process in the following steps: @@ -142,7 +143,7 @@ private async Task AddNewDocumentWithSingleTypeDeclarationAsync(Docume documentEditor.RemoveAllAttributes(root); // Now remove any leading directives on the type-node that actually correspond to prior nodes we removed. - var leadingTrivia = State.TypeNode.GetLeadingTrivia().ToSet(); + var leadingTrivia = this.TypeDeclaration.GetLeadingTrivia().ToSet(); foreach (var directive in correspondingDirectives) { if (leadingTrivia.Contains(directive.ParentTrivia)) @@ -185,7 +186,7 @@ void AddCorrespondingDirectives(SyntaxNode member, HashSet directive private void RemoveLeadingBlankLinesFromMovedType(DocumentEditor documentEditor) { - documentEditor.ReplaceNode(State.TypeNode, + documentEditor.ReplaceNode(this.TypeDeclaration, (currentNode, generator) => { var currentTypeNode = (TTypeDeclarationSyntax)currentNode; @@ -240,7 +241,7 @@ private async Task RemoveTypeFromSourceDocumentAsync(Document sourceDo // Now cleanup and remove the type we're moving to the new file. RemoveLeadingBlankLinesFromMovedType(documentEditor); - documentEditor.RemoveNode(State.TypeNode, SyntaxRemoveOptions.KeepUnbalancedDirectives); + documentEditor.RemoveNode(this.TypeDeclaration, SyntaxRemoveOptions.KeepUnbalancedDirectives); var updatedDocument = documentEditor.GetChangedDocument(); updatedDocument = await AddFileBannerHelpers.CopyBannerAsync(updatedDocument, sourceDocument.FilePath, sourceDocument, this.CancellationToken).ConfigureAwait(false); @@ -260,12 +261,12 @@ private ISet GetMembersToRemove(SyntaxNode root) var spine = new HashSet(); // collect the parent chain of declarations to keep. - spine.AddRange(State.TypeNode.GetAncestors()); + spine.AddRange(this.TypeDeclaration.GetAncestors()); // get potential namespace, types and members to remove. var removableCandidates = root .DescendantNodes(spine.Contains) - .Where(n => FilterToTopLevelMembers(n, State.TypeNode)).ToSet(); + .Where(n => FilterToTopLevelMembers(n, this.TypeDeclaration)).ToSet(); // diff candidates with items we want to keep. removableCandidates.ExceptWith(spine); @@ -304,12 +305,12 @@ private void AddPartialModifiersToTypeChain( bool removeTypeInheritance, bool removePrimaryConstructor) { - var semanticFacts = State.SemanticDocument.Document.GetRequiredLanguageService(); - var typeChain = State.TypeNode.Ancestors().OfType(); + var semanticFacts = SemanticDocument.GetRequiredLanguageService(); + var typeChain = this.TypeDeclaration.Ancestors().OfType(); foreach (var node in typeChain) { - var symbol = (INamedTypeSymbol?)State.SemanticDocument.SemanticModel.GetDeclaredSymbol(node, CancellationToken); + var symbol = (INamedTypeSymbol)SemanticDocument.SemanticModel.GetRequiredDeclaredSymbol(node, CancellationToken); Contract.ThrowIfNull(symbol); if (!semanticFacts.IsPartial(symbol, CancellationToken)) { @@ -338,8 +339,8 @@ private void AddPartialModifiersToTypeChain( private TTypeDeclarationSyntax RemoveLeadingBlankLines( TTypeDeclarationSyntax currentTypeNode) { - var syntaxFacts = State.SemanticDocument.Document.GetRequiredLanguageService(); - var bannerService = State.SemanticDocument.Document.GetRequiredLanguageService(); + var syntaxFacts = SemanticDocument.GetRequiredLanguageService(); + var bannerService = SemanticDocument.GetRequiredLanguageService(); var withoutBlankLines = bannerService.GetNodeWithoutLeadingBlankLines(currentTypeNode); diff --git a/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.MoveTypeNamespaceScopeEditor.cs b/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.MoveTypeNamespaceScopeEditor.cs index 73aa4788eb51b..596ed816f2a80 100644 --- a/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.MoveTypeNamespaceScopeEditor.cs +++ b/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.MoveTypeNamespaceScopeEditor.cs @@ -20,43 +20,40 @@ internal abstract partial class AbstractMoveTypeService< /// it will evaluate if the namespace scope needs to be closed and reopened to create a new scope. /// private sealed class MoveTypeNamespaceScopeEditor( - TService service, State state, string fileName, CancellationToken cancellationToken) - : Editor(service, state, fileName, cancellationToken) + TService service, + SemanticDocument document, + TTypeDeclarationSyntax typeDeclaration, + string fileName, + CancellationToken cancellationToken) + : Editor(service, document, typeDeclaration, fileName, cancellationToken) { public override async Task GetModifiedSolutionAsync() { - var node = State.TypeNode; - var documentToEdit = State.SemanticDocument.Document; - - if (node.Parent is not TNamespaceDeclarationSyntax namespaceDeclaration) - return null; - - return await GetNamespaceScopeChangedSolutionAsync(namespaceDeclaration, node, documentToEdit, CancellationToken).ConfigureAwait(false); + return TypeDeclaration.Parent is TNamespaceDeclarationSyntax namespaceDeclaration + ? await GetNamespaceScopeChangedSolutionAsync(namespaceDeclaration).ConfigureAwait(false) + : null; } - private static async Task GetNamespaceScopeChangedSolutionAsync( - TNamespaceDeclarationSyntax namespaceDeclaration, - TTypeDeclarationSyntax typeToMove, - Document documentToEdit, - CancellationToken cancellationToken) + private async Task GetNamespaceScopeChangedSolutionAsync( + TNamespaceDeclarationSyntax namespaceDeclaration) { - var syntaxFactsService = documentToEdit.GetRequiredLanguageService(); + var syntaxFactsService = SemanticDocument.GetRequiredLanguageService(); var childNodes = syntaxFactsService.GetMembersOfBaseNamespaceDeclaration(namespaceDeclaration); if (childNodes.Count <= 1) return null; - var editor = await DocumentEditor.CreateAsync(documentToEdit, cancellationToken).ConfigureAwait(false); - editor.RemoveNode(typeToMove, SyntaxRemoveOptions.KeepNoTrivia); + var editor = await DocumentEditor.CreateAsync(SemanticDocument.Document, this.CancellationToken).ConfigureAwait(false); + editor.RemoveNode(this.TypeDeclaration, SyntaxRemoveOptions.KeepNoTrivia); var generator = editor.Generator; - var index = childNodes.IndexOf(typeToMove); + var index = childNodes.IndexOf(this.TypeDeclaration); var itemsBefore = childNodes.Take(index).ToImmutableArray(); var itemsAfter = childNodes.Skip(index + 1).ToImmutableArray(); var name = syntaxFactsService.GetDisplayName(namespaceDeclaration, DisplayNameOptions.IncludeNamespaces); - var newNamespaceDeclaration = generator.NamespaceDeclaration(name, WithElasticTrivia(typeToMove)).WithAdditionalAnnotations(NamespaceScopeMovedAnnotation); + var newNamespaceDeclaration = generator.NamespaceDeclaration(name, WithElasticTrivia(this.TypeDeclaration)).WithAdditionalAnnotations(NamespaceScopeMovedAnnotation); if (itemsBefore.Any() && itemsAfter.Any()) { diff --git a/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.RenameFileEditor.cs b/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.RenameFileEditor.cs index 031364a87dbd4..73607e6b9e236 100644 --- a/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.RenameFileEditor.cs +++ b/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.RenameFileEditor.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. -#nullable disable - using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -13,29 +11,25 @@ namespace Microsoft.CodeAnalysis.CodeRefactorings.MoveType; internal abstract partial class AbstractMoveTypeService { - private sealed class RenameFileEditor(TService service, State state, string fileName, CancellationToken cancellationToken) : Editor(service, state, fileName, cancellationToken) + /// + /// Renames the file to match the type contained in it. + /// + private sealed class RenameFileEditor( + TService service, + SemanticDocument document, + TTypeDeclarationSyntax typeDeclaration, + string fileName, + CancellationToken cancellationToken) : Editor(service, document, typeDeclaration, fileName, cancellationToken) { - public override Task> GetOperationsAsync() - => Task.FromResult(RenameFileToMatchTypeName()); - - public override Task GetModifiedSolutionAsync() - { - var modifiedSolution = SemanticDocument.Project.Solution - .WithDocumentName(SemanticDocument.Document.Id, FileName); - - return Task.FromResult(modifiedSolution); - } - - /// - /// Renames the file to match the type contained in it. - /// - private ImmutableArray RenameFileToMatchTypeName() + public override async Task> GetOperationsAsync() { - var documentId = SemanticDocument.Document.Id; - var oldSolution = SemanticDocument.Document.Project.Solution; - var newSolution = oldSolution.WithDocumentName(documentId, FileName); - + var newSolution = await GetModifiedSolutionAsync().ConfigureAwait(false); + Contract.ThrowIfNull(newSolution); return [new ApplyChangesOperation(newSolution)]; } + + public override Task GetModifiedSolutionAsync() + => Task.FromResult( + SemanticDocument.Project.Solution.WithDocumentName(SemanticDocument.Document.Id, FileName)); } } diff --git a/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.RenameTypeEditor.cs b/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.RenameTypeEditor.cs index a7ede6d827d32..6ec382833414d 100644 --- a/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.RenameTypeEditor.cs +++ b/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.RenameTypeEditor.cs @@ -2,29 +2,32 @@ // 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.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Rename; +using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.CodeRefactorings.MoveType; internal abstract partial class AbstractMoveTypeService { - private sealed class RenameTypeEditor(TService service, State state, string fileName, CancellationToken cancellationToken) : Editor(service, state, fileName, cancellationToken) + private sealed class RenameTypeEditor( + TService service, + SemanticDocument document, + TTypeDeclarationSyntax typeDeclaration, + string fileName, + CancellationToken cancellationToken) : Editor(service, document, typeDeclaration, fileName, cancellationToken) { - /// /// Renames a type to match its containing file name. /// - public override async Task GetModifiedSolutionAsync() + public override async Task GetModifiedSolutionAsync() { // TODO: detect conflicts ahead of time and open an inline rename session if any exists. // this will bring up dashboard with conflicts and will allow the user to resolve them. // if no such conflicts exist, proceed with RenameSymbolAsync. - var solution = SemanticDocument.Document.Project.Solution; - var symbol = State.SemanticDocument.SemanticModel.GetDeclaredSymbol(State.TypeNode, CancellationToken); + var solution = SemanticDocument.Project.Solution; + var symbol = SemanticDocument.SemanticModel.GetRequiredDeclaredSymbol(this.TypeDeclaration, CancellationToken); return await Renamer.RenameSymbolAsync(solution, symbol, new SymbolRenameOptions(), FileName, CancellationToken).ConfigureAwait(false); } } diff --git a/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.State.cs b/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.State.cs deleted file mode 100644 index e7b7c1141a844..0000000000000 --- a/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.State.cs +++ /dev/null @@ -1,75 +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. - -#nullable disable - -using System.IO; -using System.Linq; -using System.Threading; -using Microsoft.CodeAnalysis.LanguageService; -using Microsoft.CodeAnalysis.Shared.Extensions; - -namespace Microsoft.CodeAnalysis.CodeRefactorings.MoveType; - -internal abstract partial class AbstractMoveTypeService -{ - private sealed class State - { - public SemanticDocument SemanticDocument { get; } - - public TTypeDeclarationSyntax TypeNode { get; set; } - public string TypeName { get; set; } - public string DocumentNameWithoutExtension { get; set; } - public bool IsDocumentNameAValidIdentifier { get; set; } - - private State(SemanticDocument document) - { - SemanticDocument = document; - } - - internal static State Generate( - SemanticDocument document, TTypeDeclarationSyntax typeDeclaration, - CancellationToken cancellationToken) - { - var state = new State(document); - if (!state.TryInitialize(typeDeclaration, cancellationToken)) - { - return null; - } - - return state; - } - - private bool TryInitialize( - TTypeDeclarationSyntax typeDeclaration, - CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return false; - } - - var tree = SemanticDocument.SyntaxTree; - var root = SemanticDocument.Root; - var syntaxFacts = SemanticDocument.Document.GetLanguageService(); - - // compiler declared types, anonymous types, types defined in metadata should be filtered out. - if (SemanticDocument.SemanticModel.GetDeclaredSymbol(typeDeclaration, cancellationToken) is not INamedTypeSymbol typeSymbol || - typeSymbol.Locations.Any(static loc => loc.IsInMetadata) || - typeSymbol.IsAnonymousType || - typeSymbol.IsImplicitlyDeclared || - typeSymbol.Name == string.Empty) - { - return false; - } - - TypeNode = typeDeclaration; - TypeName = typeSymbol.Name; - DocumentNameWithoutExtension = Path.GetFileNameWithoutExtension(SemanticDocument.Document.Name); - IsDocumentNameAValidIdentifier = syntaxFacts.IsValidIdentifier(DocumentNameWithoutExtension); - - return true; - } - } -} diff --git a/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.cs b/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.cs index ffc6f4db6dbca..39ff19a2d8b3b 100644 --- a/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.cs @@ -11,7 +11,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -38,83 +37,69 @@ internal abstract partial class AbstractMoveTypeService GetRelevantNodeAsync(Document document, TextSpan textSpan, CancellationToken cancellationToken); protected abstract bool IsMemberDeclaration(SyntaxNode syntaxNode); + protected string GetSymbolName(TTypeDeclarationSyntax syntax) + => GetSymbolNameAndArity(syntax).name; + public override async Task> GetRefactoringAsync( Document document, TextSpan textSpan, CancellationToken cancellationToken) { - var state = await CreateStateAsync(document, textSpan, cancellationToken).ConfigureAwait(false); - return CreateActions(state, cancellationToken); + var semanticDocument = await SemanticDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false); + var typeDeclaration = await GetTypeDeclarationAsync(document, textSpan, cancellationToken).ConfigureAwait(false); + return CreateActions(semanticDocument, typeDeclaration); } public override async Task GetModifiedSolutionAsync(Document document, TextSpan textSpan, MoveTypeOperationKind operationKind, CancellationToken cancellationToken) { - var state = await CreateStateAsync(document, textSpan, cancellationToken).ConfigureAwait(false); - - if (state == null) - { + var typeDeclaration = await GetTypeDeclarationAsync(document, textSpan, cancellationToken).ConfigureAwait(false); + if (typeDeclaration == null) return document.Project.Solution; - } - var suggestedFileNames = GetSuggestedFileNames( - state.TypeNode, - IsNestedType(state.TypeNode), - state.TypeName, - state.SemanticDocument.Document.Name, - state.SemanticDocument.SemanticModel, - cancellationToken); - - var editor = Editor.GetEditor(operationKind, (TService)this, state, suggestedFileNames.FirstOrDefault(), cancellationToken); + var semanticDocument = await SemanticDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false); + var suggestedFileNames = GetSuggestedFileNames(semanticDocument, typeDeclaration, includeComplexFileNames: false); + + var editor = Editor.GetEditor(operationKind, (TService)this, semanticDocument, typeDeclaration, suggestedFileNames.First(), cancellationToken); var modifiedSolution = await editor.GetModifiedSolutionAsync().ConfigureAwait(false); return modifiedSolution ?? document.Project.Solution; } - private async Task CreateStateAsync(Document document, TextSpan textSpan, CancellationToken cancellationToken) + private async Task GetTypeDeclarationAsync(Document document, TextSpan textSpan, CancellationToken cancellationToken) { var nodeToAnalyze = await GetRelevantNodeAsync(document, textSpan, cancellationToken).ConfigureAwait(false); if (nodeToAnalyze == null) return null; - var semanticDocument = await SemanticDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false); - return State.Generate(semanticDocument, nodeToAnalyze, cancellationToken); + var name = this.GetSymbolName(nodeToAnalyze); + return name == "" ? null : nodeToAnalyze; } - private ImmutableArray CreateActions(State? state, CancellationToken cancellationToken) + private ImmutableArray CreateActions( + SemanticDocument document, TTypeDeclarationSyntax? typeDeclaration) { - if (state is null) + if (typeDeclaration is null) return []; - var typeMatchesDocumentName = TypeMatchesDocumentName( - state.TypeNode, - state.TypeName, - state.DocumentNameWithoutExtension, - state.SemanticDocument.SemanticModel, - cancellationToken); + var documentNameWithoutExtension = GetDocumentNameWithoutExtension(document); + var typeMatchesDocumentName = TypeMatchesDocumentName(typeDeclaration, documentNameWithoutExtension); + // if type name matches document name, per style conventions, we have nothing to do. if (typeMatchesDocumentName) - { - // if type name matches document name, per style conventions, we have nothing to do. return []; - } using var _ = ArrayBuilder.GetInstance(out var actions); - var manyTypes = MultipleTopLevelTypeDeclarationInSourceDocument(state.SemanticDocument.Root); - var isNestedType = IsNestedType(state.TypeNode); - var syntaxFacts = state.SemanticDocument.Document.GetRequiredLanguageService(); - var isClassNextToGlobalStatements = manyTypes - ? false - : ClassNextToGlobalStatements(state.SemanticDocument.Root, syntaxFacts); + var manyTypes = MultipleTopLevelTypeDeclarationInSourceDocument(document.Root); + var isNestedType = IsNestedType(typeDeclaration); + + var syntaxFacts = document.GetRequiredLanguageService(); + var isClassNextToGlobalStatements = !manyTypes && ClassNextToGlobalStatements(document.Root, syntaxFacts); var suggestedFileNames = GetSuggestedFileNames( - state.TypeNode, - isNestedType, - state.TypeName, - state.SemanticDocument.Document.Name, - state.SemanticDocument.SemanticModel, - cancellationToken); + document, typeDeclaration, includeComplexFileNames: false); // (1) Add Move type to new file code action: // case 1: There are multiple type declarations in current document. offer, move to new file. @@ -126,26 +111,21 @@ private ImmutableArray CreateActions(State? state, CancellationToken if (manyTypes || isNestedType || isClassNextToGlobalStatements) { foreach (var fileName in suggestedFileNames) - { - actions.Add(GetCodeAction(state, fileName, operationKind: MoveTypeOperationKind.MoveType)); - } + actions.Add(GetCodeAction(fileName, operationKind: MoveTypeOperationKind.MoveType)); } // (2) Add rename file and rename type code actions: // Case: No type declaration in file matches the file name. - if (!AnyTopLevelTypeMatchesDocumentName(state, cancellationToken)) + if (!AnyTopLevelTypeMatchesDocumentName()) { foreach (var fileName in suggestedFileNames) - { - actions.Add(GetCodeAction(state, fileName, operationKind: MoveTypeOperationKind.RenameFile)); - } + actions.Add(GetCodeAction(fileName, operationKind: MoveTypeOperationKind.RenameFile)); - // only if the document name can be legal identifier in the language, - // offer to rename type with document name - if (state.IsDocumentNameAValidIdentifier) + // Only if the document name can be legal identifier in the language, offer to rename type with document name + if (syntaxFacts.IsValidIdentifier(documentNameWithoutExtension)) { actions.Add(GetCodeAction( - state, fileName: state.DocumentNameWithoutExtension, + fileName: documentNameWithoutExtension, operationKind: MoveTypeOperationKind.RenameType)); } } @@ -153,14 +133,19 @@ private ImmutableArray CreateActions(State? state, CancellationToken Debug.Assert(actions.Count != 0, "No code actions found for MoveType Refactoring"); return actions.ToImmutableAndClear(); + + bool AnyTopLevelTypeMatchesDocumentName() + => TopLevelTypeDeclarations(document.Root).Any( + typeDeclaration => TypeMatchesDocumentName( + typeDeclaration, documentNameWithoutExtension)); + + MoveTypeCodeAction GetCodeAction(string fileName, MoveTypeOperationKind operationKind) + => new((TService)this, document, typeDeclaration, operationKind, fileName); } private static bool ClassNextToGlobalStatements(SyntaxNode root, ISyntaxFactsService syntaxFacts) => syntaxFacts.ContainsGlobalStatement(root); - private MoveTypeCodeAction GetCodeAction(State state, string fileName, MoveTypeOperationKind operationKind) - => new((TService)this, state, operationKind, fileName); - private static bool IsNestedType(TTypeDeclarationSyntax typeNode) => typeNode.Parent is TTypeDeclarationSyntax; @@ -174,23 +159,10 @@ private static bool MultipleTopLevelTypeDeclarationInSourceDocument(SyntaxNode r => TopLevelTypeDeclarations(root).Skip(1).Any(); private static IEnumerable TopLevelTypeDeclarations(SyntaxNode root) - => root.DescendantNodes(n => n is TCompilationUnitSyntax or TNamespaceDeclarationSyntax) - .OfType(); - - private static bool AnyTopLevelTypeMatchesDocumentName(State state, CancellationToken cancellationToken) - { - var root = state.SemanticDocument.Root; - var semanticModel = state.SemanticDocument.SemanticModel; + => root.DescendantNodes(n => n is TCompilationUnitSyntax or TNamespaceDeclarationSyntax).OfType(); - return TopLevelTypeDeclarations(root).Any( - typeDeclaration => - { - var typeName = semanticModel.GetRequiredDeclaredSymbol(typeDeclaration, cancellationToken).Name; - return TypeMatchesDocumentName( - typeDeclaration, typeName, state.DocumentNameWithoutExtension, - semanticModel, cancellationToken); - }); - } + private static string GetDocumentNameWithoutExtension(SemanticDocument document) + => Path.GetFileNameWithoutExtension(document.Document.Name); /// /// checks if type name matches its parent document name, per style rules. @@ -199,58 +171,90 @@ private static bool AnyTopLevelTypeMatchesDocumentName(State state, Cancellation /// Note: For a nested type, a matching document name could be just the type name or a /// dotted qualified name of its type hierarchy. /// - protected static bool TypeMatchesDocumentName( + protected bool TypeMatchesDocumentName( TTypeDeclarationSyntax typeNode, - string typeName, - string documentNameWithoutExtension, - SemanticModel semanticModel, - CancellationToken cancellationToken) + string documentNameWithoutExtension) { // If it is not a nested type, we compare the unqualified type name with the document name. // If it is a nested type, the type name `Outer.Inner` matches file names `Inner.cs` and `Outer.Inner.cs` - var namesMatch = typeName.Equals(documentNameWithoutExtension, StringComparison.CurrentCulture); - if (!namesMatch) - { - var typeNameParts = GetTypeNamePartsForNestedTypeNode(typeNode, semanticModel, cancellationToken); - var fileNameParts = documentNameWithoutExtension.Split('.'); + var (typeName, arity) = GetSymbolNameAndArity(typeNode); + if (TypeNameMatches(documentNameWithoutExtension, typeName, arity)) + return true; + + var typeNameParts = GetTypeNamePartsForNestedTypeNode(typeNode).ToImmutableArray(); + var fileNameParts = documentNameWithoutExtension.Split('.', '+'); - // qualified type name `Outer.Inner` matches file names `Inner.cs` and `Outer.Inner.cs` - return typeNameParts.SequenceEqual(fileNameParts, StringComparer.CurrentCulture); + if (typeNameParts.Length != fileNameParts.Length) + return false; + + // qualified type name `Outer.Inner` matches file names `Inner.cs` and `Outer.Inner.cs` as well as + // Outer`1.Inner`2.cs + for (int i = 0, n = typeNameParts.Length; i < n; i++) + { + if (!TypeNameMatches(fileNameParts[i], typeNameParts[i].name, typeNameParts[i].arity)) + return false; } - return namesMatch; + return true; + } + + private static bool TypeNameMatches(string documentNameWithoutExtension, string typeName, int arity) + { + if (typeName.Equals(documentNameWithoutExtension, StringComparison.CurrentCulture)) + return true; + + if ($"{typeName}`{arity}".Equals(documentNameWithoutExtension, StringComparison.CurrentCulture)) + return true; + + return false; } - private static ImmutableArray GetSuggestedFileNames( + private ImmutableArray GetSuggestedFileNames( + SemanticDocument document, TTypeDeclarationSyntax typeNode, - bool isNestedType, - string typeName, - string documentNameWithExtension, - SemanticModel semanticModel, - CancellationToken cancellationToken) + bool includeComplexFileNames) { + var documentNameWithExtension = document.Document.Name; + var isNestedType = IsNestedType(typeNode); + var (typeName, arity) = this.GetSymbolNameAndArity(typeNode); var fileExtension = Path.GetExtension(documentNameWithExtension); var standaloneName = typeName + fileExtension; - // If it is a nested type, we should match type hierarchy's name parts with the file name. + using var _ = ArrayBuilder.GetInstance(out var suggestedFileNames); + + suggestedFileNames.Add(typeName + fileExtension); + if (includeComplexFileNames && arity > 0) + suggestedFileNames.Add($"{typeName}`{arity}{fileExtension}"); + if (isNestedType) { - var typeNameParts = GetTypeNamePartsForNestedTypeNode(typeNode, semanticModel, cancellationToken); - var dottedName = typeNameParts.Join(".") + fileExtension; + var typeNameParts = GetTypeNamePartsForNestedTypeNode(typeNode); + AddNameParts(typeNameParts.Select(t => t.name)); + + if (includeComplexFileNames && typeNameParts.Any(t => t.arity > 0)) + AddNameParts(typeNameParts.Select(t => t.arity > 0 ? $"{t.name}`{t.arity}" : t.name)); + } + + return suggestedFileNames.ToImmutableAndClear(); + + void AddNameParts(IEnumerable parts) + { + AddNamePartsWithSeparator(parts, "."); - return [standaloneName, dottedName]; + if (includeComplexFileNames) + AddNamePartsWithSeparator(parts, "+"); } - else + + void AddNamePartsWithSeparator(IEnumerable parts, string separator) { - return [standaloneName]; + suggestedFileNames.Add(parts.Join(separator) + fileExtension); } } - private static IEnumerable GetTypeNamePartsForNestedTypeNode( - TTypeDeclarationSyntax typeNode, SemanticModel semanticModel, CancellationToken cancellationToken) - => typeNode.AncestorsAndSelf() - .OfType() - .Select(n => semanticModel.GetRequiredDeclaredSymbol(n, cancellationToken).Name) - .Reverse(); + private IEnumerable<(string name, int arity)> GetTypeNamePartsForNestedTypeNode(TTypeDeclarationSyntax typeNode) + => typeNode.AncestorsAndSelf() + .OfType() + .Select(GetSymbolNameAndArity) + .Reverse(); } diff --git a/src/Features/Core/Portable/Completion/Providers/SymbolCompletionItem.cs b/src/Features/Core/Portable/Completion/Providers/SymbolCompletionItem.cs index a4ce028eefdb4..0d8ad3bde2e12 100644 --- a/src/Features/Core/Portable/Completion/Providers/SymbolCompletionItem.cs +++ b/src/Features/Core/Portable/Completion/Providers/SymbolCompletionItem.cs @@ -24,7 +24,7 @@ internal static class SymbolCompletionItem private static readonly Action, ArrayBuilder>> s_addSymbolEncoding = AddSymbolEncoding; private static readonly Action, ArrayBuilder>> s_addSymbolInfo = AddSymbolInfo; - private static readonly char[] s_projectSeperators = [';']; + private const char ProjectSeperatorChar = ';'; private static CompletionItem CreateWorker( string displayText, @@ -236,13 +236,45 @@ private static void AddSupportedPlatforms(ArrayBuilder ProjectId.CreateFromSerialized(Guid.Parse(s))), - candidateProjects.Split(s_projectSeperators).SelectAsArray(s => ProjectId.CreateFromSerialized(Guid.Parse(s)))); + SplitIntoProjectIds(invalidProjects), + SplitIntoProjectIds(candidateProjects)); } return null; } + private static ImmutableArray SplitIntoProjectIds(string projectIds) + { + // Does the equivalent of string.Split, with fewer allocations + var start = 0; + var current = 0; + using var _ = ArrayBuilder.GetInstance(out var builder); + + while (current < projectIds.Length) + { + if (projectIds[current] == ProjectSeperatorChar) + { + if (start != current) + { + var projectGuid = Guid.Parse(projectIds.Substring(start, current - start)); + builder.Add(ProjectId.CreateFromSerialized(projectGuid)); + } + + start = current + 1; + } + + current++; + } + + if (start != current) + { + var projectGuid = Guid.Parse(projectIds.Substring(start, current - start)); + builder.Add(ProjectId.CreateFromSerialized(projectGuid)); + } + + return builder.ToImmutableAndClear(); + } + public static int GetContextPosition(CompletionItem item) { if (item.TryGetProperty("ContextPosition", out var text) && diff --git a/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertPlaceholderToInterpolatedStringRefactoringProvider.cs b/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertPlaceholderToInterpolatedStringRefactoringProvider.cs index 6f6906bec80e7..45f15e9dc395f 100644 --- a/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertPlaceholderToInterpolatedStringRefactoringProvider.cs +++ b/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertPlaceholderToInterpolatedStringRefactoringProvider.cs @@ -9,12 +9,15 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ConvertToInterpolatedString; @@ -26,7 +29,7 @@ internal abstract class AbstractConvertPlaceholderToInterpolatedStringRefactorin TInterpolatedStringExpressionSyntax, TArgumentSyntax, TArgumentListExpressionSyntax, - TInterpolationSyntax> : CodeRefactoringProvider + TInterpolationSyntax> : SyntaxEditorBasedCodeRefactoringProvider where TExpressionSyntax : SyntaxNode where TLiteralExpressionSyntax : TExpressionSyntax where TInvocationExpressionSyntax : TExpressionSyntax @@ -35,29 +38,32 @@ internal abstract class AbstractConvertPlaceholderToInterpolatedStringRefactorin where TArgumentListExpressionSyntax : SyntaxNode where TInterpolationSyntax : SyntaxNode { - protected abstract TExpressionSyntax ParseExpression(string text); + private readonly record struct InvocationData( + TInvocationExpressionSyntax Invocation, + TArgumentSyntax PlaceholderArgument, + IMethodSymbol InvocationSymbol, + TInterpolatedStringExpressionSyntax InterpolatedString, + bool ShouldReplaceInvocation); - public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) - { - var (document, span, cancellationToken) = context; - var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + protected abstract TExpressionSyntax ParseExpression(string text); - var stringType = semanticModel.Compilation.GetSpecialType(SpecialType.System_String); - if (stringType.IsErrorType()) - return; + protected override ImmutableArray SupportedFixAllScopes { get; } = AllFixAllScopes; + private InvocationData? AnalyzeInvocation( + Document document, + SemanticModel semanticModel, + TInvocationExpressionSyntax invocation, + TArgumentSyntax placeholderArgument, + CancellationToken cancellationToken) + { var syntaxFacts = document.GetRequiredLanguageService(); - var (invocation, placeholderArgument) = await TryFindInvocationAsync().ConfigureAwait(false); - if (invocation is null || placeholderArgument is null) - return; - var placeholderExpression = syntaxFacts.GetExpressionOfArgument(placeholderArgument); var stringToken = placeholderExpression.GetFirstToken(); // don't offer if the string argument has errors in it, or if converting it to an interpolated string creates errors. if (stringToken.GetDiagnostics().Any(d => d.Severity == DiagnosticSeverity.Error)) - return; + return null; // Not supported if there are any omitted arguments following the placeholder. var arguments = syntaxFacts.GetArgumentsOfInvocationExpression(invocation); @@ -67,14 +73,14 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte { var argument = arguments[i]; if (syntaxFacts.GetExpressionOfArgument(argument) is null) - return; + return null; if (syntaxFacts.GetRefKindOfArgument(argument) != RefKind.None) - return; + return null; } if (semanticModel.GetSymbolInfo(invocation, cancellationToken).GetAnySymbol() is not IMethodSymbol invocationSymbol) - return; + return null; // If the user is actually passing an array to a params argument, we can't change this to be an interpolated string. if (invocationSymbol.IsParams()) @@ -82,31 +88,52 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte var lastArgument = syntaxFacts.GetArgumentsOfInvocationExpression(invocation).Last(); var lastArgumentType = semanticModel.GetTypeInfo(syntaxFacts.GetExpressionOfArgument(lastArgument), cancellationToken).Type; if (lastArgumentType is IArrayTypeSymbol) - return; + return null; } // if the user is explicitly passing in a CultureInfo, don't offer as it's likely they want specialized // formatting for the values. foreach (var argument in arguments) { - var type = semanticModel.GetTypeInfo(syntaxFacts.GetExpressionOfArgument(argument)).Type; + var type = semanticModel.GetTypeInfo(syntaxFacts.GetExpressionOfArgument(argument), cancellationToken).Type; if (type is { Name: nameof(CultureInfo), ContainingNamespace.Name: nameof(System.Globalization), ContainingNamespace.ContainingNamespace.Name: nameof(System) }) - return; + return null; } if (ParseExpression("$" + stringToken.Text) is not TInterpolatedStringExpressionSyntax interpolatedString) - return; + return null; if (interpolatedString.GetDiagnostics().Any(d => d.Severity == DiagnosticSeverity.Error)) - return; + return null; var shouldReplaceInvocation = invocationSymbol is { ContainingType.SpecialType: SpecialType.System_String, Name: nameof(string.Format) }; + return new(invocation, placeholderArgument, invocationSymbol, interpolatedString, shouldReplaceInvocation); + } + + public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) + { + var (document, span, cancellationToken) = context; + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + + var stringType = semanticModel.Compilation.GetSpecialType(SpecialType.System_String); + if (stringType.IsErrorType()) + return; + + var syntaxFacts = document.GetRequiredLanguageService(); + + var (invocation, placeholderArgument) = await TryFindInvocationAsync().ConfigureAwait(false); + if (invocation is null || placeholderArgument is null) + return; + + var data = this.AnalyzeInvocation(document, semanticModel, invocation, placeholderArgument, cancellationToken); + if (data is null) + return; + context.RegisterRefactoring( CodeAction.Create( FeaturesResources.Convert_to_interpolated_string, - cancellationToken => CreateInterpolatedStringAsync( - document, invocation, placeholderArgument, invocationSymbol, interpolatedString, shouldReplaceInvocation, cancellationToken), + cancellationToken => CreateInterpolatedStringAsync(document, data.Value, cancellationToken), nameof(FeaturesResources.Convert_to_interpolated_string)), invocation.Span); @@ -118,7 +145,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte var invocations = await document.GetRelevantNodesAsync(span, cancellationToken).ConfigureAwait(false); foreach (var invocation in invocations) { - var placeholderArgument = FindValidPlaceholderArgument(invocation); + var placeholderArgument = FindValidPlaceholderArgument(syntaxFacts, invocation, cancellationToken); if (placeholderArgument != null) return (invocation, placeholderArgument); } @@ -127,7 +154,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte // User selected a single argument of the invocation (expression / format string) instead of the whole invocation. var selectedArgument = await document.TryGetRelevantNodeAsync(span, cancellationToken).ConfigureAwait(false); var invocation = selectedArgument?.Parent?.Parent as TInvocationExpressionSyntax; - var placeholderArgument = FindValidPlaceholderArgument(invocation); + var placeholderArgument = FindValidPlaceholderArgument(syntaxFacts, invocation, cancellationToken); if (placeholderArgument != null) return (invocation, placeholderArgument); } @@ -136,47 +163,52 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte // User selected the whole argument list: string format with placeholders plus all expressions var argumentList = await document.TryGetRelevantNodeAsync(span, cancellationToken).ConfigureAwait(false); var invocation = argumentList?.Parent as TInvocationExpressionSyntax; - var placeholderArgument = FindValidPlaceholderArgument(invocation); + var placeholderArgument = FindValidPlaceholderArgument(syntaxFacts, invocation, cancellationToken); if (placeholderArgument != null) return (invocation, placeholderArgument); } return default; } + } - TArgumentSyntax? FindValidPlaceholderArgument(TInvocationExpressionSyntax? invocation) + private static TArgumentSyntax? FindValidPlaceholderArgument( + ISyntaxFacts syntaxFacts, TInvocationExpressionSyntax? invocation, CancellationToken cancellationToken) + { + if (invocation != null) { - if (invocation != null) + // look for a string argument containing `"...{0}..."`, followed by more arguments. + var arguments = (SeparatedSyntaxList)syntaxFacts.GetArgumentsOfInvocationExpression(invocation); + for (int i = 0, n = arguments.Count - 1; i < n; i++) { - // look for a string argument containing `"...{0}..."`, followed by more arguments. - var arguments = (SeparatedSyntaxList)syntaxFacts.GetArgumentsOfInvocationExpression(invocation); - for (int i = 0, n = arguments.Count - 1; i < n; i++) + cancellationToken.ThrowIfCancellationRequested(); + + var argument = arguments[i]; + var expression = syntaxFacts.GetExpressionOfArgument(argument); + if (syntaxFacts.IsStringLiteralExpression(expression)) { - var argument = arguments[i]; - var expression = syntaxFacts.GetExpressionOfArgument(argument); - if (syntaxFacts.IsStringLiteralExpression(expression)) + var remainingArgCount = arguments.Count - i - 1; + Debug.Assert(remainingArgCount > 0); + var stringLiteralText = expression.GetFirstToken().Text; + if (stringLiteralText.Contains('{') && stringLiteralText.Contains('}')) { - var remainingArgCount = arguments.Count - i - 1; - Debug.Assert(remainingArgCount > 0); - var stringLiteralText = expression.GetFirstToken().Text; - if (stringLiteralText.Contains('{') && stringLiteralText.Contains('}')) - { - if (IsValidPlaceholderArgument(stringLiteralText, remainingArgCount)) - return (TArgumentSyntax)argument; - } + if (IsValidPlaceholderArgument(stringLiteralText, remainingArgCount)) + return argument; } } } - - return null; } + return null; + bool IsValidPlaceholderArgument(string stringLiteralText, int remainingArgCount) { // See how many arguments follow the `"...{0}..."`. We have to have a {0}, {1}, ... {N} part in the // string for each of them. Note, those could be in any order. for (var i = 0; i < remainingArgCount; i++) { + cancellationToken.ThrowIfCancellationRequested(); + var indexString = i.ToString(CultureInfo.InvariantCulture); if (!ContainsIndex(stringLiteralText, indexString)) return false; @@ -219,23 +251,79 @@ bool ContainsIndex(string stringLiteralText, string indexString) } } - private static async Task CreateInterpolatedStringAsync( + protected override async Task FixAllAsync( Document document, - TInvocationExpressionSyntax invocation, - TArgumentSyntax placeholderArgument, - IMethodSymbol invocationSymbol, - TInterpolatedStringExpressionSyntax interpolatedString, - bool shouldReplaceInvocation, + ImmutableArray fixAllSpans, + SyntaxEditor editor, + string? equivalenceKey, CancellationToken cancellationToken) { var syntaxFacts = document.GetRequiredLanguageService(); + + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + + var normalizedSpans = new NormalizedTextSpanCollection(fixAllSpans); + + using var _ = ArrayBuilder.GetInstance(out var stack); + stack.Push(root); + + while (stack.TryPop(out var node)) + { + if (node is TInvocationExpressionSyntax invocation) + { + var placeholderArgument = FindValidPlaceholderArgument(syntaxFacts, invocation, cancellationToken); + if (placeholderArgument is not null && + AnalyzeInvocation(document, semanticModel, invocation, placeholderArgument, cancellationToken) is { } invocationData) + { + ReplaceInvocation(editor, semanticModel, syntaxFacts, invocationData, cancellationToken); + continue; + } + } + + foreach (var child in node.ChildNodesAndTokens()) + { + if (child.IsNode) + { + var childNode = child.AsNode(); + if (normalizedSpans.IntersectsWith(childNode!.Span)) + stack.Push(childNode); + } + } + } + } + + private static async Task CreateInterpolatedStringAsync( + Document document, + InvocationData invocationData, + CancellationToken cancellationToken) + { + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var syntaxFacts = document.GetRequiredLanguageService(); + var syntaxGenerator = document.GetRequiredLanguageService(); + var editor = new SyntaxEditor(root, syntaxGenerator); + + ReplaceInvocation(editor, semanticModel, syntaxFacts, invocationData, cancellationToken); + + return document.WithSyntaxRoot(editor.GetChangedRoot()); + } + + private static void ReplaceInvocation( + SyntaxEditor editor, + SemanticModel semanticModel, + ISyntaxFacts syntaxFacts, + InvocationData invocationData, + CancellationToken cancellationToken) + { + var (invocation, placeholderArgument, invocationSymbol, interpolatedString, shouldReplaceInvocation) = invocationData; + var arguments = (SeparatedSyntaxList)syntaxFacts.GetArgumentsOfInvocationExpression(invocation); var literalExpression = (TLiteralExpressionSyntax?)syntaxFacts.GetExpressionOfArgument(placeholderArgument); Contract.ThrowIfNull(literalExpression); - var syntaxGenerator = document.GetRequiredLanguageService(); + var syntaxGenerator = editor.Generator; var newInterpolatedString = InsertArgumentsIntoInterpolatedString( @@ -248,9 +336,8 @@ private static async Task CreateInterpolatedStringAsync( syntaxFacts.GetExpressionOfInvocationExpression(invocation), newInterpolatedString); - var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var newRoot = root.ReplaceNode(invocation, replacementNode.WithTriviaFrom(invocation)); - return document.WithSyntaxRoot(newRoot); + editor.ReplaceNode(invocation, replacementNode.WithTriviaFrom(invocation)); + return; ImmutableArray GetReorderedArgumentsAfterPlaceholderArgument() { diff --git a/src/Features/Core/Portable/GenerateFromMembers/GenerateFromMembersHelpers.cs b/src/Features/Core/Portable/GenerateFromMembers/GenerateFromMembersHelpers.cs index 2b3d574956fac..0c627eaa1a961 100644 --- a/src/Features/Core/Portable/GenerateFromMembers/GenerateFromMembersHelpers.cs +++ b/src/Features/Core/Portable/GenerateFromMembers/GenerateFromMembersHelpers.cs @@ -70,10 +70,10 @@ private static bool IsWritableFieldOrProperty(ISymbol symbol) }; private static bool IsViableField(IFieldSymbol field) - => field.AssociatedSymbol == null; + => field is { AssociatedSymbol: null, CanBeReferencedByName: true }; private static bool IsViableProperty(IPropertySymbol property) - => property.Parameters.IsEmpty; + => property is { Parameters.IsEmpty: true, CanBeReferencedByName: true }; /// /// Returns an array of parameter symbols that correspond to selected member symbols. diff --git a/src/Features/Core/Portable/QuickInfo/QuickInfoUtilities.cs b/src/Features/Core/Portable/QuickInfo/QuickInfoUtilities.cs index 865997ee60408..fdb53dfa525e5 100644 --- a/src/Features/Core/Portable/QuickInfo/QuickInfoUtilities.cs +++ b/src/Features/Core/Portable/QuickInfo/QuickInfoUtilities.cs @@ -18,6 +18,11 @@ namespace Microsoft.CodeAnalysis.QuickInfo; internal static class QuickInfoUtilities { + /// + /// Display variable name only. + /// + private static readonly SymbolDisplayFormat s_nullableDisplayFormat = new SymbolDisplayFormat(); + public static Task CreateQuickInfoItemAsync(SolutionServices services, SemanticModel semanticModel, TextSpan span, ImmutableArray symbols, SymbolDescriptionOptions options, CancellationToken cancellationToken) => CreateQuickInfoItemAsync(services, semanticModel, span, symbols, supportedPlatforms: null, showAwaitReturn: false, flowState: NullableFlowState.None, options, onTheFlyDocsInfo: null, cancellationToken); @@ -136,8 +141,8 @@ public static async Task CreateQuickInfoItemAsync( var nullableMessage = flowState switch { - NullableFlowState.MaybeNull => string.Format(FeaturesResources._0_may_be_null_here, symbol.Name), - NullableFlowState.NotNull => string.Format(FeaturesResources._0_is_not_null_here, symbol.Name), + NullableFlowState.MaybeNull => string.Format(FeaturesResources._0_may_be_null_here, symbol.ToDisplayString(s_nullableDisplayFormat)), + NullableFlowState.NotNull => string.Format(FeaturesResources._0_is_not_null_here, symbol.ToDisplayString(s_nullableDisplayFormat)), _ => null }; diff --git a/src/Features/Core/Portable/Snippets/SnippetProviders/AbstractUsingSnippetProvider.cs b/src/Features/Core/Portable/Snippets/SnippetProviders/AbstractUsingSnippetProvider.cs new file mode 100644 index 0000000000000..f64d64c1af24b --- /dev/null +++ b/src/Features/Core/Portable/Snippets/SnippetProviders/AbstractUsingSnippetProvider.cs @@ -0,0 +1,26 @@ +// 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.Editing; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Utilities; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Snippets.SnippetProviders; + +internal abstract class AbstractUsingSnippetProvider : AbstractStatementSnippetProvider + where TUsingStatementSyntax : SyntaxNode +{ + protected sealed override async Task GenerateSnippetTextChangeAsync(Document document, int position, CancellationToken cancellationToken) + { + var generator = SyntaxGenerator.GetGenerator(document); + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var identifierName = NameGenerator.GenerateUniqueName("resource", + n => semanticModel.LookupSymbols(position, name: n).IsEmpty); + var statement = generator.UsingStatement(generator.IdentifierName(identifierName), statements: []); + return new TextChange(TextSpan.FromBounds(position, position), statement.NormalizeWhitespace().ToFullString()); + } +} diff --git a/src/Features/Lsif/GeneratorTest/FoldingRangeTests.vb b/src/Features/Lsif/GeneratorTest/FoldingRangeTests.vb index ed51d59f2626f..07ab30c4f4720 100644 --- a/src/Features/Lsif/GeneratorTest/FoldingRangeTests.vb +++ b/src/Features/Lsif/GeneratorTest/FoldingRangeTests.vb @@ -2,11 +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.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.Test.Utilities Imports Roslyn.LanguageServer.Protocol Imports Roslyn.Test.Utilities -Imports Roslyn.Utilities Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests @@ -52,7 +50,6 @@ using System.Linq;|}", "...")> , openDocuments:=False, composition:=TestLsifOutput.TestComposition) - Dim annotatedLocations = Await AbstractLanguageServerProtocolTests.GetAnnotatedLocationsAsync(workspace, workspace.CurrentSolution) Dim expectedRanges = annotatedLocations.SelectMany(Function(kvp) kvp.Value.Select(Function(location) CreateFoldingRange(kvp.Key, location.Range, collapsedText))).OrderByDescending(Function(range) range.StartLine).ToArray() diff --git a/src/Features/Lsif/GeneratorTest/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests.vbproj b/src/Features/Lsif/GeneratorTest/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests.vbproj index 0a9d98d4bcde6..ac2f33f5aabe0 100644 --- a/src/Features/Lsif/GeneratorTest/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests.vbproj +++ b/src/Features/Lsif/GeneratorTest/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests.vbproj @@ -10,6 +10,7 @@ + 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/Features/VisualBasic/Portable/CodeRefactorings/MoveType/VisualBasicMoveTypeService.vb b/src/Features/VisualBasic/Portable/CodeRefactorings/MoveType/VisualBasicMoveTypeService.vb index 6c2b0e8efd6a6..4ff27cb564f00 100644 --- a/src/Features/VisualBasic/Portable/CodeRefactorings/MoveType/VisualBasicMoveTypeService.vb +++ b/src/Features/VisualBasic/Portable/CodeRefactorings/MoveType/VisualBasicMoveTypeService.vb @@ -20,6 +20,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.MoveType Public Sub New() End Sub + Protected Overrides Function GetSymbolNameAndArity(syntax As TypeBlockSyntax) As (name As String, arity As Integer) + Dim statement = syntax.BlockStatement + Return (statement.Identifier.ValueText, If(statement.TypeParameterList?.Parameters.Count, 0)) + End Function + Protected Overrides Function IsMemberDeclaration(syntaxNode As SyntaxNode) As Boolean Return TypeOf syntaxNode Is MethodBaseSyntax OrElse TypeOf syntaxNode Is MethodBlockBaseSyntax End Function diff --git a/src/Features/VisualBasic/Portable/ConvertToInterpolatedString/VisualBasicConvertPlaceholderToInterpolatedStringRefactoringProvider.vb b/src/Features/VisualBasic/Portable/ConvertToInterpolatedString/VisualBasicConvertPlaceholderToInterpolatedStringRefactoringProvider.vb index 8ef9b016e58ea..0bbc2ea490efe 100644 --- a/src/Features/VisualBasic/Portable/ConvertToInterpolatedString/VisualBasicConvertPlaceholderToInterpolatedStringRefactoringProvider.vb +++ b/src/Features/VisualBasic/Portable/ConvertToInterpolatedString/VisualBasicConvertPlaceholderToInterpolatedStringRefactoringProvider.vb @@ -10,7 +10,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.ConvertToInterpolatedString - Partial Friend Class VisualBasicConvertPlaceholderToInterpolatedStringRefactoringProvider + Partial Friend NotInheritable Class VisualBasicConvertPlaceholderToInterpolatedStringRefactoringProvider Inherits AbstractConvertPlaceholderToInterpolatedStringRefactoringProvider(Of ExpressionSyntax, LiteralExpressionSyntax, 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 83% rename from src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs rename to src/LanguageServer/Protocol.TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs index 29071e6c24179..949301098af8b 100644 --- a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs +++ b/src/LanguageServer/Protocol.TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs @@ -15,16 +15,12 @@ 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; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions; using Microsoft.CodeAnalysis.LanguageServer.Handler.Completion; -using Microsoft.CodeAnalysis.LanguageServer.UnitTests; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; @@ -51,10 +47,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 +100,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 +172,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 +180,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 +271,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 +313,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 +366,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 +382,19 @@ 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) + where TDocument : TestHostDocument + where TProject : TestHostProject + where TSolution : TestHostSolution { var workspaceWaiter = GetWorkspaceWaiter(workspace); await workspaceWaiter.ExpeditedWaitAsync(); } - private static IAsynchronousOperationWaiter GetWorkspaceWaiter(EditorTestWorkspace workspace) + private static IAsynchronousOperationWaiter GetWorkspaceWaiter(TestWorkspace workspace) + where TDocument : TestHostDocument + where TProject : TestHostProject + where TSolution : TestHostSolution { var operations = workspace.ExportProvider.GetExportedValue(); return operations.GetWaiter(FeatureAttribute.Workspace); @@ -419,7 +417,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 +431,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 +442,10 @@ 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(TestWorkspace workspace, Solution solution) + where TDocument : TestHostDocument + where TProject : TestHostProject + where TSolution : TestHostSolution { var locations = new Dictionary>(); foreach (var testDocument in workspace.Documents) @@ -531,44 +532,71 @@ private static LSP.DidCloseTextDocumentParams CreateDidCloseTextDocumentParams(U } }; - internal sealed class TestLspServer : IAsyncDisposable + /// + /// Implementation of + /// using the workspace. + /// + internal sealed class TestLspServer : AbstractTestLspServer { - public readonly EditorTestWorkspace TestWorkspace; - private readonly Dictionary> _locations; + public TestLspServer(LspTestWorkspace testWorkspace, Dictionary> locations, InitializationOptions initializationOptions, AbstractLspLogger logger) + : base(testWorkspace, locations, initializationOptions, logger) + { + } + + public static async Task CreateAsync(LspTestWorkspace testWorkspace, InitializationOptions initializationOptions, AbstractLspLogger logger) + { + var locations = await GetAnnotatedLocationsAsync(testWorkspace, testWorkspace.CurrentSolution); + var server = new TestLspServer(testWorkspace, locations, initializationOptions, logger); + await server.InitializeAsync(); + return server; + } + } + + internal abstract class AbstractTestLspServer : IAsyncDisposable + where TDocument : TestHostDocument + where TProject : TestHostProject + where TSolution : TestHostSolution + where TWorkspace : TestWorkspace + { + public readonly TWorkspace TestWorkspace; private readonly JsonRpc _clientRpc; + private readonly Dictionary> _locations; private readonly ICodeAnalysisDiagnosticAnalyzerService _codeAnalysisService; - - private readonly RoslynLanguageServer LanguageServer; + private readonly InitializationOptions _initializationOptions; + private readonly Lazy _languageServer; public LSP.ClientCapabilities ClientCapabilities { get; } - private TestLspServer( - EditorTestWorkspace testWorkspace, + public AbstractTestLspServer( + TWorkspace testWorkspace, Dictionary> locations, - LSP.ClientCapabilities clientCapabilities, - RoslynLanguageServer target, - Stream clientStream, - object? clientTarget = null, - IJsonRpcMessageFormatter? clientMessageFormatter = null) + InitializationOptions initializationOptions, + AbstractLspLogger logger) { TestWorkspace = testWorkspace; - ClientCapabilities = clientCapabilities; + _initializationOptions = initializationOptions; _locations = locations; _codeAnalysisService = testWorkspace.Services.GetRequiredService(); - LanguageServer = target; + ClientCapabilities = initializationOptions.ClientCapabilities; - clientMessageFormatter ??= RoslynLanguageServer.CreateJsonMessageFormatter(); + var clientMessageFormatter = initializationOptions.ClientMessageFormatter ?? RoslynLanguageServer.CreateJsonMessageFormatter(); - _clientRpc = new JsonRpc(new HeaderDelimitedMessageHandler(clientStream, clientStream, clientMessageFormatter), clientTarget) + var (clientStream, serverStream) = FullDuplexStream.CreatePair(); + + _clientRpc = new JsonRpc(new HeaderDelimitedMessageHandler(clientStream, clientStream, clientMessageFormatter), initializationOptions.ClientTarget) { ExceptionStrategy = ExceptionProcessing.ISerializable, }; - // Workspace listener events do not run in tests, so we manually register the lsp misc workspace. - TestWorkspace.GetService().Register(GetManagerAccessor().GetLspMiscellaneousFilesWorkspace()); + _languageServer = new(() => + { + var server = CreateLanguageServer(serverStream, serverStream, _initializationOptions.ServerKind, logger); + + InitializeClientRpc(); + return server; + }); - InitializeClientRpc(); } private void InitializeClientRpc() @@ -579,56 +607,39 @@ private void InitializeClientRpc() Assert.False(workspaceWaiter.HasPendingWork); } - internal static async Task CreateAsync(EditorTestWorkspace testWorkspace, InitializationOptions initializationOptions, AbstractLspLogger logger) + internal async Task InitializeAsync() { // 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 // created by the initial test steps. This can interfere with the expected test state. - await WaitForWorkspaceOperationsAsync(testWorkspace); + await WaitForWorkspaceOperationsAsync(TestWorkspace); - var locations = await GetAnnotatedLocationsAsync(testWorkspace, testWorkspace.CurrentSolution); - - var (clientStream, serverStream) = FullDuplexStream.CreatePair(); - var languageServer = CreateLanguageServer(serverStream, serverStream, testWorkspace, initializationOptions.ServerKind, logger); + // Initialize the language server + _ = _languageServer.Value; - var server = new TestLspServer(testWorkspace, locations, initializationOptions.ClientCapabilities, languageServer, clientStream, initializationOptions.ClientTarget, initializationOptions.ClientMessageFormatter); + // Workspace listener events do not run in tests, so we manually register the lsp misc workspace. + // This must be done after the language server is created in order to access the misc workspace off of the LSP workspace manager. + TestWorkspace.GetService().Register(GetManagerAccessor().GetLspMiscellaneousFilesWorkspace()); - if (initializationOptions.CallInitialize) + if (_initializationOptions.CallInitialize) { - await server.ExecuteRequestAsync(LSP.Methods.InitializeName, new LSP.InitializeParams + await this.ExecuteRequestAsync(LSP.Methods.InitializeName, new LSP.InitializeParams { - Capabilities = initializationOptions.ClientCapabilities, - Locale = initializationOptions.Locale, + Capabilities = _initializationOptions.ClientCapabilities, + Locale = _initializationOptions.Locale, }, CancellationToken.None); } - if (initializationOptions.CallInitialized) + if (_initializationOptions.CallInitialized) { - await server.ExecuteRequestAsync(LSP.Methods.InitializedName, new LSP.InitializedParams { }, CancellationToken.None); + await this.ExecuteRequestAsync(LSP.Methods.InitializedName, new LSP.InitializedParams { }, CancellationToken.None); } - - return server; - } - - internal static async Task CreateAsync(EditorTestWorkspace testWorkspace, LSP.ClientCapabilities clientCapabilities, RoslynLanguageServer target, Stream clientStream) - { - var locations = await GetAnnotatedLocationsAsync(testWorkspace, testWorkspace.CurrentSolution); - var server = new TestLspServer(testWorkspace, locations, clientCapabilities, target, clientStream); - - await server.ExecuteRequestAsync(LSP.Methods.InitializeName, new LSP.InitializeParams - { - Capabilities = clientCapabilities, - }, CancellationToken.None); - - await server.ExecuteRequestAsync(LSP.Methods.InitializedName, new LSP.InitializedParams { }, CancellationToken.None); - - return server; } - private static RoslynLanguageServer CreateLanguageServer(Stream inputStream, Stream outputStream, EditorTestWorkspace workspace, WellKnownLspServerKinds serverKind, AbstractLspLogger logger) + protected virtual RoslynLanguageServer CreateLanguageServer(Stream inputStream, Stream outputStream, WellKnownLspServerKinds serverKind, AbstractLspLogger logger) { - var capabilitiesProvider = workspace.ExportProvider.GetExportedValue(); - var factory = workspace.ExportProvider.GetExportedValue(); + var capabilitiesProvider = TestWorkspace.ExportProvider.GetExportedValue(); + var factory = TestWorkspace.ExportProvider.GetExportedValue(); var jsonMessageFormatter = RoslynLanguageServer.CreateJsonMessageFormatter(); var jsonRpc = new JsonRpc(new HeaderDelimitedMessageHandler(outputStream, inputStream, jsonMessageFormatter)) @@ -636,7 +647,7 @@ private static RoslynLanguageServer CreateLanguageServer(Stream inputStream, Str ExceptionStrategy = ExceptionProcessing.ISerializable, }; - var languageServer = (RoslynLanguageServer)factory.Create(jsonRpc, jsonMessageFormatter.JsonSerializerOptions, capabilitiesProvider, serverKind, logger, workspace.Services.HostServices); + var languageServer = (RoslynLanguageServer)factory.Create(jsonRpc, jsonMessageFormatter.JsonSerializerOptions, capabilitiesProvider, serverKind, logger, TestWorkspace.Services.HostServices); jsonRpc.StartListening(); return languageServer; @@ -698,6 +709,33 @@ 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); + + 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( @@ -741,7 +779,7 @@ public async Task ExitTestServerAsync() // of the request itself since it will throw a ConnectionLostException. // Instead we wait for the server's exit task to be completed. await _clientRpc.NotifyAsync(LSP.Methods.ExitName).ConfigureAwait(false); - await LanguageServer.WaitForExitAsync().ConfigureAwait(false); + await _languageServer.Value.WaitForExitAsync().ConfigureAwait(false); } public IList GetLocations(string locationName) => _locations[locationName]; @@ -772,15 +810,15 @@ internal async Task WaitForDiagnosticsAsync() await listenerProvider.GetWaiter(FeatureAttribute.DiagnosticService).ExpeditedWaitAsync(); } - internal RequestExecutionQueue.TestAccessor? GetQueueAccessor() => LanguageServer.GetTestAccessor().GetQueueAccessor(); + internal RequestExecutionQueue.TestAccessor? GetQueueAccessor() => _languageServer.Value.GetTestAccessor().GetQueueAccessor(); internal LspWorkspaceManager.TestAccessor GetManagerAccessor() => GetRequiredLspService().GetTestAccessor(); internal LspWorkspaceManager GetManager() => GetRequiredLspService(); - internal AbstractLanguageServer.TestAccessor GetServerAccessor() => LanguageServer.GetTestAccessor(); + internal AbstractLanguageServer.TestAccessor GetServerAccessor() => _languageServer.Value.GetTestAccessor(); - internal T GetRequiredLspService() where T : class, ILspService => LanguageServer.GetTestAccessor().GetRequiredLspService(); + internal T GetRequiredLspService() where T : class, ILspService => _languageServer.Value.GetTestAccessor().GetRequiredLspService(); internal ImmutableArray GetTrackedTexts() => [.. GetManager().GetTrackedLspText().Values.Select(v => v.Text)]; @@ -794,14 +832,14 @@ public async ValueTask DisposeAsync() // Some tests will manually call shutdown and exit, so attempting to call this during dispose // will fail as the server's jsonrpc instance will be disposed of. - if (!LanguageServer.GetTestAccessor().HasShutdownStarted()) + if (!_languageServer.Value.GetTestAccessor().HasShutdownStarted()) { await ShutdownTestServerAsync(); await ExitTestServerAsync(); } // Wait for all the exit notifications to run to completion. - await LanguageServer.WaitForExitAsync(); + await _languageServer.Value.WaitForExitAsync(); TestWorkspace.Dispose(); _clientRpc.Dispose(); 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 95% rename from src/EditorFeatures/TestUtilities/LanguageServer/TestOutputLspLogger.cs rename to src/LanguageServer/Protocol.TestUtilities/LanguageServer/TestOutputLspLogger.cs index c8ba5e8bdbd99..8b7dfa57e4db3 100644 --- a/src/EditorFeatures/TestUtilities/LanguageServer/TestOutputLspLogger.cs +++ b/src/LanguageServer/Protocol.TestUtilities/LanguageServer/TestOutputLspLogger.cs @@ -3,10 +3,11 @@ // See the LICENSE file in the project root for more information. using System; +using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CommonLanguageServerProtocol.Framework; using Xunit.Abstractions; -namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests; +namespace Roslyn.Test.Utilities; internal sealed class TestOutputLspLogger : AbstractLspLogger, ILspService { diff --git a/src/EditorFeatures/TestUtilities/LanguageServer/TestWorkspaceRegistrationService.cs b/src/LanguageServer/Protocol.TestUtilities/LanguageServer/TestWorkspaceRegistrationService.cs similarity index 50% rename from src/EditorFeatures/TestUtilities/LanguageServer/TestWorkspaceRegistrationService.cs rename to src/LanguageServer/Protocol.TestUtilities/LanguageServer/TestWorkspaceRegistrationService.cs index f83897f0c6930..db393fa2ad034 100644 --- a/src/EditorFeatures/TestUtilities/LanguageServer/TestWorkspaceRegistrationService.cs +++ b/src/LanguageServer/Protocol.TestUtilities/LanguageServer/TestWorkspaceRegistrationService.cs @@ -10,15 +10,9 @@ namespace Roslyn.Test.Utilities; -public abstract partial class AbstractLanguageServerProtocolTests +[Export(typeof(LspWorkspaceRegistrationService)), Shared, PartNotDiscoverable] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal class TestWorkspaceRegistrationService() : LspWorkspaceRegistrationService { - [Export(typeof(LspWorkspaceRegistrationService)), Shared, PartNotDiscoverable] - internal class TestWorkspaceRegistrationService : LspWorkspaceRegistrationService - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public TestWorkspaceRegistrationService() - { - } - } } 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..91693179de616 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,10 @@ + + + + + \ 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/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/Protocol/Features/Diagnostics/DocumentAnalysisExecutor_Helpers.cs b/src/LanguageServer/Protocol/Features/Diagnostics/DocumentAnalysisExecutor_Helpers.cs index 7de75d8b668ab..ef96728725dcb 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/DocumentAnalysisExecutor_Helpers.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/DocumentAnalysisExecutor_Helpers.cs @@ -5,13 +5,9 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs index 09efb8642d42b..52dde0ab537aa 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs @@ -18,13 +18,13 @@ 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. /// - 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,25 +36,30 @@ internal partial class DiagnosticAnalyzerService if (!project.SupportsCompilation) return null; + var projectState = project.State; + var checksum = await project.GetDependentChecksumAsync(cancellationToken).ConfigureAwait(false); + // 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) || + tupleBox.Value.checksum != checksum || !analyzers.IsSubsetOf(tupleBox.Value.analyzers)) { - var compilationWithAnalyzersPair = await CreateCompilationWithAnalyzersAsync().ConfigureAwait(false); - tupleBox = new((analyzers, compilationWithAnalyzersPair)); + var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); + var compilationWithAnalyzersPair = CreateCompilationWithAnalyzers(projectState, compilation); + tupleBox = new((checksum, analyzers, compilationWithAnalyzersPair)); #if NET - s_projectToCompilationWithAnalyzers.AddOrUpdate(project, tupleBox); + s_projectToCompilationWithAnalyzers.AddOrUpdate(projectState, 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 - 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); #endif } @@ -63,13 +68,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()); @@ -83,9 +87,6 @@ internal partial class DiagnosticAnalyzerService return null; } - Contract.ThrowIfFalse(project.SupportsCompilation); - AssertCompilation(project, compilation); - var exceptionFilter = (Exception ex) => { if (ex is not OperationCanceledException && crashOnAnalyzerException) @@ -105,7 +106,7 @@ internal partial class DiagnosticAnalyzerService var projectCompilation = !filteredProjectAnalyzers.Any() ? null : compilation.WithAnalyzers(filteredProjectAnalyzers, new CompilationWithAnalyzersOptions( - options: project.AnalyzerOptions, + options: project.ProjectAnalyzerOptions, onAnalyzerException: null, analyzerExceptionFilter: exceptionFilter, concurrentAnalysis: false, @@ -126,12 +127,4 @@ internal partial class DiagnosticAnalyzerService return new CompilationWithAnalyzersPair(projectCompilation, hostCompilation); } } - - [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.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..3c52425994a08 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,18 +54,18 @@ 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 analyzersPerReference = hostAnalyzers.CreateProjectDiagnosticAnalyzersPerReference(project); + var solutionAnalyzers = solution.Analyzers; + var analyzersPerReference = solutionAnalyzers.CreateProjectDiagnosticAnalyzersPerReference(project); if (analyzersPerReference.Count == 0) { return ProjectAnalyzerInfo.Default; @@ -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 = solutionAnalyzers.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 cd07e28f14daf..2a46388f88f88 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs @@ -2,16 +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; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -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; @@ -47,17 +43,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 e968d7d55b57a..65beecdfe360d 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -50,7 +50,7 @@ public DiagnosticIncrementalAnalyzer( internal DiagnosticAnalyzerInfoCache DiagnosticAnalyzerInfoCache => _diagnosticAnalyzerRunner.AnalyzerInfoCache; public Task> GetAnalyzersForTestingPurposesOnlyAsync(Project project, CancellationToken cancellationToken) - => _stateManager.GetOrCreateAnalyzersAsync(project, cancellationToken); + => _stateManager.GetOrCreateAnalyzersAsync(project.Solution.SolutionState, project.State, cancellationToken); 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_GetDiagnostics.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index 5d6c9799b1038..78746d3e3c66d 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -60,8 +60,11 @@ private async Task> ProduceProjectDiagnosticsAsyn { using var _ = ArrayBuilder.GetInstance(out var builder); - var analyzersForProject = await _stateManager.GetOrCreateAnalyzersAsync(project, cancellationToken).ConfigureAwait(false); - var hostAnalyzerInfo = await _stateManager.GetOrCreateHostAnalyzerInfoAsync(project, cancellationToken).ConfigureAwait(false); + var solution = project.Solution; + var analyzersForProject = await _stateManager.GetOrCreateAnalyzersAsync( + solution.SolutionState, project.State, cancellationToken).ConfigureAwait(false); + var hostAnalyzerInfo = await _stateManager.GetOrCreateHostAnalyzerInfoAsync( + solution.SolutionState, project.State, cancellationToken).ConfigureAwait(false); var analyzers = analyzersForProject.WhereAsArray(a => ShouldIncludeAnalyzer(project, a)); var result = await GetOrComputeDiagnosticAnalysisResultsAsync(analyzers).ConfigureAwait(false); @@ -108,17 +111,19 @@ 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* 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: the caller will loop over *its* analyzers, 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) && + if (s_projectToForceAnalysisData.TryGetValue(project.State, out var box) && analyzers.IsSubsetOf(box.Value.analyzers)) { - return box.Value.diagnosticAnalysisResults; + var checksum = await project.GetDependentChecksumAsync(cancellationToken).ConfigureAwait(false); + if (box.Value.checksum == checksum) + return box.Value.diagnosticAnalysisResults; } // Otherwise, just compute for the analyzers we care about. diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs index f53626179aa28..48da4ffee222a 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs @@ -59,12 +59,14 @@ public async Task> GetDiagnosticsForSpanAsync( { var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); + var project = document.Project; + var solutionState = project.Solution.SolutionState; var unfilteredAnalyzers = await _stateManager - .GetOrCreateAnalyzersAsync(document.Project, cancellationToken) + .GetOrCreateAnalyzersAsync(solutionState, project.State, cancellationToken) .ConfigureAwait(false); var analyzers = unfilteredAnalyzers .WhereAsArray(a => DocumentAnalysisExecutor.IsAnalyzerEnabledForProject(a, document.Project, GlobalOptions)); - var hostAnalyzerInfo = await _stateManager.GetOrCreateHostAnalyzerInfoAsync(document.Project, cancellationToken).ConfigureAwait(false); + var hostAnalyzerInfo = await _stateManager.GetOrCreateHostAnalyzerInfoAsync(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. @@ -229,7 +231,7 @@ async Task ComputeDocumentDiagnosticsAsync( analyzers = filteredAnalyzers.ToImmutable(); - var hostAnalyzerInfo = await _stateManager.GetOrCreateHostAnalyzerInfoAsync(document.Project, cancellationToken).ConfigureAwait(false); + var hostAnalyzerInfo = await _stateManager.GetOrCreateHostAnalyzerInfoAsync(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); diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 99147ea7972d1..889161e4e2647 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics; @@ -20,34 +19,46 @@ 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. /// - private readonly ConditionalWeakTable analyzers, ImmutableDictionary diagnosticAnalysisResults)>> _projectToForceAnalysisData = new(); + /// + /// 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 static readonly ConditionalWeakTable analyzers, ImmutableDictionary diagnosticAnalysisResults)>> s_projectToForceAnalysisData = new(); public async Task> ForceAnalyzeProjectAsync(Project project, CancellationToken cancellationToken) { + var projectState = project.State; + var checksum = await project.GetDependentChecksumAsync(cancellationToken).ConfigureAwait(false); + try { - if (!_projectToForceAnalysisData.TryGetValue(project, out var box)) + if (!s_projectToForceAnalysisData.TryGetValue(projectState, out var box) || + box.Value.checksum != checksum) { 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.TryAdd(project, box); - Contract.ThrowIfFalse(_projectToForceAnalysisData.TryGetValue(project, out box)); + if (!s_projectToForceAnalysisData.TryAdd(projectState, box)) + Contract.ThrowIfFalse(s_projectToForceAnalysisData.TryGetValue(projectState, out box)); #else - box = _projectToForceAnalysisData.GetValue(project, _ => box); + box = s_projectToForceAnalysisData.GetValue(projectState, _ => box); #endif } using var _ = ArrayBuilder.GetInstance(out var diagnostics); - var (analyzers, projectAnalysisData) = box.Value; + var (_, analyzers, projectAnalysisData) = box.Value; foreach (var analyzer in analyzers) { if (projectAnalysisData.TryGetValue(analyzer, out var analyzerResult)) @@ -61,10 +72,11 @@ public async Task> ForceAnalyzeProjectAsync(Proje throw ExceptionUtilities.Unreachable(); } - async Task<(ImmutableArray analyzers, ImmutableDictionary diagnosticAnalysisResults)> ComputeForceAnalyzeProjectAsync() + async Task<(Checksum checksum, 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) => IsCandidateForFullSolutionAnalysis( @@ -75,7 +87,7 @@ public async Task> ForceAnalyzeProjectAsync(Proje project, fullSolutionAnalysisAnalyzers, hostAnalyzerInfo, AnalyzerService.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); var projectAnalysisData = await ComputeDiagnosticAnalysisResultsAsync(compilationWithAnalyzers, project, fullSolutionAnalysisAnalyzers, cancellationToken).ConfigureAwait(false); - return (fullSolutionAnalysisAnalyzers, projectAnalysisData); + return (checksum, fullSolutionAnalysisAnalyzers, projectAnalysisData); } static bool IsCandidateForFullSolutionAnalysis( 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/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 ba8b1838059ff..3948eb2317890 100644 --- a/src/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj +++ b/src/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj @@ -37,10 +37,22 @@ + + + + - + + + + + + + + + @@ -90,6 +102,7 @@ + diff --git a/src/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticKind.cs b/src/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticKind.cs index 3ac8099a356ec..1956ef22d949c 100644 --- a/src/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticKind.cs +++ b/src/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticKind.cs @@ -22,6 +22,11 @@ internal readonly record struct VSInternalDiagnosticKind(string Value) : IString /// /// Edit and Continue diagnostic kind. /// - public static readonly VSInternalDiagnosticKind EditAndContiue = new("enc"); + public static readonly VSInternalDiagnosticKind EditAndContinue = new("enc"); + + /// + /// Syntax diagnostic kind. + /// + 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. /// 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..e04e6459b4a65 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 = @@ -90,13 +90,13 @@ 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); } - [WpfTheory, CombinatorialData] + [Theory, CombinatorialData] public async Task TestCodeActionHasCorrectDiagnostics(bool mutatingLspWorkspace) { var markup = @@ -134,11 +134,11 @@ 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); } - [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 = """ @@ -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,10 +218,10 @@ 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); } - [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 = """ @@ -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/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/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 12c2b718c5e91..04875a010bc6f 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; @@ -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/PullDiagnosticTests.cs b/src/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs index 07731ea745e5e..7b9becddaa4dd 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); @@ -66,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")] @@ -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); @@ -92,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); @@ -109,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,17 +158,14 @@ 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); 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); } @@ -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); @@ -194,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); } @@ -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); @@ -220,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] @@ -231,13 +216,11 @@ 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); 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] @@ -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. @@ -259,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); @@ -278,16 +258,13 @@ 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); 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( @@ -304,16 +281,13 @@ 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); 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; @@ -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); + 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,25 +328,22 @@ 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); + 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( 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] @@ -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); @@ -397,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] @@ -407,16 +373,13 @@ 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); 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] @@ -449,18 +412,21 @@ 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); 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. @@ -471,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)); } } @@ -500,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) @@ -561,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; @@ -613,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 @@ -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); @@ -645,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] @@ -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); @@ -668,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] @@ -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))); @@ -807,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); } } @@ -831,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")] @@ -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); @@ -882,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 @@ -914,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); } @@ -933,16 +887,15 @@ 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); // 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)); @@ -974,16 +927,15 @@ 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); // 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)); @@ -1021,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] @@ -1056,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] @@ -1088,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] @@ -1126,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] @@ -1270,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] @@ -1322,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); } @@ -1390,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); } @@ -1405,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); @@ -1431,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); @@ -1452,12 +1408,11 @@ 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); - 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)); @@ -1478,27 +1433,21 @@ 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); - 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); - 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); @@ -1517,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); @@ -1540,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); } @@ -1585,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; @@ -1667,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. @@ -1691,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. @@ -1741,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. @@ -1810,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(); @@ -1829,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] @@ -1872,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(); @@ -1928,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(); @@ -2098,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(); @@ -2143,9 +2092,17 @@ 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); } + 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/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/Microsoft.CodeAnalysis.LanguageServer.Protocol.UnitTests.csproj b/src/LanguageServer/ProtocolUnitTests/Microsoft.CodeAnalysis.LanguageServer.Protocol.UnitTests.csproj index 1cf6c995899f4..77dcec96afa1b 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,11 +36,9 @@ + - - - 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/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/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/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/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))); 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]); diff --git a/src/Tools/BuildActionTelemetryTable/Program.cs b/src/Tools/BuildActionTelemetryTable/Program.cs index ed7802956b81f..57e8189c34faa 100644 --- a/src/Tools/BuildActionTelemetryTable/Program.cs +++ b/src/Tools/BuildActionTelemetryTable/Program.cs @@ -76,7 +76,7 @@ public static class Program { "Microsoft.CodeAnalysis.CodeStyle.CSharpFormattingCodeFixProvider", "Formatting" }, { "Microsoft.CodeAnalysis.CodeStyle.VisualBasicFormattingCodeFixProvider", "Formatting" }, { "Microsoft.CodeAnalysis.ConvertToInterpolatedString.ConvertRegularStringToInterpolatedStringRefactoringProvider", "Convert To Interpolated String: Convert Regular String To Interpolated String (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.AddAccessibilityModifiers.CSharpAddAccessibilityModifiersCodeFixProvider", "Add Accessibility Modifiers" }, + { "Microsoft.CodeAnalysis.CSharp.AddOrRemoveAccessibilityModifiers.CSharpAddOrRemoveAccessibilityModifiersCodeFixProvider", "Add Accessibility Modifiers" }, { "Microsoft.CodeAnalysis.CSharp.AddAnonymousTypeMemberName.CSharpAddAnonymousTypeMemberNameCodeFixProvider", "Add Anonymous Type Member Name" }, { "Microsoft.CodeAnalysis.CSharp.AddDebuggerDisplay.CSharpAddDebuggerDisplayCodeRefactoringProvider", "Add Debugger Display (Refactoring)" }, { "Microsoft.CodeAnalysis.CSharp.AddFileBanner.CSharpAddFileBannerCodeRefactoringProvider", "Add File Banner (Refactoring)" }, @@ -312,7 +312,7 @@ public static class Program { "Microsoft.CodeAnalysis.UseExplicitTupleName.UseExplicitTupleNameCodeFixProvider", "Use Explicit Tuple Name" }, { "Microsoft.CodeAnalysis.UseSystemHashCode.UseSystemHashCodeCodeFixProvider", "Use System Hash Code" }, { "Microsoft.CodeAnalysis.UseThrowExpression.UseThrowExpressionCodeFixProvider", "Use Throw Expression" }, - { "Microsoft.CodeAnalysis.VisualBasic.AddAccessibilityModifiers.VisualBasicAddAccessibilityModifiersCodeFixProvider", "Add Accessibility Modifiers" }, + { "Microsoft.CodeAnalysis.VisualBasic.AddOrRemoveAccessibilityModifiers.VisualBasicAddOrRemoveAccessibilityModifiersCodeFixProvider", "Add Accessibility Modifiers" }, { "Microsoft.CodeAnalysis.VisualBasic.AddAnonymousTypeMemberName.VisualBasicAddAnonymousTypeMemberNameCodeFixProvider", "Add Anonymous Type Member Name" }, { "Microsoft.CodeAnalysis.VisualBasic.AddDebuggerDisplay.VisualBasicAddDebuggerDisplayCodeRefactoringProvider", "Add Debugger Display (Refactoring)" }, { "Microsoft.CodeAnalysis.VisualBasic.AddFileBanner.VisualBasicAddFileBannerCodeRefactoringProvider", "Add File Banner (Refactoring)" }, diff --git a/src/Tools/ExternalAccess/Razor/RazorPredefinedCodeFixProviderNames.cs b/src/Tools/ExternalAccess/Razor/RazorPredefinedCodeFixProviderNames.cs index 34bd12e2e12f1..991e600c2e763 100644 --- a/src/Tools/ExternalAccess/Razor/RazorPredefinedCodeFixProviderNames.cs +++ b/src/Tools/ExternalAccess/Razor/RazorPredefinedCodeFixProviderNames.cs @@ -8,7 +8,7 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Razor { internal static class RazorPredefinedCodeFixProviderNames { - public static string AddAccessibilityModifiers => PredefinedCodeFixProviderNames.AddAccessibilityModifiers; + public static string AddAccessibilityModifiers => PredefinedCodeFixProviderNames.AddOrRemoveAccessibilityModifiers; public static string AddAnonymousTypeMemberName => PredefinedCodeFixProviderNames.AddAnonymousTypeMemberName; public static string AddAsync => PredefinedCodeFixProviderNames.AddAsync; public static string AddBraces => PredefinedCodeFixProviderNames.AddBraces; diff --git a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt index 7f8d403ef4ae5..ae3b53b6e80d9 100644 --- a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt +++ b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt @@ -542,6 +542,7 @@ Microsoft.CodeAnalysis.Diagnostics.AnalyzerFileReference.GetGenerators Microsoft.CodeAnalysis.Diagnostics.AnalyzerFileReference.GetGenerators(System.String) Microsoft.CodeAnalysis.Diagnostics.AnalyzerFileReference.GetGeneratorsForAllLanguages Microsoft.CodeAnalysis.Diagnostics.AnalyzerFileReference.GetHashCode +Microsoft.CodeAnalysis.Diagnostics.AnalyzerFileReference.ToString Microsoft.CodeAnalysis.Diagnostics.AnalyzerFileReference.add_AnalyzerLoadFailed(System.EventHandler{Microsoft.CodeAnalysis.Diagnostics.AnalyzerLoadFailureEventArgs}) Microsoft.CodeAnalysis.Diagnostics.AnalyzerFileReference.get_AssemblyLoader Microsoft.CodeAnalysis.Diagnostics.AnalyzerFileReference.get_Display diff --git a/src/VisualStudio/CSharp/Test/DocumentOutline/DocumentOutlineTestsBase.cs b/src/VisualStudio/CSharp/Test/DocumentOutline/DocumentOutlineTestsBase.cs index 5b549fcc253d8..9df6aa70e214d 100644 --- a/src/VisualStudio/CSharp/Test/DocumentOutline/DocumentOutlineTestsBase.cs +++ b/src/VisualStudio/CSharp/Test/DocumentOutline/DocumentOutlineTestsBase.cs @@ -2,6 +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.Linq; using System.Runtime.Serialization; using System.Threading; @@ -9,17 +10,17 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.CodeAnalysis.Editor.Test; using Microsoft.CodeAnalysis.Editor.UnitTests; using Microsoft.CodeAnalysis.LanguageServer.Handler; -using Microsoft.CodeAnalysis.LanguageServer.UnitTests; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; +using Microsoft.CommonLanguageServerProtocol.Framework; using Microsoft.VisualStudio.LanguageServer.Client; using Microsoft.VisualStudio.LanguageServices.DocumentOutline; using Microsoft.VisualStudio.Text; +using Roslyn.LanguageServer.Protocol; +using Roslyn.Test.Utilities; using StreamJsonRpc; using Xunit.Abstractions; using static Roslyn.Test.Utilities.AbstractLanguageServerProtocolTests; @@ -94,7 +95,7 @@ protected async Task CreateMocksAsync(string code) } } - private async Task CreateTestLspServerAsync(EditorTestWorkspace workspace) + private async Task CreateTestLspServerAsync(EditorTestWorkspace workspace) { var solution = workspace.CurrentSolution; @@ -118,10 +119,25 @@ private async Task CreateTestLspServerAsync(EditorTestWorkspace w solution = solution.WithAnalyzerReferences([new TestAnalyzerReferenceByLanguage(DiagnosticExtensions.GetCompilerDiagnosticAnalyzersMap())]); await workspace.ChangeSolutionAsync(solution); - var server = await TestLspServer.CreateAsync(workspace, new InitializationOptions(), _logger); + var server = await EditorTestLspServer.CreateAsync(workspace, new InitializationOptions(), _logger); return server; } + internal class EditorTestLspServer : AbstractTestLspServer + { + private EditorTestLspServer(EditorTestWorkspace testWorkspace, Dictionary> locations, InitializationOptions options, AbstractLspLogger logger) : base(testWorkspace, locations, options, logger) + { + } + + public static async Task CreateAsync(EditorTestWorkspace testWorkspace, InitializationOptions initializationOptions, AbstractLspLogger logger) + { + var locations = await GetAnnotatedLocationsAsync(testWorkspace, testWorkspace.CurrentSolution); + var server = new EditorTestLspServer(testWorkspace, locations, initializationOptions, logger); + await server.InitializeAsync(); + return server; + } + } + [DataContract] private class NewtonsoftInitializeParams { diff --git a/src/VisualStudio/CSharp/Test/Microsoft.VisualStudio.LanguageServices.CSharp.UnitTests.csproj b/src/VisualStudio/CSharp/Test/Microsoft.VisualStudio.LanguageServices.CSharp.UnitTests.csproj index cab7be9c43233..3b17836fefa1e 100644 --- a/src/VisualStudio/CSharp/Test/Microsoft.VisualStudio.LanguageServices.CSharp.UnitTests.csproj +++ b/src/VisualStudio/CSharp/Test/Microsoft.VisualStudio.LanguageServices.CSharp.UnitTests.csproj @@ -16,6 +16,7 @@ + diff --git a/src/VisualStudio/Core/Def/CodeCleanup/CommonCodeCleanUpFixerDiagnosticIds.cs b/src/VisualStudio/Core/Def/CodeCleanup/CommonCodeCleanUpFixerDiagnosticIds.cs index 45bd1af61476a..5be87f357eafc 100644 --- a/src/VisualStudio/Core/Def/CodeCleanup/CommonCodeCleanUpFixerDiagnosticIds.cs +++ b/src/VisualStudio/Core/Def/CodeCleanup/CommonCodeCleanUpFixerDiagnosticIds.cs @@ -31,18 +31,18 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds public static readonly FixIdDefinition? RemoveQualificationDiagnosticId; [Export] - [FixId(IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId)] - [Name(IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId)] + [FixId(IDEDiagnosticIds.AddOrRemoveAccessibilityModifiersDiagnosticId)] + [Name(IDEDiagnosticIds.AddOrRemoveAccessibilityModifiersDiagnosticId)] [Order(After = IDEDiagnosticIds.AddBracesDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId}")] + [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.AddOrRemoveAccessibilityModifiersDiagnosticId}")] [LocalizedName(typeof(AnalyzersResources), nameof(AnalyzersResources.Add_accessibility_modifiers))] - public static readonly FixIdDefinition? AddAccessibilityModifiersDiagnosticId; + public static readonly FixIdDefinition? AddOrRemoveAccessibilityModifiersDiagnosticId; [Export] [FixId(IDEDiagnosticIds.OrderModifiersDiagnosticId)] [Name(IDEDiagnosticIds.OrderModifiersDiagnosticId)] - [Order(After = IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId)] + [Order(After = IDEDiagnosticIds.AddOrRemoveAccessibilityModifiersDiagnosticId)] [ConfigurationKey("unused")] [HelpLink($"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.OrderModifiersDiagnosticId}")] [LocalizedName(typeof(AnalyzersResources), nameof(AnalyzersResources.Order_modifiers))] diff --git a/src/VisualStudio/Core/Def/ColorSchemes/VisualStudio2017.xml b/src/VisualStudio/Core/Def/ColorSchemes/VisualStudio2017.xml index ba5c889c9428f..2cf5c5cfa8692 100644 --- a/src/VisualStudio/Core/Def/ColorSchemes/VisualStudio2017.xml +++ b/src/VisualStudio/Core/Def/ColorSchemes/VisualStudio2017.xml @@ -790,7 +790,8 @@ - + + diff --git a/src/VisualStudio/Core/Def/ColorSchemes/VisualStudio2019.xml b/src/VisualStudio/Core/Def/ColorSchemes/VisualStudio2019.xml index 99a3dbfdf8685..7dc8bafcedef3 100644 --- a/src/VisualStudio/Core/Def/ColorSchemes/VisualStudio2019.xml +++ b/src/VisualStudio/Core/Def/ColorSchemes/VisualStudio2019.xml @@ -817,7 +817,8 @@ - + + diff --git a/src/VisualStudio/Core/Def/LanguageService/AbstractPackage.cs b/src/VisualStudio/Core/Def/LanguageService/AbstractPackage.cs index e6babc4236130..64ff1b7dde1fb 100644 --- a/src/VisualStudio/Core/Def/LanguageService/AbstractPackage.cs +++ b/src/VisualStudio/Core/Def/LanguageService/AbstractPackage.cs @@ -35,16 +35,17 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke Assumes.Present(_componentModel_doNotAccessDirectly); } - protected override Task OnAfterPackageLoadedAsync(CancellationToken cancellationToken) + protected override async Task OnAfterPackageLoadedAsync(CancellationToken cancellationToken) { + await base.OnAfterPackageLoadedAsync(cancellationToken).ConfigureAwait(false); + // TODO: remove, workaround for https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1985204 var globalOptions = ComponentModel.GetService(); if (globalOptions.GetOption(SemanticSearchFeatureFlag.Enabled)) { + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); UIContext.FromUIContextGuid(new Guid(SemanticSearchFeatureFlag.UIContextId)).IsActive = true; } - - return base.OnAfterPackageLoadedAsync(cancellationToken); } protected async Task LoadComponentsInUIContextOnceSolutionFullyLoadedAsync(CancellationToken cancellationToken) diff --git a/src/VisualStudio/LiveShare/Test/AbstractLiveShareRequestHandlerTests.cs b/src/VisualStudio/LiveShare/Test/AbstractLiveShareRequestHandlerTests.cs index 7c54bc3a2e028..013e914955982 100644 --- a/src/VisualStudio/LiveShare/Test/AbstractLiveShareRequestHandlerTests.cs +++ b/src/VisualStudio/LiveShare/Test/AbstractLiveShareRequestHandlerTests.cs @@ -63,7 +63,7 @@ protected static async Task TestHandleAsync GetHandler(Solution solution, string methodName) { - var workspace = (EditorTestWorkspace)solution.Workspace; + var workspace = (LspTestWorkspace)solution.Workspace; var handlers = workspace.ExportProvider.GetExportedValues(LiveShareConstants.RoslynContractName); return (ILspRequestHandler)handlers.Single(handler => handler is ILspRequestHandler && IsMatchingMethod(handler, methodName)); diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs b/src/Workspaces/Core/Portable/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs index 3064aa9009685..6c97f2080b6e3 100644 --- a/src/Workspaces/Core/Portable/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs +++ b/src/Workspaces/Core/Portable/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs @@ -6,7 +6,6 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -28,9 +27,7 @@ internal abstract partial class SyntaxEditorBasedCodeRefactoringProvider : CodeR return FixAllProvider.Create( async (fixAllContext, document, fixAllSpans) => - { - return await this.FixAllAsync(document, fixAllSpans, fixAllContext.CodeActionEquivalenceKey, fixAllContext.CancellationToken).ConfigureAwait(false); - }, + await this.FixAllAsync(document, fixAllSpans, fixAllContext.CodeActionEquivalenceKey, fixAllContext.CancellationToken).ConfigureAwait(false), SupportedFixAllScopes); } 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/Serialization/SerializerService_Reference.cs b/src/Workspaces/Core/Portable/Serialization/SerializerService_Reference.cs index 7bb2618b19b0a..9c1adb44c8940 100644 --- a/src/Workspaces/Core/Portable/Serialization/SerializerService_Reference.cs +++ b/src/Workspaces/Core/Portable/Serialization/SerializerService_Reference.cs @@ -15,6 +15,7 @@ namespace Microsoft.CodeAnalysis.Serialization; +using static Microsoft.CodeAnalysis.Serialization.SerializerService.TestAccessor; using static TemporaryStorageService; internal partial class SerializerService @@ -29,18 +30,18 @@ internal partial class SerializerService /// pretend that a is a during tests. /// private static readonly object s_analyzerImageReferenceMapGate = new(); - private static IBidirectionalMap s_analyzerImageReferenceMap = BidirectionalMap.Empty; + private static IBidirectionalMap s_analyzerReferenceMap = BidirectionalMap.Empty; private static bool TryGetAnalyzerImageReferenceGuid(AnalyzerImageReference imageReference, out Guid guid) { lock (s_analyzerImageReferenceMapGate) - return s_analyzerImageReferenceMap.TryGetValue(imageReference, out guid); + return s_analyzerReferenceMap.TryGetValue(imageReference, out guid); } - private static bool TryGetAnalyzerImageReferenceFromGuid(Guid guid, [NotNullWhen(true)] out AnalyzerImageReference? imageReference) + private static bool TryGetAnalyzerImageReferenceFromGuid(Guid guid, [NotNullWhen(true)] out AnalyzerReference? analyzerReference) { lock (s_analyzerImageReferenceMapGate) - return s_analyzerImageReferenceMap.TryGetKey(guid, out imageReference); + return s_analyzerReferenceMap.TryGetKey(guid, out analyzerReference); } private static Checksum CreateChecksum(MetadataReference reference) @@ -78,6 +79,12 @@ protected virtual Checksum CreateChecksum(AnalyzerReference reference) writer.WriteGuid(guid); break; + case IAnalyzerReferenceWithGuid analyzerReferenceWithGuid: + lock (s_analyzerImageReferenceMapGate) + s_analyzerReferenceMap = s_analyzerReferenceMap.Add(reference, analyzerReferenceWithGuid.Guid); + writer.WriteGuid(analyzerReferenceWithGuid.Guid); + break; + default: throw ExceptionUtilities.UnexpectedValue(reference); } @@ -482,12 +489,6 @@ private static unsafe void WriteTo(MetadataReader reader, ObjectWriter writer) writer.WriteSpan(new ReadOnlySpan(reader.MetadataPointer, reader.MetadataLength)); } - private static void WriteUnresolvedAnalyzerReferenceTo(AnalyzerReference reference, ObjectWriter writer) - { - writer.WriteString(nameof(UnresolvedAnalyzerReference)); - writer.WriteString(reference.FullPath); - } - private static Metadata? TryGetMetadata(PortableExecutableReference reference) { try @@ -541,9 +542,23 @@ public static void AddAnalyzerImageReference(AnalyzerImageReference analyzerImag { lock (s_analyzerImageReferenceMapGate) { - if (!s_analyzerImageReferenceMap.ContainsKey(analyzerImageReference)) - s_analyzerImageReferenceMap = s_analyzerImageReferenceMap.Add(analyzerImageReference, Guid.NewGuid()); + if (!s_analyzerReferenceMap.ContainsKey(analyzerImageReference)) + s_analyzerReferenceMap = s_analyzerReferenceMap.Add(analyzerImageReference, Guid.NewGuid()); } } + + public static void AddAnalyzerImageReferences(IReadOnlyList analyzerReferences) + { + foreach (var analyzer in analyzerReferences) + { + if (analyzer is AnalyzerImageReference analyzerImageReference) + AddAnalyzerImageReference(analyzerImageReference); + } + } + + public interface IAnalyzerReferenceWithGuid + { + Guid Guid { get; } + } } } 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); diff --git a/src/Workspaces/CoreTestUtilities/TestAnalyzerReferenceByLanguage.cs b/src/Workspaces/CoreTestUtilities/TestAnalyzerReferenceByLanguage.cs index c8a762042ae32..5371d55acc443 100644 --- a/src/Workspaces/CoreTestUtilities/TestAnalyzerReferenceByLanguage.cs +++ b/src/Workspaces/CoreTestUtilities/TestAnalyzerReferenceByLanguage.cs @@ -6,11 +6,14 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using Microsoft.CodeAnalysis.Serialization; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics { - internal class TestAnalyzerReferenceByLanguage : AnalyzerReference + internal class TestAnalyzerReferenceByLanguage : + AnalyzerReference, + SerializerService.TestAccessor.IAnalyzerReferenceWithGuid { private readonly IReadOnlyDictionary> _analyzersMap; @@ -28,6 +31,7 @@ public TestAnalyzerReferenceByLanguage(IReadOnlyDictionary nameof(TestAnalyzerReferenceByLanguage); public override object Id => Display; + public Guid Guid { get; } = Guid.NewGuid(); public Checksum Checksum; diff --git a/src/EditorFeatures/TestUtilities/DocumentTracking/TestDocumentTrackingService.cs b/src/Workspaces/CoreTestUtilities/Workspaces/TestDocumentTrackingService.cs similarity index 95% rename from src/EditorFeatures/TestUtilities/DocumentTracking/TestDocumentTrackingService.cs rename to src/Workspaces/CoreTestUtilities/Workspaces/TestDocumentTrackingService.cs index ec01c8c69affc..529f126e62ef1 100644 --- a/src/EditorFeatures/TestUtilities/DocumentTracking/TestDocumentTrackingService.cs +++ b/src/Workspaces/CoreTestUtilities/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/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace`1.cs b/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace`1.cs index bac4fc5437153..40533be8db84b 100644 --- a/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace`1.cs +++ b/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace`1.cs @@ -774,15 +774,9 @@ public override bool TryApplyChanges(Solution newSolution) // Ensure that any in-memory analyzer references in this test workspace are known by the serializer service // so that we can validate OOP scenarios involving analyzers. - foreach (var analyzer in this.CurrentSolution.AnalyzerReferences) - { - if (analyzer is AnalyzerImageReference analyzerImageReference) - { #pragma warning disable CA1416 // Validate platform compatibility - SerializerService.TestAccessor.AddAnalyzerImageReference(analyzerImageReference); + SerializerService.TestAccessor.AddAnalyzerImageReferences(this.CurrentSolution.AnalyzerReferences); #pragma warning restore CA1416 // Validate platform compatibility - } - } return result; } diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs index 2aec192ec8b6d..a74ca6f544d62 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs @@ -575,23 +575,27 @@ private async Task UpdateDocumentInfoAsync(TextDocument document, // there is no api to change these once document is created Contract.ThrowIfFalse(document.State.Attributes.Id == newDocumentInfo.Id); - Contract.ThrowIfFalse(document.State.Attributes.Name == newDocumentInfo.Name); - Contract.ThrowIfFalse(document.State.Attributes.FilePath == newDocumentInfo.FilePath); Contract.ThrowIfFalse(document.State.Attributes.IsGenerated == newDocumentInfo.IsGenerated); Contract.ThrowIfFalse(document.State.Attributes.DesignTimeOnly == newDocumentInfo.DesignTimeOnly); + if (document.State.Attributes.Name != newDocumentInfo.Name) + document = document.Project.Solution.WithDocumentName(document.Id, newDocumentInfo.Name).GetRequiredDocument(document.Id); + + if (document.State.Attributes.FilePath != newDocumentInfo.FilePath) + document = document.Project.Solution.WithDocumentFilePath(document.Id, newDocumentInfo.FilePath).GetRequiredDocument(document.Id); + if (document.State.Attributes.Folders != newDocumentInfo.Folders) { // additional document can't change folder once created Contract.ThrowIfFalse(document is Document); - document = document.Project.Solution.WithDocumentFolders(document.Id, newDocumentInfo.Folders).GetDocument(document.Id)!; + document = document.Project.Solution.WithDocumentFolders(document.Id, newDocumentInfo.Folders).GetRequiredDocument(document.Id); } if (document.State.Attributes.SourceCodeKind != newDocumentInfo.SourceCodeKind) { // additional document can't change sourcecode kind once created Contract.ThrowIfFalse(document is Document); - document = document.Project.Solution.WithDocumentSourceCodeKind(document.Id, newDocumentInfo.SourceCodeKind).GetDocument(document.Id)!; + document = document.Project.Solution.WithDocumentSourceCodeKind(document.Id, newDocumentInfo.SourceCodeKind).GetRequiredDocument(document.Id); } return document; 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);