Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new analyzer API (DiagnosticSuppressor) to allow programmatic sup… #36067

Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2686,9 +2686,9 @@ internal override bool CompileMethods(
filterOpt: filterOpt,
cancellationToken: cancellationToken);

bool hasMethodBodyErrorOrWarningAsError = !FilterAndAppendAndFreeDiagnostics(diagnostics, ref methodBodyDiagnosticBag);
bool hasMethodBodyError = !FilterAndAppendAndFreeDiagnostics(diagnostics, ref methodBodyDiagnosticBag);

if (hasDeclarationErrors || hasMethodBodyErrorOrWarningAsError)
if (hasDeclarationErrors || hasMethodBodyError)
{
return false;
}
Expand Down
313 changes: 311 additions & 2 deletions src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -2766,7 +2766,7 @@ public void TestSymbolStartAnalyzer_MultipleAnalyzers_NamedTypeAndMethods()
}

[Fact]
private void TestSymbolStartAnalyzer_MultipleAnalyzers_AllSymbolKinds()
public void TestSymbolStartAnalyzer_MultipleAnalyzers_AllSymbolKinds()
{
testCore("SymbolStartTopLevelRuleId", topLevel: true);
testCore("SymbolStartRuleId", topLevel: false);
Expand Down

Large diffs are not rendered by default.

77 changes: 74 additions & 3 deletions src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions src/Compilers/Core/Portable/CodeAnalysisResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,9 @@
<data name="DiagnosticIdCantBeNullOrWhitespace" xml:space="preserve">
<value>A DiagnosticDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</value>
</data>
<data name="SuppressionIdCantBeNullOrWhitespace" xml:space="preserve">
<value>A SuppressionDescriptor must have an Id that is neither null nor an empty string nor a string that only contains white space.</value>
</data>
<data name="RuleSetHasDuplicateRules" xml:space="preserve">
<value>The rule set file has duplicate rules for '{0}' with differing actions '{1}' and '{2}'.</value>
</data>
Expand All @@ -443,6 +446,15 @@
<data name="UnsupportedDiagnosticReported" xml:space="preserve">
<value>Reported diagnostic with ID '{0}' is not supported by the analyzer.</value>
</data>
<data name="UnsupportedSuppressionReported" xml:space="preserve">
<value>Reported suppression with ID '{0}' is not supported by the suppressor.</value>
</data>
<data name="InvalidDiagnosticSuppressionReported" xml:space="preserve">
<value>Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor.</value>
</data>
<data name="NonReportedDiagnosticCannotBeSuppressed" xml:space="preserve">
<value>Non-reported diagnostic with ID '{0}' cannot be suppressed.</value>
</data>
<data name="InvalidDiagnosticIdReported" xml:space="preserve">
<value>Reported diagnostic has an ID '{0}', which is not a valid identifier.</value>
</data>
Expand All @@ -452,6 +464,9 @@
<data name="SupportedDiagnosticsHasNullDescriptor" xml:space="preserve">
<value>Analyzer '{0}' contains a null descriptor in its 'SupportedDiagnostics'.</value>
</data>
<data name="SupportedSuppressionsHasNullDescriptor" xml:space="preserve">
<value>Analyzer '{0}' contains a null descriptor in its 'SupportedSuppressions'.</value>
</data>
<data name="The_type_0_is_not_understood_by_the_serialization_binder" xml:space="preserve">
<value>The type '{0}' is not understood by the serialization binder.</value>
</data>
Expand Down Expand Up @@ -645,4 +660,13 @@
<data name="WRN_InvalidSeverityInAnalyzerConfig_Title" xml:space="preserve">
<value>Invalid severity in analyzer config file.</value>
</data>
<data name="DiagnosticSuppressorFeatureDisabled" xml:space="preserve">
<value>Feature 'DiagnosticSuppressor' is disabled.</value>
</data>
<data name="SuppressionDiagnosticDescriptorTitle" xml:space="preserve">
<value>Programmatic suppression of an analyzer diagnostic</value>
</data>
<data name="SuppressionDiagnosticDescriptorMessage" xml:space="preserve">
<value>Diagnostic '{0}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{1}' and justification '{2}'</value>
</data>
</root>
53 changes: 46 additions & 7 deletions src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,14 @@ internal bool ReportDiagnostics(IEnumerable<Diagnostic> diagnostics, TextWriter
{
bool hasErrors = false;
foreach (var diag in diagnostics)
{
reportDiagnostic(diag);
}

return hasErrors;

// Local functions
void reportDiagnostic(Diagnostic diag)
{
if (_reportedDiagnostics.Contains(diag))
{
Expand All @@ -496,20 +504,38 @@ internal bool ReportDiagnostics(IEnumerable<Diagnostic> diagnostics, TextWriter
// we previously created.
//this assert isn't valid if we change the design to not bail out after each phase.
//System.Diagnostics.Debug.Assert(diag.Severity != DiagnosticSeverity.Error);
continue;
return;
}
else if (diag.Severity == DiagnosticSeverity.Hidden)
{
// Not reported from the command-line compiler.
continue;
return;
}

// We want to report diagnostics with source suppression in the error log file.
// However, these diagnostics should not be reported on the console output.
errorLoggerOpt?.LogDiagnostic(diag);

// If the diagnostic was suppressed by one or more DiagnosticSuppressor(s), then we report info diagnostics for each suppression
// so that the suppression information is available in the binary logs and verbose build logs.
if (diag.ProgrammaticSuppressionInfo != null)
{
foreach (var (id, justification) in diag.ProgrammaticSuppressionInfo.Suppressions)
{
// Diagnostic '{0}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{1}' and justification '{2}'
var suppressionDiag = Diagnostic.Create(SuppressionDiagnosticDescriptor, diag.Location, diag.Id, id, justification);
mavasani marked this conversation as resolved.
Show resolved Hide resolved
if (_reportedDiagnostics.Add(suppressionDiag))
{
PrintError(suppressionDiag, consoleOutput);
}
}

return;
}

if (diag.IsSuppressed)
{
continue;
return;
}

// Diagnostics that aren't suppressed will be reported to the console output and, if they are errors,
Expand All @@ -523,10 +549,16 @@ internal bool ReportDiagnostics(IEnumerable<Diagnostic> diagnostics, TextWriter

_reportedDiagnostics.Add(diag);
}

return hasErrors;
}

private readonly DiagnosticDescriptor SuppressionDiagnosticDescriptor = new DiagnosticDescriptor(
"SP0001",
CodeAnalysisResources.SuppressionDiagnosticDescriptorTitle,
CodeAnalysisResources.SuppressionDiagnosticDescriptorMessage,
"ProgrammaticSuppression",
DiagnosticSeverity.Info,
isEnabledByDefault: true);

/// <summary>Returns true if there were any errors, false otherwise.</summary>
private bool ReportDiagnostics(DiagnosticBag diagnostics, TextWriter consoleOutput, ErrorLogger errorLoggerOpt)
=> ReportDiagnostics(diagnostics.ToReadOnly(), consoleOutput, errorLoggerOpt);
Expand Down Expand Up @@ -1022,9 +1054,16 @@ private void CompileAndEmit(
// TryComplete, we may miss diagnostics.
var hostDiagnostics = analyzerDriver.GetDiagnosticsAsync(compilation).Result;
diagnostics.AddRange(hostDiagnostics);
if (hostDiagnostics.Any(IsReportedError))

if (!diagnostics.IsEmptyWithoutResolution)
{
success = false;
// Apply diagnostic suppressions for analyzer and/or compiler diagnostics from diagnostic suppressors.
analyzerDriver.ApplyProgrammaticSuppressions(diagnostics, compilation);

if (diagnostics.AsEnumerable().Any(IsReportedError))
mavasani marked this conversation as resolved.
Show resolved Hide resolved
{
success = false;
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/Core/Portable/Compilation/Compilation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1379,7 +1379,7 @@ internal bool FilterAndAppendDiagnostics(DiagnosticBag accumulator, IEnumerable<
continue;
}
else if (filtered.Severity == DiagnosticSeverity.Error &&
!filtered.IsSuppressed)
!filtered.IsSuppressed)
{
hasError = true;
mavasani marked this conversation as resolved.
Show resolved Hide resolved
}
Expand Down
Loading