diff --git a/src/Compilers/Core/CodeAnalysisTest/Diagnostics/CompilationWithAnalyzersTests.cs b/src/Compilers/Core/CodeAnalysisTest/Diagnostics/CompilationWithAnalyzersTests.cs index c89a4925ec0464ab92ca39d1c80768e709864e41..dcdef4057db66c8e1fd7531928ee1b1c44b0dd8e 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Diagnostics/CompilationWithAnalyzersTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Diagnostics/CompilationWithAnalyzersTests.cs @@ -4,11 +4,13 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Threading; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; +using static Microsoft.CodeAnalysis.CommonDiagnosticAnalyzers; namespace Microsoft.CodeAnalysis.UnitTests.Diagnostics { @@ -45,5 +47,22 @@ public void GetEffectiveDiagnostics() AssertEx.Equal(new[] { d1 }, filtered); } + + [Fact] + public void GetAnalyzerTelemetry() + { + var compilation = CSharpCompilation.Create("c", options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); + DiagnosticAnalyzer analyzer = new AnalyzerWithDisabledRules(); + var analyzers = ImmutableArray.Create(analyzer); + var analyzerOptions = new AnalyzerOptions(ImmutableArray.Empty); + var compWithAnalyzers = new CompilationWithAnalyzers(compilation, analyzers, analyzerOptions, CancellationToken.None); + + var analysisResult = compWithAnalyzers.GetAnalysisResultAsync(CancellationToken.None).Result; + Assert.Empty(analysisResult.CompilationDiagnostics); + + // Even though the analyzer registers a symbol action, it should never be invoked because all of its rules are disabled. + var analyzerTelemetry = compWithAnalyzers.GetAnalyzerTelemetryInfoAsync(analyzer, CancellationToken.None).Result; + Assert.Equal(0, analyzerTelemetry.SymbolActionsCount); + } } } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs index 6ba672dcbf7c91afbdf6d23a76f66bacc883ce89..daae9320e2c1cf1326e6700a7127e0582f238f34 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs @@ -51,6 +51,7 @@ internal partial class AnalysisState private readonly HashSet _treesWithGeneratedSourceEvents; private readonly HashSet _partialSymbolsWithGeneratedSourceEvents; private readonly CompilationData _compilationData; + private readonly CompilationOptions _compilationOptions; private bool _compilationStartGenerated; private bool _compilationEndGenerated; @@ -63,11 +64,12 @@ internal partial class AnalysisState private readonly ObjectPool> _compilationEventsPool; private readonly HashSet _pooledEventsWithAnyActionsSet; - public AnalysisState(ImmutableArray analyzers, CompilationData compilationData) + public AnalysisState(ImmutableArray analyzers, CompilationData compilationData, CompilationOptions compilationOptions) { _gate = new object(); _analyzerStateMap = CreateAnalyzerStateMap(analyzers, out _analyzerStates); _compilationData = compilationData; + _compilationOptions = compilationOptions; _pendingSourceEvents = new Dictionary>(); _pendingNonSourceEvents = new HashSet(); _lazyAnalyzerActionCountsMap = null; @@ -402,7 +404,7 @@ private async Task EnsureAnalyzerActionCountsInitializedAsync(AnalyzerDriver dri var builder = ImmutableDictionary.CreateBuilder(); foreach (var analyzer in _analyzerStateMap.Keys) { - var actionCounts = await driver.GetAnalyzerActionCountsAsync(analyzer, cancellationToken).ConfigureAwait(false); + var actionCounts = await driver.GetAnalyzerActionCountsAsync(analyzer, _compilationOptions, cancellationToken).ConfigureAwait(false); builder.Add(analyzer, actionCounts); } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerActionCounts.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerActionCounts.cs index a114cf50ccc395a3696fd693f84a21fce08a86dd..928a976ae57358784c1abd5dc8e84ff532f41a76 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerActionCounts.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerActionCounts.cs @@ -7,6 +7,8 @@ namespace Microsoft.CodeAnalysis.Diagnostics.Telemetry /// internal class AnalyzerActionCounts { + internal static readonly AnalyzerActionCounts Empty = new AnalyzerActionCounts(null); + internal AnalyzerActionCounts(AnalyzerActions analyzerActions) : this( analyzerActions?.CompilationStartActionsCount ?? 0, diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs index c89caed3d1c61baa35bb429591dfea2b8be7d1e0..30785813c3b203300b11cb79000e0ee7200c5128 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs @@ -1298,9 +1298,14 @@ protected bool IsGeneratedCode(SyntaxTree tree) protected bool DoNotAnalyzeGeneratedCode => _doNotAnalyzeGeneratedCode; - internal async Task GetAnalyzerActionCountsAsync(DiagnosticAnalyzer analyzer, CancellationToken cancellationToken) + internal async Task GetAnalyzerActionCountsAsync(DiagnosticAnalyzer analyzer, CompilationOptions compilationOptions, CancellationToken cancellationToken) { var executor = analyzerExecutor.WithCancellationToken(cancellationToken); + if (IsDiagnosticAnalyzerSuppressed(analyzer, compilationOptions, analyzerManager, executor)) + { + return AnalyzerActionCounts.Empty; + } + var analyzerActions = await analyzerManager.GetAnalyzerActionsAsync(analyzer, executor).ConfigureAwait(false); return new AnalyzerActionCounts(analyzerActions); } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs index fc1f3c03f9bcecd31042f8f7f2a984a4cf5a219d..a442abecc28ee99f9236a88cf917669d844b345d 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs @@ -129,7 +129,7 @@ private CompilationWithAnalyzers(Compilation compilation, ImmutableArray(() => _compilation.AnalyzerForLanguage(analyzers, AnalyzerManager.Instance)); _executingConcurrentTreeTasksOpt = analysisOptions.ConcurrentAnalysis ? new Dictionary>() : null; @@ -412,7 +412,7 @@ private async Task ComputeAnalyzerDiagnosticsWithoutStateTrackingAsync(Cancellat var analyzerActionCounts = new Dictionary(analyzers.Length); foreach (var analyzer in analyzers) { - var actionCounts = await driver.GetAnalyzerActionCountsAsync(analyzer, cancellationToken).ConfigureAwait(false); + var actionCounts = await driver.GetAnalyzerActionCountsAsync(analyzer, compilation.Options, cancellationToken).ConfigureAwait(false); analyzerActionCounts.Add(analyzer, actionCounts); } Func getAnalyzerActionCounts = analyzer => analyzerActionCounts[analyzer]; diff --git a/src/Test/Utilities/Desktop/CommonDiagnosticAnalyzers.cs b/src/Test/Utilities/Desktop/CommonDiagnosticAnalyzers.cs index 744523bf3f41245244ceeedafef5135c031fbc10..98d6ac34de454c4e74e69fd123e7c8999bf2d4d7 100644 --- a/src/Test/Utilities/Desktop/CommonDiagnosticAnalyzers.cs +++ b/src/Test/Utilities/Desktop/CommonDiagnosticAnalyzers.cs @@ -384,6 +384,24 @@ public sealed class AnalyzerWithNoActions : DiagnosticAnalyzer public override void Initialize(AnalysisContext context) { } } + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class AnalyzerWithDisabledRules : DiagnosticAnalyzer + { + public static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + "ID1", + "Title1", + "Message1", + "Category1", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: false); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + public override void Initialize(AnalysisContext context) + { + context.RegisterSymbolAction(_ => { }, SymbolKind.NamedType); + } + } + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class EnsureNoMergedNamespaceSymbolAnalyzer : DiagnosticAnalyzer {