• M
    Add new analyzer API (DiagnosticSuppressor) to allow programmatic suppression... · f2f7a069
    Manish Vasani 提交于
    Add new analyzer API (DiagnosticSuppressor) to allow programmatic suppression of analyzer and/or compiler non-error diagnostics
    
    Fixes #20242 and #30172
    
    Detailed design proposal [here](https://gist.github.com/mavasani/fcac17a9581b5c54cef8a689eeec954a).
    
    Added public APIs with documentation comments:
    ```cs
    namespace Microsoft.CodeAnalysis.Diagnostics
    {
        /// <summary>
        /// The base type for diagnostic suppressors that can programmatically suppress analyzer and/or compiler non-error diagnostics.
        /// </summary>
        public abstract class DiagnosticSuppressor : DiagnosticAnalyzer
        {
            // Disallow suppressors from reporting diagnostics or registering analysis actions.
            public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray<DiagnosticDescriptor>.Empty;
    
            public sealed override void Initialize(AnalysisContext context) { }
    
            /// <summary>
            /// Returns a set of descriptors for the suppressions that this suppressor is capable of producing.
            /// </summary>
            public abstract ImmutableArray<SuppressionDescriptor> SupportedSuppressions { get; }
    
            /// <summary>
            /// Suppress analyzer and/or compiler non-error diagnostics reported for the compilation.
            /// This may be a subset of the full set of reported diagnostics, as an optimization for
            /// supporting incremental and partial analysis scenarios.
            /// A diagnostic is considered suppressible by a DiagnosticSuppressor if *all* of the following conditions are met:
            ///     1. Diagnostic is not already suppressed in source via pragma/suppress message attribute.
            ///     2. Diagnostic's <see cref="Diagnostic.DefaultSeverity"/> is not <see cref="DiagnosticSeverity.Error"/>.
            ///     3. Diagnostic is not tagged with <see cref="WellKnownDiagnosticTags.NotConfigurable"/> custom tag.
            /// </summary>
            public abstract void ReportSuppressions(SuppressionAnalysisContext context);
        }
    
        /// <summary>
        /// Provides a description about a programmatic suppression of a <see cref="Diagnostic"/> by a <see cref="DiagnosticSuppressor"/>.
        /// </summary>
        public sealed class SuppressionDescriptor : IEquatable<SuppressionDescriptor>
        {
            /// <summary>
            /// An unique identifier for the suppression.
            /// </summary>
            public string Id { get; }
    
            /// <summary>
            /// Identifier of the suppressed diagnostic, i.e. <see cref="Diagnostic.Id"/>.
            /// </summary>
            public string SuppressedDiagnosticId { get; }
    
            /// <summary>
            /// A localizable description about the suppression.
            /// </summary>
            public LocalizableString Description { get; }
        }
    
        /// <summary>
        /// Context for suppressing analyzer and/or compiler non-error diagnostics reported for the compilation.
        /// </summary>
        public struct SuppressionAnalysisContext
        {
            /// <summary>
            /// Suppressible analyzer and/or compiler non-error diagnostics reported for the compilation.
            /// This may be a subset of the full set of reported diagnostics, as an optimization for
            /// supporting incremental and partial analysis scenarios.
            /// A diagnostic is considered suppressible by a DiagnosticSuppressor if *all* of the following conditions are met:
            ///     1. Diagnostic is not already suppressed in source via pragma/suppress message attribute.
            ///     2. Diagnostic's <see cref="Diagnostic.DefaultSeverity"/> is not <see cref="DiagnosticSeverity.Error"/>.
            ///     3. Diagnostic is not tagged with <see cref="WellKnownDiagnosticTags.NotConfigurable"/> custom tag.
            /// </summary>
            public ImmutableArray<Diagnostic> ReportedDiagnostics { get; }
    
            /// <summary>
            /// Report a <see cref="Suppression"/> for a reported diagnostic.
            /// </summary>
            public void ReportSuppression(Suppression suppression);
    
            /// <summary>
            /// Gets a <see cref="SemanticModel"/> for the given <see cref="SyntaxTree"/>, which is shared across all analyzers.
            /// </summary>
            public SemanticModel GetSemanticModel(SyntaxTree syntaxTree);
    
            /// <summary>
            /// <see cref="CodeAnalysis.Compilation"/> for the context.
            /// </summary>
            public Compilation Compilation { get; }
    
            /// <summary>
            /// Options specified for the analysis.
            /// </summary>
            public AnalyzerOptions Options { get; }
    
            /// <summary>
            /// Token to check for requested cancellation of the analysis.
            /// </summary>
            public CancellationToken CancellationToken { get; }
        }
    
        /// <summary>
        /// Programmatic suppression of a <see cref="Diagnostic"/> by a <see cref="DiagnosticSuppressor"/>.
        /// </summary>
        public struct Suppression
        {
            /// <summary>
            /// Creates a suppression of a <see cref="Diagnostic"/> with the given <see cref="SuppressionDescriptor"/>.
            /// </summary>
            /// <param name="descriptor">
            /// Descriptor for the suppression, which must be from <see cref="DiagnosticSuppressor.SupportedSuppressions"/>
            /// for the <see cref="DiagnosticSuppressor"/> creating this suppression.
            /// </param>
            /// <param name="suppressedDiagnostic">
            /// <see cref="Diagnostic"/> to be suppressed, which must be from <see cref="SuppressionAnalysisContext.ReportedDiagnostics"/>
            /// for the suppression context in which this suppression is being created.</param>
            public static Suppression Create(SuppressionDescriptor descriptor, Diagnostic suppressedDiagnostic);
    
            /// <summary>
            /// Descriptor for this suppression.
            /// </summary>
            public SuppressionDescriptor Descriptor { get; }
    
            /// <summary>
            /// Diagnostic suppressed by this suppression.
            /// </summary>
            public Diagnostic SuppressedDiagnostic { get; }
        }
    }
    ```
    
    For batch compilation, suppressors always run after all the analyzer diagnostics have been computed.
    For IDE partial/incremental analysis scenario, we may run the suppressors with partial diagnostics.
    Suppressed diagnostics from diagnostic suppressors are equivalent to source suppressed diagnostics: they show up in the error list with "Suppression State" column as "Suppressed" and are also output to errorlog as suppressed diagnostics.
    f2f7a069
DiagnosticSuppressor.cs 1.9 KB