|
// 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.
#nullable disable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
using static Microsoft.CodeAnalysis.CommonDiagnosticAnalyzers;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
public partial class DiagnosticSuppressorTests : CompilingTestBase
{
private static CSharpCompilation VerifyAnalyzerDiagnostics(
CSharpCompilation c,
DiagnosticAnalyzer[] analyzers,
params DiagnosticDescription[] expected)
=> c.VerifyAnalyzerDiagnostics(analyzers, expected: expected);
private static CSharpCompilation VerifySuppressedDiagnostics(
CSharpCompilation c,
DiagnosticAnalyzer[] analyzers,
params DiagnosticDescription[] expected)
=> c.VerifySuppressedDiagnostics(analyzers, expected: expected);
private static CSharpCompilation VerifySuppressedAndFilteredDiagnostics(
CSharpCompilation c,
DiagnosticAnalyzer[] analyzers)
=> c.VerifySuppressedAndFilteredDiagnostics(analyzers);
[Fact, WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
public void TestSuppression_CompilerSyntaxWarning()
{
// NOTE: Empty switch block warning is reported by the C# language parser
string source = @"
class C
{
void M(int i)
{
switch (i)
{
}
}
}";
var compilation = CreateCompilation(source);
compilation.VerifyDiagnostics(
// (7,9): warning CS1522: Empty switch block
// {
Diagnostic(ErrorCode.WRN_EmptySwitch, "{", isSuppressed: false).WithLocation(7, 9));
// Verify compiler syntax warning can be suppressed with a suppressor.
var analyzers = new DiagnosticAnalyzer[] { new DiagnosticSuppressorForId("CS1522") };
VerifySuppressedDiagnostics(compilation, analyzers,
// (7,9): warning CS1522: Empty switch block
// {
Diagnostic("CS1522", "{", isSuppressed: true).WithLocation(7, 9));
VerifySuppressedAndFilteredDiagnostics(compilation, analyzers);
}
[WorkItem(62540, "https://github.com/dotnet/roslyn/issues/62540")]
[Fact]
public void TestSuppression_CompilerSyntaxBindingError_AndSuppressibleWarning()
{
const string SourceCode = @"
public class MyClass
{
void MyPrivateMethod(int i)
{
// warning CS1522: Empty switch block
switch (i)
{
}
}
}
public class YourClass
{
void YourPrivateMethod()
{
// Cannot access private method
new MyClass().MyPrivateMethod();
}
}";
var compilation = CreateCompilation(SourceCode);
compilation.VerifyDiagnostics(
// (8,25): warning CS1522: Empty switch block
// {
Diagnostic(ErrorCode.WRN_EmptySwitch, "{", isSuppressed: false).WithLocation(line: 8, column: 25),
// (17,39): error CS0122: 'MyClass.MyPrivateMethod(int)' is inaccessible due to its protection level
// new MyClass().MyPrivateMethod();
Diagnostic(ErrorCode.ERR_BadAccess, "MyPrivateMethod").WithArguments("MyClass.MyPrivateMethod(int)").WithLocation(17, 39));
// Verify that suppression takes place even there are declaration errors
var analyzers = new DiagnosticAnalyzer[] { new DiagnosticSuppressorForId("CS1522") };
VerifySuppressedDiagnostics(compilation, analyzers,
// (8,25): warning CS1522: Empty switch block
// {
Diagnostic("CS1522", "{", isSuppressed: true).WithLocation(line: 8, column: 25));
VerifySuppressedAndFilteredDiagnostics(compilation, analyzers);
}
[WorkItem(62540, "https://github.com/dotnet/roslyn/issues/62540")]
[Fact]
public void TestSuppression_CompilerSyntaxDeclarationError_AndSuppressibleWarning()
{
const string SourceCode = @"
// warning CS1522: Empty switch block
class C
{
void M(int i)
{
switch (i)
{
}
}
}
public abstract class MyAbstractClass
{
// error CS0180: Methods cannot be both extern and abstract -- this is a declaration error
public extern abstract void MyFaultyMethod()
{
}
}";
var compilation = CreateCompilation(SourceCode);
compilation.VerifyDiagnostics(
// (8,25): warning CS1522: Empty switch block
// {
Diagnostic(ErrorCode.WRN_EmptySwitch, "{", isSuppressed: false).WithLocation(line: 8, column: 25),
// (16,49): error CS0180: 'MyAbstractClass.MyFaultyMethod()' cannot be both extern and abstract
// public extern abstract void MyFaultyMethod()
Diagnostic(ErrorCode.ERR_AbstractAndExtern, "MyFaultyMethod").WithArguments("MyAbstractClass.MyFaultyMethod()").WithLocation(16, 49));
// Verify that suppression takes place even there are declaration errors
var analyzers = new DiagnosticAnalyzer[] { new DiagnosticSuppressorForId("CS1522") };
VerifySuppressedDiagnostics(compilation, analyzers,
// (8,25): warning CS1522: Empty switch block
// {
Diagnostic("CS1522", "{", isSuppressed: true).WithLocation(line: 8, column: 25));
VerifySuppressedAndFilteredDiagnostics(compilation, analyzers);
}
[Fact, WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
public void TestSuppression_CompilerSemanticWarning()
{
string source = @"
class C
{
// warning CS0169: The field 'C.f' is never used
private readonly int f;
}";
var compilation = CreateCompilation(source);
compilation.VerifyDiagnostics(
// (5,26): warning CS0169: The field 'C.f' is never used
// private readonly int f;
Diagnostic(ErrorCode.WRN_UnreferencedField, "f", isSuppressed: false).WithArguments("C.f").WithLocation(5, 26));
// Verify compiler semantic warning can be suppressed with a suppressor.
var analyzers = new DiagnosticAnalyzer[] { new DiagnosticSuppressorForId("CS0169") };
VerifySuppressedDiagnostics(compilation, analyzers,
// (5,26): warning CS0169: The field 'C.f' is never used
// private readonly int f;
Diagnostic("CS0169", "f", isSuppressed: true).WithArguments("C.f").WithLocation(5, 26));
VerifySuppressedAndFilteredDiagnostics(compilation, analyzers);
}
[Fact, WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
public void TestNoSuppression_CompilerSyntaxError()
{
string source = @"
class { }";
var compilation = CreateCompilation(source);
compilation.VerifyDiagnostics(
// (2,7): error CS1001: Identifier expected
// class { }
Diagnostic(ErrorCode.ERR_IdentifierExpected, "{").WithLocation(2, 7));
// Verify compiler syntax error cannot be suppressed.
var analyzers = new DiagnosticAnalyzer[] { new DiagnosticSuppressorForId("CS1001") };
VerifySuppressedDiagnostics(compilation, analyzers);
}
[Fact, WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
public void TestNoSuppression_CompilerSemanticError()
{
string source = @"
class C
{
void M(UndefinedType x) { }
}";
var compilation = CreateCompilation(source);
compilation.VerifyDiagnostics(
// (4,12): error CS0246: The type or namespace name 'UndefinedType' could not be found (are you missing a using directive or an assembly reference?)
// void M(UndefinedType x) { }
Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "UndefinedType").WithArguments("UndefinedType").WithLocation(4, 12));
// Verify compiler semantic error cannot be suppressed.
var analyzers = new DiagnosticAnalyzer[] { new DiagnosticSuppressorForId("CS0246") };
VerifySuppressedDiagnostics(compilation, analyzers);
}
[Fact, WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
public void TestSuppression_MultipleDiagnostics()
{
string source1 = @"class C1 { }";
string source2 = @"class C2 { }";
var compilation = CreateCompilation(new[] { source1, source2 });
compilation.VerifyDiagnostics();
var analyzer = new CompilationAnalyzerWithSeverity(DiagnosticSeverity.Warning, configurable: true);
var expectedDiagnostics = new DiagnosticDescription[] {
Diagnostic(analyzer.Descriptor.Id, source1, isSuppressed: false).WithLocation(1, 1),
Diagnostic(analyzer.Descriptor.Id, source2, isSuppressed: false).WithLocation(1, 1),
};
VerifyAnalyzerDiagnostics(compilation, new DiagnosticAnalyzer[] { analyzer }, expectedDiagnostics);
var analyzersAndSuppressors = new DiagnosticAnalyzer[] { analyzer, new DiagnosticSuppressorForId(analyzer.Descriptor.Id) };
expectedDiagnostics = new DiagnosticDescription[] {
Diagnostic(analyzer.Descriptor.Id, source1, isSuppressed: true).WithLocation(1, 1),
Diagnostic(analyzer.Descriptor.Id, source2, isSuppressed: true).WithLocation(1, 1),
};
VerifySuppressedDiagnostics(compilation, analyzersAndSuppressors, expectedDiagnostics);
VerifySuppressedAndFilteredDiagnostics(compilation, analyzersAndSuppressors);
}
[Fact, WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
public void TestSuppression_MultipleSuppressors_SameDiagnostic()
{
string source = @"class C1 { }";
var compilation = CreateCompilation(source);
compilation.VerifyDiagnostics();
var analyzer = new CompilationAnalyzerWithSeverity(DiagnosticSeverity.Warning, configurable: true);
var expectedDiagnostic = Diagnostic(analyzer.Descriptor.Id, source, isSuppressed: false).WithLocation(1, 1);
VerifyAnalyzerDiagnostics(compilation, new DiagnosticAnalyzer[] { analyzer }, expectedDiagnostic);
// Multiple suppressors with same suppression ID.
expectedDiagnostic = Diagnostic(analyzer.Descriptor.Id, source, isSuppressed: true).WithLocation(1, 1);
var analyzersAndSuppressors = new DiagnosticAnalyzer[] { analyzer, new DiagnosticSuppressorForId(analyzer.Descriptor.Id), new DiagnosticSuppressorForId(analyzer.Descriptor.Id) };
VerifySuppressedDiagnostics(compilation, analyzersAndSuppressors, expectedDiagnostic);
VerifySuppressedAndFilteredDiagnostics(compilation, analyzersAndSuppressors);
// Multiple suppressors with different suppression ID.
analyzersAndSuppressors = new DiagnosticAnalyzer[] { analyzer, new DiagnosticSuppressorForId(analyzer.Descriptor.Id, suppressionId: "SPR0001"), new DiagnosticSuppressorForId(analyzer.Descriptor.Id, suppressionId: "SPR0002") };
VerifySuppressedDiagnostics(compilation, analyzersAndSuppressors, expectedDiagnostic);
VerifySuppressedAndFilteredDiagnostics(compilation, analyzersAndSuppressors);
}
[Fact, WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
public void TestSuppression_MultipleSuppressors_DifferentDiagnostic()
{
string source = @"class C1 { private readonly int f; }";
var compilation = CreateCompilation(source);
compilation.VerifyDiagnostics(
// (1,33): warning CS0169: The field 'C1.f' is never used
// class C1 { private readonly int f; }
Diagnostic(ErrorCode.WRN_UnreferencedField, "f").WithArguments("C1.f").WithLocation(1, 33));
var analyzer = new CompilationAnalyzerWithSeverity(DiagnosticSeverity.Warning, configurable: true);
VerifyAnalyzerDiagnostics(compilation, new DiagnosticAnalyzer[] { analyzer },
Diagnostic(analyzer.Descriptor.Id, source));
var suppressor1 = new DiagnosticSuppressorForId("CS0169");
var suppressor2 = new DiagnosticSuppressorForId(analyzer.Descriptor.Id);
var analyzersAndSuppressors = new DiagnosticAnalyzer[] { analyzer, suppressor1, suppressor2 };
VerifySuppressedDiagnostics(compilation, analyzersAndSuppressors,
Diagnostic("CS0169", "f", isSuppressed: true).WithArguments("C1.f").WithLocation(1, 33),
Diagnostic(analyzer.Descriptor.Id, source, isSuppressed: true));
VerifySuppressedAndFilteredDiagnostics(compilation, analyzersAndSuppressors);
}
[Fact, WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
public void TestNoSuppression_SpecificOptionsTurnsOffSuppressor()
{
string source = @"class C1 { }";
var compilation = CreateCompilation(source);
compilation.VerifyDiagnostics();
var analyzer = new CompilationAnalyzerWithSeverity(DiagnosticSeverity.Warning, configurable: true);
var expectedDiagnostic = Diagnostic(analyzer.Descriptor.Id, source, isSuppressed: false);
VerifyAnalyzerDiagnostics(compilation, new DiagnosticAnalyzer[] { analyzer }, expectedDiagnostic);
const string suppressionId = "SPR1001";
var analyzersAndSuppressors = new DiagnosticAnalyzer[] { analyzer, new DiagnosticSuppressorForId(analyzer.Descriptor.Id, suppressionId) };
expectedDiagnostic = Diagnostic(analyzer.Descriptor.Id, source, isSuppressed: true);
VerifySuppressedDiagnostics(compilation, analyzersAndSuppressors, expectedDiagnostic);
var specificDiagnosticOptions = compilation.Options.SpecificDiagnosticOptions.Add(suppressionId, ReportDiagnostic.Suppress);
compilation = compilation.WithOptions(compilation.Options.WithSpecificDiagnosticOptions(specificDiagnosticOptions));
VerifySuppressedDiagnostics(compilation, analyzersAndSuppressors);
}
[Fact, WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
public void TestSuppression_AnalyzerDiagnostics_SeveritiesAndConfigurableMatrix()
{
string source = @"
class C { }";
var compilation = CreateCompilation(source);
compilation.VerifyDiagnostics();
var configurations = new[] { false, true };
var severities = Enum.GetValues(typeof(DiagnosticSeverity));
var originalSpecificDiagnosticOptions = compilation.Options.SpecificDiagnosticOptions;
foreach (var configurable in configurations)
{
foreach (DiagnosticSeverity defaultSeverity in severities)
{
foreach (DiagnosticSeverity effectiveSeverity in severities)
{
var diagnostic = Diagnostic("ID1000", "class C { }", isSuppressed: true)
.WithLocation(2, 1)
.WithDefaultSeverity(defaultSeverity)
.WithEffectiveSeverity(configurable ? effectiveSeverity : defaultSeverity);
var diagnosticNoSuppressor = Diagnostic("ID1000", "class C { }", isSuppressed: false)
.WithLocation(2, 1)
.WithDefaultSeverity(defaultSeverity)
.WithEffectiveSeverity(configurable ? effectiveSeverity : defaultSeverity);
if (defaultSeverity == DiagnosticSeverity.Warning &&
effectiveSeverity == DiagnosticSeverity.Error &&
configurable)
{
diagnostic = diagnostic.WithWarningAsError(true);
diagnosticNoSuppressor = diagnosticNoSuppressor.WithWarningAsError(true);
}
var analyzer = new CompilationAnalyzerWithSeverity(defaultSeverity, configurable);
var suppressor = new DiagnosticSuppressorForId(analyzer.Descriptor.Id);
var analyzersWithoutSuppressor = new DiagnosticAnalyzer[] { analyzer };
var analyzersWithSuppressor = new DiagnosticAnalyzer[] { analyzer, suppressor };
var specificDiagnosticOptions = originalSpecificDiagnosticOptions.Add(
key: analyzer.Descriptor.Id,
value: DiagnosticDescriptor.MapSeverityToReport(effectiveSeverity));
compilation = compilation.WithOptions(compilation.Options.WithSpecificDiagnosticOptions(specificDiagnosticOptions));
// Verify analyzer diagnostic without suppressor, also verify no suppressions.
VerifyAnalyzerDiagnostics(compilation, analyzersWithoutSuppressor, diagnosticNoSuppressor);
VerifySuppressedDiagnostics(compilation, analyzersWithoutSuppressor);
// Verify suppressed analyzer diagnostic, except when default severity is Error or diagnostic is not-configurable.
if (defaultSeverity == DiagnosticSeverity.Error || !configurable)
{
VerifySuppressedDiagnostics(compilation, analyzersWithSuppressor);
}
else
{
VerifySuppressedDiagnostics(compilation, analyzersWithSuppressor, diagnostic);
}
}
}
}
}
[Fact, WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
public void TestExceptionFromSupportedSuppressions()
{
string source = "class C { }";
var compilation = CreateCompilation(source);
compilation.VerifyDiagnostics();
var expectedException = new NotImplementedException();
var analyzer = new CompilationAnalyzerWithSeverity(DiagnosticSeverity.Warning, configurable: true);
var suppressor = new DiagnosticSuppressorThrowsExceptionFromSupportedSuppressions(expectedException);
var exceptions = new List<Exception>();
EventHandler<FirstChanceExceptionEventArgs> firstChanceException =
(sender, e) =>
{
if (e.Exception == expectedException)
{
exceptions.Add(e.Exception);
}
};
try
{
AppDomain.CurrentDomain.FirstChanceException += firstChanceException;
IFormattable context = $@"{new LazyToString(() => exceptions[0])}
-----";
var analyzersAndSuppressors = new DiagnosticAnalyzer[] { analyzer, suppressor };
VerifyAnalyzerDiagnostics(compilation, analyzersAndSuppressors,
Diagnostic("AD0001").WithArguments(suppressor.ToString(), typeof(NotImplementedException).FullName, new NotImplementedException().Message, context).WithLocation(1, 1),
Diagnostic("ID1000", "class C { }").WithLocation(1, 1));
VerifySuppressedDiagnostics(compilation, analyzersAndSuppressors);
}
finally
{
AppDomain.CurrentDomain.FirstChanceException -= firstChanceException;
}
}
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/41212")]
[WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
[WorkItem(41212, "https://github.com/dotnet/roslyn/issues/41212")]
public void TestExceptionFromSuppressor()
{
string source = "class C { }";
var compilation = CreateCompilation(source);
compilation.VerifyDiagnostics();
var expectedException = new NotImplementedException();
var analyzer = new CompilationAnalyzerWithSeverity(DiagnosticSeverity.Warning, configurable: true);
var suppressor = new DiagnosticSuppressorThrowsExceptionFromReportedSuppressions(analyzer.Descriptor.Id, expectedException);
var exceptions = new List<Exception>();
EventHandler<FirstChanceExceptionEventArgs> firstChanceException =
(sender, e) =>
{
if (e.Exception == expectedException)
{
exceptions.Add(e.Exception);
}
};
try
{
AppDomain.CurrentDomain.FirstChanceException += firstChanceException;
IFormattable context = $@"{string.Format(CodeAnalysisResources.ExceptionContext, $@"Compilation: {compilation.AssemblyName}")}
{new LazyToString(() => exceptions[0])}
-----";
var analyzersAndSuppressors = new DiagnosticAnalyzer[] { analyzer, suppressor };
VerifyAnalyzerDiagnostics(compilation, analyzersAndSuppressors,
Diagnostic("AD0001").WithArguments(suppressor.ToString(), typeof(NotImplementedException).FullName, new NotImplementedException().Message, context).WithLocation(1, 1),
Diagnostic("ID1000", "class C { }").WithLocation(1, 1));
VerifySuppressedDiagnostics(compilation, analyzersAndSuppressors);
}
finally
{
AppDomain.CurrentDomain.FirstChanceException -= firstChanceException;
}
}
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/41212")]
[WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
[WorkItem(41212, "https://github.com/dotnet/roslyn/issues/41212")]
public void TestUnsupportedSuppressionReported()
{
string source = @"
class C { }";
var compilation = CreateCompilation(source);
compilation.VerifyDiagnostics();
const string supportedSuppressionId = "supportedId";
const string unsupportedSuppressionId = "unsupportedId";
var analyzer = new CompilationAnalyzerWithSeverity(DiagnosticSeverity.Warning, configurable: true);
var suppressor = new DiagnosticSuppressor_UnsupportedSuppressionReported(analyzer.Descriptor.Id, supportedSuppressionId, unsupportedSuppressionId);
var analyzersAndSuppressors = new DiagnosticAnalyzer[] { analyzer, suppressor };
// "Reported suppression with ID '{0}' is not supported by the suppressor."
var exceptionMessage = string.Format(CodeAnalysisResources.UnsupportedSuppressionReported, unsupportedSuppressionId);
var exceptions = new List<Exception>();
EventHandler<FirstChanceExceptionEventArgs> firstChanceException =
(sender, e) =>
{
if (e.Exception is ArgumentException
&& e.Exception.Message == exceptionMessage)
{
exceptions.Add(e.Exception);
}
};
try
{
AppDomain.CurrentDomain.FirstChanceException += firstChanceException;
IFormattable context = $@"{string.Format(CodeAnalysisResources.ExceptionContext, $@"Compilation: {compilation.AssemblyName}")}
{new LazyToString(() => exceptions[0])}
-----";
VerifyAnalyzerDiagnostics(compilation, analyzersAndSuppressors,
Diagnostic("AD0001").WithArguments(suppressor.ToString(),
typeof(ArgumentException).FullName,
exceptionMessage,
context)
.WithLocation(1, 1),
Diagnostic("ID1000", "class C { }").WithLocation(2, 1));
VerifySuppressedDiagnostics(compilation, analyzersAndSuppressors);
}
finally
{
AppDomain.CurrentDomain.FirstChanceException -= firstChanceException;
}
}
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/41212")]
[WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
[WorkItem(41212, "https://github.com/dotnet/roslyn/issues/41212")]
public void TestInvalidDiagnosticSuppressionReported()
{
string source = @"
class C { }";
var compilation = CreateCompilation(source);
compilation.VerifyDiagnostics();
const string unsupportedSuppressedId = "UnsupportedId";
var analyzer = new CompilationAnalyzerWithSeverity(DiagnosticSeverity.Warning, configurable: true);
var suppressor = new DiagnosticSuppressor_InvalidDiagnosticSuppressionReported(analyzer.Descriptor.Id, unsupportedSuppressedId);
var analyzersAndSuppressors = new DiagnosticAnalyzer[] { analyzer, suppressor };
// "Suppressed diagnostic ID '{0}' does not match suppressable ID '{1}' for the given suppression descriptor."
var exceptionMessage = string.Format(CodeAnalysisResources.InvalidDiagnosticSuppressionReported, analyzer.Descriptor.Id, unsupportedSuppressedId);
var exceptions = new List<Exception>();
EventHandler<FirstChanceExceptionEventArgs> firstChanceException =
(sender, e) =>
{
if (e.Exception is ArgumentException
&& e.Exception.Message == exceptionMessage)
{
exceptions.Add(e.Exception);
}
};
try
{
AppDomain.CurrentDomain.FirstChanceException += firstChanceException;
IFormattable context = $@"{string.Format(CodeAnalysisResources.ExceptionContext, $@"Compilation: {compilation.AssemblyName}")}
{new LazyToString(() => exceptions[0])}
-----";
VerifyAnalyzerDiagnostics(compilation, analyzersAndSuppressors,
Diagnostic("AD0001").WithArguments(suppressor.ToString(),
typeof(System.ArgumentException).FullName,
exceptionMessage,
context)
.WithLocation(1, 1),
Diagnostic("ID1000", "class C { }").WithLocation(2, 1));
VerifySuppressedDiagnostics(compilation, analyzersAndSuppressors);
}
finally
{
AppDomain.CurrentDomain.FirstChanceException -= firstChanceException;
}
}
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/41212")]
[WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
[WorkItem(41212, "https://github.com/dotnet/roslyn/issues/41212")]
public void TestNonReportedDiagnosticCannotBeSuppressed()
{
string source = @"
class C { }";
var compilation = CreateCompilation(source);
compilation.VerifyDiagnostics();
const string nonReportedDiagnosticId = "NonReportedId";
var analyzer = new CompilationAnalyzerWithSeverity(DiagnosticSeverity.Warning, configurable: true);
var suppressor = new DiagnosticSuppressor_NonReportedDiagnosticCannotBeSuppressed(analyzer.Descriptor.Id, nonReportedDiagnosticId);
var analyzersAndSuppressors = new DiagnosticAnalyzer[] { analyzer, suppressor };
// "Non-reported diagnostic with ID '{0}' cannot be suppressed."
var exceptionMessage = string.Format(CodeAnalysisResources.NonReportedDiagnosticCannotBeSuppressed, nonReportedDiagnosticId);
var exceptions = new List<Exception>();
EventHandler<FirstChanceExceptionEventArgs> firstChanceException =
(sender, e) =>
{
if (e.Exception is ArgumentException
&& e.Exception.Message == exceptionMessage)
{
exceptions.Add(e.Exception);
}
};
try
{
AppDomain.CurrentDomain.FirstChanceException += firstChanceException;
IFormattable context = $@"{string.Format(CodeAnalysisResources.ExceptionContext, $@"Compilation: {compilation.AssemblyName}")}
{new LazyToString(() => exceptions[0])}
-----";
VerifyAnalyzerDiagnostics(compilation, analyzersAndSuppressors,
Diagnostic("AD0001").WithArguments(suppressor.ToString(),
typeof(System.ArgumentException).FullName,
exceptionMessage,
context)
.WithLocation(1, 1),
Diagnostic("ID1000", "class C { }").WithLocation(2, 1));
VerifySuppressedDiagnostics(compilation, analyzersAndSuppressors);
}
finally
{
AppDomain.CurrentDomain.FirstChanceException -= firstChanceException;
}
}
[Fact, WorkItem(20242, "https://github.com/dotnet/roslyn/issues/20242")]
public void TestProgrammaticSuppressionInfo_DiagnosticSuppressor()
{
string source = @"class C1 { }";
var compilation = CreateCompilation(source);
compilation.VerifyDiagnostics();
var analyzer = new CompilationAnalyzerWithSeverity(DiagnosticSeverity.Warning, configurable: true);
var expectedDiagnostic = Diagnostic(analyzer.Descriptor.Id, source);
VerifyAnalyzerDiagnostics(compilation, new DiagnosticAnalyzer[] { analyzer }, expectedDiagnostic);
const string suppressionId = "SPR1001";
var suppressor = new DiagnosticSuppressorForId(analyzer.Descriptor.Id, suppressionId);
var analyzersAndSuppressors = new DiagnosticAnalyzer[] { analyzer, suppressor };
var diagnostics = compilation.GetAnalyzerDiagnostics(analyzersAndSuppressors, reportSuppressedDiagnostics: true);
var diagnostic = Assert.Single(diagnostics);
var suppression = diagnostic.ProgrammaticSuppressionInfo.Suppressions.Single();
Assert.Equal(suppressionId, suppression.Descriptor.Id);
Assert.Equal(suppressor.SuppressionDescriptor.Justification, suppression.Descriptor.Justification);
var suppressionInfo = diagnostic.GetSuppressionInfo(compilation);
Assert.Equal(suppression, suppressionInfo.ProgrammaticSuppressions.Single());
const string suppressionId2 = "SPR1002";
var suppressor2 = new DiagnosticSuppressorForId(analyzer.Descriptor.Id, suppressionId2);
analyzersAndSuppressors = new DiagnosticAnalyzer[] { analyzer, suppressor, suppressor2 };
diagnostics = compilation.GetAnalyzerDiagnostics(analyzersAndSuppressors, reportSuppressedDiagnostics: true);
diagnostic = Assert.Single(diagnostics);
var programmaticSuppression = diagnostic.ProgrammaticSuppressionInfo;
Assert.Equal(2, programmaticSuppression.Suppressions.Length);
var orderedSuppressions = programmaticSuppression.Suppressions.OrderBy(suppression => suppression.Descriptor.Id).ToImmutableArrayOrEmpty();
Assert.Equal(suppressionId, orderedSuppressions[0].Descriptor.Id);
Assert.Equal(suppressor.SuppressionDescriptor.Justification, orderedSuppressions[0].Descriptor.Justification);
Assert.Equal(suppressionId2, orderedSuppressions[1].Descriptor.Id);
Assert.Equal(suppressor2.SuppressionDescriptor.Justification, orderedSuppressions[1].Descriptor.Justification);
suppressionInfo = diagnostic.GetSuppressionInfo(compilation);
AssertEx.SetEqual(programmaticSuppression.Suppressions, suppressionInfo.ProgrammaticSuppressions);
}
[CombinatorialData]
[Theory, WorkItem(41713, "https://github.com/dotnet/roslyn/issues/41713")]
public void TestCancellationDuringSuppressorExecution(bool concurrent)
{
string source = @"class C1 { }";
var options = TestOptions.DebugDll.WithConcurrentBuild(concurrent);
var compilation = CreateCompilation(source, options: options);
compilation.VerifyDiagnostics();
var analyzer = new CompilationAnalyzerWithSeverity(DiagnosticSeverity.Warning, configurable: true);
var expectedDiagnostic = Diagnostic(analyzer.Descriptor.Id, source);
VerifyAnalyzerDiagnostics(compilation, new DiagnosticAnalyzer[] { analyzer }, expectedDiagnostic);
var suppressor = new DiagnosticSuppressorForId_ThrowsOperationCancelledException(analyzer.Descriptor.Id);
var cancellationToken = suppressor.CancellationTokenSource.Token;
var analyzersAndSuppressors = new DiagnosticAnalyzer[] { analyzer, suppressor };
Assert.Throws<OperationCanceledException>(() => compilation.GetAnalyzerDiagnostics(analyzersAndSuppressors, reportSuppressedDiagnostics: true, cancellationToken: cancellationToken));
}
[Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1819603")]
public async Task TestSuppressionAcrossCallsIntoCompilationWithAnalyzers(bool withSuppressor)
{
string source = @"class C1 { }";
var compilation = CreateCompilation(new[] { source });
compilation.VerifyDiagnostics();
// First verify analyzer diagnostics without any suppressors
var analyzer1 = new SemanticModelAnalyzerWithId("ID0001");
var analyzer2 = new SemanticModelAnalyzerWithId("ID0002");
var expectedDiagnostics = new DiagnosticDescription[]
{
Diagnostic(analyzer1.Descriptor.Id, source, isSuppressed: false).WithLocation(1, 1),
Diagnostic(analyzer2.Descriptor.Id, source, isSuppressed: false).WithLocation(1, 1),
};
VerifyAnalyzerDiagnostics(compilation, new DiagnosticAnalyzer[] { analyzer1, analyzer2 }, expectedDiagnostics);
// Verify whole compilation analyzer diagnostics including suppressors.
var suppressor = new DiagnosticSuppressorForMultipleIds(analyzer1.Descriptor.Id, analyzer2.Descriptor.Id);
var analyzersAndSuppressors = new DiagnosticAnalyzer[] { analyzer1, analyzer2, suppressor };
expectedDiagnostics = new DiagnosticDescription[] {
Diagnostic(analyzer1.Descriptor.Id, source, isSuppressed: true).WithLocation(1, 1),
Diagnostic(analyzer2.Descriptor.Id, source, isSuppressed: true).WithLocation(1, 1),
};
VerifySuppressedDiagnostics(compilation, analyzersAndSuppressors, expectedDiagnostics);
VerifySuppressedAndFilteredDiagnostics(compilation, analyzersAndSuppressors);
// Now, verify single file analyzer diagnostics with multiple calls using subset of analyzers.
var options = new CompilationWithAnalyzersOptions(AnalyzerOptions.Empty, onAnalyzerException: null, concurrentAnalysis: true, logAnalyzerExecutionTime: true, reportSuppressedDiagnostics: true);
var compilationWithAnalyzers = new CompilationWithAnalyzers(compilation, analyzersAndSuppressors.ToImmutableArray(), options);
var tree = compilation.SyntaxTrees[0];
var semanticModel = compilation.GetSemanticModel(tree);
var analyzers = withSuppressor ? ImmutableArray.Create<DiagnosticAnalyzer>(analyzer1, suppressor) : ImmutableArray.Create<DiagnosticAnalyzer>(analyzer1);
var diagnostics1 = await compilationWithAnalyzers.GetAnalyzerSemanticDiagnosticsAsync(semanticModel, filterSpan: null, analyzers, CancellationToken.None);
diagnostics1.Verify(
Diagnostic(analyzer1.Descriptor.Id, source, isSuppressed: true).WithLocation(1, 1));
analyzers = withSuppressor ? ImmutableArray.Create<DiagnosticAnalyzer>(analyzer2, suppressor) : ImmutableArray.Create<DiagnosticAnalyzer>(analyzer2);
var diagnostics2 = await compilationWithAnalyzers.GetAnalyzerSemanticDiagnosticsAsync(semanticModel, filterSpan: null, analyzers, CancellationToken.None);
diagnostics2.Verify(
Diagnostic(analyzer2.Descriptor.Id, source, isSuppressed: true).WithLocation(1, 1));
}
}
}
|