From b4dce0802fc31260d375f7b581963371f372925d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 24 Jun 2024 14:49:28 -0700 Subject: [PATCH 01/17] Update logic Update tests Add test NRT --- .../DesignerAttributeDiscoveryService.cs | 56 +++++- .../DesignerAttributeServiceTests.cs | 162 +++++++++++------- 2 files changed, 149 insertions(+), 69 deletions(-) diff --git a/src/Features/Core/Portable/DesignerAttribute/DesignerAttributeDiscoveryService.cs b/src/Features/Core/Portable/DesignerAttribute/DesignerAttributeDiscoveryService.cs index cbfd81891bc1e..dcbc91df9b4b9 100644 --- a/src/Features/Core/Portable/DesignerAttribute/DesignerAttributeDiscoveryService.cs +++ b/src/Features/Core/Portable/DesignerAttribute/DesignerAttributeDiscoveryService.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel; using System.Composition; @@ -30,6 +31,17 @@ namespace Microsoft.CodeAnalysis.DesignerAttribute; [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] internal sealed partial class DesignerAttributeDiscoveryService() : IDesignerAttributeDiscoveryService { + /// + /// Ugly, but sufficient hack. During times where we're missing global usings (which may not always be available + /// while the sdk is regenerating/restoring on things like a tfm switch), we hardcode in knowledge we have about + /// which namespaces the core designable types are in. That way we can still make a solid guess about what the base + /// type is, even if we can't resolve it at this moment. + /// + private static readonly ImmutableArray s_wellKnownDesignerNamespaces = [ + "System.Windows.Forms.Form", + "System.Windows.Forms.Design", + "System.ComponentModel"]; + /// /// Cache from the individual references a project has, to a boolean specifying if reference knows about the /// System.ComponentModel.DesignerCategoryAttribute attribute. @@ -244,7 +256,7 @@ private async Task ScanForDesignerCategoryUsageAsync( } hasDesignerCategoryType ??= await HasDesignerCategoryTypeAsync(project, cancellationToken).ConfigureAwait(false); - var data = await ComputeDesignerAttributeDataAsync(project, documentId, filePath, hasDesignerCategoryType.Value).ConfigureAwait(false); + var data = await ComputeDesignerAttributeDataAsync(project, documentId, filePath, hasDesignerCategoryType.Value, existingInfo.category).ConfigureAwait(false); if (data.Category != existingInfo.category) results.Add((data, projectVersion)); } @@ -252,13 +264,13 @@ private async Task ScanForDesignerCategoryUsageAsync( return results.ToImmutableAndClear(); async Task ComputeDesignerAttributeDataAsync( - Project project, DocumentId documentId, string filePath, bool hasDesignerCategoryType) + Project project, DocumentId documentId, string filePath, bool hasDesignerCategoryType, string? existingCategory) { // We either haven't computed the designer info, or our data was out of date. We need // So recompute here. Figure out what the current category is, and if that's different // from what we previously stored. var category = await ComputeDesignerAttributeCategoryAsync( - hasDesignerCategoryType, project, documentId, cancellationToken).ConfigureAwait(false); + hasDesignerCategoryType, project, documentId, existingCategory, cancellationToken).ConfigureAwait(false); return new DesignerAttributeData { @@ -270,7 +282,7 @@ async Task ComputeDesignerAttributeDataAsync( } public static async Task ComputeDesignerAttributeCategoryAsync( - bool hasDesignerCategoryType, Project project, DocumentId documentId, CancellationToken cancellationToken) + bool hasDesignerCategoryType, Project project, DocumentId documentId, string? existingCategory, CancellationToken cancellationToken) { // simple case. If there's no DesignerCategory type in this compilation, then there's definitely no // designable types. @@ -292,10 +304,17 @@ async Task ComputeDesignerAttributeDataAsync( var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var firstClassType = (INamedTypeSymbol)semanticModel.GetRequiredDeclaredSymbol(firstClass, cancellationToken); - foreach (var type in firstClassType.GetBaseTypesAndThis()) + foreach (var type in GetBaseTypesAndThis(semanticModel.Compilation, firstClassType)) { cancellationToken.ThrowIfCancellationRequested(); + // If we hit an error type while walking up, then preserve the existing category. We do want a temporary + // invalid base type to not cause us to lose the existing category, causing a designable type to revert to + // an undesignable one. The designer can still support scenarios like this as it is itself error tolerant, + // falling back to the prior category in a case like this. + if (type is IErrorTypeSymbol errorType) + return existingCategory; + // See if it has the designer attribute on it. Use symbol-equivalence instead of direct equality // as the symbol we have var attribute = type.GetAttributes().FirstOrDefault(d => IsDesignerAttribute(d.AttributeClass)); @@ -305,6 +324,33 @@ async Task ComputeDesignerAttributeDataAsync( return null; + static IEnumerable GetBaseTypesAndThis(Compilation compilation, INamedTypeSymbol firstType) + { + var current = firstType; + while (current != null) + { + yield return current; + current = current.BaseType; + + if (current is IErrorTypeSymbol errorType) + current = TryMapToNonErrorType(compilation, errorType); + } + } + + static INamedTypeSymbol? TryMapToNonErrorType(Compilation compilation, IErrorTypeSymbol errorType) + { + foreach (var wellKnownNamespace in s_wellKnownDesignerNamespaces) + { + var wellKnownType = compilation.GetTypeByMetadataName($"{wellKnownNamespace}.{errorType.Name}"); + if (wellKnownType != null) + return wellKnownType; + } + + // Couldn't find a match. Just return the error type as is. Caller will handle this case and try to + // preserve the existing category. + return errorType; + } + static bool IsDesignerAttribute(INamedTypeSymbol? attributeClass) => attributeClass is { diff --git a/src/VisualStudio/CSharp/Test/DesignerAttribute/DesignerAttributeServiceTests.cs b/src/VisualStudio/CSharp/Test/DesignerAttribute/DesignerAttributeServiceTests.cs index f5e6e411f4b2f..49ac948899936 100644 --- a/src/VisualStudio/CSharp/Test/DesignerAttribute/DesignerAttributeServiceTests.cs +++ b/src/VisualStudio/CSharp/Test/DesignerAttribute/DesignerAttributeServiceTests.cs @@ -11,85 +11,119 @@ using Roslyn.Utilities; using Xunit; -namespace Microsoft.VisualStudio.LanguageServices.CSharp.UnitTests.DesignerAttributes +namespace Microsoft.VisualStudio.LanguageServices.CSharp.UnitTests.DesignerAttributes; + +[UseExportProvider] +public sealed class DesignerAttributeServiceTests { - [UseExportProvider] - public class DesignerAttributeServiceTests + [Fact] + public async Task NoDesignerTest1() { - [Fact] - public async Task NoDesignerTest1() - { - var code = @"class Test { }"; - - await TestAsync(code, category: null); - } - - [Fact] - public async Task NoDesignerOnSecondClass() - { - - await TestAsync( -@"class Test1 { } + await TestAsync(@"class Test { }", category: null); + } -[System.ComponentModel.DesignerCategory(""Form"")] -class Test2 { }", category: null); - } + [Fact] + public async Task NoDesignerOnSecondClass() + { + await TestAsync( + """ + class Test1 { } - [Fact] - public async Task NoDesignerOnStruct() - { + [System.ComponentModel.DesignerCategory("Form")] + class Test2 { } + """, category: null); + } - await TestAsync( -@" -[System.ComponentModel.DesignerCategory(""Form"")] -struct Test1 { }", category: null); - } + [Fact] + public async Task NoDesignerOnStruct() + { + await TestAsync( + """ - [Fact] - public async Task NoDesignerOnNestedClass() - { + [System.ComponentModel.DesignerCategory("Form")] + struct Test1 { } + """, category: null); + } - await TestAsync( -@"class Test1 -{ - [System.ComponentModel.DesignerCategory(""Form"")] - class Test2 { } -}", category: null); - } + [Fact] + public async Task NoDesignerOnNestedClass() + { + await TestAsync( + """ + class Test1 + { + [System.ComponentModel.DesignerCategory("Form")] + class Test2 { } + } + """, category: null); + } - [Fact] - public async Task SimpleDesignerTest() - { + [Fact] + public async Task SimpleDesignerTest() + { + await TestAsync( + """ + [System.ComponentModel.DesignerCategory("Form")] + class Test { } + """, "Form"); + } - await TestAsync( -@"[System.ComponentModel.DesignerCategory(""Form"")] -class Test { }", "Form"); - } + [Fact] + public async Task SimpleDesignerTest2() + { + await TestAsync( + """ + using System.ComponentModel; - [Fact] - public async Task SimpleDesignerTest2() - { + [DesignerCategory("Form")] + class Test { } + """, "Form"); + } - await TestAsync( -@"using System.ComponentModel; + [Theory] + [InlineData(null)] + [InlineData("Form")] + [InlineData("Form1")] + public async Task TestUnboundBase1(string? existingCategory) + { + await TestAsync( + """ + namespace System.Windows.Forms + { + [System.ComponentModel.DesignerCategory("Form")] + public class Form { } + } + + // The base type won't bind. That's ok. We should fallback to looking it up in a particular namespace. + // This should always work and not be impacted by the existing category. + class Test : Form { } + """, "Form", existingCategory); + } -[DesignerCategory(""Form"")] -class Test { }", "Form"); - } + [Theory] + [InlineData(null)] + [InlineData("Form")] + public async Task TestUnboundBaseUseOldValueIfNotFound(string? category) + { + await TestAsync( + """ + // The base type won't bind. Return existing category if we have one. + class Test : Form { } + """, category: category, existingCategory: category); + } - private static async Task TestAsync(string codeWithMarker, string? category) - { - using var workspace = TestWorkspace.CreateCSharp(codeWithMarker, openDocuments: false); + private static async Task TestAsync(string codeWithMarker, string? category, string? existingCategory = null) + { + using var workspace = TestWorkspace.CreateCSharp(codeWithMarker, openDocuments: false); - var hostDocument = workspace.Documents.First(); - var documentId = hostDocument.Id; - var document = workspace.CurrentSolution.GetRequiredDocument(documentId); + var hostDocument = workspace.Documents.First(); + var documentId = hostDocument.Id; + var document = workspace.CurrentSolution.GetRequiredDocument(documentId); - var compilation = await document.Project.GetRequiredCompilationAsync(CancellationToken.None); - var actual = await DesignerAttributeDiscoveryService.ComputeDesignerAttributeCategoryAsync( - compilation.DesignerCategoryAttributeType() != null, document.Project, document.Id, CancellationToken.None); + var compilation = await document.Project.GetRequiredCompilationAsync(CancellationToken.None); + var actual = await DesignerAttributeDiscoveryService.ComputeDesignerAttributeCategoryAsync( + compilation.DesignerCategoryAttributeType() != null, document.Project, document.Id, existingCategory, CancellationToken.None); - Assert.Equal(category, actual); - } + Assert.Equal(category, actual); } } From 067bf523fc4dba9b84b012a10f729630dc690dbe Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 3 Jul 2024 13:31:50 -0700 Subject: [PATCH 02/17] Remove more unused code --- .../Core/Def/ID.RoslynCommands.cs | 7 -- src/VisualStudio/Core/Def/RoslynPackage.cs | 2 +- ...StudioDiagnosticListTableCommandHandler.cs | 94 +------------------ 3 files changed, 2 insertions(+), 101 deletions(-) diff --git a/src/VisualStudio/Core/Def/ID.RoslynCommands.cs b/src/VisualStudio/Core/Def/ID.RoslynCommands.cs index a4f3cb0924c6b..35b69bdf39809 100644 --- a/src/VisualStudio/Core/Def/ID.RoslynCommands.cs +++ b/src/VisualStudio/Core/Def/ID.RoslynCommands.cs @@ -36,13 +36,6 @@ public static class RoslynCommands public const int AddSuppressionsInSource = 0x011f; public const int AddSuppressionsInSuppressionFile = 0x0120; public const int RemoveSuppressions = 0x0121; - public const int ErrorListSetSeveritySubMenu = 0x0122; - public const int ErrorListSetSeverityError = 0x0124; - public const int ErrorListSetSeverityWarning = 0x0125; - public const int ErrorListSetSeverityInfo = 0x0126; - public const int ErrorListSetSeverityHidden = 0x0127; - public const int ErrorListSetSeverityNone = 0x0128; - public const int ErrorListSetSeverityDefault = 0x0129; // Analyze and Code Cleanup menu IDs public const int AnalysisScopeDefault = 0x0131; diff --git a/src/VisualStudio/Core/Def/RoslynPackage.cs b/src/VisualStudio/Core/Def/RoslynPackage.cs index 7db8d50df8e0d..8dfd128dd3e99 100644 --- a/src/VisualStudio/Core/Def/RoslynPackage.cs +++ b/src/VisualStudio/Core/Def/RoslynPackage.cs @@ -228,7 +228,7 @@ protected override async Task LoadComponentsAsync(CancellationToken cancellation // we need to load it as early as possible since we can have errors from // package from each language very early await this.ComponentModel.GetService().InitializeAsync(this).ConfigureAwait(false); - await this.ComponentModel.GetService().InitializeAsync(this, cancellationToken).ConfigureAwait(false); + await this.ComponentModel.GetService().InitializeAsync(this).ConfigureAwait(false); await this.ComponentModel.GetService().InitializeAsync(this, cancellationToken).ConfigureAwait(false); await this.ComponentModel.GetService().InitializeAsync(this, cancellationToken).ConfigureAwait(false); diff --git a/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListTableCommandHandler.cs b/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListTableCommandHandler.cs index 39e1915142a9d..d5fb85a789cd9 100644 --- a/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListTableCommandHandler.cs +++ b/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListTableCommandHandler.cs @@ -7,12 +7,10 @@ using System.ComponentModel.Composition; using System.ComponentModel.Design; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes.Configuration; -using Microsoft.CodeAnalysis.CodeFixes.Suppression; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.ErrorReporting; @@ -25,7 +23,6 @@ using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Shell.TableControl; using Microsoft.VisualStudio.Utilities; -using Roslyn.Utilities; using Task = System.Threading.Tasks.Task; namespace Microsoft.VisualStudio.LanguageServices.Implementation.TableDataSource; @@ -67,40 +64,10 @@ public VisualStudioDiagnosticListTableCommandHandler( _listener = listenerProvider.GetListener(FeatureAttribute.ErrorList); } - public async Task InitializeAsync(IAsyncServiceProvider serviceProvider, CancellationToken cancellationToken) + public async Task InitializeAsync(IAsyncServiceProvider serviceProvider) { var errorList = await serviceProvider.GetServiceAsync(_threadingContext.JoinableTaskFactory, throwOnFailure: false).ConfigureAwait(false); _tableControl = errorList?.TableControl; - - // Add command handlers for bulk suppression commands. - var menuCommandService = await serviceProvider.GetServiceAsync(_threadingContext.JoinableTaskFactory, throwOnFailure: false).ConfigureAwait(false); - if (menuCommandService != null) - { - await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - AddErrorListSetSeverityMenuHandlers(menuCommandService); - - // The Add/Remove suppression(s) have been moved to the VS code analysis layer, so we don't add the commands here. - - // TODO: Figure out how to access menu commands registered by CodeAnalysisPackage and - // add the commands here if we cannot find the new command(s) in the code analysis layer. - - // AddSuppressionsCommandHandlers(menuCommandService); - } - } - - private void AddErrorListSetSeverityMenuHandlers(IMenuCommandService menuCommandService) - { - Contract.ThrowIfFalse(_threadingContext.HasMainThread); - - AddCommand(menuCommandService, ID.RoslynCommands.ErrorListSetSeveritySubMenu, delegate { }, OnErrorListSetSeveritySubMenuStatus); - - // Severity menu items - AddCommand(menuCommandService, ID.RoslynCommands.ErrorListSetSeverityDefault, SetSeverityHandler, delegate { }); - AddCommand(menuCommandService, ID.RoslynCommands.ErrorListSetSeverityError, SetSeverityHandler, delegate { }); - AddCommand(menuCommandService, ID.RoslynCommands.ErrorListSetSeverityWarning, SetSeverityHandler, delegate { }); - AddCommand(menuCommandService, ID.RoslynCommands.ErrorListSetSeverityInfo, SetSeverityHandler, delegate { }); - AddCommand(menuCommandService, ID.RoslynCommands.ErrorListSetSeverityHidden, SetSeverityHandler, delegate { }); - AddCommand(menuCommandService, ID.RoslynCommands.ErrorListSetSeverityNone, SetSeverityHandler, delegate { }); } /// @@ -155,41 +122,6 @@ private void OnAddSuppressionsInSuppressionFile(object sender, EventArgs e) private void OnRemoveSuppressions(object sender, EventArgs e) => _suppressionFixService.RemoveSuppressions(selectedErrorListEntriesOnly: true, projectHierarchy: null); - private void OnErrorListSetSeveritySubMenuStatus(object sender, EventArgs e) - { - // For now, we only enable the Set severity menu when a single configurable diagnostic is selected in the error list - // and we can update/create an editorconfig file for the configuration entry. - // In future, we can enable support for configuring in presence of multi-selection. - var command = (MenuCommand)sender; - var selectedEntry = TryGetSingleSelectedEntry(); - command.Visible = selectedEntry != null && - !SuppressionHelpers.IsNotConfigurableDiagnostic(selectedEntry) && - TryGetPathToAnalyzerConfigDoc(selectedEntry, out _, out _); - command.Enabled = command.Visible && !KnownUIContexts.SolutionBuildingContext.IsActive; - } - - private void SetSeverityHandler(object sender, EventArgs args) - { - var selectedItem = (MenuCommand)sender; - var reportDiagnostic = TryMapSelectedItemToReportDiagnostic(selectedItem); - if (reportDiagnostic == null) - { - return; - } - - var selectedDiagnostic = TryGetSingleSelectedEntry(); - if (selectedDiagnostic == null) - { - return; - } - - if (TryGetPathToAnalyzerConfigDoc(selectedDiagnostic, out var project, out _)) - { - // Fire and forget. - _ = SetSeverityHandlerAsync(reportDiagnostic.Value, selectedDiagnostic, project); - } - } - private async Task SetSeverityHandlerAsync(ReportDiagnostic reportDiagnostic, DiagnosticData selectedDiagnostic, Project project) { try @@ -234,34 +166,10 @@ async System.Threading.Tasks.Task ConfigureSeverityAsync(CancellationT } } - private static DiagnosticData? TryGetSingleSelectedEntry() - { - return null; - } - private bool TryGetPathToAnalyzerConfigDoc(DiagnosticData selectedDiagnostic, [NotNullWhen(true)] out Project? project, [NotNullWhen(true)] out string? pathToAnalyzerConfigDoc) { project = _workspace.CurrentSolution.GetProject(selectedDiagnostic.ProjectId); pathToAnalyzerConfigDoc = project?.TryGetAnalyzerConfigPathForProjectConfiguration(); return pathToAnalyzerConfigDoc is not null; } - - private static ReportDiagnostic? TryMapSelectedItemToReportDiagnostic(MenuCommand selectedItem) - { - if (selectedItem.CommandID.Guid == Guids.RoslynGroupId) - { - return selectedItem.CommandID.ID switch - { - ID.RoslynCommands.ErrorListSetSeverityDefault => ReportDiagnostic.Default, - ID.RoslynCommands.ErrorListSetSeverityError => ReportDiagnostic.Error, - ID.RoslynCommands.ErrorListSetSeverityWarning => ReportDiagnostic.Warn, - ID.RoslynCommands.ErrorListSetSeverityInfo => ReportDiagnostic.Info, - ID.RoslynCommands.ErrorListSetSeverityHidden => ReportDiagnostic.Hidden, - ID.RoslynCommands.ErrorListSetSeverityNone => ReportDiagnostic.Suppress, - _ => (ReportDiagnostic?)null - }; - } - - return null; - } } From a6fa0b8c9b21e4e298ce5f2047fe7fa72d5c00b4 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 3 Jul 2024 13:36:11 -0700 Subject: [PATCH 03/17] Remove more unused code --- ...StudioDiagnosticListTableCommandHandler.cs | 59 ------------------- 1 file changed, 59 deletions(-) diff --git a/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListTableCommandHandler.cs b/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListTableCommandHandler.cs index d5fb85a789cd9..853d69a7b3c7b 100644 --- a/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListTableCommandHandler.cs +++ b/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListTableCommandHandler.cs @@ -70,58 +70,6 @@ public async Task InitializeAsync(IAsyncServiceProvider serviceProvider) _tableControl = errorList?.TableControl; } - /// - /// Add a command handler and status query handler for a menu item - /// - private static OleMenuCommand AddCommand( - IMenuCommandService menuCommandService, - int commandId, - EventHandler invokeHandler, - EventHandler beforeQueryStatus) - { - var commandIdWithGroupId = new CommandID(Guids.RoslynGroupId, commandId); - var command = new OleMenuCommand(invokeHandler, delegate { }, beforeQueryStatus, commandIdWithGroupId); - menuCommandService.AddCommand(command); - return command; - } - - private void OnAddSuppressionsStatus(object sender, EventArgs e) - { - var command = (MenuCommand)sender; - command.Visible = _suppressionStateService.CanSuppressSelectedEntries; - command.Enabled = command.Visible && !KnownUIContexts.SolutionBuildingContext.IsActive; - } - - private void OnRemoveSuppressionsStatus(object sender, EventArgs e) - { - var command = (MenuCommand)sender; - command.Visible = _suppressionStateService.CanRemoveSuppressionsSelectedEntries; - command.Enabled = command.Visible && !KnownUIContexts.SolutionBuildingContext.IsActive; - } - - private void OnAddSuppressionsInSourceStatus(object sender, EventArgs e) - { - var command = (MenuCommand)sender; - command.Visible = _suppressionStateService.CanSuppressSelectedEntriesInSource; - command.Enabled = command.Visible && !KnownUIContexts.SolutionBuildingContext.IsActive; - } - - private void OnAddSuppressionsInSuppressionFileStatus(object sender, EventArgs e) - { - var command = (MenuCommand)sender; - command.Visible = _suppressionStateService.CanSuppressSelectedEntriesInSuppressionFiles; - command.Enabled = command.Visible && !KnownUIContexts.SolutionBuildingContext.IsActive; - } - - private void OnAddSuppressionsInSource(object sender, EventArgs e) - => _suppressionFixService.AddSuppressions(selectedErrorListEntriesOnly: true, suppressInSource: true, projectHierarchy: null); - - private void OnAddSuppressionsInSuppressionFile(object sender, EventArgs e) - => _suppressionFixService.AddSuppressions(selectedErrorListEntriesOnly: true, suppressInSource: false, projectHierarchy: null); - - private void OnRemoveSuppressions(object sender, EventArgs e) - => _suppressionFixService.RemoveSuppressions(selectedErrorListEntriesOnly: true, projectHierarchy: null); - private async Task SetSeverityHandlerAsync(ReportDiagnostic reportDiagnostic, DiagnosticData selectedDiagnostic, Project project) { try @@ -165,11 +113,4 @@ async System.Threading.Tasks.Task ConfigureSeverityAsync(CancellationT return await ConfigurationUpdater.ConfigureSeverityAsync(reportDiagnostic, diagnostic, project, cancellationToken).ConfigureAwait(false); } } - - private bool TryGetPathToAnalyzerConfigDoc(DiagnosticData selectedDiagnostic, [NotNullWhen(true)] out Project? project, [NotNullWhen(true)] out string? pathToAnalyzerConfigDoc) - { - project = _workspace.CurrentSolution.GetProject(selectedDiagnostic.ProjectId); - pathToAnalyzerConfigDoc = project?.TryGetAnalyzerConfigPathForProjectConfiguration(); - return pathToAnalyzerConfigDoc is not null; - } } From 6f6824c69345040e8ad4a6d8920aa123506410ea Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 3 Jul 2024 13:50:28 -0700 Subject: [PATCH 04/17] Remove uncalled method --- ...StudioDiagnosticListTableCommandHandler.cs | 44 ------------------- 1 file changed, 44 deletions(-) diff --git a/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListTableCommandHandler.cs b/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListTableCommandHandler.cs index 853d69a7b3c7b..40f4381256c18 100644 --- a/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListTableCommandHandler.cs +++ b/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListTableCommandHandler.cs @@ -69,48 +69,4 @@ public async Task InitializeAsync(IAsyncServiceProvider serviceProvider) var errorList = await serviceProvider.GetServiceAsync(_threadingContext.JoinableTaskFactory, throwOnFailure: false).ConfigureAwait(false); _tableControl = errorList?.TableControl; } - - private async Task SetSeverityHandlerAsync(ReportDiagnostic reportDiagnostic, DiagnosticData selectedDiagnostic, Project project) - { - try - { - using var token = _listener.BeginAsyncOperation(nameof(SetSeverityHandlerAsync)); - using var context = _uiThreadOperationExecutor.BeginExecute( - title: ServicesVSResources.Updating_severity, - defaultDescription: ServicesVSResources.Updating_severity, - allowCancellation: true, - showProgress: true); - - var newSolution = await ConfigureSeverityAsync(context.UserCancellationToken).ConfigureAwait(false); - var operations = ImmutableArray.Create(new ApplyChangesOperation(newSolution)); - using var scope = context.AddScope(allowCancellation: true, ServicesVSResources.Updating_severity); - await _editHandlerService.ApplyAsync( - _workspace, - project.Solution, - fromDocument: null, - operations, - title: ServicesVSResources.Updating_severity, - scope.GetCodeAnalysisProgress(), - context.UserCancellationToken).ConfigureAwait(false); - - // Kick off diagnostic re-analysis for affected document so that the configured diagnostic gets refreshed. - if (selectedDiagnostic.DocumentId != null) - _diagnosticService.RequestDiagnosticRefresh(); - } - catch (OperationCanceledException) - { - } - catch (Exception ex) when (FatalError.ReportAndCatch(ex)) - { - } - - return; - - // Local functions. - async System.Threading.Tasks.Task ConfigureSeverityAsync(CancellationToken cancellationToken) - { - var diagnostic = await selectedDiagnostic.ToDiagnosticAsync(project, cancellationToken).ConfigureAwait(false); - return await ConfigurationUpdater.ConfigureSeverityAsync(reportDiagnostic, diagnostic, project, cancellationToken).ConfigureAwait(false); - } - } } From a1cb67cea27ce0a53ab978a657f25fa051893117 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 3 Jul 2024 13:52:12 -0700 Subject: [PATCH 05/17] remove type that now serves no purpose --- src/VisualStudio/Core/Def/RoslynPackage.cs | 2 - ...StudioDiagnosticListTableCommandHandler.cs | 72 ------------------- 2 files changed, 74 deletions(-) delete mode 100644 src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListTableCommandHandler.cs diff --git a/src/VisualStudio/Core/Def/RoslynPackage.cs b/src/VisualStudio/Core/Def/RoslynPackage.cs index 8dfd128dd3e99..1ec2bec5601c6 100644 --- a/src/VisualStudio/Core/Def/RoslynPackage.cs +++ b/src/VisualStudio/Core/Def/RoslynPackage.cs @@ -13,7 +13,6 @@ using Microsoft.CodeAnalysis.Common; using Microsoft.CodeAnalysis.EditAndContinue; using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncCompletion; -using Microsoft.CodeAnalysis.Editor.QuickInfo; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Notification; @@ -228,7 +227,6 @@ protected override async Task LoadComponentsAsync(CancellationToken cancellation // we need to load it as early as possible since we can have errors from // package from each language very early await this.ComponentModel.GetService().InitializeAsync(this).ConfigureAwait(false); - await this.ComponentModel.GetService().InitializeAsync(this).ConfigureAwait(false); await this.ComponentModel.GetService().InitializeAsync(this, cancellationToken).ConfigureAwait(false); await this.ComponentModel.GetService().InitializeAsync(this, cancellationToken).ConfigureAwait(false); diff --git a/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListTableCommandHandler.cs b/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListTableCommandHandler.cs deleted file mode 100644 index 40f4381256c18..0000000000000 --- a/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListTableCommandHandler.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Immutable; -using System.ComponentModel.Composition; -using System.ComponentModel.Design; -using System.Diagnostics.CodeAnalysis; -using System.Threading; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixes.Configuration; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Progress; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.VisualStudio.LanguageServices.Implementation.Suppression; -using Microsoft.VisualStudio.Shell; -using Microsoft.VisualStudio.Shell.Interop; -using Microsoft.VisualStudio.Shell.TableControl; -using Microsoft.VisualStudio.Utilities; -using Task = System.Threading.Tasks.Task; - -namespace Microsoft.VisualStudio.LanguageServices.Implementation.TableDataSource; - -[Export(typeof(VisualStudioDiagnosticListTableCommandHandler))] -internal partial class VisualStudioDiagnosticListTableCommandHandler -{ - private readonly IThreadingContext _threadingContext; - private readonly VisualStudioWorkspace _workspace; - private readonly VisualStudioSuppressionFixService _suppressionFixService; - private readonly VisualStudioDiagnosticListSuppressionStateService _suppressionStateService; - private readonly IUIThreadOperationExecutor _uiThreadOperationExecutor; - private readonly IDiagnosticAnalyzerService _diagnosticService; - private readonly ICodeActionEditHandlerService _editHandlerService; - private readonly IAsynchronousOperationListener _listener; - - private IWpfTableControl? _tableControl; - - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public VisualStudioDiagnosticListTableCommandHandler( - IThreadingContext threadingContext, - SVsServiceProvider serviceProvider, - VisualStudioWorkspace workspace, - IVisualStudioSuppressionFixService suppressionFixService, - VisualStudioDiagnosticListSuppressionStateService suppressionStateService, - IUIThreadOperationExecutor uiThreadOperationExecutor, - IDiagnosticAnalyzerService diagnosticService, - ICodeActionEditHandlerService editHandlerService, - IAsynchronousOperationListenerProvider listenerProvider) - { - _threadingContext = threadingContext; - _workspace = workspace; - _suppressionFixService = (VisualStudioSuppressionFixService)suppressionFixService; - _suppressionStateService = suppressionStateService; - _uiThreadOperationExecutor = uiThreadOperationExecutor; - _diagnosticService = diagnosticService; - _editHandlerService = editHandlerService; - _listener = listenerProvider.GetListener(FeatureAttribute.ErrorList); - } - - public async Task InitializeAsync(IAsyncServiceProvider serviceProvider) - { - var errorList = await serviceProvider.GetServiceAsync(_threadingContext.JoinableTaskFactory, throwOnFailure: false).ConfigureAwait(false); - _tableControl = errorList?.TableControl; - } -} From 6e8362020c82c1235653d61acfe54ecf0b66550c Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Fri, 5 Jul 2024 07:57:40 -0700 Subject: [PATCH 06/17] Reduce allocations in SymbolDeclaredCompilationEvent (#74250) * Reduce allocations in SymbolDeclaredCompilationEvent The SymbolDeclaredCompilationEvent ctor is showing up as 0.5% of allocations in vbcscompiler.exe in a customer profile I'm looking at. All these costs are attributable to the use of Lazy in this class. Instead, just use a nullable field, and accept the fact that during extremely high contention, it is possible for an ImmutableArray to be created multiple times. --- .../SymbolDeclaredCompilationEvent.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/SymbolDeclaredCompilationEvent.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/SymbolDeclaredCompilationEvent.cs index f03018973710f..381f4f62f51ff 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/SymbolDeclaredCompilationEvent.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/SymbolDeclaredCompilationEvent.cs @@ -2,9 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Immutable; using Microsoft.CodeAnalysis.Symbols; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics { @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.Diagnostics /// internal sealed class SymbolDeclaredCompilationEvent : CompilationEvent { - private readonly Lazy> _lazyCachedDeclaringReferences; + private ImmutableArray _lazyCachedDeclaringReferences; public SymbolDeclaredCompilationEvent( Compilation compilation, @@ -25,7 +25,7 @@ public SymbolDeclaredCompilationEvent( { SymbolInternal = symbolInternal; SemanticModelWithCachedBoundNodes = semanticModelWithCachedBoundNodes; - _lazyCachedDeclaringReferences = new Lazy>(() => Symbol.DeclaringSyntaxReferences); + _lazyCachedDeclaringReferences = default(ImmutableArray); } public ISymbol Symbol => SymbolInternal.GetISymbol(); @@ -35,7 +35,16 @@ public SymbolDeclaredCompilationEvent( public SemanticModel? SemanticModelWithCachedBoundNodes { get; } // PERF: We avoid allocations in re-computing syntax references for declared symbol during event processing by caching them directly on this member. - public ImmutableArray DeclaringSyntaxReferences => _lazyCachedDeclaringReferences.Value; + public ImmutableArray DeclaringSyntaxReferences + { + get + { + return InterlockedOperations.Initialize( + ref _lazyCachedDeclaringReferences, + static self => self.Symbol.DeclaringSyntaxReferences, + this); + } + } public override string ToString() { From 4dbedf6dfabc2d2c13496ded946f3e61f3d8de7e Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Fri, 5 Jul 2024 07:59:37 -0700 Subject: [PATCH 07/17] Reduce closures allocated during invocation of CapturedSymbolReplacement.Replacement (#74258) * Reduce closures allocated during invocation of CapturedSymbolReplacement.Replacement This method previously took in a Func and most callers allocated a closure to implement the callback. This PR uses the args pattern and adds a parameter to the Replacement method that is passed back into that callback to better allow static lambdas to be used. This was the top allocating closure allocation in vbcscompiler in a customer profile I'm looking at (0.2% -- 42 MB) --- .../ClosureConversion/ClosureConversion.cs | 11 ++++-- .../Lowering/MethodToClassRewriter.cs | 6 +++- .../StateMachineRewriter/CapturedSymbol.cs | 12 +++---- .../MethodToStateMachineRewriter.cs | 8 ++--- .../StateMachineRewriter.cs | 35 ++++++++++++++----- 5 files changed, 50 insertions(+), 22 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.cs b/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.cs index 800a97a466638..01e209bb2bb5b 100644 --- a/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.cs @@ -622,7 +622,10 @@ protected override BoundExpression FramePointer(SyntaxNode syntax, NamedTypeSymb // However, frame pointer local variables themselves can be "captured". In that case // the inner frames contain pointers to the enclosing frames. That is, nested // frame pointers are organized in a linked list. - return proxyField.Replacement(syntax, frameType => FramePointer(syntax, frameType)); + return proxyField.Replacement( + syntax, + static (frameType, arg) => arg.self.FramePointer(arg.syntax, frameType), + (syntax, self: this)); } var localFrame = (LocalSymbol)framePointer; @@ -777,7 +780,11 @@ private void InitVariableProxy(SyntaxNode syntax, Symbol symbol, LocalSymbol fra throw ExceptionUtilities.UnexpectedValue(symbol.Kind); } - var left = proxy.Replacement(syntax, frameType1 => new BoundLocal(syntax, framePointer, null, framePointer.Type)); + var left = proxy.Replacement( + syntax, + static (frameType1, arg) => new BoundLocal(arg.syntax, arg.framePointer, null, arg.framePointer.Type), + (syntax, framePointer)); + var assignToProxy = new BoundAssignmentOperator(syntax, left, value, value.Type); if (_currentMethod.MethodKind == MethodKind.Constructor && symbol == _currentMethod.ThisParameter && diff --git a/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs index 49236fa2a3a89..fbc1881b5924f 100644 --- a/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs @@ -369,7 +369,11 @@ private bool TryReplaceWithProxy(Symbol parameterOrLocal, SyntaxNode syntax, [No { if (proxies.TryGetValue(parameterOrLocal, out CapturedSymbolReplacement? proxy)) { - replacement = proxy.Replacement(syntax, frameType => FramePointer(syntax, frameType)); + replacement = proxy.Replacement( + syntax, + static (frameType, arg) => arg.self.FramePointer(arg.syntax, frameType), + (syntax, self: this)); + return true; } diff --git a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/CapturedSymbol.cs b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/CapturedSymbol.cs index 08ddeca6b61a9..a78955a399c9c 100644 --- a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/CapturedSymbol.cs +++ b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/CapturedSymbol.cs @@ -23,7 +23,7 @@ public CapturedSymbolReplacement(bool isReusable) /// Rewrite the replacement expression for the hoisted local so all synthesized field are accessed as members /// of the appropriate frame. /// - public abstract BoundExpression Replacement(SyntaxNode node, Func makeFrame); + public abstract BoundExpression Replacement(SyntaxNode node, Func makeFrame, TArg arg); } internal sealed class CapturedToFrameSymbolReplacement : CapturedSymbolReplacement @@ -36,9 +36,9 @@ public CapturedToFrameSymbolReplacement(LambdaCapturedVariable hoistedField, boo this.HoistedField = hoistedField; } - public override BoundExpression Replacement(SyntaxNode node, Func makeFrame) + public override BoundExpression Replacement(SyntaxNode node, Func makeFrame, TArg arg) { - var frame = makeFrame(this.HoistedField.ContainingType); + var frame = makeFrame(this.HoistedField.ContainingType, arg); var field = this.HoistedField.AsMember((NamedTypeSymbol)frame.Type); return new BoundFieldAccess(node, frame, field, constantValueOpt: null); } @@ -54,9 +54,9 @@ public CapturedToStateMachineFieldReplacement(StateMachineFieldSymbol hoistedFie this.HoistedField = hoistedField; } - public override BoundExpression Replacement(SyntaxNode node, Func makeFrame) + public override BoundExpression Replacement(SyntaxNode node, Func makeFrame, TArg arg) { - var frame = makeFrame(this.HoistedField.ContainingType); + var frame = makeFrame(this.HoistedField.ContainingType, arg); var field = this.HoistedField.AsMember((NamedTypeSymbol)frame.Type); return new BoundFieldAccess(node, frame, field, constantValueOpt: null); } @@ -74,7 +74,7 @@ public CapturedToExpressionSymbolReplacement(BoundExpression replacement, Immuta this.HoistedFields = hoistedFields; } - public override BoundExpression Replacement(SyntaxNode node, Func makeFrame) + public override BoundExpression Replacement(SyntaxNode node, Func makeFrame, TArg arg) { // By returning the same replacement each time, it is possible we // are constructing a DAG instead of a tree for the translation. diff --git a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs index 25bb0a30587e4..673a6c968cb4c 100644 --- a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs @@ -140,7 +140,7 @@ public MethodToStateMachineRewriter( proxies.TryGetValue(thisParameter, out thisProxy) && F.Compilation.Options.OptimizationLevel == OptimizationLevel.Release) { - BoundExpression thisProxyReplacement = thisProxy.Replacement(F.Syntax, frameType => F.This()); + BoundExpression thisProxyReplacement = thisProxy.Replacement(F.Syntax, static (frameType, F) => F.This(), F); Debug.Assert(thisProxyReplacement.Type is not null); this.cachedThis = F.SynthesizedLocal(thisProxyReplacement.Type, syntax: F.Syntax, kind: SynthesizedLocalKind.FrameCache); } @@ -925,7 +925,7 @@ protected BoundStatement CacheThisIfNeeded() if ((object)this.cachedThis != null) { CapturedSymbolReplacement proxy = proxies[this.OriginalMethod.ThisParameter]; - var fetchThis = proxy.Replacement(F.Syntax, frameType => F.This()); + var fetchThis = proxy.Replacement(F.Syntax, static (frameType, F) => F.This(), F); return F.Assignment(F.Local(this.cachedThis), fetchThis); } @@ -961,7 +961,7 @@ public sealed override BoundNode VisitThisReference(BoundThisReference node) else { Debug.Assert(proxy != null); - return proxy.Replacement(F.Syntax, frameType => F.This()); + return proxy.Replacement(F.Syntax, static (frameType, F) => F.This(), F); } } @@ -977,7 +977,7 @@ public override BoundNode VisitBaseReference(BoundBaseReference node) CapturedSymbolReplacement proxy = proxies[this.OriginalMethod.ThisParameter]; Debug.Assert(proxy != null); - return proxy.Replacement(F.Syntax, frameType => F.This()); + return proxy.Replacement(F.Syntax, static (frameType, F) => F.This(), F); } #endregion diff --git a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineRewriter.cs index ef2ad4236dbb8..4720e12ee1dfb 100644 --- a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineRewriter.cs @@ -311,7 +311,12 @@ protected BoundStatement GenerateParameterStorage(LocalSymbol stateMachineVariab CapturedSymbolReplacement proxy; if (proxies.TryGetValue(method.ThisParameter, out proxy)) { - bodyBuilder.Add(F.Assignment(proxy.Replacement(F.Syntax, frameType1 => F.Local(stateMachineVariable)), F.This())); + var leftExpression = proxy.Replacement( + F.Syntax, + static (frameType1, arg) => arg.F.Local(arg.stateMachineVariable), + (F, stateMachineVariable)); + + bodyBuilder.Add(F.Assignment(leftExpression, F.This())); } } @@ -320,8 +325,12 @@ protected BoundStatement GenerateParameterStorage(LocalSymbol stateMachineVariab CapturedSymbolReplacement proxy; if (proxies.TryGetValue(parameter, out proxy)) { - bodyBuilder.Add(F.Assignment(proxy.Replacement(F.Syntax, frameType1 => F.Local(stateMachineVariable)), - F.Parameter(parameter))); + var leftExpression = proxy.Replacement( + F.Syntax, + static (frameType1, arg) => arg.F.Local(arg.stateMachineVariable), + (F, stateMachineVariable)); + + bodyBuilder.Add(F.Assignment(leftExpression, F.Parameter(parameter))); } } @@ -456,10 +465,14 @@ protected SynthesizedImplementationMethod GenerateIteratorGetEnumerator(MethodSy CapturedSymbolReplacement proxy; if (copyDest.TryGetValue(method.ThisParameter, out proxy)) { - bodyBuilder.Add( - F.Assignment( - proxy.Replacement(F.Syntax, stateMachineType => F.Local(resultVariable)), - copySrc[method.ThisParameter].Replacement(F.Syntax, stateMachineType => F.This()))); + var leftExpression = proxy.Replacement( + F.Syntax, + static (stateMachineType, arg) => arg.F.Local(arg.resultVariable), + (F, resultVariable)); + + var rightExpression = copySrc[method.ThisParameter].Replacement(F.Syntax, static (stateMachineType, F) => F.This(), F); + + bodyBuilder.Add(F.Assignment(leftExpression, rightExpression)); } } @@ -471,9 +484,13 @@ protected SynthesizedImplementationMethod GenerateIteratorGetEnumerator(MethodSy if (copyDest.TryGetValue(parameter, out proxy)) { // result.parameter - BoundExpression resultParameter = proxy.Replacement(F.Syntax, stateMachineType => F.Local(resultVariable)); + BoundExpression resultParameter = proxy.Replacement( + F.Syntax, + static (stateMachineType, arg) => arg.F.Local(arg.resultVariable), + (F, resultVariable)); + // this.parameterProxy - BoundExpression parameterProxy = copySrc[parameter].Replacement(F.Syntax, stateMachineType => F.This()); + BoundExpression parameterProxy = copySrc[parameter].Replacement(F.Syntax, static (stateMachineType, F) => F.This(), F); BoundStatement copy = InitializeParameterField(getEnumeratorMethod, parameter, resultParameter, parameterProxy); bodyBuilder.Add(copy); From 1874807a1fa6d3a6c10fce99215f2aa9e5c89636 Mon Sep 17 00:00:00 2001 From: Ankita Khera <40616383+akhera99@users.noreply.github.com> Date: Fri, 5 Jul 2024 12:10:24 -0700 Subject: [PATCH 08/17] fix (#74237) --- src/Features/Core/Portable/QuickInfo/OnTheFlyDocsLogger.cs | 2 +- .../Compiler/Core/Log/FunctionId.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/QuickInfo/OnTheFlyDocsLogger.cs b/src/Features/Core/Portable/QuickInfo/OnTheFlyDocsLogger.cs index f0f46e65d7e17..36edcf66005c0 100644 --- a/src/Features/Core/Portable/QuickInfo/OnTheFlyDocsLogger.cs +++ b/src/Features/Core/Portable/QuickInfo/OnTheFlyDocsLogger.cs @@ -41,7 +41,7 @@ internal static void LogOnTheFlyDocsResultsRequestedWithDocComments() public static void ReportTelemetry() { - Logger.Log(FunctionId.InheritanceMargin_GetInheritanceMemberItems, KeyValueLogMessage.Create(m => + Logger.Log(FunctionId.Copilot_On_The_Fly_Docs_Get_Counts, KeyValueLogMessage.Create(m => { foreach (var kv in s_countLogAggregator) { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs index ab07429edd946..bae882174e5d4 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs @@ -631,5 +631,6 @@ internal enum FunctionId Copilot_On_The_Fly_Docs_Results_Displayed = 812, Copilot_On_The_Fly_Docs_Error_Displayed = 813, Copilot_On_The_Fly_Docs_Results_Canceled = 814, + Copilot_On_The_Fly_Docs_Get_Counts = 815, Copilot_Rename = 851 } From a1007090a49a4680eb7e47d72f17391644bfc2f3 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 5 Jul 2024 16:24:31 -0700 Subject: [PATCH 09/17] Remove more --- src/VisualStudio/Core/Def/ID.RoslynCommands.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/VisualStudio/Core/Def/ID.RoslynCommands.cs b/src/VisualStudio/Core/Def/ID.RoslynCommands.cs index 35b69bdf39809..3b35d239777e2 100644 --- a/src/VisualStudio/Core/Def/ID.RoslynCommands.cs +++ b/src/VisualStudio/Core/Def/ID.RoslynCommands.cs @@ -31,12 +31,6 @@ public static class RoslynCommands public const int SetActiveRuleSet = 0x0118; public const int SetSeverityDefault = 0x011b; - // Error list context menu command IDs for suppressions and setting severity - public const int AddSuppressions = 0x011d; - public const int AddSuppressionsInSource = 0x011f; - public const int AddSuppressionsInSuppressionFile = 0x0120; - public const int RemoveSuppressions = 0x0121; - // Analyze and Code Cleanup menu IDs public const int AnalysisScopeDefault = 0x0131; public const int AnalysisScopeCurrentDocument = 0x0132; From 269363694d9c2855c34d3106d4f2c7a6e10f9802 Mon Sep 17 00:00:00 2001 From: Ankita Khera <40616383+akhera99@users.noreply.github.com> Date: Mon, 8 Jul 2024 09:02:37 -0700 Subject: [PATCH 10/17] fix (#74276) --- src/Features/Core/Portable/QuickInfo/OnTheFlyDocsLogger.cs | 2 +- .../Compiler/Core/Log/FunctionId.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/QuickInfo/OnTheFlyDocsLogger.cs b/src/Features/Core/Portable/QuickInfo/OnTheFlyDocsLogger.cs index f0f46e65d7e17..36edcf66005c0 100644 --- a/src/Features/Core/Portable/QuickInfo/OnTheFlyDocsLogger.cs +++ b/src/Features/Core/Portable/QuickInfo/OnTheFlyDocsLogger.cs @@ -41,7 +41,7 @@ internal static void LogOnTheFlyDocsResultsRequestedWithDocComments() public static void ReportTelemetry() { - Logger.Log(FunctionId.InheritanceMargin_GetInheritanceMemberItems, KeyValueLogMessage.Create(m => + Logger.Log(FunctionId.Copilot_On_The_Fly_Docs_Get_Counts, KeyValueLogMessage.Create(m => { foreach (var kv in s_countLogAggregator) { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs index ab07429edd946..bae882174e5d4 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs @@ -631,5 +631,6 @@ internal enum FunctionId Copilot_On_The_Fly_Docs_Results_Displayed = 812, Copilot_On_The_Fly_Docs_Error_Displayed = 813, Copilot_On_The_Fly_Docs_Results_Canceled = 814, + Copilot_On_The_Fly_Docs_Get_Counts = 815, Copilot_Rename = 851 } From b5430a4483e94692ddacca1583b33a2d104accd8 Mon Sep 17 00:00:00 2001 From: dotnet bot Date: Mon, 8 Jul 2024 11:03:01 -0700 Subject: [PATCH 11/17] Localized file check-in by OneLocBuild Task: Build definition ID 327: Build ID 2490585 (#74287) --- .../Core/xlf/EditorFeaturesResources.zh-Hant.xlf | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hant.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hant.xlf index 2e6ed866c1573..e0d4a6621871d 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hant.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hant.xlf @@ -29,17 +29,17 @@ Copilot - Copilot + Copilot Copilot thinking... - Copilot thinking... + Copilot 思考中... Describe with Copilot - Describe with Copilot + 使用 Copilot 加以描述 @@ -79,7 +79,7 @@ Generate summary with Copilot (might be inaccurate) - Generate summary with Copilot (might be inaccurate) + 使用 Copilot 產生摘要 (可能不準確) @@ -639,7 +639,7 @@ '{0}' intercepted locations - '{0}' intercepted locations + '{0}' 已攔截的位置 From f4fada16052e42ac91a33524fea91762ae8d826c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Mon, 8 Jul 2024 13:42:58 -0700 Subject: [PATCH 12/17] Syntax formatting options (#74223) --- .../CSharpCollectionExpressionRewriter.cs | 3 +- ...ctionExpressionForFluentCodeFixProvider.cs | 3 +- .../Core/Analyzers/AnalyzerOptionsProvider.cs | 2 +- .../AutomaticLineEnderCommandHandler.cs | 4 ++- .../ConvertNamespaceCommandHandler.cs | 2 +- .../CSharpFormattingInteractionService.cs | 6 ++-- .../RawStringLiteralCommandHandler_Return.cs | 2 +- .../SplitStringLiteralCommandHandler.cs | 2 +- .../StringCopyPasteCommandHandler.cs | 6 ++-- ...bstractAutomaticLineEnderCommandHandler.cs | 7 ++-- ...nSessionProvider.BraceCompletionSession.cs | 13 +++---- .../AbstractCommentSelectionBase.cs | 2 +- .../Options/EditorAnalyzerConfigOptions.cs | 4 +-- .../Core/Options/TextBufferOptionProviders.cs | 29 +++++++--------- .../OrganizeDocumentCommandHandler.cs | 2 +- .../Extensions/ITextSnapshotExtensions.cs | 2 +- .../Core/SmartIndent/SmartIndent.cs | 2 +- .../ChangeSignatureTestState.cs | 2 +- .../AbstractAutomaticBraceCompletionTests.cs | 3 +- .../ExtractInterfaceTestState.cs | 2 +- .../Formatting/CoreFormatterTestsBase.cs | 6 ++-- .../AutomaticLineEnderCommandHandler.vb | 3 +- .../EndConstructCommandHandler.vb | 2 +- .../VisualBasic/LineCommit/CommitFormatter.vb | 2 +- ...NavigationBarItemService_CodeGeneration.vb | 2 +- ...tringToRawStringCodeRefactoringProvider.cs | 13 ++++--- ...tructorParameterCodeRefactoringProvider.cs | 2 +- .../AbstractCSharpTypeSnippetProvider.cs | 2 +- .../Snippets/CSharpIntMainSnippetProvider.cs | 2 +- .../Portable/Snippets/CSharpSnippetHelpers.cs | 2 +- .../AbstractBraceCompletionService.cs | 5 +-- .../IBraceCompletionService.cs | 7 ++-- .../AbstractChangeSignatureService.cs | 2 +- ...ConfigureCodeStyleOptionCodeFixProvider.cs | 4 +-- .../ConfigureSeverityLevelCodeFixProvider.cs | 4 +-- ...rovider.GlobalSuppressMessageCodeAction.cs | 6 ++-- ...onCodeFixProvider.PragmaBatchFixHelpers.cs | 5 ++- ...ovider.PragmaWarningBatchFixAllProvider.cs | 2 +- ....RemoveSuppressionCodeAction.BatchFixer.cs | 4 +-- ...FixProvider.RemoveSuppressionCodeAction.cs | 3 +- .../AbstractSuppressionCodeFixProvider.cs | 24 ++++++------- .../Suppression/WrapperCodeFixProvider.cs | 4 +-- .../AbstractMoveTypeService.MoveTypeEditor.cs | 4 +-- .../AbstractChangeNamespaceService.cs | 4 +-- ...stractMemberInsertingCompletionProvider.cs | 2 +- .../AbstractImportCompletionProvider.cs | 2 +- ...ymousTypeToClassCodeRefactoringProvider.cs | 2 +- ...ertTupleToStructCodeRefactoringProvider.cs | 6 ++-- .../AbstractEncapsulateFieldService.cs | 2 +- .../AbstractExtractInterfaceService.cs | 2 +- .../GenerateEqualsAndGetHashCodeAction.cs | 2 +- ...erCodeRefactoringProviderMemberCreation.cs | 2 +- .../AbstractMoveToNamespaceService.cs | 2 +- .../AbstractSnippetProvider.cs | 2 +- .../AbstractUseAutoPropertyCodeFixProvider.cs | 10 +++--- .../AbstractSuppressionAllCodeTests.cs | 2 +- ...SharpDocumentationCommentOptionsWrapper.cs | 2 +- ...OmniSharpSyntaxFormattingOptionsWrapper.cs | 2 +- ...oadBaseCodeFixProvider.AddKeywordAction.vb | 6 ++-- .../OverloadBaseCodeFixProvider.vb | 4 +-- .../Extensions/ProtocolConversions.cs | 3 +- .../Razor/FormatNewFileHandler.cs | 2 +- .../CodeCleanup/AbstractCodeCleanupService.cs | 4 +-- .../Features/CodeFixes/CodeFixService.cs | 2 +- .../Options/IndentationOptionsStorage.cs | 2 +- .../Options/SyntaxFormattingOptionsStorage.cs | 20 ----------- .../AbstractFormatDocumentHandlerBase.cs | 3 +- .../Formatting/FormatDocumentHandler.cs | 15 +++----- .../Formatting/FormatDocumentOnTypeHandler.cs | 2 +- .../Formatting/FormatDocumentRangeHandler.cs | 15 +++----- .../InlineCompletionsHandler.cs | 2 +- .../OnAutoInsert/OnAutoInsertHandler.cs | 5 +-- .../VisualStudioExtractClassOptionsService.cs | 7 ++-- ...actLanguageService`2.IVsLanguageTextOps.cs | 2 +- .../Def/Snippets/SnippetExpansionClient.cs | 5 +-- .../Core/Def/Venus/ContainedDocument.cs | 2 +- .../CodeModel/AbstractCodeModelService.cs | 2 +- .../Core/Impl/CodeModel/FileCodeModel.cs | 2 +- .../Options/AbstractOptionPreviewViewModel.cs | 2 +- .../Supression/IConfigurationFixProvider.cs | 4 +-- .../Core/Portable/Formatting/Formatter.cs | 6 ++-- ...GlobalCodeActionOptionsWorkspaceService.cs | 2 +- .../Portable/Workspace/Solution/Project.cs | 6 ++++ .../Formatting/FormattingTestBase.cs | 2 +- .../Workspaces/TestWorkspace`1.cs | 34 ++++++++++++++----- .../Formatting/CSharpSyntaxFormatting.cs | 4 +-- .../Core/CodeCleanup/CodeCleanupOptions.cs | 2 +- .../Formatting/AbstractSyntaxFormatting.cs | 2 +- .../Core/Formatting/ISyntaxFormatting.cs | 2 +- .../Formatting/SyntaxFormattingOptions.cs | 11 +++--- .../CodeActions/CodeFixOptionsProvider.cs | 9 +---- .../Formatting/VisualBasicSyntaxFormatting.vb | 4 +-- 92 files changed, 210 insertions(+), 237 deletions(-) delete mode 100644 src/LanguageServer/Protocol/Features/Options/SyntaxFormattingOptionsStorage.cs diff --git a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpCollectionExpressionRewriter.cs b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpCollectionExpressionRewriter.cs index 5de5853e93db8..4fbb6222312c9 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpCollectionExpressionRewriter.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpCollectionExpressionRewriter.cs @@ -50,8 +50,7 @@ public static async Task CreateCollectionExpressionA #if CODE_STYLE var formattingOptions = CSharpSyntaxFormattingOptions.Default; #else - var formattingOptions = (CSharpSyntaxFormattingOptions)await workspaceDocument.GetSyntaxFormattingOptionsAsync( - fallbackOptions, cancellationToken).ConfigureAwait(false); + var formattingOptions = (CSharpSyntaxFormattingOptions)await workspaceDocument.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); #endif var indentationOptions = new IndentationOptions(formattingOptions); diff --git a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForFluentCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForFluentCodeFixProvider.cs index fab7a804b6b76..d81166a7aea57 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForFluentCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForFluentCodeFixProvider.cs @@ -150,8 +150,7 @@ static async Task> GetArgumentsAsync( #if CODE_STYLE var formattingOptions = SyntaxFormattingOptions.CommonDefaults; #else - var formattingOptions = await document.GetSyntaxFormattingOptionsAsync( - fallbackOptions, cancellationToken).ConfigureAwait(false); + var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); #endif using var _ = ArrayBuilder.GetInstance(out var nodesAndTokens); diff --git a/src/Analyzers/Core/Analyzers/AnalyzerOptionsProvider.cs b/src/Analyzers/Core/Analyzers/AnalyzerOptionsProvider.cs index efa914b38a10b..868cb3b170b66 100644 --- a/src/Analyzers/Core/Analyzers/AnalyzerOptionsProvider.cs +++ b/src/Analyzers/Core/Analyzers/AnalyzerOptionsProvider.cs @@ -54,7 +54,7 @@ public SimplifierOptions GetSimplifierOptions(ISimplification simplification) // SyntaxFormattingOptions public SyntaxFormattingOptions GetSyntaxFormattingOptions(ISyntaxFormatting formatting) - => formatting.GetFormattingOptions(_options, _fallbackOptions.CleanupOptions?.FormattingOptions); + => formatting.GetFormattingOptions(_options); // CodeGenerationOptions diff --git a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs index c3fcbfbd57629..9636bd3fbb871 100644 --- a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs +++ b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs @@ -28,6 +28,7 @@ using Microsoft.VisualStudio.Utilities; using Roslyn.Utilities; using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Diagnostics; namespace Microsoft.CodeAnalysis.Editor.CSharp.AutomaticCompletion; @@ -313,9 +314,10 @@ protected override void ModifySelectedNode( SyntaxNode selectedNode, bool addBrace, int caretPosition, + StructuredAnalyzerConfigOptions fallbackOptions, CancellationToken cancellationToken) { - var formattingOptions = args.SubjectBuffer.GetSyntaxFormattingOptions(EditorOptionsService, document.LanguageServices, explicitFormat: false); + var formattingOptions = args.SubjectBuffer.GetSyntaxFormattingOptions(EditorOptionsService, fallbackOptions, document.LanguageServices, explicitFormat: false); // Add braces for the selected node if (addBrace) diff --git a/src/EditorFeatures/CSharp/ConvertNamespace/ConvertNamespaceCommandHandler.cs b/src/EditorFeatures/CSharp/ConvertNamespace/ConvertNamespaceCommandHandler.cs index 4ad682630baff..76e1a337054ad 100644 --- a/src/EditorFeatures/CSharp/ConvertNamespace/ConvertNamespaceCommandHandler.cs +++ b/src/EditorFeatures/CSharp/ConvertNamespace/ConvertNamespaceCommandHandler.cs @@ -141,7 +141,7 @@ public void ExecuteCommand(TypeCharCommandArgs args, Action nextCommandHandler, if (!ConvertNamespaceAnalysis.CanOfferUseFileScoped(s_fileScopedNamespacePreferenceOption, (CompilationUnitSyntax)parsedDocument.Root, namespaceDecl, forAnalyzer: true, LanguageVersion.CSharp10)) return default; - var formattingOptions = subjectBuffer.GetSyntaxFormattingOptions(_editorOptionsService, document.Project.Services, explicitFormat: false); + var formattingOptions = subjectBuffer.GetSyntaxFormattingOptions(_editorOptionsService, document.Project.GetFallbackAnalyzerOptions(), document.Project.Services, explicitFormat: false); return ConvertNamespaceTransform.ConvertNamespaceDeclaration(parsedDocument, namespaceDecl, formattingOptions, cancellationToken); } } diff --git a/src/EditorFeatures/CSharp/Formatting/CSharpFormattingInteractionService.cs b/src/EditorFeatures/CSharp/Formatting/CSharpFormattingInteractionService.cs index 0cb940aba6795..c51e8dd9b7c3f 100644 --- a/src/EditorFeatures/CSharp/Formatting/CSharpFormattingInteractionService.cs +++ b/src/EditorFeatures/CSharp/Formatting/CSharpFormattingInteractionService.cs @@ -86,7 +86,7 @@ public Task> GetFormattingChangesAsync( CancellationToken cancellationToken) { var parsedDocument = ParsedDocument.CreateSynchronously(document, cancellationToken); - var options = textBuffer.GetSyntaxFormattingOptions(_editorOptionsService, parsedDocument.LanguageServices, explicitFormat: true); + var options = textBuffer.GetSyntaxFormattingOptions(_editorOptionsService, document.Project.GetFallbackAnalyzerOptions(), parsedDocument.LanguageServices, explicitFormat: true); var span = textSpan ?? new TextSpan(0, parsedDocument.Root.FullSpan.Length); var formattingSpan = CommonFormattingHelpers.GetFormattingSpan(parsedDocument.Root, span); @@ -97,7 +97,7 @@ public Task> GetFormattingChangesAsync( public Task> GetFormattingChangesOnPasteAsync(Document document, ITextBuffer textBuffer, TextSpan textSpan, CancellationToken cancellationToken) { var parsedDocument = ParsedDocument.CreateSynchronously(document, cancellationToken); - var options = textBuffer.GetSyntaxFormattingOptions(_editorOptionsService, parsedDocument.LanguageServices, explicitFormat: true); + var options = textBuffer.GetSyntaxFormattingOptions(_editorOptionsService, document.Project.GetFallbackAnalyzerOptions(), parsedDocument.LanguageServices, explicitFormat: true); var service = parsedDocument.LanguageServices.GetRequiredService(); return Task.FromResult(service.GetFormattingChangesOnPaste(parsedDocument, textSpan, options, cancellationToken)); } @@ -112,7 +112,7 @@ public Task> GetFormattingChangesAsync(Document docum if (service.ShouldFormatOnTypedCharacter(parsedDocument, typedChar, position, cancellationToken)) { - var indentationOptions = textBuffer.GetIndentationOptions(_editorOptionsService, parsedDocument.LanguageServices, explicitFormat: false); + var indentationOptions = textBuffer.GetIndentationOptions(_editorOptionsService, document.Project.GetFallbackAnalyzerOptions(), parsedDocument.LanguageServices, explicitFormat: false); return Task.FromResult(service.GetFormattingChangesOnTypedCharacter(parsedDocument, position, indentationOptions, cancellationToken)); } diff --git a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs index 8e163ffa7583d..0c66d57a8fa48 100644 --- a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs +++ b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs @@ -98,7 +98,7 @@ SyntaxKind.InterpolatedSingleLineRawStringStartToken or return false; } - var indentationOptions = subjectBuffer.GetIndentationOptions(_editorOptionsService, document.Project.Services, explicitFormat: false); + var indentationOptions = subjectBuffer.GetIndentationOptions(_editorOptionsService, document.Project.GetFallbackAnalyzerOptions(), document.Project.Services, explicitFormat: false); var indentation = token.GetPreferredIndentation(parsedDocument, indentationOptions, cancellationToken); var newLine = indentationOptions.FormattingOptions.NewLine; diff --git a/src/EditorFeatures/CSharp/SplitStringLiteral/SplitStringLiteralCommandHandler.cs b/src/EditorFeatures/CSharp/SplitStringLiteral/SplitStringLiteralCommandHandler.cs index aa0819766aa6e..d5cab6ad4a0d8 100644 --- a/src/EditorFeatures/CSharp/SplitStringLiteral/SplitStringLiteralCommandHandler.cs +++ b/src/EditorFeatures/CSharp/SplitStringLiteral/SplitStringLiteralCommandHandler.cs @@ -80,7 +80,7 @@ public bool ExecuteCommandWorker(ReturnKeyCommandArgs args, CancellationToken ca return false; var parsedDocument = ParsedDocument.CreateSynchronously(document, CancellationToken.None); - var indentationOptions = subjectBuffer.GetIndentationOptions(_editorOptionsService, parsedDocument.LanguageServices, explicitFormat: false); + var indentationOptions = subjectBuffer.GetIndentationOptions(_editorOptionsService, document.Project.GetFallbackAnalyzerOptions(), parsedDocument.LanguageServices, explicitFormat: false); // We now go through the verified string literals and split each of them. // The list of spans is traversed in reverse order so we do not have to diff --git a/src/EditorFeatures/CSharp/StringCopyPaste/StringCopyPasteCommandHandler.cs b/src/EditorFeatures/CSharp/StringCopyPaste/StringCopyPasteCommandHandler.cs index 592925bdef8ac..d1f7077462368 100644 --- a/src/EditorFeatures/CSharp/StringCopyPaste/StringCopyPasteCommandHandler.cs +++ b/src/EditorFeatures/CSharp/StringCopyPaste/StringCopyPasteCommandHandler.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Editor.StringCopyPaste; @@ -186,7 +187,7 @@ ImmutableArray GetEdits(CancellationToken cancellationToken) { var newLine = textView.Options.GetNewLineCharacter(); var indentationWhitespace = DetermineIndentationWhitespace( - parsedDocumentBeforePaste, subjectBuffer, snapshotBeforePaste.AsText(), stringExpressionBeforePaste, cancellationToken); + parsedDocumentBeforePaste, subjectBuffer, snapshotBeforePaste.AsText(), stringExpressionBeforePaste, documentBeforePaste.Project.GetFallbackAnalyzerOptions(), cancellationToken); // See if this is a paste of the last copy that we heard about. var edits = TryGetEditsFromKnownCopySource(newLine, indentationWhitespace); @@ -235,6 +236,7 @@ private string DetermineIndentationWhitespace( ITextBuffer textBuffer, SourceText textBeforePaste, ExpressionSyntax stringExpressionBeforePaste, + StructuredAnalyzerConfigOptions fallbackOptions, CancellationToken cancellationToken) { // Only raw strings care about indentation. Don't bother computing if we don't need it. @@ -252,7 +254,7 @@ private string DetermineIndentationWhitespace( // Otherwise, we have a single-line raw string. Determine the default indentation desired here. // We'll use that if we have to convert this single-line raw string to a multi-line one. - var indentationOptions = textBuffer.GetIndentationOptions(_editorOptionsService, documentBeforePaste.LanguageServices, explicitFormat: false); + var indentationOptions = textBuffer.GetIndentationOptions(_editorOptionsService, fallbackOptions, documentBeforePaste.LanguageServices, explicitFormat: false); return stringExpressionBeforePaste.GetFirstToken().GetPreferredIndentation(documentBeforePaste, indentationOptions, cancellationToken); } diff --git a/src/EditorFeatures/Core/AutomaticCompletion/AbstractAutomaticLineEnderCommandHandler.cs b/src/EditorFeatures/Core/AutomaticCompletion/AbstractAutomaticLineEnderCommandHandler.cs index 9515c1e4ed9c9..04a34b24d239b 100644 --- a/src/EditorFeatures/Core/AutomaticCompletion/AbstractAutomaticLineEnderCommandHandler.cs +++ b/src/EditorFeatures/Core/AutomaticCompletion/AbstractAutomaticLineEnderCommandHandler.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Threading; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Options; @@ -61,7 +62,7 @@ protected AbstractAutomaticLineEnderCommandHandler( /// /// Add or remove the braces for . /// - protected abstract void ModifySelectedNode(AutomaticLineEnderCommandArgs args, ParsedDocument document, SyntaxNode selectedNode, bool addBrace, int caretPosition, CancellationToken cancellationToken); + protected abstract void ModifySelectedNode(AutomaticLineEnderCommandArgs args, ParsedDocument document, SyntaxNode selectedNode, bool addBrace, int caretPosition, StructuredAnalyzerConfigOptions fallbackOptions, CancellationToken cancellationToken); /// /// Get the syntax node needs add/remove braces. @@ -131,7 +132,7 @@ public void ExecuteCommand(AutomaticLineEnderCommandArgs args, Action nextHandle { var (selectedNode, addBrace) = selectNodeAndOperationKind.Value; using var transaction = args.TextView.CreateEditTransaction(EditorFeaturesResources.Automatic_Line_Ender, _undoRegistry, _editorOperationsFactoryService); - ModifySelectedNode(args, parsedDocument, selectedNode, addBrace, caretPosition, cancellationToken); + ModifySelectedNode(args, parsedDocument, selectedNode, addBrace, caretPosition, document.Project.GetFallbackAnalyzerOptions(), cancellationToken); NextAction(operations, nextHandler); transaction.Complete(); return; @@ -142,7 +143,7 @@ public void ExecuteCommand(AutomaticLineEnderCommandArgs args, Action nextHandle if (endingInsertionPosition != null) { using var transaction = args.TextView.CreateEditTransaction(EditorFeaturesResources.Automatic_Line_Ender, _undoRegistry, _editorOperationsFactoryService); - var formattingOptions = args.SubjectBuffer.GetSyntaxFormattingOptions(EditorOptionsService, parsedDocument.LanguageServices, explicitFormat: false); + var formattingOptions = args.SubjectBuffer.GetSyntaxFormattingOptions(EditorOptionsService, document.Project.GetFallbackAnalyzerOptions(), parsedDocument.LanguageServices, explicitFormat: false); InsertEnding(args.TextView, args.SubjectBuffer, parsedDocument, endingInsertionPosition.Value, caretPosition, formattingOptions, cancellationToken); NextAction(operations, nextHandler); transaction.Complete(); diff --git a/src/EditorFeatures/Core/AutomaticCompletion/BraceCompletionSessionProvider.BraceCompletionSession.cs b/src/EditorFeatures/Core/AutomaticCompletion/BraceCompletionSessionProvider.BraceCompletionSession.cs index 3d520a1dc087d..3d9c496a7b667 100644 --- a/src/EditorFeatures/Core/AutomaticCompletion/BraceCompletionSessionProvider.BraceCompletionSession.cs +++ b/src/EditorFeatures/Core/AutomaticCompletion/BraceCompletionSessionProvider.BraceCompletionSession.cs @@ -8,6 +8,7 @@ using System.Diagnostics; using System.Threading; using Microsoft.CodeAnalysis.BraceCompletion; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.ErrorReporting; @@ -105,7 +106,7 @@ private bool TryStart(CancellationToken cancellationToken) } var parsedDocument = ParsedDocument.CreateSynchronously(document, cancellationToken); - var context = GetBraceCompletionContext(parsedDocument); + var context = GetBraceCompletionContext(parsedDocument, document.Project.GetFallbackAnalyzerOptions()); // Note: completes synchronously unless Semantic Model is needed to determine the result: if (!_service.HasBraceCompletionAsync(context, document, cancellationToken).WaitAndGetResult(cancellationToken)) @@ -125,7 +126,7 @@ private bool TryStart(CancellationToken cancellationToken) if (TryGetBraceCompletionContext(out var contextAfterStart, cancellationToken)) { - var indentationOptions = SubjectBuffer.GetIndentationOptions(_editorOptionsService, contextAfterStart.Document.LanguageServices, explicitFormat: false); + var indentationOptions = SubjectBuffer.GetIndentationOptions(_editorOptionsService, document.Project.GetFallbackAnalyzerOptions(), contextAfterStart.Document.LanguageServices, explicitFormat: false); var changesAfterStart = _service.GetTextChangesAfterCompletion(contextAfterStart, indentationOptions, cancellationToken); if (changesAfterStart != null) { @@ -283,7 +284,7 @@ public void PostReturn() return; } - var indentationOptions = SubjectBuffer.GetIndentationOptions(_editorOptionsService, context.Document.LanguageServices, explicitFormat: false); + var indentationOptions = SubjectBuffer.GetIndentationOptions(_editorOptionsService, context.FallbackOptions, context.Document.LanguageServices, explicitFormat: false); var changesAfterReturn = _service.GetTextChangeAfterReturn(context, indentationOptions, CancellationToken.None); if (changesAfterReturn != null) { @@ -397,11 +398,11 @@ private bool TryGetBraceCompletionContext(out BraceCompletionContext context, Ca return false; } - context = GetBraceCompletionContext(ParsedDocument.CreateSynchronously(document, cancellationToken)); + context = GetBraceCompletionContext(ParsedDocument.CreateSynchronously(document, cancellationToken), document.Project.GetFallbackAnalyzerOptions()); return true; } - private BraceCompletionContext GetBraceCompletionContext(ParsedDocument document) + private BraceCompletionContext GetBraceCompletionContext(ParsedDocument document, StructuredAnalyzerConfigOptions fallbackOptions) { _threadingContext.ThrowIfNotOnUIThread(); var snapshot = SubjectBuffer.CurrentSnapshot; @@ -411,7 +412,7 @@ private BraceCompletionContext GetBraceCompletionContext(ParsedDocument document // The user is actively typing so the caret position should not be null. var caretPosition = this.GetCaretPosition().Value.Position; - return new BraceCompletionContext(document, openingSnapshotPoint, closingSnapshotPoint, caretPosition); + return new BraceCompletionContext(document, fallbackOptions, openingSnapshotPoint, closingSnapshotPoint, caretPosition); } private void ApplyBraceCompletionResult(BraceCompletionResult result) diff --git a/src/EditorFeatures/Core/CommentSelection/AbstractCommentSelectionBase.cs b/src/EditorFeatures/Core/CommentSelection/AbstractCommentSelectionBase.cs index 7609d2fc0377f..57a7ca49ceefa 100644 --- a/src/EditorFeatures/Core/CommentSelection/AbstractCommentSelectionBase.cs +++ b/src/EditorFeatures/Core/CommentSelection/AbstractCommentSelectionBase.cs @@ -149,7 +149,7 @@ private void ApplyEdits(Document document, ITextView textView, ITextBuffer subje var oldSyntaxTree = document.GetSyntaxTreeSynchronously(cancellationToken); var newRoot = oldSyntaxTree.WithChangedText(newText).GetRoot(cancellationToken); - var formattingOptions = subjectBuffer.GetSyntaxFormattingOptions(_editorOptionsService, document.Project.Services, explicitFormat: false); + var formattingOptions = subjectBuffer.GetSyntaxFormattingOptions(_editorOptionsService, document.Project.GetFallbackAnalyzerOptions(), document.Project.Services, explicitFormat: false); var formattingSpans = trackingSnapshotSpans.Select(change => CommonFormattingHelpers.GetFormattingSpan(newRoot, change.Span.ToTextSpan())); var formattedChanges = Formatter.GetFormattedTextChanges(newRoot, formattingSpans, document.Project.Solution.Services, formattingOptions, rules: default, cancellationToken); diff --git a/src/EditorFeatures/Core/Options/EditorAnalyzerConfigOptions.cs b/src/EditorFeatures/Core/Options/EditorAnalyzerConfigOptions.cs index 89086b1131b26..49a55053e7043 100644 --- a/src/EditorFeatures/Core/Options/EditorAnalyzerConfigOptions.cs +++ b/src/EditorFeatures/Core/Options/EditorAnalyzerConfigOptions.cs @@ -61,6 +61,6 @@ private static bool IsLowercase(string str) internal static partial class EditorOptionsExtensions { - public static StructuredAnalyzerConfigOptions ToAnalyzerConfigOptions(this IEditorOptions editorOptions) - => StructuredAnalyzerConfigOptions.Create(new EditorAnalyzerConfigOptions(editorOptions)); + public static StructuredAnalyzerConfigOptions ToAnalyzerConfigOptions(this IEditorOptions editorOptions, StructuredAnalyzerConfigOptions fallbackOptions) + => StructuredAnalyzerConfigOptions.Create(new EditorAnalyzerConfigOptions(editorOptions), fallbackOptions); } diff --git a/src/EditorFeatures/Core/Options/TextBufferOptionProviders.cs b/src/EditorFeatures/Core/Options/TextBufferOptionProviders.cs index ce61da12f9add..50ab0fb90a025 100644 --- a/src/EditorFeatures/Core/Options/TextBufferOptionProviders.cs +++ b/src/EditorFeatures/Core/Options/TextBufferOptionProviders.cs @@ -40,23 +40,22 @@ private static LineFormattingOptions GetLineFormattingOptionsImpl(ITextBuffer te }; } - public static SyntaxFormattingOptions GetSyntaxFormattingOptions(this ITextBuffer textBuffer, EditorOptionsService optionsProvider, LanguageServices languageServices, bool explicitFormat) - => GetSyntaxFormattingOptionsImpl(textBuffer, optionsProvider.Factory.GetOptions(textBuffer), optionsProvider.IndentationManager, optionsProvider.GlobalOptions, languageServices, explicitFormat); + public static SyntaxFormattingOptions GetSyntaxFormattingOptions(this ITextBuffer textBuffer, EditorOptionsService optionsProvider, StructuredAnalyzerConfigOptions fallbackOptions, LanguageServices languageServices, bool explicitFormat) + => GetSyntaxFormattingOptionsImpl(textBuffer, optionsProvider.Factory.GetOptions(textBuffer), fallbackOptions, optionsProvider.IndentationManager, languageServices, explicitFormat); - private static SyntaxFormattingOptions GetSyntaxFormattingOptionsImpl(ITextBuffer textBuffer, IEditorOptions editorOptions, IIndentationManagerService indentationManager, IGlobalOptionService globalOptions, LanguageServices languageServices, bool explicitFormat) + private static SyntaxFormattingOptions GetSyntaxFormattingOptionsImpl(ITextBuffer textBuffer, IEditorOptions editorOptions, StructuredAnalyzerConfigOptions fallbackOptions, IIndentationManagerService indentationManager, LanguageServices languageServices, bool explicitFormat) { - var configOptions = editorOptions.ToAnalyzerConfigOptions(); - var fallbackOptions = globalOptions.GetSyntaxFormattingOptions(languageServices); - var options = configOptions.GetSyntaxFormattingOptions(languageServices, fallbackOptions); + var configOptions = editorOptions.ToAnalyzerConfigOptions(fallbackOptions); + var options = configOptions.GetSyntaxFormattingOptions(languageServices); var lineFormattingOptions = GetLineFormattingOptionsImpl(textBuffer, editorOptions, indentationManager, explicitFormat); return options with { LineFormatting = lineFormattingOptions }; } - public static IndentationOptions GetIndentationOptions(this ITextBuffer textBuffer, EditorOptionsService optionsProvider, LanguageServices languageServices, bool explicitFormat) + public static IndentationOptions GetIndentationOptions(this ITextBuffer textBuffer, EditorOptionsService optionsProvider, StructuredAnalyzerConfigOptions fallbackOptions, LanguageServices languageServices, bool explicitFormat) { var editorOptions = optionsProvider.Factory.GetOptions(textBuffer); - var formattingOptions = GetSyntaxFormattingOptionsImpl(textBuffer, editorOptions, optionsProvider.IndentationManager, optionsProvider.GlobalOptions, languageServices, explicitFormat); + var formattingOptions = GetSyntaxFormattingOptionsImpl(textBuffer, editorOptions, fallbackOptions, optionsProvider.IndentationManager, languageServices, explicitFormat); return new IndentationOptions(formattingOptions) { @@ -66,21 +65,19 @@ public static IndentationOptions GetIndentationOptions(this ITextBuffer textBuff }; } - public static AddImportPlacementOptions GetAddImportPlacementOptions(this ITextBuffer textBuffer, EditorOptionsService optionsProvider, LanguageServices languageServices, bool allowInHiddenRegions) + public static AddImportPlacementOptions GetAddImportPlacementOptions(this ITextBuffer textBuffer, EditorOptionsService optionsProvider, StructuredAnalyzerConfigOptions fallbackOptions, LanguageServices languageServices, bool allowInHiddenRegions) { var editorOptions = optionsProvider.Factory.GetOptions(textBuffer); - var configOptions = editorOptions.ToAnalyzerConfigOptions(); - var fallbackOptions = optionsProvider.GlobalOptions.GetAddImportPlacementOptions(languageServices); - return configOptions.GetAddImportPlacementOptions(languageServices, allowInHiddenRegions, fallbackOptions); + var configOptions = editorOptions.ToAnalyzerConfigOptions(fallbackOptions); + return configOptions.GetAddImportPlacementOptions(languageServices, allowInHiddenRegions, fallbackOptions: null); } - public static CodeCleanupOptions GetCodeCleanupOptions(this ITextBuffer textBuffer, EditorOptionsService optionsProvider, LanguageServices languageServices, bool explicitFormat, bool allowImportsInHiddenRegions) + public static CodeCleanupOptions GetCodeCleanupOptions(this ITextBuffer textBuffer, EditorOptionsService optionsProvider, StructuredAnalyzerConfigOptions fallbackOptions, LanguageServices languageServices, bool explicitFormat, bool allowImportsInHiddenRegions) { var editorOptions = optionsProvider.Factory.GetOptions(textBuffer); - var configOptions = editorOptions.ToAnalyzerConfigOptions(); - var fallbackOptions = optionsProvider.GlobalOptions.GetCodeCleanupOptions(languageServices); + var configOptions = editorOptions.ToAnalyzerConfigOptions(fallbackOptions); - var options = configOptions.GetCodeCleanupOptions(languageServices, allowImportsInHiddenRegions, fallbackOptions); + var options = configOptions.GetCodeCleanupOptions(languageServices, allowImportsInHiddenRegions, fallbackOptions: null); var lineFormattingOptions = GetLineFormattingOptionsImpl(textBuffer, editorOptions, optionsProvider.IndentationManager, explicitFormat); return options with { FormattingOptions = options.FormattingOptions with { LineFormatting = lineFormattingOptions } }; diff --git a/src/EditorFeatures/Core/Organizing/OrganizeDocumentCommandHandler.cs b/src/EditorFeatures/Core/Organizing/OrganizeDocumentCommandHandler.cs index 6e14ab4d415b6..eb78e0649ee55 100644 --- a/src/EditorFeatures/Core/Organizing/OrganizeDocumentCommandHandler.cs +++ b/src/EditorFeatures/Core/Organizing/OrganizeDocumentCommandHandler.cs @@ -187,7 +187,7 @@ public bool ExecuteCommand(SortAndRemoveUnnecessaryImportsCommandArgs args, Comm async (document, cancellationToken) => { var formattingOptions = document.SupportsSyntaxTree - ? await document.GetSyntaxFormattingOptionsAsync(_globalOptions, cancellationToken).ConfigureAwait(false) + ? await document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false) : null; var removeImportsService = document.GetRequiredLanguageService(); diff --git a/src/EditorFeatures/Core/Shared/Extensions/ITextSnapshotExtensions.cs b/src/EditorFeatures/Core/Shared/Extensions/ITextSnapshotExtensions.cs index e9e3c5b7e0c9b..5b5b5b540f502 100644 --- a/src/EditorFeatures/Core/Shared/Extensions/ITextSnapshotExtensions.cs +++ b/src/EditorFeatures/Core/Shared/Extensions/ITextSnapshotExtensions.cs @@ -39,7 +39,7 @@ public static void FormatAndApplyToBuffer( var formatter = document.GetRequiredLanguageService(); - var options = textBuffer.GetSyntaxFormattingOptions(editorOptionsService, document.Project.Services, explicitFormat: false); + var options = textBuffer.GetSyntaxFormattingOptions(editorOptionsService, document.Project.GetFallbackAnalyzerOptions(), document.Project.Services, explicitFormat: false); var result = formatter.GetFormattingResult(documentSyntax.Root, [span], options, rules, cancellationToken); var changes = result.GetTextChanges(cancellationToken); diff --git a/src/EditorFeatures/Core/SmartIndent/SmartIndent.cs b/src/EditorFeatures/Core/SmartIndent/SmartIndent.cs index 335d0519d9661..7d573c4db7f21 100644 --- a/src/EditorFeatures/Core/SmartIndent/SmartIndent.cs +++ b/src/EditorFeatures/Core/SmartIndent/SmartIndent.cs @@ -42,7 +42,7 @@ public void Dispose() if (newService == null) return null; - var indentationOptions = line.Snapshot.TextBuffer.GetIndentationOptions(_editorOptionsService, document.Project.Services, explicitFormat: false); + var indentationOptions = line.Snapshot.TextBuffer.GetIndentationOptions(_editorOptionsService, document.Project.GetFallbackAnalyzerOptions(), document.Project.Services, explicitFormat: false); var parsedDocument = ParsedDocument.CreateSynchronously(document, cancellationToken); var result = newService.GetIndentation(parsedDocument, line.LineNumber, indentationOptions, cancellationToken); return result.GetIndentation(_textView, line); diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/ChangeSignature/ChangeSignatureTestState.cs b/src/EditorFeatures/DiagnosticsTestUtilities/ChangeSignature/ChangeSignatureTestState.cs index ec9853314c97e..9c7a3734976ca 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/ChangeSignature/ChangeSignatureTestState.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/ChangeSignature/ChangeSignatureTestState.cs @@ -38,7 +38,7 @@ public static ChangeSignatureTestState Create(string markup, string languageName _ => throw new ArgumentException("Invalid language name.") }; - options?.SetGlobalOptions(workspace.GlobalOptions); + workspace.SetAnalyzerFallbackAndGlobalOptions(options); return new ChangeSignatureTestState(workspace); } diff --git a/src/EditorFeatures/TestUtilities/AutomaticCompletion/AbstractAutomaticBraceCompletionTests.cs b/src/EditorFeatures/TestUtilities/AutomaticCompletion/AbstractAutomaticBraceCompletionTests.cs index d1b2607fc1a28..034d50afdbfaa 100644 --- a/src/EditorFeatures/TestUtilities/AutomaticCompletion/AbstractAutomaticBraceCompletionTests.cs +++ b/src/EditorFeatures/TestUtilities/AutomaticCompletion/AbstractAutomaticBraceCompletionTests.cs @@ -144,6 +144,8 @@ internal static void Type(IBraceCompletionSession session, string text) internal static Holder CreateSession(EditorTestWorkspace workspace, char opening, char closing, OptionsCollection globalOptions = null) { + workspace.SetAnalyzerFallbackAndGlobalOptions(globalOptions); + var document = workspace.Documents.First(); var provider = Assert.IsType(workspace.GetService()); @@ -151,7 +153,6 @@ internal static Holder CreateSession(EditorTestWorkspace workspace, char opening var openingPoint = new SnapshotPoint(document.GetTextBuffer().CurrentSnapshot, document.CursorPosition.Value); var textView = document.GetTextView(); - globalOptions?.SetGlobalOptions(workspace.GlobalOptions); workspace.GlobalOptions.SetEditorOptions(textView.Options.GlobalOptions, document.Project.Language); if (provider.TryCreateSession(textView, openingPoint, opening, closing, out var session)) diff --git a/src/EditorFeatures/TestUtilities/ExtractInterface/ExtractInterfaceTestState.cs b/src/EditorFeatures/TestUtilities/ExtractInterface/ExtractInterfaceTestState.cs index e10dfa5df6db2..c466125a9c7ac 100644 --- a/src/EditorFeatures/TestUtilities/ExtractInterface/ExtractInterfaceTestState.cs +++ b/src/EditorFeatures/TestUtilities/ExtractInterface/ExtractInterfaceTestState.cs @@ -42,7 +42,7 @@ public static ExtractInterfaceTestState Create( ? EditorTestWorkspace.CreateCSharp(markup, composition: Composition, compilationOptions: compilationOptions, parseOptions: parseOptions) : EditorTestWorkspace.CreateVisualBasic(markup, composition: Composition, compilationOptions: compilationOptions, parseOptions: parseOptions); - options?.SetGlobalOptions(workspace.GlobalOptions); + workspace.SetAnalyzerFallbackAndGlobalOptions(options); return new ExtractInterfaceTestState(workspace); } diff --git a/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs index 8334f80402998..f99a17ee49a6d 100644 --- a/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs +++ b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs @@ -131,7 +131,7 @@ private protected void AssertFormatWithView(string expectedWithMarker, string co { using var workspace = CreateWorkspace(codeWithMarker, parseOptions); - options?.SetGlobalOptions(workspace.GlobalOptions); + workspace.SetAnalyzerFallbackAndGlobalOptions(options); // set up caret position var testDocument = workspace.Documents.Single(); @@ -200,7 +200,7 @@ private protected async Task AssertFormatAsync(string expected, string code, IEn var formattingService = document.GetRequiredLanguageService(); var formattingOptions = (options != null) - ? formattingService.GetFormattingOptions(options, fallbackOptions: null) + ? formattingService.GetFormattingOptions(options) : formattingService.DefaultOptions; ImmutableArray rules = [formattingRuleProvider.CreateRule(documentSyntax, 0), .. Formatter.GetDefaultFormattingRules(document)]; @@ -286,7 +286,7 @@ protected static void AssertFormatOnArbitraryNode(SyntaxNode node, string expect { using var workspace = new AdhocWorkspace(); var formattingService = workspace.Services.GetLanguageServices(node.Language).GetRequiredService(); - var options = formattingService.GetFormattingOptions(StructuredAnalyzerConfigOptions.Empty, fallbackOptions: null); + var options = formattingService.GetFormattingOptions(StructuredAnalyzerConfigOptions.Empty); var result = Formatter.Format(node, workspace.Services.SolutionServices, options, CancellationToken.None); var actual = result.GetText().ToString(); diff --git a/src/EditorFeatures/VisualBasic/AutomaticCompletion/AutomaticLineEnderCommandHandler.vb b/src/EditorFeatures/VisualBasic/AutomaticCompletion/AutomaticLineEnderCommandHandler.vb index 3ff2273657ce8..aeae57260ace0 100644 --- a/src/EditorFeatures/VisualBasic/AutomaticCompletion/AutomaticLineEnderCommandHandler.vb +++ b/src/EditorFeatures/VisualBasic/AutomaticCompletion/AutomaticLineEnderCommandHandler.vb @@ -5,6 +5,7 @@ Imports System.ComponentModel.Composition Imports System.Threading Imports Microsoft.CodeAnalysis.AutomaticCompletion +Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.Formatting Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.Options @@ -46,7 +47,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.AutomaticCompletion Return False End Function - Protected Overrides Sub ModifySelectedNode(args As AutomaticLineEnderCommandArgs, document As ParsedDocument, selectedNode As SyntaxNode, addBrace As Boolean, caretPosition As Integer, cancellationToken As CancellationToken) + Protected Overrides Sub ModifySelectedNode(args As AutomaticLineEnderCommandArgs, document As ParsedDocument, selectedNode As SyntaxNode, addBrace As Boolean, caretPosition As Integer, fallbackOptions As StructuredAnalyzerConfigOptions, cancellationToken As CancellationToken) End Sub Protected Overrides Function GetValidNodeToModifyBraces(document As ParsedDocument, caretPosition As Integer, cancellationToken As CancellationToken) As (SyntaxNode, Boolean)? diff --git a/src/EditorFeatures/VisualBasic/EndConstructGeneration/EndConstructCommandHandler.vb b/src/EditorFeatures/VisualBasic/EndConstructGeneration/EndConstructCommandHandler.vb index b7e5fc83c4665..7d593f2862e37 100644 --- a/src/EditorFeatures/VisualBasic/EndConstructGeneration/EndConstructCommandHandler.vb +++ b/src/EditorFeatures/VisualBasic/EndConstructGeneration/EndConstructCommandHandler.vb @@ -140,7 +140,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.EndConstructGeneration Return p.Name = PredefinedCodeCleanupProviderNames.NormalizeModifiersOrOperators End Function) - Dim options = buffer.GetCodeCleanupOptions(_editorOptionsService, document.Project.Services, explicitFormat:=False, allowImportsInHiddenRegions:=document.AllowImportsInHiddenRegions()) + Dim options = buffer.GetCodeCleanupOptions(_editorOptionsService, document.Project.GetFallbackAnalyzerOptions(), document.Project.Services, explicitFormat:=False, allowImportsInHiddenRegions:=document.AllowImportsInHiddenRegions()) Dim cleanDocument = CodeCleaner.CleanupAsync(document, GetSpanToCleanup(statement), Options, codeCleanups, cancellationToken:=cancellationToken).WaitAndGetResult(cancellationToken) Dim changes = cleanDocument.GetTextChangesAsync(document, cancellationToken).WaitAndGetResult(cancellationToken) diff --git a/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb b/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb index 6fa92b52053b2..89db5228db666 100644 --- a/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb +++ b/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb @@ -72,7 +72,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.LineCommit End If ' create commit formatting cleanup provider that has line commit specific behavior - Dim cleanupOptions = buffer.GetCodeCleanupOptions(_editorOptionsService, document.Project.Services, isExplicitFormat, allowImportsInHiddenRegions:=document.AllowImportsInHiddenRegions()) + Dim cleanupOptions = buffer.GetCodeCleanupOptions(_editorOptionsService, document.Project.GetFallbackAnalyzerOptions(), document.Project.Services, isExplicitFormat, allowImportsInHiddenRegions:=document.AllowImportsInHiddenRegions()) Dim commitFormattingCleanup = GetCommitFormattingCleanupProvider( document.Id, document.Project.Services, diff --git a/src/EditorFeatures/VisualBasic/NavigationBar/VisualBasicEditorNavigationBarItemService_CodeGeneration.vb b/src/EditorFeatures/VisualBasic/NavigationBar/VisualBasicEditorNavigationBarItemService_CodeGeneration.vb index 3bd42bc2532c8..b98364b8ffbf6 100644 --- a/src/EditorFeatures/VisualBasic/NavigationBar/VisualBasicEditorNavigationBarItemService_CodeGeneration.vb +++ b/src/EditorFeatures/VisualBasic/NavigationBar/VisualBasicEditorNavigationBarItemService_CodeGeneration.vb @@ -61,7 +61,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.NavigationBar End If Dim simplifierOptions = Await newDocument.GetSimplifierOptionsAsync(cancellationToken).ConfigureAwait(False) - Dim formattingOptions = Await newDocument.GetSyntaxFormattingOptionsAsync(globalOptions, cancellationToken).ConfigureAwait(False) + Dim formattingOptions = Await newDocument.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(False) newDocument = Await Simplifier.ReduceAsync(newDocument, Simplifier.Annotation, simplifierOptions, cancellationToken).ConfigureAwait(False) diff --git a/src/Features/CSharp/Portable/ConvertToRawString/ConvertStringToRawStringCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertToRawString/ConvertStringToRawStringCodeRefactoringProvider.cs index 3e001256b88f8..fb7e0ac174695 100644 --- a/src/Features/CSharp/Portable/ConvertToRawString/ConvertStringToRawStringCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertToRawString/ConvertStringToRawStringCodeRefactoringProvider.cs @@ -77,7 +77,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte return; var options = context.Options; - var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(options, cancellationToken).ConfigureAwait(false); + var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); var parsedDocument = await ParsedDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false); if (!CanConvert(parsedDocument, parentExpression, formattingOptions, out var convertParams, out var provider, cancellationToken)) @@ -90,7 +90,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte context.RegisterRefactoring( CodeAction.Create( CSharpFeaturesResources.Convert_to_raw_string, - cancellationToken => UpdateDocumentAsync(document, parentExpression, ConvertToRawKind.SingleLine, options, provider, cancellationToken), + cancellationToken => UpdateDocumentAsync(document, parentExpression, ConvertToRawKind.SingleLine, provider, cancellationToken), s_kindToEquivalenceKeyMap[ConvertToRawKind.SingleLine], priority), token.Span); @@ -100,7 +100,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte context.RegisterRefactoring( CodeAction.Create( CSharpFeaturesResources.Convert_to_raw_string, - cancellationToken => UpdateDocumentAsync(document, parentExpression, ConvertToRawKind.MultiLineIndented, options, provider, cancellationToken), + cancellationToken => UpdateDocumentAsync(document, parentExpression, ConvertToRawKind.MultiLineIndented, provider, cancellationToken), s_kindToEquivalenceKeyMap[ConvertToRawKind.MultiLineIndented], priority), token.Span); @@ -110,7 +110,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte context.RegisterRefactoring( CodeAction.Create( CSharpFeaturesResources.without_leading_whitespace_may_change_semantics, - cancellationToken => UpdateDocumentAsync(document, parentExpression, ConvertToRawKind.MultiLineWithoutLeadingWhitespace, options, provider, cancellationToken), + cancellationToken => UpdateDocumentAsync(document, parentExpression, ConvertToRawKind.MultiLineWithoutLeadingWhitespace, provider, cancellationToken), s_kindToEquivalenceKeyMap[ConvertToRawKind.MultiLineWithoutLeadingWhitespace], priority), token.Span); @@ -122,11 +122,10 @@ private static async Task UpdateDocumentAsync( Document document, ExpressionSyntax expression, ConvertToRawKind kind, - CodeActionOptionsProvider optionsProvider, IConvertStringProvider provider, CancellationToken cancellationToken) { - var options = await document.GetSyntaxFormattingOptionsAsync(optionsProvider, cancellationToken).ConfigureAwait(false); + var options = await document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var parsedDocument = await ParsedDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false); @@ -146,7 +145,7 @@ protected override async Task FixAllAsync( Debug.Assert(equivalenceKey != null); var kind = s_kindToEquivalenceKeyMap[equivalenceKey]; - var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(optionsProvider, cancellationToken).ConfigureAwait(false); + var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); var parsedDocument = await ParsedDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false); foreach (var fixSpan in fixAllSpans) diff --git a/src/Features/CSharp/Portable/InitializeParameter/CSharpInitializeMemberFromPrimaryConstructorParameterCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/InitializeParameter/CSharpInitializeMemberFromPrimaryConstructorParameterCodeRefactoringProvider.cs index c7892f461d62a..bb0d0296fc386 100644 --- a/src/Features/CSharp/Portable/InitializeParameter/CSharpInitializeMemberFromPrimaryConstructorParameterCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/InitializeParameter/CSharpInitializeMemberFromPrimaryConstructorParameterCodeRefactoringProvider.cs @@ -74,7 +74,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte if (parameterNameParts.BaseName == "") return; - var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(fallbackOptions, cancellationToken).ConfigureAwait(false); + var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); var fieldOrProperty = TryFindMatchingUninitializedFieldOrPropertySymbol(); var refactorings = fieldOrProperty == null diff --git a/src/Features/CSharp/Portable/Snippets/AbstractCSharpTypeSnippetProvider.cs b/src/Features/CSharp/Portable/Snippets/AbstractCSharpTypeSnippetProvider.cs index 5ba4d33c0368f..6569ef7bdd066 100644 --- a/src/Features/CSharp/Portable/Snippets/AbstractCSharpTypeSnippetProvider.cs +++ b/src/Features/CSharp/Portable/Snippets/AbstractCSharpTypeSnippetProvider.cs @@ -95,7 +95,7 @@ protected override async Task AddIndentationToDocumentAsync(Document d { var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var syntaxFormattingOptions = await document.GetSyntaxFormattingOptionsAsync(fallbackOptions: null, cancellationToken).ConfigureAwait(false); + var syntaxFormattingOptions = await document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); var indentationString = CSharpSnippetHelpers.GetBlockLikeIndentationString(document, typeDeclaration.OpenBraceToken.SpanStart, syntaxFormattingOptions, cancellationToken); var newTypeDeclaration = typeDeclaration.WithCloseBraceToken( diff --git a/src/Features/CSharp/Portable/Snippets/CSharpIntMainSnippetProvider.cs b/src/Features/CSharp/Portable/Snippets/CSharpIntMainSnippetProvider.cs index 8016a03df0e8e..47f4954c017fa 100644 --- a/src/Features/CSharp/Portable/Snippets/CSharpIntMainSnippetProvider.cs +++ b/src/Features/CSharp/Portable/Snippets/CSharpIntMainSnippetProvider.cs @@ -54,7 +54,7 @@ protected override async Task AddIndentationToDocumentAsync(Document d var body = methodDeclaration.Body!; var returnStatement = body.Statements.First(); - var syntaxFormattingOptions = await document.GetSyntaxFormattingOptionsAsync(fallbackOptions: null, cancellationToken).ConfigureAwait(false); + var syntaxFormattingOptions = await document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); var indentationString = CSharpSnippetHelpers.GetBlockLikeIndentationString(document, body.OpenBraceToken.SpanStart, syntaxFormattingOptions, cancellationToken); var updatedReturnStatement = returnStatement.WithPrependedLeadingTrivia(SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, indentationString)); diff --git a/src/Features/CSharp/Portable/Snippets/CSharpSnippetHelpers.cs b/src/Features/CSharp/Portable/Snippets/CSharpSnippetHelpers.cs index 40ee535b91968..9b6676458596c 100644 --- a/src/Features/CSharp/Portable/Snippets/CSharpSnippetHelpers.cs +++ b/src/Features/CSharp/Portable/Snippets/CSharpSnippetHelpers.cs @@ -49,7 +49,7 @@ public static async Task AddBlockIndentationToDocumentAsync - BraceCompletionContext? GetCompletedBraceContext(ParsedDocument document, int caretLocation); + BraceCompletionContext? GetCompletedBraceContext(ParsedDocument document, StructuredAnalyzerConfigOptions fallbackOptions, int caretLocation); /// /// Returns true if over typing should be allowed given the caret location and completed pair of braces. @@ -89,10 +90,12 @@ internal readonly struct BraceCompletionResult(ImmutableArray textCh public LinePosition CaretLocation { get; } = caretLocation; } -internal readonly struct BraceCompletionContext(ParsedDocument document, int openingPoint, int closingPoint, int caretLocation) +internal readonly struct BraceCompletionContext(ParsedDocument document, StructuredAnalyzerConfigOptions fallbackOptions, int openingPoint, int closingPoint, int caretLocation) { public ParsedDocument Document { get; } = document; + public StructuredAnalyzerConfigOptions FallbackOptions { get; } = fallbackOptions; + public int OpeningPoint { get; } = openingPoint; public int ClosingPoint { get; } = closingPoint; diff --git a/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs b/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs index 928e676f85863..440ae7946921c 100644 --- a/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs +++ b/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs @@ -401,7 +401,7 @@ private static async Task> FindChangeSignatureR }); var annotatedNodes = newRoot.GetAnnotatedNodes(syntaxAnnotation: changeSignatureFormattingAnnotation); - var formattingOptions = await doc.GetSyntaxFormattingOptionsAsync(context.FallbackOptions, cancellationToken).ConfigureAwait(false); + var formattingOptions = await doc.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); var formattedRoot = Formatter.Format( newRoot, diff --git a/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureCodeStyle/ConfigureCodeStyleOptionCodeFixProvider.cs b/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureCodeStyle/ConfigureCodeStyleOptionCodeFixProvider.cs index 42e5660f87dac..2ce7630a16636 100644 --- a/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureCodeStyle/ConfigureCodeStyleOptionCodeFixProvider.cs +++ b/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureCodeStyle/ConfigureCodeStyleOptionCodeFixProvider.cs @@ -53,10 +53,10 @@ public bool IsFixableDiagnostic(Diagnostic diagnostic) public FixAllProvider? GetFixAllProvider() => null; - public Task> GetFixesAsync(TextDocument document, TextSpan span, IEnumerable diagnostics, CodeActionOptionsProvider fallbackOptions, CancellationToken cancellationToken) + public Task> GetFixesAsync(TextDocument document, TextSpan span, IEnumerable diagnostics, CancellationToken cancellationToken) => Task.FromResult(GetConfigurations(document.Project, diagnostics)); - public Task> GetFixesAsync(Project project, IEnumerable diagnostics, CodeActionOptionsProvider fallbackOptions, CancellationToken cancellationToken) + public Task> GetFixesAsync(Project project, IEnumerable diagnostics, CancellationToken cancellationToken) => Task.FromResult(GetConfigurations(project, diagnostics)); private static ImmutableArray GetConfigurations(Project project, IEnumerable diagnostics) diff --git a/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureSeverity/ConfigureSeverityLevelCodeFixProvider.cs b/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureSeverity/ConfigureSeverityLevelCodeFixProvider.cs index 5d8e0d9cc85ee..3ae7001b1ec2d 100644 --- a/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureSeverity/ConfigureSeverityLevelCodeFixProvider.cs +++ b/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureSeverity/ConfigureSeverityLevelCodeFixProvider.cs @@ -44,10 +44,10 @@ public bool IsFixableDiagnostic(Diagnostic diagnostic) public FixAllProvider? GetFixAllProvider() => null; - public Task> GetFixesAsync(TextDocument document, TextSpan span, IEnumerable diagnostics, CodeActionOptionsProvider fallbackOptions, CancellationToken cancellationToken) + public Task> GetFixesAsync(TextDocument document, TextSpan span, IEnumerable diagnostics, CancellationToken cancellationToken) => Task.FromResult(GetConfigurations(document.Project, diagnostics, cancellationToken)); - public Task> GetFixesAsync(Project project, IEnumerable diagnostics, CodeActionOptionsProvider fallbackOptions, CancellationToken cancellationToken) + public Task> GetFixesAsync(Project project, IEnumerable diagnostics, CancellationToken cancellationToken) => Task.FromResult(GetConfigurations(project, diagnostics, cancellationToken)); private static ImmutableArray GetConfigurations(Project project, IEnumerable diagnostics, CancellationToken cancellationToken) diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.GlobalSuppressMessageCodeAction.cs b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.GlobalSuppressMessageCodeAction.cs index c9df6009c13d7..4c5dcd72e5d3f 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.GlobalSuppressMessageCodeAction.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.GlobalSuppressMessageCodeAction.cs @@ -18,13 +18,11 @@ internal abstract partial class AbstractSuppressionCodeFixProvider : IConfigurat internal sealed class GlobalSuppressMessageCodeAction( ISymbol targetSymbol, INamedTypeSymbol suppressMessageAttribute, Project project, Diagnostic diagnostic, - AbstractSuppressionCodeFixProvider fixer, - CodeActionOptionsProvider fallbackOptions) : AbstractGlobalSuppressMessageCodeAction(fixer, project) + AbstractSuppressionCodeFixProvider fixer) : AbstractGlobalSuppressMessageCodeAction(fixer, project) { private readonly ISymbol _targetSymbol = targetSymbol; private readonly INamedTypeSymbol _suppressMessageAttribute = suppressMessageAttribute; private readonly Diagnostic _diagnostic = diagnostic; - private readonly CodeActionOptionsProvider _fallbackOptions = fallbackOptions; protected override async Task GetChangedSuppressionDocumentAsync(CancellationToken cancellationToken) { @@ -32,7 +30,7 @@ protected override async Task GetChangedSuppressionDocumentAsync(Cance var services = suppressionsDoc.Project.Solution.Services; var suppressionsRoot = await suppressionsDoc.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var addImportsService = suppressionsDoc.GetRequiredLanguageService(); - var options = await suppressionsDoc.GetSyntaxFormattingOptionsAsync(_fallbackOptions, cancellationToken).ConfigureAwait(false); + var options = await suppressionsDoc.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); suppressionsRoot = Fixer.AddGlobalSuppressMessageAttribute( suppressionsRoot, _targetSymbol, _suppressMessageAttribute, _diagnostic, services, options, addImportsService, cancellationToken); diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.PragmaBatchFixHelpers.cs b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.PragmaBatchFixHelpers.cs index 81bd3002bced4..dd7aab504f8e2 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.PragmaBatchFixHelpers.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.PragmaBatchFixHelpers.cs @@ -34,7 +34,7 @@ public static CodeAction CreateBatchPragmaFix( return CodeAction.Create( ((CodeAction)pragmaActions[0]).Title, createChangedDocument: ct => - BatchPragmaFixesAsync(suppressionFixProvider, document, pragmaActions, pragmaDiagnostics, fixAllState.CodeActionOptionsProvider, cancellationToken), + BatchPragmaFixesAsync(suppressionFixProvider, document, pragmaActions, pragmaDiagnostics, cancellationToken), equivalenceKey: fixAllState.CodeActionEquivalenceKey); } @@ -43,7 +43,6 @@ private static async Task BatchPragmaFixesAsync( Document document, ImmutableArray pragmaActions, ImmutableArray diagnostics, - CodeActionOptionsProvider fallbackOptions, CancellationToken cancellationToken) { // We apply all the pragma suppression fixes sequentially. @@ -86,7 +85,7 @@ private static async Task BatchPragmaFixesAsync( properties: diagnostic.Properties, isSuppressed: diagnostic.IsSuppressed); - var newSuppressionFixes = await suppressionFixProvider.GetFixesAsync(currentDocument, currentDiagnosticSpan, [diagnostic], fallbackOptions, cancellationToken).ConfigureAwait(false); + var newSuppressionFixes = await suppressionFixProvider.GetFixesAsync(currentDocument, currentDiagnosticSpan, [diagnostic], cancellationToken).ConfigureAwait(false); var newSuppressionFix = newSuppressionFixes.SingleOrDefault(); if (newSuppressionFix != null) { diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.PragmaWarningBatchFixAllProvider.cs b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.PragmaWarningBatchFixAllProvider.cs index 78695f9c6e311..40be76361e23a 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.PragmaWarningBatchFixAllProvider.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.PragmaWarningBatchFixAllProvider.cs @@ -35,7 +35,7 @@ protected override async Task AddDocumentFixesAsync( { var span = diagnostic.Location.SourceSpan; var pragmaSuppressions = await _suppressionFixProvider.GetPragmaSuppressionsAsync( - document, span, [diagnostic], fixAllState.CodeActionOptionsProvider, cancellationToken).ConfigureAwait(false); + document, span, [diagnostic], cancellationToken).ConfigureAwait(false); var pragmaSuppression = pragmaSuppressions.SingleOrDefault(); if (pragmaSuppression != null) { diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.RemoveSuppressionCodeAction.BatchFixer.cs b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.RemoveSuppressionCodeAction.BatchFixer.cs index 84ee578089529..ec1584616a016 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.RemoveSuppressionCodeAction.BatchFixer.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.RemoveSuppressionCodeAction.BatchFixer.cs @@ -44,7 +44,7 @@ protected override async Task AddDocumentFixesAsync( { var span = diagnostic.Location.SourceSpan; var removeSuppressionFixes = await _suppressionFixProvider.GetFixesAsync( - document, span, [diagnostic], fixAllState.CodeActionOptionsProvider, cancellationToken).ConfigureAwait(false); + document, span, [diagnostic], cancellationToken).ConfigureAwait(false); var removeSuppressionFix = removeSuppressionFixes.SingleOrDefault(); if (removeSuppressionFix != null) { @@ -89,7 +89,7 @@ protected override async Task AddProjectFixesAsync( foreach (var diagnostic in diagnostics.Where(d => !d.Location.IsInSource && d.IsSuppressed)) { var removeSuppressionFixes = await _suppressionFixProvider.GetFixesAsync( - project, [diagnostic], fixAllState.CodeActionOptionsProvider, cancellationToken).ConfigureAwait(false); + project, [diagnostic], cancellationToken).ConfigureAwait(false); if (removeSuppressionFixes.SingleOrDefault()?.Action is RemoveSuppressionCodeAction removeSuppressionCodeAction) { if (fixAllState.IsFixMultiple) diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.RemoveSuppressionCodeAction.cs b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.RemoveSuppressionCodeAction.cs index 029be4d52cdea..b90e142d69b3b 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.RemoveSuppressionCodeAction.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.RemoveSuppressionCodeAction.cs @@ -27,7 +27,6 @@ public static async Task CreateAsync( Project project, Diagnostic diagnostic, AbstractSuppressionCodeFixProvider fixer, - CodeActionOptionsProvider options, CancellationToken cancellationToken) { var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); @@ -38,7 +37,7 @@ public static async Task CreateAsync( } else if (documentOpt != null && !SuppressionHelpers.IsSynthesizedExternalSourceDiagnostic(diagnostic)) { - var formattingOptions = await documentOpt.GetSyntaxFormattingOptionsAsync(options, cancellationToken).ConfigureAwait(false); + var formattingOptions = await documentOpt.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); return PragmaRemoveAction.Create(suppressionTargetInfo, documentOpt, formattingOptions, diagnostic, fixer); } else diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.cs b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.cs index 410c85da807df..7afb14e30a060 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.cs @@ -139,24 +139,24 @@ private SyntaxToken GetAdjustedTokenForPragmaRestore(SyntaxToken token, SyntaxNo } public Task> GetFixesAsync( - TextDocument textDocument, TextSpan span, IEnumerable diagnostics, CodeActionOptionsProvider fallbackOptions, CancellationToken cancellationToken) + TextDocument textDocument, TextSpan span, IEnumerable diagnostics, CancellationToken cancellationToken) { if (textDocument is not Document document) return Task.FromResult(ImmutableArray.Empty); - return GetSuppressionsAsync(document, span, diagnostics, fallbackOptions, skipSuppressMessage: false, skipUnsuppress: false, cancellationToken: cancellationToken); + return GetSuppressionsAsync(document, span, diagnostics, skipSuppressMessage: false, skipUnsuppress: false, cancellationToken: cancellationToken); } - internal async Task> GetPragmaSuppressionsAsync(Document document, TextSpan span, IEnumerable diagnostics, CodeActionOptionsProvider fallbackOptions, CancellationToken cancellationToken) + internal async Task> GetPragmaSuppressionsAsync(Document document, TextSpan span, IEnumerable diagnostics, CancellationToken cancellationToken) { - var codeFixes = await GetSuppressionsAsync(document, span, diagnostics, fallbackOptions, skipSuppressMessage: true, skipUnsuppress: true, cancellationToken: cancellationToken).ConfigureAwait(false); + var codeFixes = await GetSuppressionsAsync(document, span, diagnostics, skipSuppressMessage: true, skipUnsuppress: true, cancellationToken: cancellationToken).ConfigureAwait(false); return codeFixes.SelectMany(fix => fix.Action.NestedActions) .OfType() .ToImmutableArray(); } private async Task> GetSuppressionsAsync( - Document document, TextSpan span, IEnumerable diagnostics, CodeActionOptionsProvider fallbackOptions, bool skipSuppressMessage, bool skipUnsuppress, CancellationToken cancellationToken) + Document document, TextSpan span, IEnumerable diagnostics, bool skipSuppressMessage, bool skipUnsuppress, CancellationToken cancellationToken) { var suppressionTargetInfo = await GetSuppressionTargetInfoAsync(document, span, cancellationToken).ConfigureAwait(false); if (suppressionTargetInfo == null) @@ -165,11 +165,11 @@ private async Task> GetSuppressionsAsync( } return await GetSuppressionsAsync( - document, document.Project, diagnostics, suppressionTargetInfo, fallbackOptions, skipSuppressMessage, skipUnsuppress, cancellationToken).ConfigureAwait(false); + document, document.Project, diagnostics, suppressionTargetInfo, skipSuppressMessage, skipUnsuppress, cancellationToken).ConfigureAwait(false); } public async Task> GetFixesAsync( - Project project, IEnumerable diagnostics, CodeActionOptionsProvider fallbackOptions, CancellationToken cancellationToken) + Project project, IEnumerable diagnostics, CancellationToken cancellationToken) { if (!project.SupportsCompilation) { @@ -180,12 +180,12 @@ public async Task> GetFixesAsync( var suppressionTargetInfo = new SuppressionTargetInfo() { TargetSymbol = compilation.Assembly }; return await GetSuppressionsAsync( documentOpt: null, project, diagnostics, suppressionTargetInfo, - fallbackOptions, skipSuppressMessage: false, skipUnsuppress: false, + skipSuppressMessage: false, skipUnsuppress: false, cancellationToken).ConfigureAwait(false); } private async Task> GetSuppressionsAsync( - Document documentOpt, Project project, IEnumerable diagnostics, SuppressionTargetInfo suppressionTargetInfo, CodeActionOptionsProvider fallbackOptions, bool skipSuppressMessage, bool skipUnsuppress, CancellationToken cancellationToken) + Document documentOpt, Project project, IEnumerable diagnostics, SuppressionTargetInfo suppressionTargetInfo, bool skipSuppressMessage, bool skipUnsuppress, CancellationToken cancellationToken) { // We only care about diagnostics that can be suppressed/unsuppressed. diagnostics = diagnostics.Where(IsFixableDiagnostic); @@ -212,7 +212,7 @@ private async Task> GetSuppressionsAsync( if (diagnostic.Location.IsInSource && documentOpt != null) { // pragma warning disable. - lazyFormattingOptions ??= await documentOpt.GetSyntaxFormattingOptionsAsync(fallbackOptions, cancellationToken).ConfigureAwait(false); + lazyFormattingOptions ??= await documentOpt.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); nestedActions.Add(PragmaWarningCodeAction.Create(suppressionTargetInfo, documentOpt, lazyFormattingOptions, diagnostic, this)); } @@ -221,7 +221,7 @@ private async Task> GetSuppressionsAsync( { // global assembly-level suppress message attribute. nestedActions.Add(new GlobalSuppressMessageCodeAction( - suppressionTargetInfo.TargetSymbol, suppressMessageAttribute, project, diagnostic, this, fallbackOptions)); + suppressionTargetInfo.TargetSymbol, suppressMessageAttribute, project, diagnostic, this)); // local suppress message attribute // please note that in order to avoid issues with existing unit tests referencing the code fix @@ -242,7 +242,7 @@ private async Task> GetSuppressionsAsync( } else if (!skipUnsuppress) { - var codeAction = await RemoveSuppressionCodeAction.CreateAsync(suppressionTargetInfo, documentOpt, project, diagnostic, this, fallbackOptions, cancellationToken).ConfigureAwait(false); + var codeAction = await RemoveSuppressionCodeAction.CreateAsync(suppressionTargetInfo, documentOpt, project, diagnostic, this, cancellationToken).ConfigureAwait(false); if (codeAction != null) { result.Add(new CodeFix(project, codeAction, diagnostic)); diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/WrapperCodeFixProvider.cs b/src/Features/Core/Portable/CodeFixes/Suppression/WrapperCodeFixProvider.cs index 3c3884d3c2899..027e2a48e2782 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/WrapperCodeFixProvider.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/WrapperCodeFixProvider.cs @@ -26,14 +26,14 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) var documentDiagnostics = diagnostics.Where(d => d.Location.IsInSource).ToImmutableArray(); if (!documentDiagnostics.IsEmpty) { - var suppressionFixes = await _suppressionFixProvider.GetFixesAsync(context.Document, context.Span, documentDiagnostics, context.Options, context.CancellationToken).ConfigureAwait(false); + var suppressionFixes = await _suppressionFixProvider.GetFixesAsync(context.Document, context.Span, documentDiagnostics, context.CancellationToken).ConfigureAwait(false); RegisterSuppressionFixes(context, suppressionFixes); } var projectDiagnostics = diagnostics.Where(d => !d.Location.IsInSource).ToImmutableArray(); if (!projectDiagnostics.IsEmpty) { - var suppressionFixes = await _suppressionFixProvider.GetFixesAsync(context.Document.Project, projectDiagnostics, context.Options, context.CancellationToken).ConfigureAwait(false); + var suppressionFixes = await _suppressionFixProvider.GetFixesAsync(context.Document.Project, projectDiagnostics, context.CancellationToken).ConfigureAwait(false); RegisterSuppressionFixes(context, suppressionFixes); } } diff --git a/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.MoveTypeEditor.cs b/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.MoveTypeEditor.cs index fd88a578f05cf..7f687654b0529 100644 --- a/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.MoveTypeEditor.cs +++ b/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.MoveTypeEditor.cs @@ -76,7 +76,7 @@ private async Task RemoveUnnecessaryImportsAsync( Solution solution, DocumentId sourceDocumentId, DocumentId documentWithMovedTypeId) { var documentWithMovedType = solution.GetRequiredDocument(documentWithMovedTypeId); - var documentWithMovedTypeFormattingOptions = await documentWithMovedType.GetSyntaxFormattingOptionsAsync(State.FallbackOptions, CancellationToken).ConfigureAwait(false); + var documentWithMovedTypeFormattingOptions = await documentWithMovedType.GetSyntaxFormattingOptionsAsync(CancellationToken).ConfigureAwait(false); var syntaxFacts = documentWithMovedType.GetRequiredLanguageService(); var removeUnnecessaryImports = documentWithMovedType.GetRequiredLanguageService(); @@ -95,7 +95,7 @@ private async Task RemoveUnnecessaryImportsAsync( // Now remove any unnecessary imports from the original doc that moved to the new doc. var sourceDocument = solution.GetRequiredDocument(sourceDocumentId); - var sourceDocumentFormattingOptions = await sourceDocument.GetSyntaxFormattingOptionsAsync(State.FallbackOptions, CancellationToken).ConfigureAwait(false); + var sourceDocumentFormattingOptions = await sourceDocument.GetSyntaxFormattingOptionsAsync(CancellationToken).ConfigureAwait(false); sourceDocument = await removeUnnecessaryImports.RemoveUnnecessaryImportsAsync( sourceDocument, n => movedImports.Contains(i => syntaxFacts.AreEquivalent(i, n)), diff --git a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs index aa05c41fdfba2..f41600e751013 100644 --- a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs @@ -789,7 +789,6 @@ private static async Task RemoveUnnecessaryImportsAsync( var result = await RemoveUnnecessaryImportsWorkerAsync( doc, CreateImports(doc, names, withFormatterAnnotation: false), - fallbackOptions, cancellationToken).ConfigureAwait(false); callback((result.Id, await result.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false))); }, @@ -801,12 +800,11 @@ private static async Task RemoveUnnecessaryImportsAsync( async static Task RemoveUnnecessaryImportsWorkerAsync( Document doc, IEnumerable importsToRemove, - CodeCleanupOptionsProvider fallbackOptions, CancellationToken token) { var removeImportService = doc.GetRequiredLanguageService(); var syntaxFacts = doc.GetRequiredLanguageService(); - var formattingOptions = await doc.GetSyntaxFormattingOptionsAsync(fallbackOptions, token).ConfigureAwait(false); + var formattingOptions = await doc.GetSyntaxFormattingOptionsAsync(token).ConfigureAwait(false); return await removeImportService.RemoveUnnecessaryImportsAsync( doc, diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractMemberInsertingCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractMemberInsertingCompletionProvider.cs index 1a5dfd50d3ac5..0120aed951328 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractMemberInsertingCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractMemberInsertingCompletionProvider.cs @@ -112,7 +112,7 @@ private async Task DetermineNewDocumentAsync( var declaration = GetSyntax(newRoot.FindToken(destinationSpan.End)); document = document.WithSyntaxRoot(newRoot.ReplaceNode(declaration, declaration.WithAdditionalAnnotations(_annotation))); - var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(fallbackOptions, cancellationToken).ConfigureAwait(false); + var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); return await Formatter.FormatAsync(document, _annotation, formattingOptions, cancellationToken).ConfigureAwait(false); } diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractImportCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractImportCompletionProvider.cs index 9e34d43f12d36..81edfcc979f8a 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractImportCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractImportCompletionProvider.cs @@ -154,7 +154,7 @@ public override async Task GetChangeAsync( var fallbackOptions = globalOptions?.Provider ?? CodeActionOptions.DefaultProvider; var addImportsOptions = await document.GetAddImportPlacementOptionsAsync(fallbackOptions, cancellationToken).ConfigureAwait(false); - var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(fallbackOptions, cancellationToken).ConfigureAwait(false); + var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); var importNode = CreateImport(document, containingNamespace); diff --git a/src/Features/Core/Portable/ConvertAnonymousType/AbstractConvertAnonymousTypeToClassCodeRefactoringProvider.cs b/src/Features/Core/Portable/ConvertAnonymousType/AbstractConvertAnonymousTypeToClassCodeRefactoringProvider.cs index 138400c9c290f..64b091094e07e 100644 --- a/src/Features/Core/Portable/ConvertAnonymousType/AbstractConvertAnonymousTypeToClassCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/ConvertAnonymousType/AbstractConvertAnonymousTypeToClassCodeRefactoringProvider.cs @@ -130,7 +130,7 @@ await ReplaceMatchingAnonymousTypesAsync( autoInsertionLocation: false); var info = await document.GetCodeGenerationInfoAsync(context, fallbackOptions, cancellationToken).ConfigureAwait(false); - var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(fallbackOptions, cancellationToken).ConfigureAwait(false); + var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); // Then, actually insert the new class in the appropriate container. var container = anonymousObject.GetAncestor() ?? root; diff --git a/src/Features/Core/Portable/ConvertTupleToStruct/AbstractConvertTupleToStructCodeRefactoringProvider.cs b/src/Features/Core/Portable/ConvertTupleToStruct/AbstractConvertTupleToStructCodeRefactoringProvider.cs index 83f8c53636c82..5a5864579ecad 100644 --- a/src/Features/Core/Portable/ConvertTupleToStruct/AbstractConvertTupleToStructCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/ConvertTupleToStruct/AbstractConvertTupleToStructCodeRefactoringProvider.cs @@ -318,7 +318,7 @@ await GenerateStructIntoContainingNamespaceAsync( documentToEditorMap, fallbackOptions, cancellationToken).ConfigureAwait(false); var updatedSolution = await ApplyChangesAsync( - document, documentToEditorMap, fallbackOptions, cancellationToken).ConfigureAwait(false); + document, documentToEditorMap, cancellationToken).ConfigureAwait(false); return updatedSolution; } @@ -583,7 +583,7 @@ private static async Task GenerateStructIntoContainingNamespaceAsync( } private static async Task ApplyChangesAsync( - Document startingDocument, Dictionary documentToEditorMap, CodeCleanupOptionsProvider fallbackOptions, CancellationToken cancellationToken) + Document startingDocument, Dictionary documentToEditorMap, CancellationToken cancellationToken) { var currentSolution = startingDocument.Project.Solution; @@ -600,7 +600,7 @@ private static async Task ApplyChangesAsync( // so that our generated methods follow any special formatting rules specific to // them. var equalsAndGetHashCodeService = startingDocument.GetRequiredLanguageService(); - var formattingOptions = await updatedDocument.GetSyntaxFormattingOptionsAsync(fallbackOptions, cancellationToken).ConfigureAwait(false); + var formattingOptions = await updatedDocument.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); updatedDocument = await equalsAndGetHashCodeService.FormatDocumentAsync( updatedDocument, formattingOptions, cancellationToken).ConfigureAwait(false); diff --git a/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs b/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs index e8e0cd659dab4..9649e4abbdde2 100644 --- a/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs +++ b/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs @@ -201,7 +201,7 @@ private async Task EncapsulateFieldAsync( var markFieldPrivate = field.DeclaredAccessibility != Accessibility.Private; var rewrittenFieldDeclaration = await RewriteFieldNameAndAccessibilityAsync(finalFieldName, markFieldPrivate, document, declarationAnnotation, fallbackOptions, cancellationToken).ConfigureAwait(false); - var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(fallbackOptions, cancellationToken).ConfigureAwait(false); + var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); document = await Formatter.FormatAsync(document.WithSyntaxRoot(rewrittenFieldDeclaration), Formatter.Annotation, formattingOptions, cancellationToken).ConfigureAwait(false); diff --git a/src/Features/Core/Portable/ExtractInterface/AbstractExtractInterfaceService.cs b/src/Features/Core/Portable/ExtractInterface/AbstractExtractInterfaceService.cs index 12684fa5f487d..7db9025faab74 100644 --- a/src/Features/Core/Portable/ExtractInterface/AbstractExtractInterfaceService.cs +++ b/src/Features/Core/Portable/ExtractInterface/AbstractExtractInterfaceService.cs @@ -273,7 +273,7 @@ internal static async Task GetExtractInterfaceOpt var defaultInterfaceName = NameGenerator.GenerateUniqueName(candidateInterfaceName, name => !conflictingTypeNames.Contains(name)); var syntaxFactsService = document.GetLanguageService(); var notificationService = document.Project.Solution.Services.GetService(); - var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(fallbackOptions, cancellationToken).ConfigureAwait(false); + var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); var generatedNameTypeParameterSuffix = ExtractTypeHelpers.GetTypeParameterSuffix(document, formattingOptions, type, extractableMembers, cancellationToken); var service = document.Project.Solution.Services.GetService(); diff --git a/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeAction.cs b/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeAction.cs index 6811d69231553..89696ad7a4c93 100644 --- a/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeAction.cs +++ b/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeAction.cs @@ -73,7 +73,7 @@ protected override async Task GetChangedDocumentAsync(CancellationToke } var info = await _document.GetCodeGenerationInfoAsync(CodeGenerationContext.Default, _fallbackOptions, cancellationToken).ConfigureAwait(false); - var formattingOptions = await _document.GetSyntaxFormattingOptionsAsync(_fallbackOptions, cancellationToken).ConfigureAwait(false); + var formattingOptions = await _document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); var newTypeDeclaration = info.Service.AddMembers(_typeDeclaration, methods, info, cancellationToken); diff --git a/src/Features/Core/Portable/InitializeParameter/AbstractInitializeMemberFromParameterCodeRefactoringProviderMemberCreation.cs b/src/Features/Core/Portable/InitializeParameter/AbstractInitializeMemberFromParameterCodeRefactoringProviderMemberCreation.cs index 51b300ef0a504..6b9515fe627aa 100644 --- a/src/Features/Core/Portable/InitializeParameter/AbstractInitializeMemberFromParameterCodeRefactoringProviderMemberCreation.cs +++ b/src/Features/Core/Portable/InitializeParameter/AbstractInitializeMemberFromParameterCodeRefactoringProviderMemberCreation.cs @@ -118,7 +118,7 @@ private async Task> HandleNoExistingFieldOrPropertyAs // Offer to create new one and assign to that. using var _ = ArrayBuilder.GetInstance(out var allActions); - var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(fallbackOptions, cancellationToken).ConfigureAwait(false); + var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); var (fieldAction, propertyAction) = AddSpecificParameterInitializationActions( document, parameter, constructorDeclaration, blockStatement, rules, formattingOptions.AccessibilityModifiersRequired, fallbackOptions); diff --git a/src/Features/Core/Portable/MoveToNamespace/AbstractMoveToNamespaceService.cs b/src/Features/Core/Portable/MoveToNamespace/AbstractMoveToNamespaceService.cs index e9de1bd657dcb..37daf96bef5d4 100644 --- a/src/Features/Core/Portable/MoveToNamespace/AbstractMoveToNamespaceService.cs +++ b/src/Features/Core/Portable/MoveToNamespace/AbstractMoveToNamespaceService.cs @@ -262,7 +262,7 @@ private static async Task MoveTypeToNamespaceAsync( // Since MoveTypeService doesn't handle linked files, we need to merge the diff ourselves, // otherwise, we will end up with multiple linked documents with different content. - var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(fallbackOptions, cancellationToken).ConfigureAwait(false); + var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); var mergedSolution = await PropagateChangeToLinkedDocumentsAsync(modifiedDocument, formattingOptions, cancellationToken).ConfigureAwait(false); var mergedDocument = mergedSolution.GetDocument(document.Id); diff --git a/src/Features/Core/Portable/Snippets/SnippetProviders/AbstractSnippetProvider.cs b/src/Features/Core/Portable/Snippets/SnippetProviders/AbstractSnippetProvider.cs index 081bff40383e7..fa87099bef9d6 100644 --- a/src/Features/Core/Portable/Snippets/SnippetProviders/AbstractSnippetProvider.cs +++ b/src/Features/Core/Portable/Snippets/SnippetProviders/AbstractSnippetProvider.cs @@ -152,7 +152,7 @@ private async Task CleanupDocumentAsync( { var addImportPlacementOptions = await document.GetAddImportPlacementOptionsAsync(fallbackOptions: null, cancellationToken).ConfigureAwait(false); var simplifierOptions = await document.GetSimplifierOptionsAsync(cancellationToken).ConfigureAwait(false); - var syntaxFormattingOptions = await document.GetSyntaxFormattingOptionsAsync(fallbackOptions: null, cancellationToken).ConfigureAwait(false); + var syntaxFormattingOptions = await document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); document = await ImportAdder.AddImportsFromSymbolAnnotationAsync( document, FindSnippetAnnotation, addImportPlacementOptions, cancellationToken: cancellationToken).ConfigureAwait(false); diff --git a/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs b/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs index fdc3680c652ce..a6a50f11e9981 100644 --- a/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs +++ b/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs @@ -211,7 +211,7 @@ private async Task ProcessResultAsync(CodeFixContext context, Diagnost editor.RemoveNode(nodeToRemove, syntaxRemoveOptions); var newRoot = editor.GetChangedRoot(); - newRoot = await FormatAsync(newRoot, fieldDocument, context.Options, cancellationToken).ConfigureAwait(false); + newRoot = await FormatAsync(newRoot, fieldDocument, cancellationToken).ConfigureAwait(false); return solution.WithDocumentSyntaxRoot(fieldDocument.Id, newRoot); } @@ -225,8 +225,8 @@ private async Task ProcessResultAsync(CodeFixContext context, Diagnost Contract.ThrowIfNull(newFieldTreeRoot); var newPropertyTreeRoot = propertyTreeRoot.ReplaceNode(property, updatedProperty); - newFieldTreeRoot = await FormatAsync(newFieldTreeRoot, fieldDocument, context.Options, cancellationToken).ConfigureAwait(false); - newPropertyTreeRoot = await FormatAsync(newPropertyTreeRoot, propertyDocument, context.Options, cancellationToken).ConfigureAwait(false); + newFieldTreeRoot = await FormatAsync(newFieldTreeRoot, fieldDocument, cancellationToken).ConfigureAwait(false); + newPropertyTreeRoot = await FormatAsync(newPropertyTreeRoot, propertyDocument, cancellationToken).ConfigureAwait(false); var updatedSolution = solution.WithDocumentSyntaxRoot(fieldDocument.Id, newFieldTreeRoot); updatedSolution = updatedSolution.WithDocumentSyntaxRoot(propertyDocument.Id, newPropertyTreeRoot); @@ -277,13 +277,13 @@ private static bool CanEditDocument( return canEditDocument; } - private async Task FormatAsync(SyntaxNode newRoot, Document document, CodeCleanupOptionsProvider fallbackOptions, CancellationToken cancellationToken) + private async Task FormatAsync(SyntaxNode newRoot, Document document, CancellationToken cancellationToken) { var formattingRules = GetFormattingRules(document); if (formattingRules.IsDefault) return newRoot; - var options = await document.GetSyntaxFormattingOptionsAsync(fallbackOptions, cancellationToken).ConfigureAwait(false); + var options = await document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); return Formatter.Format(newRoot, SpecializedFormattingAnnotation, document.Project.Solution.Services, options, formattingRules, cancellationToken); } diff --git a/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionAllCodeTests.cs b/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionAllCodeTests.cs index ed3217dc8a698..a2d716ae40716 100644 --- a/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionAllCodeTests.cs +++ b/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionAllCodeTests.cs @@ -86,7 +86,7 @@ protected async Task TestPragmaOrAttributeAsync( continue; } - var fixes = fixer.GetFixesAsync(document, diagnostic.Location.SourceSpan, [diagnostic], CodeActionOptions.DefaultProvider, CancellationToken.None).GetAwaiter().GetResult(); + var fixes = fixer.GetFixesAsync(document, diagnostic.Location.SourceSpan, [diagnostic], CancellationToken.None).GetAwaiter().GetResult(); if (fixes == null || fixes.Count() <= 0) { continue; diff --git a/src/Features/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentOptionsWrapper.cs b/src/Features/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentOptionsWrapper.cs index 46da88e2699b7..e97f58dfcd72a 100644 --- a/src/Features/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentOptionsWrapper.cs +++ b/src/Features/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentOptionsWrapper.cs @@ -40,7 +40,7 @@ public static async ValueTask FromD bool autoXmlDocCommentGeneration, CancellationToken cancellationToken) { - var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(CodeActionOptions.DefaultProvider, cancellationToken).ConfigureAwait(false); + var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); return new(new() { LineFormatting = formattingOptions.LineFormatting, AutoXmlDocCommentGeneration = autoXmlDocCommentGeneration }); } diff --git a/src/Features/ExternalAccess/OmniSharp/Formatting/OmniSharpSyntaxFormattingOptionsWrapper.cs b/src/Features/ExternalAccess/OmniSharp/Formatting/OmniSharpSyntaxFormattingOptionsWrapper.cs index 3120877ca0262..2998a2a2bb754 100644 --- a/src/Features/ExternalAccess/OmniSharp/Formatting/OmniSharpSyntaxFormattingOptionsWrapper.cs +++ b/src/Features/ExternalAccess/OmniSharp/Formatting/OmniSharpSyntaxFormattingOptionsWrapper.cs @@ -34,7 +34,7 @@ public static async ValueTask FromDocum .Add(FormattingOptions2.NewLine.Definition.ConfigName, FormattingOptions2.NewLine.Definition.Serializer.Serialize(fallbackLineFormattingOptions.NewLine))))); return new OmniSharpSyntaxFormattingOptionsWrapper( - optionsWithFallback.GetSyntaxFormattingOptions(document.Project.Services, fallbackOptions: null)); + optionsWithFallback.GetSyntaxFormattingOptions(document.Project.Services)); } } } diff --git a/src/Features/VisualBasic/Portable/CodeFixes/OverloadBase/OverloadBaseCodeFixProvider.AddKeywordAction.vb b/src/Features/VisualBasic/Portable/CodeFixes/OverloadBase/OverloadBaseCodeFixProvider.AddKeywordAction.vb index 7c00a8abc3ddc..7291ecdb0064c 100644 --- a/src/Features/VisualBasic/Portable/CodeFixes/OverloadBase/OverloadBaseCodeFixProvider.AddKeywordAction.vb +++ b/src/Features/VisualBasic/Portable/CodeFixes/OverloadBase/OverloadBaseCodeFixProvider.AddKeywordAction.vb @@ -20,7 +20,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.OverloadBase Private ReadOnly _node As SyntaxNode Private ReadOnly _title As String Private ReadOnly _modifier As SyntaxKind - Private ReadOnly _fallbackOptions As SyntaxFormattingOptionsProvider Public Overrides ReadOnly Property Title As String Get @@ -34,17 +33,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.OverloadBase End Get End Property - Public Sub New(document As Document, node As SyntaxNode, title As String, modifier As SyntaxKind, fallbackOptions As SyntaxFormattingOptionsProvider) + Public Sub New(document As Document, node As SyntaxNode, title As String, modifier As SyntaxKind) _document = document _node = node _title = title _modifier = modifier - _fallbackOptions = fallbackOptions End Sub Protected Overrides Async Function GetChangedDocumentAsync(cancellationToken As CancellationToken) As Task(Of Document) Dim root = Await _document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(False) - Dim options = Await _document.GetSyntaxFormattingOptionsAsync(_fallbackOptions, cancellationToken).ConfigureAwait(False) + Dim options = Await _document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(False) Dim newNode = Await GetNewNodeAsync(_document, _node, options, cancellationToken).ConfigureAwait(False) Dim newRoot = root.ReplaceNode(_node, newNode) diff --git a/src/Features/VisualBasic/Portable/CodeFixes/OverloadBase/OverloadBaseCodeFixProvider.vb b/src/Features/VisualBasic/Portable/CodeFixes/OverloadBase/OverloadBaseCodeFixProvider.vb index 99cdd9589a84b..124357f4926d8 100644 --- a/src/Features/VisualBasic/Portable/CodeFixes/OverloadBase/OverloadBaseCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/CodeFixes/OverloadBase/OverloadBaseCodeFixProvider.vb @@ -46,9 +46,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.OverloadBase End If If diagnostic.Descriptor.Id = BC40003 Then - context.RegisterCodeFix(New AddKeywordAction(context.Document, token.Parent, VBFeaturesResources.Add_Overloads, SyntaxKind.OverloadsKeyword, context.Options), context.Diagnostics) + context.RegisterCodeFix(New AddKeywordAction(context.Document, token.Parent, VBFeaturesResources.Add_Overloads, SyntaxKind.OverloadsKeyword), context.Diagnostics) ElseIf diagnostic.Descriptor.Id = BC40004 Then - context.RegisterCodeFix(New AddKeywordAction(context.Document, token.Parent, VBFeaturesResources.Add_Shadows, SyntaxKind.ShadowsKeyword, context.Options), context.Diagnostics) + context.RegisterCodeFix(New AddKeywordAction(context.Document, token.Parent, VBFeaturesResources.Add_Shadows, SyntaxKind.ShadowsKeyword), context.Diagnostics) End If End Function End Class diff --git a/src/LanguageServer/Protocol/Extensions/ProtocolConversions.cs b/src/LanguageServer/Protocol/Extensions/ProtocolConversions.cs index 2f00c118bca7e..8a160dc30d3fc 100644 --- a/src/LanguageServer/Protocol/Extensions/ProtocolConversions.cs +++ b/src/LanguageServer/Protocol/Extensions/ProtocolConversions.cs @@ -849,10 +849,9 @@ public static LSP.VSProjectContext ProjectToProjectContext(Project project) public static async Task GetFormattingOptionsAsync( LSP.FormattingOptions? options, Document document, - IGlobalOptionService globalOptions, CancellationToken cancellationToken) { - var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(globalOptions, cancellationToken).ConfigureAwait(false); + var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); if (options != null) { diff --git a/src/LanguageServer/Protocol/ExternalAccess/Razor/FormatNewFileHandler.cs b/src/LanguageServer/Protocol/ExternalAccess/Razor/FormatNewFileHandler.cs index 5c40e875f7be4..6dfb370319944 100644 --- a/src/LanguageServer/Protocol/ExternalAccess/Razor/FormatNewFileHandler.cs +++ b/src/LanguageServer/Protocol/ExternalAccess/Razor/FormatNewFileHandler.cs @@ -69,7 +69,7 @@ public FormatNewFileHandler(IGlobalOptionService globalOptions) } // Unlike normal new file formatting, Razor also wants to remove unnecessary usings - var syntaxFormattingOptions = await document.GetSyntaxFormattingOptionsAsync(_globalOptions, cancellationToken).ConfigureAwait(false); + var syntaxFormattingOptions = await document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); var removeImportsService = document.GetLanguageService(); if (removeImportsService is not null) { diff --git a/src/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs b/src/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs index 3b6da1a9e2f29..ae71e08efe42f 100644 --- a/src/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs +++ b/src/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs @@ -89,7 +89,7 @@ public async Task CleanupAsync( if (enabledDiagnostics.FormatDocument) { - var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(fallbackOptions, cancellationToken).ConfigureAwait(false); + var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); progressTracker.Report(CodeAnalysisProgress.Description(FeaturesResources.Formatting_document)); using (Logger.LogBlock(FunctionId.CodeCleanup_Format, cancellationToken)) @@ -116,7 +116,7 @@ private static async Task RemoveSortUsingsAsync( { using (Logger.LogBlock(FunctionId.CodeCleanup_RemoveUnusedImports, cancellationToken)) { - var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(fallbackOptions, cancellationToken).ConfigureAwait(false); + var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); document = await removeUsingsService.RemoveUnnecessaryImportsAsync(document, formattingOptions, cancellationToken).ConfigureAwait(false); } } diff --git a/src/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs b/src/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs index 5a47aae4b19a3..7ad0124d5fb6b 100644 --- a/src/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs +++ b/src/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs @@ -725,7 +725,7 @@ private async IAsyncEnumerable StreamConfigurationFixesAsync( hasFix: d => provider.IsFixableDiagnostic(d), getFixes: async dxs => { - var fixes = await provider.GetFixesAsync(document, diagnosticsSpan, dxs, fallbackOptions, cancellationToken).ConfigureAwait(false); + var fixes = await provider.GetFixesAsync(document, diagnosticsSpan, dxs, cancellationToken).ConfigureAwait(false); return fixes.WhereAsArray(f => registeredConfigurationFixTitles.Add(f.Action.Title)); }, fallbackOptions, diff --git a/src/LanguageServer/Protocol/Features/Options/IndentationOptionsStorage.cs b/src/LanguageServer/Protocol/Features/Options/IndentationOptionsStorage.cs index 9fd8f38c93dcc..5c18ca87fe454 100644 --- a/src/LanguageServer/Protocol/Features/Options/IndentationOptionsStorage.cs +++ b/src/LanguageServer/Protocol/Features/Options/IndentationOptionsStorage.cs @@ -13,7 +13,7 @@ internal static class IndentationOptionsStorage { public static async Task GetIndentationOptionsAsync(this Document document, IGlobalOptionService globalOptions, CancellationToken cancellationToken) { - var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(globalOptions, cancellationToken).ConfigureAwait(false); + var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); return new IndentationOptions(formattingOptions) { diff --git a/src/LanguageServer/Protocol/Features/Options/SyntaxFormattingOptionsStorage.cs b/src/LanguageServer/Protocol/Features/Options/SyntaxFormattingOptionsStorage.cs deleted file mode 100644 index 58c8c0aac714e..0000000000000 --- a/src/LanguageServer/Protocol/Features/Options/SyntaxFormattingOptionsStorage.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Options; - -namespace Microsoft.CodeAnalysis.Formatting; - -internal static class SyntaxFormattingOptionsStorage -{ - public static ValueTask GetSyntaxFormattingOptionsAsync(this Document document, IGlobalOptionService globalOptions, CancellationToken cancellationToken) - => document.GetSyntaxFormattingOptionsAsync(globalOptions.GetSyntaxFormattingOptions(document.Project.Services), cancellationToken); - - public static SyntaxFormattingOptions GetSyntaxFormattingOptions(this IGlobalOptionService globalOptions, LanguageServices languageServices) - => globalOptions.GetSyntaxFormattingOptions(languageServices, fallbackOptions: null); -} - diff --git a/src/LanguageServer/Protocol/Handler/Formatting/AbstractFormatDocumentHandlerBase.cs b/src/LanguageServer/Protocol/Handler/Formatting/AbstractFormatDocumentHandlerBase.cs index c9b1712f9d66a..45f13c67d6d03 100644 --- a/src/LanguageServer/Protocol/Handler/Formatting/AbstractFormatDocumentHandlerBase.cs +++ b/src/LanguageServer/Protocol/Handler/Formatting/AbstractFormatDocumentHandlerBase.cs @@ -23,7 +23,6 @@ internal abstract class AbstractFormatDocumentHandlerBase GetTextEditsAsync( RequestContext context, LSP.FormattingOptions options, - IGlobalOptionService globalOptions, CancellationToken cancellationToken, LSP.Range? range = null) { @@ -38,7 +37,7 @@ internal abstract class AbstractFormatDocumentHandlerBase + [method: ImportingConstructor] + [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + internal sealed class FormatDocumentHandler() : AbstractFormatDocumentHandlerBase { - private readonly IGlobalOptionService _globalOptions; - - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public FormatDocumentHandler(IGlobalOptionService globalOptions) - { - _globalOptions = globalOptions; - } - public override LSP.TextDocumentIdentifier GetTextDocumentIdentifier(LSP.DocumentFormattingParams request) => request.TextDocument; public override Task HandleRequestAsync( LSP.DocumentFormattingParams request, RequestContext context, CancellationToken cancellationToken) - => GetTextEditsAsync(context, request.Options, _globalOptions, cancellationToken); + => GetTextEditsAsync(context, request.Options, cancellationToken); } } diff --git a/src/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs b/src/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs index a3762bb7fe1ad..f15993645ca9c 100644 --- a/src/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs +++ b/src/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs @@ -60,7 +60,7 @@ public FormatDocumentOnTypeHandler(IGlobalOptionService globalOptions) } // We should use the options passed in by LSP instead of the document's options. - var formattingOptions = await ProtocolConversions.GetFormattingOptionsAsync(request.Options, document, _globalOptions, cancellationToken).ConfigureAwait(false); + var formattingOptions = await ProtocolConversions.GetFormattingOptionsAsync(request.Options, document, cancellationToken).ConfigureAwait(false); var indentationOptions = new IndentationOptions(formattingOptions) { AutoFormattingOptions = _globalOptions.GetAutoFormattingOptions(document.Project.Language) diff --git a/src/LanguageServer/Protocol/Handler/Formatting/FormatDocumentRangeHandler.cs b/src/LanguageServer/Protocol/Handler/Formatting/FormatDocumentRangeHandler.cs index dce2297839510..6bb7432fde53f 100644 --- a/src/LanguageServer/Protocol/Handler/Formatting/FormatDocumentRangeHandler.cs +++ b/src/LanguageServer/Protocol/Handler/Formatting/FormatDocumentRangeHandler.cs @@ -14,23 +14,16 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { [ExportCSharpVisualBasicStatelessLspService(typeof(FormatDocumentRangeHandler)), Shared] [Method(Methods.TextDocumentRangeFormattingName)] - internal sealed class FormatDocumentRangeHandler : AbstractFormatDocumentHandlerBase + [method: ImportingConstructor] + [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + internal sealed class FormatDocumentRangeHandler() : AbstractFormatDocumentHandlerBase { - private readonly IGlobalOptionService _globalOptions; - - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public FormatDocumentRangeHandler(IGlobalOptionService globalOptions) - { - _globalOptions = globalOptions; - } - public override TextDocumentIdentifier GetTextDocumentIdentifier(DocumentRangeFormattingParams request) => request.TextDocument; public override Task HandleRequestAsync( DocumentRangeFormattingParams request, RequestContext context, CancellationToken cancellationToken) - => GetTextEditsAsync(context, request.Options, _globalOptions, cancellationToken, range: request.Range); + => GetTextEditsAsync(context, request.Options, cancellationToken, range: request.Range); } } diff --git a/src/LanguageServer/Protocol/Handler/InlineCompletions/InlineCompletionsHandler.cs b/src/LanguageServer/Protocol/Handler/InlineCompletions/InlineCompletionsHandler.cs index 3704fdb11da52..4c10603b4f061 100644 --- a/src/LanguageServer/Protocol/Handler/InlineCompletions/InlineCompletionsHandler.cs +++ b/src/LanguageServer/Protocol/Handler/InlineCompletions/InlineCompletionsHandler.cs @@ -137,7 +137,7 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(VSInternalInlineCompleti } // Use the formatting options specified by the client to format the snippet. - var formattingOptions = await ProtocolConversions.GetFormattingOptionsAsync(request.Options, document, _globalOptions, cancellationToken).ConfigureAwait(false); + var formattingOptions = await ProtocolConversions.GetFormattingOptionsAsync(request.Options, document, cancellationToken).ConfigureAwait(false); var simplifierOptions = await document.GetSimplifierOptionsAsync(cancellationToken).ConfigureAwait(false); var formattedLspSnippet = await GetFormattedLspSnippetAsync(parsedSnippet, wordOnLeft.Value, document, sourceText, formattingOptions, simplifierOptions, cancellationToken).ConfigureAwait(false); diff --git a/src/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs b/src/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs index b2d1e13496e6f..0e8cfb9fe6f41 100644 --- a/src/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs +++ b/src/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs @@ -69,7 +69,7 @@ internal sealed class OnAutoInsertHandler( var service = document.GetRequiredLanguageService(); // We should use the options passed in by LSP instead of the document's options. - var formattingOptions = await ProtocolConversions.GetFormattingOptionsAsync(lspFormattingOptions, document, globalOptions, cancellationToken).ConfigureAwait(false); + var formattingOptions = await ProtocolConversions.GetFormattingOptionsAsync(lspFormattingOptions, document, cancellationToken).ConfigureAwait(false); // The editor calls this handler for C# and VB comment characters, but we only need to process the one for the language that matches the document if (character == "\n" || character == service.DocumentationCommentCharacter) @@ -239,10 +239,11 @@ static string GetTextChangeTextWithCaretAtLocation(SourceText sourceText, TextCh private static async Task<(IBraceCompletionService Service, BraceCompletionContext Context)?> GetBraceCompletionContextAsync(ImmutableArray servicesForDocument, int caretLocation, Document document, CancellationToken cancellationToken) { var parsedDocument = await ParsedDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false); + var fallbackOptions = document.Project.GetFallbackAnalyzerOptions(); foreach (var service in servicesForDocument) { - var context = service.GetCompletedBraceContext(parsedDocument, caretLocation); + var context = service.GetCompletedBraceContext(parsedDocument, fallbackOptions, caretLocation); if (context != null) { return (service, context.Value); diff --git a/src/VisualStudio/Core/Def/ExtractClass/VisualStudioExtractClassOptionsService.cs b/src/VisualStudio/Core/Def/ExtractClass/VisualStudioExtractClassOptionsService.cs index eb53373a3fd69..74e48a82e22d4 100644 --- a/src/VisualStudio/Core/Def/ExtractClass/VisualStudioExtractClassOptionsService.cs +++ b/src/VisualStudio/Core/Def/ExtractClass/VisualStudioExtractClassOptionsService.cs @@ -33,20 +33,17 @@ internal class VisualStudioExtractClassOptionsService : IExtractClassOptionsServ private readonly IThreadingContext _threadingContext; private readonly IGlyphService _glyphService; private readonly IUIThreadOperationExecutor _uiThreadOperationExecutor; - private readonly IGlobalOptionService _globalOptions; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public VisualStudioExtractClassOptionsService( IThreadingContext threadingContext, IGlyphService glyphService, - IUIThreadOperationExecutor uiThreadOperationExecutor, - IGlobalOptionService globalOptions) + IUIThreadOperationExecutor uiThreadOperationExecutor) { _threadingContext = threadingContext; _glyphService = glyphService; _uiThreadOperationExecutor = uiThreadOperationExecutor; - _globalOptions = globalOptions; } public async Task GetExtractClassOptionsAsync(Document document, INamedTypeSymbol selectedType, ImmutableArray selectedMembers, CancellationToken cancellationToken) @@ -77,7 +74,7 @@ public VisualStudioExtractClassOptionsService( ? string.Empty : selectedType.ContainingNamespace.ToDisplayString(); - var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(_globalOptions, cancellationToken).ConfigureAwait(false); + var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); var generatedNameTypeParameterSuffix = ExtractTypeHelpers.GetTypeParameterSuffix(document, formattingOptions, selectedType, membersInType, cancellationToken); var viewModel = new ExtractClassViewModel( diff --git a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs index 186c4f4c72ec8..4f3ce55b5f064 100644 --- a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs +++ b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs @@ -56,7 +56,7 @@ private int FormatWorker(IVsTextLayer textLayer, TextSpan[] selections, Cancella var documentSyntax = ParsedDocument.CreateSynchronously(document, cancellationToken); var text = documentSyntax.Text; var root = documentSyntax.Root; - var formattingOptions = textBuffer.GetSyntaxFormattingOptions(EditorOptionsService, document.Project.Services, explicitFormat: true); + var formattingOptions = textBuffer.GetSyntaxFormattingOptions(EditorOptionsService, document.Project.GetFallbackAnalyzerOptions(), document.Project.Services, explicitFormat: true); var ts = selections.Single(); var start = text.Lines[ts.iStartLine].Start + ts.iStartIndex; diff --git a/src/VisualStudio/Core/Def/Snippets/SnippetExpansionClient.cs b/src/VisualStudio/Core/Def/Snippets/SnippetExpansionClient.cs index 033fc59795f93..c8af6b722519b 100644 --- a/src/VisualStudio/Core/Def/Snippets/SnippetExpansionClient.cs +++ b/src/VisualStudio/Core/Def/Snippets/SnippetExpansionClient.cs @@ -1062,8 +1062,9 @@ private void AddReferencesAndImports( } var languageServices = documentWithImports.Project.Services; - var addImportOptions = SubjectBuffer.GetAddImportPlacementOptions(EditorOptionsService, languageServices, documentWithImports.AllowImportsInHiddenRegions()); - var formattingOptions = SubjectBuffer.GetSyntaxFormattingOptions(EditorOptionsService, languageServices, explicitFormat: false); + var fallbackOptions = documentWithImports.Project.GetFallbackAnalyzerOptions(); + var addImportOptions = SubjectBuffer.GetAddImportPlacementOptions(EditorOptionsService, fallbackOptions, languageServices, documentWithImports.AllowImportsInHiddenRegions()); + var formattingOptions = SubjectBuffer.GetSyntaxFormattingOptions(EditorOptionsService, fallbackOptions, languageServices, explicitFormat: false); documentWithImports = _languageHelper.AddImports(documentWithImports, addImportOptions, formattingOptions, position, snippetNode, cancellationToken); AddReferences(documentWithImports.Project, snippetNode); diff --git a/src/VisualStudio/Core/Def/Venus/ContainedDocument.cs b/src/VisualStudio/Core/Def/Venus/ContainedDocument.cs index ae479cb51a38d..494ec8edaad02 100644 --- a/src/VisualStudio/Core/Def/Venus/ContainedDocument.cs +++ b/src/VisualStudio/Core/Def/Venus/ContainedDocument.cs @@ -770,7 +770,7 @@ private void AdjustIndentation(IProjectionBuffer subjectBuffer, IEnumerable Debug.Assert(ReferenceEquals(parsedDocument.Text, subjectBuffer.CurrentSnapshot.AsText())); var editorOptionsService = _componentModel.GetService(); - var formattingOptions = subjectBuffer.GetSyntaxFormattingOptions(editorOptionsService, document.Project.Services, explicitFormat: false); + var formattingOptions = subjectBuffer.GetSyntaxFormattingOptions(editorOptionsService, document.Project.GetFallbackAnalyzerOptions(), document.Project.Services, explicitFormat: false); using var pooledObject = SharedPools.Default>().GetPooledObject(); diff --git a/src/VisualStudio/Core/Impl/CodeModel/AbstractCodeModelService.cs b/src/VisualStudio/Core/Impl/CodeModel/AbstractCodeModelService.cs index 5a3744b54f520..0f8b0e399d1c6 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/AbstractCodeModelService.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/AbstractCodeModelService.cs @@ -1042,7 +1042,7 @@ private Document FormatAnnotatedNode(Document document, SyntaxAnnotation annotat return _threadingContext.JoinableTaskFactory.Run(async () => { - var options = await document.GetSyntaxFormattingOptionsAsync(_editorOptionsService.GlobalOptions, cancellationToken).ConfigureAwait(false); + var options = await document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); return await Formatter.FormatAsync( document, diff --git a/src/VisualStudio/Core/Impl/CodeModel/FileCodeModel.cs b/src/VisualStudio/Core/Impl/CodeModel/FileCodeModel.cs index 62359b046a62e..6837a3f4123b7 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/FileCodeModel.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/FileCodeModel.cs @@ -347,7 +347,7 @@ internal void PerformEdit(Func action) var formatted = State.ThreadingContext.JoinableTaskFactory.Run(async () => { - var formattingOptions = await result.GetSyntaxFormattingOptionsAsync(GlobalOptions, CancellationToken.None).ConfigureAwait(false); + var formattingOptions = await result.GetSyntaxFormattingOptionsAsync(CancellationToken.None).ConfigureAwait(false); var formatted = await Formatter.FormatAsync(result, Formatter.Annotation, formattingOptions, CancellationToken.None).ConfigureAwait(true); formatted = await Formatter.FormatAsync(formatted, SyntaxAnnotation.ElasticAnnotation, formattingOptions, CancellationToken.None).ConfigureAwait(true); diff --git a/src/VisualStudio/Core/Impl/Options/AbstractOptionPreviewViewModel.cs b/src/VisualStudio/Core/Impl/Options/AbstractOptionPreviewViewModel.cs index 3ec296405e07d..71416ef4a42cb 100644 --- a/src/VisualStudio/Core/Impl/Options/AbstractOptionPreviewViewModel.cs +++ b/src/VisualStudio/Core/Impl/Options/AbstractOptionPreviewViewModel.cs @@ -137,7 +137,7 @@ public void UpdatePreview(string text) var document = project.AddDocument("document", SourceText.From(text, Encoding.UTF8)); var fallbackFormattingOptions = OptionStore.GlobalOptions.GetSyntaxFormattingOptions(document.Project.Services); var formattingService = document.GetRequiredLanguageService(); - var formattingOptions = formattingService.GetFormattingOptions(OptionStore, fallbackFormattingOptions); + var formattingOptions = formattingService.GetFormattingOptions(OptionStore); var formatted = Formatter.FormatAsync(document, formattingOptions, CancellationToken.None).WaitAndGetResult(CancellationToken.None); var textBuffer = _textBufferCloneService.Clone(formatted.GetTextSynchronously(CancellationToken.None), _contentType); diff --git a/src/Workspaces/Core/Portable/CodeFixes/Supression/IConfigurationFixProvider.cs b/src/Workspaces/Core/Portable/CodeFixes/Supression/IConfigurationFixProvider.cs index 2fd5dfb0a34d1..1968fc91db5af 100644 --- a/src/Workspaces/Core/Portable/CodeFixes/Supression/IConfigurationFixProvider.cs +++ b/src/Workspaces/Core/Portable/CodeFixes/Supression/IConfigurationFixProvider.cs @@ -25,13 +25,13 @@ internal interface IConfigurationFixProvider /// Gets one or more add suppression, remove suppression, or configuration fixes for the specified diagnostics represented as a list of 's. /// /// A list of zero or more potential 'es. It is also safe to return null if there are none. - Task> GetFixesAsync(TextDocument document, TextSpan span, IEnumerable diagnostics, CodeActionOptionsProvider fallbackOptions, CancellationToken cancellationToken); + Task> GetFixesAsync(TextDocument document, TextSpan span, IEnumerable diagnostics, CancellationToken cancellationToken); /// /// Gets one or more add suppression, remove suppression, or configuration fixes for the specified no-location diagnostics represented as a list of 's. /// /// A list of zero or more potential 'es. It is also safe to return null if there are none. - Task> GetFixesAsync(Project project, IEnumerable diagnostics, CodeActionOptionsProvider fallbackOptions, CancellationToken cancellationToken); + Task> GetFixesAsync(Project project, IEnumerable diagnostics, CancellationToken cancellationToken); /// /// Gets an optional that can fix all/multiple occurrences of diagnostics fixed by this fix provider. diff --git a/src/Workspaces/Core/Portable/Formatting/Formatter.cs b/src/Workspaces/Core/Portable/Formatting/Formatter.cs index 9810d95a87592..8741477c480fe 100644 --- a/src/Workspaces/Core/Portable/Formatting/Formatter.cs +++ b/src/Workspaces/Core/Portable/Formatting/Formatter.cs @@ -90,7 +90,7 @@ public static async Task FormatAsync(Document document, IEnumerable FormatAsync(Document document, IEnumerable? spans, SyntaxFormattingOptions? options, ImmutableArray rules, CancellationToken cancellationToken) { - options ??= await document.GetSyntaxFormattingOptionsAsync(fallbackOptions: null, cancellationToken).ConfigureAwait(false); + options ??= await document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var services = document.Project.Solution.Services; return document.WithSyntaxRoot(Format(root, spans, services, options, rules, cancellationToken)); @@ -318,7 +318,7 @@ internal static IList GetFormattedTextChanges(SyntaxNode node, IEnum internal static SyntaxFormattingOptions GetFormattingOptions(Workspace workspace, OptionSet? optionSet, string language) { var syntaxFormattingService = workspace.Services.GetRequiredLanguageService(language); - return syntaxFormattingService.GetFormattingOptions(optionSet ?? workspace.CurrentSolution.Options, fallbackOptions: null); + return syntaxFormattingService.GetFormattingOptions(optionSet ?? workspace.CurrentSolution.Options); } #pragma warning disable RS0030 // Do not used banned APIs (backwards compatibility) @@ -332,7 +332,7 @@ internal static SyntaxFormattingOptions GetFormattingOptions(Workspace workspace var syntaxFormattingService = document.GetLanguageService(); if (syntaxFormattingService != null) { - syntaxFormattingOptions = syntaxFormattingService.GetFormattingOptions(optionSet, fallbackOptions: null); + syntaxFormattingOptions = syntaxFormattingService.GetFormattingOptions(optionSet); lineFormattingOptions = syntaxFormattingOptions.LineFormatting; } else diff --git a/src/Workspaces/Core/Portable/Options/LegacyGlobalCodeActionOptionsWorkspaceService.cs b/src/Workspaces/Core/Portable/Options/LegacyGlobalCodeActionOptionsWorkspaceService.cs index ff2967895982c..586ca3474750a 100644 --- a/src/Workspaces/Core/Portable/Options/LegacyGlobalCodeActionOptionsWorkspaceService.cs +++ b/src/Workspaces/Core/Portable/Options/LegacyGlobalCodeActionOptionsWorkspaceService.cs @@ -38,7 +38,7 @@ ValueTask OptionsProvider. => ValueTaskFactory.FromResult(options.GetDocumentFormattingOptions(fallbackOptions: null)); ValueTask OptionsProvider.GetOptionsAsync(LanguageServices languageServices, CancellationToken cancellationToken) - => ValueTaskFactory.FromResult(options.GetSyntaxFormattingOptions(languageServices, fallbackOptions: null)); + => ValueTaskFactory.FromResult(options.GetSyntaxFormattingOptions(languageServices)); ValueTask OptionsProvider.GetOptionsAsync(LanguageServices languageServices, CancellationToken cancellationToken) => ValueTaskFactory.FromResult(options.GetSimplifierOptions(languageServices)); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Project.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Project.cs index d5775d1b4f502..cb6832d8646bc 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Project.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Project.cs @@ -802,6 +802,12 @@ private void CheckIdsContainedInProject(ImmutableArray documentIds) internal AnalyzerConfigData? GetAnalyzerConfigOptions() => _projectState.GetAnalyzerConfigOptions(); + /// + /// Retrieves fallback analyzer options for this project's language. + /// + internal StructuredAnalyzerConfigOptions GetFallbackAnalyzerOptions() + => _solution.FallbackAnalyzerOptions.GetValueOrDefault(Language, StructuredAnalyzerConfigOptions.Empty); + private string GetDebuggerDisplay() => this.Name; diff --git a/src/Workspaces/CoreTestUtilities/Formatting/FormattingTestBase.cs b/src/Workspaces/CoreTestUtilities/Formatting/FormattingTestBase.cs index 53491c70ed86d..fc2f16633463e 100644 --- a/src/Workspaces/CoreTestUtilities/Formatting/FormattingTestBase.cs +++ b/src/Workspaces/CoreTestUtilities/Formatting/FormattingTestBase.cs @@ -54,7 +54,7 @@ private protected async Task AssertFormatAsync( var formattingService = document.GetRequiredLanguageService(); var formattingOptions = changedOptions != null - ? formattingService.GetFormattingOptions(changedOptions, fallbackOptions: null) + ? formattingService.GetFormattingOptions(changedOptions) : formattingService.DefaultOptions; var syntaxTree = await document.GetRequiredSyntaxTreeAsync(CancellationToken.None); diff --git a/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace`1.cs b/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace`1.cs index 3d20103db09f8..4313e5532c122 100644 --- a/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace`1.cs +++ b/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace`1.cs @@ -116,21 +116,37 @@ internal TestWorkspace( } } - public void SetAnalyzerFallbackOptions(string language, params (string name, string value)[] options) + /// + /// Use to set specified editorconfig options as . + /// + internal void SetAnalyzerFallbackOptions(OptionsCollection? options) { + if (options == null) + { + return; + } + SetCurrentSolution( - s => s.WithFallbackAnalyzerOptions(s.FallbackAnalyzerOptions.SetItem(language, - StructuredAnalyzerConfigOptions.Create( - new DictionaryAnalyzerConfigOptions( - options.Select(static o => KeyValuePairUtil.Create(o.name, o.value)).ToImmutableDictionary())))), + s => s.WithFallbackAnalyzerOptions(s.FallbackAnalyzerOptions.SetItem(options.LanguageName, options.ToAnalyzerConfigOptions())), changeKind: WorkspaceChangeKind.SolutionChanged); } - internal void SetAnalyzerFallbackOptions(OptionsCollection options) + /// + /// Use to set specified options both as global options and as . + /// Only editorconfig options listed in will be set to the latter. + /// + internal void SetAnalyzerFallbackAndGlobalOptions(OptionsCollection? options) { - SetCurrentSolution( - s => s.WithFallbackAnalyzerOptions(s.FallbackAnalyzerOptions.SetItem(options.LanguageName, options.ToAnalyzerConfigOptions())), - changeKind: WorkspaceChangeKind.SolutionChanged); + if (options == null) + { + return; + } + + var configOptions = new OptionsCollection(options.LanguageName); + configOptions.AddRange(options.Where(entry => entry.Key.Option.Definition.IsEditorConfigOption)); + SetAnalyzerFallbackOptions(configOptions); + + options.SetGlobalOptions(GlobalOptions); } private static HostServices GetHostServices([NotNull] ref TestComposition? composition, bool hasWorkspaceConfigurationOptions) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/CSharpSyntaxFormatting.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/CSharpSyntaxFormatting.cs index 19b67ce8243ab..d8572dffba6c5 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/CSharpSyntaxFormatting.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/CSharpSyntaxFormatting.cs @@ -39,8 +39,8 @@ public override ImmutableArray GetDefaultFormattingRules public override SyntaxFormattingOptions DefaultOptions => CSharpSyntaxFormattingOptions.Default; - public override SyntaxFormattingOptions GetFormattingOptions(IOptionsReader options, SyntaxFormattingOptions? fallbackOptions) - => new CSharpSyntaxFormattingOptions(options, (CSharpSyntaxFormattingOptions?)fallbackOptions); + public override SyntaxFormattingOptions GetFormattingOptions(IOptionsReader options) + => new CSharpSyntaxFormattingOptions(options, fallbackOptions: null); protected override IFormattingResult CreateAggregatedFormattingResult(SyntaxNode node, IList results, TextSpanMutableIntervalTree? formattingSpans = null) => new AggregatedFormattingResult(node, results, formattingSpans); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeCleanup/CodeCleanupOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeCleanup/CodeCleanupOptions.cs index 658ddb186f855..6ea3b329423cb 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeCleanup/CodeCleanupOptions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeCleanup/CodeCleanupOptions.cs @@ -87,7 +87,7 @@ internal static class CodeCleanupOptionsProviders public static CodeCleanupOptions GetCodeCleanupOptions(this IOptionsReader options, LanguageServices languageServices, bool? allowImportsInHiddenRegions, CodeCleanupOptions? fallbackOptions) => new() { - FormattingOptions = options.GetSyntaxFormattingOptions(languageServices, fallbackOptions?.FormattingOptions), + FormattingOptions = options.GetSyntaxFormattingOptions(languageServices), SimplifierOptions = options.GetSimplifierOptions(languageServices), AddImportOptions = options.GetAddImportPlacementOptions(languageServices, allowImportsInHiddenRegions, fallbackOptions?.AddImportOptions), DocumentFormattingOptions = options.GetDocumentFormattingOptions(fallbackOptions?.DocumentFormattingOptions), diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormatting.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormatting.cs index 8817da296f1b2..1c78cb66e3e6c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormatting.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormatting.cs @@ -21,7 +21,7 @@ internal abstract class AbstractSyntaxFormatting : ISyntaxFormatting private static readonly Func s_notEmpty = s => !s.IsEmpty; public abstract SyntaxFormattingOptions DefaultOptions { get; } - public abstract SyntaxFormattingOptions GetFormattingOptions(IOptionsReader options, SyntaxFormattingOptions? fallbackOptions); + public abstract SyntaxFormattingOptions GetFormattingOptions(IOptionsReader options); public abstract ImmutableArray GetDefaultFormattingRules(); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormatting.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormatting.cs index aa3bf201c3742..6542f10269a5b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormatting.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormatting.cs @@ -14,7 +14,7 @@ namespace Microsoft.CodeAnalysis.Formatting; internal interface ISyntaxFormatting { SyntaxFormattingOptions DefaultOptions { get; } - SyntaxFormattingOptions GetFormattingOptions(IOptionsReader options, SyntaxFormattingOptions? fallbackOptions); + SyntaxFormattingOptions GetFormattingOptions(IOptionsReader options); ImmutableArray GetDefaultFormattingRules(); IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerable? spans, SyntaxFormattingOptions options, ImmutableArray rules, CancellationToken cancellationToken); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/SyntaxFormattingOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/SyntaxFormattingOptions.cs index 69a0415ff71d1..28fa9f8675fc6 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/SyntaxFormattingOptions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/SyntaxFormattingOptions.cs @@ -64,16 +64,13 @@ internal interface SyntaxFormattingOptionsProvider : internal static partial class SyntaxFormattingOptionsProviders { #if !CODE_STYLE - public static SyntaxFormattingOptions GetSyntaxFormattingOptions(this IOptionsReader options, LanguageServices languageServices, SyntaxFormattingOptions? fallbackOptions) - => languageServices.GetRequiredService().GetFormattingOptions(options, fallbackOptions); + public static SyntaxFormattingOptions GetSyntaxFormattingOptions(this IOptionsReader options, LanguageServices languageServices) + => languageServices.GetRequiredService().GetFormattingOptions(options); - public static async ValueTask GetSyntaxFormattingOptionsAsync(this Document document, SyntaxFormattingOptions? fallbackOptions, CancellationToken cancellationToken) + public static async ValueTask GetSyntaxFormattingOptionsAsync(this Document document, CancellationToken cancellationToken) { var configOptions = await document.GetAnalyzerConfigOptionsAsync(cancellationToken).ConfigureAwait(false); - return configOptions.GetSyntaxFormattingOptions(document.Project.Services, fallbackOptions); + return configOptions.GetSyntaxFormattingOptions(document.Project.Services); } - - public static async ValueTask GetSyntaxFormattingOptionsAsync(this Document document, SyntaxFormattingOptionsProvider fallbackOptionsProvider, CancellationToken cancellationToken) - => await GetSyntaxFormattingOptionsAsync(document, await ((OptionsProvider)fallbackOptionsProvider).GetOptionsAsync(document.Project.Services, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); #endif } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeActions/CodeFixOptionsProvider.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeActions/CodeFixOptionsProvider.cs index 1f572ec2cd76c..39bcea51a4d08 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeActions/CodeFixOptionsProvider.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeActions/CodeFixOptionsProvider.cs @@ -54,7 +54,7 @@ public LineFormattingOptions GetLineFormattingOptions() // SyntaxFormattingOptions public SyntaxFormattingOptions GetFormattingOptions(ISyntaxFormatting formatting) - => formatting.GetFormattingOptions(_options, FallbackSyntaxFormattingOptions); + => formatting.GetFormattingOptions(_options); public AccessibilityModifiersRequired AccessibilityModifiersRequired => _options.GetOptionValue(CodeStyleOptions2.AccessibilityModifiersRequired, _languageServices.Language, FallbackCommonSyntaxFormattingOptions.AccessibilityModifiersRequired); @@ -68,13 +68,6 @@ private LineFormattingOptions FallbackLineFormattingOptions => _fallbackOptions.GetOptions(_languageServices.LanguageServices).CleanupOptions.FormattingOptions.LineFormatting; #endif - private SyntaxFormattingOptions? FallbackSyntaxFormattingOptions -#if CODE_STYLE - => null; -#else - => _fallbackOptions.GetOptions(_languageServices.LanguageServices).CleanupOptions.FormattingOptions; -#endif - private SyntaxFormattingOptions FallbackCommonSyntaxFormattingOptions #if CODE_STYLE => SyntaxFormattingOptions.CommonDefaults; diff --git a/src/Workspaces/VisualBasic/Portable/Formatting/VisualBasicSyntaxFormatting.vb b/src/Workspaces/VisualBasic/Portable/Formatting/VisualBasicSyntaxFormatting.vb index 9ab542e29c0ce..9a13c1e0b8da1 100644 --- a/src/Workspaces/VisualBasic/Portable/Formatting/VisualBasicSyntaxFormatting.vb +++ b/src/Workspaces/VisualBasic/Portable/Formatting/VisualBasicSyntaxFormatting.vb @@ -35,8 +35,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting End Get End Property - Public Overrides Function GetFormattingOptions(options As IOptionsReader, fallbackOptions As SyntaxFormattingOptions) As SyntaxFormattingOptions - Return New VisualBasicSyntaxFormattingOptions(options, DirectCast(fallbackOptions, VisualBasicSyntaxFormattingOptions)) + Public Overrides Function GetFormattingOptions(options As IOptionsReader) As SyntaxFormattingOptions + Return New VisualBasicSyntaxFormattingOptions(options, fallbackOptions:=Nothing) End Function Protected Overrides Function CreateAggregatedFormattingResult(node As SyntaxNode, results As IList(Of AbstractFormattingResult), Optional formattingSpans As TextSpanMutableIntervalTree = Nothing) As IFormattingResult From 21fd45be332b22b81d9953be98eead183eb8537c Mon Sep 17 00:00:00 2001 From: DoctorKrolic <70431552+DoctorKrolic@users.noreply.github.com> Date: Tue, 9 Jul 2024 00:36:52 +0300 Subject: [PATCH 13/17] Improve parser recovery around nullable types in patterns (#72805) --- .../CSharp/Portable/Parser/LanguageParser.cs | 79 +- .../Parser/LanguageParser_Patterns.cs | 51 +- .../Emit2/Semantics/PatternMatchingTests.cs | 18 +- .../Emit2/Semantics/PatternMatchingTests2.cs | 27 +- .../Emit2/Semantics/PatternMatchingTests4.cs | 35 +- .../PatternMatchingTests_NullableTypes.cs | 675 ++++++++++++++++++ .../Test/Semantic/Semantics/NullableTests.cs | 16 +- .../Test/Syntax/Parsing/AsyncParsingTests.cs | 194 ++++- .../Test/Syntax/Parsing/AwaitParsingTests.cs | 359 ++++++++++ .../Parsing/DeclarationExpressionTests.cs | 61 +- .../Syntax/Parsing/NullableParsingTests.cs | 582 +++++++++++++++ .../Syntax/Parsing/PatternParsingTests2.cs | 384 ++++++++++ .../PatternParsingTests_ListPatterns.cs | 507 ++++++++++++- .../Syntax/Parsing/StatementParsingTests.cs | 323 +++++++++ .../Parsing/SwitchExpressionParsingTests.cs | 654 +++++++++++++++++ 15 files changed, 3851 insertions(+), 114 deletions(-) create mode 100644 src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests_NullableTypes.cs diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 31c2e242c1c8e..887dfabee8fb7 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -7223,18 +7223,79 @@ bool canFollowNullableType() case ParseTypeMode.AfterIs: case ParseTypeMode.DefinitePattern: case ParseTypeMode.AsExpression: - // These contexts might be a type that is at the end of an expression. In these contexts we only - // permit the nullable qualifier if it is followed by a token that could not start an - // expression, because for backward compatibility we want to consider a `?` token as part of the - // `?:` operator if possible. + // We are currently after `?` token after a nullable type pattern and need to decide how to + // parse what we see next. In the case of an identifier (e.g. `x ? a` there are two ways we can + // see things // - // Similarly, if we have `T?[]` we do want to treat that as an array of nullables (following - // existing parsing), not a conditional that returns a list. + // 1. As a start of conditional expression, e.g. `var a = obj is string ? a : b` + // 2. As a designation of a nullable-typed pattern, e.g. `if (obj is string? str)` + // + // Since nullable types (no matter reference or value types) are not valid in patterns by + // default we are biased towards the first option and consider case 2 only for error recovery + // purposes (if we parse here as nullable type pattern an error will be reported during + // binding). This condition checks for simple cases, where we better use option 2 and parse a + // nullable-typed pattern + if (IsTrueIdentifier(this.CurrentToken)) + { + // In a non-async method, `await` is a simple identifier. However, if we see `x ? await` + // it's almost certainly the start of an `await expression` in a conditional expression + // (e.g. `x is Y ? await ...`), not a nullable type pattern (since users would not use + // 'await' as the name of a variable). So just treat this as a conditional expression. + // Similarly, `async` can start a simple lambda in a conditional expression + // (e.g. `x is Y ? async a => ...`). The correct behavior is to treat `async` as a keyword + if (this.CurrentToken.ContextualKind is SyntaxKind.AsyncKeyword or SyntaxKind.AwaitKeyword) + return false; + + var nextTokenKind = PeekToken(1).Kind; + + // These token either 100% end a pattern or start a new one: + + // A literal token starts a new pattern. Can occur in list pattern with missing separation + // `,`. For example, in `x is [int[]? arr 5]` we'd prefer to parse this as a missing `,` + // after `arr` + if (SyntaxFacts.IsLiteral(nextTokenKind)) + return true; + + // A predefined type is basically the same case: `x is [string[]? slice char ch]`. We'd + // prefer to parse this as a missing `,` after `slice`. + if (SyntaxFacts.IsPredefinedType(nextTokenKind)) + return true; + + // `)`, `]` and `}` obviously end a pattern. For example: + // `if (x is int? i)`, `indexable[x is string? s]`, `x is { Prop: Type? typeVar }` + if (nextTokenKind is SyntaxKind.CloseParenToken or SyntaxKind.CloseBracketToken or SyntaxKind.CloseBraceToken) + return true; + + // `{` starts a new pattern. For example: `x is A? { ...`. Note, that `[` and `(` are not + // in the list because they can start an invocation/indexer + if (nextTokenKind == SyntaxKind.OpenBraceToken) + return true; + + // `,` ends a pattern in list/property pattern. For example `x is { Prop1: Type1? type, Prop2: Type2 }` or + // `x is [Type1? type, ...]` + if (nextTokenKind == SyntaxKind.CommaToken) + return true; + + // `;` ends a pattern if it finishes an expression statement: var y = x is bool? b; + if (nextTokenKind == SyntaxKind.SemicolonToken) + return true; + + // EndOfFileToken is obviously the end of parsing. We are better parsing a pattern rather + // than an unfinished conditional expression + if (nextTokenKind == SyntaxKind.EndOfFileToken) + return true; + + return false; + } + + // If nothing from above worked permit the nullable qualifier if it is followed by a token that + // could not start an expression. If we have `T?[]` we do want to treat that as an array of + // nullables (following existing parsing), not a conditional that returns a list. return !CanStartExpression() || this.CurrentToken.Kind is SyntaxKind.OpenBracketToken; case ParseTypeMode.NewExpression: - // A nullable qualifier is permitted as part of the type in a `new` expression. - // e.g. `new int?()` is allowed. It creates a null value of type `Nullable`. - // Similarly `new int? {}` is allowed. + // A nullable qualifier is permitted as part of the type in a `new` expression. e.g. `new + // int?()` is allowed. It creates a null value of type `Nullable`. Similarly `new int? {}` + // is allowed. return this.CurrentToken.Kind is SyntaxKind.OpenParenToken or // ctor parameters SyntaxKind.OpenBracketToken or // array type diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs index e9b3e9a56cb83..ad961242a9386 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs @@ -50,21 +50,21 @@ when ConvertExpressionToType(expr, out var leftType): }; } - private PatternSyntax ParsePattern(Precedence precedence, bool afterIs = false, bool whenIsKeyword = false) + private PatternSyntax ParsePattern(Precedence precedence, bool afterIs = false, bool inSwitchArmPattern = false) { - return ParseDisjunctivePattern(precedence, afterIs, whenIsKeyword); + return ParseDisjunctivePattern(precedence, afterIs, inSwitchArmPattern); } - private PatternSyntax ParseDisjunctivePattern(Precedence precedence, bool afterIs, bool whenIsKeyword) + private PatternSyntax ParseDisjunctivePattern(Precedence precedence, bool afterIs, bool inSwitchArmPattern) { - PatternSyntax result = ParseConjunctivePattern(precedence, afterIs, whenIsKeyword); + PatternSyntax result = ParseConjunctivePattern(precedence, afterIs, inSwitchArmPattern); while (this.CurrentToken.ContextualKind == SyntaxKind.OrKeyword) { result = _syntaxFactory.BinaryPattern( SyntaxKind.OrPattern, result, ConvertToKeyword(this.EatToken()), - ParseConjunctivePattern(precedence, afterIs, whenIsKeyword)); + ParseConjunctivePattern(precedence, afterIs, inSwitchArmPattern)); } return result; @@ -101,16 +101,16 @@ private bool LooksLikeTypeOfPattern() return false; } - private PatternSyntax ParseConjunctivePattern(Precedence precedence, bool afterIs, bool whenIsKeyword) + private PatternSyntax ParseConjunctivePattern(Precedence precedence, bool afterIs, bool inSwitchArmPattern) { - PatternSyntax result = ParseNegatedPattern(precedence, afterIs, whenIsKeyword); + PatternSyntax result = ParseNegatedPattern(precedence, afterIs, inSwitchArmPattern); while (this.CurrentToken.ContextualKind == SyntaxKind.AndKeyword) { result = _syntaxFactory.BinaryPattern( SyntaxKind.AndPattern, result, ConvertToKeyword(this.EatToken()), - ParseNegatedPattern(precedence, afterIs, whenIsKeyword)); + ParseNegatedPattern(precedence, afterIs, inSwitchArmPattern)); } return result; @@ -155,21 +155,21 @@ private bool ScanDesignation(bool permitTuple) } } - private PatternSyntax ParseNegatedPattern(Precedence precedence, bool afterIs, bool whenIsKeyword) + private PatternSyntax ParseNegatedPattern(Precedence precedence, bool afterIs, bool inSwitchArmPattern) { if (this.CurrentToken.ContextualKind == SyntaxKind.NotKeyword) { return _syntaxFactory.UnaryPattern( ConvertToKeyword(this.EatToken()), - ParseNegatedPattern(precedence, afterIs, whenIsKeyword)); + ParseNegatedPattern(precedence, afterIs, inSwitchArmPattern)); } else { - return ParsePrimaryPattern(precedence, afterIs, whenIsKeyword); + return ParsePrimaryPattern(precedence, afterIs, inSwitchArmPattern); } } - private PatternSyntax ParsePrimaryPattern(Precedence precedence, bool afterIs, bool whenIsKeyword) + private PatternSyntax ParsePrimaryPattern(Precedence precedence, bool afterIs, bool inSwitchArmPattern) { // handle common error recovery situations during typing var tk = this.CurrentToken.Kind; @@ -192,10 +192,10 @@ private PatternSyntax ParsePrimaryPattern(Precedence precedence, bool afterIs, b switch (CurrentToken.Kind) { case SyntaxKind.OpenBracketToken: - return this.ParseListPattern(whenIsKeyword); + return this.ParseListPattern(inSwitchArmPattern); case SyntaxKind.DotDotToken: return _syntaxFactory.SlicePattern(EatToken(), - IsPossibleSubpatternElement() ? ParsePattern(precedence, afterIs: false, whenIsKeyword) : null); + IsPossibleSubpatternElement() ? ParsePattern(precedence, afterIs: false, inSwitchArmPattern) : null); case SyntaxKind.LessThanToken: case SyntaxKind.LessThanEqualsToken: case SyntaxKind.GreaterThanToken: @@ -214,7 +214,8 @@ private PatternSyntax ParsePrimaryPattern(Precedence precedence, bool afterIs, b TypeSyntax? type = null; if (LooksLikeTypeOfPattern()) { - type = this.ParseType(afterIs ? ParseTypeMode.AfterIs : ParseTypeMode.DefinitePattern); + type = this.ParseType( + afterIs ? ParseTypeMode.AfterIs : ParseTypeMode.DefinitePattern); if (type.IsMissing || !CanTokenFollowTypeInPattern(precedence)) { // either it is not shaped like a type, or it is a constant expression. @@ -223,7 +224,7 @@ private PatternSyntax ParsePrimaryPattern(Precedence precedence, bool afterIs, b } } - var pattern = ParsePatternContinued(type, precedence, whenIsKeyword); + var pattern = ParsePatternContinued(type, precedence, inSwitchArmPattern); if (pattern != null) return pattern; @@ -262,14 +263,14 @@ bool CanTokenFollowTypeInPattern(Precedence precedence) } } - private PatternSyntax? ParsePatternContinued(TypeSyntax? type, Precedence precedence, bool whenIsKeyword) + private PatternSyntax? ParsePatternContinued(TypeSyntax? type, Precedence precedence, bool inSwitchArmPattern) { if (type?.Kind == SyntaxKind.IdentifierName) { var typeIdentifier = (IdentifierNameSyntax)type; var typeIdentifierToken = typeIdentifier.Identifier; if (typeIdentifierToken.ContextualKind == SyntaxKind.VarKeyword && - (this.CurrentToken.Kind == SyntaxKind.OpenParenToken || this.IsValidPatternDesignation(whenIsKeyword))) + (this.CurrentToken.Kind == SyntaxKind.OpenParenToken || this.IsValidPatternDesignation(inSwitchArmPattern))) { // we have a "var" pattern; "var" is not permitted to be a stand-in for a type (or a constant) in a pattern. var varToken = ConvertToKeyword(typeIdentifierToken); @@ -295,7 +296,7 @@ bool CanTokenFollowTypeInPattern(Precedence precedence) var closeParenToken = this.EatToken(SyntaxKind.CloseParenToken); parsePropertyPatternClause(out PropertyPatternClauseSyntax? propertyPatternClause0); - var designation0 = TryParseSimpleDesignation(whenIsKeyword); + var designation0 = TryParseSimpleDesignation(inSwitchArmPattern); if (type == null && propertyPatternClause0 == null && @@ -333,12 +334,12 @@ bool CanTokenFollowTypeInPattern(Precedence precedence) { return _syntaxFactory.RecursivePattern( type, positionalPatternClause: null, propertyPatternClause, - TryParseSimpleDesignation(whenIsKeyword)); + TryParseSimpleDesignation(inSwitchArmPattern)); } if (type != null) { - var designation = TryParseSimpleDesignation(whenIsKeyword); + var designation = TryParseSimpleDesignation(inSwitchArmPattern); if (designation != null) return _syntaxFactory.DeclarationPattern(type, designation); @@ -431,7 +432,7 @@ private CSharpSyntaxNode ParseExpressionOrPatternForSwitchStatement() { var savedState = _termState; _termState |= TerminatorState.IsExpressionOrPatternInCaseLabelOfSwitchStatement; - var pattern = ParsePattern(Precedence.Conditional, whenIsKeyword: true); + var pattern = ParsePattern(Precedence.Conditional, inSwitchArmPattern: true); _termState = savedState; return ConvertPatternToExpressionIfPossible(pattern); } @@ -589,7 +590,7 @@ SeparatedSyntaxList parseSwitchExpressionArms() var savedState = _termState; _termState |= TerminatorState.IsPatternInSwitchExpressionArm; - var pattern = ParsePattern(Precedence.Coalescing, whenIsKeyword: true); + var pattern = ParsePattern(Precedence.Coalescing, inSwitchArmPattern: true); _termState = savedState; // We use a precedence that excludes lambdas, assignments, and a conditional which could have a @@ -628,7 +629,7 @@ SeparatedSyntaxList parseSwitchExpressionArms() } } - private ListPatternSyntax ParseListPattern(bool whenIsKeyword) + private ListPatternSyntax ParseListPattern(bool inSwitchArmPattern) { var openBracket = this.EatToken(SyntaxKind.OpenBracketToken); var list = this.ParseCommaSeparatedSyntaxList( @@ -645,7 +646,7 @@ private ListPatternSyntax ParseListPattern(bool whenIsKeyword) openBracket, list, this.EatToken(SyntaxKind.CloseBracketToken), - TryParseSimpleDesignation(whenIsKeyword)); + TryParseSimpleDesignation(inSwitchArmPattern)); } } } diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests.cs index ac537827d6664..2e6c81c785b24 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests.cs @@ -4773,26 +4773,28 @@ public static void Main(object o) private static object M() => null; }"; var compilation = CreateCompilation(program).VerifyDiagnostics( - // (9,32): error CS1525: Invalid expression term 'break' - // case Color? Color2: - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("break").WithLocation(9, 32), - // (9,32): error CS1003: Syntax error, ':' expected - // case Color? Color2: - Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(":").WithLocation(9, 32), // (8,18): error CS0118: 'Color' is a variable but is used like a type // case Color Color: Diagnostic(ErrorCode.ERR_BadSKknown, "Color").WithArguments("Color", "variable", "type").WithLocation(8, 18), // (9,25): error CS0103: The name 'Color2' does not exist in the current context // case Color? Color2: - Diagnostic(ErrorCode.ERR_NameNotInContext, "Color2").WithArguments("Color2").WithLocation(9, 25) - ); + Diagnostic(ErrorCode.ERR_NameNotInContext, "Color2").WithArguments("Color2").WithLocation(9, 25), + // (9,32): error CS1525: Invalid expression term 'break' + // case Color? Color2: + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("break").WithLocation(9, 32), + // (9,32): error CS1003: Syntax error, ':' expected + // case Color? Color2: + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(":").WithLocation(9, 32)); + var tree = compilation.SyntaxTrees.Single(); var model = compilation.GetSemanticModel(tree); var colorDecl = GetPatternDeclarations(tree, "Color").ToArray(); var colorRef = GetReferences(tree, "Color").ToArray(); + Assert.Equal(1, colorDecl.Length); Assert.Equal(2, colorRef.Length); + Assert.Null(model.GetSymbolInfo(colorRef[0]).Symbol); VerifyModelForDeclarationOrVarSimplePattern(model, colorDecl[0], colorRef[1]); } diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests2.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests2.cs index 8b28cfd8f6468..3cf79e3499614 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests2.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests2.cs @@ -4,11 +4,7 @@ #nullable disable -using System; -using System.Collections.Generic; using System.Linq; -using System.Text; -using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; @@ -2680,7 +2676,7 @@ public void M(string s) ); } - [Fact] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] public void IsNullableReferenceType_01() { var source = @@ -2712,25 +2708,12 @@ public void M5(object o) { // (10,22): error CS8650: It is not legal to use nullable reference type 'string?' in an is-type expression; use the underlying type 'string' instead. // var t = o is string?; Diagnostic(ErrorCode.ERR_IsNullableType, "string?").WithArguments("string").WithLocation(10, 22), - // (13,30): error CS0103: The name '_' does not exist in the current context - // var t = o is string? _; - Diagnostic(ErrorCode.ERR_NameNotInContext, "_").WithArguments("_").WithLocation(13, 30), - // (13,31): error CS1003: Syntax error, ':' expected - // var t = o is string? _; - Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(":").WithLocation(13, 31), - // (13,31): error CS1525: Invalid expression term ';' + // (13,22): error CS8116: It is not legal to use nullable type 'string?' in a pattern; use the underlying type 'string' instead. // var t = o is string? _; - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ";").WithArguments(";").WithLocation(13, 31), - // (16,22): error CS8129: No suitable 'Deconstruct' instance or extension method was found for type 'object', with 2 out parameters and a void return type. + Diagnostic(ErrorCode.ERR_PatternNullableType, "string?").WithArguments("string").WithLocation(13, 22), + // (16,23): error CS8116: It is not legal to use nullable type 'string?' in a pattern; use the underlying type 'string' instead. // var t = o is (string? _); - Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(string? _)").WithArguments("object", "2").WithLocation(16, 22), - // (16,29): error CS1003: Syntax error, ',' expected - // var t = o is (string? _); - Diagnostic(ErrorCode.ERR_SyntaxError, "?").WithArguments(",").WithLocation(16, 29), - // (16,31): error CS1003: Syntax error, ',' expected - // var t = o is (string? _); - Diagnostic(ErrorCode.ERR_SyntaxError, "_").WithArguments(",").WithLocation(16, 31) - ); + Diagnostic(ErrorCode.ERR_PatternNullableType, "string?").WithArguments("string").WithLocation(16, 23)); } [Fact] diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests4.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests4.cs index 9f8814f4ecf8b..4665233220ada 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests4.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests4.cs @@ -3759,11 +3759,11 @@ public class A static void M(object o, bool c) { if (o is A?) { } // error 1 (can't test for is nullable reference type) - if (o is A? b1) { } // error 2 (missing :) + if (o is A? b1) { } // error 2 (can't test for is nullable reference type) if (o is A? b2 && c) { } // error 3 (missing :) - if (o is A[]? b5) { } // error 4 (missing :) + if (o is A[]? b5) { } // error 4 (can't test for is nullable reference type) if (o is A[]? b6 && c) { } // error 5 (missing :) - if (o is A[][]? b7) { } // error 6 (missing :) + if (o is A[][]? b7) { } // error 6 (can't test for is nullable reference type) if (o is A[][]? b8 && c) { } // error 7 (missing :) if (o is A? && c) { } // error 8 (can't test for is nullable reference type) _ = o is A[][]?; // error 9 (can't test for is nullable reference type) @@ -3776,36 +3776,27 @@ static void M(object o, bool c) // (7,18): error CS8650: It is not legal to use nullable reference type 'A?' in an is-type expression; use the underlying type 'A' instead. // if (o is A?) { } // error 1 (can't test for is nullable reference type) Diagnostic(ErrorCode.ERR_IsNullableType, "A?").WithArguments("A").WithLocation(7, 18), - // (8,23): error CS1003: Syntax error, ':' expected - // if (o is A? b1) { } // error 2 (missing :) - Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(":").WithLocation(8, 23), - // (8,23): error CS1525: Invalid expression term ')' - // if (o is A? b1) { } // error 2 (missing :) - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(8, 23), - // (9,28): error CS1003: Syntax error, ':' expected + // 0.cs(8,18): error CS8116: It is not legal to use nullable reference type 'A?' in an is-type expression; use the underlying type 'A' instead. + // if (o is A? b1) { } // error 2 (can't test for is nullable reference type) + Diagnostic(ErrorCode.ERR_PatternNullableType, "A?").WithArguments("A").WithLocation(8, 18), + // 0.cs(9,28): error CS1003: Syntax error, ':' expected // if (o is A? b2 && c) { } // error 3 (missing :) Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(":").WithLocation(9, 28), // (9,28): error CS1525: Invalid expression term ')' // if (o is A? b2 && c) { } // error 3 (missing :) Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(9, 28), - // (10,25): error CS1003: Syntax error, ':' expected - // if (o is A[]? b5) { } // error 4 (missing :) - Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(":").WithLocation(10, 25), - // (10,25): error CS1525: Invalid expression term ')' - // if (o is A[]? b5) { } // error 4 (missing :) - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(10, 25), + // (10,18): error CS8116: It is not legal to use nullable reference type 'A[]?' in an is-type expression; use the underlying type 'A[]' instead. + // if (o is A[]? b5) { } // error 4 (can't test for is nullable reference type) + Diagnostic(ErrorCode.ERR_PatternNullableType, "A[]?").WithArguments("A[]").WithLocation(10, 18), // (11,30): error CS1003: Syntax error, ':' expected // if (o is A[]? b6 && c) { } // error 5 (missing :) Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(":").WithLocation(11, 30), // (11,30): error CS1525: Invalid expression term ')' // if (o is A[]? b6 && c) { } // error 5 (missing :) Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(11, 30), - // (12,27): error CS1003: Syntax error, ':' expected - // if (o is A[][]? b7) { } // error 6 (missing :) - Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(":").WithLocation(12, 27), - // (12,27): error CS1525: Invalid expression term ')' - // if (o is A[][]? b7) { } // error 6 (missing :) - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(12, 27), + // (12,18): error CS8116: It is not legal to use nullable reference type 'A[][]?' in an is-type expression; use the underlying type 'A[][]' instead. + // if (o is A[][]? b7) { } // error 6 (can't test for is nullable reference type) + Diagnostic(ErrorCode.ERR_PatternNullableType, "A[][]?").WithArguments("A[][]").WithLocation(12, 18), // (13,32): error CS1003: Syntax error, ':' expected // if (o is A[][]? b8 && c) { } // error 7 (missing :) Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(":").WithLocation(13, 32), diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests_NullableTypes.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests_NullableTypes.cs new file mode 100644 index 0000000000000..ecfa3b7758616 --- /dev/null +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests_NullableTypes.cs @@ -0,0 +1,675 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests; + +public class PatternMatchingTests_NullableTypes : PatternMatchingTestBase +{ + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void PatternCombinations() + { + var source = """ + #nullable enable + + class C + { + void M(object obj) + { + if (obj is int? or string?) { } + if (obj is int? i1 or string? s) { } + if (obj is int? and { }) { } + if (obj is int? i2 and { }) { } + if (obj is int? i3 and (1, 2)) { } + if (obj is int? i4 and []) { } + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,20): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // if (obj is int? or string?) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(7, 20), + // (7,28): error CS8116: It is not legal to use nullable type 'string?' in a pattern; use the underlying type 'string' instead. + // if (obj is int? or string?) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string?").WithArguments("string").WithLocation(7, 28), + // (8,20): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // if (obj is int? i1 or string? s) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(8, 20), + // (8,25): error CS8780: A variable may not be declared within a 'not' or 'or' pattern. + // if (obj is int? i1 or string? s) { } + Diagnostic(ErrorCode.ERR_DesignatorBeneathPatternCombinator, "i1").WithLocation(8, 25), + // (8,31): error CS8116: It is not legal to use nullable type 'string?' in a pattern; use the underlying type 'string' instead. + // if (obj is int? i1 or string? s) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string?").WithArguments("string").WithLocation(8, 31), + // (8,39): error CS8780: A variable may not be declared within a 'not' or 'or' pattern. + // if (obj is int? i1 or string? s) { } + Diagnostic(ErrorCode.ERR_DesignatorBeneathPatternCombinator, "s").WithLocation(8, 39), + // (9,20): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // if (obj is int? and { }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(9, 20), + // (10,20): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // if (obj is int? i2 and { }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(10, 20), + // (11,20): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // if (obj is int? i3 and (1, 2)) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(11, 20), + // (11,32): error CS1061: 'int' does not contain a definition for 'Deconstruct' and no accessible extension method 'Deconstruct' accepting a first argument of type 'int' could be found (are you missing a using directive or an assembly reference?) + // if (obj is int? i3 and (1, 2)) { } + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "(1, 2)").WithArguments("int", "Deconstruct").WithLocation(11, 32), + // (11,32): error CS8129: No suitable 'Deconstruct' instance or extension method was found for type 'int', with 2 out parameters and a void return type. + // if (obj is int? i3 and (1, 2)) { } + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(1, 2)").WithArguments("int", "2").WithLocation(11, 32), + // (12,20): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // if (obj is int? i4 and []) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(12, 20), + // (12,32): error CS8985: List patterns may not be used for a value of type 'int'. No suitable 'Length' or 'Count' property was found. + // if (obj is int? i4 and []) { } + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[]").WithArguments("int").WithLocation(12, 32), + // (12,32): error CS0518: Predefined type 'System.Index' is not defined or imported + // if (obj is int? i4 and []) { } + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[]").WithArguments("System.Index").WithLocation(12, 32), + // (12,32): error CS0021: Cannot apply indexing with [] to an expression of type 'int' + // if (obj is int? i4 and []) { } + Diagnostic(ErrorCode.ERR_BadIndexLHS, "[]").WithArguments("int").WithLocation(12, 32)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void PatternCombinations_Arrays() + { + var source = """ + #nullable enable + + class C + { + void M(object obj) + { + if (obj is int[]? or string[]?) { } + if (obj is int[]? i1 or string[]? s) { } + if (obj is int[]? and { }) { } + if (obj is int[]? i2 and { }) { } + if (obj is int[]? i3 and (1, 2)) { } + if (obj is int[]? i4 and []) { } + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,20): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // if (obj is int[]? or string[]?) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(7, 20), + // (7,30): error CS8116: It is not legal to use nullable type 'string[]?' in a pattern; use the underlying type 'string[]' instead. + // if (obj is int[]? or string[]?) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string[]?").WithArguments("string[]").WithLocation(7, 30), + // (8,20): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // if (obj is int[]? i1 or string[]? s) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(8, 20), + // (8,27): error CS8780: A variable may not be declared within a 'not' or 'or' pattern. + // if (obj is int[]? i1 or string[]? s) { } + Diagnostic(ErrorCode.ERR_DesignatorBeneathPatternCombinator, "i1").WithLocation(8, 27), + // (8,33): error CS8116: It is not legal to use nullable type 'string[]?' in a pattern; use the underlying type 'string[]' instead. + // if (obj is int[]? i1 or string[]? s) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string[]?").WithArguments("string[]").WithLocation(8, 33), + // (8,43): error CS8780: A variable may not be declared within a 'not' or 'or' pattern. + // if (obj is int[]? i1 or string[]? s) { } + Diagnostic(ErrorCode.ERR_DesignatorBeneathPatternCombinator, "s").WithLocation(8, 43), + // (9,20): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // if (obj is int[]? and { }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(9, 20), + // (10,20): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // if (obj is int[]? i2 and { }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(10, 20), + // (11,20): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // if (obj is int[]? i3 and (1, 2)) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(11, 20), + // (11,34): error CS1061: 'int[]' does not contain a definition for 'Deconstruct' and no accessible extension method 'Deconstruct' accepting a first argument of type 'int[]' could be found (are you missing a using directive or an assembly reference?) + // if (obj is int[]? i3 and (1, 2)) { } + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "(1, 2)").WithArguments("int[]", "Deconstruct").WithLocation(11, 34), + // (11,34): error CS8129: No suitable 'Deconstruct' instance or extension method was found for type 'int[]', with 2 out parameters and a void return type. + // if (obj is int[]? i3 and (1, 2)) { } + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(1, 2)").WithArguments("int[]", "2").WithLocation(11, 34), + // (12,20): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // if (obj is int[]? i4 and []) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(12, 20), + // (12,34): error CS0518: Predefined type 'System.Index' is not defined or imported + // if (obj is int[]? i4 and []) { } + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[]").WithArguments("System.Index").WithLocation(12, 34)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void PatternsInSwitchStatement() + { + var source = """ + using System.Threading.Tasks; + + #nullable enable + + class C + { + void M(object obj) + { + var x = 0; + switch (obj) + { + case int?: + break; + case Task?: + break; + case int? i1: + break; + case Task? t1: + break; + case int? when x > 0: + break; + case Task? when x > 0: + break; + case int? i2 when x > 0: + break; + case Task? t2 when x > 0: + break; + case (int? when) when x > 0: + break; + case (Task? when) when x > 0: + break; + } + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (12,18): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // case int?: + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(12, 18), + // (14,18): error CS8116: It is not legal to use nullable type 'Task?' in a pattern; use the underlying type 'Task' instead. + // case Task?: + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task?").WithArguments("System.Threading.Tasks.Task").WithLocation(14, 18), + // (16,21): error CS1003: Syntax error, ':' expected + // case int? i1: + Diagnostic(ErrorCode.ERR_SyntaxError, "?").WithArguments(":").WithLocation(16, 21), + // (16,21): error CS1513: } expected + // case int? i1: + Diagnostic(ErrorCode.ERR_RbraceExpected, "?").WithLocation(16, 21), + // (16,23): warning CS0164: This label has not been referenced + // case int? i1: + Diagnostic(ErrorCode.WRN_UnreferencedLabel, "i1").WithLocation(16, 23), + // (18,18): error CS0119: 'Task' is a type, which is not valid in the given context + // case Task? t1: + Diagnostic(ErrorCode.ERR_BadSKunknown, "Task").WithArguments("System.Threading.Tasks.Task", "type").WithLocation(18, 18), + // (18,24): error CS0103: The name 't1' does not exist in the current context + // case Task? t1: + Diagnostic(ErrorCode.ERR_NameNotInContext, "t1").WithArguments("t1").WithLocation(18, 24), + // (18,27): error CS1525: Invalid expression term 'break' + // case Task? t1: + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("break").WithLocation(18, 27), + // (18,27): error CS1003: Syntax error, ':' expected + // case Task? t1: + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(":").WithLocation(18, 27), + // (20,18): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // case int? when x > 0: + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(20, 18), + // (22,18): error CS8116: It is not legal to use nullable type 'Task?' in a pattern; use the underlying type 'Task' instead. + // case Task? when x > 0: + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task?").WithArguments("System.Threading.Tasks.Task").WithLocation(22, 18), + // (24,18): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // case int? i2 when x > 0: + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(24, 18), + // (26,18): error CS8116: It is not legal to use nullable type 'Task?' in a pattern; use the underlying type 'Task' instead. + // case Task? t2 when x > 0: + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task?").WithArguments("System.Threading.Tasks.Task").WithLocation(26, 18), + // (28,19): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // case (int? when) when x > 0: + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(28, 19), + // (30,19): error CS8116: It is not legal to use nullable type 'Task?' in a pattern; use the underlying type 'Task' instead. + // case (Task? when) when x > 0: + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task?").WithArguments("System.Threading.Tasks.Task").WithLocation(30, 19)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void PatternsInSwitchStatement_Arrays() + { + var source = """ + using System.Threading.Tasks; + + #nullable enable + + class C + { + void M(object obj) + { + var x = 0; + switch (obj) + { + case int[]?: + break; + case Task[]?: + break; + case int[]? i1: + break; + case Task[]? t1: + break; + case int[]? when x > 0: + break; + case Task[]? when x > 0: + break; + case int[]? i2 when x > 0: + break; + case Task[]? t2 when x > 0: + break; + case (int[]? when) when x > 0: + break; + case (Task[]? when) when x > 0: + break; + } + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (12,18): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // case int[]?: + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(12, 18), + // (14,18): error CS8116: It is not legal to use nullable type 'Task[]?' in a pattern; use the underlying type 'Task[]' instead. + // case Task[]?: + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task[]?").WithArguments("System.Threading.Tasks.Task[]").WithLocation(14, 18), + // (16,23): error CS1003: Syntax error, ':' expected + // case int[]? i1: + Diagnostic(ErrorCode.ERR_SyntaxError, "?").WithArguments(":").WithLocation(16, 23), + // (16,23): error CS1513: } expected + // case int[]? i1: + Diagnostic(ErrorCode.ERR_RbraceExpected, "?").WithLocation(16, 23), + // (16,25): warning CS0164: This label has not been referenced + // case int[]? i1: + Diagnostic(ErrorCode.WRN_UnreferencedLabel, "i1").WithLocation(16, 25), + // (18,24): error CS1003: Syntax error, ':' expected + // case Task[]? t1: + Diagnostic(ErrorCode.ERR_SyntaxError, "?").WithArguments(":").WithLocation(18, 24), + // (18,24): error CS1513: } expected + // case Task[]? t1: + Diagnostic(ErrorCode.ERR_RbraceExpected, "?").WithLocation(18, 24), + // (18,26): warning CS0164: This label has not been referenced + // case Task[]? t1: + Diagnostic(ErrorCode.WRN_UnreferencedLabel, "t1").WithLocation(18, 26), + // (20,18): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // case int[]? when x > 0: + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(20, 18), + // (22,18): error CS8116: It is not legal to use nullable type 'Task[]?' in a pattern; use the underlying type 'Task[]' instead. + // case Task[]? when x > 0: + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task[]?").WithArguments("System.Threading.Tasks.Task[]").WithLocation(22, 18), + // (24,18): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // case int[]? i2 when x > 0: + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(24, 18), + // (26,18): error CS8116: It is not legal to use nullable type 'Task[]?' in a pattern; use the underlying type 'Task[]' instead. + // case Task[]? t2 when x > 0: + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task[]?").WithArguments("System.Threading.Tasks.Task[]").WithLocation(26, 18), + // (28,19): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // case (int[]? when) when x > 0: + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(28, 19), + // (30,19): error CS8116: It is not legal to use nullable type 'Task[]?' in a pattern; use the underlying type 'Task[]' instead. + // case (Task[]? when) when x > 0: + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task[]?").WithArguments("System.Threading.Tasks.Task[]").WithLocation(30, 19)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void PatternsInSwitchExpression() + { + var source = """ + using System.Threading.Tasks; + + #nullable enable + + class C + { + void M(object obj) + { + var x = 0; + _ = obj switch + { + int? => 1, + Task? => 2, + int? i1 => 3, + Task? t1 => 4, + int? when x > 0 => 5, + Task? when x > 0 => 6, + int? i2 when x > 0 => 7, + Task? t2 when x > 0 => 8, + (int? when) when x > 0 => 9, + (Task? when) when x > 0 => 10 + }; + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (12,13): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // int? => 1, + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(12, 13), + // (13,13): error CS8116: It is not legal to use nullable type 'Task?' in a pattern; use the underlying type 'Task' instead. + // Task? => 2, + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task?").WithArguments("System.Threading.Tasks.Task").WithLocation(13, 13), + // (14,16): error CS1003: Syntax error, '=>' expected + // int? i1 => 3, + Diagnostic(ErrorCode.ERR_SyntaxError, "?").WithArguments("=>").WithLocation(14, 16), + // (14,16): error CS1525: Invalid expression term '?' + // int? i1 => 3, + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "?").WithArguments("?").WithLocation(14, 16), + // (14,25): error CS1003: Syntax error, ':' expected + // int? i1 => 3, + Diagnostic(ErrorCode.ERR_SyntaxError, ",").WithArguments(":").WithLocation(14, 25), + // (14,25): error CS1525: Invalid expression term ',' + // int? i1 => 3, + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ",").WithArguments(",").WithLocation(14, 25), + // (15,17): error CS1003: Syntax error, '=>' expected + // Task? t1 => 4, + Diagnostic(ErrorCode.ERR_SyntaxError, "?").WithArguments("=>").WithLocation(15, 17), + // (15,17): error CS1525: Invalid expression term '?' + // Task? t1 => 4, + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "?").WithArguments("?").WithLocation(15, 17), + // (15,26): error CS1003: Syntax error, ':' expected + // Task? t1 => 4, + Diagnostic(ErrorCode.ERR_SyntaxError, ",").WithArguments(":").WithLocation(15, 26), + // (15,26): error CS1525: Invalid expression term ',' + // Task? t1 => 4, + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ",").WithArguments(",").WithLocation(15, 26), + // (16,13): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // int? when x > 0 => 5, + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(16, 13), + // (17,13): error CS8116: It is not legal to use nullable type 'Task?' in a pattern; use the underlying type 'Task' instead. + // Task? when x > 0 => 6, + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task?").WithArguments("System.Threading.Tasks.Task").WithLocation(17, 13), + // (18,13): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // int? i2 when x > 0 => 7, + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(18, 13), + // (19,13): error CS8116: It is not legal to use nullable type 'Task?' in a pattern; use the underlying type 'Task' instead. + // Task? t2 when x > 0 => 8, + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task?").WithArguments("System.Threading.Tasks.Task").WithLocation(19, 13), + // (20,14): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // (int? when) when x > 0 => 9, + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(20, 14), + // (21,14): error CS8116: It is not legal to use nullable type 'Task?' in a pattern; use the underlying type 'Task' instead. + // (Task? when) when x > 0 => 10 + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task?").WithArguments("System.Threading.Tasks.Task").WithLocation(21, 14)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void PatternsInSwitchExpression_Arrays() + { + var source = """ + using System.Threading.Tasks; + + #nullable enable + + class C + { + void M(object obj) + { + var x = 0; + _ = obj switch + { + int[]? => 1, + Task[]? => 2, + int[]? i1 => 3, + Task[]? t1 => 4, + int[]? when x > 0 => 5, + Task[]? when x > 0 => 6, + int[]? i2 when x > 0 => 7, + Task[]? t2 when x > 0 => 8, + (int[]? when) when x > 0 => 9, + (Task[]? when) when x > 0 => 10 + }; + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (12,13): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // int[]? => 1, + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(12, 13), + // (13,13): error CS8116: It is not legal to use nullable type 'Task[]?' in a pattern; use the underlying type 'Task[]' instead. + // Task[]? => 2, + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task[]?").WithArguments("System.Threading.Tasks.Task[]").WithLocation(13, 13), + // (14,18): error CS1003: Syntax error, '=>' expected + // int[]? i1 => 3, + Diagnostic(ErrorCode.ERR_SyntaxError, "?").WithArguments("=>").WithLocation(14, 18), + // (14,18): error CS1525: Invalid expression term '?' + // int[]? i1 => 3, + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "?").WithArguments("?").WithLocation(14, 18), + // (14,27): error CS1003: Syntax error, ':' expected + // int[]? i1 => 3, + Diagnostic(ErrorCode.ERR_SyntaxError, ",").WithArguments(":").WithLocation(14, 27), + // (14,27): error CS1525: Invalid expression term ',' + // int[]? i1 => 3, + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ",").WithArguments(",").WithLocation(14, 27), + // (15,19): error CS1003: Syntax error, '=>' expected + // Task[]? t1 => 4, + Diagnostic(ErrorCode.ERR_SyntaxError, "?").WithArguments("=>").WithLocation(15, 19), + // (15,19): error CS1525: Invalid expression term '?' + // Task[]? t1 => 4, + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "?").WithArguments("?").WithLocation(15, 19), + // (15,28): error CS1003: Syntax error, ':' expected + // Task[]? t1 => 4, + Diagnostic(ErrorCode.ERR_SyntaxError, ",").WithArguments(":").WithLocation(15, 28), + // (15,28): error CS1525: Invalid expression term ',' + // Task[]? t1 => 4, + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ",").WithArguments(",").WithLocation(15, 28), + // (16,13): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // int[]? when x > 0 => 5, + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(16, 13), + // (17,13): error CS8116: It is not legal to use nullable type 'Task[]?' in a pattern; use the underlying type 'Task[]' instead. + // Task[]? when x > 0 => 6, + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task[]?").WithArguments("System.Threading.Tasks.Task[]").WithLocation(17, 13), + // (18,13): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // int[]? i2 when x > 0 => 7, + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(18, 13), + // (19,13): error CS8116: It is not legal to use nullable type 'Task[]?' in a pattern; use the underlying type 'Task[]' instead. + // Task[]? t2 when x > 0 => 8, + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task[]?").WithArguments("System.Threading.Tasks.Task[]").WithLocation(19, 13), + // (20,14): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // (int[]? when) when x > 0 => 9, + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(20, 14), + // (21,14): error CS8116: It is not legal to use nullable type 'Task[]?' in a pattern; use the underlying type 'Task[]' instead. + // (Task[]? when) when x > 0 => 10 + Diagnostic(ErrorCode.ERR_PatternNullableType, "Task[]?").WithArguments("System.Threading.Tasks.Task[]").WithLocation(21, 14)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void PropertySubpatterns() + { + var source = """ + #nullable enable + + class E + { + public object? Prop { get; set; } + public object? Prop2 { get; set; } + } + + class C + { + void M(E e) + { + if (e is { Prop: int? }) { } + if (e is { Prop: int? i1 }) { } + if (e is { Prop: int? or string? }) { } + if (e is { Prop: int? i2 or string? s1 }) { } + if (e is { Prop: int?, Prop2: string? }) { } + if (e is { Prop: int? i3, Prop2: string? s2 }) { } + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (13,26): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // if (e is { Prop: int? }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(13, 26), + // (14,26): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // if (e is { Prop: int? i1 }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(14, 26), + // (15,26): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // if (e is { Prop: int? or string? }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(15, 26), + // (15,34): error CS8116: It is not legal to use nullable type 'string?' in a pattern; use the underlying type 'string' instead. + // if (e is { Prop: int? or string? }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string?").WithArguments("string").WithLocation(15, 34), + // (16,26): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // if (e is { Prop: int? i2 or string? s1 }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(16, 26), + // (16,31): error CS8780: A variable may not be declared within a 'not' or 'or' pattern. + // if (e is { Prop: int? i2 or string? s1 }) { } + Diagnostic(ErrorCode.ERR_DesignatorBeneathPatternCombinator, "i2").WithLocation(16, 31), + // (16,37): error CS8116: It is not legal to use nullable type 'string?' in a pattern; use the underlying type 'string' instead. + // if (e is { Prop: int? i2 or string? s1 }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string?").WithArguments("string").WithLocation(16, 37), + // (16,45): error CS8780: A variable may not be declared within a 'not' or 'or' pattern. + // if (e is { Prop: int? i2 or string? s1 }) { } + Diagnostic(ErrorCode.ERR_DesignatorBeneathPatternCombinator, "s1").WithLocation(16, 45), + // (17,26): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // if (e is { Prop: int?, Prop2: string? }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(17, 26), + // (17,39): error CS8116: It is not legal to use nullable type 'string?' in a pattern; use the underlying type 'string' instead. + // if (e is { Prop: int?, Prop2: string? }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string?").WithArguments("string").WithLocation(17, 39), + // (18,26): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. + // if (e is { Prop: int? i3, Prop2: string? s2 }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(18, 26), + // (18,42): error CS8116: It is not legal to use nullable type 'string?' in a pattern; use the underlying type 'string' instead. + // if (e is { Prop: int? i3, Prop2: string? s2 }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string?").WithArguments("string").WithLocation(18, 42)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void PropertySubpatterns_Arrays() + { + var source = """ + #nullable enable + + class E + { + public object? Prop { get; set; } + public object? Prop2 { get; set; } + } + + class C + { + void M(E e) + { + if (e is { Prop: int[]? }) { } + if (e is { Prop: int[]? i1 }) { } + if (e is { Prop: int[]? or string[]? }) { } + if (e is { Prop: int[]? i2 or string[]? s1 }) { } + if (e is { Prop: int[]?, Prop2: string[]? }) { } + if (e is { Prop: int[]? i3, Prop2: string[]? s2 }) { } + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (13,26): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // if (e is { Prop: int[]? }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(13, 26), + // (14,26): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // if (e is { Prop: int[]? i1 }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(14, 26), + // (15,26): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // if (e is { Prop: int[]? or string[]? }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(15, 26), + // (15,36): error CS8116: It is not legal to use nullable type 'string[]?' in a pattern; use the underlying type 'string[]' instead. + // if (e is { Prop: int[]? or string[]? }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string[]?").WithArguments("string[]").WithLocation(15, 36), + // (16,26): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // if (e is { Prop: int[]? i2 or string[]? s1 }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(16, 26), + // (16,33): error CS8780: A variable may not be declared within a 'not' or 'or' pattern. + // if (e is { Prop: int[]? i2 or string[]? s1 }) { } + Diagnostic(ErrorCode.ERR_DesignatorBeneathPatternCombinator, "i2").WithLocation(16, 33), + // (16,39): error CS8116: It is not legal to use nullable type 'string[]?' in a pattern; use the underlying type 'string[]' instead. + // if (e is { Prop: int[]? i2 or string[]? s1 }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string[]?").WithArguments("string[]").WithLocation(16, 39), + // (16,49): error CS8780: A variable may not be declared within a 'not' or 'or' pattern. + // if (e is { Prop: int[]? i2 or string[]? s1 }) { } + Diagnostic(ErrorCode.ERR_DesignatorBeneathPatternCombinator, "s1").WithLocation(16, 49), + // (17,26): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // if (e is { Prop: int[]?, Prop2: string[]? }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(17, 26), + // (17,41): error CS8116: It is not legal to use nullable type 'string[]?' in a pattern; use the underlying type 'string[]' instead. + // if (e is { Prop: int[]?, Prop2: string[]? }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string[]?").WithArguments("string[]").WithLocation(17, 41), + // (18,26): error CS8116: It is not legal to use nullable type 'int[]?' in a pattern; use the underlying type 'int[]' instead. + // if (e is { Prop: int[]? i3, Prop2: string[]? s2 }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "int[]?").WithArguments("int[]").WithLocation(18, 26), + // (18,44): error CS8116: It is not legal to use nullable type 'string[]?' in a pattern; use the underlying type 'string[]' instead. + // if (e is { Prop: int[]? i3, Prop2: string[]? s2 }) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string[]?").WithArguments("string[]").WithLocation(18, 44)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ListPatterns() + { + var source = """ + #nullable enable + + class C + { + void M2(string s) + { + if (s is [.. string?]) { } + if (s is [.. string? slice1]) { } + if (s is [.. string? slice2, ')']) { } + } + } + """; + + var comp = CreateCompilationWithIndexAndRange(source); + comp.VerifyDiagnostics( + // (7,22): error CS8116: It is not legal to use nullable type 'string?' in a pattern; use the underlying type 'string' instead. + // if (s is [.. string?]) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string?").WithArguments("string").WithLocation(7, 22), + // (8,22): error CS8116: It is not legal to use nullable type 'string?' in a pattern; use the underlying type 'string' instead. + // if (s is [.. string? slice1]) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string?").WithArguments("string").WithLocation(8, 22), + // (9,22): error CS8116: It is not legal to use nullable type 'string?' in a pattern; use the underlying type 'string' instead. + // if (s is [.. string? slice2, ')']) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string?").WithArguments("string").WithLocation(9, 22)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ListPatterns_Arrays() + { + var source = """ + #nullable enable + + class C + { + void M(string[] s) + { + if (s is [.. string[]?]) { } + if (s is [.. string[]? slice1]) { } + if (s is [.. string[]? slice2, ")"]) { } + } + } + """; + + var comp = CreateCompilationWithIndexAndRange([source, TestSources.GetSubArray]); + comp.VerifyDiagnostics( + // (7,22): error CS8116: It is not legal to use nullable type 'string[]?' in a pattern; use the underlying type 'string[]' instead. + // if (s is [.. string[]?]) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string[]?").WithArguments("string[]").WithLocation(7, 22), + // (8,22): error CS8116: It is not legal to use nullable type 'string[]?' in a pattern; use the underlying type 'string[]' instead. + // if (s is [.. string[]? slice1]) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string[]?").WithArguments("string[]").WithLocation(8, 22), + // (9,22): error CS8116: It is not legal to use nullable type 'string[]?' in a pattern; use the underlying type 'string[]' instead. + // if (s is [.. string[]? slice2, ')']) { } + Diagnostic(ErrorCode.ERR_PatternNullableType, "string[]?").WithArguments("string[]").WithLocation(9, 22)); + } +} diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableTests.cs index 73ba7b40eccba..602da008a3af4 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableTests.cs @@ -6,11 +6,7 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; using System.Text; -using Microsoft.CodeAnalysis.CSharp.Symbols; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; @@ -2133,7 +2129,7 @@ struct S ); } - [Fact] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] public void TestIsNullable1() { var source = @" @@ -2149,15 +2145,9 @@ void M(object o) "; CreateCompilation(source).VerifyDiagnostics( - // (6,23): error CS0103: The name 'i' does not exist in the current context - // if (o is int? i) - Diagnostic(ErrorCode.ERR_NameNotInContext, "i").WithArguments("i").WithLocation(6, 23), - // (6,24): error CS1003: Syntax error, ':' expected - // if (o is int? i) - Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(":").WithLocation(6, 24), - // (6,24): error CS1525: Invalid expression term ')' + // (6,18): error CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead. // if (o is int? i) - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(6, 24)); + Diagnostic(ErrorCode.ERR_PatternNullableType, "int?").WithArguments("int").WithLocation(6, 18)); } [Fact] diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/AsyncParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/AsyncParsingTests.cs index 91a5bf720bf4b..90944462784b9 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/AsyncParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/AsyncParsingTests.cs @@ -4,9 +4,9 @@ #nullable disable +using System; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Roslyn.Test.Utilities; -using System; using Xunit; using Xunit.Abstractions; @@ -2658,5 +2658,197 @@ public void AsyncAsType_Property_ExplicitInterface() } EOF(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void AsyncLambdaInConditionalExpressionAfterPattern1() + { + UsingExpression("x is A ? async b => 0 : null"); + + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.SimpleLambdaExpression); + { + N(SyntaxKind.AsyncKeyword); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void AsyncLambdaInConditionalExpressionAfterPattern2() + { + UsingExpression("x is A a ? async b => 0 : null"); + + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.SimpleLambdaExpression); + { + N(SyntaxKind.AsyncKeyword); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void AsyncLambdaInConditionalExpressionAfterPattern3() + { + UsingExpression("x is A ? async (b) => 0 : null"); + + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.AsyncKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void AsyncLambdaInConditionalExpressionAfterPattern4() + { + UsingExpression("x is A a ? async (b) => 0 : null"); + + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.AsyncKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + EOF(); + } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/AwaitParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/AwaitParsingTests.cs index 56c40064345ad..3479451b547ab 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/AwaitParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/AwaitParsingTests.cs @@ -4,6 +4,7 @@ #nullable disable +using Roslyn.Test.Utilities; using Xunit; using Xunit.Abstractions; @@ -1147,6 +1148,364 @@ public void AwaitUsingTest() } } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void AwaitInConditionalExpressionAfterPattern1() + { + UsingExpression("x is int ? await y : z"); + + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.AwaitExpression); + { + N(SyntaxKind.AwaitKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "z"); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void AwaitInConditionalExpressionAfterPattern2() + { + UsingExpression("x is int ? await this.SomeMethodAsync() : z"); + + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.AwaitExpression); + { + N(SyntaxKind.AwaitKeyword); + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.ThisExpression); + { + N(SyntaxKind.ThisKeyword); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "SomeMethodAsync"); + } + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "z"); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void AwaitInConditionalExpressionAfterPattern3() + { + UsingExpression("x is int ? await base.SomeMethodAsync() : z"); + + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.AwaitExpression); + { + N(SyntaxKind.AwaitKeyword); + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.BaseExpression); + { + N(SyntaxKind.BaseKeyword); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "SomeMethodAsync"); + } + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "z"); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void AwaitInConditionalExpressionAfterPattern4() + { + UsingExpression("x is int ? await (myTask) : z"); + + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "await"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "myTask"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "z"); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void AwaitAsStartOfExpressionInConditional1() + { + UsingExpression("f(x is int? await)", + // (1,18): error CS1003: Syntax error, ':' expected + // f(x is int? await) + Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(":").WithLocation(1, 18), + // (1,18): error CS1525: Invalid expression term ')' + // f(x is int? await) + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(1, 18)); + + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "f"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "await"); + } + M(SyntaxKind.ColonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void AwaitAsStartOfExpressionInConditional2() + { + UsingExpression("dict[x is int? await]", + // (1,21): error CS1003: Syntax error, ':' expected + // dict[x is int? await] + Diagnostic(ErrorCode.ERR_SyntaxError, "]").WithArguments(":").WithLocation(1, 21), + // (1,21): error CS1525: Invalid expression term ']' + // dict[x is int? await] + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "]").WithArguments("]").WithLocation(1, 21)); + + N(SyntaxKind.ElementAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "dict"); + } + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "await"); + } + M(SyntaxKind.ColonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + N(SyntaxKind.CloseBracketToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void AwaitAsStartOfExpressionInConditional3() + { + UsingExpression("x is { Prop: int? await }", + // (1,17): error CS1003: Syntax error, ',' expected + // x is { Prop: int? await } + Diagnostic(ErrorCode.ERR_SyntaxError, "?").WithArguments(",").WithLocation(1, 17), + // (1,19): error CS1003: Syntax error, ',' expected + // x is { Prop: int? await } + Diagnostic(ErrorCode.ERR_SyntaxError, "await").WithArguments(",").WithLocation(1, 19)); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Prop"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "await"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + #region AwaitExpressionInSyncContext [Fact] diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationExpressionTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationExpressionTests.cs index a8dc74a200040..cce90916c45e4 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationExpressionTests.cs @@ -5,6 +5,7 @@ #nullable disable using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; using Xunit; using Xunit.Abstractions; @@ -134,17 +135,53 @@ public void NullableTypeTest_02() EOF(); } - [Fact] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] public void NullableTypeTest_03() { - UsingStatement("if (e is int? x) {}", - // (1,16): error CS1003: Syntax error, ':' expected - // if (e is int? x) {} - Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(":").WithLocation(1, 16), - // (1,16): error CS1525: Invalid expression term ')' - // if (e is int? x) {} - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(1, 16) - ); + UsingStatement("if (e is int? x) {}"); + + N(SyntaxKind.IfStatement); + { + N(SyntaxKind.IfKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void NullableTypeTest_03_2() + { + UsingStatement("if (e is int ? x : y) {}"); + N(SyntaxKind.IfStatement); { N(SyntaxKind.IfKeyword); @@ -168,10 +205,10 @@ public void NullableTypeTest_03() { N(SyntaxKind.IdentifierToken, "x"); } - M(SyntaxKind.ColonToken); - M(SyntaxKind.IdentifierName); + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); { - M(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "y"); } } N(SyntaxKind.CloseParenToken); diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/NullableParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/NullableParsingTests.cs index aaacbfe16dcc9..be7321a78f231 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/NullableParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/NullableParsingTests.cs @@ -688,6 +688,7 @@ public void DeclarationPattern_NullableType() // (1,25): error CS1003: Syntax error, ':' expected // switch (e) { case T? t: break; } Diagnostic(ErrorCode.ERR_SyntaxError, "break").WithArguments(":").WithLocation(1, 25)); + N(SyntaxKind.SwitchStatement); { N(SyntaxKind.SwitchKeyword); @@ -836,6 +837,7 @@ public void DeclarationPattern_NullableArray() // (1,22): error CS1513: } expected // switch (e) { case T[]? t: break; } Diagnostic(ErrorCode.ERR_RbraceExpected, "?").WithLocation(1, 22)); + N(SyntaxKind.SwitchStatement); { N(SyntaxKind.SwitchKeyword); @@ -1774,5 +1776,585 @@ public void CreateNullableArray_07() } EOF(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void IsExpressionOfNullableTypeInStatement() + { + UsingStatement("_ = x is Type?;"); + + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "_"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void DeclarationPatternOfNullableTypeInStatement() + { + UsingStatement("_ = x is Type? t;"); + + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "_"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "t"); + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void DisjunctivePattern_NullableType1() + { + UsingExpression("x is int? or string?"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.OrPattern); + { + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.QuestionToken); + } + } + N(SyntaxKind.OrKeyword); + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.QuestionToken); + } + } + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void DisjunctivePattern_NullableType2() + { + UsingExpression("x is int? i or string? s"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.OrPattern); + { + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "i"); + } + } + N(SyntaxKind.OrKeyword); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "s"); + } + } + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ConjunctivePattern_NullableType1() + { + UsingExpression("x is Type? and { }"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.AndPattern); + { + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + } + N(SyntaxKind.AndKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ConjunctivePattern_NullableType2() + { + UsingExpression("x is Type? t and { }"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.AndPattern); + { + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "t"); + } + } + N(SyntaxKind.AndKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ConjunctivePattern_ConditionalExpressionInsteadOfNullableType3() + { + UsingExpression("x is Type? and (1, 2)", + // (1,22): error CS1003: Syntax error, ':' expected + // x is Type? and (1, 2) + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(":").WithLocation(1, 22), + // (1,22): error CS1733: Expected expression + // x is Type? and (1, 2) + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 22)); + + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "and"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "2"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + M(SyntaxKind.ColonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ConjunctivePattern_ConditionalExpressionInsteadOfNullableType3_2() + { + UsingExpression("x is Type ? f(1, 2) : 0"); + + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "f"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "2"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ConjunctivePattern_NullableType4() + { + UsingExpression("x is Type? t and (1, 2)"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.AndPattern); + { + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "t"); + } + } + N(SyntaxKind.AndKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PositionalPatternClause); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "2"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ConjunctivePattern_ConditionalExpressionInsteadOfNullableType5() + { + UsingExpression("x is Type? and []", + // (1,17): error CS0443: Syntax error; value expected + // x is Type? and [] + Diagnostic(ErrorCode.ERR_ValueExpected, "]").WithLocation(1, 17), + // (1,18): error CS1003: Syntax error, ':' expected + // x is Type? and [] + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(":").WithLocation(1, 18), + // (1,18): error CS1733: Expected expression + // x is Type? and [] + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 18)); + + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.ElementAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "and"); + } + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + M(SyntaxKind.Argument); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + M(SyntaxKind.ColonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ConjunctivePattern_ConditionalExpressionInsteadOfNullableType5_2() + { + UsingExpression("x is Type ? dict[key] : default"); + + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IsExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.ElementAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "dict"); + } + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "key"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.DefaultLiteralExpression); + { + N(SyntaxKind.DefaultKeyword); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ConjunctivePattern_NullableType6() + { + UsingExpression("x is Type? t and []"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.AndPattern); + { + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "t"); + } + } + N(SyntaxKind.AndKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.CloseBracketToken); + } + } + } + EOF(); + } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests2.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests2.cs index ddc8174b9dbe3..df04aa31a3fa9 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests2.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests2.cs @@ -4,6 +4,7 @@ using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; using Xunit; using Xunit.Abstractions; @@ -1361,5 +1362,388 @@ public void MissingClosingAngleBracket07() } #endregion + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ExtendedPropertySubpattern_NullableType1() + { + UsingExpression("e is { Prop: Type? }"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Prop"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ExtendedPropertySubpattern_NullableType2() + { + UsingExpression("e is { Prop: Type? propVal }"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Prop"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "propVal"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ExtendedPropertySubpattern_NullableType3() + { + UsingExpression("e is { Prop: Type? propVal, Prop2: int? val2 }"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Prop"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "propVal"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Prop2"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "val2"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ExtendedPropertySubpattern_NullableType4() + { + UsingExpression("e is { Prop: Type? propVal Prop2: int? val2 }", + // (1,28): error CS1003: Syntax error, ',' expected + // e is { Prop: Type? propVal Prop2: int? val2 } + Diagnostic(ErrorCode.ERR_SyntaxError, "Prop2").WithArguments(",").WithLocation(1, 28)); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Prop"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "propVal"); + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Prop2"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "val2"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ExtendedPropertySubpattern_NullableType5() + { + UsingExpression("e is { Prop: Type? or AnotherType? }"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Prop"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.OrPattern); + { + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + } + N(SyntaxKind.OrKeyword); + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "AnotherType"); + } + N(SyntaxKind.QuestionToken); + } + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void ExtendedPropertySubpattern_NullableType6() + { + UsingExpression("e is { Prop: Type? t or AnotherType? a }"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Prop"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.OrPattern); + { + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "t"); + } + } + N(SyntaxKind.OrKeyword); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "AnotherType"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + } + EOF(); + } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests_ListPatterns.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests_ListPatterns.cs index 0bcd4870ba5af..422e1f3cb456f 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests_ListPatterns.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests_ListPatterns.cs @@ -4,6 +4,7 @@ using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; using Xunit; using Xunit.Abstractions; @@ -767,7 +768,7 @@ public void SlicePattern_11() { UsingExpression(@"c is [var x ..]", // (1,13): error CS1003: Syntax error, ',' expected - // c is {var x ..} + // c is [var x ..] Diagnostic(ErrorCode.ERR_SyntaxError, "..").WithArguments(",").WithLocation(1, 13)); N(SyntaxKind.IsPatternExpression); @@ -835,7 +836,7 @@ public void SlicePattern_13() { UsingExpression(@"c is [[]..]", // (1,9): error CS1003: Syntax error, ',' expected - // c is {{}..} + // c is [[]..] Diagnostic(ErrorCode.ERR_SyntaxError, "..").WithArguments(",").WithLocation(1, 9)); N(SyntaxKind.IsPatternExpression); @@ -1098,6 +1099,508 @@ public void SlicePattern_19() } EOF(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void SlicePattern_20() + { + UsingExpression(@"c is [.. string?]"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.SlicePattern); + { + N(SyntaxKind.DotDotToken); + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.QuestionToken); + } + } + } + N(SyntaxKind.CloseBracketToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void SlicePattern_21() + { + UsingExpression(@"c is [.. string? slice]"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.SlicePattern); + { + N(SyntaxKind.DotDotToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "slice"); + } + } + } + N(SyntaxKind.CloseBracketToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void SlicePattern_22() + { + UsingExpression(@"c is [.. string? slice, ')']"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.SlicePattern); + { + N(SyntaxKind.DotDotToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "slice"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.CharacterLiteralExpression); + { + N(SyntaxKind.CharacterLiteralToken); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void SlicePattern_23() + { + UsingExpression(@"c is [.. string? slice ')']", + // (1,24): error CS1003: Syntax error, ',' expected + // c is [.. string? slice ')'] + Diagnostic(ErrorCode.ERR_SyntaxError, "')'").WithArguments(",").WithLocation(1, 24)); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.SlicePattern); + { + N(SyntaxKind.DotDotToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "slice"); + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.CharacterLiteralExpression); + { + N(SyntaxKind.CharacterLiteralToken); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void SlicePattern_24() + { + UsingExpression(@"c is [.. string[]? slice "")""]", + // (1,26): error CS1003: Syntax error, ',' expected + // c is [.. string[]? slice ")"] + Diagnostic(ErrorCode.ERR_SyntaxError, @""")""").WithArguments(",").WithLocation(1, 26)); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.SlicePattern); + { + N(SyntaxKind.DotDotToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "slice"); + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.StringLiteralExpression); + { + N(SyntaxKind.StringLiteralToken, "\")\""); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void SlicePattern_25() + { + UsingExpression(@"c is [.. int[]? slice 5]", + // (1,23): error CS1003: Syntax error, ',' expected + // c is [.. int[]? slice 5] + Diagnostic(ErrorCode.ERR_SyntaxError, "5").WithArguments(",").WithLocation(1, 23)); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.SlicePattern); + { + N(SyntaxKind.DotDotToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + 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.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "slice"); + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "5"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void SlicePattern_26() + { + UsingExpression(@"c is [.. int[]? slice int i]", + // (1,23): error CS1003: Syntax error, ',' expected + // c is [.. int[]? slice int i] + Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments(",").WithLocation(1, 23)); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.SlicePattern); + { + N(SyntaxKind.DotDotToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + 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.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "slice"); + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "i"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void SlicePattern_27() + { + UsingExpression(@"c is [.. string[]? slice string s]", + // (1,26): error CS1003: Syntax error, ',' expected + // c is [.. string[]? slice string s] + Diagnostic(ErrorCode.ERR_SyntaxError, "string").WithArguments(",").WithLocation(1, 26)); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.SlicePattern); + { + N(SyntaxKind.DotDotToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "slice"); + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "s"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void SlicePattern_28() + { + UsingExpression(@"c is [.. char[]? slice char ch]", + // (1,24): error CS1003: Syntax error, ',' expected + // c is [.. char[]? slice char ch] + Diagnostic(ErrorCode.ERR_SyntaxError, "char").WithArguments(",").WithLocation(1, 24)); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.SlicePattern); + { + N(SyntaxKind.DotDotToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.CharKeyword); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "slice"); + } + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.CharKeyword); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "ch"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + EOF(); + } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs index d94f42dd3cfed..1ca8465151f27 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs @@ -5488,6 +5488,329 @@ public void ParseSwitchStatementWithUnclosedPatternAndArrow() EOF(); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestSwitchStatementWithNullableTypeInPattern1() + { + UsingStatement(""" + switch (obj) + { + case Type?: + break; + } + """); + + N(SyntaxKind.SwitchStatement); + { + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchSection); + { + N(SyntaxKind.CasePatternSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestSwitchStatementWithNullableTypeInPattern2() + { + UsingStatement(""" + switch (obj) + { + case Type? varName: + break; + } + """, + // (3,24): error CS1525: Invalid expression term 'break' + // case Type? varName: + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("break").WithLocation(3, 24), + // (3,24): error CS1003: Syntax error, ':' expected + // case Type? varName: + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(":").WithLocation(3, 24)); + + N(SyntaxKind.SwitchStatement); + { + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchSection); + { + N(SyntaxKind.CaseSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "varName"); + } + N(SyntaxKind.ColonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.ColonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestSwitchStatementWithNullableTypeInPattern3() + { + UsingStatement(""" + switch (obj) + { + case Type? when x > 0: + break; + } + """); + + N(SyntaxKind.SwitchStatement); + { + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchSection); + { + N(SyntaxKind.CasePatternSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + } + N(SyntaxKind.WhenClause); + { + N(SyntaxKind.WhenKeyword); + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.GreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestSwitchStatementWithNullableTypeInPattern4() + { + UsingStatement(""" + switch (obj) + { + case Type? varName when x > 0: + break; + } + """); + + N(SyntaxKind.SwitchStatement); + { + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchSection); + { + N(SyntaxKind.CasePatternSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "varName"); + } + } + N(SyntaxKind.WhenClause); + { + N(SyntaxKind.WhenKeyword); + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.GreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestSwitchStatementWithNullableTypeInPattern5() + { + UsingStatement(""" + switch (obj) + { + case (Type? when) when x > 0: + break; + } + """); + + N(SyntaxKind.SwitchStatement); + { + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchSection); + { + N(SyntaxKind.CasePatternSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.ParenthesizedPattern); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "when"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.WhenClause); + { + N(SyntaxKind.WhenKeyword); + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.GreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + private sealed class TokenAndTriviaWalker : CSharpSyntaxWalker { public int Tokens; diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/SwitchExpressionParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/SwitchExpressionParsingTests.cs index 0f33ab868b05d..77f90db34eb36 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/SwitchExpressionParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/SwitchExpressionParsingTests.cs @@ -4231,4 +4231,658 @@ public void TestIncompleteSwitchExpression() } EOF(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestNullableTypeInPattern1() + { + UsingExpression(""" + obj switch + { + Type? => 1 + } + """); + + N(SyntaxKind.SwitchExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchExpressionArm); + { + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestNullableTypeInPattern1_Colon() + { + UsingExpression(""" + obj switch + { + Type? : 1 + } + """, + // (3,11): error CS1003: Syntax error, '=>' expected + // Type? : 1 + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments("=>").WithLocation(3, 11)); + + N(SyntaxKind.SwitchExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchExpressionArm); + { + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + } + M(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestNullableTypeInPattern2() + { + UsingExpression(""" + obj switch + { + Type? varName => 1 + } + """, + // (3,9): error CS1003: Syntax error, '=>' expected + // Type? varName => 1 + Diagnostic(ErrorCode.ERR_SyntaxError, "?").WithArguments("=>").WithLocation(3, 9), + // (3,9): error CS1525: Invalid expression term '?' + // Type? varName => 1 + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "?").WithArguments("?").WithLocation(3, 9), + // (3,23): error CS1003: Syntax error, ':' expected + // Type? varName => 1 + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(":").WithLocation(3, 23), + // (3,23): error CS1525: Invalid expression term '}' + // Type? varName => 1 + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(3, 23)); + + N(SyntaxKind.SwitchExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchExpressionArm); + { + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + M(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.ConditionalExpression); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.SimpleLambdaExpression); + { + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierToken, "varName"); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + M(SyntaxKind.ColonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestNullableTypeInPattern2_Colon() + { + UsingExpression(""" + obj switch + { + Type? varName : 1 + } + """, + // (3,9): error CS1003: Syntax error, '=>' expected + // Type? varName : 1 + Diagnostic(ErrorCode.ERR_SyntaxError, "?").WithArguments("=>").WithLocation(3, 9), + // (3,9): error CS1525: Invalid expression term '?' + // Type? varName : 1 + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "?").WithArguments("?").WithLocation(3, 9)); + + N(SyntaxKind.SwitchExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchExpressionArm); + { + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + M(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.ConditionalExpression); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "varName"); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestNullableTypeInPattern3() + { + UsingExpression(""" + obj switch + { + Type? when x > 0 => 1 + } + """); + + N(SyntaxKind.SwitchExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchExpressionArm); + { + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + } + N(SyntaxKind.WhenClause); + { + N(SyntaxKind.WhenKeyword); + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.GreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestNullableTypeInPattern3_Colon() + { + UsingExpression(""" + obj switch + { + Type? when x > 0 : 1 + } + """, + // (3,22): error CS1003: Syntax error, '=>' expected + // Type? when x > 0 : 1 + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments("=>").WithLocation(3, 22)); + + N(SyntaxKind.SwitchExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchExpressionArm); + { + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + } + N(SyntaxKind.WhenClause); + { + N(SyntaxKind.WhenKeyword); + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.GreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + M(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestNullableTypeInPattern4() + { + UsingExpression(""" + obj switch + { + Type? varName when x > 0 => 1 + } + """); + + N(SyntaxKind.SwitchExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchExpressionArm); + { + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "varName"); + } + } + N(SyntaxKind.WhenClause); + { + N(SyntaxKind.WhenKeyword); + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.GreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestNullableTypeInPattern4_Colon() + { + UsingExpression(""" + obj switch + { + Type? varName when x > 0 : 1 + } + """, + // (3,30): error CS1003: Syntax error, '=>' expected + // Type? varName when x > 0 : 1 + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments("=>").WithLocation(3, 30)); + + N(SyntaxKind.SwitchExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchExpressionArm); + { + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "varName"); + } + } + N(SyntaxKind.WhenClause); + { + N(SyntaxKind.WhenKeyword); + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.GreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + M(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestNullableTypeInPattern5() + { + UsingExpression(""" + obj switch + { + (Type? when) when x > 0 => 1 + } + """); + + N(SyntaxKind.SwitchExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchExpressionArm); + { + N(SyntaxKind.ParenthesizedPattern); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "when"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.WhenClause); + { + N(SyntaxKind.WhenKeyword); + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.GreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestNullableTypeInPattern5_Colon() + { + UsingExpression(""" + obj switch + { + (Type? when) when x > 0 : 1 + } + """, + // (3,29): error CS1003: Syntax error, '=>' expected + // (Type? when) when x > 0 : 1 + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments("=>").WithLocation(3, 29)); + + N(SyntaxKind.SwitchExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchExpressionArm); + { + N(SyntaxKind.ParenthesizedPattern); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.DeclarationPattern); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "when"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.WhenClause); + { + N(SyntaxKind.WhenKeyword); + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.GreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + M(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72720")] + public void TestConditionalExpressionAsPattern() + { + UsingExpression(""" + obj switch + { + (flag ? a : b) => 1 + } + """); + + N(SyntaxKind.SwitchExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchExpressionArm); + { + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.ParenthesizedExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.ConditionalExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "flag"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } } From 5704d856945422b670f5686098ec768f66e0689d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 8 Jul 2024 14:44:48 -0700 Subject: [PATCH 14/17] Fix msbuild issue --- Directory.Build.rsp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.rsp b/Directory.Build.rsp index d2d2d1d2f4e8e..424936667b087 100644 --- a/Directory.Build.rsp +++ b/Directory.Build.rsp @@ -1 +1 @@ -# This file intentionally left blank to avoid accidental import during build. +-p:_IsDisjointMSBuildVersion=false \ No newline at end of file From a873d9b7a8817eb5e5bd2d8cbb0ea8ffbd678537 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Mon, 8 Jul 2024 15:15:05 -0700 Subject: [PATCH 15/17] Remove fallback options from IdeAnalyzerOptions (#74235) --- .../CSharpAnalyzerOptionsProvider.cs | 131 +++++++---------- .../Core/Analyzers/AnalyzerOptionsProvider.cs | 136 ++++++------------ .../CodeFixes/AnalyzerOptionsProviders.cs | 5 +- .../VisualBasicAnalyzerOptionsProvider.vb | 46 ++---- .../CSharpTest/Formatting/CodeCleanupTests.cs | 13 +- .../DiagnosticIncrementalAnalyzer.Executor.cs | 4 +- ...alyzer.InProcOrRemoteHostAnalyzerRunner.cs | 2 +- ...IncrementalAnalyzer_IncrementalAnalyzer.cs | 2 +- .../Options/IdeAnalyzerOptionsStorage.cs | 6 +- .../Formatting/FormatDocumentHandler.cs | 1 - .../Formatting/FormatDocumentRangeHandler.cs | 1 - ...alStudioDiagnosticAnalyzerExecutorTests.cs | 53 ++----- .../Diagnostics/WorkspaceAnalyzerOptions.cs | 5 + .../Workspaces/TestWorkspace`1.cs | 10 ++ .../Core/Diagnostics/IdeAnalyzerOptions.cs | 21 +-- 15 files changed, 155 insertions(+), 281 deletions(-) diff --git a/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs b/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs index dedfa4b189a42..4413bf53ca539 100644 --- a/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs +++ b/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs @@ -6,7 +6,6 @@ using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeGeneration; using Microsoft.CodeAnalysis.CSharp.CodeStyle; -using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.CSharp.Simplification; using Microsoft.CodeAnalysis.Options; @@ -15,115 +14,87 @@ namespace Microsoft.CodeAnalysis.Diagnostics; /// /// Provides C# analyzers a convenient access to editorconfig options with fallback to IDE default values. /// -internal readonly struct CSharpAnalyzerOptionsProvider(IOptionsReader options, IdeAnalyzerOptions fallbackOptions) +internal readonly struct CSharpAnalyzerOptionsProvider(IOptionsReader options) { - /// - /// Document editorconfig options. - /// - private readonly IOptionsReader _options = options; - - /// - /// Fallback options - the default options in Code Style layer. - /// - private readonly IdeAnalyzerOptions _fallbackOptions = fallbackOptions; - - public CSharpAnalyzerOptionsProvider(IOptionsReader options, AnalyzerOptions fallbackOptions) - : this(options, fallbackOptions.GetIdeOptions()) - { - } + private IOptionsReader Options => options; // SimplifierOptions - public CodeStyleOption2 VarForBuiltInTypes => GetOption(CSharpCodeStyleOptions.VarForBuiltInTypes, FallbackSimplifierOptions.VarForBuiltInTypes); - public CodeStyleOption2 VarWhenTypeIsApparent => GetOption(CSharpCodeStyleOptions.VarWhenTypeIsApparent, FallbackSimplifierOptions.VarWhenTypeIsApparent); - public CodeStyleOption2 VarElsewhere => GetOption(CSharpCodeStyleOptions.VarElsewhere, FallbackSimplifierOptions.VarElsewhere); - public CodeStyleOption2 PreferSimpleDefaultExpression => GetOption(CSharpCodeStyleOptions.PreferSimpleDefaultExpression, FallbackSimplifierOptions.PreferSimpleDefaultExpression); - public CodeStyleOption2 AllowEmbeddedStatementsOnSameLine => GetOption(CSharpCodeStyleOptions.AllowEmbeddedStatementsOnSameLine, FallbackSimplifierOptions.AllowEmbeddedStatementsOnSameLine); - public CodeStyleOption2 PreferThrowExpression => GetOption(CSharpCodeStyleOptions.PreferThrowExpression, FallbackSimplifierOptions.PreferThrowExpression); - public CodeStyleOption2 PreferBraces => GetOption(CSharpCodeStyleOptions.PreferBraces, FallbackSimplifierOptions.PreferBraces); + public CodeStyleOption2 VarForBuiltInTypes => GetOption(CSharpCodeStyleOptions.VarForBuiltInTypes); + public CodeStyleOption2 VarWhenTypeIsApparent => GetOption(CSharpCodeStyleOptions.VarWhenTypeIsApparent); + public CodeStyleOption2 VarElsewhere => GetOption(CSharpCodeStyleOptions.VarElsewhere); + public CodeStyleOption2 PreferSimpleDefaultExpression => GetOption(CSharpCodeStyleOptions.PreferSimpleDefaultExpression); + public CodeStyleOption2 AllowEmbeddedStatementsOnSameLine => GetOption(CSharpCodeStyleOptions.AllowEmbeddedStatementsOnSameLine); + public CodeStyleOption2 PreferThrowExpression => GetOption(CSharpCodeStyleOptions.PreferThrowExpression); + public CodeStyleOption2 PreferBraces => GetOption(CSharpCodeStyleOptions.PreferBraces); internal CSharpSimplifierOptions GetSimplifierOptions() - => new(_options, FallbackSimplifierOptions); + => new(options, fallbackOptions: null); // SyntaxFormattingOptions - public CodeStyleOption2 NamespaceDeclarations => GetOption(CSharpCodeStyleOptions.NamespaceDeclarations, FallbackSyntaxFormattingOptions.NamespaceDeclarations); - public CodeStyleOption2 PreferTopLevelStatements => GetOption(CSharpCodeStyleOptions.PreferTopLevelStatements, FallbackSyntaxFormattingOptions.PreferTopLevelStatements); + public CodeStyleOption2 NamespaceDeclarations => GetOption(CSharpCodeStyleOptions.NamespaceDeclarations); + public CodeStyleOption2 PreferTopLevelStatements => GetOption(CSharpCodeStyleOptions.PreferTopLevelStatements); // AddImportPlacementOptions - public CodeStyleOption2 UsingDirectivePlacement => GetOption(CSharpCodeStyleOptions.PreferredUsingDirectivePlacement, FallbackAddImportPlacementOptions.UsingDirectivePlacement); + public CodeStyleOption2 UsingDirectivePlacement => GetOption(CSharpCodeStyleOptions.PreferredUsingDirectivePlacement); // CodeStyleOptions - public CodeStyleOption2 ImplicitObjectCreationWhenTypeIsApparent => GetOption(CSharpCodeStyleOptions.ImplicitObjectCreationWhenTypeIsApparent, FallbackCodeStyleOptions.ImplicitObjectCreationWhenTypeIsApparent); - public CodeStyleOption2 PreferNullCheckOverTypeCheck => GetOption(CSharpCodeStyleOptions.PreferNullCheckOverTypeCheck, FallbackCodeStyleOptions.PreferNullCheckOverTypeCheck); - public CodeStyleOption2 AllowBlankLinesBetweenConsecutiveBraces => GetOption(CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, FallbackCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces); - public CodeStyleOption2 AllowBlankLineAfterColonInConstructorInitializer => GetOption(CSharpCodeStyleOptions.AllowBlankLineAfterColonInConstructorInitializer, FallbackCodeStyleOptions.AllowBlankLineAfterColonInConstructorInitializer); - public CodeStyleOption2 AllowBlankLineAfterTokenInArrowExpressionClause => GetOption(CSharpCodeStyleOptions.AllowBlankLineAfterTokenInArrowExpressionClause, FallbackCodeStyleOptions.AllowBlankLineAfterTokenInArrowExpressionClause); - public CodeStyleOption2 AllowBlankLineAfterTokenInConditionalExpression => GetOption(CSharpCodeStyleOptions.AllowBlankLineAfterTokenInConditionalExpression, FallbackCodeStyleOptions.AllowBlankLineAfterTokenInConditionalExpression); - public CodeStyleOption2 PreferConditionalDelegateCall => GetOption(CSharpCodeStyleOptions.PreferConditionalDelegateCall, FallbackCodeStyleOptions.PreferConditionalDelegateCall); - public CodeStyleOption2 PreferSwitchExpression => GetOption(CSharpCodeStyleOptions.PreferSwitchExpression, FallbackCodeStyleOptions.PreferSwitchExpression); - public CodeStyleOption2 PreferPatternMatching => GetOption(CSharpCodeStyleOptions.PreferPatternMatching, FallbackCodeStyleOptions.PreferPatternMatching); - public CodeStyleOption2 PreferPatternMatchingOverAsWithNullCheck => GetOption(CSharpCodeStyleOptions.PreferPatternMatchingOverAsWithNullCheck, FallbackCodeStyleOptions.PreferPatternMatchingOverAsWithNullCheck); - public CodeStyleOption2 PreferPatternMatchingOverIsWithCastCheck => GetOption(CSharpCodeStyleOptions.PreferPatternMatchingOverIsWithCastCheck, FallbackCodeStyleOptions.PreferPatternMatchingOverIsWithCastCheck); - public CodeStyleOption2 PreferNotPattern => GetOption(CSharpCodeStyleOptions.PreferNotPattern, FallbackCodeStyleOptions.PreferNotPattern); - public CodeStyleOption2 PreferExtendedPropertyPattern => GetOption(CSharpCodeStyleOptions.PreferExtendedPropertyPattern, FallbackCodeStyleOptions.PreferExtendedPropertyPattern); - public CodeStyleOption2 PreferInlinedVariableDeclaration => GetOption(CSharpCodeStyleOptions.PreferInlinedVariableDeclaration, FallbackCodeStyleOptions.PreferInlinedVariableDeclaration); - public CodeStyleOption2 PreferDeconstructedVariableDeclaration => GetOption(CSharpCodeStyleOptions.PreferDeconstructedVariableDeclaration, FallbackCodeStyleOptions.PreferDeconstructedVariableDeclaration); - public CodeStyleOption2 PreferIndexOperator => GetOption(CSharpCodeStyleOptions.PreferIndexOperator, FallbackCodeStyleOptions.PreferIndexOperator); - public CodeStyleOption2 PreferRangeOperator => GetOption(CSharpCodeStyleOptions.PreferRangeOperator, FallbackCodeStyleOptions.PreferRangeOperator); - public CodeStyleOption2 PreferUtf8StringLiterals => GetOption(CSharpCodeStyleOptions.PreferUtf8StringLiterals, FallbackCodeStyleOptions.PreferUtf8StringLiterals); - public CodeStyleOption2 PreferredModifierOrder => GetOption(CSharpCodeStyleOptions.PreferredModifierOrder, FallbackCodeStyleOptions.PreferredModifierOrder); - public CodeStyleOption2 PreferSimpleUsingStatement => GetOption(CSharpCodeStyleOptions.PreferSimpleUsingStatement, FallbackCodeStyleOptions.PreferSimpleUsingStatement); - public CodeStyleOption2 PreferLocalOverAnonymousFunction => GetOption(CSharpCodeStyleOptions.PreferLocalOverAnonymousFunction, FallbackCodeStyleOptions.PreferLocalOverAnonymousFunction); - public CodeStyleOption2 PreferTupleSwap => GetOption(CSharpCodeStyleOptions.PreferTupleSwap, FallbackCodeStyleOptions.PreferTupleSwap); - public CodeStyleOption2 UnusedValueExpressionStatement => GetOption(CSharpCodeStyleOptions.UnusedValueExpressionStatement, FallbackCodeStyleOptions.UnusedValueExpressionStatement); - public CodeStyleOption2 UnusedValueAssignment => GetOption(CSharpCodeStyleOptions.UnusedValueAssignment, FallbackCodeStyleOptions.UnusedValueAssignment); - public CodeStyleOption2 PreferMethodGroupConversion => GetOption(CSharpCodeStyleOptions.PreferMethodGroupConversion, FallbackCodeStyleOptions.PreferMethodGroupConversion); - public CodeStyleOption2 PreferPrimaryConstructors => GetOption(CSharpCodeStyleOptions.PreferPrimaryConstructors, FallbackCodeStyleOptions.PreferPrimaryConstructors); - public CodeStyleOption2 PreferSystemThreadingLock => GetOption(CSharpCodeStyleOptions.PreferSystemThreadingLock, FallbackCodeStyleOptions.PreferSystemThreadingLock); + public CodeStyleOption2 ImplicitObjectCreationWhenTypeIsApparent => GetOption(CSharpCodeStyleOptions.ImplicitObjectCreationWhenTypeIsApparent); + public CodeStyleOption2 PreferNullCheckOverTypeCheck => GetOption(CSharpCodeStyleOptions.PreferNullCheckOverTypeCheck); + public CodeStyleOption2 AllowBlankLinesBetweenConsecutiveBraces => GetOption(CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces); + public CodeStyleOption2 AllowBlankLineAfterColonInConstructorInitializer => GetOption(CSharpCodeStyleOptions.AllowBlankLineAfterColonInConstructorInitializer); + public CodeStyleOption2 AllowBlankLineAfterTokenInArrowExpressionClause => GetOption(CSharpCodeStyleOptions.AllowBlankLineAfterTokenInArrowExpressionClause); + public CodeStyleOption2 AllowBlankLineAfterTokenInConditionalExpression => GetOption(CSharpCodeStyleOptions.AllowBlankLineAfterTokenInConditionalExpression); + public CodeStyleOption2 PreferConditionalDelegateCall => GetOption(CSharpCodeStyleOptions.PreferConditionalDelegateCall); + public CodeStyleOption2 PreferSwitchExpression => GetOption(CSharpCodeStyleOptions.PreferSwitchExpression); + public CodeStyleOption2 PreferPatternMatching => GetOption(CSharpCodeStyleOptions.PreferPatternMatching); + public CodeStyleOption2 PreferPatternMatchingOverAsWithNullCheck => GetOption(CSharpCodeStyleOptions.PreferPatternMatchingOverAsWithNullCheck); + public CodeStyleOption2 PreferPatternMatchingOverIsWithCastCheck => GetOption(CSharpCodeStyleOptions.PreferPatternMatchingOverIsWithCastCheck); + public CodeStyleOption2 PreferNotPattern => GetOption(CSharpCodeStyleOptions.PreferNotPattern); + public CodeStyleOption2 PreferExtendedPropertyPattern => GetOption(CSharpCodeStyleOptions.PreferExtendedPropertyPattern); + public CodeStyleOption2 PreferInlinedVariableDeclaration => GetOption(CSharpCodeStyleOptions.PreferInlinedVariableDeclaration); + public CodeStyleOption2 PreferDeconstructedVariableDeclaration => GetOption(CSharpCodeStyleOptions.PreferDeconstructedVariableDeclaration); + public CodeStyleOption2 PreferIndexOperator => GetOption(CSharpCodeStyleOptions.PreferIndexOperator); + public CodeStyleOption2 PreferRangeOperator => GetOption(CSharpCodeStyleOptions.PreferRangeOperator); + public CodeStyleOption2 PreferUtf8StringLiterals => GetOption(CSharpCodeStyleOptions.PreferUtf8StringLiterals); + public CodeStyleOption2 PreferredModifierOrder => GetOption(CSharpCodeStyleOptions.PreferredModifierOrder); + public CodeStyleOption2 PreferSimpleUsingStatement => GetOption(CSharpCodeStyleOptions.PreferSimpleUsingStatement); + public CodeStyleOption2 PreferLocalOverAnonymousFunction => GetOption(CSharpCodeStyleOptions.PreferLocalOverAnonymousFunction); + public CodeStyleOption2 PreferTupleSwap => GetOption(CSharpCodeStyleOptions.PreferTupleSwap); + public CodeStyleOption2 UnusedValueExpressionStatement => GetOption(CSharpCodeStyleOptions.UnusedValueExpressionStatement); + public CodeStyleOption2 UnusedValueAssignment => GetOption(CSharpCodeStyleOptions.UnusedValueAssignment); + public CodeStyleOption2 PreferMethodGroupConversion => GetOption(CSharpCodeStyleOptions.PreferMethodGroupConversion); + public CodeStyleOption2 PreferPrimaryConstructors => GetOption(CSharpCodeStyleOptions.PreferPrimaryConstructors); + public CodeStyleOption2 PreferSystemThreadingLock => GetOption(CSharpCodeStyleOptions.PreferSystemThreadingLock); // CodeGenerationOptions internal CSharpCodeGenerationOptions GetCodeGenerationOptions() - => new(_options, FallbackCodeGenerationOptions); - - public CodeStyleOption2 PreferExpressionBodiedLambdas => GetOption(CSharpCodeStyleOptions.PreferExpressionBodiedLambdas, FallbackCodeStyleOptions.PreferExpressionBodiedLambdas); - public CodeStyleOption2 PreferReadOnlyStruct => GetOption(CSharpCodeStyleOptions.PreferReadOnlyStruct, FallbackCodeStyleOptions.PreferReadOnlyStruct); - public CodeStyleOption2 PreferReadOnlyStructMember => GetOption(CSharpCodeStyleOptions.PreferReadOnlyStructMember, FallbackCodeStyleOptions.PreferReadOnlyStructMember); - public CodeStyleOption2 PreferStaticLocalFunction => GetOption(CSharpCodeStyleOptions.PreferStaticLocalFunction, FallbackCodeStyleOptions.PreferStaticLocalFunction); - public CodeStyleOption2 PreferStaticAnonymousFunction => GetOption(CSharpCodeStyleOptions.PreferStaticAnonymousFunction, FallbackCodeStyleOptions.PreferStaticAnonymousFunction); - - private TValue GetOption(Option2 option, TValue defaultValue) - => _options.GetOption(option, defaultValue); - - private CSharpIdeCodeStyleOptions FallbackCodeStyleOptions - => (CSharpIdeCodeStyleOptions?)_fallbackOptions.CodeStyleOptions ?? CSharpIdeCodeStyleOptions.Default; - - private CSharpSimplifierOptions FallbackSimplifierOptions - => (CSharpSimplifierOptions?)_fallbackOptions.CleanupOptions?.SimplifierOptions ?? CSharpSimplifierOptions.Default; - - private CSharpSyntaxFormattingOptions FallbackSyntaxFormattingOptions - => (CSharpSyntaxFormattingOptions?)_fallbackOptions.CleanupOptions?.FormattingOptions ?? CSharpSyntaxFormattingOptions.Default; + => new(options, fallbackOptions: null); - private AddImportPlacementOptions FallbackAddImportPlacementOptions - => _fallbackOptions.CleanupOptions?.AddImportOptions ?? AddImportPlacementOptions.Default; + public CodeStyleOption2 PreferExpressionBodiedLambdas => GetOption(CSharpCodeStyleOptions.PreferExpressionBodiedLambdas); + public CodeStyleOption2 PreferReadOnlyStruct => GetOption(CSharpCodeStyleOptions.PreferReadOnlyStruct); + public CodeStyleOption2 PreferReadOnlyStructMember => GetOption(CSharpCodeStyleOptions.PreferReadOnlyStructMember); + public CodeStyleOption2 PreferStaticLocalFunction => GetOption(CSharpCodeStyleOptions.PreferStaticLocalFunction); + public CodeStyleOption2 PreferStaticAnonymousFunction => GetOption(CSharpCodeStyleOptions.PreferStaticAnonymousFunction); - private CSharpCodeGenerationOptions FallbackCodeGenerationOptions - => (CSharpCodeGenerationOptions?)_fallbackOptions.GenerationOptions ?? CSharpCodeGenerationOptions.Default; + private TValue GetOption(Option2 option) + => options.GetOption(option); public static explicit operator CSharpAnalyzerOptionsProvider(AnalyzerOptionsProvider provider) - => new(provider.GetAnalyzerConfigOptions(), provider.GetFallbackOptions()); + => new(provider.GetAnalyzerConfigOptions()); public static implicit operator AnalyzerOptionsProvider(CSharpAnalyzerOptionsProvider provider) - => new(provider._options, LanguageNames.CSharp, provider._fallbackOptions); + => new(provider.Options, LanguageNames.CSharp); } internal static class CSharpAnalyzerOptionsProviders { public static CSharpAnalyzerOptionsProvider GetCSharpAnalyzerOptions(this AnalyzerOptions options, SyntaxTree syntaxTree) - => new(options.AnalyzerConfigOptionsProvider.GetOptions(syntaxTree).GetOptionsReader(), options); + => new(options.AnalyzerConfigOptionsProvider.GetOptions(syntaxTree).GetOptionsReader()); public static CSharpAnalyzerOptionsProvider GetCSharpAnalyzerOptions(this SemanticModelAnalysisContext context) => GetCSharpAnalyzerOptions(context.Options, context.SemanticModel.SyntaxTree); diff --git a/src/Analyzers/Core/Analyzers/AnalyzerOptionsProvider.cs b/src/Analyzers/Core/Analyzers/AnalyzerOptionsProvider.cs index 868cb3b170b66..9ff472e4bf746 100644 --- a/src/Analyzers/Core/Analyzers/AnalyzerOptionsProvider.cs +++ b/src/Analyzers/Core/Analyzers/AnalyzerOptionsProvider.cs @@ -2,118 +2,83 @@ // 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.Text; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.CodeStyle; -using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Simplification; namespace Microsoft.CodeAnalysis.Diagnostics; /// -/// Provides C# and VB analyzers a convenient access to common editorconfig options with fallback to IDE default values. +/// Provides C# and VB analyzers a convenient access to common editorconfig options. /// -internal readonly struct AnalyzerOptionsProvider(IOptionsReader options, string language, IdeAnalyzerOptions fallbackOptions) +internal readonly struct AnalyzerOptionsProvider(IOptionsReader options, string language) { - /// - /// Document editorconfig options. - /// - private readonly IOptionsReader _options = options; - - /// - /// Fallback options - the default options in Code Style layer. - /// - private readonly IdeAnalyzerOptions _fallbackOptions = fallbackOptions; - - private readonly string _language = language; - - public AnalyzerOptionsProvider(IOptionsReader options, string language, AnalyzerOptions fallbackOptions) - : this(options, language, fallbackOptions.GetIdeOptions()) - { - } - // SimplifierOptions - public CodeStyleOption2 QualifyFieldAccess => GetOption(CodeStyleOptions2.QualifyFieldAccess, FallbackSimplifierOptions.QualifyFieldAccess); - public CodeStyleOption2 QualifyPropertyAccess => GetOption(CodeStyleOptions2.QualifyPropertyAccess, FallbackSimplifierOptions.QualifyPropertyAccess); - public CodeStyleOption2 QualifyMethodAccess => GetOption(CodeStyleOptions2.QualifyMethodAccess, FallbackSimplifierOptions.QualifyMethodAccess); - public CodeStyleOption2 QualifyEventAccess => GetOption(CodeStyleOptions2.QualifyEventAccess, FallbackSimplifierOptions.QualifyEventAccess); - public CodeStyleOption2 PreferPredefinedTypeKeywordInMemberAccess => GetOption(CodeStyleOptions2.PreferIntrinsicPredefinedTypeKeywordInMemberAccess, FallbackSimplifierOptions.PreferPredefinedTypeKeywordInMemberAccess); - public CodeStyleOption2 PreferPredefinedTypeKeywordInDeclaration => GetOption(CodeStyleOptions2.PreferIntrinsicPredefinedTypeKeywordInDeclaration, FallbackSimplifierOptions.PreferPredefinedTypeKeywordInDeclaration); + public CodeStyleOption2 QualifyFieldAccess => GetOption(CodeStyleOptions2.QualifyFieldAccess); + public CodeStyleOption2 QualifyPropertyAccess => GetOption(CodeStyleOptions2.QualifyPropertyAccess); + public CodeStyleOption2 QualifyMethodAccess => GetOption(CodeStyleOptions2.QualifyMethodAccess); + public CodeStyleOption2 QualifyEventAccess => GetOption(CodeStyleOptions2.QualifyEventAccess); + public CodeStyleOption2 PreferPredefinedTypeKeywordInMemberAccess => GetOption(CodeStyleOptions2.PreferIntrinsicPredefinedTypeKeywordInMemberAccess); + public CodeStyleOption2 PreferPredefinedTypeKeywordInDeclaration => GetOption(CodeStyleOptions2.PreferIntrinsicPredefinedTypeKeywordInDeclaration); public SimplifierOptions GetSimplifierOptions(ISimplification simplification) - => simplification.GetSimplifierOptions(_options); + => simplification.GetSimplifierOptions(options); // SyntaxFormattingOptions public SyntaxFormattingOptions GetSyntaxFormattingOptions(ISyntaxFormatting formatting) - => formatting.GetFormattingOptions(_options); + => formatting.GetFormattingOptions(options); // CodeGenerationOptions - public NamingStylePreferences NamingPreferences => GetOption(NamingStyleOptions.NamingPreferences, _fallbackOptions.GenerationOptions?.NamingStyle ?? NamingStylePreferences.Default); + public NamingStylePreferences NamingPreferences => GetOption(NamingStyleOptions.NamingPreferences); // CodeStyleOptions - public CodeStyleOption2 PreferObjectInitializer => GetOption(CodeStyleOptions2.PreferObjectInitializer, FallbackCodeStyleOptions.PreferObjectInitializer); - public CodeStyleOption2 PreferCollectionExpression => GetOption(CodeStyleOptions2.PreferCollectionExpression, FallbackCodeStyleOptions.PreferCollectionExpression); - public CodeStyleOption2 PreferCollectionInitializer => GetOption(CodeStyleOptions2.PreferCollectionInitializer, FallbackCodeStyleOptions.PreferCollectionInitializer); - public CodeStyleOption2 PreferSimplifiedBooleanExpressions => GetOption(CodeStyleOptions2.PreferSimplifiedBooleanExpressions, FallbackCodeStyleOptions.PreferSimplifiedBooleanExpressions); - public OperatorPlacementWhenWrappingPreference OperatorPlacementWhenWrapping => GetOption(CodeStyleOptions2.OperatorPlacementWhenWrapping, FallbackCodeStyleOptions.OperatorPlacementWhenWrapping); - public CodeStyleOption2 PreferCoalesceExpression => GetOption(CodeStyleOptions2.PreferCoalesceExpression, FallbackCodeStyleOptions.PreferCoalesceExpression); - public CodeStyleOption2 PreferNullPropagation => GetOption(CodeStyleOptions2.PreferNullPropagation, FallbackCodeStyleOptions.PreferNullPropagation); - public CodeStyleOption2 PreferExplicitTupleNames => GetOption(CodeStyleOptions2.PreferExplicitTupleNames, FallbackCodeStyleOptions.PreferExplicitTupleNames); - public CodeStyleOption2 PreferAutoProperties => GetOption(CodeStyleOptions2.PreferAutoProperties, FallbackCodeStyleOptions.PreferAutoProperties); - public CodeStyleOption2 PreferInferredTupleNames => GetOption(CodeStyleOptions2.PreferInferredTupleNames, FallbackCodeStyleOptions.PreferInferredTupleNames); - public CodeStyleOption2 PreferInferredAnonymousTypeMemberNames => GetOption(CodeStyleOptions2.PreferInferredAnonymousTypeMemberNames, FallbackCodeStyleOptions.PreferInferredAnonymousTypeMemberNames); - public CodeStyleOption2 PreferIsNullCheckOverReferenceEqualityMethod => GetOption(CodeStyleOptions2.PreferIsNullCheckOverReferenceEqualityMethod, FallbackCodeStyleOptions.PreferIsNullCheckOverReferenceEqualityMethod); - public CodeStyleOption2 PreferConditionalExpressionOverAssignment => GetOption(CodeStyleOptions2.PreferConditionalExpressionOverAssignment, FallbackCodeStyleOptions.PreferConditionalExpressionOverAssignment); - public CodeStyleOption2 PreferConditionalExpressionOverReturn => GetOption(CodeStyleOptions2.PreferConditionalExpressionOverReturn, FallbackCodeStyleOptions.PreferConditionalExpressionOverReturn); - public CodeStyleOption2 PreferCompoundAssignment => GetOption(CodeStyleOptions2.PreferCompoundAssignment, FallbackCodeStyleOptions.PreferCompoundAssignment); - public CodeStyleOption2 PreferSimplifiedInterpolation => GetOption(CodeStyleOptions2.PreferSimplifiedInterpolation, FallbackCodeStyleOptions.PreferSimplifiedInterpolation); + public CodeStyleOption2 PreferObjectInitializer => GetOption(CodeStyleOptions2.PreferObjectInitializer); + public CodeStyleOption2 PreferCollectionExpression => GetOption(CodeStyleOptions2.PreferCollectionExpression); + public CodeStyleOption2 PreferCollectionInitializer => GetOption(CodeStyleOptions2.PreferCollectionInitializer); + public CodeStyleOption2 PreferSimplifiedBooleanExpressions => GetOption(CodeStyleOptions2.PreferSimplifiedBooleanExpressions); + public OperatorPlacementWhenWrappingPreference OperatorPlacementWhenWrapping => GetOption(CodeStyleOptions2.OperatorPlacementWhenWrapping); + public CodeStyleOption2 PreferCoalesceExpression => GetOption(CodeStyleOptions2.PreferCoalesceExpression); + public CodeStyleOption2 PreferNullPropagation => GetOption(CodeStyleOptions2.PreferNullPropagation); + public CodeStyleOption2 PreferExplicitTupleNames => GetOption(CodeStyleOptions2.PreferExplicitTupleNames); + public CodeStyleOption2 PreferAutoProperties => GetOption(CodeStyleOptions2.PreferAutoProperties); + public CodeStyleOption2 PreferInferredTupleNames => GetOption(CodeStyleOptions2.PreferInferredTupleNames); + public CodeStyleOption2 PreferInferredAnonymousTypeMemberNames => GetOption(CodeStyleOptions2.PreferInferredAnonymousTypeMemberNames); + public CodeStyleOption2 PreferIsNullCheckOverReferenceEqualityMethod => GetOption(CodeStyleOptions2.PreferIsNullCheckOverReferenceEqualityMethod); + public CodeStyleOption2 PreferConditionalExpressionOverAssignment => GetOption(CodeStyleOptions2.PreferConditionalExpressionOverAssignment); + public CodeStyleOption2 PreferConditionalExpressionOverReturn => GetOption(CodeStyleOptions2.PreferConditionalExpressionOverReturn); + public CodeStyleOption2 PreferCompoundAssignment => GetOption(CodeStyleOptions2.PreferCompoundAssignment); + public CodeStyleOption2 PreferSimplifiedInterpolation => GetOption(CodeStyleOptions2.PreferSimplifiedInterpolation); public CodeStyleOption2 PreferSystemHashCode => GetOption(CodeStyleOptions2.PreferSystemHashCode); - public CodeStyleOption2 UnusedParameters => GetOption(CodeStyleOptions2.UnusedParameters, FallbackCodeStyleOptions.UnusedParameters); - public CodeStyleOption2 RequireAccessibilityModifiers => GetOption(CodeStyleOptions2.AccessibilityModifiersRequired, FallbackCodeStyleOptions.AccessibilityModifiersRequired); - public CodeStyleOption2 PreferReadonly => GetOption(CodeStyleOptions2.PreferReadonly, FallbackCodeStyleOptions.PreferReadonly); - public CodeStyleOption2 ArithmeticBinaryParentheses => GetOption(CodeStyleOptions2.ArithmeticBinaryParentheses, FallbackCodeStyleOptions.ArithmeticBinaryParentheses); - public CodeStyleOption2 OtherBinaryParentheses => GetOption(CodeStyleOptions2.OtherBinaryParentheses, FallbackCodeStyleOptions.OtherBinaryParentheses); - public CodeStyleOption2 RelationalBinaryParentheses => GetOption(CodeStyleOptions2.RelationalBinaryParentheses, FallbackCodeStyleOptions.RelationalBinaryParentheses); - public CodeStyleOption2 OtherParentheses => GetOption(CodeStyleOptions2.OtherParentheses, FallbackCodeStyleOptions.OtherParentheses); - public CodeStyleOption2 ForEachExplicitCastInSource => GetOption(CodeStyleOptions2.ForEachExplicitCastInSource, FallbackCodeStyleOptions.ForEachExplicitCastInSource); - public CodeStyleOption2 PreferNamespaceAndFolderMatchStructure => GetOption(CodeStyleOptions2.PreferNamespaceAndFolderMatchStructure, FallbackCodeStyleOptions.PreferNamespaceAndFolderMatchStructure); - public CodeStyleOption2 AllowMultipleBlankLines => GetOption(CodeStyleOptions2.AllowMultipleBlankLines, FallbackCodeStyleOptions.AllowMultipleBlankLines); - public CodeStyleOption2 AllowStatementImmediatelyAfterBlock => GetOption(CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, FallbackCodeStyleOptions.AllowStatementImmediatelyAfterBlock); - public string RemoveUnnecessarySuppressionExclusions => GetOption(CodeStyleOptions2.RemoveUnnecessarySuppressionExclusions, FallbackCodeStyleOptions.RemoveUnnecessarySuppressionExclusions); - - public string FileHeaderTemplate => GetOption(CodeStyleOptions2.FileHeaderTemplate, defaultValue: string.Empty); // no fallback IDE option - - private TValue GetOption(Option2 option, TValue defaultValue) - => _options.GetOption(option, defaultValue); + public CodeStyleOption2 UnusedParameters => GetOption(CodeStyleOptions2.UnusedParameters); + public CodeStyleOption2 RequireAccessibilityModifiers => GetOption(CodeStyleOptions2.AccessibilityModifiersRequired); + public CodeStyleOption2 PreferReadonly => GetOption(CodeStyleOptions2.PreferReadonly); + public CodeStyleOption2 ArithmeticBinaryParentheses => GetOption(CodeStyleOptions2.ArithmeticBinaryParentheses); + public CodeStyleOption2 OtherBinaryParentheses => GetOption(CodeStyleOptions2.OtherBinaryParentheses); + public CodeStyleOption2 RelationalBinaryParentheses => GetOption(CodeStyleOptions2.RelationalBinaryParentheses); + public CodeStyleOption2 OtherParentheses => GetOption(CodeStyleOptions2.OtherParentheses); + public CodeStyleOption2 ForEachExplicitCastInSource => GetOption(CodeStyleOptions2.ForEachExplicitCastInSource); + public CodeStyleOption2 PreferNamespaceAndFolderMatchStructure => GetOption(CodeStyleOptions2.PreferNamespaceAndFolderMatchStructure); + public CodeStyleOption2 AllowMultipleBlankLines => GetOption(CodeStyleOptions2.AllowMultipleBlankLines); + public CodeStyleOption2 AllowStatementImmediatelyAfterBlock => GetOption(CodeStyleOptions2.AllowStatementImmediatelyAfterBlock); + public string RemoveUnnecessarySuppressionExclusions => GetOption(CodeStyleOptions2.RemoveUnnecessarySuppressionExclusions); + + public string FileHeaderTemplate => GetOption(CodeStyleOptions2.FileHeaderTemplate); public TValue GetOption(PerLanguageOption2 option) - => _options.GetOption(option, _language); + => options.GetOption(option, language); - private TValue GetOption(PerLanguageOption2 option, TValue defaultValue) - => _options.GetOption(option, _language, defaultValue); - - private IdeCodeStyleOptions FallbackCodeStyleOptions - => _fallbackOptions.CodeStyleOptions ?? IdeCodeStyleOptions.CommonDefaults; - - private SimplifierOptions FallbackSimplifierOptions - => _fallbackOptions.CleanupOptions?.SimplifierOptions ?? SimplifierOptions.CommonDefaults; + private TValue GetOption(Option2 option) + => options.GetOption(option); internal IOptionsReader GetAnalyzerConfigOptions() - => _options; - - internal IdeAnalyzerOptions GetFallbackOptions() - => _fallbackOptions; + => options; } internal static partial class AnalyzerOptionsProviders @@ -126,7 +91,7 @@ public static IdeAnalyzerOptions GetIdeOptions(this AnalyzerOptions options) #endif public static AnalyzerOptionsProvider GetAnalyzerOptions(this AnalyzerOptions analyzerOptions, SyntaxTree syntaxTree) - => new(analyzerOptions.AnalyzerConfigOptionsProvider.GetOptions(syntaxTree).GetOptionsReader(), syntaxTree.Options.Language, analyzerOptions); + => new(analyzerOptions.AnalyzerConfigOptionsProvider.GetOptions(syntaxTree).GetOptionsReader(), syntaxTree.Options.Language); public static AnalyzerOptionsProvider GetAnalyzerOptions(this SemanticModelAnalysisContext context) => GetAnalyzerOptions(context.Options, context.SemanticModel.SyntaxTree); @@ -148,13 +113,4 @@ public static IdeAnalyzerOptions GetIdeAnalyzerOptions(this SemanticModelAnalysi public static IdeAnalyzerOptions GetIdeAnalyzerOptions(this SyntaxNodeAnalysisContext context) => context.Options.GetIdeOptions(); - - public static IdeAnalyzerOptions GetIdeAnalyzerOptions(this SyntaxTreeAnalysisContext context) - => context.Options.GetIdeOptions(); - - public static IdeAnalyzerOptions GetIdeAnalyzerOptions(this OperationAnalysisContext context) - => context.Options.GetIdeOptions(); - - public static IdeAnalyzerOptions GetIdeAnalyzerOptions(this CodeBlockAnalysisContext context) - => context.Options.GetIdeOptions(); } diff --git a/src/Analyzers/Core/CodeFixes/AnalyzerOptionsProviders.cs b/src/Analyzers/Core/CodeFixes/AnalyzerOptionsProviders.cs index c799a31e569e9..e270d3156c84e 100644 --- a/src/Analyzers/Core/CodeFixes/AnalyzerOptionsProviders.cs +++ b/src/Analyzers/Core/CodeFixes/AnalyzerOptionsProviders.cs @@ -2,11 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Threading; using System.Threading.Tasks; -using System.Collections.Generic; -using System.Text; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Options; @@ -20,6 +17,6 @@ public static async ValueTask GetAnalyzerOptionsProvide var analyzerOptions = document.Project.AnalyzerOptions; var configOptions = analyzerOptions.AnalyzerConfigOptionsProvider.GetOptions(syntaxTree).GetOptionsReader(); - return new AnalyzerOptionsProvider(configOptions, document.Project.Language, analyzerOptions); + return new AnalyzerOptionsProvider(configOptions, document.Project.Language); } } diff --git a/src/Analyzers/VisualBasic/Analyzers/CodeStyle/VisualBasicAnalyzerOptionsProvider.vb b/src/Analyzers/VisualBasic/Analyzers/CodeStyle/VisualBasicAnalyzerOptionsProvider.vb index 8e24a0c492cd1..324ccd7641048 100644 --- a/src/Analyzers/VisualBasic/Analyzers/CodeStyle/VisualBasicAnalyzerOptionsProvider.vb +++ b/src/Analyzers/VisualBasic/Analyzers/CodeStyle/VisualBasicAnalyzerOptionsProvider.vb @@ -18,83 +18,61 @@ Namespace Microsoft.CodeAnalysis.Diagnostics ''' Private ReadOnly _options As IOptionsReader - ''' - ''' Fallback options - the default options in Code Style layer. - ''' - Private ReadOnly _fallbackOptions As IdeAnalyzerOptions - - Public Sub New(options As IOptionsReader, fallbackOptions As IdeAnalyzerOptions) + Public Sub New(options As IOptionsReader) _options = options - _fallbackOptions = fallbackOptions - End Sub - - Public Sub New(options As IOptionsReader, fallbackOptions As AnalyzerOptions) - MyClass.New(options, fallbackOptions.GetIdeOptions()) End Sub Public Function GetSimplifierOptions() As VisualBasicSimplifierOptions - Return New VisualBasicSimplifierOptions(_options, FallbackSimplifierOptions) + Return New VisualBasicSimplifierOptions(_options, fallbackOptions:=Nothing) End Function Public ReadOnly Property PreferredModifierOrder As CodeStyleOption2(Of String) Get - Return GetOption(VisualBasicCodeStyleOptions.PreferredModifierOrder, FallbackCodeStyleOptions.PreferredModifierOrder) + Return GetOption(VisualBasicCodeStyleOptions.PreferredModifierOrder) End Get End Property Public ReadOnly Property PreferIsNotExpression As CodeStyleOption2(Of Boolean) Get - Return GetOption(VisualBasicCodeStyleOptions.PreferIsNotExpression, FallbackCodeStyleOptions.PreferIsNotExpression) + Return GetOption(VisualBasicCodeStyleOptions.PreferIsNotExpression) End Get End Property Public ReadOnly Property PreferSimplifiedObjectCreation As CodeStyleOption2(Of Boolean) Get - Return GetOption(VisualBasicCodeStyleOptions.PreferSimplifiedObjectCreation, FallbackCodeStyleOptions.PreferSimplifiedObjectCreation) + Return GetOption(VisualBasicCodeStyleOptions.PreferSimplifiedObjectCreation) End Get End Property Public ReadOnly Property UnusedValueExpressionStatement As CodeStyleOption2(Of UnusedValuePreference) Get - Return GetOption(VisualBasicCodeStyleOptions.UnusedValueExpressionStatement, FallbackCodeStyleOptions.UnusedValueExpressionStatement) + Return GetOption(VisualBasicCodeStyleOptions.UnusedValueExpressionStatement) End Get End Property Public ReadOnly Property UnusedValueAssignment As CodeStyleOption2(Of UnusedValuePreference) Get - Return GetOption(VisualBasicCodeStyleOptions.UnusedValueAssignment, FallbackCodeStyleOptions.UnusedValueAssignment) + Return GetOption(VisualBasicCodeStyleOptions.UnusedValueAssignment) End Get End Property - Private Function GetOption(Of TValue)([option] As Option2(Of CodeStyleOption2(Of TValue)), defaultValue As CodeStyleOption2(Of TValue)) As CodeStyleOption2(Of TValue) - Return _options.GetOption([option], defaultValue) + Private Function GetOption(Of TValue)([option] As Option2(Of CodeStyleOption2(Of TValue))) As CodeStyleOption2(Of TValue) + Return _options.GetOption([option]) End Function - Private ReadOnly Property FallbackSimplifierOptions As VisualBasicSimplifierOptions - Get - Return If(DirectCast(_fallbackOptions.CleanupOptions?.SimplifierOptions, VisualBasicSimplifierOptions), VisualBasicSimplifierOptions.Default) - End Get - End Property - - Private ReadOnly Property FallbackCodeStyleOptions As VisualBasicIdeCodeStyleOptions - Get - Return If(DirectCast(_fallbackOptions.CodeStyleOptions, VisualBasicIdeCodeStyleOptions), VisualBasicIdeCodeStyleOptions.Default) - End Get - End Property - Public Shared Narrowing Operator CType(provider As AnalyzerOptionsProvider) As VisualBasicAnalyzerOptionsProvider - Return New VisualBasicAnalyzerOptionsProvider(provider.GetAnalyzerConfigOptions(), provider.GetFallbackOptions()) + Return New VisualBasicAnalyzerOptionsProvider(provider.GetAnalyzerConfigOptions()) End Operator Public Shared Widening Operator CType(provider As VisualBasicAnalyzerOptionsProvider) As AnalyzerOptionsProvider - Return New AnalyzerOptionsProvider(provider._options, LanguageNames.VisualBasic, provider._fallbackOptions) + Return New AnalyzerOptionsProvider(provider._options, LanguageNames.VisualBasic) End Operator End Structure Friend Module VisualBasicAnalyzerOptionsProviders Public Function GetVisualBasicAnalyzerOptions(options As AnalyzerOptions, syntaxTree As SyntaxTree) As VisualBasicAnalyzerOptionsProvider - Return New VisualBasicAnalyzerOptionsProvider(options.AnalyzerConfigOptionsProvider.GetOptions(syntaxTree).GetOptionsReader(), options) + Return New VisualBasicAnalyzerOptionsProvider(options.AnalyzerConfigOptionsProvider.GetOptions(syntaxTree).GetOptionsReader()) End Function diff --git a/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs b/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs index a4ab425197cd9..df7f3b889c6ed 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs @@ -21,6 +21,7 @@ using Microsoft.CodeAnalysis.Diagnostics.CSharp; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Editor.UnitTests; +using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; @@ -886,10 +887,12 @@ private protected static async Task AssertCodeCleanupResult(string expected, str using var workspace = EditorTestWorkspace.CreateCSharp(code, composition: EditorTestCompositions.EditorFeaturesWpf); // must set global options since incremental analyzer infra reads from global options - var globalOptions = workspace.GlobalOptions; - globalOptions.SetGlobalOption(GenerationOptions.SeparateImportDirectiveGroups, LanguageNames.CSharp, separateUsingGroups); - globalOptions.SetGlobalOption(GenerationOptions.PlaceSystemNamespaceFirst, LanguageNames.CSharp, systemUsingsFirst); - globalOptions.SetGlobalOption(CSharpCodeStyleOptions.PreferredUsingDirectivePlacement, preferredImportPlacement); + workspace.SetAnalyzerFallbackAndGlobalOptions(new OptionsCollection(LanguageNames.CSharp) + { + { GenerationOptions.SeparateImportDirectiveGroups, separateUsingGroups }, + { GenerationOptions.PlaceSystemNamespaceFirst, systemUsingsFirst }, + { CSharpCodeStyleOptions.PreferredUsingDirectivePlacement, preferredImportPlacement }, + }); var solution = workspace.CurrentSolution.WithAnalyzerReferences(new[] { @@ -923,7 +926,7 @@ private protected static async Task AssertCodeCleanupResult(string expected, str enabledDiagnostics = VisualStudio.LanguageServices.Implementation.CodeCleanup.AbstractCodeCleanUpFixer.AdjustDiagnosticOptions(enabledDiagnostics, enabledFixIdsFilter); var newDoc = await codeCleanupService.CleanupAsync( - document, enabledDiagnostics, CodeAnalysisProgress.None, globalOptions.CreateProvider(), CancellationToken.None); + document, enabledDiagnostics, CodeAnalysisProgress.None, workspace.GlobalOptions.CreateProvider(), CancellationToken.None); var actual = await newDoc.GetTextAsync(); diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs index 789e2ef71f0cd..260baa5f17f93 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs @@ -35,7 +35,7 @@ private async Task GetProjectAnalysisDataAsync( // We can't return here if we have open file only analyzers since saved data for open file only analyzer // is incomplete -- it only contains info on open files rather than whole project. - if (existingData.Version == version && !CompilationHasOpenFileOnlyAnalyzers(compilationWithAnalyzers, ideOptions.CleanupOptions?.SimplifierOptions)) + if (existingData.Version == version && !CompilationHasOpenFileOnlyAnalyzers(compilationWithAnalyzers, ideOptions.SimplifierOptions)) { return existingData; } @@ -214,7 +214,7 @@ private static bool TryReduceAnalyzersToRun( { if (existing.TryGetValue(analyzer, out var analysisResult) && analysisResult.Version == version && - !analyzer.IsOpenFileOnly(ideOptions.CleanupOptions?.SimplifierOptions)) + !analyzer.IsOpenFileOnly(ideOptions.SimplifierOptions)) { // we already have up to date result. continue; diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InProcOrRemoteHostAnalyzerRunner.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InProcOrRemoteHostAnalyzerRunner.cs index 88214593ae6d4..e507a0b07d2bb 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InProcOrRemoteHostAnalyzerRunner.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InProcOrRemoteHostAnalyzerRunner.cs @@ -196,7 +196,7 @@ private static async Task forceExecuteAllAnalyzers || !a.IsOpenFileOnly(ideOptions.CleanupOptions?.SimplifierOptions)); + compilationWithAnalyzers.Analyzers.Where(a => forceExecuteAllAnalyzers || !a.IsOpenFileOnly(ideOptions.SimplifierOptions)); analyzerMap.AppendAnalyzerMap(analyzers); diff --git a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 041715c46a099..5b9c64754eec5 100644 --- a/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -32,7 +32,7 @@ public async Task> ForceAnalyzeProjectAsync(Proje // this is perf optimization. we cache these result since we know the result. (no diagnostics) var activeAnalyzers = stateSets .Select(s => s.Analyzer) - .Where(a => !a.IsOpenFileOnly(ideOptions.CleanupOptions?.SimplifierOptions)); + .Where(a => !a.IsOpenFileOnly(ideOptions.SimplifierOptions)); CompilationWithAnalyzers? compilationWithAnalyzers = null; diff --git a/src/LanguageServer/Protocol/Features/Options/IdeAnalyzerOptionsStorage.cs b/src/LanguageServer/Protocol/Features/Options/IdeAnalyzerOptionsStorage.cs index d4cb86c44517e..c3b84c406891e 100644 --- a/src/LanguageServer/Protocol/Features/Options/IdeAnalyzerOptionsStorage.cs +++ b/src/LanguageServer/Protocol/Features/Options/IdeAnalyzerOptionsStorage.cs @@ -7,6 +7,7 @@ using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Simplification; namespace Microsoft.CodeAnalysis.Diagnostics; @@ -17,16 +18,13 @@ public static IdeAnalyzerOptions GetIdeAnalyzerOptions(this IGlobalOptionService public static IdeAnalyzerOptions GetIdeAnalyzerOptions(this IGlobalOptionService globalOptions, LanguageServices languageServices) { - var language = languageServices.Language; - // avoid throwing for languages other than C# and VB: var supportsLanguageSpecificOptions = languageServices.GetService() != null; return new() { CrashOnAnalyzerException = globalOptions.GetOption(CrashOnAnalyzerException), - CleanCodeGenerationOptions = supportsLanguageSpecificOptions ? globalOptions.GetCleanCodeGenerationOptions(languageServices) : null, - CodeStyleOptions = supportsLanguageSpecificOptions ? globalOptions.GetCodeStyleOptions(languageServices) : null, + SimplifierOptions = supportsLanguageSpecificOptions ? globalOptions.GetSimplifierOptions(languageServices) : null, }; } diff --git a/src/LanguageServer/Protocol/Handler/Formatting/FormatDocumentHandler.cs b/src/LanguageServer/Protocol/Handler/Formatting/FormatDocumentHandler.cs index 6ba5da7b3afb5..543f18d5ef3b4 100644 --- a/src/LanguageServer/Protocol/Handler/Formatting/FormatDocumentHandler.cs +++ b/src/LanguageServer/Protocol/Handler/Formatting/FormatDocumentHandler.cs @@ -7,7 +7,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; using LSP = Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler diff --git a/src/LanguageServer/Protocol/Handler/Formatting/FormatDocumentRangeHandler.cs b/src/LanguageServer/Protocol/Handler/Formatting/FormatDocumentRangeHandler.cs index 6bb7432fde53f..c5b7205715520 100644 --- a/src/LanguageServer/Protocol/Handler/Formatting/FormatDocumentRangeHandler.cs +++ b/src/LanguageServer/Protocol/Handler/Formatting/FormatDocumentRangeHandler.cs @@ -7,7 +7,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; using Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler diff --git a/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs b/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs index 5e7ab4334dded..06f09db296468 100644 --- a/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs @@ -11,11 +11,8 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeStyle; -using Microsoft.CodeAnalysis.CSharp.CodeGeneration; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Diagnostics.TypeStyle; -using Microsoft.CodeAnalysis.CSharp.Formatting; -using Microsoft.CodeAnalysis.CSharp.Simplification; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.UnitTests; using Microsoft.CodeAnalysis.Remote; @@ -24,7 +21,6 @@ using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.VisualBasic.CodeStyle; using Microsoft.CodeAnalysis.VisualBasic.UseNullPropagation; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; using Roslyn.Test.Utilities; @@ -50,30 +46,15 @@ void Method() using var workspace = CreateWorkspace(LanguageNames.CSharp, code); var analyzerType = typeof(CSharpUseExplicitTypeDiagnosticAnalyzer); - var analyzerResult = await AnalyzeAsync(workspace, workspace.CurrentSolution.ProjectIds.First(), analyzerType, - IdeAnalyzerOptions.GetDefault(workspace.Services.SolutionServices.GetLanguageServices(LanguageNames.CSharp))); + var analyzerResult = await AnalyzeAsync(workspace, workspace.CurrentSolution.ProjectIds.First(), analyzerType); var diagnostics = analyzerResult.GetDocumentDiagnostics(analyzerResult.DocumentIds.First(), AnalysisKind.Semantic); Assert.Equal(IDEDiagnosticIds.UseExplicitTypeDiagnosticId, diagnostics[0].Id); Assert.Equal(DiagnosticSeverity.Hidden, diagnostics[0].Severity); - var ideOptions = new IdeAnalyzerOptions() - { - CleanCodeGenerationOptions = new() - { - GenerationOptions = CSharpCodeGenerationOptions.Default, - CleanupOptions = new() - { - FormattingOptions = CSharpSyntaxFormattingOptions.Default, - SimplifierOptions = new CSharpSimplifierOptions() - { - VarWhenTypeIsApparent = new CodeStyleOption2(false, NotificationOption2.Suggestion) - } - } - } - }; + workspace.SetAnalyzerFallbackOptions(LanguageNames.CSharp, ("csharp_style_var_when_type_is_apparent", "false:suggestion")); - analyzerResult = await AnalyzeAsync(workspace, workspace.CurrentSolution.ProjectIds.First(), analyzerType, ideOptions); + analyzerResult = await AnalyzeAsync(workspace, workspace.CurrentSolution.ProjectIds.First(), analyzerType); diagnostics = analyzerResult.GetDocumentDiagnostics(analyzerResult.DocumentIds.First(), AnalysisKind.Semantic); Assert.Equal(IDEDiagnosticIds.UseExplicitTypeDiagnosticId, diagnostics[0].Id); @@ -91,30 +72,17 @@ End Sub End Class"; using var workspace = CreateWorkspace(LanguageNames.VisualBasic, code); - var ideAnalyzerOptions = IdeAnalyzerOptions.GetDefault(workspace.Services.SolutionServices.GetLanguageServices(LanguageNames.VisualBasic)); - ideAnalyzerOptions = ideAnalyzerOptions with - { - CodeStyleOptions = new VisualBasicIdeCodeStyleOptions() - { - PreferNullPropagation = new CodeStyleOption2(false, NotificationOption2.Silent) - } - }; + workspace.SetAnalyzerFallbackOptions(LanguageNames.VisualBasic, ("dotnet_style_null_propagation", "false:silent")); var analyzerType = typeof(VisualBasicUseNullPropagationDiagnosticAnalyzer); - var analyzerResult = await AnalyzeAsync(workspace, workspace.CurrentSolution.ProjectIds.First(), analyzerType, ideAnalyzerOptions); + var analyzerResult = await AnalyzeAsync(workspace, workspace.CurrentSolution.ProjectIds.First(), analyzerType); Assert.True(analyzerResult.IsEmpty); - ideAnalyzerOptions = ideAnalyzerOptions with - { - CodeStyleOptions = new VisualBasicIdeCodeStyleOptions() - { - PreferNullPropagation = new CodeStyleOption2(true, NotificationOption2.Error) - } - }; + workspace.SetAnalyzerFallbackOptions(LanguageNames.VisualBasic, ("dotnet_style_null_propagation", "true:error")); - analyzerResult = await AnalyzeAsync(workspace, workspace.CurrentSolution.ProjectIds.First(), analyzerType, ideAnalyzerOptions); + analyzerResult = await AnalyzeAsync(workspace, workspace.CurrentSolution.ProjectIds.First(), analyzerType); var diagnostics = analyzerResult.GetDocumentDiagnostics(analyzerResult.DocumentIds.First(), AnalysisKind.Semantic); Assert.Equal(IDEDiagnosticIds.UseNullPropagationDiagnosticId, diagnostics[0].Id); @@ -126,7 +94,6 @@ public async Task TestCancellation() var code = @"class Test { void Method() { } }"; using var workspace = CreateWorkspace(LanguageNames.CSharp, code); - var ideAnalyzerOptions = IdeAnalyzerOptions.GetDefault(workspace.Services.SolutionServices.GetLanguageServices(LanguageNames.CSharp)); var analyzerType = typeof(MyAnalyzer); @@ -136,7 +103,7 @@ public async Task TestCancellation() try { - var task = Task.Run(() => AnalyzeAsync(workspace, workspace.CurrentSolution.ProjectIds.First(), analyzerType, ideAnalyzerOptions, source.Token)); + var task = Task.Run(() => AnalyzeAsync(workspace, workspace.CurrentSolution.ProjectIds.First(), analyzerType, source.Token)); // wait random milli-second var random = new Random(Environment.TickCount); @@ -235,7 +202,7 @@ void Method() private static InProcOrRemoteHostAnalyzerRunner CreateAnalyzerRunner() => new(enabled: true, new DiagnosticAnalyzerInfoCache()); - private static async Task AnalyzeAsync(TestWorkspace workspace, ProjectId projectId, Type analyzerType, IdeAnalyzerOptions ideOptions, CancellationToken cancellationToken = default) + private static async Task AnalyzeAsync(TestWorkspace workspace, ProjectId projectId, Type analyzerType, CancellationToken cancellationToken = default) { var executor = CreateAnalyzerRunner(); @@ -244,7 +211,7 @@ private static async Task AnalyzeAsync(TestWorkspace w var analyzerDriver = (await project.GetCompilationAsync()).WithAnalyzers( analyzerReference.GetAnalyzers(project.Language).Where(a => a.GetType() == analyzerType).ToImmutableArray(), - new WorkspaceAnalyzerOptions(project.AnalyzerOptions, ideOptions)); + new WorkspaceAnalyzerOptions(project.AnalyzerOptions, IdeAnalyzerOptions.GetDefault(project.Services))); var result = await executor.AnalyzeProjectAsync( project, analyzerDriver, logPerformanceInfo: false, getTelemetryInfo: false, cancellationToken); diff --git a/src/Workspaces/Core/Portable/Diagnostics/WorkspaceAnalyzerOptions.cs b/src/Workspaces/Core/Portable/Diagnostics/WorkspaceAnalyzerOptions.cs index ca42a234a37be..d29fd966eba69 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/WorkspaceAnalyzerOptions.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/WorkspaceAnalyzerOptions.cs @@ -4,6 +4,7 @@ #nullable disable +using Microsoft.CodeAnalysis.Simplification; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics; @@ -14,6 +15,10 @@ namespace Microsoft.CodeAnalysis.Diagnostics; /// internal sealed class WorkspaceAnalyzerOptions(AnalyzerOptions options, IdeAnalyzerOptions ideOptions) : AnalyzerOptions(options.AdditionalFiles, options.AnalyzerConfigOptionsProvider) { + /// + /// Currently needed to implement . + /// Should be removed: https://github.com/dotnet/roslyn/issues/74048 + /// public IdeAnalyzerOptions IdeOptions { get; } = ideOptions; public override bool Equals(object obj) diff --git a/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace`1.cs b/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace`1.cs index 4313e5532c122..e6c461f6aa2af 100644 --- a/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace`1.cs +++ b/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace`1.cs @@ -116,6 +116,16 @@ internal TestWorkspace( } } + public void SetAnalyzerFallbackOptions(string language, params (string name, string value)[] options) + { + SetCurrentSolution( + s => s.WithFallbackAnalyzerOptions(s.FallbackAnalyzerOptions.SetItem(language, + StructuredAnalyzerConfigOptions.Create( + new DictionaryAnalyzerConfigOptions( + options.Select(static o => KeyValuePairUtil.Create(o.name, o.value)).ToImmutableDictionary())))), + changeKind: WorkspaceChangeKind.SolutionChanged); + } + /// /// Use to set specified editorconfig options as . /// diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Diagnostics/IdeAnalyzerOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Diagnostics/IdeAnalyzerOptions.cs index 61ed91c84206c..51c1700a6a48c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Diagnostics/IdeAnalyzerOptions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Diagnostics/IdeAnalyzerOptions.cs @@ -3,12 +3,11 @@ // See the LICENSE file in the project root for more information. using System.Runtime.Serialization; -using Microsoft.CodeAnalysis.CodeCleanup; -using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.CodeStyle; #if !CODE_STYLE using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Simplification; #endif namespace Microsoft.CodeAnalysis.Diagnostics; @@ -23,25 +22,17 @@ internal sealed record class IdeAnalyzerOptions [DataMember] public bool CrashOnAnalyzerException { get; init; } = false; +#if !CODE_STYLE /// - /// Default values for , or null if not available (the project language does not support these options). - /// - [DataMember] public CleanCodeGenerationOptions? CleanCodeGenerationOptions { get; init; } = null; - - /// - /// Default values for , or null if not available (the project language does not support these options). + /// Currently needed to implement . + /// Should be removed: https://github.com/dotnet/roslyn/issues/74048 /// - [DataMember] public IdeCodeStyleOptions? CodeStyleOptions { get; init; } = null; - - public CodeCleanupOptions? CleanupOptions => CleanCodeGenerationOptions?.CleanupOptions; - public CodeGenerationOptions? GenerationOptions => CleanCodeGenerationOptions?.GenerationOptions; + [DataMember] public SimplifierOptions? SimplifierOptions { get; init; } = null; -#if !CODE_STYLE public static IdeAnalyzerOptions GetDefault(LanguageServices languageServices) => new() { - CleanCodeGenerationOptions = CodeGeneration.CleanCodeGenerationOptions.GetDefault(languageServices), - CodeStyleOptions = IdeCodeStyleOptions.GetDefault(languageServices), + SimplifierOptions = SimplifierOptions.GetDefault(languageServices), }; #endif } From 7cc4ff4e0a84f420d29a9610dbb74468c445c2c6 Mon Sep 17 00:00:00 2001 From: Arun Chander Date: Mon, 8 Jul 2024 15:18:02 -0700 Subject: [PATCH 16/17] Update Directory.Build.rsp --- Directory.Build.rsp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Directory.Build.rsp b/Directory.Build.rsp index 424936667b087..37bf61d3487f8 100644 --- a/Directory.Build.rsp +++ b/Directory.Build.rsp @@ -1 +1,2 @@ --p:_IsDisjointMSBuildVersion=false \ No newline at end of file +# Workaround for https://github.com/dotnet/sdk/issues/41791 +-p:_IsDisjointMSBuildVersion=false From 0971b49f5852a2ed2d091c3e5343a43e909b10ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Mon, 8 Jul 2024 15:52:17 -0700 Subject: [PATCH 17/17] Allow Document.FilePath to be set to null (#74290) --- .../Portable/Workspace/Solution/Document.cs | 18 ++--- .../Workspace/Solution/DocumentState.cs | 59 ++++++--------- .../Portable/Workspace/Solution/Solution.cs | 11 +-- .../Core/Portable/Workspace/Workspace.cs | 5 +- .../CoreTest/SolutionTests/SolutionTests.cs | 71 +++++++++++++++++-- 5 files changed, 98 insertions(+), 66 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs index ba601240cab4c..0d7fa9786fed0 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs @@ -383,41 +383,37 @@ async Task GetSemanticModelWorkerAsync() /// Creates a new instance of this document updated to have the source code kind specified. /// public Document WithSourceCodeKind(SourceCodeKind kind) - => this.Project.Solution.WithDocumentSourceCodeKind(this.Id, kind).GetDocument(this.Id)!; + => this.Project.Solution.WithDocumentSourceCodeKind(this.Id, kind).GetRequiredDocument(Id); /// /// Creates a new instance of this document updated to have the text specified. /// public Document WithText(SourceText text) - => this.Project.Solution.WithDocumentText(this.Id, text, PreservationMode.PreserveIdentity).GetDocument(this.Id)!; + => this.Project.Solution.WithDocumentText(this.Id, text, PreservationMode.PreserveIdentity).GetRequiredDocument(Id); /// /// Creates a new instance of this document updated to have a syntax tree rooted by the specified syntax node. /// public Document WithSyntaxRoot(SyntaxNode root) - => this.Project.Solution.WithDocumentSyntaxRoot(this.Id, root, PreservationMode.PreserveIdentity).GetDocument(this.Id)!; + => this.Project.Solution.WithDocumentSyntaxRoot(this.Id, root, PreservationMode.PreserveIdentity).GetRequiredDocument(Id); /// /// Creates a new instance of this document updated to have the specified name. /// public Document WithName(string name) - => this.Project.Solution.WithDocumentName(this.Id, name).GetDocument(this.Id)!; + => this.Project.Solution.WithDocumentName(this.Id, name).GetRequiredDocument(Id); /// /// Creates a new instance of this document updated to have the specified folders. /// public Document WithFolders(IEnumerable folders) - => this.Project.Solution.WithDocumentFolders(this.Id, folders).GetDocument(this.Id)!; + => this.Project.Solution.WithDocumentFolders(this.Id, folders).GetRequiredDocument(Id); /// /// Creates a new instance of this document updated to have the specified file path. /// - /// - // TODO (https://github.com/dotnet/roslyn/issues/37125): Solution.WithDocumentFilePath will throw if - // filePath is null, but it's odd because we *do* support null file paths. Why can't you switch a - // document back to null? - public Document WithFilePath(string filePath) - => this.Project.Solution.WithDocumentFilePath(this.Id, filePath).GetDocument(this.Id)!; + public Document WithFilePath(string? filePath) + => this.Project.Solution.WithDocumentFilePath(this.Id, filePath).GetRequiredDocument(Id); /// /// Get the text changes between this document and a prior version of the same document. diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs index 5eaacf839d0b6..285e12ba49633 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs @@ -350,15 +350,7 @@ public DocumentState UpdateParseOptions(ParseOptions options, bool onlyPreproces private DocumentState SetParseOptions(ParseOptions options, bool onlyPreprocessorDirectiveChange) { - if (options == null) - { - throw new ArgumentNullException(nameof(options)); - } - - if (!SupportsSyntaxTree) - { - throw new InvalidOperationException(); - } + Contract.ThrowIfFalse(SupportsSyntaxTree); ITreeAndVersionSource? newTreeSource = null; @@ -413,42 +405,37 @@ public DocumentState UpdateSourceCodeKind(SourceCodeKind kind) return this.SetParseOptions(this.ParseOptions.WithKind(kind), onlyPreprocessorDirectiveChange: false); } - // TODO: https://github.com/dotnet/roslyn/issues/37125 - // if FilePath is null, then this will change the name of the underlying tree, but we aren't producing a new tree in that case. public DocumentState UpdateName(string name) => UpdateAttributes(Attributes.With(name: name)); + public DocumentState UpdateFilePath(string? path) + => UpdateAttributes(Attributes.With(filePath: path)); + public DocumentState UpdateFolders(IReadOnlyList folders) => UpdateAttributes(Attributes.With(folders: folders)); - private DocumentState UpdateAttributes(DocumentInfo.DocumentAttributes attributes) - { - Debug.Assert(attributes != Attributes); - - return new DocumentState( - LanguageServices, - Services, - attributes, - _options, - TextAndVersionSource, - LoadTextOptions, - _treeSource); - } - - public DocumentState UpdateFilePath(string? filePath) + private DocumentState UpdateAttributes(DocumentInfo.DocumentAttributes newAttributes) { - var newAttributes = Attributes.With(filePath: filePath); Debug.Assert(newAttributes != Attributes); - // TODO: it's overkill to fully reparse the tree if we had the tree already; all we have to do is update the - // file path and diagnostic options for that tree. - var newTreeSource = SupportsSyntaxTree ? - CreateLazyFullyParsedTree( - TextAndVersionSource, - LoadTextOptions, - newAttributes.SyntaxTreeFilePath, - _options, - LanguageServices) : null; + ITreeAndVersionSource? newTreeSource; + + if (newAttributes.SyntaxTreeFilePath != Attributes.SyntaxTreeFilePath) + { + // TODO: it's overkill to fully reparse the tree if we had the tree already; all we have to do is update the + // file path and diagnostic options for that tree. + newTreeSource = SupportsSyntaxTree ? + CreateLazyFullyParsedTree( + TextAndVersionSource, + LoadTextOptions, + newAttributes.SyntaxTreeFilePath, + _options, + LanguageServices) : null; + } + else + { + newTreeSource = _treeSource; + } return new DocumentState( LanguageServices, diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs index e102e911df007..438640169d706 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs @@ -1169,18 +1169,9 @@ public Solution WithDocumentFolders(DocumentId documentId, IEnumerable? /// /// Creates a new solution instance with the document specified updated to have the specified file path. /// - public Solution WithDocumentFilePath(DocumentId documentId, string filePath) + public Solution WithDocumentFilePath(DocumentId documentId, string? filePath) { CheckContainsDocument(documentId); - - // TODO (https://github.com/dotnet/roslyn/issues/37125): - // We *do* support null file paths. Why can't you switch a document back to null? - // See DocumentState.GetSyntaxTreeFilePath - if (filePath == null) - { - throw new ArgumentNullException(nameof(filePath)); - } - return WithCompilationState(_compilationState.WithDocumentFilePath(documentId, filePath)); } diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace.cs b/src/Workspaces/Core/Portable/Workspace/Workspace.cs index 6bf7e5efe4372..7f7e86d82952e 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace.cs @@ -1088,10 +1088,7 @@ protected internal void OnDocumentInfoChanged(DocumentId documentId, DocumentInf if (oldAttributes.FilePath != newInfo.FilePath) { - // TODO (https://github.com/dotnet/roslyn/issues/37125): Solution.WithDocumentFilePath will throw if - // filePath is null, but it's odd because we *do* support null file paths. The suppression here is to silence it - // but should be removed when the bug is fixed. - newSolution = newSolution.WithDocumentFilePath(documentId, newInfo.FilePath!); + newSolution = newSolution.WithDocumentFilePath(documentId, newInfo.FilePath); } if (oldAttributes.SourceCodeKind != newInfo.SourceCodeKind) diff --git a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs index 2bf869fa41587..b068688cc7798 100644 --- a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs @@ -226,19 +226,19 @@ public void WithDocumentFilePath() var path = "new path"; var newSolution1 = solution.WithDocumentFilePath(documentId, path); - Assert.Equal(path, newSolution1.GetDocument(documentId)!.FilePath); + Assert.Equal(path, newSolution1.GetRequiredDocument(documentId).FilePath); AssertEx.Equal(new[] { documentId }, newSolution1.GetDocumentIdsWithFilePath(path)); var newSolution2 = newSolution1.WithDocumentFilePath(documentId, path); Assert.Same(newSolution1, newSolution2); - // empty path (TODO https://github.com/dotnet/roslyn/issues/37125): var newSolution3 = solution.WithDocumentFilePath(documentId, ""); - Assert.Equal("", newSolution3.GetDocument(documentId)!.FilePath); + Assert.Equal("", newSolution3.GetRequiredDocument(documentId).FilePath); Assert.Empty(newSolution3.GetDocumentIdsWithFilePath("")); - // TODO: https://github.com/dotnet/roslyn/issues/37125 - Assert.Throws(() => solution.WithDocumentFilePath(documentId, filePath: null!)); + var newSolution4 = solution.WithDocumentFilePath(documentId, null); + Assert.Null(newSolution4.GetRequiredDocument(documentId).FilePath); + Assert.Empty(newSolution4.GetDocumentIdsWithFilePath(null)); Assert.Throws(() => solution.WithDocumentFilePath(null!, path)); Assert.Throws(() => solution.WithDocumentFilePath(s_unrelatedDocumentId, path)); @@ -1341,6 +1341,67 @@ public async Task ChangingPreprocessorDirectivesMayReparse(string source, bool e Assert.Equal(expectReuse, oldRoot.IsIncrementallyIdenticalTo(newTree.GetRoot())); } + [Theory] + [InlineData(null, "test.cs")] + [InlineData("test.cs", null)] + [InlineData("", null)] + [InlineData("test.cs", "")] + public async Task ChangingFilePathReparses(string oldPath, string newPath) + { + var projectId = ProjectId.CreateNewId(); + var documentId = DocumentId.CreateNewId(projectId); + + using var workspace = CreateWorkspace(); + var document = workspace.CurrentSolution + .AddProject(projectId, "proj1", "proj1.dll", LanguageNames.CSharp) + .AddDocument(documentId, name: "Test.cs", text: "// File", filePath: oldPath) + .GetRequiredDocument(documentId); + + var oldTree = await document.GetRequiredSyntaxTreeAsync(CancellationToken.None); + var newDocument = document.WithFilePath(newPath); + var newTree = await newDocument.GetRequiredSyntaxTreeAsync(CancellationToken.None); + + Assert.False(oldTree.GetRoot().IsIncrementallyIdenticalTo(newTree.GetRoot())); + } + + [Fact] + public async Task ChangingName_ReparsesWhenPathIsNull() + { + var projectId = ProjectId.CreateNewId(); + var documentId = DocumentId.CreateNewId(projectId); + + using var workspace = CreateWorkspace(); + var document = workspace.CurrentSolution + .AddProject(projectId, "proj1", "proj1.dll", LanguageNames.CSharp) + .AddDocument(documentId, name: "name1", text: "// File", filePath: null) + .GetRequiredDocument(documentId); + + var oldTree = await document.GetRequiredSyntaxTreeAsync(CancellationToken.None); + var newDocument = document.WithName("name2"); + var newTree = await newDocument.GetRequiredSyntaxTreeAsync(CancellationToken.None); + + Assert.False(oldTree.GetRoot().IsIncrementallyIdenticalTo(newTree.GetRoot())); + } + + [Fact] + public async Task ChangingName_NoReparse() + { + var projectId = ProjectId.CreateNewId(); + var documentId = DocumentId.CreateNewId(projectId); + + using var workspace = CreateWorkspace(); + var document = workspace.CurrentSolution + .AddProject(projectId, "proj1", "proj1.dll", LanguageNames.CSharp) + .AddDocument(documentId, name: "name1", text: "// File", filePath: "") + .GetRequiredDocument(documentId); + + var oldTree = await document.GetRequiredSyntaxTreeAsync(CancellationToken.None); + var newDocument = document.WithName("name2"); + var newTree = await newDocument.GetRequiredSyntaxTreeAsync(CancellationToken.None); + + Assert.True(oldTree.GetRoot().IsIncrementallyIdenticalTo(newTree.GetRoot())); + } + [Fact] public void WithProjectReferences() {