From 00f38309aa8e633d4112568d19da9148d681ff9c Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Wed, 7 Aug 2019 08:37:04 -0700 Subject: [PATCH] Add support to use editorconfig for set severity command in the Analyzers node context menu 1. Renamed "Set Rule Set Severity" command to "Set severity". Also renamed "Info" and "Hidden" sub-menus to "Suggestion" and "Silent" respectively to align with our editorconfig severity terminology. 2. The set severity command handler checks if the project uses no ruleset or the default built-in ruleset, and if so it adds or updates editorconfig to configure severity. Otherwise, we continue using specified ruleset for configuration. 3. Updated the effective ruleset severity computation for the rule nodes under Analyzers node to account for severity settings from editorconfig (analyzer config documents). --- .../Configuration/ConfigurationUpdater.cs | 80 ++++++--------- src/VisualStudio/Core/Def/Commands.vsct | 20 ++-- .../Core/Def/xlf/Commands.vsct.cs.xlf | 28 +++--- .../Core/Def/xlf/Commands.vsct.de.xlf | 28 +++--- .../Core/Def/xlf/Commands.vsct.es.xlf | 28 +++--- .../Core/Def/xlf/Commands.vsct.fr.xlf | 28 +++--- .../Core/Def/xlf/Commands.vsct.it.xlf | 28 +++--- .../Core/Def/xlf/Commands.vsct.ja.xlf | 28 +++--- .../Core/Def/xlf/Commands.vsct.ko.xlf | 28 +++--- .../Core/Def/xlf/Commands.vsct.pl.xlf | 28 +++--- .../Core/Def/xlf/Commands.vsct.pt-BR.xlf | 28 +++--- .../Core/Def/xlf/Commands.vsct.ru.xlf | 28 +++--- .../Core/Def/xlf/Commands.vsct.tr.xlf | 28 +++--- .../Core/Def/xlf/Commands.vsct.zh-Hans.xlf | 28 +++--- .../Core/Def/xlf/Commands.vsct.zh-Hant.xlf | 28 +++--- .../AnalyzersCommandHandler.cs | 82 ++++++++++------ .../DiagnosticItem/BaseDiagnosticItem.cs | 18 ++-- .../BaseDiagnosticItemSource.cs | 97 ++++++++++++------- .../DiagnosticItem/CpsDiagnosticItemSource.cs | 22 ++--- .../LegacyDiagnosticItemSource.cs | 2 +- .../SolutionExplorerShim.Designer.cs | 9 ++ .../SolutionExplorerShim.resx | 3 + .../xlf/SolutionExplorerShim.cs.xlf | 5 + .../xlf/SolutionExplorerShim.de.xlf | 5 + .../xlf/SolutionExplorerShim.es.xlf | 5 + .../xlf/SolutionExplorerShim.fr.xlf | 5 + .../xlf/SolutionExplorerShim.it.xlf | 5 + .../xlf/SolutionExplorerShim.ja.xlf | 5 + .../xlf/SolutionExplorerShim.ko.xlf | 5 + .../xlf/SolutionExplorerShim.pl.xlf | 5 + .../xlf/SolutionExplorerShim.pt-BR.xlf | 5 + .../xlf/SolutionExplorerShim.ru.xlf | 5 + .../xlf/SolutionExplorerShim.tr.xlf | 5 + .../xlf/SolutionExplorerShim.zh-Hans.xlf | 5 + .../xlf/SolutionExplorerShim.zh-Hant.xlf | 5 + .../NotificationOptionExtensions.cs | 16 +-- .../Extensions/ReportDiagnosticExtensions.cs | 22 +++++ .../DiagnosticDescriptorExtensions.cs | 38 ++++++++ .../Shared/Extensions/ProjectExtensions.cs | 80 +++++++++++++++ .../Portable/Workspace/Solution/Project.cs | 4 + .../Workspace/Solution/ProjectState.cs | 31 ++++++ 41 files changed, 612 insertions(+), 341 deletions(-) create mode 100644 src/Workspaces/Core/Portable/Extensions/ReportDiagnosticExtensions.cs create mode 100644 src/Workspaces/Core/Portable/Shared/Extensions/DiagnosticDescriptorExtensions.cs diff --git a/src/Features/Core/Portable/CodeFixes/Configuration/ConfigurationUpdater.cs b/src/Features/Core/Portable/CodeFixes/Configuration/ConfigurationUpdater.cs index 4f2bd13e97f..1b00adf6cb3 100644 --- a/src/Features/Core/Portable/CodeFixes/Configuration/ConfigurationUpdater.cs +++ b/src/Features/Core/Portable/CodeFixes/Configuration/ConfigurationUpdater.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Threading; @@ -12,6 +13,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -80,7 +82,26 @@ private enum ConfigurationKind /// . /// public static Task ConfigureSeverityAsync( - string severity, + ReportDiagnostic severity, + Diagnostic diagnostic, + Project project, + CancellationToken cancellationToken) + { + if (severity == ReportDiagnostic.Default) + { + severity = diagnostic.DefaultSeverity.ToReportDiagnostic(); + } + + return ConfigureSeverityAsync(severity.ToEditorConfigString(), diagnostic, project, cancellationToken); + } + + /// + /// Updates or adds an .editorconfig to the given + /// so that the severity of the given is configured to be the given + /// . + /// + public static Task ConfigureSeverityAsync( + string editorConfigSeverity, Diagnostic diagnostic, Project project, CancellationToken cancellationToken) @@ -93,12 +114,12 @@ private enum ConfigurationKind if (!codeStyleOptionValues.IsEmpty) { return ConfigureCodeStyleOptionsAsync( - codeStyleOptionValues.Select(t => (t.optionName, t.currentOptionValue, severity)), + codeStyleOptionValues.Select(t => (t.optionName, t.currentOptionValue, editorConfigSeverity)), diagnostic, project, configurationKind: ConfigurationKind.Severity, cancellationToken); } else { - updater = new ConfigurationUpdater(optionNameOpt: null, newOptionValueOpt: null, severity, + updater = new ConfigurationUpdater(optionNameOpt: null, newOptionValueOpt: null, editorConfigSeverity, configurationKind: ConfigurationKind.Severity, diagnostic, project, cancellationToken); return updater.ConfigureAsync(); } @@ -143,16 +164,14 @@ private enum ConfigurationKind private async Task ConfigureAsync() { - var solution = _project.Solution; - // Find existing .editorconfig or generate a new one if none exists. - var editorConfigDocument = FindOrGenerateEditorConfig(solution); + var editorConfigDocument = FindOrGenerateEditorConfig(); if (editorConfigDocument == null) { - return solution; + return _project.Solution; } - solution = editorConfigDocument.Project.Solution; + var solution = editorConfigDocument.Project.Solution; var headers = new Dictionary(); var originalText = await editorConfigDocument.GetTextAsync(_cancellationToken).ConfigureAwait(false); @@ -165,52 +184,15 @@ private async Task ConfigureAsync() : solution; } - private AnalyzerConfigDocument FindOrGenerateEditorConfig(Solution solution) + private AnalyzerConfigDocument FindOrGenerateEditorConfig() { - if (_project.AnalyzerConfigDocuments.Any()) - { - var diagnosticFilePath = PathUtilities.GetDirectoryName(_diagnostic.Location.SourceTree?.FilePath ?? _project.FilePath); - if (!PathUtilities.IsAbsolute(diagnosticFilePath)) - { - return null; - } - - // Currently, we use a simple heuristic to find existing .editorconfig file. - // We start from the directory of the source file where the diagnostic was reported and walk up - // the directory tree to find an .editorconfig file. - // In future, we might change this algorithm, or allow end users to customize it based on options. - - var bestPath = string.Empty; - AnalyzerConfigDocument bestAnalyzerConfigDocument = null; - foreach (var analyzerConfigDocument in _project.AnalyzerConfigDocuments) - { - var analyzerConfigDirectory = PathUtilities.GetDirectoryName(analyzerConfigDocument.FilePath); - if (diagnosticFilePath.StartsWith(analyzerConfigDirectory) && - analyzerConfigDirectory.Length > bestPath.Length) - { - bestPath = analyzerConfigDirectory; - bestAnalyzerConfigDocument = analyzerConfigDocument; - } - } - - if (bestAnalyzerConfigDocument != null) - { - return bestAnalyzerConfigDocument; - } - } - - // Did not find any existing .editorconfig, so create one at root of the project. - if (!PathUtilities.IsAbsolute(_project.FilePath)) + var analyzerConfigPath = _project.TryGetAnalyzerConfigPathForDiagnosticConfiguration(_diagnostic); + if (analyzerConfigPath == null) { return null; } - var projectFilePath = PathUtilities.GetDirectoryName(_project.FilePath); - var newEditorConfigPath = PathUtilities.CombineAbsoluteAndRelativePaths(projectFilePath, ".editorconfig"); - var id = DocumentId.CreateNewId(_project.Id); - var documentInfo = DocumentInfo.Create(id, ".editorconfig", filePath: newEditorConfigPath); - var newSolution = solution.AddAnalyzerConfigDocuments(ImmutableArray.Create(documentInfo)); - return newSolution.GetProject(_project.Id).GetAnalyzerConfigDocument(id); + return _project.GetOrCreateAnalyzerConfigDocument(analyzerConfigPath); } private static ImmutableArray<(string optionName, string currentOptionValue, string currentSeverity)> GetCodeStyleOptionValuesForDiagnostic( diff --git a/src/VisualStudio/Core/Def/Commands.vsct b/src/VisualStudio/Core/Def/Commands.vsct index 03bf54aa4b5..a6d7ce38d45 100644 --- a/src/VisualStudio/Core/Def/Commands.vsct +++ b/src/VisualStudio/Core/Def/Commands.vsct @@ -194,18 +194,18 @@ @@ -346,10 +346,10 @@ - Set Rule Set Severity - Set Rule Set Severity - Set Rule Set Severity - Set Rule Set Severity + Set severity + Set severity + Set severity + Set severity diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.cs.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.cs.xlf index e391a4afb6f..fe6cfbe4dfd 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.cs.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.cs.xlf @@ -148,13 +148,13 @@ - &Info - &Informace + &Suggestion + &Informace - Info - Informace + Suggestion + Informace @@ -163,13 +163,13 @@ - &Hidden - &Skryté + &Silent + &Skryté - Hidden - Skryté + Silent + Skryté @@ -333,18 +333,18 @@ - Set Rule Set Severity - Nastavit závažnost sady pravidel + Set severity + Nastavit závažnost sady pravidel - Set Rule Set Severity - Nastavit závažnost sady pravidel + Set severity + Nastavit závažnost sady pravidel - Set Rule Set Severity - Nastavit závažnost sady pravidel + Set severity + Nastavit závažnost sady pravidel diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.de.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.de.xlf index 59304e23d70..85aac5c4b16 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.de.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.de.xlf @@ -148,13 +148,13 @@ - &Info - &Info + &Suggestion + &Info - Info - Info + Suggestion + Info @@ -163,13 +163,13 @@ - &Hidden - &Ausgeblendet + &Silent + &Ausgeblendet - Hidden - Ausgeblendet + Silent + Ausgeblendet @@ -333,18 +333,18 @@ - Set Rule Set Severity - Schweregrad für Regelsatz festlegen + Set severity + Schweregrad für Regelsatz festlegen - Set Rule Set Severity - Schweregrad für Regelsatz festlegen + Set severity + Schweregrad für Regelsatz festlegen - Set Rule Set Severity - Schweregrad für Regelsatz festlegen + Set severity + Schweregrad für Regelsatz festlegen diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.es.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.es.xlf index fdf90bd4828..86d27bdf7ec 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.es.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.es.xlf @@ -148,13 +148,13 @@ - &Info - &Información + &Suggestion + &Información - Info - Información + Suggestion + Información @@ -163,13 +163,13 @@ - &Hidden - &Oculto + &Silent + &Oculto - Hidden - Oculto + Silent + Oculto @@ -333,18 +333,18 @@ - Set Rule Set Severity - Configurar gravedad del conjunto de reglas + Set severity + Configurar gravedad del conjunto de reglas - Set Rule Set Severity - Configurar gravedad del conjunto de reglas + Set severity + Configurar gravedad del conjunto de reglas - Set Rule Set Severity - Configurar gravedad del conjunto de reglas + Set severity + Configurar gravedad del conjunto de reglas diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.fr.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.fr.xlf index cc9c8a5bb02..c2f87a7624f 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.fr.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.fr.xlf @@ -148,13 +148,13 @@ - &Info - &Informations + &Suggestion + &Informations - Info - Info + Suggestion + Info @@ -163,13 +163,13 @@ - &Hidden - &Masqué + &Silent + &Masqué - Hidden - Masqué + Silent + Masqué @@ -333,18 +333,18 @@ - Set Rule Set Severity - Définir la gravité de l'ensemble de règles + Set severity + Définir la gravité de l'ensemble de règles - Set Rule Set Severity - Définir la gravité de l'ensemble de règles + Set severity + Définir la gravité de l'ensemble de règles - Set Rule Set Severity - Définir la gravité de l'ensemble de règles + Set severity + Définir la gravité de l'ensemble de règles diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.it.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.it.xlf index 196ee12a3fe..c30f71d2b3b 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.it.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.it.xlf @@ -148,13 +148,13 @@ - &Info - In&formazioni + &Suggestion + In&formazioni - Info - Info + Suggestion + Info @@ -163,13 +163,13 @@ - &Hidden - &Nascosto + &Silent + &Nascosto - Hidden - Nascosto + Silent + Nascosto @@ -333,18 +333,18 @@ - Set Rule Set Severity - Imposta gravit?? set di regole + Set severity + Imposta gravit?? set di regole - Set Rule Set Severity - Imposta gravit?? set di regole + Set severity + Imposta gravit?? set di regole - Set Rule Set Severity - Imposta gravit?? set di regole + Set severity + Imposta gravit?? set di regole diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ja.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ja.xlf index 5c0becfc89c..59bf627c2f3 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ja.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ja.xlf @@ -148,13 +148,13 @@ - &Info - 情報(&I) + &Suggestion + 情報(&I) - Info - 情報 + Suggestion + 情報 @@ -163,13 +163,13 @@ - &Hidden - 非表示(&H) + &Silent + 非表示(&H) - Hidden - 非表示 + Silent + 非表示 @@ -333,18 +333,18 @@ - Set Rule Set Severity - ルール セットの重要度を設定 + Set severity + ルール セットの重要度を設定 - Set Rule Set Severity - ルール セットの重要度を設定 + Set severity + ルール セットの重要度を設定 - Set Rule Set Severity - ルール セットの重要度を設定 + Set severity + ルール セットの重要度を設定 diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ko.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ko.xlf index c1af6a37a73..3874662f5e8 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ko.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ko.xlf @@ -148,13 +148,13 @@ - &Info - 정보(&I) + &Suggestion + 정보(&I) - Info - 정보 + Suggestion + 정보 @@ -163,13 +163,13 @@ - &Hidden - 숨김(&H) + &Silent + 숨김(&H) - Hidden - 숨김 + Silent + 숨김 @@ -333,18 +333,18 @@ - Set Rule Set Severity - 규칙 집합 심각도 설정 + Set severity + 규칙 집합 심각도 설정 - Set Rule Set Severity - 규칙 집합 심각도 설정 + Set severity + 규칙 집합 심각도 설정 - Set Rule Set Severity - 규칙 집합 심각도 설정 + Set severity + 규칙 집합 심각도 설정 diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.pl.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.pl.xlf index f91d04c66b5..67b1ee542c5 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.pl.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.pl.xlf @@ -148,13 +148,13 @@ - &Info - &Informacje + &Suggestion + &Informacje - Info - Informacje + Suggestion + Informacje @@ -163,13 +163,13 @@ - &Hidden - &Ukryte + &Silent + &Ukryte - Hidden - Ukryty + Silent + Ukryty @@ -333,18 +333,18 @@ - Set Rule Set Severity - Ustaw ważność zestawu reguł + Set severity + Ustaw ważność zestawu reguł - Set Rule Set Severity - Ustaw ważność zestawu reguł + Set severity + Ustaw ważność zestawu reguł - Set Rule Set Severity - Ustaw ważność zestawu reguł + Set severity + Ustaw ważność zestawu reguł diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.pt-BR.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.pt-BR.xlf index 763e4b12265..2d1c9d8a5c2 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.pt-BR.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.pt-BR.xlf @@ -148,13 +148,13 @@ - &Info - &Informações + &Suggestion + &Informações - Info - Informação + Suggestion + Informação @@ -163,13 +163,13 @@ - &Hidden - &Oculto + &Silent + &Oculto - Hidden - Oculto + Silent + Oculto @@ -333,18 +333,18 @@ - Set Rule Set Severity - Definir Regra Definir Gravidade + Set severity + Definir Regra Definir Gravidade - Set Rule Set Severity - Definir Regra Definir Gravidade + Set severity + Definir Regra Definir Gravidade - Set Rule Set Severity - Definir Regra Definir Gravidade + Set severity + Definir Regra Definir Gravidade diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ru.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ru.xlf index f99b50607e7..cc05785fca8 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.ru.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.ru.xlf @@ -148,13 +148,13 @@ - &Info - &Информация + &Suggestion + &Информация - Info - Информация + Suggestion + Информация @@ -163,13 +163,13 @@ - &Hidden - &Скрытый + &Silent + &Скрытый - Hidden - Скрытый + Silent + Скрытый @@ -333,18 +333,18 @@ - Set Rule Set Severity - Установить серьезность набора правил + Set severity + Установить серьезность набора правил - Set Rule Set Severity - Установить серьезность набора правил + Set severity + Установить серьезность набора правил - Set Rule Set Severity - Установить серьезность набора правил + Set severity + Установить серьезность набора правил diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.tr.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.tr.xlf index 8acbf37068e..4a7fcc99e72 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.tr.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.tr.xlf @@ -148,13 +148,13 @@ - &Info - &Bilgi + &Suggestion + &Bilgi - Info - Bilgi + Suggestion + Bilgi @@ -163,13 +163,13 @@ - &Hidden - &Gizli + &Silent + &Gizli - Hidden - Gizli + Silent + Gizli @@ -333,18 +333,18 @@ - Set Rule Set Severity - Kural Kümesi Önem Derecesini Ayarla + Set severity + Kural Kümesi Önem Derecesini Ayarla - Set Rule Set Severity - Kural Kümesi Önem Derecesini Ayarla + Set severity + Kural Kümesi Önem Derecesini Ayarla - Set Rule Set Severity - Kural Kümesi Önem Derecesini Ayarla + Set severity + Kural Kümesi Önem Derecesini Ayarla diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hans.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hans.xlf index dee9f3ad633..121b78e2110 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hans.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hans.xlf @@ -148,13 +148,13 @@ - &Info - 信息(&I) + &Suggestion + 信息(&I) - Info - 信息 + Suggestion + 信息 @@ -163,13 +163,13 @@ - &Hidden - 隐藏(&H) + &Silent + 隐藏(&H) - Hidden - 隐藏 + Silent + 隐藏 @@ -333,18 +333,18 @@ - Set Rule Set Severity - 设置规则集严重性 + Set severity + 设置规则集严重性 - Set Rule Set Severity - 设置规则集严重性 + Set severity + 设置规则集严重性 - Set Rule Set Severity - 设置规则集严重性 + Set severity + 设置规则集严重性 diff --git a/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hant.xlf b/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hant.xlf index 06f3fafccc0..969b0afa9d1 100644 --- a/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hant.xlf +++ b/src/VisualStudio/Core/Def/xlf/Commands.vsct.zh-Hant.xlf @@ -148,13 +148,13 @@ - &Info - 資訊(&I) + &Suggestion + 資訊(&I) - Info - 資訊 + Suggestion + 資訊 @@ -163,13 +163,13 @@ - &Hidden - 隱藏(&H) + &Silent + 隱藏(&H) - Hidden - 隱藏 + Silent + 隱藏 @@ -333,18 +333,18 @@ - Set Rule Set Severity - 設定規則集合嚴重性 + Set severity + 設定規則集合嚴重性 - Set Rule Set Severity - 設定規則集合嚴重性 + Set severity + 設定規則集合嚴重性 - Set Rule Set Severity - 設定規則集合嚴重性 + Set severity + 設定規則集合嚴重性 diff --git a/src/VisualStudio/Core/SolutionExplorerShim/AnalyzersCommandHandler.cs b/src/VisualStudio/Core/SolutionExplorerShim/AnalyzersCommandHandler.cs index bc5cefc2dde..3b8b20a9cb9 100644 --- a/src/VisualStudio/Core/SolutionExplorerShim/AnalyzersCommandHandler.cs +++ b/src/VisualStudio/Core/SolutionExplorerShim/AnalyzersCommandHandler.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections; using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel.Composition; @@ -12,6 +13,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Host; using Microsoft.CodeAnalysis.Notification; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.Internal.VisualStudio.PlatformUI; using Microsoft.VisualStudio.CodeAnalysis; using Microsoft.VisualStudio.ComponentModelHost; @@ -22,6 +24,7 @@ using Microsoft.VisualStudio.Shell.Interop; using Roslyn.Utilities; using VSLangProj140; +using Project = Microsoft.CodeAnalysis.Project; namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer { @@ -172,7 +175,10 @@ private bool ShouldShowAnalyzerContextMenu(IEnumerable items) private void UpdateAnalyzerContextMenu() { - _removeMenuItem.Enabled = _allowProjectSystemOperations; + if (_removeMenuItem != null) + { + _removeMenuItem.Enabled = _allowProjectSystemOperations; + } } public IContextMenuController DiagnosticContextMenuController @@ -265,28 +271,25 @@ private void UpdateSeverityMenuItemsChecked() foreach (var group in groups) { - var ruleSetPath = workspace.TryGetRuleSetPathForProject(group.Key); + var project = workspace.CurrentSolution.GetProject(group.Key); + if (project == null) + { + continue; + } - if (ruleSetPath != null) + var analyzerConfigSpecificDiagnosticOptions = project.GetAnalyzerConfigSpecialDiagnosticOptions(); + + foreach (var diagnosticItem in group) { - var ruleSetManager = workspace.Services.GetRequiredService(); - using (var ruleSet = ruleSetManager.GetOrCreateRuleSet(ruleSetPath)) + var severity = ReportDiagnostic.Default; + if (project.CompilationOptions.SpecificDiagnosticOptions.ContainsKey(diagnosticItem.Descriptor.Id) || + analyzerConfigSpecificDiagnosticOptions.ContainsKey(diagnosticItem.Descriptor.Id)) { - var specificOptions = ruleSet.Target.Value.GetSpecificDiagnosticOptions(); - - foreach (var diagnosticItem in group) - { - if (specificOptions.TryGetValue(diagnosticItem.Descriptor.Id, out var ruleSetSeverity)) - { - selectedItemSeverities.Add(ruleSetSeverity); - } - else - { - // The rule has no setting. - selectedItemSeverities.Add(ReportDiagnostic.Default); - } - } + // Severity is overridden by end user. + severity = diagnosticItem.Descriptor.GetEffectiveSeverity(project.CompilationOptions, analyzerConfigSpecificDiagnosticOptions); } + + selectedItemSeverities.Add(severity); } } @@ -320,11 +323,6 @@ private void UpdateSeverityMenuItemsChecked() } } - private bool AnyDiagnosticsWithSeverity(ReportDiagnostic severity) - { - return _tracker.SelectedDiagnosticItems.Any(item => item.EffectiveSeverity == severity); - } - private void UpdateSeverityMenuItemsEnabled() { var configurable = !_tracker.SelectedDiagnosticItems.Any(item => item.Descriptor.CustomTags.Contains(WellKnownDiagnosticTags.NotConfigurable)); @@ -421,18 +419,46 @@ private void SetSeverityHandler(object sender, EventArgs args) var projectId = selectedDiagnostic.ProjectId; var pathToRuleSet = workspace.TryGetRuleSetPathForProject(projectId); - if (pathToRuleSet == null) + var project = workspace.CurrentSolution.GetProject(projectId); + var pathToAnalyzerConfigDoc = project?.TryGetAnalyzerConfigPathForProjectConfiguration(); + + if (pathToRuleSet == null && pathToAnalyzerConfigDoc == null) { SendUnableToUpdateRuleSetNotification(workspace, SolutionExplorerShim.No_rule_set_file_is_specified_or_the_file_does_not_exist); continue; } + var componentModel = (IComponentModel)_serviceProvider.GetService(typeof(SComponentModel)); + var waitIndicator = componentModel.GetService(); + try { var envDteProject = workspace.TryGetDTEProject(projectId); - if (SdkUiUtilities.IsBuiltInRuleSet(pathToRuleSet, _serviceProvider)) + if (pathToRuleSet == null || SdkUiUtilities.IsBuiltInRuleSet(pathToRuleSet, _serviceProvider)) { + // If project is using the default built-in ruleset or no ruleset, then prefer .editorconfig for severity configuration. + if (pathToAnalyzerConfigDoc != null) + { + waitIndicator.Wait( + title: SolutionExplorerShim.Updating_severity, + message: SolutionExplorerShim.Updating_severity, + allowCancel: true, + action: c => + { + var newSolution = selectedDiagnostic.GetSolutionWithUpdatedAnalyzerConfigSeverityAsync(selectedAction.Value, project, c.CancellationToken).WaitAndGetResult(c.CancellationToken); + _workspace.TryApplyChanges(newSolution, c.ProgressTracker); + }); + continue; + } + + // Otherwise, fall back to using ruleset. + if (pathToRuleSet == null) + { + SendUnableToUpdateRuleSetNotification(workspace, SolutionExplorerShim.No_rule_set_file_is_specified_or_the_file_does_not_exist); + continue; + } + pathToRuleSet = CreateCopyOfRuleSetForProject(pathToRuleSet, envDteProject); if (pathToRuleSet == null) { @@ -444,8 +470,6 @@ private void SetSeverityHandler(object sender, EventArgs args) fileInfo.IsReadOnly = false; } - var componentModel = (IComponentModel)_serviceProvider.GetService(typeof(SComponentModel)); - var waitIndicator = componentModel.GetService(); waitIndicator.Wait( title: SolutionExplorerShim.Rule_Set, message: string.Format(SolutionExplorerShim.Checking_out_0_for_editing, Path.GetFileName(pathToRuleSet)), @@ -458,7 +482,7 @@ private void SetSeverityHandler(object sender, EventArgs args) } }); - selectedDiagnostic.SetSeverity(selectedAction.Value, pathToRuleSet); + selectedDiagnostic.SetRuleSetSeverity(selectedAction.Value, pathToRuleSet); } catch (Exception e) { diff --git a/src/VisualStudio/Core/SolutionExplorerShim/DiagnosticItem/BaseDiagnosticItem.cs b/src/VisualStudio/Core/SolutionExplorerShim/DiagnosticItem/BaseDiagnosticItem.cs index 1f74343f4e5..1bdc2778e10 100644 --- a/src/VisualStudio/Core/SolutionExplorerShim/DiagnosticItem/BaseDiagnosticItem.cs +++ b/src/VisualStudio/Core/SolutionExplorerShim/DiagnosticItem/BaseDiagnosticItem.cs @@ -2,13 +2,17 @@ using System; using System.ComponentModel; +using System.Threading; +using System.Threading.Tasks; using System.Xml.Linq; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes.Configuration; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.VisualStudio.Imaging; using Microsoft.VisualStudio.Imaging.Interop; using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities; +using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer { @@ -106,12 +110,7 @@ private void NotifyPropertyChanged(string propertyName) PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } - internal void SetSeverity(ReportDiagnostic value, string pathToRuleSet) - { - UpdateRuleSetFile(pathToRuleSet, value); - } - - private void UpdateRuleSetFile(string pathToRuleSet, ReportDiagnostic value) + internal void SetRuleSetSeverity(ReportDiagnostic value, string pathToRuleSet) { var ruleSetDocument = XDocument.Load(pathToRuleSet); @@ -119,5 +118,12 @@ private void UpdateRuleSetFile(string pathToRuleSet, ReportDiagnostic value) ruleSetDocument.Save(pathToRuleSet); } + + internal Task GetSolutionWithUpdatedAnalyzerConfigSeverityAsync(ReportDiagnostic value, Project project, CancellationToken cancellationToken) + { + var effectiveSeverity = value.ToDiagnosticSeverity() ?? _descriptor.DefaultSeverity; + var diagnostic = Diagnostic.Create(_descriptor, Location.None, effectiveSeverity, additionalLocations: null, properties: null); + return ConfigurationUpdater.ConfigureSeverityAsync(value, diagnostic, project, cancellationToken); + } } } diff --git a/src/VisualStudio/Core/SolutionExplorerShim/DiagnosticItem/BaseDiagnosticItemSource.cs b/src/VisualStudio/Core/SolutionExplorerShim/DiagnosticItem/BaseDiagnosticItemSource.cs index ecf2ffc145c..cafa659fda1 100644 --- a/src/VisualStudio/Core/SolutionExplorerShim/DiagnosticItem/BaseDiagnosticItemSource.cs +++ b/src/VisualStudio/Core/SolutionExplorerShim/DiagnosticItem/BaseDiagnosticItemSource.cs @@ -8,35 +8,35 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Shell; namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer { - using Workspace = Microsoft.CodeAnalysis.Workspace; - internal abstract partial class BaseDiagnosticItemSource : IAttachedCollectionSource { - protected static readonly DiagnosticDescriptorComparer s_comparer = new DiagnosticDescriptorComparer(); - - protected readonly Workspace _workspace; - protected readonly ProjectId _projectId; - protected readonly IAnalyzersCommandHandler _commandHandler; - protected readonly IDiagnosticAnalyzerService _diagnosticAnalyzerService; + private static readonly DiagnosticDescriptorComparer s_comparer = new DiagnosticDescriptorComparer(); - protected BulkObservableCollection _diagnosticItems; + private readonly IDiagnosticAnalyzerService _diagnosticAnalyzerService; - protected ReportDiagnostic _generalDiagnosticOption; - protected ImmutableDictionary _specificDiagnosticOptions; + private BulkObservableCollection _diagnosticItems; + private ReportDiagnostic _generalDiagnosticOption; + private ImmutableDictionary _specificDiagnosticOptions; + private ImmutableDictionary _analyzerConfigSpecificDiagnosticOptions; public BaseDiagnosticItemSource(Workspace workspace, ProjectId projectId, IAnalyzersCommandHandler commandHandler, IDiagnosticAnalyzerService diagnosticAnalyzerService) { - _workspace = workspace; - _projectId = projectId; - _commandHandler = commandHandler; + Workspace = workspace; + ProjectId = projectId; + CommandHandler = commandHandler; _diagnosticAnalyzerService = diagnosticAnalyzerService; } + public Workspace Workspace { get; } + public ProjectId ProjectId { get; } + protected IAnalyzersCommandHandler CommandHandler { get; } + public abstract AnalyzerReference AnalyzerReference { get; } protected abstract BaseDiagnosticItem CreateItem(DiagnosticDescriptor diagnostic, ReportDiagnostic effectiveSeverity); @@ -56,7 +56,7 @@ public bool HasItems return false; } - var project = _workspace.CurrentSolution.GetProject(_projectId); + var project = Workspace.CurrentSolution.GetProject(ProjectId); return AnalyzerReference.GetAnalyzers(project.Language).Length > 0; } } @@ -67,14 +67,15 @@ public IEnumerable Items { if (_diagnosticItems == null) { - var project = _workspace.CurrentSolution.GetProject(_projectId); + var project = Workspace.CurrentSolution.GetProject(ProjectId); _generalDiagnosticOption = project.CompilationOptions.GeneralDiagnosticOption; _specificDiagnosticOptions = project.CompilationOptions.SpecificDiagnosticOptions; + _analyzerConfigSpecificDiagnosticOptions = project.GetAnalyzerConfigSpecialDiagnosticOptions(); _diagnosticItems = new BulkObservableCollection(); - _diagnosticItems.AddRange(GetDiagnosticItems(project.Language, project.CompilationOptions)); + _diagnosticItems.AddRange(GetDiagnosticItems(project.Language, project.CompilationOptions, _analyzerConfigSpecificDiagnosticOptions)); - _workspace.WorkspaceChanged += OnWorkspaceChangedLookForOptionsChanges; + Workspace.WorkspaceChanged += OnWorkspaceChangedLookForOptionsChanges; } Logger.Log( @@ -85,7 +86,7 @@ public IEnumerable Items } } - private IEnumerable GetDiagnosticItems(string language, CompilationOptions options) + private IEnumerable GetDiagnosticItems(string language, CompilationOptions options, ImmutableDictionary analyzerConfigSpecificDiagnosticOptions) { // Within an analyzer assembly, an individual analyzer may report multiple different diagnostics // with the same ID. Or, multiple analyzers may report diagnostics with the same ID. Or a @@ -103,7 +104,7 @@ private IEnumerable GetDiagnosticItems(string language, Comp .Select(g => { var selectedDiagnostic = g.OrderBy(d => d, s_comparer).First(); - var effectiveSeverity = selectedDiagnostic.GetEffectiveSeverity(options); + var effectiveSeverity = selectedDiagnostic.GetEffectiveSeverity(options, analyzerConfigSpecificDiagnosticOptions); return CreateItem(selectedDiagnostic, effectiveSeverity); }); } @@ -114,35 +115,57 @@ private void OnWorkspaceChangedLookForOptionsChanges(object sender, WorkspaceCha e.Kind == WorkspaceChangeKind.SolutionReloaded || e.Kind == WorkspaceChangeKind.SolutionRemoved) { - _workspace.WorkspaceChanged -= OnWorkspaceChangedLookForOptionsChanges; + Workspace.WorkspaceChanged -= OnWorkspaceChangedLookForOptionsChanges; } - else if (e.ProjectId == _projectId) + else if (e.ProjectId == ProjectId) { if (e.Kind == WorkspaceChangeKind.ProjectRemoved) { - _workspace.WorkspaceChanged -= OnWorkspaceChangedLookForOptionsChanges; + Workspace.WorkspaceChanged -= OnWorkspaceChangedLookForOptionsChanges; } else if (e.Kind == WorkspaceChangeKind.ProjectChanged) { - var project = e.NewSolution.GetProject(_projectId); - var newGeneralDiagnosticOption = project.CompilationOptions.GeneralDiagnosticOption; - var newSpecificDiagnosticOptions = project.CompilationOptions.SpecificDiagnosticOptions; + OnProjectConfigurationChanged(); + } + else if (e.DocumentId != null) + { + switch (e.Kind) + { + case WorkspaceChangeKind.AnalyzerConfigDocumentAdded: + case WorkspaceChangeKind.AnalyzerConfigDocumentChanged: + case WorkspaceChangeKind.AnalyzerConfigDocumentReloaded: + case WorkspaceChangeKind.AnalyzerConfigDocumentRemoved: + OnProjectConfigurationChanged(); + break; + } + } + } + + return; + + // Local functions. + void OnProjectConfigurationChanged() + { + var project = e.NewSolution.GetProject(ProjectId); + var newGeneralDiagnosticOption = project.CompilationOptions.GeneralDiagnosticOption; + var newSpecificDiagnosticOptions = project.CompilationOptions.SpecificDiagnosticOptions; + var newAnalyzerConfigSpecificDiagnosticOptions = project.GetAnalyzerConfigSpecialDiagnosticOptions(); + + if (newGeneralDiagnosticOption != _generalDiagnosticOption || + !object.ReferenceEquals(newSpecificDiagnosticOptions, _specificDiagnosticOptions) || + !object.ReferenceEquals(newAnalyzerConfigSpecificDiagnosticOptions, _analyzerConfigSpecificDiagnosticOptions)) + { + _generalDiagnosticOption = newGeneralDiagnosticOption; + _specificDiagnosticOptions = newSpecificDiagnosticOptions; + _analyzerConfigSpecificDiagnosticOptions = newAnalyzerConfigSpecificDiagnosticOptions; - if (newGeneralDiagnosticOption != _generalDiagnosticOption || - !object.ReferenceEquals(newSpecificDiagnosticOptions, _specificDiagnosticOptions)) + foreach (var item in _diagnosticItems) { - _generalDiagnosticOption = newGeneralDiagnosticOption; - _specificDiagnosticOptions = newSpecificDiagnosticOptions; - - foreach (var item in _diagnosticItems) - { - var effectiveSeverity = item.Descriptor.GetEffectiveSeverity(project.CompilationOptions); - item.UpdateEffectiveSeverity(effectiveSeverity); - } + var effectiveSeverity = item.Descriptor.GetEffectiveSeverity(project.CompilationOptions, newAnalyzerConfigSpecificDiagnosticOptions); + item.UpdateEffectiveSeverity(effectiveSeverity); } } } } - } } diff --git a/src/VisualStudio/Core/SolutionExplorerShim/DiagnosticItem/CpsDiagnosticItemSource.cs b/src/VisualStudio/Core/SolutionExplorerShim/DiagnosticItem/CpsDiagnosticItemSource.cs index af4972b028b..bb579cbd84d 100644 --- a/src/VisualStudio/Core/SolutionExplorerShim/DiagnosticItem/CpsDiagnosticItemSource.cs +++ b/src/VisualStudio/Core/SolutionExplorerShim/DiagnosticItem/CpsDiagnosticItemSource.cs @@ -11,8 +11,6 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer { - using Workspace = Microsoft.CodeAnalysis.Workspace; - internal partial class CpsDiagnosticItemSource : BaseDiagnosticItemSource, INotifyPropertyChanged { private readonly IVsHierarchyItem _item; @@ -28,18 +26,16 @@ public CpsDiagnosticItemSource(Workspace workspace, string projectPath, ProjectI _item = item; _projectDirectoryPath = Path.GetDirectoryName(projectPath); - _analyzerReference = TryGetAnalyzerReference(_workspace.CurrentSolution); + _analyzerReference = TryGetAnalyzerReference(Workspace.CurrentSolution); if (_analyzerReference == null) { // The workspace doesn't know about the project and/or the analyzer yet. // Hook up an event handler so we can update when it does. - _workspace.WorkspaceChanged += OnWorkspaceChangedLookForAnalyzer; + Workspace.WorkspaceChanged += OnWorkspaceChangedLookForAnalyzer; } } - public IContextMenuController DiagnosticItemContextMenuController => _commandHandler.DiagnosticContextMenuController; - public Workspace Workspace => _workspace; - public ProjectId ProjectId => _projectId; + public IContextMenuController DiagnosticItemContextMenuController => CommandHandler.DiagnosticContextMenuController; public override object SourceItem => _item; @@ -55,23 +51,23 @@ private void OnWorkspaceChangedLookForAnalyzer(object sender, WorkspaceChangeEve e.Kind == WorkspaceChangeKind.SolutionReloaded || e.Kind == WorkspaceChangeKind.SolutionRemoved) { - _workspace.WorkspaceChanged -= OnWorkspaceChangedLookForAnalyzer; + Workspace.WorkspaceChanged -= OnWorkspaceChangedLookForAnalyzer; } else if (e.Kind == WorkspaceChangeKind.SolutionAdded) { _analyzerReference = TryGetAnalyzerReference(e.NewSolution); if (_analyzerReference != null) { - _workspace.WorkspaceChanged -= OnWorkspaceChangedLookForAnalyzer; + Workspace.WorkspaceChanged -= OnWorkspaceChangedLookForAnalyzer; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(HasItems))); } } - else if (e.ProjectId == _projectId) + else if (e.ProjectId == ProjectId) { if (e.Kind == WorkspaceChangeKind.ProjectRemoved) { - _workspace.WorkspaceChanged -= OnWorkspaceChangedLookForAnalyzer; + Workspace.WorkspaceChanged -= OnWorkspaceChangedLookForAnalyzer; } else if (e.Kind == WorkspaceChangeKind.ProjectAdded || e.Kind == WorkspaceChangeKind.ProjectChanged) @@ -79,7 +75,7 @@ private void OnWorkspaceChangedLookForAnalyzer(object sender, WorkspaceChangeEve _analyzerReference = TryGetAnalyzerReference(e.NewSolution); if (_analyzerReference != null) { - _workspace.WorkspaceChanged -= OnWorkspaceChangedLookForAnalyzer; + Workspace.WorkspaceChanged -= OnWorkspaceChangedLookForAnalyzer; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(HasItems))); } @@ -89,7 +85,7 @@ private void OnWorkspaceChangedLookForAnalyzer(object sender, WorkspaceChangeEve private AnalyzerReference TryGetAnalyzerReference(Solution solution) { - var project = solution.GetProject(_projectId); + var project = solution.GetProject(ProjectId); if (project == null) { diff --git a/src/VisualStudio/Core/SolutionExplorerShim/DiagnosticItem/LegacyDiagnosticItemSource.cs b/src/VisualStudio/Core/SolutionExplorerShim/DiagnosticItem/LegacyDiagnosticItemSource.cs index 734352af0a1..1506cc5f423 100644 --- a/src/VisualStudio/Core/SolutionExplorerShim/DiagnosticItem/LegacyDiagnosticItemSource.cs +++ b/src/VisualStudio/Core/SolutionExplorerShim/DiagnosticItem/LegacyDiagnosticItemSource.cs @@ -30,7 +30,7 @@ public override AnalyzerReference AnalyzerReference protected override BaseDiagnosticItem CreateItem(DiagnosticDescriptor diagnostic, ReportDiagnostic effectiveSeverity) { - return new LegacyDiagnosticItem(_item, diagnostic, effectiveSeverity, _commandHandler.DiagnosticContextMenuController); + return new LegacyDiagnosticItem(_item, diagnostic, effectiveSeverity, CommandHandler.DiagnosticContextMenuController); } } } diff --git a/src/VisualStudio/Core/SolutionExplorerShim/SolutionExplorerShim.Designer.cs b/src/VisualStudio/Core/SolutionExplorerShim/SolutionExplorerShim.Designer.cs index 835cf42eeaa..8861f0dffde 100644 --- a/src/VisualStudio/Core/SolutionExplorerShim/SolutionExplorerShim.Designer.cs +++ b/src/VisualStudio/Core/SolutionExplorerShim/SolutionExplorerShim.Designer.cs @@ -321,6 +321,15 @@ internal class SolutionExplorerShim { } } + /// + /// Looks up a localized string similar to Updating severity. + /// + internal static string Updating_severity { + get { + return ResourceManager.GetString("Updating_severity", resourceCulture); + } + } + /// /// Looks up a localized string similar to Warning. /// diff --git a/src/VisualStudio/Core/SolutionExplorerShim/SolutionExplorerShim.resx b/src/VisualStudio/Core/SolutionExplorerShim/SolutionExplorerShim.resx index e3bf109a5cc..58495e15459 100644 --- a/src/VisualStudio/Core/SolutionExplorerShim/SolutionExplorerShim.resx +++ b/src/VisualStudio/Core/SolutionExplorerShim/SolutionExplorerShim.resx @@ -207,4 +207,7 @@ Title + + Updating severity + \ No newline at end of file diff --git a/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.cs.xlf b/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.cs.xlf index 7a9d6a10505..afd0f99eb05 100644 --- a/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.cs.xlf +++ b/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.cs.xlf @@ -62,6 +62,11 @@ Chyba + + Updating severity + Updating severity + + Warning Upozornění diff --git a/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.de.xlf b/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.de.xlf index 6ddcf20058f..e495f46fe68 100644 --- a/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.de.xlf +++ b/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.de.xlf @@ -62,6 +62,11 @@ Fehler + + Updating severity + Updating severity + + Warning Warnung diff --git a/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.es.xlf b/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.es.xlf index e92107db189..70f108ac7d1 100644 --- a/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.es.xlf +++ b/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.es.xlf @@ -62,6 +62,11 @@ Fehler + + Updating severity + Updating severity + + Warning Advertencia diff --git a/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.fr.xlf b/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.fr.xlf index 4d78351094f..856b4127002 100644 --- a/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.fr.xlf +++ b/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.fr.xlf @@ -62,6 +62,11 @@ Erreur + + Updating severity + Updating severity + + Warning Avertissement diff --git a/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.it.xlf b/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.it.xlf index 99ddd453c67..a01df421268 100644 --- a/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.it.xlf +++ b/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.it.xlf @@ -62,6 +62,11 @@ Errore + + Updating severity + Updating severity + + Warning Avviso diff --git a/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.ja.xlf b/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.ja.xlf index 617679a2899..9cc55c57ad1 100644 --- a/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.ja.xlf +++ b/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.ja.xlf @@ -62,6 +62,11 @@ エラー + + Updating severity + Updating severity + + Warning 警告 diff --git a/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.ko.xlf b/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.ko.xlf index 7dd13048ccc..bfcc8dfacea 100644 --- a/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.ko.xlf +++ b/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.ko.xlf @@ -62,6 +62,11 @@ 오류 + + Updating severity + Updating severity + + Warning 경고 diff --git a/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.pl.xlf b/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.pl.xlf index 47b7f23de76..b764f37902b 100644 --- a/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.pl.xlf +++ b/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.pl.xlf @@ -62,6 +62,11 @@ Błąd + + Updating severity + Updating severity + + Warning Ostrzeżenie diff --git a/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.pt-BR.xlf b/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.pt-BR.xlf index 60a47309008..7bb2eaee147 100644 --- a/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.pt-BR.xlf +++ b/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.pt-BR.xlf @@ -62,6 +62,11 @@ Erro + + Updating severity + Updating severity + + Warning Aviso diff --git a/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.ru.xlf b/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.ru.xlf index 244c5259d2a..f0876316f9c 100644 --- a/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.ru.xlf +++ b/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.ru.xlf @@ -62,6 +62,11 @@ Ошибка + + Updating severity + Updating severity + + Warning Предупреждение diff --git a/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.tr.xlf b/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.tr.xlf index f1a3e9e7a7d..1254939186f 100644 --- a/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.tr.xlf +++ b/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.tr.xlf @@ -62,6 +62,11 @@ Hata + + Updating severity + Updating severity + + Warning Uyarı diff --git a/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.zh-Hans.xlf b/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.zh-Hans.xlf index 4364de4d50c..f6ad98e7ce6 100644 --- a/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.zh-Hans.xlf +++ b/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.zh-Hans.xlf @@ -62,6 +62,11 @@ 错误 + + Updating severity + Updating severity + + Warning 警告 diff --git a/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.zh-Hant.xlf b/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.zh-Hant.xlf index 8a346b20c91..7ceb14f3f14 100644 --- a/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.zh-Hant.xlf +++ b/src/VisualStudio/Core/SolutionExplorerShim/xlf/SolutionExplorerShim.zh-Hant.xlf @@ -62,6 +62,11 @@ 錯誤 + + Updating severity + Updating severity + + Warning 警告 diff --git a/src/Workspaces/Core/Portable/Extensions/NotificationOptionExtensions.cs b/src/Workspaces/Core/Portable/Extensions/NotificationOptionExtensions.cs index 4cee2a28466..bef1619b277 100644 --- a/src/Workspaces/Core/Portable/Extensions/NotificationOptionExtensions.cs +++ b/src/Workspaces/Core/Portable/Extensions/NotificationOptionExtensions.cs @@ -1,22 +1,10 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Roslyn.Utilities; +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.CodeAnalysis.CodeStyle { internal static class NotificationOptionExtensions { public static string ToEditorConfigString(this NotificationOption notificationOption) - { - return notificationOption.Severity switch - { - ReportDiagnostic.Suppress => EditorConfigSeverityStrings.None, - ReportDiagnostic.Hidden => EditorConfigSeverityStrings.Silent, - ReportDiagnostic.Info => EditorConfigSeverityStrings.Suggestion, - ReportDiagnostic.Warn => EditorConfigSeverityStrings.Warning, - ReportDiagnostic.Error => EditorConfigSeverityStrings.Error, - _ => throw ExceptionUtilities.UnexpectedValue(notificationOption.Severity) - }; - } + => notificationOption.Severity.ToEditorConfigString(); } } diff --git a/src/Workspaces/Core/Portable/Extensions/ReportDiagnosticExtensions.cs b/src/Workspaces/Core/Portable/Extensions/ReportDiagnosticExtensions.cs new file mode 100644 index 00000000000..92509ede62d --- /dev/null +++ b/src/Workspaces/Core/Portable/Extensions/ReportDiagnosticExtensions.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis +{ + internal static class ReportDiagnosticExtensions + { + public static string ToEditorConfigString(this ReportDiagnostic reportDiagnostic) + { + return reportDiagnostic switch + { + ReportDiagnostic.Suppress => EditorConfigSeverityStrings.None, + ReportDiagnostic.Hidden => EditorConfigSeverityStrings.Silent, + ReportDiagnostic.Info => EditorConfigSeverityStrings.Suggestion, + ReportDiagnostic.Warn => EditorConfigSeverityStrings.Warning, + ReportDiagnostic.Error => EditorConfigSeverityStrings.Error, + _ => throw ExceptionUtilities.UnexpectedValue(reportDiagnostic) + }; + } + } +} diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/DiagnosticDescriptorExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/DiagnosticDescriptorExtensions.cs new file mode 100644 index 00000000000..cfebedd79cf --- /dev/null +++ b/src/Workspaces/Core/Portable/Shared/Extensions/DiagnosticDescriptorExtensions.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Immutable; + +namespace Microsoft.CodeAnalysis.Shared.Extensions +{ + internal static class DiagnosticDescriptorExtensions + { + /// + /// Gets project-level effective severity of the given accounting for severity configurations from both the following sources: + /// 1. Compilation options from ruleset file, if any, and command line options such as /nowarn, /warnaserror, etc. + /// 2. Analyzer config documents at the project root directory or in ancestor directories. + /// + public static ReportDiagnostic GetEffectiveSeverity(this DiagnosticDescriptor descriptor, CompilationOptions compilationOptions, ImmutableDictionary analyzerConfigSpecificDiagnosticOptions) + { + var effectiveSeverity = descriptor.GetEffectiveSeverity(compilationOptions); + + // Apply analyzer config options on top of compilation options, unless the diagnostic is explicitly suppressed by compilation options (/nowarn). + var isSuppressedByCompilationOptions = effectiveSeverity == ReportDiagnostic.Suppress && + compilationOptions.SpecificDiagnosticOptions.ContainsKey(descriptor.Id); + if (!isSuppressedByCompilationOptions && + analyzerConfigSpecificDiagnosticOptions.TryGetValue(descriptor.Id, out var reportDiagnostic)) + { + effectiveSeverity = reportDiagnostic; + } + + return effectiveSeverity; + } + + /// + /// Gets project-level effective severity of the given accounting for severity configurations from both the following sources: + /// 1. Compilation options from ruleset file, if any, and command line options such as /nowarn, /warnaserror, etc. + /// 2. Analyzer config documents at the project root directory or in ancestor directories. + /// + public static ReportDiagnostic GetEffectiveSeverity(this DiagnosticDescriptor descriptor, Project project) + => descriptor.GetEffectiveSeverity(project.CompilationOptions, project.GetAnalyzerConfigSpecialDiagnosticOptions()); + } +} diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/ProjectExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/ProjectExtensions.cs index 7154dd7374e..2eec883d6c1 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/ProjectExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/ProjectExtensions.cs @@ -1,7 +1,12 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Shared.Extensions { @@ -38,5 +43,80 @@ public static async Task GetVersionAsync(this Project project, Can return version.GetNewerVersion(latestVersion); } + + public static string TryGetAnalyzerConfigPathForProjectConfiguration(this Project project) + => TryGetAnalyzerConfigPathForProjectOrDiagnosticConfiguration(project, diagnosticOpt: null); + + public static string TryGetAnalyzerConfigPathForDiagnosticConfiguration(this Project project, Diagnostic diagnostic) + { + Debug.Assert(diagnostic != null); + return TryGetAnalyzerConfigPathForProjectOrDiagnosticConfiguration(project, diagnostic); + } + + private static string TryGetAnalyzerConfigPathForProjectOrDiagnosticConfiguration(Project project, Diagnostic diagnosticOpt) + { + if (project.AnalyzerConfigDocuments.Any()) + { + var diagnosticFilePath = PathUtilities.GetDirectoryName(diagnosticOpt?.Location.SourceTree?.FilePath ?? project.FilePath); + if (!PathUtilities.IsAbsolute(diagnosticFilePath)) + { + return null; + } + + // Currently, we use a simple heuristic to find existing .editorconfig file. + // We start from the directory of the source file where the diagnostic was reported and walk up + // the directory tree to find an .editorconfig file. + // In future, we might change this algorithm, or allow end users to customize it based on options. + + var bestPath = string.Empty; + AnalyzerConfigDocument bestAnalyzerConfigDocument = null; + foreach (var analyzerConfigDocument in project.AnalyzerConfigDocuments) + { + var analyzerConfigDirectory = PathUtilities.GetDirectoryName(analyzerConfigDocument.FilePath); + if (diagnosticFilePath.StartsWith(analyzerConfigDirectory) && + analyzerConfigDirectory.Length > bestPath.Length) + { + bestPath = analyzerConfigDirectory; + bestAnalyzerConfigDocument = analyzerConfigDocument; + } + } + + if (bestAnalyzerConfigDocument != null) + { + return bestAnalyzerConfigDocument.FilePath; + } + } + + // Did not find any existing .editorconfig, so create one at root of the project. + if (!PathUtilities.IsAbsolute(project.FilePath)) + { + return null; + } + + var projectFilePath = PathUtilities.GetDirectoryName(project.FilePath); + return PathUtilities.CombineAbsoluteAndRelativePaths(projectFilePath, ".editorconfig"); + } + + public static AnalyzerConfigDocument TryGetExistingAnalyzerConfigDocumentAtPath(this Project project, string analyzerConfigPath) + { + Debug.Assert(analyzerConfigPath != null); + Debug.Assert(PathUtilities.IsAbsolute(analyzerConfigPath)); + + return project.AnalyzerConfigDocuments.FirstOrDefault(d => d.FilePath == analyzerConfigPath); + } + + public static AnalyzerConfigDocument GetOrCreateAnalyzerConfigDocument(this Project project, string analyzerConfigPath) + { + var existingAnalyzerConfigDocument = project.TryGetExistingAnalyzerConfigDocumentAtPath(analyzerConfigPath); + if (existingAnalyzerConfigDocument != null) + { + return existingAnalyzerConfigDocument; + } + + var id = DocumentId.CreateNewId(project.Id); + var documentInfo = DocumentInfo.Create(id, ".editorconfig", filePath: analyzerConfigPath); + var newSolution = project.Solution.AddAnalyzerConfigDocuments(ImmutableArray.Create(documentInfo)); + return newSolution.GetProject(project.Id).GetAnalyzerConfigDocument(id); + } } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Project.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Project.cs index 3349fd1512c..d56621a07b5 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Project.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Project.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Threading; @@ -603,6 +604,9 @@ public Project RemoveAnalyzerConfigDocument(DocumentId documentId) return this.Solution.RemoveAnalyzerConfigDocument(documentId).GetProject(this.Id); } + internal ImmutableDictionary GetAnalyzerConfigSpecialDiagnosticOptions() + => _projectState.GetAnalyzerConfigSpecialDiagnosticOptions(); + private string GetDebuggerDisplay() { return this.Name; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs index 4c3ffae7881..d6b4a94173f 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs @@ -260,6 +260,37 @@ public AnalyzerOptions AnalyzerOptions } } + public ImmutableDictionary GetAnalyzerConfigSpecialDiagnosticOptions() + { + // We need to find the analyzer config options at the root of the project. + // Currently, there is no compiler API to query analyzer config options for a directory in a language agnostic fashion. + // So, we use a dummy language-specific file name appended to the project directory to query analyzer config options. + + var projectDirectory = PathUtilities.GetDirectoryName(_projectInfo.FilePath); + if (!PathUtilities.IsAbsolute(projectDirectory)) + { + return ImmutableDictionary.Empty; + } + + var fileName = Guid.NewGuid().ToString(); + string sourceFilePath; + switch (_projectInfo.Language) + { + case LanguageNames.CSharp: + sourceFilePath = PathUtilities.CombineAbsoluteAndRelativePaths(projectDirectory, $"{fileName}.cs"); + break; + + case LanguageNames.VisualBasic: + sourceFilePath = PathUtilities.CombineAbsoluteAndRelativePaths(projectDirectory, $"{fileName}.vb"); + break; + + default: + return ImmutableDictionary.Empty; + } + + return _lazyAnalyzerConfigSet.GetValue(CancellationToken.None).GetOptionsForSourcePath(sourceFilePath).TreeOptions; + } + private sealed class WorkspaceAnalyzerConfigOptionsProvider : AnalyzerConfigOptionsProvider { private readonly ProjectState _projectState; -- GitLab