Ignore suppressed diagnostics unless produced by a DiagnosticSuppressor
This commit is contained in:
Родитель
43040ec5db
Коммит
80b8aff23a
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче