diff --git a/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.cs b/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.cs index 37a1844ad4adcfacd898cbf5b93acfc2e3a3e9e0..608361387f603da11c20a4770a0999b4c3232ba5 100644 --- a/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.cs @@ -133,7 +133,7 @@ internal override Diagnostic WithSeverity(DiagnosticSeverity severity) private class ComplainAboutX : DiagnosticAnalyzer { private static readonly DiagnosticDescriptor s_CA9999_UseOfVariableThatStartsWithX = - new DiagnosticDescriptor(id: "CA9999", title: "CA9999_UseOfVariableThatStartsWithX", messageFormat: "Use of variable whose name starts with 'x': '{0}'", category: "Test", defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true); + new DiagnosticDescriptor(id: "CA9999_UseOfVariableThatStartsWithX", title: "CA9999_UseOfVariableThatStartsWithX", messageFormat: "Use of variable whose name starts with 'x': '{0}'", category: "Test", defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics { @@ -153,7 +153,7 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context) var id = (IdentifierNameSyntax)context.Node; if (id.Identifier.ValueText.StartsWith("x", StringComparison.Ordinal)) { - context.ReportDiagnostic(new TestDiagnostic("CA9999_UseOfVariableThatStartsWithX", "CsTest", DiagnosticSeverity.Warning, id.Location, "Use of variable whose name starts with 'x': '{0}'", id.Identifier.ValueText)); + context.ReportDiagnostic(CodeAnalysis.Diagnostic.Create(s_CA9999_UseOfVariableThatStartsWithX, id.Location, id.Identifier.ValueText)); } } } @@ -935,5 +935,45 @@ public override void Initialize(AnalysisContext context) }, SymbolKind.Method); } } + + [Fact, WorkItem(252, "https://github.com/dotnet/roslyn/issues/252")] + public void TestReportingUnsupportedDiagnostic() + { + string source = @""; + var analyzers = new DiagnosticAnalyzer[] { new AnalyzerReportingUnsupportedDiagnostic() }; + + CreateCompilationWithMscorlib45(source) + .VerifyDiagnostics() + .VerifyAnalyzerDiagnostics(analyzers, null, null, logAnalyzerExceptionAsDiagnostics: true, + expected: Diagnostic("AD0001") + .WithArguments("Microsoft.CodeAnalysis.CSharp.UnitTests.DiagnosticAnalyzerTests+AnalyzerReportingUnsupportedDiagnostic", + @"Reported diagnostic with ID 'ID_2' is not supported by the analyzer. +Parameter name: diagnostic") + .WithLocation(1, 1)); + } + + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public class AnalyzerReportingUnsupportedDiagnostic : DiagnosticAnalyzer + { + public static readonly DiagnosticDescriptor SupportedDescriptor = + new DiagnosticDescriptor("ID_1", "DummyTitle", "DummyMessage", "DummyCategory", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + public static readonly DiagnosticDescriptor UnsupportedDescriptor = + new DiagnosticDescriptor("ID_2", "DummyTitle", "DummyMessage", "DummyCategory", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(SupportedDescriptor); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterCompilationAction(compilationContext => + compilationContext.ReportDiagnostic(CodeAnalysis.Diagnostic.Create(UnsupportedDescriptor, Location.None))); + } + } } } diff --git a/src/Compilers/Core/AnalyzerDriver/AnalyzerExecutor.cs b/src/Compilers/Core/AnalyzerDriver/AnalyzerExecutor.cs index 525f54d5f14756ad4314f0e31c1a8ccdbe499968..46b26a1ac294fbe65e87abb1619b37be907f443d 100644 --- a/src/Compilers/Core/AnalyzerDriver/AnalyzerExecutor.cs +++ b/src/Compilers/Core/AnalyzerDriver/AnalyzerExecutor.cs @@ -24,10 +24,12 @@ internal class AnalyzerExecutor private readonly AnalyzerOptions _analyzerOptions; private readonly Action _addDiagnostic; private readonly Action _onAnalyzerException; + private readonly AnalyzerManager _analyzerManager; + private readonly Func _isCompilerAnalyzer; private readonly CancellationToken _cancellationToken; /// - /// Creates AnalyzerActionsExecutor to execute analyzer actions with given arguments + /// Creates to execute analyzer actions with given arguments /// /// Compilation to be used in the analysis. /// Analyzer options. @@ -36,34 +38,43 @@ internal class AnalyzerExecutor /// Optional delegate which is invoked when an analyzer throws an exception. /// Delegate can do custom tasks such as report the given analyzer exception diagnostic, report a non-fatal watson for the exception, etc. /// + /// Delegate to determine if the given analyzer is compiler analyzer. + /// We need to special case the compiler analyzer at few places for performance reasons. + /// Analyzer manager to fetch supported diagnostics. /// Cancellation token. public static AnalyzerExecutor Create( Compilation compilation, AnalyzerOptions analyzerOptions, Action addDiagnostic, Action onAnalyzerException, + Func isCompilerAnalyzer, + AnalyzerManager analyzerManager, CancellationToken cancellationToken) { - return new AnalyzerExecutor(compilation, analyzerOptions, addDiagnostic, onAnalyzerException, cancellationToken); + return new AnalyzerExecutor(compilation, analyzerOptions, addDiagnostic, onAnalyzerException, isCompilerAnalyzer, analyzerManager, cancellationToken); } /// - /// Creates AnalyzerActionsExecutor to fetch . + /// Creates to fetch . /// /// /// Optional delegate which is invoked when an analyzer throws an exception. /// Delegate can do custom tasks such as report the given analyzer exception diagnostic, report a non-fatal watson for the exception, etc. /// + /// Analyzer manager to fetch supported diagnostics. /// Cancellation token. public static AnalyzerExecutor CreateForSupportedDiagnostics( Action onAnalyzerException, + AnalyzerManager analyzerManager, CancellationToken cancellationToken) { return new AnalyzerExecutor( compilation: null, analyzerOptions: null, addDiagnostic: null, + isCompilerAnalyzer: null, onAnalyzerException: onAnalyzerException, + analyzerManager: analyzerManager, cancellationToken: cancellationToken); } @@ -72,12 +83,16 @@ internal class AnalyzerExecutor AnalyzerOptions analyzerOptions, Action addDiagnostic, Action onAnalyzerException, + Func isCompilerAnalyzer, + AnalyzerManager analyzerManager, CancellationToken cancellationToken) { _compilation = compilation; _analyzerOptions = analyzerOptions; _addDiagnostic = addDiagnostic; _onAnalyzerException = onAnalyzerException; + _isCompilerAnalyzer = isCompilerAnalyzer; + _analyzerManager = analyzerManager; _cancellationToken = cancellationToken; } @@ -127,7 +142,8 @@ public void ExecuteCompilationActions(ImmutableArray { _cancellationToken.ThrowIfCancellationRequested(); ExecuteAndCatchIfThrows(endAction.Analyzer, - () => endAction.Action(new CompilationAnalysisContext(_compilation, _analyzerOptions, _addDiagnostic, _cancellationToken))); + () => endAction.Action(new CompilationAnalysisContext(_compilation, _analyzerOptions, _addDiagnostic, + d => IsSupportedDiagnostic(endAction.Analyzer, d), _cancellationToken))); } } @@ -172,7 +188,8 @@ public void ExecuteSymbolActions(ImmutableArray symbolActi { _cancellationToken.ThrowIfCancellationRequested(); ExecuteAndCatchIfThrows(symbolAction.Analyzer, - () => action(new SymbolAnalysisContext(symbol, _compilation, _analyzerOptions, addDiagnostic, _cancellationToken))); + () => action(new SymbolAnalysisContext(symbol, _compilation, _analyzerOptions, addDiagnostic, + d => IsSupportedDiagnostic(symbolAction.Analyzer, d), _cancellationToken))); } } } @@ -200,7 +217,8 @@ public void ExecuteSemanticModelActions(ImmutableArray semanticModelAction.Action(new SemanticModelAnalysisContext(semanticModel, _analyzerOptions, _addDiagnostic, _cancellationToken))); + () => semanticModelAction.Action(new SemanticModelAnalysisContext(semanticModel, _analyzerOptions, _addDiagnostic, + d => IsSupportedDiagnostic(semanticModelAction.Analyzer, d), _cancellationToken))); } } @@ -227,7 +245,8 @@ public void ExecuteSyntaxTreeActions(ImmutableArray sy // Catch Exception from action. ExecuteAndCatchIfThrows(syntaxTreeAction.Analyzer, - () => syntaxTreeAction.Action(new SyntaxTreeAnalysisContext(tree, _analyzerOptions, _addDiagnostic, _cancellationToken))); + () => syntaxTreeAction.Action(new SyntaxTreeAnalysisContext(tree, _analyzerOptions, _addDiagnostic, + d => IsSupportedDiagnostic(syntaxTreeAction.Analyzer, d), _cancellationToken))); } } @@ -265,7 +284,8 @@ public void ExecuteSyntaxTreeActions(ImmutableArray sy SemanticModel semanticModel) where TLanguageKindEnum : struct { - var syntaxNodeContext = new SyntaxNodeAnalysisContext(node, semanticModel, _analyzerOptions, _addDiagnostic, _cancellationToken); + var syntaxNodeContext = new SyntaxNodeAnalysisContext(node, semanticModel, _analyzerOptions, _addDiagnostic, + d => IsSupportedDiagnostic(syntaxNodeAction.Analyzer, d), _cancellationToken); ExecuteAndCatchIfThrows(syntaxNodeAction.Analyzer, () => syntaxNodeAction.Action(syntaxNodeContext)); } @@ -369,7 +389,8 @@ private void ExecuteCodeBlockActions(PooledHashSet bloc foreach (var blockAction in blockActions) { ExecuteAndCatchIfThrows(blockAction.Analyzer, - () => blockAction.Action(new CodeBlockAnalysisContext(declaredNode, declaredSymbol, semanticModel, _analyzerOptions, _addDiagnostic, _cancellationToken))); + () => blockAction.Action(new CodeBlockAnalysisContext(declaredNode, declaredSymbol, semanticModel, _analyzerOptions, _addDiagnostic, + d => IsSupportedDiagnostic(blockAction.Analyzer, d), _cancellationToken))); } blockActions.Free(); @@ -513,5 +534,12 @@ internal static bool IsAnalyzerExceptionDiagnostic(Diagnostic diagnostic) return false; } + + private bool IsSupportedDiagnostic(DiagnosticAnalyzer analyzer, Diagnostic diagnostic) + { + Debug.Assert(_isCompilerAnalyzer != null); + + return _analyzerManager.IsSupportedDiagnostic(analyzer, diagnostic, _isCompilerAnalyzer, this); + } } } diff --git a/src/Compilers/Core/AnalyzerDriver/AnalyzerManager.cs b/src/Compilers/Core/AnalyzerDriver/AnalyzerManager.cs index 9e51b8d5c717d7328cdeb443fc7e54ede0cfa397..fb8001792987d45b212553b3c5a6c743d44ecb7e 100644 --- a/src/Compilers/Core/AnalyzerDriver/AnalyzerManager.cs +++ b/src/Compilers/Core/AnalyzerDriver/AnalyzerManager.cs @@ -223,6 +223,28 @@ internal void ClearAnalyzerExceptionHandlers(DiagnosticAnalyzer analyzer) } } + internal bool IsSupportedDiagnostic(DiagnosticAnalyzer analyzer, Diagnostic diagnostic, Func isCompilerAnalyzer, AnalyzerExecutor analyzerExecutor) + { + // Avoid realizing all the descriptors for all compiler diagnostics by assuming that compiler analyzer doesn't report unsupported diagnostics. + if (isCompilerAnalyzer(analyzer)) + { + return true; + } + + // Get all the supported diagnostics and scan them linearly to see if the reported diagnostic is supported by the analyzer. + // The linear scan is okay, given that this runs only if a diagnostic is being reported and a given analyzer is quite unlikely to have hundreds of thousands of supported diagnostics. + var supportedDescriptors = GetSupportedDiagnosticDescriptors(analyzer, analyzerExecutor); + foreach (var descriptor in supportedDescriptors) + { + if (descriptor.Id.Equals(diagnostic.Id, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + + return false; + } + /// /// Returns true if all the diagnostics that can be produced by this analyzer are suppressed through options. /// diff --git a/src/Compilers/Core/AnalyzerDriver/DiagnosticAnalysisContextHelpers.cs b/src/Compilers/Core/AnalyzerDriver/DiagnosticAnalysisContextHelpers.cs index a33415df8b3c04b2ca9ab1ba7fd4d64a89d225c4..75b860baf305097aa6a4b870ce8786a1c30154ea 100644 --- a/src/Compilers/Core/AnalyzerDriver/DiagnosticAnalysisContextHelpers.cs +++ b/src/Compilers/Core/AnalyzerDriver/DiagnosticAnalysisContextHelpers.cs @@ -25,12 +25,17 @@ internal static void VerifyArguments(Action action, Immutabl VerifySyntaxKinds(syntaxKinds); } - internal static void VerifyArguments(Diagnostic diagnostic) + internal static void VerifyArguments(Diagnostic diagnostic, Func isSupportedDiagnostic) { if (diagnostic == null) { throw new ArgumentNullException(nameof(diagnostic)); } + + if (!isSupportedDiagnostic(diagnostic)) + { + throw new ArgumentException(string.Format(AnalyzerDriverResources.UnsupportedDiagnosticReported, diagnostic.Id), nameof(diagnostic)); + } } private static void VerifyAction(Action action) diff --git a/src/Compilers/Core/CodeAnalysisTest/Diagnostics/DiagnosticLocalizationTests.cs b/src/Compilers/Core/CodeAnalysisTest/Diagnostics/DiagnosticLocalizationTests.cs index f62e16fdd14778459f0d2a8dea36fe12df9e97cc..508b3e458862f92a62762bba88315cb88abe5564 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Diagnostics/DiagnosticLocalizationTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Diagnostics/DiagnosticLocalizationTests.cs @@ -276,7 +276,7 @@ private static void TestDescriptorIsExceptionSafeCore(DiagnosticDescriptor descr var analyzer = new MyAnalyzer(descriptor); var exceptionDiagnostics = new List(); Action onAnalyzerException = (ex, a, diag) => exceptionDiagnostics.Add(diag); - var analyzerExecutor = AnalyzerExecutor.CreateForSupportedDiagnostics(onAnalyzerException, CancellationToken.None); + var analyzerExecutor = AnalyzerExecutor.CreateForSupportedDiagnostics(onAnalyzerException, AnalyzerManager.Instance, CancellationToken.None); var descriptors = AnalyzerManager.Instance.GetSupportedDiagnosticDescriptors(analyzer, analyzerExecutor); Assert.Equal(1, descriptors.Length); diff --git a/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs b/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs index 4abe5fc591c669a6c2a29aceba04af16a7da67b8..22a7d7cd18a2d720c0cb22b0b691fa9544c5a4b4 100644 --- a/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs +++ b/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs @@ -970,6 +970,15 @@ internal class CodeAnalysisResources { } } + /// + /// Looks up a localized string similar to Reported diagnostic with ID '{0}' is not supported by the analyzer.. + /// + internal static string UnsupportedDiagnosticReported { + get { + return ResourceManager.GetString("UnsupportedDiagnosticReported", resourceCulture); + } + } + /// /// Looks up a localized string similar to Unsupported hash algorithm.. /// diff --git a/src/Compilers/Core/Portable/CodeAnalysisResources.resx b/src/Compilers/Core/Portable/CodeAnalysisResources.resx index 3131d3aec6c3e5e31bc4b730c40beba92c8f5c1e..6672070d88864d412151bc8f74dda7e1e7f57d90 100644 --- a/src/Compilers/Core/Portable/CodeAnalysisResources.resx +++ b/src/Compilers/Core/Portable/CodeAnalysisResources.resx @@ -402,6 +402,9 @@ Argument cannot have a null element. + + Reported diagnostic with ID '{0}' is not supported by the analyzer. + Cannot deserialize type '{0}', no binder supplied. diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs index d5f86f589265d0407978f9e21c3d99747778af36..14bd5e1763162640ade675d63a2cf46eaa415a9d 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs @@ -215,7 +215,7 @@ private Task ExecuteSyntaxTreeActions(CancellationToken cancellationToken) newOnAnalyzerException = (ex, analyzer, diagnostic) => addDiagnostic(diagnostic); } - var analyzerExecutor = AnalyzerExecutor.Create(newCompilation, options, addDiagnostic, newOnAnalyzerException, cancellationToken); + var analyzerExecutor = AnalyzerExecutor.Create(newCompilation, options, addDiagnostic, newOnAnalyzerException, IsCompilerAnalyzer, analyzerManager, cancellationToken); analyzerDriver.Initialize(newCompilation, analyzerExecutor, cancellationToken); @@ -969,5 +969,6 @@ internal static class AnalyzerDriverResources internal static string DiagnosticDescriptorThrows => CodeAnalysisResources.DiagnosticDescriptorThrows; internal static string ArgumentElementCannotBeNull => CodeAnalysisResources.ArgumentElementCannotBeNull; internal static string ArgumentCannotBeEmpty => CodeAnalysisResources.ArgumentCannotBeEmpty; + internal static string UnsupportedDiagnosticReported => CodeAnalysisResources.UnsupportedDiagnosticReported; } } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs index 93438e209842a5245304b6dcb92711690b3ac817..0c1682c212d8ce990649f87f0d5ded07771907f5 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs @@ -120,7 +120,7 @@ public static bool IsDiagnosticAnalyzerSuppressed(DiagnosticAnalyzer analyzer, C Action voidHandler = (ex, a, diag) => { }; onAnalyzerException = onAnalyzerException ?? voidHandler; - var analyzerExecutor = AnalyzerExecutor.CreateForSupportedDiagnostics(onAnalyzerException, CancellationToken.None); + var analyzerExecutor = AnalyzerExecutor.CreateForSupportedDiagnostics(onAnalyzerException, AnalyzerManager.Instance, CancellationToken.None); return AnalyzerDriver.IsDiagnosticAnalyzerSuppressed(analyzer, options, AnalyzerManager.Instance, analyzerExecutor); } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticAnalysisContext.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticAnalysisContext.cs index b6fddb70c4a999cdcb295d16337fdea2af33dbbf..e3d4705a952ebc39afbeae177d6dcb71c01d570a 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticAnalysisContext.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticAnalysisContext.cs @@ -94,7 +94,7 @@ public void RegisterSymbolAction(Action action, params Sy /// A code block action reports s about code blocks. /// /// Action to be executed for a code block. - public abstract void RegisterCodeBlockAction(Action action); + public abstract void RegisterCodeBlockAction(Action action); /// /// Register an action to be executed at completion of parsing of a code document. @@ -224,7 +224,7 @@ public void RegisterSymbolAction(Action action, params Sy /// Enum type giving the syntax node kinds of the source language for which the action applies. /// Action to be executed at the start of semantic analysis of a code block. public abstract void RegisterCodeBlockStartAction(Action> action) where TLanguageKindEnum : struct; - + /// /// Register an action to be executed at the end of semantic analysis of a method body or an expression appearing outside a method body. /// A code block action reports s about code blocks. @@ -272,6 +272,7 @@ public struct CompilationAnalysisContext private readonly Compilation _compilation; private readonly AnalyzerOptions _options; private readonly Action _reportDiagnostic; + private readonly Func _isSupportedDiagnostic; private readonly CancellationToken _cancellationToken; /// @@ -289,11 +290,12 @@ public struct CompilationAnalysisContext /// public CancellationToken CancellationToken { get { return _cancellationToken; } } - public CompilationAnalysisContext(Compilation compilation, AnalyzerOptions options, Action reportDiagnostic, CancellationToken cancellationToken) + public CompilationAnalysisContext(Compilation compilation, AnalyzerOptions options, Action reportDiagnostic, Func isSupportedDiagnostic, CancellationToken cancellationToken) { _compilation = compilation; _options = options; _reportDiagnostic = reportDiagnostic; + _isSupportedDiagnostic = isSupportedDiagnostic; _cancellationToken = cancellationToken; } @@ -303,7 +305,7 @@ public CompilationAnalysisContext(Compilation compilation, AnalyzerOptions optio /// to be reported. public void ReportDiagnostic(Diagnostic diagnostic) { - DiagnosticAnalysisContextHelpers.VerifyArguments(diagnostic); + DiagnosticAnalysisContextHelpers.VerifyArguments(diagnostic, _isSupportedDiagnostic); lock (_reportDiagnostic) { _reportDiagnostic(diagnostic); @@ -320,6 +322,7 @@ public struct SemanticModelAnalysisContext private readonly SemanticModel _semanticModel; private readonly AnalyzerOptions _options; private readonly Action _reportDiagnostic; + private readonly Func _isSupportedDiagnostic; private readonly CancellationToken _cancellationToken; /// @@ -337,11 +340,12 @@ public struct SemanticModelAnalysisContext /// public CancellationToken CancellationToken { get { return _cancellationToken; } } - public SemanticModelAnalysisContext(SemanticModel semanticModel, AnalyzerOptions options, Action reportDiagnostic, CancellationToken cancellationToken) + public SemanticModelAnalysisContext(SemanticModel semanticModel, AnalyzerOptions options, Action reportDiagnostic, Func isSupportedDiagnostic, CancellationToken cancellationToken) { _semanticModel = semanticModel; _options = options; _reportDiagnostic = reportDiagnostic; + _isSupportedDiagnostic = isSupportedDiagnostic; _cancellationToken = cancellationToken; } @@ -351,7 +355,7 @@ public SemanticModelAnalysisContext(SemanticModel semanticModel, AnalyzerOptions /// to be reported. public void ReportDiagnostic(Diagnostic diagnostic) { - DiagnosticAnalysisContextHelpers.VerifyArguments(diagnostic); + DiagnosticAnalysisContextHelpers.VerifyArguments(diagnostic, _isSupportedDiagnostic); lock (_reportDiagnostic) { _reportDiagnostic(diagnostic); @@ -369,6 +373,7 @@ public struct SymbolAnalysisContext private readonly Compilation _compilation; private readonly AnalyzerOptions _options; private readonly Action _reportDiagnostic; + private readonly Func _isSupportedDiagnostic; private readonly CancellationToken _cancellationToken; /// @@ -391,12 +396,13 @@ public struct SymbolAnalysisContext /// public CancellationToken CancellationToken { get { return _cancellationToken; } } - public SymbolAnalysisContext(ISymbol symbol, Compilation compilation, AnalyzerOptions options, Action reportDiagnostic, CancellationToken cancellationToken) + public SymbolAnalysisContext(ISymbol symbol, Compilation compilation, AnalyzerOptions options, Action reportDiagnostic, Func isSupportedDiagnostic, CancellationToken cancellationToken) { _symbol = symbol; _compilation = compilation; _options = options; _reportDiagnostic = reportDiagnostic; + _isSupportedDiagnostic = isSupportedDiagnostic; _cancellationToken = cancellationToken; } @@ -406,7 +412,7 @@ public SymbolAnalysisContext(ISymbol symbol, Compilation compilation, AnalyzerOp /// to be reported. public void ReportDiagnostic(Diagnostic diagnostic) { - DiagnosticAnalysisContextHelpers.VerifyArguments(diagnostic); + DiagnosticAnalysisContextHelpers.VerifyArguments(diagnostic, _isSupportedDiagnostic); lock (_reportDiagnostic) { _reportDiagnostic(diagnostic); @@ -509,6 +515,7 @@ public struct CodeBlockAnalysisContext private readonly SemanticModel _semanticModel; private readonly AnalyzerOptions _options; private readonly Action _reportDiagnostic; + private readonly Func _isSupportedDiagnostic; private readonly CancellationToken _cancellationToken; /// @@ -536,13 +543,14 @@ public struct CodeBlockAnalysisContext /// public CancellationToken CancellationToken { get { return _cancellationToken; } } - public CodeBlockAnalysisContext(SyntaxNode codeBlock, ISymbol owningSymbol, SemanticModel semanticModel, AnalyzerOptions options, Action reportDiagnostic, CancellationToken cancellationToken) + public CodeBlockAnalysisContext(SyntaxNode codeBlock, ISymbol owningSymbol, SemanticModel semanticModel, AnalyzerOptions options, Action reportDiagnostic, Func isSupportedDiagnostic, CancellationToken cancellationToken) { _codeBlock = codeBlock; _owningSymbol = owningSymbol; _semanticModel = semanticModel; _options = options; _reportDiagnostic = reportDiagnostic; + _isSupportedDiagnostic = isSupportedDiagnostic; _cancellationToken = cancellationToken; } @@ -552,7 +560,7 @@ public CodeBlockAnalysisContext(SyntaxNode codeBlock, ISymbol owningSymbol, Sema /// to be reported. public void ReportDiagnostic(Diagnostic diagnostic) { - DiagnosticAnalysisContextHelpers.VerifyArguments(diagnostic); + DiagnosticAnalysisContextHelpers.VerifyArguments(diagnostic, _isSupportedDiagnostic); lock (_reportDiagnostic) { _reportDiagnostic(diagnostic); @@ -569,6 +577,7 @@ public struct SyntaxTreeAnalysisContext private readonly SyntaxTree _tree; private readonly AnalyzerOptions _options; private readonly Action _reportDiagnostic; + private readonly Func _isSupportedDiagnostic; private readonly CancellationToken _cancellationToken; /// @@ -586,11 +595,12 @@ public struct SyntaxTreeAnalysisContext /// public CancellationToken CancellationToken { get { return _cancellationToken; } } - public SyntaxTreeAnalysisContext(SyntaxTree tree, AnalyzerOptions options, Action reportDiagnostic, CancellationToken cancellationToken) + public SyntaxTreeAnalysisContext(SyntaxTree tree, AnalyzerOptions options, Action reportDiagnostic, Func isSupportedDiagnostic, CancellationToken cancellationToken) { _tree = tree; _options = options; _reportDiagnostic = reportDiagnostic; + _isSupportedDiagnostic = isSupportedDiagnostic; _cancellationToken = cancellationToken; } @@ -600,7 +610,7 @@ public SyntaxTreeAnalysisContext(SyntaxTree tree, AnalyzerOptions options, Actio /// to be reported. public void ReportDiagnostic(Diagnostic diagnostic) { - DiagnosticAnalysisContextHelpers.VerifyArguments(diagnostic); + DiagnosticAnalysisContextHelpers.VerifyArguments(diagnostic, _isSupportedDiagnostic); lock (_reportDiagnostic) { _reportDiagnostic(diagnostic); @@ -618,6 +628,7 @@ public struct SyntaxNodeAnalysisContext private readonly SemanticModel _semanticModel; private readonly AnalyzerOptions _options; private readonly Action _reportDiagnostic; + private readonly Func _isSupportedDiagnostic; private readonly CancellationToken _cancellationToken; /// @@ -640,12 +651,13 @@ public struct SyntaxNodeAnalysisContext /// public CancellationToken CancellationToken { get { return _cancellationToken; } } - public SyntaxNodeAnalysisContext(SyntaxNode node, SemanticModel semanticModel, AnalyzerOptions options, Action reportDiagnostic, CancellationToken cancellationToken) + public SyntaxNodeAnalysisContext(SyntaxNode node, SemanticModel semanticModel, AnalyzerOptions options, Action reportDiagnostic, Func isSupportedDiagnostic, CancellationToken cancellationToken) { _node = node; _semanticModel = semanticModel; _options = options; _reportDiagnostic = reportDiagnostic; + _isSupportedDiagnostic = isSupportedDiagnostic; _cancellationToken = cancellationToken; } @@ -655,7 +667,7 @@ public SyntaxNodeAnalysisContext(SyntaxNode node, SemanticModel semanticModel, A /// to be reported. public void ReportDiagnostic(Diagnostic diagnostic) { - DiagnosticAnalysisContextHelpers.VerifyArguments(diagnostic); + DiagnosticAnalysisContextHelpers.VerifyArguments(diagnostic, _isSupportedDiagnostic); lock (_reportDiagnostic) { _reportDiagnostic(diagnostic); diff --git a/src/Compilers/Core/Portable/PublicAPI.txt b/src/Compilers/Core/Portable/PublicAPI.txt index dedb02b48abada9c9d03933bc2d27289fc29b04a..4bbd391d62c0722576f17823c0b19a1b20887032 100644 --- a/src/Compilers/Core/Portable/PublicAPI.txt +++ b/src/Compilers/Core/Portable/PublicAPI.txt @@ -243,7 +243,7 @@ Microsoft.CodeAnalysis.Diagnostics.AnalyzerReference.AnalyzerReference() -> void Microsoft.CodeAnalysis.Diagnostics.CodeBlockAnalysisContext Microsoft.CodeAnalysis.Diagnostics.CodeBlockAnalysisContext.CancellationToken.get -> System.Threading.CancellationToken Microsoft.CodeAnalysis.Diagnostics.CodeBlockAnalysisContext.CodeBlock.get -> Microsoft.CodeAnalysis.SyntaxNode -Microsoft.CodeAnalysis.Diagnostics.CodeBlockAnalysisContext.CodeBlockAnalysisContext(Microsoft.CodeAnalysis.SyntaxNode codeBlock, Microsoft.CodeAnalysis.ISymbol owningSymbol, Microsoft.CodeAnalysis.SemanticModel semanticModel, Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions options, System.Action reportDiagnostic, System.Threading.CancellationToken cancellationToken) -> void +Microsoft.CodeAnalysis.Diagnostics.CodeBlockAnalysisContext.CodeBlockAnalysisContext(Microsoft.CodeAnalysis.SyntaxNode codeBlock, Microsoft.CodeAnalysis.ISymbol owningSymbol, Microsoft.CodeAnalysis.SemanticModel semanticModel, Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions options, System.Action reportDiagnostic, System.Func isSupportedDiagnostic, System.Threading.CancellationToken cancellationToken) -> void Microsoft.CodeAnalysis.Diagnostics.CodeBlockAnalysisContext.Options.get -> Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions Microsoft.CodeAnalysis.Diagnostics.CodeBlockAnalysisContext.OwningSymbol.get -> Microsoft.CodeAnalysis.ISymbol Microsoft.CodeAnalysis.Diagnostics.CodeBlockAnalysisContext.ReportDiagnostic(Microsoft.CodeAnalysis.Diagnostic diagnostic) -> void @@ -259,7 +259,7 @@ Microsoft.CodeAnalysis.Diagnostics.CodeBlockStartAnalysisContext System.Threading.CancellationToken Microsoft.CodeAnalysis.Diagnostics.CompilationAnalysisContext.Compilation.get -> Microsoft.CodeAnalysis.Compilation -Microsoft.CodeAnalysis.Diagnostics.CompilationAnalysisContext.CompilationAnalysisContext(Microsoft.CodeAnalysis.Compilation compilation, Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions options, System.Action reportDiagnostic, System.Threading.CancellationToken cancellationToken) -> void +Microsoft.CodeAnalysis.Diagnostics.CompilationAnalysisContext.CompilationAnalysisContext(Microsoft.CodeAnalysis.Compilation compilation, Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions options, System.Action reportDiagnostic, System.Func isSupportedDiagnostic, System.Threading.CancellationToken cancellationToken) -> void Microsoft.CodeAnalysis.Diagnostics.CompilationAnalysisContext.Options.get -> Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions Microsoft.CodeAnalysis.Diagnostics.CompilationAnalysisContext.ReportDiagnostic(Microsoft.CodeAnalysis.Diagnostic diagnostic) -> void Microsoft.CodeAnalysis.Diagnostics.CompilationStartAnalysisContext @@ -284,26 +284,26 @@ Microsoft.CodeAnalysis.Diagnostics.SemanticModelAnalysisContext.CancellationToke Microsoft.CodeAnalysis.Diagnostics.SemanticModelAnalysisContext.Options.get -> Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions Microsoft.CodeAnalysis.Diagnostics.SemanticModelAnalysisContext.ReportDiagnostic(Microsoft.CodeAnalysis.Diagnostic diagnostic) -> void Microsoft.CodeAnalysis.Diagnostics.SemanticModelAnalysisContext.SemanticModel.get -> Microsoft.CodeAnalysis.SemanticModel -Microsoft.CodeAnalysis.Diagnostics.SemanticModelAnalysisContext.SemanticModelAnalysisContext(Microsoft.CodeAnalysis.SemanticModel semanticModel, Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions options, System.Action reportDiagnostic, System.Threading.CancellationToken cancellationToken) -> void +Microsoft.CodeAnalysis.Diagnostics.SemanticModelAnalysisContext.SemanticModelAnalysisContext(Microsoft.CodeAnalysis.SemanticModel semanticModel, Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions options, System.Action reportDiagnostic, System.Func isSupportedDiagnostic, System.Threading.CancellationToken cancellationToken) -> void Microsoft.CodeAnalysis.Diagnostics.SymbolAnalysisContext Microsoft.CodeAnalysis.Diagnostics.SymbolAnalysisContext.CancellationToken.get -> System.Threading.CancellationToken Microsoft.CodeAnalysis.Diagnostics.SymbolAnalysisContext.Compilation.get -> Microsoft.CodeAnalysis.Compilation Microsoft.CodeAnalysis.Diagnostics.SymbolAnalysisContext.Options.get -> Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions Microsoft.CodeAnalysis.Diagnostics.SymbolAnalysisContext.ReportDiagnostic(Microsoft.CodeAnalysis.Diagnostic diagnostic) -> void Microsoft.CodeAnalysis.Diagnostics.SymbolAnalysisContext.Symbol.get -> Microsoft.CodeAnalysis.ISymbol -Microsoft.CodeAnalysis.Diagnostics.SymbolAnalysisContext.SymbolAnalysisContext(Microsoft.CodeAnalysis.ISymbol symbol, Microsoft.CodeAnalysis.Compilation compilation, Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions options, System.Action reportDiagnostic, System.Threading.CancellationToken cancellationToken) -> void +Microsoft.CodeAnalysis.Diagnostics.SymbolAnalysisContext.SymbolAnalysisContext(Microsoft.CodeAnalysis.ISymbol symbol, Microsoft.CodeAnalysis.Compilation compilation, Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions options, System.Action reportDiagnostic, System.Func isSupportedDiagnostic, System.Threading.CancellationToken cancellationToken) -> void Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext.CancellationToken.get -> System.Threading.CancellationToken Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext.Node.get -> Microsoft.CodeAnalysis.SyntaxNode Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext.Options.get -> Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext.ReportDiagnostic(Microsoft.CodeAnalysis.Diagnostic diagnostic) -> void Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext.SemanticModel.get -> Microsoft.CodeAnalysis.SemanticModel -Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext.SyntaxNodeAnalysisContext(Microsoft.CodeAnalysis.SyntaxNode node, Microsoft.CodeAnalysis.SemanticModel semanticModel, Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions options, System.Action reportDiagnostic, System.Threading.CancellationToken cancellationToken) -> void +Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext.SyntaxNodeAnalysisContext(Microsoft.CodeAnalysis.SyntaxNode node, Microsoft.CodeAnalysis.SemanticModel semanticModel, Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions options, System.Action reportDiagnostic, System.Func isSupportedDiagnostic, System.Threading.CancellationToken cancellationToken) -> void Microsoft.CodeAnalysis.Diagnostics.SyntaxTreeAnalysisContext Microsoft.CodeAnalysis.Diagnostics.SyntaxTreeAnalysisContext.CancellationToken.get -> System.Threading.CancellationToken Microsoft.CodeAnalysis.Diagnostics.SyntaxTreeAnalysisContext.Options.get -> Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions Microsoft.CodeAnalysis.Diagnostics.SyntaxTreeAnalysisContext.ReportDiagnostic(Microsoft.CodeAnalysis.Diagnostic diagnostic) -> void -Microsoft.CodeAnalysis.Diagnostics.SyntaxTreeAnalysisContext.SyntaxTreeAnalysisContext(Microsoft.CodeAnalysis.SyntaxTree tree, Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions options, System.Action reportDiagnostic, System.Threading.CancellationToken cancellationToken) -> void +Microsoft.CodeAnalysis.Diagnostics.SyntaxTreeAnalysisContext.SyntaxTreeAnalysisContext(Microsoft.CodeAnalysis.SyntaxTree tree, Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions options, System.Action reportDiagnostic, System.Func isSupportedDiagnostic, System.Threading.CancellationToken cancellationToken) -> void Microsoft.CodeAnalysis.Diagnostics.SyntaxTreeAnalysisContext.Tree.get -> Microsoft.CodeAnalysis.SyntaxTree Microsoft.CodeAnalysis.Diagnostics.UnresolvedAnalyzerReference Microsoft.CodeAnalysis.Diagnostics.UnresolvedAnalyzerReference.UnresolvedAnalyzerReference(string unresolvedPath) -> void @@ -2132,4 +2132,4 @@ virtual Microsoft.CodeAnalysis.Text.SourceText.ToString(Microsoft.CodeAnalysis.T virtual Microsoft.CodeAnalysis.Text.SourceText.WithChanges(System.Collections.Generic.IEnumerable changes) -> Microsoft.CodeAnalysis.Text.SourceText virtual Microsoft.CodeAnalysis.Text.SourceText.Write(System.IO.TextWriter writer, Microsoft.CodeAnalysis.Text.TextSpan span, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> void virtual Microsoft.CodeAnalysis.Text.TextLineCollection.GetLineFromPosition(int position) -> Microsoft.CodeAnalysis.Text.TextLine -virtual Microsoft.CodeAnalysis.Text.TextLineCollection.GetLinePosition(int position) -> Microsoft.CodeAnalysis.Text.LinePosition +virtual Microsoft.CodeAnalysis.Text.TextLineCollection.GetLinePosition(int position) -> Microsoft.CodeAnalysis.Text.LinePosition \ No newline at end of file diff --git a/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.vb b/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.vb index fb04da29c3c78efbe30e975069c978ed2c8d993f..739d4f090ff2270ecc9e45081ca9c25cc06983ea 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.vb @@ -147,7 +147,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Semantics Class ComplainAboutX Inherits DiagnosticAnalyzer - Private Shared ReadOnly CA9999_UseOfVariableThatStartsWithX As DiagnosticDescriptor = New DiagnosticDescriptor(id:="CA9999", title:="CA9999_UseOfVariableThatStartsWithX", messageFormat:="Use of variable whose name starts with 'x': '{0}'", category:="Test", defaultSeverity:=DiagnosticSeverity.Warning, isEnabledByDefault:=True) + Private Shared ReadOnly CA9999_UseOfVariableThatStartsWithX As DiagnosticDescriptor = New DiagnosticDescriptor(id:="CA9999_UseOfVariableThatStartsWithX", title:="CA9999_UseOfVariableThatStartsWithX", messageFormat:="Use of variable whose name starts with 'x': '{0}'", category:="Test", defaultSeverity:=DiagnosticSeverity.Warning, isEnabledByDefault:=True) Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) Get @@ -162,7 +162,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Semantics Public Sub AnalyzeNode(context As SyntaxNodeAnalysisContext) Dim id = CType(context.Node, IdentifierNameSyntax) If id.Identifier.ValueText.StartsWith("x", StringComparison.Ordinal) Then - context.ReportDiagnostic(New TestDiagnostic("CA9999_UseOfVariableThatStartsWithX", "CsTest", DiagnosticSeverity.Warning, id.GetLocation(), "Use of variable whose name starts with 'x': '{0}'", False, id.Identifier.ValueText)) + context.ReportDiagnostic(CodeAnalysis.Diagnostic.Create(CA9999_UseOfVariableThatStartsWithX, id.GetLocation, id.Identifier.ValueText)) End If End Sub End Class diff --git a/src/Features/Core/Diagnostics/AnalyzerDriverResources.cs b/src/Features/Core/Diagnostics/AnalyzerDriverResources.cs index dec9d7ccdab9dfabeda37a829138f79ebbb680c7..937164c42128a608d3384fd3e514d1622ebe0cac 100644 --- a/src/Features/Core/Diagnostics/AnalyzerDriverResources.cs +++ b/src/Features/Core/Diagnostics/AnalyzerDriverResources.cs @@ -9,5 +9,6 @@ internal static class AnalyzerDriverResources internal static string DiagnosticDescriptorThrows => FeaturesResources.DiagnosticDescriptorThrows; internal static string ArgumentElementCannotBeNull => FeaturesResources.ArgumentElementCannotBeNull; internal static string ArgumentCannotBeEmpty => FeaturesResources.ArgumentCannotBeEmpty; + internal static string UnsupportedDiagnosticReported => FeaturesResources.UnsupportedDiagnosticReported; } } diff --git a/src/Features/Core/Diagnostics/AnalyzerHelper.cs b/src/Features/Core/Diagnostics/AnalyzerHelper.cs index f74d528207d564720bce9001a2f556fd1b2a9366..94bd472910b96bb7a031946f2a0357c9e8ee4614 100644 --- a/src/Features/Core/Diagnostics/AnalyzerHelper.cs +++ b/src/Features/Core/Diagnostics/AnalyzerHelper.cs @@ -54,7 +54,7 @@ public static bool IsCompilerAnalyzer(this DiagnosticAnalyzer analyzer) Action defaultOnAnalyzerException = (ex, a, diagnostic) => OnAnalyzerException_NoTelemetryLogging(ex, a, diagnostic, hostDiagnosticUpdateSource); - return AnalyzerExecutor.CreateForSupportedDiagnostics(onAnalyzerException ?? defaultOnAnalyzerException, cancellationToken); + return AnalyzerExecutor.CreateForSupportedDiagnostics(onAnalyzerException ?? defaultOnAnalyzerException, AnalyzerManager.Instance, cancellationToken); } internal static void OnAnalyzerException_NoTelemetryLogging( diff --git a/src/Features/Core/Diagnostics/EngineV1/DiagnosticAnalyzerDriver.cs b/src/Features/Core/Diagnostics/EngineV1/DiagnosticAnalyzerDriver.cs index fa2cd50c8730366f729d63aa7fb009bdc7f15c49..6f763d489e92758ba6e2215a3e15c9a341657e7c 100644 --- a/src/Features/Core/Diagnostics/EngineV1/DiagnosticAnalyzerDriver.cs +++ b/src/Features/Core/Diagnostics/EngineV1/DiagnosticAnalyzerDriver.cs @@ -321,7 +321,7 @@ internal void OnAnalyzerException(Exception ex, DiagnosticAnalyzer analyzer, Com private AnalyzerExecutor GetAnalyzerExecutor(DiagnosticAnalyzer analyzer, Compilation compilation, Action addDiagnostic) { - return AnalyzerExecutor.Create(compilation, _analyzerOptions, addDiagnostic, _onAnalyzerException, _cancellationToken); + return AnalyzerExecutor.Create(compilation, _analyzerOptions, addDiagnostic, _onAnalyzerException, AnalyzerHelper.IsCompilerAnalyzer, AnalyzerManager.Instance, _cancellationToken); } public async Task GetAnalyzerActionsAsync(DiagnosticAnalyzer analyzer) diff --git a/src/Features/Core/FeaturesResources.Designer.cs b/src/Features/Core/FeaturesResources.Designer.cs index e3fa0822ba400758aa819cb6db7b5b617894b3dd..f4d36d51cc99a8b5bc0928263c369b4ad22b1957 100644 --- a/src/Features/Core/FeaturesResources.Designer.cs +++ b/src/Features/Core/FeaturesResources.Designer.cs @@ -1763,6 +1763,15 @@ internal class FeaturesResources { } } + /// + /// Looks up a localized string similar to Reported diagnostic with ID '{0}' is not supported by the analyzer.. + /// + internal static string UnsupportedDiagnosticReported { + get { + return ResourceManager.GetString("UnsupportedDiagnosticReported", resourceCulture); + } + } + /// /// Looks up a localized string similar to Updating an active statement will prevent the debug session from continuing.. /// diff --git a/src/Features/Core/FeaturesResources.resx b/src/Features/Core/FeaturesResources.resx index a07fc008ae857733ab6d85f1e23ee69842296ec7..ca6843073a1086275747437feae4a97af99ec213 100644 --- a/src/Features/Core/FeaturesResources.resx +++ b/src/Features/Core/FeaturesResources.resx @@ -737,6 +737,9 @@ Do you want to continue? Argument cannot be empty. + + Reported diagnostic with ID '{0}' is not supported by the analyzer. + Document