Skip to content

Commit

Permalink
Merge pull request #36067 from mavasani/DiagnosticSuppressorForCompil…
Browse files Browse the repository at this point in the history
…erAndAnalyzerDiagnostics

Add new analyzer API (DiagnosticSuppressor) to allow programmatic sup…
  • Loading branch information
mavasani authored Jun 19, 2019
2 parents cc909f1 + a47a69a commit f7ce215
Show file tree
Hide file tree
Showing 50 changed files with 2,824 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2693,9 +2693,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
312 changes: 308 additions & 4 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.

68 changes: 65 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.

21 changes: 21 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,10 @@
<data name="WRN_InvalidSeverityInAnalyzerConfig_Title" xml:space="preserve">
<value>Invalid severity in analyzer config file.</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}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'</value>
</data>
</root>
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis
{
internal abstract partial class CommonCompiler
{
/// <summary>
/// Special informational diagnostic for each programmatic <see cref="Diagnostics.Suppression"/> reported by a <see cref="Diagnostics.DiagnosticSuppressor"/>.
/// </summary>
private sealed class SuppressionDiagnostic : Diagnostic
{
private static readonly DiagnosticDescriptor s_suppressionDiagnosticDescriptor = new DiagnosticDescriptor(
"SP0001",
CodeAnalysisResources.SuppressionDiagnosticDescriptorTitle,
CodeAnalysisResources.SuppressionDiagnosticDescriptorMessage,
"ProgrammaticSuppression",
DiagnosticSeverity.Info,
isEnabledByDefault: true);

private readonly Diagnostic _originalDiagnostic;
private readonly string _suppressionId;
private readonly LocalizableString _suppressionJustification;

public SuppressionDiagnostic(
Diagnostic originalDiagnostic,
string suppressionId,
LocalizableString suppressionJustification)
{
Debug.Assert(originalDiagnostic != null);
Debug.Assert(originalDiagnostic.ProgrammaticSuppressionInfo != null);
Debug.Assert(!string.IsNullOrEmpty(suppressionId));
Debug.Assert(suppressionJustification != null);

_originalDiagnostic = originalDiagnostic;
_suppressionId = suppressionId;
_suppressionJustification = suppressionJustification;
}

public override DiagnosticDescriptor Descriptor => s_suppressionDiagnosticDescriptor;

public override string Id => Descriptor.Id;

public override string GetMessage(IFormatProvider formatProvider = null)
{
// Diagnostic '{0}: {1}' was programmatically suppressed by a DiagnosticSuppressor with suppresion ID '{2}' and justification '{3}'
var localizableMessageFormat = s_suppressionDiagnosticDescriptor.MessageFormat.ToString(formatProvider);
return string.Format(formatProvider,
localizableMessageFormat,
_originalDiagnostic.Id,
_originalDiagnostic.GetMessage(formatProvider),
_suppressionId,
_suppressionJustification.ToString(formatProvider));
}

public override DiagnosticSeverity Severity => DiagnosticSeverity.Info;
public override bool IsSuppressed => false;
public override int WarningLevel => GetDefaultWarningLevel(DiagnosticSeverity.Info);
public override Location Location => _originalDiagnostic.Location;
public override IReadOnlyList<Location> AdditionalLocations => _originalDiagnostic.AdditionalLocations;
public override ImmutableDictionary<string, string> Properties => ImmutableDictionary<string, string>.Empty;

public override bool Equals(Diagnostic obj)
{
var other = obj as SuppressionDiagnostic;
if (other == null)
{
return false;
}

if (ReferenceEquals(this, other))
{
return true;
}

return Equals(_originalDiagnostic, other._originalDiagnostic) &&
Equals(_suppressionId, other._suppressionId) &&
Equals(_suppressionJustification, other._suppressionJustification);
}

public override bool Equals(object obj)
{
return this.Equals(obj as Diagnostic);
}

public override int GetHashCode()
{
return Hash.Combine(_originalDiagnostic.GetHashCode(),
Hash.Combine(_suppressionId.GetHashCode(), _suppressionJustification.GetHashCode()));
}

internal override Diagnostic WithLocation(Location location)
{
throw new NotSupportedException();
}

internal override Diagnostic WithSeverity(DiagnosticSeverity severity)
{
throw new NotSupportedException();
}

internal override Diagnostic WithIsSuppressed(bool isSuppressed)
{
throw new NotSupportedException();
}
}
}
}
45 changes: 38 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)
{
var suppressionDiag = new SuppressionDiagnostic(diag, id, justification);
if (_reportedDiagnostics.Add(suppressionDiag))
{
PrintError(suppressionDiag, consoleOutput);
}
}

_reportedDiagnostics.Add(diag);
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,8 +549,6 @@ internal bool ReportDiagnostics(IEnumerable<Diagnostic> diagnostics, TextWriter

_reportedDiagnostics.Add(diag);
}

return hasErrors;
}

/// <summary>Returns true if there were any errors, false otherwise.</summary>
Expand Down Expand Up @@ -1022,9 +1046,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 (HasUnsuppressedErrors(diagnostics))
{
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;
}
Expand Down
Loading

0 comments on commit f7ce215

Please sign in to comment.