Skip to content

Commit

Permalink
Don't silently eat a certain class of analyzer-thrown exceptions
Browse files Browse the repository at this point in the history
If an analyzer threw an exception, we generate a message that tells
you what diagnostic IDs the analzyer produces, so you know what to
disable so you can work around the analyzer. If the analyzer then also
were to throw exceptions while we ask it for it's SupportedDiagnostics,
we were eating that exception. This includes that as well.
  • Loading branch information
jasonmalinowski committed Feb 3, 2022
1 parent d23fe1e commit 496d572
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 8 deletions.
8 changes: 7 additions & 1 deletion src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11722,7 +11722,13 @@ public void LoadinganalyzerNetStandard13()
at TestAnalyzer.get_SupportedDiagnostics()
at Microsoft.CodeAnalysis.Diagnostics.AnalyzerManager.AnalyzerExecutionContext.<>c__DisplayClass20_0.<ComputeDiagnosticDescriptors>b__0(Object _)
at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteAndCatchIfThrows_NoLock[TArg](DiagnosticAnalyzer analyzer, Action`1 analyze, TArg argument, Nullable`1 info)
-----", outputWithoutPaths);
-----
Analyzer 'TestAnalyzer' threw the following exception:
'System.NotImplementedException: 28
at TestAnalyzer.get_SupportedDiagnostics()
at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.CreateDisablingMessage(DiagnosticAnalyzer analyzer, String analyzerName)
-----
'.", outputWithoutPaths);

Assert.Equal(0, result.ExitCode);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,13 +154,16 @@ public class C2
public async Task AnalyzerExceptionFromSupportedDiagnosticsCall()
{
var exception = new Exception();
const string AnalyzerName = "Microsoft.CodeAnalysis.UnitTests.Diagnostics.SuppressMessageAttributeTests+ThrowExceptionFromSupportedDiagnostics";

var diagnostic = Diagnostic("AD0001", null)
.WithArguments(
"Microsoft.CodeAnalysis.UnitTests.Diagnostics.SuppressMessageAttributeTests+ThrowExceptionFromSupportedDiagnostics",
AnalyzerName,
"System.Exception",
exception.Message,
(IFormattable)$@"{new LazyToString(() => exception.ToString().Substring(0, exception.ToString().IndexOf("---")))}-----")
(IFormattable)$@"{new LazyToString(() =>
exception.ToString().Substring(0, exception.ToString().IndexOf("---")) + "-----" + Environment.NewLine + Environment.NewLine +
string.Format(CodeAnalysisResources.CompilerAnalyzerThrowsDescription, AnalyzerName, exception.ToString() + Environment.NewLine + "-----" + Environment.NewLine))}")
.WithLocation(1, 1);

await VerifyCSharpAsync("public class C { }",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1629,7 +1629,7 @@ internal static Diagnostic CreateAnalyzerExceptionDiagnostic(DiagnosticAnalyzer
var analyzerName = analyzer.ToString();
var title = CodeAnalysisResources.CompilerAnalyzerFailure;
var messageFormat = CodeAnalysisResources.CompilerAnalyzerThrows;
var contextInformation = string.Join(Environment.NewLine, CreateDiagnosticDescription(info, e), CreateDisablingMessage(analyzer)).Trim();
var contextInformation = string.Join(Environment.NewLine, CreateDiagnosticDescription(info, e), CreateDisablingMessage(analyzer, analyzerName)).Trim();
var messageArguments = new[] { analyzerName, e.GetType().ToString(), e.Message, contextInformation };
var description = string.Format(CodeAnalysisResources.CompilerAnalyzerThrowsDescription, analyzerName, CreateDiagnosticDescription(info, e));
var descriptor = GetAnalyzerExceptionDiagnosticDescriptor(AnalyzerExceptionDiagnosticId, title, description, messageFormat);
Expand All @@ -1647,19 +1647,23 @@ private static string CreateDiagnosticDescription(AnalysisContextInfo? info, Exc
string.Format(CodeAnalysisResources.ExceptionContext, info?.GetContext()), e.CreateDiagnosticDescription());
}

private static string CreateDisablingMessage(DiagnosticAnalyzer analyzer)
private static string CreateDisablingMessage(DiagnosticAnalyzer analyzer, string analyzerName)
{
var diagnosticIds = ImmutableSortedSet<string>.Empty.WithComparer(StringComparer.OrdinalIgnoreCase);
try
{
foreach (var diagnostic in analyzer.SupportedDiagnostics)
{
diagnosticIds = diagnosticIds.Add(diagnostic.Id);
// If a null diagnostic is returned, we would have already reported that to the user earlier; we can just skip this.
if (diagnostic != null)
{
diagnosticIds = diagnosticIds.Add(diagnostic.Id);
}
}
}
catch (Exception)
catch (Exception ex)
{
// Intentionally empty
return string.Format(CodeAnalysisResources.CompilerAnalyzerThrowsDescription, analyzerName, ex.CreateDiagnosticDescription());
}

if (diagnosticIds.IsEmpty)
Expand Down

0 comments on commit 496d572

Please sign in to comment.