diff --git a/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.AllInOne.cs b/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.AllInOne.cs index eab99935bbd636ae4ee20679af2da20ff9ba6625..e41e42114fcba0683d7f25fd6eaa67e30b73b553 100644 --- a/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.AllInOne.cs +++ b/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.AllInOne.cs @@ -87,7 +87,7 @@ public void AnalyzerDriverIsSafeAgainstAnalyzerExceptions() { var compilation = CreateCompilationWithMscorlib45(TestResource.AllInOneCSharpCode, parseOptions: TestOptions.Regular); ThrowingDiagnosticAnalyzer.VerifyAnalyzerEngineIsSafeAgainstExceptions(analyzer => - compilation.GetAnalyzerDiagnostics(new[] { analyzer }, null, CodeAnalysis.DiagnosticExtensions.AlwaysCatchAnalyzerExceptions)); + compilation.GetAnalyzerDiagnostics(new[] { analyzer }, null, CodeAnalysis.DiagnosticExtensions.AlwaysCatchAnalyzerException)); } [Fact] diff --git a/src/Compilers/Core/AnalyzerDriver/AnalyzerExecutor.cs b/src/Compilers/Core/AnalyzerDriver/AnalyzerExecutor.cs index 0befea8dbfb731f23aebeabffe77dfad8af764d8..9738f53be4a3244669517d7c4e815305c70a3081 100644 --- a/src/Compilers/Core/AnalyzerDriver/AnalyzerExecutor.cs +++ b/src/Compilers/Core/AnalyzerDriver/AnalyzerExecutor.cs @@ -22,8 +22,7 @@ internal class AnalyzerExecutor private readonly Compilation _compilation; private readonly AnalyzerOptions _analyzerOptions; private readonly Action _addDiagnostic; - private readonly Action _addExceptionDiagnostic; - private readonly Func _continueOnAnalyzerException; + private readonly Action _onAnalyzerException; private readonly CancellationToken _cancellationToken; /// @@ -32,45 +31,38 @@ internal class AnalyzerExecutor /// Compilation to be used in the analysis. /// Analyzer options. /// Delegate to add analyzer diagnostics. - /// Delegate to add diagnostics generated for handled exceptions from third party analyzers. - /// Delegate which is invoked when an analyzer throws an exception. - /// If a non-null delegate is provided and it returns true, then the exception is handled and converted into a diagnostic and driver continues with other analyzers. - /// Otherwise if it returns false, then the exception is not handled by the executor. - /// If null, then the executor always handles the exception. + /// + /// 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. /// /// Cancellation token. public static AnalyzerExecutor Create( Compilation compilation, AnalyzerOptions analyzerOptions, Action addDiagnostic, - Action addExceptionDiagnostic, - Func continueOnAnalyzerException, + Action onAnalyzerException, CancellationToken cancellationToken) { - return new AnalyzerExecutor(compilation, analyzerOptions, addDiagnostic, addExceptionDiagnostic, continueOnAnalyzerException, cancellationToken); + return new AnalyzerExecutor(compilation, analyzerOptions, addDiagnostic, onAnalyzerException, cancellationToken); } /// /// Creates AnalyzerActionsExecutor to fetch . /// - /// Delegate to add diagnostics generated for handled exceptions from third party analyzers. - /// Delegate which is invoked when an analyzer throws an exception. - /// If a non-null delegate is provided and it returns true, then the exception is handled and converted into a diagnostic and driver continues with other analyzers. - /// Otherwise if it returns false, then the exception is not handled by the executor. - /// If null, then the executor always handles the exception. + /// + /// 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. /// /// Cancellation token. public static AnalyzerExecutor CreateForSupportedDiagnostics( - Action addExceptionDiagnostic, - Func continueOnAnalyzerException, + Action onAnalyzerException, CancellationToken cancellationToken) { return new AnalyzerExecutor( compilation: null, analyzerOptions: null, addDiagnostic: null, - addExceptionDiagnostic: addExceptionDiagnostic, - continueOnAnalyzerException: continueOnAnalyzerException, + onAnalyzerException: onAnalyzerException, cancellationToken: cancellationToken); } @@ -78,15 +70,13 @@ internal class AnalyzerExecutor Compilation compilation, AnalyzerOptions analyzerOptions, Action addDiagnostic, - Action addExceptionDiagnostic, - Func continueOnAnalyzerException, + Action onAnalyzerException, CancellationToken cancellationToken) { this._compilation = compilation; this._analyzerOptions = analyzerOptions; this._addDiagnostic = addDiagnostic; - this._addExceptionDiagnostic = addExceptionDiagnostic; - this._continueOnAnalyzerException = continueOnAnalyzerException; + this._onAnalyzerException = onAnalyzerException; this._cancellationToken = cancellationToken; } @@ -450,34 +440,33 @@ internal static bool CanHaveExecutableCodeBlock(ISymbol symbol) internal void ExecuteAndCatchIfThrows(DiagnosticAnalyzer analyzer, Action analyze) { - ExecuteAndCatchIfThrows(analyzer, analyze, _addExceptionDiagnostic, _continueOnAnalyzerException, _cancellationToken); + ExecuteAndCatchIfThrows(analyzer, analyze, _onAnalyzerException, _cancellationToken); } private static void ExecuteAndCatchIfThrows( DiagnosticAnalyzer analyzer, Action analyze, - Action addExceptionDiagnostic, - Func continueOnAnalyzerException, + Action onAnalyzerException, CancellationToken cancellationToken) { try { analyze(); } - catch (OperationCanceledException oce) when (continueOnAnalyzerException(oce, analyzer)) + catch (OperationCanceledException oce) { if (oce.CancellationToken != cancellationToken) { - // Add diagnostic for analyzer exception. + // Diagnostic for analyzer exception. var diagnostic = GetAnalyzerDiagnostic(analyzer, oce); - addExceptionDiagnostic(diagnostic); + onAnalyzerException(oce, analyzer, diagnostic); } } - catch (Exception e) when (continueOnAnalyzerException(e, analyzer)) + catch (Exception e) { - // Add diagnostic for analyzer exception. + // Diagnostic for analyzer exception. var diagnostic = GetAnalyzerDiagnostic(analyzer, e); - addExceptionDiagnostic(diagnostic); + onAnalyzerException(e, analyzer, diagnostic); } } diff --git a/src/Compilers/Core/CodeAnalysisTest/Diagnostics/SuppressMessageAttributeTests.cs b/src/Compilers/Core/CodeAnalysisTest/Diagnostics/SuppressMessageAttributeTests.cs index e6da8fc117a9b1bce9d485da53fb36d19e1781cb..e49ec2e5cbf5b7323308381e20baad7cca846505 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Diagnostics/SuppressMessageAttributeTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Diagnostics/SuppressMessageAttributeTests.cs @@ -1108,8 +1108,6 @@ End Class [Fact] public void SuppressDuplicateAnalyzerExceptionDiagnostics() { - Func continueOnAnalyzerException = (ex, a) => true; - VerifyCSharp(@" public class C { @@ -1122,7 +1120,7 @@ public class C2 } ", new[] { new ThrowExceptionForEachNamedTypeAnalyzer() }, - continueOnAnalyzerException, + DiagnosticExtensions.AlwaysCatchAnalyzerException, Diagnostic("AD0001", null).WithLocation(1, 1)); } @@ -1133,9 +1131,9 @@ protected void VerifyCSharp(string source, DiagnosticAnalyzer[] analyzers, param Verify(source, LanguageNames.CSharp, analyzers, diagnostics); } - protected void VerifyCSharp(string source, DiagnosticAnalyzer[] analyzers, Func continueOnAnalyzerException, params DiagnosticDescription[] diagnostics) + protected void VerifyCSharp(string source, DiagnosticAnalyzer[] analyzers, Action onAnalyzerException, params DiagnosticDescription[] diagnostics) { - Verify(source, LanguageNames.CSharp, analyzers, diagnostics, continueOnAnalyzerException); + Verify(source, LanguageNames.CSharp, analyzers, diagnostics, onAnalyzerException); } protected void VerifyTokenDiagnosticsCSharp(string markup, params DiagnosticDescription[] diagnostics) @@ -1146,7 +1144,7 @@ protected void VerifyTokenDiagnosticsCSharp(string markup, params DiagnosticDesc protected void VerifyBasic(string source, string rootNamespace, DiagnosticAnalyzer[] analyzers, params DiagnosticDescription[] diagnostics) { Assert.False(string.IsNullOrWhiteSpace(rootNamespace), string.Format("Invalid root namespace '{0}'", rootNamespace)); - Verify(source, LanguageNames.VisualBasic, analyzers, diagnostics, continueOnAnalyzerException: null, rootNamespace: rootNamespace); + Verify(source, LanguageNames.VisualBasic, analyzers, diagnostics, onAnalyzerException: null, rootNamespace: rootNamespace); } protected void VerifyBasic(string source, DiagnosticAnalyzer[] analyzers, params DiagnosticDescription[] diagnostics) @@ -1154,9 +1152,9 @@ protected void VerifyBasic(string source, DiagnosticAnalyzer[] analyzers, params Verify(source, LanguageNames.VisualBasic, analyzers, diagnostics); } - protected void VerifyBasic(string source, DiagnosticAnalyzer[] analyzers, Func continueOnAnalyzerException, params DiagnosticDescription[] diagnostics) + protected void VerifyBasic(string source, DiagnosticAnalyzer[] analyzers, Action onAnalyzerException, params DiagnosticDescription[] diagnostics) { - Verify(source, LanguageNames.VisualBasic, analyzers, diagnostics, continueOnAnalyzerException); + Verify(source, LanguageNames.VisualBasic, analyzers, diagnostics, onAnalyzerException); } protected void VerifyTokenDiagnosticsBasic(string markup, params DiagnosticDescription[] diagnostics) @@ -1164,11 +1162,11 @@ protected void VerifyTokenDiagnosticsBasic(string markup, params DiagnosticDescr VerifyTokenDiagnostics(markup, LanguageNames.VisualBasic, diagnostics); } - protected virtual void Verify(string source, string language, DiagnosticAnalyzer[] analyzers, DiagnosticDescription[] diagnostics, Func continueOnAnalyzerException = null, string rootNamespace = null) + protected virtual void Verify(string source, string language, DiagnosticAnalyzer[] analyzers, DiagnosticDescription[] diagnostics, Action onAnalyzerException = null, string rootNamespace = null) { Assert.True(analyzers != null && analyzers.Length > 0, "Must specify at least one diagnostic analyzer to test suppression"); var compilation = CreateCompilation(source, language, analyzers, rootNamespace); - compilation.VerifyAnalyzerDiagnostics(analyzers, continueOnAnalyzerException: continueOnAnalyzerException, expected: diagnostics); + compilation.VerifyAnalyzerDiagnostics(analyzers, onAnalyzerException: onAnalyzerException, expected: diagnostics); } // Generate a diagnostic on every token in the specified spans, and verify that only the specified diagnostics are not suppressed diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs index 15b57e3433fec9b576b7f0b376886cd8f031f7ee..01f6cfc2527a73af70f997fa2f8fa7dfdf3dcc56 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs @@ -150,7 +150,7 @@ private Task ExecuteSyntaxTreeActions(CancellationToken cancellationToken) public static AnalyzerDriver Create( Compilation compilation, ImmutableArray analyzers, - AnalyzerOptions options, + AnalyzerOptions options, AnalyzerManager analyzerManager, Action addExceptionDiagnostic, out Compilation newCompilation, @@ -171,7 +171,14 @@ private Task ExecuteSyntaxTreeActions(CancellationToken cancellationToken) throw new ArgumentException(CodeAnalysisResources.ArgumentElementCannotBeNull, nameof(analyzers)); } - return Create(compilation, analyzers, options, analyzerManager, addExceptionDiagnostic, out newCompilation, continueOnAnalyzerException: null, cancellationToken: cancellationToken); + Action onAnalyzerException = (ex, analyzer, diagnostic) => + { + if (addExceptionDiagnostic != null) + { + addExceptionDiagnostic(diagnostic); + } + }; + return Create(compilation, analyzers, options, analyzerManager, onAnalyzerException, out newCompilation, cancellationToken: cancellationToken); } // internal for testing purposes @@ -180,21 +187,30 @@ private Task ExecuteSyntaxTreeActions(CancellationToken cancellationToken) ImmutableArray analyzers, AnalyzerOptions options, AnalyzerManager analyzerManager, - Action addExceptionDiagnostic, + Action onAnalyzerException, out Compilation newCompilation, - Func continueOnAnalyzerException, CancellationToken cancellationToken) { options = options ?? AnalyzerOptions.Empty; AnalyzerDriver analyzerDriver = compilation.AnalyzerForLanguage(analyzers, analyzerManager, cancellationToken); newCompilation = compilation.WithEventQueue(analyzerDriver.CompilationEventQueue); - continueOnAnalyzerException = continueOnAnalyzerException ?? ((exception, analyzer) => true); var addDiagnostic = GetDiagnosticSinkWithSuppression(analyzerDriver.DiagnosticQueue.Enqueue, newCompilation); - addExceptionDiagnostic = addExceptionDiagnostic != null ? - GetDiagnosticSinkWithSuppression(addExceptionDiagnostic, newCompilation) : - addDiagnostic; - var analyzerExecutor = AnalyzerExecutor.Create(newCompilation, options, addDiagnostic, addExceptionDiagnostic, continueOnAnalyzerException, cancellationToken); + + if (onAnalyzerException != null) + { + // Wrap onAnalyzerException to pass in filtered diagnostic. + var comp = newCompilation; + onAnalyzerException = (ex, analyzer, diagnostic) => + onAnalyzerException(ex, analyzer, GetFilteredDiagnostic(diagnostic, comp)); + } + else + { + // Add exception diagnostic to regular diagnostic bag. + onAnalyzerException = (ex, analyzer, diagnostic) => addDiagnostic(diagnostic); + } + + var analyzerExecutor = AnalyzerExecutor.Create(newCompilation, options, addDiagnostic, onAnalyzerException, cancellationToken); analyzerDriver.Initialize(newCompilation, analyzerExecutor, cancellationToken); @@ -515,18 +531,29 @@ internal static Action GetDiagnosticSinkWithSuppression(Action { - var filteredDiagnostic = compilation.FilterDiagnostic(diagnostic); + var filteredDiagnostic = GetFilteredDiagnostic(diagnostic, compilation, symbolOpt); if (filteredDiagnostic != null) { - var suppressMessageState = SuppressMessageStateByCompilation.GetValue(compilation, (c) => new SuppressMessageAttributeState(c)); - if (!suppressMessageState.IsDiagnosticSuppressed(filteredDiagnostic, symbolOpt: symbolOpt)) - { - addDiagnosticCore(filteredDiagnostic); - } + addDiagnosticCore(filteredDiagnostic); } }; } + private static Diagnostic GetFilteredDiagnostic(Diagnostic diagnostic, Compilation compilation, ISymbol symbolOpt = null) + { + var filteredDiagnostic = compilation.FilterDiagnostic(diagnostic); + if (filteredDiagnostic != null) + { + var suppressMessageState = SuppressMessageStateByCompilation.GetValue(compilation, (c) => new SuppressMessageAttributeState(c)); + if (suppressMessageState.IsDiagnosticSuppressed(filteredDiagnostic, symbolOpt: symbolOpt)) + { + return null; + } + } + + return filteredDiagnostic; + } + private static Task GetAnalyzerActionsAsync( ImmutableArray analyzers, AnalyzerManager analyzerManager, diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs index 992a5eda6cfc6cb2ce4ac730b8009aeeca4d5c7c..93438e209842a5245304b6dcb92711690b3ac817 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs @@ -99,9 +99,14 @@ public static IEnumerable GetEffectiveDiagnostics(IEnumerable /// Returns true if all the diagnostics that can be produced by this analyzer are suppressed through options. - /// says whether the caller would like the exception thrown by the analyzers to be handled or not. If true - Handles ; False - Not handled. + /// Analyzer to be checked for suppression. + /// Compilation options. + /// + /// 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. + /// /// - public static bool IsDiagnosticAnalyzerSuppressed(DiagnosticAnalyzer analyzer, CompilationOptions options, Func continueOnAnalyzerException) + public static bool IsDiagnosticAnalyzerSuppressed(DiagnosticAnalyzer analyzer, CompilationOptions options, Action onAnalyzerException = null) { if (analyzer == null) { @@ -113,14 +118,9 @@ public static bool IsDiagnosticAnalyzerSuppressed(DiagnosticAnalyzer analyzer, C throw new ArgumentNullException(nameof(options)); } - if (continueOnAnalyzerException == null) - { - throw new ArgumentNullException(nameof(continueOnAnalyzerException)); - } - - // TODO: Public API change to surface exception diagnostics? - Action addExceptionDiagnostic = diagnostic => { }; - var analyzerExecutor = AnalyzerExecutor.CreateForSupportedDiagnostics(addExceptionDiagnostic, continueOnAnalyzerException, CancellationToken.None); + Action voidHandler = (ex, a, diag) => { }; + onAnalyzerException = onAnalyzerException ?? voidHandler; + var analyzerExecutor = AnalyzerExecutor.CreateForSupportedDiagnostics(onAnalyzerException, CancellationToken.None); return AnalyzerDriver.IsDiagnosticAnalyzerSuppressed(analyzer, options, AnalyzerManager.Instance, analyzerExecutor); } diff --git a/src/Compilers/Core/Portable/PublicAPI.txt b/src/Compilers/Core/Portable/PublicAPI.txt index c3f15753d3e524e9706ca207af39e935a64659e5..f5fad49b5cf5d3ecd09a6cf8dfa0503665746b25 100644 --- a/src/Compilers/Core/Portable/PublicAPI.txt +++ b/src/Compilers/Core/Portable/PublicAPI.txt @@ -1900,7 +1900,7 @@ static Microsoft.CodeAnalysis.Diagnostic.Create(Microsoft.CodeAnalysis.Diagnosti static Microsoft.CodeAnalysis.Diagnostic.Create(Microsoft.CodeAnalysis.DiagnosticDescriptor descriptor, Microsoft.CodeAnalysis.Location location, params object[] messageArgs) static Microsoft.CodeAnalysis.Diagnostic.Create(string id, string category, Microsoft.CodeAnalysis.LocalizableString message, Microsoft.CodeAnalysis.DiagnosticSeverity severity, Microsoft.CodeAnalysis.DiagnosticSeverity defaultSeverity, bool isEnabledByDefault, int warningLevel, Microsoft.CodeAnalysis.LocalizableString title = null, Microsoft.CodeAnalysis.LocalizableString description = null, string helpLink = null, Microsoft.CodeAnalysis.Location location = null, System.Collections.Generic.IEnumerable additionalLocations = null, System.Collections.Generic.IEnumerable customTags = null, System.Collections.Immutable.ImmutableDictionary properties = null) static Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzers.GetEffectiveDiagnostics(System.Collections.Generic.IEnumerable diagnostics, Microsoft.CodeAnalysis.Compilation compilation) -static Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzers.IsDiagnosticAnalyzerSuppressed(Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer analyzer, Microsoft.CodeAnalysis.CompilationOptions options, System.Func continueOnAnalyzerException) +static Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzers.IsDiagnosticAnalyzerSuppressed(Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer analyzer, Microsoft.CodeAnalysis.CompilationOptions options, System.Action onAnalyzerException = null) static Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzerExtensions.WithAnalyzers(this Microsoft.CodeAnalysis.Compilation compilation, System.Collections.Immutable.ImmutableArray analyzers, Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) static Microsoft.CodeAnalysis.Emit.EditAndContinueMethodDebugInformation.Create(System.Collections.Immutable.ImmutableArray compressedSlotMap, System.Collections.Immutable.ImmutableArray compressedLambdaMap) static Microsoft.CodeAnalysis.Emit.EmitBaseline.CreateInitialBaseline(Microsoft.CodeAnalysis.ModuleMetadata module, System.Func debugInformationProvider) @@ -2017,8 +2017,8 @@ static Microsoft.CodeAnalysis.Text.LinePosition.operator >=(Microsoft.CodeAnalys static Microsoft.CodeAnalysis.Text.LinePositionSpan.operator !=(Microsoft.CodeAnalysis.Text.LinePositionSpan left, Microsoft.CodeAnalysis.Text.LinePositionSpan right) static Microsoft.CodeAnalysis.Text.LinePositionSpan.operator ==(Microsoft.CodeAnalysis.Text.LinePositionSpan left, Microsoft.CodeAnalysis.Text.LinePositionSpan right) static Microsoft.CodeAnalysis.Text.SourceText.CalculateChecksum(byte[] buffer, int offset, int count, Microsoft.CodeAnalysis.Text.SourceHashAlgorithm algorithmId) -static Microsoft.CodeAnalysis.Text.SourceText.From(byte[] buffer, int length, System.Text.Encoding encoding = null, Microsoft.CodeAnalysis.Text.SourceHashAlgorithm checksumAlgorithm = Microsoft.CodeAnalysis.Text.SourceHashAlgorithm.Sha1, bool throwIfBinaryDetected = false) static Microsoft.CodeAnalysis.Text.SourceText.From(System.IO.Stream stream, System.Text.Encoding encoding = null, Microsoft.CodeAnalysis.Text.SourceHashAlgorithm checksumAlgorithm = Microsoft.CodeAnalysis.Text.SourceHashAlgorithm.Sha1, bool throwIfBinaryDetected = false) +static Microsoft.CodeAnalysis.Text.SourceText.From(byte[] buffer, int length, System.Text.Encoding encoding = null, Microsoft.CodeAnalysis.Text.SourceHashAlgorithm checksumAlgorithm = Microsoft.CodeAnalysis.Text.SourceHashAlgorithm.Sha1, bool throwIfBinaryDetected = false) static Microsoft.CodeAnalysis.Text.SourceText.From(string text, System.Text.Encoding encoding = null, Microsoft.CodeAnalysis.Text.SourceHashAlgorithm checksumAlgorithm = Microsoft.CodeAnalysis.Text.SourceHashAlgorithm.Sha1) static Microsoft.CodeAnalysis.Text.TextChange.implicit operator Microsoft.CodeAnalysis.Text.TextChangeRange(Microsoft.CodeAnalysis.Text.TextChange change) static Microsoft.CodeAnalysis.Text.TextChange.operator !=(Microsoft.CodeAnalysis.Text.TextChange left, Microsoft.CodeAnalysis.Text.TextChange right) @@ -2067,8 +2067,8 @@ virtual Microsoft.CodeAnalysis.Location.MetadataModule.get virtual Microsoft.CodeAnalysis.Location.SourceSpan.get virtual Microsoft.CodeAnalysis.Location.SourceTree.get virtual Microsoft.CodeAnalysis.MetadataReference.Display.get -virtual Microsoft.CodeAnalysis.SemanticModel.IgnoresAccessibility.get virtual Microsoft.CodeAnalysis.SemanticModel.GetTopmostNodeForDiagnosticAnalysis(Microsoft.CodeAnalysis.ISymbol symbol, Microsoft.CodeAnalysis.SyntaxNode declaringSyntax) +virtual Microsoft.CodeAnalysis.SemanticModel.IgnoresAccessibility.get virtual Microsoft.CodeAnalysis.SymbolVisitor.DefaultVisit(Microsoft.CodeAnalysis.ISymbol symbol) virtual Microsoft.CodeAnalysis.SymbolVisitor.Visit(Microsoft.CodeAnalysis.ISymbol symbol) virtual Microsoft.CodeAnalysis.SymbolVisitor.VisitAlias(Microsoft.CodeAnalysis.IAliasSymbol symbol) diff --git a/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.AllInOne.vb b/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.AllInOne.vb index 6a38126699b2ef93672434fb2d952e15ddf9cc05..9cd5309174aadc697bb13ad82ffc142ce4a13ce0 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.AllInOne.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.AllInOne.vb @@ -43,7 +43,7 @@ End Enum Public Sub AnalyzerDriverIsSafeAgainstAnalyzerExceptions() Dim compilation = CreateCompilationWithMscorlib({TestResource.AllInOneVisualBasicCode}) ThrowingDiagnosticAnalyzer(Of SyntaxKind).VerifyAnalyzerEngineIsSafeAgainstExceptions( - Function(analyzer) compilation.GetAnalyzerDiagnostics({analyzer}, Nothing, DiagnosticExtensions.AlwaysCatchAnalyzerExceptions)) + Function(analyzer) compilation.GetAnalyzerDiagnostics({analyzer}, Nothing, DiagnosticExtensions.AlwaysCatchAnalyzerException)) End Sub diff --git a/src/Diagnostics/FxCop/Test/HardeningAnalyzer/HardeningAnalyzerTests.cs b/src/Diagnostics/FxCop/Test/HardeningAnalyzer/HardeningAnalyzerTests.cs index 320efbde9ffadd55c60d0e5fb0c9669987d2fbb1..2148042de141c568d9c77f75ac48309707c9f789 100644 --- a/src/Diagnostics/FxCop/Test/HardeningAnalyzer/HardeningAnalyzerTests.cs +++ b/src/Diagnostics/FxCop/Test/HardeningAnalyzer/HardeningAnalyzerTests.cs @@ -44,7 +44,7 @@ public class Class6 "; var diagnosticsBag = DiagnosticBag.GetInstance(); var documentsAndSpan = GetDocumentsAndSpans(new[] { source }, LanguageNames.CSharp); - AnalyzeDocumentCore(GetCSharpDiagnosticAnalyzer(), documentsAndSpan.Item1[0], diagnosticsBag.Add, null, continueOnAnalyzerException: DiagnosticExtensions.AlwaysCatchAnalyzerExceptions); + AnalyzeDocumentCore(GetCSharpDiagnosticAnalyzer(), documentsAndSpan.Item1[0], diagnosticsBag.Add, null, onAnalyzerException: DiagnosticExtensions.AlwaysCatchAnalyzerException); var diagnostics = diagnosticsBag.ToReadOnlyAndFree(); Assert.True(diagnostics.Length > 0); Assert.Equal(string.Format("info AD0001: " + AnalyzerDriverResources.AnalyzerThrows, GetCSharpDiagnosticAnalyzer().GetType(), "The method or operation is not implemented."), diff --git a/src/Diagnostics/Test/Utilities/DiagnosticAnalyzerTestBase.cs b/src/Diagnostics/Test/Utilities/DiagnosticAnalyzerTestBase.cs index 3786e7679ba147f0e82b61430fba5a03ac6c40ac..c76d1129f31ed3c43a0db51f31db54d2738b15b4 100644 --- a/src/Diagnostics/Test/Utilities/DiagnosticAnalyzerTestBase.cs +++ b/src/Diagnostics/Test/Utilities/DiagnosticAnalyzerTestBase.cs @@ -355,13 +355,13 @@ private static Compilation EnableAnalyzer(DiagnosticAnalyzer analyzer, Compilati .ToImmutableDictionaryOrEmpty())); } - protected static void AnalyzeDocumentCore(DiagnosticAnalyzer analyzer, Document document, Action addDiagnostic, TextSpan? span = null, Func continueOnAnalyzerException = null) + protected static void AnalyzeDocumentCore(DiagnosticAnalyzer analyzer, Document document, Action addDiagnostic, TextSpan? span = null, Action onAnalyzerException = null) { var semanticModel = document.GetSemanticModelAsync().Result; var compilation = semanticModel.Compilation; compilation = EnableAnalyzer(analyzer, compilation); - var diagnostics = compilation.GetAnalyzerDiagnostics(new[] { analyzer }, continueOnAnalyzerException: continueOnAnalyzerException); + var diagnostics = compilation.GetAnalyzerDiagnostics(new[] { analyzer }, onAnalyzerException: onAnalyzerException); foreach (var diagnostic in diagnostics) { if (!span.HasValue || diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticProviderTestUtilities.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticProviderTestUtilities.cs index 428907a4ed1f22a747b77e57997e578a7d3a88f7..6f3bc9e6af580d71cf18251587238f3b59b84be4 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticProviderTestUtilities.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticProviderTestUtilities.cs @@ -49,7 +49,7 @@ private static IEnumerable GetDiagnostics(DiagnosticAnalyzer analyze syntaxNodeAnalyzerService: nodeInBodyAnalyzerService, hostDiagnosticUpdateSource: exceptionDiagnosticsSource, cancellationToken: CancellationToken.None, - testOnly_DonotCatchAnalyzerExceptions: donotCatchAnalyzerExceptions); + testOnly_RethrowAnalyzerException: donotCatchAnalyzerExceptions); var diagnosticAnalyzerCategory = analyzer.GetDiagnosticAnalyzerCategory(driver); bool supportsSemanticInSpan = (diagnosticAnalyzerCategory & DiagnosticAnalyzerCategory.SemanticSpanAnalysis) != 0; if (supportsSemanticInSpan) diff --git a/src/EditorFeatures/Test/Diagnostics/SuppressMessageAttributeTests.cs b/src/EditorFeatures/Test/Diagnostics/SuppressMessageAttributeTests.cs index 29aae3e43c63ef542a26a6db272e3d5540664859..109660ae3e0985b9f241c10d642480f8c863192a 100644 --- a/src/EditorFeatures/Test/Diagnostics/SuppressMessageAttributeTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/SuppressMessageAttributeTests.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.UnitTests.Diagnostics { public class SuppressMessageAttributeWorkspaceTests : SuppressMessageAttributeTests { - protected override void Verify(string source, string language, DiagnosticAnalyzer[] analyzers, DiagnosticDescription[] expectedDiagnostics, Func continueOnAnalyzerException = null, string rootNamespace = null) + protected override void Verify(string source, string language, DiagnosticAnalyzer[] analyzers, DiagnosticDescription[] expectedDiagnostics, Action onAnalyzerException = null, string rootNamespace = null) { using (var workspace = CreateWorkspaceFromFile(source, language, rootNamespace)) { @@ -24,7 +24,7 @@ protected override void Verify(string source, string language, DiagnosticAnalyze var actualDiagnostics = new List(); foreach (var analyzer in analyzers) { - actualDiagnostics.AddRange(DiagnosticProviderTestUtilities.GetAllDiagnostics(analyzer, document, span, donotCatchAnalyzerExceptions: continueOnAnalyzerException == null)); + actualDiagnostics.AddRange(DiagnosticProviderTestUtilities.GetAllDiagnostics(analyzer, document, span, donotCatchAnalyzerExceptions: onAnalyzerException == null)); } actualDiagnostics.Verify(expectedDiagnostics); diff --git a/src/Features/Core/Diagnostics/AnalyzerHelper.cs b/src/Features/Core/Diagnostics/AnalyzerHelper.cs index 5be54eec9948e9917cbb9356f1f671bba5c7086d..34e2a37e0ce291491dd70633e8cff08623ece267 100644 --- a/src/Features/Core/Diagnostics/AnalyzerHelper.cs +++ b/src/Features/Core/Diagnostics/AnalyzerHelper.cs @@ -34,43 +34,41 @@ public static bool IsCompilerAnalyzer(DiagnosticAnalyzer analyzer) return false; } - public static Action GetAddExceptionDiagnosticDelegate(DiagnosticAnalyzer analyzer, AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource, Project project) + internal static AnalyzerExecutor GetAnalyzerExecutorForSupportedDiagnostics( + DiagnosticAnalyzer analyzer, + AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource, + CancellationToken cancellationToken = default(CancellationToken)) { - return diagnostic => - hostDiagnosticUpdateSource?.ReportAnalyzerDiagnostic(analyzer, diagnostic, project.Solution.Workspace, project); - } + // Skip telemetry logging if the exception is thrown as we are computing supported diagnostics and + // we can't determine if any descriptors support getting telemetry without having the descriptors. + Action onAnalyzerException = (ex, a, diagnostic) => + OnAnalyzerException_NoTelemetryLogging(ex, a, diagnostic, hostDiagnosticUpdateSource); - public static Action GetAddExceptionDiagnosticDelegate(DiagnosticAnalyzer analyzer, AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource, Workspace workspace) - { - return diagnostic => - hostDiagnosticUpdateSource?.ReportAnalyzerDiagnostic(analyzer, diagnostic, workspace, null); + return AnalyzerExecutor.CreateForSupportedDiagnostics(onAnalyzerException, cancellationToken); } - public static AnalyzerExecutor GetAnalyzerExecutorForSupportedDiagnostics( - DiagnosticAnalyzer analyzer, + internal static void OnAnalyzerException_NoTelemetryLogging( + Exception e, + DiagnosticAnalyzer analyzer, + Diagnostic diagnostic, AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource, - Func continueOnAnalyzerException, - CancellationToken cancellationToken) + Project projectOpt = null, + bool testOnly_RethrowAnalyzerException = false) { - var addExceptionDiagnostic = GetAddExceptionDiagnosticDelegate(analyzer, hostDiagnosticUpdateSource, hostDiagnosticUpdateSource?.Workspace); + if (diagnostic != null) + { + hostDiagnosticUpdateSource?.ReportAnalyzerDiagnostic(analyzer, diagnostic, hostDiagnosticUpdateSource?.Workspace, projectOpt); + } - // Skip telemetry logging if the exception is thrown as we are computing supported diagnostics and - // we can't determine if any descriptors support getting telemetry without having the descriptors. - return AnalyzerExecutor.CreateForSupportedDiagnostics(addExceptionDiagnostic, continueOnAnalyzerException, cancellationToken); - } - - public static AnalyzerExecutor GetAnalyzerExecutor( - DiagnosticAnalyzer analyzer, - AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource, - Project project, - Compilation compilation, - Action addDiagnostic, - AnalyzerOptions analyzerOptions, - Func continueOnAnalyzerException, - CancellationToken cancellationToken) - { - var addExceptionDiagnostic = GetAddExceptionDiagnosticDelegate(analyzer, hostDiagnosticUpdateSource, project); - return AnalyzerExecutor.Create(compilation, analyzerOptions, addDiagnostic, addExceptionDiagnostic, continueOnAnalyzerException, cancellationToken); + if (testOnly_RethrowAnalyzerException) + { + throw e; + } + + if (IsBuiltInAnalyzer(analyzer)) + { + FatalError.ReportWithoutCrashUnlessCanceled(e); + } } } } \ No newline at end of file diff --git a/src/Features/Core/Diagnostics/EngineV1/DiagnosticAnalyzerDriver.cs b/src/Features/Core/Diagnostics/EngineV1/DiagnosticAnalyzerDriver.cs index 3fd868649aa0ca6dcba1847757e4d798dfa43c05..3feecb31d0ac3af476cb255957408bdf672bb3cd 100644 --- a/src/Features/Core/Diagnostics/EngineV1/DiagnosticAnalyzerDriver.cs +++ b/src/Features/Core/Diagnostics/EngineV1/DiagnosticAnalyzerDriver.cs @@ -34,7 +34,7 @@ internal class DiagnosticAnalyzerDriver private readonly ISyntaxFactsService _syntaxFacts; private readonly IGeneratedCodeRecognitionService _generatedCodeService; private readonly IAnalyzerDriverService _analyzerDriverService; - private readonly bool _testOnly_DonotCatchAnalyzerExceptions; + private readonly bool _testOnly_RethrowAnalyzerException; private LogAggregator _logAggregator; @@ -64,7 +64,7 @@ public DiagnosticAnalyzerDriver(Project project, LogAggregator logAggregator, Ab ISyntaxNodeAnalyzerService syntaxNodeAnalyzerService, AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource, CancellationToken cancellationToken, - bool testOnly_DonotCatchAnalyzerExceptions = false) + bool testOnly_RethrowAnalyzerException = false) { _document = document; _span = span; @@ -78,7 +78,7 @@ public DiagnosticAnalyzerDriver(Project project, LogAggregator logAggregator, Ab _generatedCodeService = document.Project.Solution.Workspace.Services.GetService(); _analyzerDriverService = document.Project.LanguageServices.GetService(); _analyzerOptions = new WorkspaceAnalyzerOptions(_project.AnalyzerOptions, _project.Solution.Workspace); - _testOnly_DonotCatchAnalyzerExceptions = testOnly_DonotCatchAnalyzerExceptions; + _testOnly_RethrowAnalyzerException = testOnly_RethrowAnalyzerException; } // internal for testing purposes @@ -260,14 +260,9 @@ public async Task> GetSyntaxDiagnosticsAsync(Diagnost await documentAnalyzer.AnalyzeSyntaxAsync(_document, diagnostics.Add, _cancellationToken).ConfigureAwait(false); return diagnostics.ToImmutableArrayOrEmpty(); } - catch (Exception e) when (CatchAnalyzerException(e, analyzer)) + catch (Exception e) { - var exceptionDiagnostic = AnalyzerExceptionToDiagnostic(analyzer, e, _cancellationToken); - if (exceptionDiagnostic != null) - { - ReportAnalyzerExceptionDiagnostic(analyzer, exceptionDiagnostic, compilation); - } - + OnAnalyzerException(e, analyzer, compilation); return ImmutableArray.Empty; } } @@ -312,47 +307,21 @@ private IEnumerable GetFilteredDocumentDiagnosticsCore(IEnumerable GetAddExceptionDiagnosticDelegate(DiagnosticAnalyzer analyzer) - { - return AnalyzerHelper.GetAddExceptionDiagnosticDelegate(analyzer, _hostDiagnosticUpdateSource, _project); - } - - private AnalyzerExecutor GetAnalyzerExecutorForSupportedDiagnostics(DiagnosticAnalyzer analyzer) - { - // Skip telemetry logging if the exception is thrown as we are computing supported diagnostics and - // we can't determine if any descriptors support getting telemetry without having the descriptors. - return AnalyzerHelper.GetAnalyzerExecutorForSupportedDiagnostics(analyzer, _hostDiagnosticUpdateSource, CatchAnalyzerException_NoTelemetryLogging, _cancellationToken); + OnAnalyzerException(ex, analyzer, exceptionDiagnostic); } private AnalyzerExecutor GetAnalyzerExecutor(DiagnosticAnalyzer analyzer, Compilation compilation, Action addDiagnostic) { - return AnalyzerHelper.GetAnalyzerExecutor(analyzer, _hostDiagnosticUpdateSource, _project, - compilation, addDiagnostic, _analyzerOptions, CatchAnalyzerException, _cancellationToken); + return AnalyzerExecutor.Create(compilation, _analyzerOptions, addDiagnostic, OnAnalyzerException, _cancellationToken); } public async Task GetAnalyzerActionsAsync(DiagnosticAnalyzer analyzer) @@ -370,7 +339,7 @@ public bool IsAnalyzerSuppressed(DiagnosticAnalyzer analyzer) return false; } - var analyzerExecutor = GetAnalyzerExecutorForSupportedDiagnostics(analyzer); + var analyzerExecutor = AnalyzerHelper.GetAnalyzerExecutorForSupportedDiagnostics(analyzer, _hostDiagnosticUpdateSource); return AnalyzerManager.Instance.IsDiagnosticAnalyzerSuppressed(analyzer, options, AnalyzerHelper.IsCompilerAnalyzer, analyzerExecutor); } @@ -406,14 +375,9 @@ public async Task> GetSemanticDiagnosticsAsync(Diagno { await documentAnalyzer.AnalyzeSemanticsAsync(_document, diagnostics.Add, _cancellationToken).ConfigureAwait(false); } - catch (Exception e) when (CatchAnalyzerException(e, analyzer)) + catch (Exception e) { - var exceptionDiagnostic = AnalyzerExceptionToDiagnostic(analyzer, e, _cancellationToken); - if (exceptionDiagnostic != null) - { - ReportAnalyzerExceptionDiagnostic(analyzer, exceptionDiagnostic, compilation); - } - + OnAnalyzerException(e, analyzer, compilation); return ImmutableArray.Empty; } } @@ -487,14 +451,10 @@ private async Task GetProjectDiagnosticsWorkerAsync(DiagnosticAnalyzer analyzer, { await projectAnalyzer.AnalyzeProjectAsync(_project, diagnostics.Add, _cancellationToken).ConfigureAwait(false); } - catch (Exception e) when (CatchAnalyzerException(e, analyzer)) + catch (Exception e) { - var exceptionDiagnostic = AnalyzerExceptionToDiagnostic(analyzer, e, _cancellationToken); - if (exceptionDiagnostic != null) - { - var compilation = await _project.GetCompilationAsync(_cancellationToken).ConfigureAwait(false); - ReportAnalyzerExceptionDiagnostic(analyzer, exceptionDiagnostic, compilation); - } + var compilation = await _project.GetCompilationAsync(_cancellationToken).ConfigureAwait(false); + OnAnalyzerException(e, analyzer, compilation); } } @@ -544,41 +504,21 @@ private static bool IsCanceled(Exception e, CancellationToken cancellationToken) return canceled != null && canceled.CancellationToken == cancellationToken; } - private bool CatchAnalyzerException(Exception e, DiagnosticAnalyzer analyzer) + private void OnAnalyzerException(Exception e, DiagnosticAnalyzer analyzer, Diagnostic diagnostic) { - return CatchAnalyzerException(e, analyzer, _testOnly_DonotCatchAnalyzerExceptions); + OnAnalyzerException(e, analyzer, diagnostic, _testOnly_RethrowAnalyzerException); } - private bool CatchAnalyzerException_NoTelemetryLogging(Exception e, DiagnosticAnalyzer analyzer) + private void OnAnalyzerException_NoTelemetryLogging(Exception e, DiagnosticAnalyzer analyzer, Diagnostic diagnostic) { - return CatchAnalyzerException_NoTelemetryLogging(e, analyzer, _testOnly_DonotCatchAnalyzerExceptions); + AnalyzerHelper.OnAnalyzerException_NoTelemetryLogging(e, analyzer, diagnostic, _hostDiagnosticUpdateSource, _project, _testOnly_RethrowAnalyzerException); } - internal bool CatchAnalyzerExceptionHandler(Exception e, DiagnosticAnalyzer analyzer) - { - return CatchAnalyzerException(e, analyzer, testOnly_DonotCatchAnalyzerExceptions: false); - } - - private bool CatchAnalyzerException(Exception e, DiagnosticAnalyzer analyzer, bool testOnly_DonotCatchAnalyzerExceptions) + private void OnAnalyzerException(Exception e, DiagnosticAnalyzer analyzer, Diagnostic diagnostic, bool testOnly_RethrowAnalyzerException) { DiagnosticAnalyzerLogger.LogAnalyzerCrashCount(analyzer, e, _logAggregator); - return CatchAnalyzerException_NoTelemetryLogging(e, analyzer, testOnly_DonotCatchAnalyzerExceptions); - } - - internal static bool CatchAnalyzerException_NoTelemetryLogging(Exception e, DiagnosticAnalyzer analyzer, bool testOnly_DonotCatchAnalyzerExceptions) - { - if (testOnly_DonotCatchAnalyzerExceptions) - { - return false; - } - - if (AnalyzerHelper.IsBuiltInAnalyzer(analyzer)) - { - return FatalError.ReportWithoutCrashUnlessCanceled(e); - } - - return true; + AnalyzerHelper.OnAnalyzerException_NoTelemetryLogging(e, analyzer, diagnostic, _hostDiagnosticUpdateSource, _project, testOnly_RethrowAnalyzerException); } } } diff --git a/src/Features/Core/Diagnostics/WorkspaceAnalyzerManager.cs b/src/Features/Core/Diagnostics/WorkspaceAnalyzerManager.cs index 107556424c9473749711802ce043746ba7bcfa43..27d001587d6660f98935f6b134fa407242e71903 100644 --- a/src/Features/Core/Diagnostics/WorkspaceAnalyzerManager.cs +++ b/src/Features/Core/Diagnostics/WorkspaceAnalyzerManager.cs @@ -81,8 +81,7 @@ public string GetAnalyzerReferenceIdentity(AnalyzerReference reference) /// public ImmutableArray GetDiagnosticDescriptors(DiagnosticAnalyzer analyzer) { - Func continueOnAnalyzerException = (ex, a) => !AnalyzerHelper.IsBuiltInAnalyzer(analyzer); - var analyzerExecutor = AnalyzerHelper.GetAnalyzerExecutorForSupportedDiagnostics(analyzer, _hostDiagnosticUpdateSource, continueOnAnalyzerException, CancellationToken.None); + var analyzerExecutor = AnalyzerHelper.GetAnalyzerExecutorForSupportedDiagnostics(analyzer, _hostDiagnosticUpdateSource); return AnalyzerManager.Instance.GetSupportedDiagnosticDescriptors(analyzer, analyzerExecutor); } diff --git a/src/Test/Utilities/DiagnosticExtensions.cs b/src/Test/Utilities/DiagnosticExtensions.cs index d2804f551c5bf84498117d2873aae6a8c0e70227..9af11b403c5097c1b74c3f20c5ce9afb3965a25a 100644 --- a/src/Test/Utilities/DiagnosticExtensions.cs +++ b/src/Test/Utilities/DiagnosticExtensions.cs @@ -20,8 +20,8 @@ namespace Microsoft.CodeAnalysis public static class DiagnosticExtensions { private const int EN_US = 1033; - public static Func AlwaysCatchAnalyzerExceptions = (e, a) => true; - public static Func DonotCatchAnalyzerExceptions = (e, a) => e is OperationCanceledException; + public static Action AlwaysCatchAnalyzerException = (e, a, d) => { }; + public static Action RethrowAnalyzerException = (e, a, d) => { if (!(e is OperationCanceledException)) throw e; }; /// /// This is obsolete. Use Verify instead. @@ -100,27 +100,39 @@ public static TCompilation VerifyDiagnostics(this TCompilation c, return c; } - public static void VerifyAnalyzerOccurrenceCount(this TCompilation c, DiagnosticAnalyzer[] analyzers, int expectedCount, Func continueOnAnalyzerException = null) + public static void VerifyAnalyzerOccurrenceCount( + this TCompilation c, + DiagnosticAnalyzer[] analyzers, + int expectedCount, + Action onAnalyzerException = null) where TCompilation : Compilation { - Assert.Equal(expectedCount, c.GetAnalyzerDiagnostics(analyzers, null, continueOnAnalyzerException).Length); + Assert.Equal(expectedCount, c.GetAnalyzerDiagnostics(analyzers, null, onAnalyzerException).Length); } public static TCompilation VerifyAnalyzerDiagnostics( - this TCompilation c, DiagnosticAnalyzer[] analyzers, AnalyzerOptions options = null, Func continueOnAnalyzerException = null, params DiagnosticDescription[] expected) + this TCompilation c, + DiagnosticAnalyzer[] analyzers, + AnalyzerOptions options = null, + Action onAnalyzerException = null, + params DiagnosticDescription[] expected) where TCompilation : Compilation { ImmutableArray diagnostics; - c = c.GetAnalyzerDiagnostics(analyzers, options, continueOnAnalyzerException, diagnostics: out diagnostics); + c = c.GetAnalyzerDiagnostics(analyzers, options, onAnalyzerException, diagnostics: out diagnostics); diagnostics.Verify(expected); return c; // note this is a new compilation } - public static ImmutableArray GetAnalyzerDiagnostics(this TCompilation c, DiagnosticAnalyzer[] analyzers, AnalyzerOptions options = null, Func continueOnAnalyzerException = null) + public static ImmutableArray GetAnalyzerDiagnostics( + this TCompilation c, + DiagnosticAnalyzer[] analyzers, + AnalyzerOptions options = null, + Action onAnalyzerException = null) where TCompilation : Compilation { ImmutableArray diagnostics; - c = GetAnalyzerDiagnostics(c, analyzers, options, continueOnAnalyzerException, out diagnostics); + c = GetAnalyzerDiagnostics(c, analyzers, options, onAnalyzerException, out diagnostics); return diagnostics; } @@ -128,19 +140,30 @@ public static ImmutableArray GetAnalyzerDiagnostics(th this TCompilation c, DiagnosticAnalyzer[] analyzers, AnalyzerOptions options, - Func continueOnAnalyzerException, + Action onAnalyzerException, out ImmutableArray diagnostics) where TCompilation : Compilation { - // We want unit tests to throw if any analyzer OR the driver throws, unless the test explicitly provides a delegate. - continueOnAnalyzerException = continueOnAnalyzerException ?? DonotCatchAnalyzerExceptions; var analyzersArray = analyzers.ToImmutableArray(); var exceptionDiagnostics = new ConcurrentSet(); - Action addExceptionDiagnostic = d => exceptionDiagnostics.Add(d); + + if (onAnalyzerException != null) + { + onAnalyzerException = (ex, analyzer, diagnostic) => + { + exceptionDiagnostics.Add(diagnostic); + onAnalyzerException(ex, analyzer, diagnostic); + }; + } + else + { + // We want unit tests to throw if any analyzer OR the driver throws, unless the test explicitly provides a delegate. + onAnalyzerException = RethrowAnalyzerException; + } Compilation newCompilation; - var driver = AnalyzerDriver.Create(c, analyzersArray, options, AnalyzerManager.Instance, addExceptionDiagnostic, out newCompilation, continueOnAnalyzerException, CancellationToken.None); + var driver = AnalyzerDriver.Create(c, analyzersArray, options, AnalyzerManager.Instance, onAnalyzerException, out newCompilation, CancellationToken.None); var discarded = newCompilation.GetDiagnostics(); diagnostics = driver.GetDiagnosticsAsync().Result.AddRange(exceptionDiagnostics); @@ -165,7 +188,7 @@ public static IEnumerable GetEffectiveDiagnostics(this Compilation c /// public static bool IsDiagnosticAnalyzerSuppressed(this DiagnosticAnalyzer analyzer, CompilationOptions options) { - return CompilationWithAnalyzers.IsDiagnosticAnalyzerSuppressed(analyzer, options, (exception, throwingAnalyzer) => true); + return CompilationWithAnalyzers.IsDiagnosticAnalyzerSuppressed(analyzer, options); } public static TCompilation VerifyEmitDiagnostics(this TCompilation c, EmitOptions options, params DiagnosticDescription[] expected)