From 882912f744e1ee217a754d6c191817de8aa0a506 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Mon, 16 May 2016 12:10:55 -0700 Subject: [PATCH] Ensure that we respect source suppressions for IDE analyzer diagnostics (analyzer dependency errors, reading ruleset errors, etc.) Fixes https://github.com/dotnet/roslyn/issues/8877 --- .../AnalyzerDependencyCheckingService.cs | 81 +++++++------------ .../AnalyzerFileWatcherService.cs | 29 ++++--- .../ProjectSystem/AbstractProject.cs | 28 +++---- .../Portable/Diagnostics/DiagnosticData.cs | 29 +++++++ 4 files changed, 85 insertions(+), 82 deletions(-) diff --git a/src/VisualStudio/Core/Def/Implementation/AnalyzerDependency/AnalyzerDependencyCheckingService.cs b/src/VisualStudio/Core/Def/Implementation/AnalyzerDependency/AnalyzerDependencyCheckingService.cs index 984bc5de393ba..7588a8eb050a5 100644 --- a/src/VisualStudio/Core/Def/Implementation/AnalyzerDependency/AnalyzerDependencyCheckingService.cs +++ b/src/VisualStudio/Core/Def/Implementation/AnalyzerDependency/AnalyzerDependencyCheckingService.cs @@ -33,6 +33,22 @@ internal sealed class AnalyzerDependencyCheckingService private Task _task = Task.FromResult(AnalyzerDependencyResults.Empty); private ImmutableHashSet _analyzerPaths = ImmutableHashSet.Create(StringComparer.OrdinalIgnoreCase); + private readonly DiagnosticDescriptor _missingAnalyzerReferenceRule = new DiagnosticDescriptor( + id: IDEDiagnosticIds.MissingAnalyzerReferenceId, + title: ServicesVSResources.WRN_MissingAnalyzerReferenceTitle, + messageFormat: ServicesVSResources.WRN_MissingAnalyzerReferenceMessage, + category: FeaturesResources.ErrorCategory, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + private readonly DiagnosticDescriptor _analyzerDependencyConflictRule = new DiagnosticDescriptor( + id: IDEDiagnosticIds.AnalyzerDependencyConflictId, + title: ServicesVSResources.WRN_AnalyzerDependencyConflictTitle, + messageFormat: ServicesVSResources.WRN_AnalyzerDependencyConflictMessage, + category: FeaturesResources.ErrorCategory, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true); + [ImportingConstructor] public AnalyzerDependencyCheckingService( VisualStudioWorkspaceImpl workspace, @@ -74,7 +90,12 @@ public async void CheckForConflictsAsync() if (project.CurrentProjectAnalyzersContains(conflict.AnalyzerFilePath1) || project.CurrentProjectAnalyzersContains(conflict.AnalyzerFilePath2)) { - builder.Add(CreateDiagnostic(project.Id, conflict)); + var messageArguments = new string[] { conflict.AnalyzerFilePath1, conflict.AnalyzerFilePath2, conflict.Identity.ToString() }; + DiagnosticData diagnostic; + if (DiagnosticData.TryCreate(_analyzerDependencyConflictRule, messageArguments, project.Id, _workspace, out diagnostic)) + { + builder.Add(diagnostic); + } } } @@ -82,7 +103,12 @@ public async void CheckForConflictsAsync() { if (project.CurrentProjectAnalyzersContains(missingDependency.AnalyzerPath)) { - builder.Add(CreateDiagnostic(project.Id, missingDependency)); + var messageArguments = new string[] { missingDependency.AnalyzerPath, missingDependency.DependencyIdentity.ToString() }; + DiagnosticData diagnostic; + if (DiagnosticData.TryCreate(_missingAnalyzerReferenceRule, messageArguments, project.Id, _workspace, out diagnostic)) + { + builder.Add(diagnostic); + } } } @@ -123,57 +149,6 @@ private void LogMissingDependency(MissingAnalyzerDependency missingDependency) })); } - private DiagnosticData CreateDiagnostic(ProjectId projectId, AnalyzerDependencyConflict conflict) - { - string message = string.Format( - ServicesVSResources.WRN_AnalyzerDependencyConflictMessage, - conflict.AnalyzerFilePath1, - conflict.AnalyzerFilePath2, - conflict.Identity.ToString()); - - DiagnosticData data = new DiagnosticData( - IDEDiagnosticIds.AnalyzerDependencyConflictId, - FeaturesResources.ErrorCategory, - message, - ServicesVSResources.WRN_AnalyzerDependencyConflictMessage, - severity: DiagnosticSeverity.Warning, - defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true, - warningLevel: 0, - customTags: ImmutableArray.Empty, - properties: ImmutableDictionary.Empty, - workspace: _workspace, - projectId: projectId, - title: ServicesVSResources.WRN_AnalyzerDependencyConflictTitle); - - return data; - } - - private DiagnosticData CreateDiagnostic(ProjectId projectId, MissingAnalyzerDependency missingDependency) - { - string message = string.Format( - ServicesVSResources.WRN_MissingAnalyzerReferenceMessage, - missingDependency.AnalyzerPath, - missingDependency.DependencyIdentity.ToString()); - - DiagnosticData data = new DiagnosticData( - IDEDiagnosticIds.MissingAnalyzerReferenceId, - FeaturesResources.ErrorCategory, - message, - ServicesVSResources.WRN_MissingAnalyzerReferenceMessage, - severity: DiagnosticSeverity.Warning, - defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true, - warningLevel: 0, - customTags: ImmutableArray.Empty, - properties: ImmutableDictionary.Empty, - workspace: _workspace, - projectId: projectId, - title: ServicesVSResources.WRN_MissingAnalyzerReferenceTitle); - - return data; - } - private Task GetConflictsAsync() { ImmutableHashSet currentAnalyzerPaths = _workspace.CurrentSolution diff --git a/src/VisualStudio/Core/Def/Implementation/AnalyzerDependency/AnalyzerFileWatcherService.cs b/src/VisualStudio/Core/Def/Implementation/AnalyzerDependency/AnalyzerFileWatcherService.cs index b28cd8fb3c513..98f059e5a6415 100644 --- a/src/VisualStudio/Core/Def/Implementation/AnalyzerDependency/AnalyzerFileWatcherService.cs +++ b/src/VisualStudio/Core/Def/Implementation/AnalyzerDependency/AnalyzerFileWatcherService.cs @@ -29,6 +29,14 @@ internal sealed class AnalyzerFileWatcherService private readonly object _guard = new object(); + private readonly DiagnosticDescriptor _analyzerChangedRule = new DiagnosticDescriptor( + id: IDEDiagnosticIds.AnalyzerChangedId, + title: ServicesVSResources.WRN_AnalyzerChangedTitle, + messageFormat: ServicesVSResources.WRN_AnalyzerChangedMessage, + category: FeaturesResources.ErrorCategory, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true); + [ImportingConstructor] public AnalyzerFileWatcherService( VisualStudioWorkspaceImpl workspace, @@ -67,21 +75,12 @@ internal void RemoveAnalyzerAlreadyLoadedDiagnostics(ProjectId projectId, string private void RaiseAnalyzerChangedWarning(ProjectId projectId, string analyzerPath) { - string message = string.Format(ServicesVSResources.WRN_AnalyzerChangedMessage, analyzerPath); - - DiagnosticData data = new DiagnosticData( - IDEDiagnosticIds.AnalyzerChangedId, - FeaturesResources.ErrorCategory, - message, - ServicesVSResources.WRN_AnalyzerChangedMessage, - severity: DiagnosticSeverity.Warning, - isEnabledByDefault: true, - warningLevel: 0, - workspace: _workspace, - projectId: projectId, - title: ServicesVSResources.WRN_AnalyzerChangedTitle); - - _updateSource.UpdateDiagnosticsForProject(projectId, Tuple.Create(s_analyzerChangedErrorId, analyzerPath), SpecializedCollections.SingletonEnumerable(data)); + var messageArguments = new string[] { analyzerPath }; + DiagnosticData diagnostic; + if (DiagnosticData.TryCreate(_analyzerChangedRule, messageArguments, projectId, _workspace, out diagnostic)) + { + _updateSource.UpdateDiagnosticsForProject(projectId, Tuple.Create(s_analyzerChangedErrorId, analyzerPath), SpecializedCollections.SingletonEnumerable(diagnostic)); + } } private DateTime? GetLastUpdateTimeUtc(string fullPath) diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/AbstractProject.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/AbstractProject.cs index 4ebe3f7c4113b..64cb20e7d99ba 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/AbstractProject.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/AbstractProject.cs @@ -99,6 +99,14 @@ internal abstract partial class AbstractProject : IVisualStudioHostProject private static readonly EventHandler s_additionalDocumentClosingEventHandler = OnAdditionalDocumentClosing; private static readonly EventHandler s_additionalDocumentUpdatedOnDiskEventHandler = OnAdditionalDocumentUpdatedOnDisk; + private readonly DiagnosticDescriptor _errorReadingRulesetRule = new DiagnosticDescriptor( + id: IDEDiagnosticIds.ErrorReadingRulesetId, + title: ServicesVSResources.ERR_CantReadRulesetFileTitle, + messageFormat: ServicesVSResources.ERR_CantReadRulesetFileMessage, + category: FeaturesResources.ErrorCategory, + defaultSeverity: DiagnosticSeverity.Error, + isEnabledByDefault: true); + public AbstractProject( VisualStudioProjectTracker projectTracker, Func reportExternalErrorCreatorOpt, @@ -1166,20 +1174,12 @@ protected void UpdateRuleSetError(IRuleSetFile ruleSetFile) } else { - string message = string.Format(ServicesVSResources.ERR_CantReadRulesetFileMessage, ruleSetFile.FilePath, ruleSetFile.GetException().Message); - var data = new DiagnosticData( - id: IDEDiagnosticIds.ErrorReadingRulesetId, - category: FeaturesResources.ErrorCategory, - message: message, - enuMessageForBingSearch: ServicesVSResources.ERR_CantReadRulesetFileMessage, - severity: DiagnosticSeverity.Error, - isEnabledByDefault: true, - warningLevel: 0, - workspace: this.Workspace, - projectId: this.Id, - title: ServicesVSResources.ERR_CantReadRulesetFileTitle); - - this.HostDiagnosticUpdateSource.UpdateDiagnosticsForProject(this.Id, RuleSetErrorId, SpecializedCollections.SingletonEnumerable(data)); + var messageArguments = new string[] { ruleSetFile.FilePath, ruleSetFile.GetException().Message }; + DiagnosticData diagnostic; + if (DiagnosticData.TryCreate(_errorReadingRulesetRule, messageArguments, this.Id, this.Workspace, out diagnostic)) + { + this.HostDiagnosticUpdateSource.UpdateDiagnosticsForProject(this.Id, RuleSetErrorId, SpecializedCollections.SingletonEnumerable(diagnostic)); + } } } diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs index b658451ee2bdb..746061315f242 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs @@ -431,6 +431,35 @@ public static DiagnosticData Create(Document document, Diagnostic diagnostic) isSuppressed: diagnostic.IsSuppressed); } + public static bool TryCreate(DiagnosticDescriptor descriptor, string[] messageArguments, ProjectId projectId, Workspace workspace, out DiagnosticData diagnosticData, CancellationToken cancellationToken = default(CancellationToken)) + { + diagnosticData = null; + var project = workspace.CurrentSolution.GetProject(projectId); + if (project == null) + { + return false; + } + + var diagnostic = Diagnostic.Create(descriptor, Location.None, messageArguments); + if (project.SupportsCompilation) + { + // Get diagnostic with effective severity. + // Additionally, if the diagnostic was suppressed by a source suppression, effectiveDiagnostics will have a diagnostic with IsSuppressed = true. + var compilation = project.GetCompilationAsync(cancellationToken).WaitAndGetResult(cancellationToken); + var effectiveDiagnostics = CompilationWithAnalyzers.GetEffectiveDiagnostics(SpecializedCollections.SingletonEnumerable(diagnostic), compilation); + if (effectiveDiagnostics == null || effectiveDiagnostics.IsEmpty()) + { + // Rule is disabled by compilation options. + return false; + } + + diagnostic = effectiveDiagnostics.Single(); + } + + diagnosticData = diagnostic.ToDiagnosticData(project); + return true; + } + private static void GetLocationInfo(Document document, Location location, out TextSpan sourceSpan, out FileLinePositionSpan originalLineInfo, out FileLinePositionSpan mappedLineInfo) { var diagnosticSpanMappingService = document.Project.Solution.Workspace.Services.GetService();