Ignore suppressed diagnostics unless produced by a DiagnosticSuppressor

This commit is contained in:
Sam Harwell 2023-07-07 09:46:09 -05:00
Родитель 43040ec5db
Коммит 80b8aff23a
5 изменённых файлов: 147 добавлений и 3 удалений

Просмотреть файл

@ -1157,7 +1157,8 @@ namespace Microsoft.CodeAnalysis.Testing
}
diagnostics.AddRange(additionalDiagnostics);
var results = SortDistinctDiagnostics(diagnostics);
var filteredDiagnostics = FilterDiagnostics(diagnostics.ToImmutable());
var results = SortDistinctDiagnostics(filteredDiagnostics);
return results;
static async Task<ImmutableArray<Diagnostic>> GetCompilerDiagnosticsAsync(AnalyzerTest<TVerifier> self, Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerOptions analyzerOptions, CancellationToken cancellationToken)
@ -1677,13 +1678,28 @@ namespace Microsoft.CodeAnalysis.Testing
protected abstract ParseOptions CreateParseOptions();
/// <summary>
/// Filter <see cref="Diagnostic"/>s to only include items of interest to testing. By default, this includes all
/// unsuppressed diagnostics, and all diagnostics suppressed by a
/// <see cref="T:Microsoft.CodeAnalysis.Diagnostics.DiagnosticSuppressor"/>.
/// </summary>
/// <param name="diagnostics">A collection of <see cref="Diagnostic"/>s to be filtered.</param>
/// <returns>A collection containing the input <paramref name="diagnostics"/>, filtered to only include
/// diagnostics relevant for testing.</returns>
protected virtual ImmutableArray<(Project project, Diagnostic diagnostic)> FilterDiagnostics(ImmutableArray<(Project project, Diagnostic diagnostic)> diagnostics)
{
return diagnostics
.Where(d => !d.diagnostic.IsSuppressed() || d.diagnostic.ProgrammaticSuppressionInfo() != null)
.ToImmutableArray();
}
/// <summary>
/// Sort <see cref="Diagnostic"/>s by location in source document.
/// </summary>
/// <param name="diagnostics">A collection of <see cref="Diagnostic"/>s to be sorted.</param>
/// <returns>A collection containing the input <paramref name="diagnostics"/>, sorted by
/// <see cref="Diagnostic.Location"/> and <see cref="Diagnostic.Id"/>.</returns>
protected virtual ImmutableArray<(Project project, Diagnostic diagnostic)> SortDistinctDiagnostics(IEnumerable<(Project project, Diagnostic diagnostic)> diagnostics)
protected virtual ImmutableArray<(Project project, Diagnostic diagnostic)> SortDistinctDiagnostics(ImmutableArray<(Project project, Diagnostic diagnostic)> diagnostics)
{
return diagnostics
.OrderBy(d => d.diagnostic.Location.GetLineSpan().Path, StringComparer.Ordinal)

Просмотреть файл

@ -22,10 +22,19 @@ namespace Microsoft.CodeAnalysis.Testing.Extensions
nameof(IsSuppressed),
defaultValue: false);
private static readonly Func<Diagnostic, object?> s_grogrammaticSuppressionInfo =
LightupHelpers.CreatePropertyAccessor<Diagnostic, object?>(
typeof(Diagnostic),
nameof(ProgrammaticSuppressionInfo),
defaultValue: null);
public static IReadOnlyList<object?> Arguments(this Diagnostic diagnostic)
=> s_arguments(diagnostic);
public static bool IsSuppressed(this Diagnostic diagnostic)
=> s_isSuppressed(diagnostic);
public static ProgrammaticSuppressionInfoWrapper? ProgrammaticSuppressionInfo(this Diagnostic diagnostic)
=> s_grogrammaticSuppressionInfo(diagnostic) is { } info ? ProgrammaticSuppressionInfoWrapper.FromInstance(info) : null;
}
}

Просмотреть файл

@ -0,0 +1,61 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Immutable;
using System.Reflection;
namespace Microsoft.CodeAnalysis.Testing.Lightup
{
internal readonly struct ProgrammaticSuppressionInfoWrapper
{
internal const string WrappedTypeName = "Microsoft.CodeAnalysis.Diagnostics.ProgrammaticSuppressionInfo";
internal static readonly Type? WrappedType = typeof(Diagnostic).GetTypeInfo().Assembly.GetType(WrappedTypeName);
private static readonly Func<object, ImmutableHashSet<(string id, LocalizableString justification)>> s_suppressions;
private readonly object _instance;
static ProgrammaticSuppressionInfoWrapper()
{
s_suppressions = LightupHelpers.CreatePropertyAccessor<object, ImmutableHashSet<(string id, LocalizableString justification)>>(WrappedType, nameof(Suppressions), ImmutableHashSet<(string id, LocalizableString justification)>.Empty);
}
private ProgrammaticSuppressionInfoWrapper(object instance)
{
_instance = instance;
}
public ImmutableHashSet<(string id, LocalizableString justification)> Suppressions => s_suppressions(_instance);
public static ProgrammaticSuppressionInfoWrapper FromInstance(object instance)
{
if (instance == null)
{
return default;
}
if (!IsInstance(instance))
{
throw new InvalidCastException($"Cannot cast '{instance.GetType().FullName}' to '{WrappedTypeName}'");
}
return new ProgrammaticSuppressionInfoWrapper(instance);
}
public static bool IsInstance(object value)
{
if (value is null)
{
return false;
}
if (WrappedType is null)
{
return false;
}
return WrappedType.IsAssignableFrom(value.GetType());
}
}
}

Просмотреть файл

@ -350,13 +350,14 @@ virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest<TVerifier>.CreateWorkspaceIm
virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest<TVerifier>.DefaultFilePath.get -> string
virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest<TVerifier>.DefaultFilePathPrefix.get -> string
virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest<TVerifier>.DefaultTestProjectName.get -> string
virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest<TVerifier>.FilterDiagnostics(System.Collections.Immutable.ImmutableArray<(Microsoft.CodeAnalysis.Project project, Microsoft.CodeAnalysis.Diagnostic diagnostic)> diagnostics) -> System.Collections.Immutable.ImmutableArray<(Microsoft.CodeAnalysis.Project project, Microsoft.CodeAnalysis.Diagnostic diagnostic)>
virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest<TVerifier>.GetAnalyzerOptions(Microsoft.CodeAnalysis.Project project) -> Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions
virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest<TVerifier>.GetDefaultDiagnostic(Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer[] analyzers) -> Microsoft.CodeAnalysis.DiagnosticDescriptor
virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest<TVerifier>.GetProjectCompilationAsync(Microsoft.CodeAnalysis.Project project, Microsoft.CodeAnalysis.Testing.IVerifier verifier, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<(Microsoft.CodeAnalysis.Compilation compilation, System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Diagnostic> generatorDiagnostics)>
virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest<TVerifier>.GetSourceGenerators() -> System.Collections.Generic.IEnumerable<System.Type>
virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest<TVerifier>.IsCompilerDiagnosticIncluded(Microsoft.CodeAnalysis.Diagnostic diagnostic, Microsoft.CodeAnalysis.Testing.CompilerDiagnostics compilerDiagnostics) -> bool
virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest<TVerifier>.RunImplAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest<TVerifier>.SortDistinctDiagnostics(System.Collections.Generic.IEnumerable<(Microsoft.CodeAnalysis.Project project, Microsoft.CodeAnalysis.Diagnostic diagnostic)> diagnostics) -> System.Collections.Immutable.ImmutableArray<(Microsoft.CodeAnalysis.Project project, Microsoft.CodeAnalysis.Diagnostic diagnostic)>
virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest<TVerifier>.SortDistinctDiagnostics(System.Collections.Immutable.ImmutableArray<(Microsoft.CodeAnalysis.Project project, Microsoft.CodeAnalysis.Diagnostic diagnostic)> diagnostics) -> System.Collections.Immutable.ImmutableArray<(Microsoft.CodeAnalysis.Project project, Microsoft.CodeAnalysis.Diagnostic diagnostic)>
virtual Microsoft.CodeAnalysis.Testing.CodeActionTest<TVerifier>.FilterCodeActions(System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.CodeActions.CodeAction> actions) -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.CodeActions.CodeAction>
virtual Microsoft.CodeAnalysis.Testing.DefaultVerifier.CreateMessage(string message) -> string
virtual Microsoft.CodeAnalysis.Testing.DefaultVerifier.Empty<T>(string collectionName, System.Collections.Generic.IEnumerable<T> collection) -> void

Просмотреть файл

@ -220,6 +220,54 @@ End Class
}.RunAsync();
}
[Fact]
public async Task TestCSharpTestIgnoresPragmaSuppressedWarnings()
{
var testCode = @"class TestClass {|#0:{|} }";
// A diagnostic is produced in the original code
await new CSharpAnalyzerTest<HighlightBraceSpanAnalyzer>
{
TestCode = testCode,
ExpectedDiagnostics =
{
new DiagnosticResult("Brace", DiagnosticSeverity.Warning).WithLocation(0),
},
}.RunAsync();
// The same diagnostic is ignored when suppressed using '#pragma warning disable'
await new CSharpAnalyzerTest<HighlightBraceSpanAnalyzer>
{
TestCode = $@"#pragma warning disable Brace
{testCode}",
}.RunAsync();
}
[Fact]
public async Task TestVisualBasicTestIgnoresPragmaSuppressedWarnings()
{
var testCode = @"Class TestClass
Dim Field As Integer() = {|#0:{|} }
End Class";
// A diagnostic is produced in the original code
await new VisualBasicAnalyzerTest<HighlightBraceSpanAnalyzer>
{
TestCode = testCode,
ExpectedDiagnostics =
{
new DiagnosticResult("Brace", DiagnosticSeverity.Warning).WithLocation(0),
},
}.RunAsync();
// The same diagnostic is ignored when suppressed using '#Disable Warning'
await new VisualBasicAnalyzerTest<HighlightBraceSpanAnalyzer>
{
TestCode = $@"#Disable Warning Brace
{testCode}",
}.RunAsync();
}
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
private class ReplaceThisWithBaseAnalyzer : DiagnosticAnalyzer
{
@ -314,6 +362,15 @@ End Class
}
}
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
private class HighlightBraceSpanAnalyzer : AbstractHighlightBracesAnalyzer
{
protected override Diagnostic CreateDiagnostic(SyntaxToken token)
{
return Diagnostic.Create(Descriptor, token.GetLocation());
}
}
private class CSharpReplaceThisWithBaseTest : CSharpAnalyzerTest<EmptyDiagnosticAnalyzer>
{
private readonly GeneratedCodeAnalysisFlags? _generatedCodeAnalysisFlags;