From 8921fa9dc83fa4f2f91eaef5dc626efd14b0fc2d Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Wed, 18 Feb 2015 13:30:36 -0800 Subject: [PATCH] AnalyzerDiagnosticUpdateSource - Working implementation --- .../Core/AnalyzerDriver/AnalyzerManager.cs | 17 +--- .../DiagnosticProviderTestUtilities.cs | 6 +- .../AnalyzerDiagnosticUpdateSource.cs | 41 +++++++-- ...AnalyzerExceptionDiagnosticUpdateSource.cs | 90 ------------------- .../BaseDiagnosticIncrementalAnalyzer.cs | 19 ---- .../Core/Diagnostics/DiagnosticData.cs | 1 + .../EngineV1/DiagnosticAnalyzerDriver.cs | 54 +++++++++-- .../EngineV2/DiagnosticIncrementalAnalyzer.cs | 31 ++++++- ...orkspaceAnalyzerExceptionDiagnosticArgs.cs | 8 +- .../Diagnostics/WorkspaceAnalyzerManager.cs | 3 +- src/Features/Core/Features.csproj | 6 +- src/Features/Core/PublicAPI.txt | 0 .../DiagnosticAnalyzerExtensions.cs | 6 +- .../ProjectSystem/VisualStudioAnalyzer.cs | 2 + .../TaskList/HostDiagnosticUpdateSource.cs | 15 +++- .../VisualStudioAnalyzerTests.vb | 2 +- 16 files changed, 145 insertions(+), 156 deletions(-) delete mode 100644 src/Features/Core/Diagnostics/AnalyzerExceptionDiagnosticUpdateSource.cs create mode 100644 src/Features/Core/PublicAPI.txt diff --git a/src/Compilers/Core/AnalyzerDriver/AnalyzerManager.cs b/src/Compilers/Core/AnalyzerDriver/AnalyzerManager.cs index c3e8ea06f3b..dda88520bd6 100644 --- a/src/Compilers/Core/AnalyzerDriver/AnalyzerManager.cs +++ b/src/Compilers/Core/AnalyzerDriver/AnalyzerManager.cs @@ -25,8 +25,8 @@ internal class AnalyzerManager // This map stores the tasks to compute HostSessionStartAnalysisScope for session wide analyzer actions, i.e. AnalyzerActions registered by analyzer's Initialize method. // These are run only once per every analyzer. - private ImmutableDictionary> _sessionScopeMap = - ImmutableDictionary>.Empty; + private ConditionalWeakTable> _sessionScopeMap = + new ConditionalWeakTable>(); // This map stores the tasks to compute HostCompilationStartAnalysisScope for per-compilation analyzer actions, i.e. AnalyzerActions registered by analyzer's CompilationStartActions. // Compilation start actions will get executed once per-each compilation as user might want to return different set of custom actions for each compilation. @@ -75,16 +75,8 @@ internal class AnalyzerManager }, cancellationToken); }; - var task = ImmutableInterlocked.GetOrAdd(ref _sessionScopeMap, analyzer, getTask); - - // Retry cancelled task. - if (task.Status == TaskStatus.Canceled) - { - ImmutableInterlocked.TryUpdate(ref _sessionScopeMap, analyzer, getTask(analyzer), task); - return _sessionScopeMap[analyzer]; - } - - return task; + var callback = new ConditionalWeakTable>.CreateValueCallback(getTask); + return _sessionScopeMap.GetValue(analyzer, callback); } /// @@ -115,7 +107,6 @@ internal class AnalyzerManager /// public ImmutableArray GetSupportedDiagnosticDescriptors( DiagnosticAnalyzer analyzer, - Action addDiagnostic, Func continueOnAnalyzerException, CancellationToken cancellationToken) { diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticProviderTestUtilities.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticProviderTestUtilities.cs index 0fd765c139b..2e8cf53410f 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticProviderTestUtilities.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticProviderTestUtilities.cs @@ -27,7 +27,7 @@ private static IEnumerable GetDiagnostics(DiagnosticAnalyzer analyze var analyzer = analyzerOpt ?? DiagnosticExtensions.GetCompilerDiagnosticAnalyzer(project.Language); var exceptionDiagnostics = new List(); - EventHandler addExceptionDiagnostic = (sender, args) => + EventHandler addExceptionDiagnostic = (sender, args) => { if (args.FaultedAnalyzer == analyzer) { @@ -35,7 +35,7 @@ private static IEnumerable GetDiagnostics(DiagnosticAnalyzer analyze } }; - AnalyzerDriverHelper.AnalyzerExceptionDiagnostic += addExceptionDiagnostic; + DiagnosticAnalyzerDriver.AnalyzerExceptionDiagnostic += addExceptionDiagnostic; if (getDocumentDiagnostics) { @@ -101,7 +101,7 @@ private static IEnumerable GetDiagnostics(DiagnosticAnalyzer analyze } } - AnalyzerDriverHelper.AnalyzerExceptionDiagnostic -= addExceptionDiagnostic; + DiagnosticAnalyzerDriver.AnalyzerExceptionDiagnostic -= addExceptionDiagnostic; return documentDiagnostics.Concat(projectDiagnostics).Concat(exceptionDiagnostics); } diff --git a/src/Features/Core/Diagnostics/AnalyzerDiagnosticUpdateSource.cs b/src/Features/Core/Diagnostics/AnalyzerDiagnosticUpdateSource.cs index 0d0d6a42fe1..e143bf6c32e 100644 --- a/src/Features/Core/Diagnostics/AnalyzerDiagnosticUpdateSource.cs +++ b/src/Features/Core/Diagnostics/AnalyzerDiagnosticUpdateSource.cs @@ -19,12 +19,16 @@ internal sealed class AnalyzerDiagnosticUpdateSource : IDiagnosticUpdateSource [ImportingConstructor] public AnalyzerDiagnosticUpdateSource() { - BaseDiagnosticIncrementalAnalyzer.AnalyzerExceptionDiagnostic += OnAnalyzerExceptionDiagnostic; + // Register for exception diagnostics from both engines. + EngineV1.DiagnosticAnalyzerDriver.AnalyzerExceptionDiagnostic += OnAnalyzerExceptionDiagnostic; + EngineV2.DiagnosticIncrementalAnalyzer.AnalyzerExceptionDiagnostic += OnAnalyzerExceptionDiagnostic; } ~AnalyzerDiagnosticUpdateSource() { - BaseDiagnosticIncrementalAnalyzer.AnalyzerExceptionDiagnostic -= OnAnalyzerExceptionDiagnostic; + // Unregister for exception diagnostics from both engines. + EngineV1.DiagnosticAnalyzerDriver.AnalyzerExceptionDiagnostic -= OnAnalyzerExceptionDiagnostic; + EngineV2.DiagnosticIncrementalAnalyzer.AnalyzerExceptionDiagnostic -= OnAnalyzerExceptionDiagnostic; } public bool SupportGetDiagnostics @@ -56,12 +60,33 @@ private void OnAnalyzerExceptionDiagnostic(object sender, WorkspaceAnalyzerExcep Contract.ThrowIfFalse(AnalyzerDriverHelper.IsAnalyzerExceptionDiagnostic(args.Diagnostic)); var diagnosticData = DiagnosticData.Create(args.Workspace, args.Diagnostic); + ImmutableArray existingDiagnostics; + if (_analyzerExceptionDiagnosticsMap.TryGetValue(args.FaultedAnalyzer, out existingDiagnostics)) + { + if (existingDiagnostics.Contains(diagnosticData)) + { + // don't fire duplicate diagnostics. + return; + } + } + else + { + existingDiagnostics = ImmutableArray.Empty; + } + var dxs = ImmutableInterlocked.AddOrUpdate(ref _analyzerExceptionDiagnosticsMap, args.FaultedAnalyzer, ImmutableArray.Create(diagnosticData), - (a, existing) => existing.Add(diagnosticData).Distinct()); + (a, existing) => + { + var newDiags = existing.Add(diagnosticData).Distinct(); + return newDiags.Length == existing.Length ? existing : newDiags; + }); - RaiseDiagnosticsUpdated(MakeArgs(args.FaultedAnalyzer, dxs, args.Workspace)); + if (dxs.Length > existingDiagnostics.Length) + { + RaiseDiagnosticsUpdated(MakeArgs(args.FaultedAnalyzer, dxs, args.Workspace, args.Project)); + } } public void ClearDiagnostics(DiagnosticAnalyzer analyzer, Workspace workspace) @@ -69,19 +94,19 @@ public void ClearDiagnostics(DiagnosticAnalyzer analyzer, Workspace workspace) ImmutableArray existing; if (ImmutableInterlocked.TryRemove(ref _analyzerExceptionDiagnosticsMap, analyzer, out existing)) { - RaiseDiagnosticsUpdated(MakeArgs(analyzer, ImmutableArray.Empty, workspace)); + RaiseDiagnosticsUpdated(MakeArgs(analyzer, ImmutableArray.Empty, workspace, project: null)); } } - private DiagnosticsUpdatedArgs MakeArgs(DiagnosticAnalyzer analyzer, ImmutableArray items, Workspace workspace) + private DiagnosticsUpdatedArgs MakeArgs(DiagnosticAnalyzer analyzer, ImmutableArray items, Workspace workspace, Project project) { var id = WorkspaceAnalyzerManager.GetUniqueIdForAnalyzer(analyzer); return new DiagnosticsUpdatedArgs( id: Tuple.Create(this, id), workspace: workspace, - solution: null, - projectId: null, + solution: project?.Solution, + projectId: project?.Id, documentId: null, diagnostics: items); } diff --git a/src/Features/Core/Diagnostics/AnalyzerExceptionDiagnosticUpdateSource.cs b/src/Features/Core/Diagnostics/AnalyzerExceptionDiagnosticUpdateSource.cs deleted file mode 100644 index e5553e4bf3a..00000000000 --- a/src/Features/Core/Diagnostics/AnalyzerExceptionDiagnosticUpdateSource.cs +++ /dev/null @@ -1,90 +0,0 @@ -// 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.Immutable; -using System.Composition; -using System.Linq; -using System.Threading; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.Diagnostics -{ - [Export(typeof(IDiagnosticUpdateSource))] - [Export(typeof(AnalyzerExceptionDiagnosticUpdateSource))] - [Shared] - internal sealed class AnalyzerExceptionDiagnosticUpdateSource : IDiagnosticUpdateSource - { - private static ImmutableDictionary> _analyzerExceptionDiagnosticsMap = - ImmutableDictionary>.Empty; - - public bool SupportGetDiagnostics - { - get - { - return false; - } - } - - public ImmutableArray GetDiagnostics(Workspace workspace, ProjectId projectId, DocumentId documentId, object id, CancellationToken cancellationToken) - { - return ImmutableArray.Empty; - } - - public event EventHandler DiagnosticsUpdated; - - private void RaiseDiagnosticsUpdated(DiagnosticsUpdatedArgs args) - { - var updated = this.DiagnosticsUpdated; - if (updated != null) - { - updated(this, args); - } - } - - public void ReportDiagnostics(DiagnosticAnalyzer analyzer, ImmutableArray diagnostics, Project project) - { - Contract.ThrowIfFalse(diagnostics.All(AnalyzerDriverHelper.IsAnalyzerExceptionDiagnostic)); - - var dxs = diagnostics.Select(d => DiagnosticData.Create(project, d)).Distinct().ToImmutableArray(); - dxs = ImmutableInterlocked.AddOrUpdate(ref _analyzerExceptionDiagnosticsMap, - analyzer, - dxs, - (a, existing) => existing.AddRange(dxs).Distinct()); - - RaiseDiagnosticsUpdated(MakeArgs(analyzer, dxs, project.Solution.Workspace, project)); - } - - public void ClearDiagnostics(DiagnosticAnalyzer analyzer, Workspace workspace) - { - ImmutableArray existing; - if (ImmutableInterlocked.TryRemove(ref _analyzerExceptionDiagnosticsMap, analyzer, out existing)) - { - RaiseDiagnosticsUpdated(MakeArgs(analyzer, ImmutableArray.Empty, workspace, project: null)); - } - } - - private DiagnosticsUpdatedArgs MakeArgs(DiagnosticAnalyzer analyzer, ImmutableArray items, Workspace workspace, Project project) - { - var id = WorkspaceAnalyzerManager.GetUniqueIdForAnalyzer(analyzer); - - return new DiagnosticsUpdatedArgs( - id: Tuple.Create(this, id), - workspace: workspace, - solution: project != null ? project.Solution : workspace.CurrentSolution, - projectId: project?.Id, - documentId: null, - diagnostics: items); - } - - internal ImmutableArray TestOnly_GetExceptionDiagnostics(DiagnosticAnalyzer analyzer) - { - ImmutableArray diagnostics; - if (!_analyzerExceptionDiagnosticsMap.TryGetValue(analyzer, out diagnostics)) - { - diagnostics = ImmutableArray.Empty; - } - - return diagnostics; - } - } -} diff --git a/src/Features/Core/Diagnostics/BaseDiagnosticIncrementalAnalyzer.cs b/src/Features/Core/Diagnostics/BaseDiagnosticIncrementalAnalyzer.cs index a88003dbf8e..b9bb8b37658 100644 --- a/src/Features/Core/Diagnostics/BaseDiagnosticIncrementalAnalyzer.cs +++ b/src/Features/Core/Diagnostics/BaseDiagnosticIncrementalAnalyzer.cs @@ -16,12 +16,6 @@ internal abstract class BaseDiagnosticIncrementalAnalyzer : IIncrementalAnalyzer protected BaseDiagnosticIncrementalAnalyzer(Workspace workspace) { this.Workspace = workspace; - AnalyzerDriverHelper.AnalyzerExceptionDiagnostic += OnAnalyzerExceptionDiagnostic; - } - - ~BaseDiagnosticIncrementalAnalyzer() - { - AnalyzerDriverHelper.AnalyzerExceptionDiagnostic -= OnAnalyzerExceptionDiagnostic; } #region IIncrementalAnalyzer @@ -56,18 +50,5 @@ public virtual bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedE public virtual void LogAnalyzerCountSummary() { } - - internal static event EventHandler AnalyzerExceptionDiagnostic; - - private void OnAnalyzerExceptionDiagnostic(object sender, AnalyzerExceptionDiagnosticArgs args) - { - var workspaceArgs = new WorkspaceAnalyzerExceptionDiagnosticArgs(args, Workspace); - AnalyzerExceptionDiagnostic?.Invoke(this, workspaceArgs); - } - - internal static void OnAnalyzerExceptionDiagnostic(object sender, WorkspaceAnalyzerExceptionDiagnosticArgs args) - { - AnalyzerExceptionDiagnostic?.Invoke(sender, args); - } } } diff --git a/src/Features/Core/Diagnostics/DiagnosticData.cs b/src/Features/Core/Diagnostics/DiagnosticData.cs index 92f77fb1c3e..622f0db845b 100644 --- a/src/Features/Core/Diagnostics/DiagnosticData.cs +++ b/src/Features/Core/Diagnostics/DiagnosticData.cs @@ -294,6 +294,7 @@ public static DiagnosticData Create(Workspace workspace, Diagnostic diagnostic) diagnostic.Descriptor.IsEnabledByDefault, diagnostic.WarningLevel, diagnostic.Descriptor.CustomTags.AsImmutableOrEmpty(), + diagnostic.Properties, workspace, projectId: null, title: diagnostic.Descriptor.Title.ToString(CultureInfo.CurrentUICulture), diff --git a/src/Features/Core/Diagnostics/EngineV1/DiagnosticAnalyzerDriver.cs b/src/Features/Core/Diagnostics/EngineV1/DiagnosticAnalyzerDriver.cs index 4fa41f8a885..15072a00e50 100644 --- a/src/Features/Core/Diagnostics/EngineV1/DiagnosticAnalyzerDriver.cs +++ b/src/Features/Core/Diagnostics/EngineV1/DiagnosticAnalyzerDriver.cs @@ -44,6 +44,8 @@ internal class DiagnosticAnalyzerDriver private AnalyzerOptions _analyzerOptions = null; + internal static event EventHandler AnalyzerExceptionDiagnostic; + public DiagnosticAnalyzerDriver(Document document, TextSpan? span, SyntaxNode root, LogAggregator logAggregator, CancellationToken cancellationToken) : this(document, span, root, document.Project.LanguageServices.GetService(), cancellationToken) { @@ -134,6 +136,26 @@ public ISyntaxNodeAnalyzerService SyntaxNodeAnalyzerService } } + private EventHandler RegisterAnalyzerExceptionDiagnosticHandler(DiagnosticAnalyzer analyzer) + { + EventHandler handler = (sender, args) => + { + if (args.FaultedAnalyzer == analyzer) + { + var workspaceArgs = new WorkspaceAnalyzerExceptionDiagnosticArgs(args, this.Project.Solution.Workspace, this.Project); + AnalyzerExceptionDiagnostic?.Invoke(this, workspaceArgs); + } + }; + + AnalyzerDriverHelper.AnalyzerExceptionDiagnostic += handler; + return handler; + } + + private void UnregisterAnalyzerExceptionDiagnosticHandler(EventHandler handler) + { + AnalyzerDriverHelper.AnalyzerExceptionDiagnostic -= handler; + } + private ImmutableArray GetDeclarationInfos(SemanticModel model) { if (_lazyDeclarationInfos == null) @@ -265,7 +287,9 @@ public async Task> GetSyntaxDiagnosticsAsync(Diagnost } } - var analyzerActions = await this.GetAnalyzerActionsAsync(analyzer).ConfigureAwait(false); + var handler = RegisterAnalyzerExceptionDiagnosticHandler(analyzer); + + var analyzerActions = await this.GetAnalyzerActionsCoreAsync(analyzer).ConfigureAwait(false); DiagnosticAnalyzerLogger.UpdateAnalyzerTypeCount(analyzer, analyzerActions, (DiagnosticLogAggregator)_logAggregator); @@ -283,6 +307,7 @@ public async Task> GetSyntaxDiagnosticsAsync(Diagnost return ImmutableArray.Empty; } + UnregisterAnalyzerExceptionDiagnosticHandler(handler); return GetFilteredDocumentDiagnostics(diagnostics, compilation).ToImmutableArray(); } } @@ -325,15 +350,23 @@ internal void ReportAnalyzerExceptionDiagnostic(DiagnosticAnalyzer analyzer, Dia } } - var args = new WorkspaceAnalyzerExceptionDiagnosticArgs(analyzer, exceptionDiagnostic, this.Project.Solution.Workspace); - DiagnosticIncrementalAnalyzer.OnAnalyzerExceptionDiagnostic(this, args); + var workspaceArgs = new WorkspaceAnalyzerExceptionDiagnosticArgs(analyzer, exceptionDiagnostic, this.Project.Solution.Workspace, this.Project); + AnalyzerExceptionDiagnostic?.Invoke(this, workspaceArgs); } public async Task GetAnalyzerActionsAsync(DiagnosticAnalyzer analyzer) { - Contract.ThrowIfFalse(_project.SupportsCompilation); + var handler = RegisterAnalyzerExceptionDiagnosticHandler(analyzer); + var actions = await GetAnalyzerActionsCoreAsync(analyzer).ConfigureAwait(false); + UnregisterAnalyzerExceptionDiagnosticHandler(handler); + return actions; + } - var compilation = await _project.GetCompilationAsync(_cancellationToken).ConfigureAwait(false); + private async Task GetAnalyzerActionsCoreAsync(DiagnosticAnalyzer analyzer) + { + var compilation = _project.SupportsCompilation ? + await _project.GetCompilationAsync(_cancellationToken).ConfigureAwait(false) : + null; var analyzerActions = await AnalyzerManager.Default.GetAnalyzerActionsAsync(analyzer, compilation, _analyzerOptions, CatchAnalyzerException, _cancellationToken).ConfigureAwait(false); DiagnosticAnalyzerLogger.UpdateAnalyzerTypeCount(analyzer, analyzerActions, (DiagnosticLogAggregator)_logAggregator); return analyzerActions; @@ -373,8 +406,9 @@ public async Task> GetSemanticDiagnosticsAsync(Diagno } else { - var analyzerActions = await GetAnalyzerActionsAsync(analyzer).ConfigureAwait(false); + var handler = RegisterAnalyzerExceptionDiagnosticHandler(analyzer); + var analyzerActions = await GetAnalyzerActionsCoreAsync(analyzer).ConfigureAwait(false); if (analyzerActions != null) { // SemanticModel actions. @@ -409,6 +443,8 @@ public async Task> GetSemanticDiagnosticsAsync(Diagno } } } + + UnregisterAnalyzerExceptionDiagnosticHandler(handler); } return GetFilteredDocumentDiagnostics(diagnostics, compilation).ToImmutableArray(); @@ -464,8 +500,10 @@ private async Task GetCompilationDiagnosticsAsync(DiagnosticAnalyzer analyzer, L { var localDiagnostics = pooledObject.Object; + var handler = RegisterAnalyzerExceptionDiagnosticHandler(analyzer); + // Get all the analyzer actions, including the per-compilation actions. - var analyzerActions = await GetAnalyzerActionsAsync(analyzer).ConfigureAwait(false); + var analyzerActions = await GetAnalyzerActionsCoreAsync(analyzer).ConfigureAwait(false); if (analyzerActions.CompilationEndActionsCount > 0 && analyzerActions.CompilationStartActionsCount > 0 && forceAnalyzeAllDocuments != null) { @@ -485,6 +523,8 @@ private async Task GetCompilationDiagnosticsAsync(DiagnosticAnalyzer analyzer, L // CompilationEnd actions. var compilation = await _project.GetCompilationAsync(_cancellationToken).ConfigureAwait(false); AnalyzerDriverHelper.ExecuteCompilationEndActions(analyzerActions, compilation, _analyzerOptions, localDiagnostics.Add, CatchAnalyzerException, _cancellationToken); + UnregisterAnalyzerExceptionDiagnosticHandler(handler); + var filteredDiagnostics = CompilationWithAnalyzers.GetEffectiveDiagnostics(localDiagnostics, compilation); diagnostics.AddRange(filteredDiagnostics); } diff --git a/src/Features/Core/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/Features/Core/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index 37f43f9e0e3..99c14f7c741 100644 --- a/src/Features/Core/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/Features/Core/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -1,5 +1,6 @@ // 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.Generic; using System.Collections.Immutable; using System.Linq; @@ -16,6 +17,8 @@ internal class DiagnosticIncrementalAnalyzer : BaseDiagnosticIncrementalAnalyzer private readonly DiagnosticAnalyzerService _owner; private readonly WorkspaceAnalyzerManager _workspaceAnalyzerManager; + internal static event EventHandler AnalyzerExceptionDiagnostic; + public DiagnosticIncrementalAnalyzer(DiagnosticAnalyzerService owner, int correlationId, Workspace workspace, WorkspaceAnalyzerManager workspaceAnalyzerManager) : base(workspace) { @@ -155,11 +158,37 @@ private async Task> GetProjectDiagnosticsAsync(Pr var analyzers = _workspaceAnalyzerManager.CreateDiagnosticAnalyzers(project); + var handler = RegisterAnalyzerExceptionDiagnosticHandler(analyzers, project); + var compilationWithAnalyzer = compilation.WithAnalyzers(analyzers, project.AnalyzerOptions, cancellationToken); // REVIEW: this API is a bit strange. // if getting diagnostic is cancelled, it has to create new compilation and do everything from scretch again? - return GetDiagnosticData(project, await compilationWithAnalyzer.GetAnalyzerDiagnosticsAsync().ConfigureAwait(false)).ToImmutableArrayOrEmpty(); + var dxs = GetDiagnosticData(project, await compilationWithAnalyzer.GetAnalyzerDiagnosticsAsync().ConfigureAwait(false)).ToImmutableArrayOrEmpty(); + + UnregisterAnalyzerExceptionDiagnosticHandler(handler); + + return dxs; + } + + private EventHandler RegisterAnalyzerExceptionDiagnosticHandler(ImmutableArray analyzers, Project project) + { + EventHandler handler = (sender, args) => + { + if (analyzers.Contains(args.FaultedAnalyzer)) + { + var workspaceArgs = new WorkspaceAnalyzerExceptionDiagnosticArgs(args, project.Solution.Workspace, project); + AnalyzerExceptionDiagnostic?.Invoke(this, workspaceArgs); + } + }; + + AnalyzerDriverHelper.AnalyzerExceptionDiagnostic += handler; + return handler; + } + + private void UnregisterAnalyzerExceptionDiagnosticHandler(EventHandler handler) + { + AnalyzerDriverHelper.AnalyzerExceptionDiagnostic -= handler; } private IEnumerable GetDiagnosticData(Project project, ImmutableArray diagnostics) diff --git a/src/Features/Core/Diagnostics/WorkspaceAnalyzerExceptionDiagnosticArgs.cs b/src/Features/Core/Diagnostics/WorkspaceAnalyzerExceptionDiagnosticArgs.cs index 23dd4632410..f6c6412ddef 100644 --- a/src/Features/Core/Diagnostics/WorkspaceAnalyzerExceptionDiagnosticArgs.cs +++ b/src/Features/Core/Diagnostics/WorkspaceAnalyzerExceptionDiagnosticArgs.cs @@ -9,17 +9,19 @@ internal class WorkspaceAnalyzerExceptionDiagnosticArgs : EventArgs public readonly Diagnostic Diagnostic; public readonly DiagnosticAnalyzer FaultedAnalyzer; public readonly Workspace Workspace; + public readonly Project Project; - public WorkspaceAnalyzerExceptionDiagnosticArgs(AnalyzerExceptionDiagnosticArgs args, Workspace workspace) - : this(args.FaultedAnalyzer, args.Diagnostic, workspace) + public WorkspaceAnalyzerExceptionDiagnosticArgs(AnalyzerExceptionDiagnosticArgs args, Workspace workspace, Project project) + : this(args.FaultedAnalyzer, args.Diagnostic, workspace, project) { } - public WorkspaceAnalyzerExceptionDiagnosticArgs(DiagnosticAnalyzer analyzer, Diagnostic diagnostic, Workspace workspace) + public WorkspaceAnalyzerExceptionDiagnosticArgs(DiagnosticAnalyzer analyzer, Diagnostic diagnostic, Workspace workspace, Project project) { this.FaultedAnalyzer = analyzer; this.Diagnostic = diagnostic; this.Workspace = workspace; + this.Project = project; } } } diff --git a/src/Features/Core/Diagnostics/WorkspaceAnalyzerManager.cs b/src/Features/Core/Diagnostics/WorkspaceAnalyzerManager.cs index cab155d9577..d99808a8325 100644 --- a/src/Features/Core/Diagnostics/WorkspaceAnalyzerManager.cs +++ b/src/Features/Core/Diagnostics/WorkspaceAnalyzerManager.cs @@ -77,10 +77,9 @@ public string GetAnalyzerReferenceIdentity(AnalyzerReference reference) public ImmutableArray GetDiagnosticDescriptors(DiagnosticAnalyzer analyzer) { // TODO: report diagnostics from exceptions thrown in DiagnosticAnalyzer.SupportedDiagnostics - Action dummyAddDiagnostic = _ => { }; Func continueOnAnalyzerException = (ex, a) => !AnalyzerHelper.IsBuiltInAnalyzer(analyzer); - return AnalyzerManager.Default.GetSupportedDiagnosticDescriptors(analyzer, dummyAddDiagnostic, continueOnAnalyzerException, CancellationToken.None); + return AnalyzerManager.Default.GetSupportedDiagnosticDescriptors(analyzer, continueOnAnalyzerException, CancellationToken.None); } /// diff --git a/src/Features/Core/Features.csproj b/src/Features/Core/Features.csproj index dbabf8a16f8..2536cd5e175 100644 --- a/src/Features/Core/Features.csproj +++ b/src/Features/Core/Features.csproj @@ -536,11 +536,13 @@ - + + + - + \ No newline at end of file diff --git a/src/Features/Core/PublicAPI.txt b/src/Features/Core/PublicAPI.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/Features/Core/Shared/Extensions/DiagnosticAnalyzerExtensions.cs b/src/Features/Core/Shared/Extensions/DiagnosticAnalyzerExtensions.cs index 3a64339affe..0f9528d9b71 100644 --- a/src/Features/Core/Shared/Extensions/DiagnosticAnalyzerExtensions.cs +++ b/src/Features/Core/Shared/Extensions/DiagnosticAnalyzerExtensions.cs @@ -27,11 +27,7 @@ public static DiagnosticAnalyzerCategory GetDiagnosticAnalyzerCategory(this Diag // to be able to operate on a limited span of the document. In practical terms, no analyzer // can have both SemanticDocumentAnalysis and SemanticSpanAnalysis as categories. bool cantSupportSemanticSpanAnalysis = false; - var analyzerActions = AnalyzerManager.Default.GetAnalyzerActionsAsync(analyzer, - null, - null, - driver.CatchAnalyzerExceptionHandler, - driver.CancellationToken).WaitAndGetResult(driver.CancellationToken); + var analyzerActions = driver.GetAnalyzerActionsAsync(analyzer).WaitAndGetResult(driver.CancellationToken); if (analyzerActions != null) { if (analyzerActions.SyntaxTreeActionsCount > 0) diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioAnalyzer.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioAnalyzer.cs index 129c5f505a1..58caebcebea 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioAnalyzer.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioAnalyzer.cs @@ -125,6 +125,8 @@ public void Dispose() { _hostDiagnosticUpdateSource.ClearDiagnosticsForProject(_projectId, this); } + + _hostDiagnosticUpdateSource.ClearAnalyzerSpecificDiagnostics((AnalyzerFileReference)_analyzerReference, _language); } _analyzerLoadErrors = null; diff --git a/src/VisualStudio/Core/Def/Implementation/TaskList/HostDiagnosticUpdateSource.cs b/src/VisualStudio/Core/Def/Implementation/TaskList/HostDiagnosticUpdateSource.cs index a4c25831d9d..710ae5b0b0b 100644 --- a/src/VisualStudio/Core/Def/Implementation/TaskList/HostDiagnosticUpdateSource.cs +++ b/src/VisualStudio/Core/Def/Implementation/TaskList/HostDiagnosticUpdateSource.cs @@ -17,14 +17,17 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.TaskList internal sealed class HostDiagnosticUpdateSource : IDiagnosticUpdateSource { private readonly VisualStudioWorkspaceImpl _workspace; + private readonly AnalyzerDiagnosticUpdateSource _analyzerDiagnosticsSource; private readonly Dictionary> _diagnosticMap = new Dictionary>(); [ImportingConstructor] public HostDiagnosticUpdateSource( - VisualStudioWorkspaceImpl workspace) + VisualStudioWorkspaceImpl workspace, + AnalyzerDiagnosticUpdateSource analyzerDiagnosticsSource) { _workspace = workspace; + _analyzerDiagnosticsSource = analyzerDiagnosticsSource; } public event EventHandler DiagnosticsUpdated; @@ -77,7 +80,7 @@ public void ClearAllDiagnosticsForProject(ProjectId projectId) { RaiseDiagnosticsUpdatedForProject(projectId, key, SpecializedCollections.EmptyEnumerable()); } - } + } } public void ClearDiagnosticsForProject(ProjectId projectId, object key) @@ -94,5 +97,13 @@ public void ClearDiagnosticsForProject(ProjectId projectId, object key) } } } + + public void ClearAnalyzerSpecificDiagnostics(AnalyzerFileReference analyzerReference, string language) + { + foreach (var analyzer in analyzerReference.GetAnalyzers(language)) + { + _analyzerDiagnosticsSource.ClearDiagnostics(analyzer, _workspace); + } + } } } diff --git a/src/VisualStudio/Core/Test/ProjectSystemShim/VisualStudioAnalyzerTests.vb b/src/VisualStudio/Core/Test/ProjectSystemShim/VisualStudioAnalyzerTests.vb index 4ca0b67bb22..32542d4b837 100644 --- a/src/VisualStudio/Core/Test/ProjectSystemShim/VisualStudioAnalyzerTests.vb +++ b/src/VisualStudio/Core/Test/ProjectSystemShim/VisualStudioAnalyzerTests.vb @@ -22,7 +22,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim Public Sub AnalyzerErrorsAreUpdated() - Dim hostDiagnosticUpdateSource = New HostDiagnosticUpdateSource(Nothing) + Dim hostDiagnosticUpdateSource = New HostDiagnosticUpdateSource(Nothing, Nothing) Dim file = Path.GetTempFileName() Dim eventHandler = New EventHandlers(file) -- GitLab