Родитель
64fdcf16db
Коммит
276abf1cc5
|
@ -1005,10 +1005,15 @@ namespace Microsoft.CodeAnalysis.Testing
|
|||
}
|
||||
}
|
||||
|
||||
private static bool IsCompilerDiagnosticId(string id)
|
||||
{
|
||||
return id.StartsWith("CS", StringComparison.Ordinal)
|
||||
|| id.StartsWith("BC", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private static bool IsSubjectToExclusion(DiagnosticResult result, ImmutableArray<DiagnosticAnalyzer> analyzers, (string filename, SourceText content)[] sources)
|
||||
{
|
||||
if (result.Id.StartsWith("CS", StringComparison.Ordinal)
|
||||
|| result.Id.StartsWith("BC", StringComparison.Ordinal))
|
||||
if (IsCompilerDiagnosticId(result.Id))
|
||||
{
|
||||
// This is a compiler diagnostic
|
||||
return false;
|
||||
|
@ -1125,12 +1130,13 @@ namespace Microsoft.CodeAnalysis.Testing
|
|||
foreach (var project in solution.Projects)
|
||||
{
|
||||
var (compilation, generatorDiagnostics) = await GetProjectCompilationAsync(project, verifier, cancellationToken).ConfigureAwait(false);
|
||||
var compilationWithAnalyzers = CreateCompilationWithAnalyzers(compilation, analyzers, GetAnalyzerOptions(project), cancellationToken);
|
||||
var analyzerOptions = GetAnalyzerOptions(project);
|
||||
var compilationWithAnalyzers = CreateCompilationWithAnalyzers(compilation, analyzers, analyzerOptions, cancellationToken);
|
||||
|
||||
ImmutableArray<Diagnostic> allDiagnostics;
|
||||
if (AnalysisResultWrapper.WrappedType is not null)
|
||||
{
|
||||
var compilationDiagnostics = compilation.GetDiagnostics(cancellationToken);
|
||||
var compilerReportedDiagnostics = await GetCompilerDiagnosticsAsync(this, compilation, analyzers, analyzerOptions, cancellationToken).ConfigureAwait(false);
|
||||
var analysisResult = await compilationWithAnalyzers.GetAnalysisResultAsync(cancellationToken).ConfigureAwait(false);
|
||||
foreach (var (analyzer, analyzerNonLocalDiagnostics) in analysisResult.CompilationDiagnostics)
|
||||
{
|
||||
|
@ -1140,7 +1146,7 @@ namespace Microsoft.CodeAnalysis.Testing
|
|||
}
|
||||
}
|
||||
|
||||
allDiagnostics = compilationDiagnostics.AddRange(analysisResult.GetAllDiagnostics());
|
||||
allDiagnostics = compilerReportedDiagnostics.AddRange(analysisResult.GetAllDiagnostics());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1154,6 +1160,39 @@ namespace Microsoft.CodeAnalysis.Testing
|
|||
diagnostics.AddRange(additionalDiagnostics);
|
||||
var results = SortDistinctDiagnostics(diagnostics);
|
||||
return results;
|
||||
|
||||
static async Task<ImmutableArray<Diagnostic>> GetCompilerDiagnosticsAsync(AnalyzerTest<TVerifier> self, Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerOptions analyzerOptions, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!analyzers.Any(static analyzer => IsCompilerDiagnosticSuppressor(analyzer)))
|
||||
{
|
||||
return compilation.GetDiagnostics(cancellationToken);
|
||||
}
|
||||
|
||||
// Need to get the compiler diagnostics through a new CompilationWithAnalyzers instance to ensure
|
||||
// suppressions are applied.
|
||||
var compilerSuppressors = analyzers.Where(static analyzer => IsCompilerDiagnosticSuppressor(analyzer)).ToImmutableArray();
|
||||
var compilationWithAnalyzers = self.CreateCompilationWithAnalyzers(compilation, compilerSuppressors, analyzerOptions, cancellationToken);
|
||||
return await compilationWithAnalyzers.GetAllDiagnosticsAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
static bool IsCompilerDiagnosticSuppressor(DiagnosticAnalyzer analyzer)
|
||||
{
|
||||
if (!DiagnosticSuppressorWrapper.IsInstance(analyzer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var wrapper = DiagnosticSuppressorWrapper.FromInstance(analyzer);
|
||||
foreach (var descriptor in wrapper.SupportedSuppressions)
|
||||
{
|
||||
if (IsCompilerDiagnosticId(descriptor.SuppressedDiagnosticId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private protected static bool IsNonLocalDiagnostic(Diagnostic diagnostic)
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
// 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;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Testing.Lightup
|
||||
{
|
||||
internal readonly struct DiagnosticSuppressorWrapper
|
||||
{
|
||||
internal const string WrappedTypeName = "Microsoft.CodeAnalysis.Diagnostics.DiagnosticSuppressor";
|
||||
internal static readonly Type? WrappedType = typeof(Diagnostic).GetTypeInfo().Assembly.GetType(WrappedTypeName);
|
||||
private static readonly Func<DiagnosticAnalyzer, IEnumerable> s_supportedSuppressions;
|
||||
|
||||
private readonly DiagnosticAnalyzer _instance;
|
||||
|
||||
static DiagnosticSuppressorWrapper()
|
||||
{
|
||||
s_supportedSuppressions = LightupHelpers.CreatePropertyAccessor<DiagnosticAnalyzer, IEnumerable>(WrappedType, nameof(SupportedSuppressions), Enumerable.Empty<object>());
|
||||
}
|
||||
|
||||
private DiagnosticSuppressorWrapper(DiagnosticAnalyzer instance)
|
||||
{
|
||||
_instance = instance;
|
||||
}
|
||||
|
||||
public ImmutableArray<SuppressionDescriptorWrapper> SupportedSuppressions
|
||||
{
|
||||
get
|
||||
{
|
||||
var suppressions = s_supportedSuppressions(_instance).Cast<object>();
|
||||
return ImmutableArray.CreateRange(suppressions.Select(SuppressionDescriptorWrapper.FromInstance));
|
||||
}
|
||||
}
|
||||
|
||||
public static DiagnosticSuppressorWrapper FromInstance(DiagnosticAnalyzer instance)
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
if (!IsInstance(instance))
|
||||
{
|
||||
throw new InvalidCastException($"Cannot cast '{instance.GetType().FullName}' to '{WrappedTypeName}'");
|
||||
}
|
||||
|
||||
return new DiagnosticSuppressorWrapper(instance);
|
||||
}
|
||||
|
||||
public static bool IsInstance(object value)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (WrappedType is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return WrappedType.IsAssignableFrom(value.GetType());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -61,7 +61,7 @@ namespace Microsoft.CodeAnalysis.Testing.Lightup
|
|||
|
||||
Expression<Func<T, TResult>> expression =
|
||||
Expression.Lambda<Func<T, TResult>>(
|
||||
Expression.Call(instance, property.GetMethod),
|
||||
Expression.Convert(Expression.Call(instance, property.GetMethod), typeof(TResult)),
|
||||
parameter);
|
||||
return expression.Compile();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
// 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.Reflection;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Testing.Lightup
|
||||
{
|
||||
internal readonly struct SuppressionDescriptorWrapper
|
||||
{
|
||||
internal const string WrappedTypeName = "Microsoft.CodeAnalysis.SuppressionDescriptor";
|
||||
internal static readonly Type? WrappedType = typeof(Diagnostic).GetTypeInfo().Assembly.GetType(WrappedTypeName);
|
||||
private static readonly Func<object, string> s_id;
|
||||
private static readonly Func<object, string> s_suppressedDiagnosticId;
|
||||
private static readonly Func<object, LocalizableString> s_justification;
|
||||
|
||||
private readonly object _instance;
|
||||
|
||||
static SuppressionDescriptorWrapper()
|
||||
{
|
||||
s_id = LightupHelpers.CreatePropertyAccessor<object, string>(WrappedType, nameof(Id), string.Empty);
|
||||
s_suppressedDiagnosticId = LightupHelpers.CreatePropertyAccessor<object, string>(WrappedType, nameof(SuppressedDiagnosticId), string.Empty);
|
||||
s_justification = LightupHelpers.CreatePropertyAccessor<object, LocalizableString>(WrappedType, nameof(Justification), string.Empty);
|
||||
}
|
||||
|
||||
private SuppressionDescriptorWrapper(object instance)
|
||||
{
|
||||
_instance = instance;
|
||||
}
|
||||
|
||||
public string Id => s_id(_instance);
|
||||
|
||||
public string SuppressedDiagnosticId => s_suppressedDiagnosticId(_instance);
|
||||
|
||||
public LocalizableString Justification => s_justification(_instance);
|
||||
|
||||
public static SuppressionDescriptorWrapper FromInstance(object instance)
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
if (!IsInstance(instance))
|
||||
{
|
||||
throw new InvalidCastException($"Cannot cast '{instance.GetType().FullName}' to '{WrappedTypeName}'");
|
||||
}
|
||||
|
||||
return new SuppressionDescriptorWrapper(instance);
|
||||
}
|
||||
|
||||
public static bool IsInstance(object value)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (WrappedType is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return WrappedType.IsAssignableFrom(value.GetType());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,6 +12,9 @@ using Microsoft.CodeAnalysis.Testing.TestAnalyzers;
|
|||
using Xunit;
|
||||
using CSharpAnalyzerTest = Microsoft.CodeAnalysis.Testing.TestAnalyzers.CSharpAnalyzerTest<
|
||||
Microsoft.CodeAnalysis.Testing.TestAnalyzers.HighlightBracesAnalyzer>;
|
||||
using CSharpCompilerTest = Microsoft.CodeAnalysis.Testing.TestAnalyzers.CSharpSuppressorTest<
|
||||
Microsoft.CodeAnalysis.Testing.EmptyDiagnosticAnalyzer,
|
||||
Microsoft.CodeAnalysis.Testing.DiagnosticSuppressorTests.NonNullableFieldSuppressor>;
|
||||
using CSharpTest = Microsoft.CodeAnalysis.Testing.TestAnalyzers.CSharpSuppressorTest<
|
||||
Microsoft.CodeAnalysis.Testing.TestAnalyzers.HighlightBracesAnalyzer,
|
||||
Microsoft.CodeAnalysis.Testing.DiagnosticSuppressorTests.HighlightBracesSuppressor>;
|
||||
|
@ -54,6 +57,29 @@ namespace Microsoft.CodeAnalysis.Testing
|
|||
}.RunAsync();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[WorkItem(1090, "https://github.com/dotnet/roslyn-sdk/issues/1090")]
|
||||
public async Task TestSuppressionOfCompilerDiagnostic()
|
||||
{
|
||||
await new CSharpCompilerTest
|
||||
{
|
||||
CompilerDiagnostics = CompilerDiagnostics.Warnings,
|
||||
TestState =
|
||||
{
|
||||
Sources =
|
||||
{
|
||||
@"#nullable enable
|
||||
class Sample { string {|#0:_value|}; }",
|
||||
},
|
||||
ExpectedDiagnostics =
|
||||
{
|
||||
DiagnosticResult.CompilerWarning("CS8618").WithLocation(0).WithIsSuppressed(true),
|
||||
DiagnosticResult.CompilerWarning("CS0169").WithLocation(0),
|
||||
},
|
||||
},
|
||||
}.RunAsync();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TestUnexpectedSuppressionPresent()
|
||||
{
|
||||
|
@ -134,6 +160,23 @@ namespace Microsoft.CodeAnalysis.Testing
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
internal class NonNullableFieldSuppressor : DiagnosticSuppressor
|
||||
{
|
||||
internal static readonly SuppressionDescriptor Descriptor =
|
||||
new SuppressionDescriptor("FieldIsAssigned", "CS8618", "justification");
|
||||
|
||||
public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions => ImmutableArray.Create(Descriptor);
|
||||
|
||||
public override void ReportSuppressions(SuppressionAnalysisContext context)
|
||||
{
|
||||
foreach (var diagnostic in context.ReportedDiagnostics)
|
||||
{
|
||||
context.ReportSuppression(Suppression.Create(Descriptor, diagnostic));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче