// 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.Immutable; using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.CodeFixes.Suppression; using Microsoft.CodeAnalysis.CSharp.Diagnostics.SimplifyTypeNames; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics.CSharp; using Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.ErrorLogger; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.Suppression { public abstract partial class CSharpSuppressionTests : AbstractSuppressionDiagnosticTest { protected override ParseOptions GetScriptOptions() => Options.Script; protected internal override string GetLanguage() => LanguageNames.CSharp; #region "Pragma disable tests" public abstract partial class CSharpPragmaWarningDisableSuppressionTests : CSharpSuppressionTests { protected sealed override int CodeActionIndex { get { return 0; } } public class CompilerDiagnosticSuppressionTests : CSharpPragmaWarningDisableSuppressionTests { internal override Tuple CreateDiagnosticProviderAndFixer(Workspace workspace) => Tuple.Create(null, new CSharpSuppressionCodeFixProvider()); [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestPragmaWarningDirective() { await TestAsync( @" class Class { void Method() { [|int x = 0;|] } }", $@" class Class {{ void Method() {{ #pragma warning disable CS0219 // {CSharpResources.WRN_UnreferencedVarAssg_Title} int x = 0; #pragma warning restore CS0219 // {CSharpResources.WRN_UnreferencedVarAssg_Title} }} }}"); } [WorkItem(26015, "https://github.com/dotnet/roslyn/issues/26015")] [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestPragmaWarningDirectiveAroundMultiLineStatement() { await TestAsync( @" class Class { void Method() { [|string x = @""multi line"";|] } }", $@" class Class {{ void Method() {{ #pragma warning disable CS0219 // {CSharpResources.WRN_UnreferencedVarAssg_Title} string x = @""multi line""; #pragma warning restore CS0219 // {CSharpResources.WRN_UnreferencedVarAssg_Title} }} }}"); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestMultilineStatementPragmaWarningDirective() { await TestAsync( @" class Class { void Method() { [|int x = 0 + 1;|] } }", $@" class Class {{ void Method() {{ #pragma warning disable CS0219 // {CSharpResources.WRN_UnreferencedVarAssg_Title} int x = 0 #pragma warning restore CS0219 // {CSharpResources.WRN_UnreferencedVarAssg_Title} + 1; }} }}"); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestPragmaWarningDirectiveWithExistingTrivia() { await TestAsync( @" class Class { void Method() { // Start comment previous line /* Start comment same line */ [|int x = 0;|] // End comment same line /* End comment next line */ } }", $@" class Class {{ void Method() {{ // Start comment previous line #pragma warning disable CS0219 // {CSharpResources.WRN_UnreferencedVarAssg_Title} /* Start comment same line */ int x = 0; // End comment same line #pragma warning restore CS0219 // {CSharpResources.WRN_UnreferencedVarAssg_Title} /* End comment next line */ }} }}"); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] [WorkItem(16681, "https://github.com/dotnet/roslyn/issues/16681")] public async Task TestPragmaWarningDirectiveWithDocumentationComment1() { await TestAsync( @" sealed class Class { /// Text [|protected void Method()|] { } }", $@" sealed class Class {{ /// Text #pragma warning disable CS0628 // {CSharpResources.WRN_ProtectedInSealed_Title} protected void Method() #pragma warning restore CS0628 // {CSharpResources.WRN_ProtectedInSealed_Title} {{ }} }}"); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] [WorkItem(16681, "https://github.com/dotnet/roslyn/issues/16681")] public async Task TestPragmaWarningDirectiveWithDocumentationComment2() { await TestAsync( @" sealed class Class { /// Text /// /// /// void Method() { } }", $@" sealed class Class {{ #pragma warning disable CS1574 // {CSharpResources.WRN_BadXMLRef_Title} /// Text /// /// /// void Method() #pragma warning restore CS1574 // {CSharpResources.WRN_BadXMLRef_Title} {{ }} }}", new CSharpParseOptions(documentationMode: DocumentationMode.Diagnose)); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestMultipleInstancesOfPragmaWarningDirective() { await TestAsync( @" class Class { void Method() { [|int x = 0, y = 0;|] } }", $@" class Class {{ void Method() {{ #pragma warning disable CS0219 // {CSharpResources.WRN_UnreferencedVarAssg_Title} int x = 0, y = 0; #pragma warning restore CS0219 // {CSharpResources.WRN_UnreferencedVarAssg_Title} }} }}"); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] [WorkItem(3311, "https://github.com/dotnet/roslyn/issues/3311")] public async Task TestNoDuplicateSuppressionCodeFixes() { var source = @" class Class { void Method() { [|int x = 0, y = 0; string s;|] } }"; var parameters = new TestParameters(); using var workspace = CreateWorkspaceFromOptions(source, parameters); var analyzerReference = new AnalyzerImageReference(ImmutableArray.Create(new CSharpCompilerDiagnosticAnalyzer())); workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences(new[] { analyzerReference })); Assert.IsType(workspace.ExportProvider.GetExportedValue()); var diagnosticService = Assert.IsType(workspace.ExportProvider.GetExportedValue()); var incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace); var suppressionProvider = CreateDiagnosticProviderAndFixer(workspace).Item2; var suppressionProviderFactory = new Lazy(() => suppressionProvider, new CodeChangeProviderMetadata("SuppressionProvider", languages: new[] { LanguageNames.CSharp })); var fixService = new CodeFixService( diagnosticService, SpecializedCollections.EmptyEnumerable>(), SpecializedCollections.EmptyEnumerable>(), SpecializedCollections.SingletonEnumerable(suppressionProviderFactory)); var document = GetDocumentAndSelectSpan(workspace, out var span); var diagnostics = await diagnosticService.GetDiagnosticsForSpanAsync(document, span); Assert.Equal(2, diagnostics.Where(d => d.Id == "CS0219").Count()); var allFixes = (await fixService.GetFixesAsync(document, span, includeConfigurationFixes: true, cancellationToken: CancellationToken.None)) .SelectMany(fixCollection => fixCollection.Fixes); var cs0219Fixes = allFixes.Where(fix => fix.PrimaryDiagnostic.Id == "CS0219").ToArray(); // Ensure that there are no duplicate suppression fixes. Assert.Equal(1, cs0219Fixes.Length); var cs0219EquivalenceKey = cs0219Fixes[0].Action.EquivalenceKey; Assert.NotNull(cs0219EquivalenceKey); // Ensure that there *is* a fix for the other warning and that it has a *different* // equivalence key so that it *doesn't* get de-duplicated Assert.Equal(1, diagnostics.Where(d => d.Id == "CS0168").Count()); var cs0168Fixes = allFixes.Where(fix => fix.PrimaryDiagnostic.Id == "CS0168"); var cs0168EquivalenceKey = cs0168Fixes.Single().Action.EquivalenceKey; Assert.NotNull(cs0168EquivalenceKey); Assert.NotEqual(cs0219EquivalenceKey, cs0168EquivalenceKey); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestErrorAndWarningScenario() { await TestAsync( @" class Class { void Method() { return 0; [|int x = ""0"";|] } }", $@" class Class {{ void Method() {{ return 0; #pragma warning disable CS0162 // {CSharpResources.WRN_UnreachableCode_Title} int x = ""0""; #pragma warning restore CS0162 // {CSharpResources.WRN_UnreachableCode_Title} }} }}"); } [WorkItem(956453, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/956453")] [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestWholeFilePragmaWarningDirective() { await TestAsync( @"class Class { void Method() { [|int x = 0;|] } }", $@"#pragma warning disable CS0219 // {CSharpResources.WRN_UnreferencedVarAssg_Title} class Class {{ void Method() {{ int x = 0; }} }} #pragma warning restore CS0219 // {CSharpResources.WRN_UnreferencedVarAssg_Title}"); } [WorkItem(970129, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/970129")] [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestSuppressionAroundSingleToken() { await TestAsync( @" using System; [Obsolete] class Session { } class Program { static void Main() { [|Session|] } }", $@" using System; [Obsolete] class Session {{ }} class Program {{ static void Main() {{ #pragma warning disable CS0612 // {CSharpResources.WRN_DeprecatedSymbol_Title} Session #pragma warning restore CS0612 // {CSharpResources.WRN_DeprecatedSymbol_Title} }} }}"); } [WorkItem(1066576, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1066576")] [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestPragmaWarningDirectiveAroundTrivia1() { await TestAsync( @" class Class { void Method() { // Comment // Comment [|#pragma abcde|] } // Comment }", $@" class Class {{ void Method() {{ // Comment // Comment #pragma warning disable CS1633 // {CSharpResources.WRN_IllegalPragma_Title} #pragma abcde }} // Comment #pragma warning restore CS1633 // {CSharpResources.WRN_IllegalPragma_Title} }}"); } [WorkItem(1066576, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1066576")] [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestPragmaWarningDirectiveAroundTrivia2() { await TestAsync( @"[|#pragma abcde|]", $@"#pragma warning disable CS1633 // {CSharpResources.WRN_IllegalPragma_Title} #pragma abcde #pragma warning restore CS1633 // {CSharpResources.WRN_IllegalPragma_Title}"); } [WorkItem(1066576, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1066576")] [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestPragmaWarningDirectiveAroundTrivia3() { await TestAsync( @"[|#pragma abcde|] ", $@"#pragma warning disable CS1633 // {CSharpResources.WRN_IllegalPragma_Title} #pragma abcde #pragma warning restore CS1633 // {CSharpResources.WRN_IllegalPragma_Title}"); } [WorkItem(1066576, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1066576")] [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestPragmaWarningDirectiveAroundTrivia4() { await TestAsync( @" [|#pragma abc|] class C { } ", $@" #pragma warning disable CS1633 // {CSharpResources.WRN_IllegalPragma_Title} #pragma abc class C {{ }} #pragma warning restore CS1633 // {CSharpResources.WRN_IllegalPragma_Title} "); } [WorkItem(1066576, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1066576")] [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestPragmaWarningDirectiveAroundTrivia5() { await TestAsync( @"class C1 { } [|#pragma abc|] class C2 { } class C3 { }", $@"class C1 {{ }} #pragma warning disable CS1633 // {CSharpResources.WRN_IllegalPragma_Title} #pragma abc class C2 {{ }} #pragma warning restore CS1633 // {CSharpResources.WRN_IllegalPragma_Title} class C3 {{ }}"); } [WorkItem(1066576, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1066576")] [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestPragmaWarningDirectiveAroundTrivia6() { await TestAsync( @"class C1 { } class C2 { } /// class C3 { } // comment // comment // comment", $@"class C1 {{ }} #pragma warning disable CS1574 // {CSharpResources.WRN_BadXMLRef_Title} class C2 {{ }} /// class #pragma warning restore CS1574 // {CSharpResources.WRN_BadXMLRef_Title} C3 {{ }} // comment // comment // comment", CSharpParseOptions.Default.WithDocumentationMode(DocumentationMode.Diagnose)); } } public class UserHiddenDiagnosticSuppressionTests : CSharpPragmaWarningDisableSuppressionTests { internal override Tuple CreateDiagnosticProviderAndFixer(Workspace workspace) { return new Tuple( new CSharpSimplifyTypeNamesDiagnosticAnalyzer(), new CSharpSuppressionCodeFixProvider()); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestHiddenDiagnosticCannotBeSuppressed() { await TestMissingAsync( @" using System; class Class { int Method() { [|System.Int32 x = 0;|] return x; } }"); } } public partial class UserInfoDiagnosticSuppressionTests : CSharpPragmaWarningDisableSuppressionTests { private class UserDiagnosticAnalyzer : DiagnosticAnalyzer { public static readonly DiagnosticDescriptor Decsciptor = new DiagnosticDescriptor("InfoDiagnostic", "InfoDiagnostic Title", "InfoDiagnostic", "InfoDiagnostic", DiagnosticSeverity.Info, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(Decsciptor); } } public override void Initialize(AnalysisContext context) => context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ClassDeclaration); public void AnalyzeNode(SyntaxNodeAnalysisContext context) { var classDecl = (ClassDeclarationSyntax)context.Node; context.ReportDiagnostic(Diagnostic.Create(Decsciptor, classDecl.Identifier.GetLocation())); } } internal override Tuple CreateDiagnosticProviderAndFixer(Workspace workspace) { return new Tuple( new UserDiagnosticAnalyzer(), new CSharpSuppressionCodeFixProvider()); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestInfoDiagnosticSuppressed() { await TestAsync( @" using System; [|class Class|] { int Method() { int x = 0; } }", @" using System; #pragma warning disable InfoDiagnostic // InfoDiagnostic Title class Class #pragma warning restore InfoDiagnostic // InfoDiagnostic Title { int Method() { int x = 0; } }"); } } public partial class FormattingDiagnosticSuppressionTests : CSharpPragmaWarningDisableSuppressionTests { internal override Tuple CreateDiagnosticProviderAndFixer(Workspace workspace) { return new Tuple( new FormattingDiagnosticAnalyzer(), new CSharpSuppressionCodeFixProvider()); } protected override Task<(ImmutableArray, CodeAction actionToInvoke)> GetCodeActionsAsync(TestWorkspace workspace, TestParameters parameters) { var solution = workspace.CurrentSolution; var compilationOptions = solution.Projects.Single().CompilationOptions; var specificDiagnosticOptions = new[] { KeyValuePairUtil.Create(IDEDiagnosticIds.FormattingDiagnosticId, ReportDiagnostic.Warn) }; compilationOptions = compilationOptions.WithSpecificDiagnosticOptions(specificDiagnosticOptions); var updatedSolution = solution.WithProjectCompilationOptions(solution.ProjectIds.Single(), compilationOptions); workspace.ChangeSolution(updatedSolution); return base.GetCodeActionsAsync(workspace, parameters); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] [WorkItem(38587, "https://github.com/dotnet/roslyn/issues/38587")] public async Task TestFormattingDiagnosticSuppressed() { await TestAsync( @" using System; class Class { int Method() { [|int x = 0 ;|] } }", @" using System; class Class { int Method() { #pragma warning disable format int x = 0 ; #pragma warning restore format } }"); } } public class UserErrorDiagnosticSuppressionTests : CSharpPragmaWarningDisableSuppressionTests { private class UserDiagnosticAnalyzer : DiagnosticAnalyzer { private readonly DiagnosticDescriptor _descriptor = new DiagnosticDescriptor("ErrorDiagnostic", "ErrorDiagnostic", "ErrorDiagnostic", "ErrorDiagnostic", DiagnosticSeverity.Error, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(_descriptor); } } public override void Initialize(AnalysisContext context) => context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ClassDeclaration); public void AnalyzeNode(SyntaxNodeAnalysisContext context) { var classDecl = (ClassDeclarationSyntax)context.Node; context.ReportDiagnostic(Diagnostic.Create(_descriptor, classDecl.Identifier.GetLocation())); } } internal override Tuple CreateDiagnosticProviderAndFixer(Workspace workspace) { return new Tuple( new UserDiagnosticAnalyzer(), new CSharpSuppressionCodeFixProvider()); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestErrorDiagnosticCanBeSuppressed() { await TestAsync( @" using System; [|class Class|] { int Method() { int x = 0; } }", @" using System; #pragma warning disable ErrorDiagnostic // ErrorDiagnostic class Class #pragma warning restore ErrorDiagnostic // ErrorDiagnostic { int Method() { int x = 0; } }"); } } public class DiagnosticWithBadIdSuppressionTests : CSharpPragmaWarningDisableSuppressionTests { // Analyzer driver generates a no-location analyzer exception diagnostic, which we don't intend to test here. protected override bool IncludeNoLocationDiagnostics => false; private class UserDiagnosticAnalyzer : DiagnosticAnalyzer { private readonly DiagnosticDescriptor _descriptor = new DiagnosticDescriptor("@~DiagnosticWithBadId", "DiagnosticWithBadId", "DiagnosticWithBadId", "DiagnosticWithBadId", DiagnosticSeverity.Info, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(_descriptor); } } public override void Initialize(AnalysisContext context) => context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ClassDeclaration); public void AnalyzeNode(SyntaxNodeAnalysisContext context) { var classDecl = (ClassDeclarationSyntax)context.Node; context.ReportDiagnostic(Diagnostic.Create(_descriptor, classDecl.Identifier.GetLocation())); } } internal override Tuple CreateDiagnosticProviderAndFixer(Workspace workspace) { return new Tuple( new UserDiagnosticAnalyzer(), new CSharpSuppressionCodeFixProvider()); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestDiagnosticWithBadIdSuppressed() { // Diagnostics with bad/invalid ID are not reported. await TestMissingAsync( @" using System; [|class Class|] { int Method() { int x = 0; } }"); } } } public partial class MultilineDiagnosticSuppressionTests : CSharpPragmaWarningDisableSuppressionTests { private class UserDiagnosticAnalyzer : DiagnosticAnalyzer { public static readonly DiagnosticDescriptor Decsciptor = new DiagnosticDescriptor("InfoDiagnostic", "InfoDiagnostic Title", "InfoDiagnostic", "InfoDiagnostic", DiagnosticSeverity.Info, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(Decsciptor); } } public override void Initialize(AnalysisContext context) => context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ClassDeclaration); public void AnalyzeNode(SyntaxNodeAnalysisContext context) { var classDecl = (ClassDeclarationSyntax)context.Node; context.ReportDiagnostic(Diagnostic.Create(Decsciptor, classDecl.GetLocation())); } } internal override Tuple CreateDiagnosticProviderAndFixer(Workspace workspace) { return new Tuple( new UserDiagnosticAnalyzer(), new CSharpSuppressionCodeFixProvider()); } [WorkItem(2764, "https://github.com/dotnet/roslyn/issues/2764")] [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestPragmaWarningDirectiveAroundMultilineDiagnostic() { await TestAsync( @" [|class Class { }|] ", $@" #pragma warning disable {UserDiagnosticAnalyzer.Decsciptor.Id} // {UserDiagnosticAnalyzer.Decsciptor.Title} class Class {{ }} #pragma warning restore {UserDiagnosticAnalyzer.Decsciptor.Id} // {UserDiagnosticAnalyzer.Decsciptor.Title} "); } } #endregion #region "SuppressMessageAttribute tests" public abstract partial class CSharpGlobalSuppressMessageSuppressionTests : CSharpSuppressionTests { protected sealed override int CodeActionIndex { get { return 1; } } public class CompilerDiagnosticSuppressionTests : CSharpGlobalSuppressMessageSuppressionTests { internal override Tuple CreateDiagnosticProviderAndFixer(Workspace workspace) => Tuple.Create(null, new CSharpSuppressionCodeFixProvider()); [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestCompilerDiagnosticsCannotBeSuppressed() { // Another test verifies we have a pragma warning action for this source, this verifies there are no other suppression actions. await TestActionCountAsync( @" class Class { void Method() { [|int x = 0;|] } }", 1); } } public class FormattingDiagnosticSuppressionTests : CSharpGlobalSuppressMessageSuppressionTests { internal override Tuple CreateDiagnosticProviderAndFixer(Workspace workspace) { return Tuple.Create( new FormattingDiagnosticAnalyzer(), new CSharpSuppressionCodeFixProvider()); } protected override Task<(ImmutableArray, CodeAction actionToInvoke)> GetCodeActionsAsync(TestWorkspace workspace, TestParameters parameters) { var solution = workspace.CurrentSolution; var compilationOptions = solution.Projects.Single().CompilationOptions; var specificDiagnosticOptions = new[] { KeyValuePairUtil.Create(IDEDiagnosticIds.FormattingDiagnosticId, ReportDiagnostic.Warn) }; compilationOptions = compilationOptions.WithSpecificDiagnosticOptions(specificDiagnosticOptions); var updatedSolution = solution.WithProjectCompilationOptions(solution.ProjectIds.Single(), compilationOptions); workspace.ChangeSolution(updatedSolution); return base.GetCodeActionsAsync(workspace, parameters); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] [WorkItem(38587, "https://github.com/dotnet/roslyn/issues/38587")] public async Task TestCompilerDiagnosticsCannotBeSuppressed() { // Another test verifies we have a pragma warning action for this source, this verifies there are no other suppression actions. await TestActionCountAsync( @" class Class { void Method() { [|int x = 0 ;|] } }", 1); } } public class UserHiddenDiagnosticSuppressionTests : CSharpGlobalSuppressMessageSuppressionTests { internal override Tuple CreateDiagnosticProviderAndFixer(Workspace workspace) { return new Tuple( new CSharpSimplifyTypeNamesDiagnosticAnalyzer(), new CSharpSuppressionCodeFixProvider()); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestHiddenDiagnosticsCannotBeSuppressed() { await TestMissingAsync( @" using System; class Class { void Method() { [|System.Int32 x = 0;|] } }"); } } public partial class UserInfoDiagnosticSuppressionTests : CSharpGlobalSuppressMessageSuppressionTests { private class UserDiagnosticAnalyzer : DiagnosticAnalyzer { public static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor("InfoDiagnostic", "InfoDiagnostic", "InfoDiagnostic", "InfoDiagnostic", DiagnosticSeverity.Info, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(Descriptor); } } public override void Initialize(AnalysisContext context) => context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ClassDeclaration, SyntaxKind.EnumDeclaration, SyntaxKind.NamespaceDeclaration, SyntaxKind.MethodDeclaration, SyntaxKind.PropertyDeclaration, SyntaxKind.FieldDeclaration, SyntaxKind.EventDeclaration); public void AnalyzeNode(SyntaxNodeAnalysisContext context) { switch (context.Node.Kind()) { case SyntaxKind.ClassDeclaration: var classDecl = (ClassDeclarationSyntax)context.Node; context.ReportDiagnostic(Diagnostic.Create(Descriptor, classDecl.Identifier.GetLocation())); break; case SyntaxKind.NamespaceDeclaration: var ns = (NamespaceDeclarationSyntax)context.Node; context.ReportDiagnostic(Diagnostic.Create(Descriptor, ns.Name.GetLocation())); break; case SyntaxKind.MethodDeclaration: var method = (MethodDeclarationSyntax)context.Node; context.ReportDiagnostic(Diagnostic.Create(Descriptor, method.Identifier.GetLocation())); break; case SyntaxKind.PropertyDeclaration: var property = (PropertyDeclarationSyntax)context.Node; context.ReportDiagnostic(Diagnostic.Create(Descriptor, property.Identifier.GetLocation())); break; case SyntaxKind.FieldDeclaration: var field = (FieldDeclarationSyntax)context.Node; context.ReportDiagnostic(Diagnostic.Create(Descriptor, field.Declaration.Variables.First().Identifier.GetLocation())); break; case SyntaxKind.EventDeclaration: var e = (EventDeclarationSyntax)context.Node; context.ReportDiagnostic(Diagnostic.Create(Descriptor, e.Identifier.GetLocation())); break; case SyntaxKind.EnumDeclaration: // Report diagnostic on each descendant comment trivia foreach (var trivia in context.Node.DescendantTrivia().Where(t => t.Kind() == SyntaxKind.SingleLineCommentTrivia || t.Kind() == SyntaxKind.MultiLineCommentTrivia)) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, trivia.GetLocation())); } break; } } } internal override Tuple CreateDiagnosticProviderAndFixer(Workspace workspace) { return new Tuple( new UserDiagnosticAnalyzer(), new CSharpSuppressionCodeFixProvider()); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] [WorkItem(37529, "https://github.com/dotnet/roslyn/issues/37529")] public async Task GeneratedCodeShouldNotHaveTrailingWhitespace() { var expected = $@"// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"", Scope = ""type"", Target = ""~T:Class"")] "; Assert.All(Regex.Split(expected, "\r?\n"), line => Assert.False(HasTrailingWhitespace(line))); await TestAsync( @" using System; [|class Class|] { int Method() { int x = 0; } }", expected); } private static bool HasTrailingWhitespace(string line) => line.LastOrNull() is char last && char.IsWhiteSpace(last); [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] [WorkItem(37529, "https://github.com/dotnet/roslyn/issues/37529")] public async Task GeneratedCodeShouldNotHaveLeadingBlankLines() { var expected = $@"// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"", Scope = ""type"", Target = ""~T:Class"")] "; var lines = Regex.Split(expected, "\r?\n"); Assert.False(string.IsNullOrWhiteSpace(lines.First())); await TestAsync( @" using System; [|class Class|] { int Method() { int x = 0; } }", expected); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] [WorkItem(37529, "https://github.com/dotnet/roslyn/issues/37529")] public async Task GeneratedCodeShouldNotHaveMoreThanOneTrailingBlankLine() { var expected = $@"// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"", Scope = ""type"", Target = ""~T:Class"")] "; var lines = Regex.Split(expected, "\r?\n"); Assert.False(string.IsNullOrWhiteSpace(lines[lines.Length - 2])); await TestAsync( @" using System; [|class Class|] { int Method() { int x = 0; } }", expected); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestSuppressionOnSimpleType() { await TestAsync( @" using System; [|class Class|] { int Method() { int x = 0; } }", $@"// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"", Scope = ""type"", Target = ""~T:Class"")] "); // Also verify that the added attribute does indeed suppress the diagnostic. await TestMissingAsync( @" using System; using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = """", Scope = ""type"", Target = ""~T:Class"")] [|class Class|] { int Method() { int x = 0; } }"); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestSuppressionOnNamespace() { await TestInRegularAndScriptAsync( @" using System; [|namespace N|] { class Class { int Method() { int x = 0; } } }", $@"// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"", Scope = ""namespace"", Target = ""~N:N"")] ", index: 1); // Also verify that the added attribute does indeed suppress the diagnostic. await TestMissingAsync( @" using System; using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = """", Scope = ""namespace"", Target = ""~N:N"")] [|namespace N|] { class Class { int Method() { int x = 0; } } }"); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestSuppressionOnTypeInsideNamespace() { await TestAsync( @" using System; namespace N1 { namespace N2 { [|class Class|] { int Method() { int x = 0; } } } }", $@"// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"", Scope = ""type"", Target = ""~T:N1.N2.Class"")] "); // Also verify that the added attribute does indeed suppress the diagnostic. await TestMissingAsync( @" using System; using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = """", Scope = ""type"", Target = ""~T:N1.N2.Class"")] namespace N1 { namespace N2 { [|class Class|] { int Method() { int x = 0; } } } }"); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestSuppressionOnNestedType() { await TestAsync( @" using System; namespace N { class Generic { [|class Class|] { int Method() { int x = 0; } } } }", $@"// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"", Scope = ""type"", Target = ""~T:N.Generic`1.Class"")] "); // Also verify that the added attribute does indeed suppress the diagnostic. await TestMissingAsync( @" using System; using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = """", Scope = ""type"", Target = ""~T:N.Generic`1.Class"")] namespace N { class Generic { [|class Class|] { int Method() { int x = 0; } } } }"); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestSuppressionOnMethod() { await TestAsync( @" using System; namespace N { class Generic { class Class { [|int Method() { int x = 0; }|] } } }", $@"// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"", Scope = ""member"", Target = ""~M:N.Generic`1.Class.Method~System.Int32"")] "); // Also verify that the added attribute does indeed suppress the diagnostic. await TestMissingAsync( @" using System; using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = """", Scope = ""member"", Target = ""~M:N.Generic`1.Class.Method~System.Int32"")] namespace N { class Generic { class Class { [|int Method()|] { int x = 0; } } } }"); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestSuppressionOnOverloadedMethod() { await TestAsync( @" using System; namespace N { class Generic { class Class { [|int Method(int y, ref char z) { int x = 0; }|] int Method() { int x = 0; } } } }", $@"// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"", Scope = ""member"", Target = ""~M:N.Generic`1.Class.Method(System.Int32,System.Char@)~System.Int32"")] "); // Also verify that the added attribute does indeed suppress the diagnostic. await TestMissingAsync( @" using System; using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = """", Scope = ""member"", Target = ""~M:N.Generic`1.Class.Method(System.Int32,System.Char@)~System.Int32"")] namespace N { class Generic { class Class { [|int Method(int y, ref char z)|] { int x = 0; } int Method() { int x = 0; } } } }"); await TestAsync( @" using System; using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = """", Scope = ""member"", Target = ""~M:N.Generic`1.Class.Method(System.Int32,System.Char@)~System.Int32"")] namespace N { class Generic { class Class { [|int Method(int y, ref char z) { int x = 0; } int Method() { int x = 0; }|] } } }", $@"// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"", Scope = ""member"", Target = ""~M:N.Generic`1.Class.Method~System.Int32"")] "); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestSuppressionOnGenericMethod() { await TestAsync( @" using System; namespace N { class Generic { class Class { [|int Method(U u) { int x = 0; }|] } } }", $@"// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"", Scope = ""member"", Target = ""~M:N.Generic`1.Class.Method``1(``0)~System.Int32"")] "); // Also verify that the added attribute does indeed suppress the diagnostic. await TestMissingAsync( @" using System; using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = """", Scope = ""member"", Target = ""~M:N.Generic`1.Class.Method``1(``0)~System.Int32"")] namespace N { class Generic { class Class { [|int Method(U u)|] { int x = 0; } } } }"); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestSuppressionOnProperty() { await TestAsync( @" using System; namespace N { class Generic { class Class { [|int Property|] { get { int x = 0; } } } } }", $@"// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"", Scope = ""member"", Target = ""~P:N.Generic.Class.Property"")] "); // Also verify that the added attribute does indeed suppress the diagnostic. await TestMissingAsync( @" using System; using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = """", Scope = ""member"", Target = ""~P:N.Generic.Class.Property"")] namespace N { class Generic { class Class { [|int Property|] { get { int x = 0; } } } } }"); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestSuppressionOnField() { await TestAsync( @" using System; class Class { [|int field = 0;|] }", $@"// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"", Scope = ""member"", Target = ""~F:Class.field"")] "); // Also verify that the added attribute does indeed suppress the diagnostic. await TestMissingAsync( @" using System; using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = """", Scope = ""member"", Target = ""~F:Class.field"")] class Class { [|int field = 0;|] }"); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] [WorkItem(6379, "https://github.com/dotnet/roslyn/issues/6379")] public async Task TestSuppressionOnTriviaBetweenFields() { await TestAsync( @" using System; // suppressions on field are not relevant. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"", Scope = ""member"", Target = ""~F:E.Field1"")] using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"", Scope = ""member"", Target = ""~F:E.Field2"")] enum E { [| Field1, // trailing trivia for comma token which doesn't belong to span of any of the fields Field2 |] }", $@"// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"", Scope = ""type"", Target = ""~T:E"")] "); // Also verify that the added attribute does indeed suppress the diagnostic. await TestMissingAsync( @" using System; using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"", Scope = ""type"", Target = ""~T:E"")] enum E { [| Field1, // trailing trivia for comma token which doesn't belong to span of any of the fields Field2 |] }"); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestSuppressionOnField2() { await TestAsync( @" using System; class Class { int [|field = 0|], field2 = 1; }", $@"// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"", Scope = ""member"", Target = ""~F:Class.field"")] "); // Also verify that the added attribute does indeed suppress the diagnostic. await TestMissingAsync( @" using System; using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = """", Scope = ""member"", Target = ""~F:Class.field"")] class Class { int [|field|] = 0, field2 = 1; }"); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestSuppressionOnEvent() { await TestAsync( @" using System; public class SampleEventArgs { public SampleEventArgs(string s) { Text = s; } public String Text {get; private set;} // readonly } class Class { // Declare the delegate (if using non-generic pattern). public delegate void SampleEventHandler(object sender, SampleEventArgs e); // Declare the event. [|public event SampleEventHandler SampleEvent { add { } remove { } }|] }", $@"// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"", Scope = ""member"", Target = ""~E:Class.SampleEvent"")] "); // Also verify that the added attribute does indeed suppress the diagnostic. await TestMissingAsync( @" using System; using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = """", Scope = ""member"", Target = ""~E:Class.SampleEvent"")] public class SampleEventArgs { public SampleEventArgs(string s) { Text = s; } public String Text {get; private set;} // readonly } class Class { // Declare the delegate (if using non-generic pattern). public delegate void SampleEventHandler(object sender, SampleEventArgs e); // Declare the event. [|public event SampleEventHandler SampleEvent|] { add { } remove { } } }"); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestSuppressionWithExistingGlobalSuppressionsDocument() { var initialMarkup = @" "", Scope = ""type"", Target = ""Class"")] ]]> "; var expectedText = $@"// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = """", Scope = ""type"", Target = ""Class"")] [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"", Scope = ""type"", Target = ""~T:Class2"")] "; await TestAsync(initialMarkup, expectedText); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestSuppressionWithExistingGlobalSuppressionsDocument2() { // Own custom file named GlobalSuppressions.cs var initialMarkup = @" "; var expectedText = $@"// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"", Scope = ""type"", Target = ""~T:Class2"")] "; await TestAsync(initialMarkup, expectedText); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestSuppressionWithExistingGlobalSuppressionsDocument3() { // Own custom file named GlobalSuppressions.cs + existing GlobalSuppressions2.cs with global suppressions var initialMarkup = @" "", Scope = ""type"", Target = ""Class"")] ]]> "; var expectedText = $@"// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = """", Scope = ""type"", Target = ""Class"")] [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"", Scope = ""type"", Target = ""~T:Class2"")] "; await TestAsync(initialMarkup, expectedText); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestSuppressionWithUsingDirectiveInExistingGlobalSuppressionsDocument() { var initialMarkup = @" "", Scope = ""type"", Target = ""Class"")] ]]> "; var expectedText = $@" using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = """", Scope = ""type"", Target = ""Class"")] [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"", Scope = ""type"", Target = ""~T:Class2"")] "; await TestAsync(initialMarkup, expectedText); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestSuppressionWithoutUsingDirectiveInExistingGlobalSuppressionsDocument() { var initialMarkup = @" "", Scope = ""type"", Target = ""Class"")] ]]> "; var expectedText = $@" using System.Diagnostics.CodeAnalysis; [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = """", Scope = ""type"", Target = ""Class"")] [assembly: SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"", Scope = ""type"", Target = ""~T:Class2"")] "; await TestAsync(initialMarkup, expectedText); } } } public abstract class CSharpLocalSuppressMessageSuppressionTests : CSharpSuppressionTests { protected sealed override int CodeActionIndex { get { return 2; } } public class UserInfoDiagnosticSuppressionTests : CSharpLocalSuppressMessageSuppressionTests { private class UserDiagnosticAnalyzer : DiagnosticAnalyzer { private readonly DiagnosticDescriptor _descriptor = new DiagnosticDescriptor("InfoDiagnostic", "InfoDiagnostic", "InfoDiagnostic", "InfoDiagnostic", DiagnosticSeverity.Info, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(_descriptor); } } public override void Initialize(AnalysisContext context) => context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ClassDeclaration, SyntaxKind.NamespaceDeclaration, SyntaxKind.MethodDeclaration); public void AnalyzeNode(SyntaxNodeAnalysisContext context) { switch (context.Node.Kind()) { case SyntaxKind.ClassDeclaration: var classDecl = (ClassDeclarationSyntax)context.Node; context.ReportDiagnostic(Diagnostic.Create(_descriptor, classDecl.Identifier.GetLocation())); break; case SyntaxKind.NamespaceDeclaration: var ns = (NamespaceDeclarationSyntax)context.Node; context.ReportDiagnostic(Diagnostic.Create(_descriptor, ns.Name.GetLocation())); break; case SyntaxKind.MethodDeclaration: var method = (MethodDeclarationSyntax)context.Node; context.ReportDiagnostic(Diagnostic.Create(_descriptor, method.Identifier.GetLocation())); break; } } } internal override Tuple CreateDiagnosticProviderAndFixer(Workspace workspace) { return new Tuple( new UserDiagnosticAnalyzer(), new CSharpSuppressionCodeFixProvider()); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestSuppressionOnSimpleType() { var initial = @" using System; // Some trivia /* More Trivia */ [|class Class|] { int Method() { int x = 0; } }"; var expected = $@" using System; // Some trivia /* More Trivia */ [System.Diagnostics.CodeAnalysis.SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"")] class Class {{ int Method() {{ int x = 0; }} }}"; await TestAsync(initial, expected); // Also verify that the added attribute does indeed suppress the diagnostic. expected = expected.Replace("class Class", "[|class Class|]"); await TestMissingAsync(expected); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestSuppressionOnSimpleType2() { // Type already has attributes. var initial = @" using System; // Some trivia /* More Trivia */ [System.Diagnostics.CodeAnalysis.SuppressMessage(""SomeOtherDiagnostic"", ""SomeOtherDiagnostic:Title"", Justification = """")] [|class Class|] { int Method() { int x = 0; } }"; var expected = $@" using System; // Some trivia /* More Trivia */ [System.Diagnostics.CodeAnalysis.SuppressMessage(""SomeOtherDiagnostic"", ""SomeOtherDiagnostic:Title"", Justification = """")] [System.Diagnostics.CodeAnalysis.SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"")] class Class {{ int Method() {{ int x = 0; }} }}"; await TestAsync(initial, expected); // Also verify that the added attribute does indeed suppress the diagnostic. expected = expected.Replace("class Class", "[|class Class|]"); await TestMissingAsync(expected); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestSuppressionOnSimpleType3() { // Type already has attributes with trailing trivia. var initial = @" using System; // Some trivia /* More Trivia */ [System.Diagnostics.CodeAnalysis.SuppressMessage(""SomeOtherDiagnostic"", ""SomeOtherDiagnostic:Title"", Justification = """")] /* Some More Trivia */ [|class Class|] { int Method() { int x = 0; } }"; var expected = $@" using System; // Some trivia /* More Trivia */ [System.Diagnostics.CodeAnalysis.SuppressMessage(""SomeOtherDiagnostic"", ""SomeOtherDiagnostic:Title"", Justification = """")] [System.Diagnostics.CodeAnalysis.SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"")] /* Some More Trivia */ class Class {{ int Method() {{ int x = 0; }} }}"; await TestAsync(initial, expected); // Also verify that the added attribute does indeed suppress the diagnostic. expected = expected.Replace("class Class", "[|class Class|]"); await TestMissingAsync(expected); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestSuppressionOnTypeInsideNamespace() { var initial = @" using System; namespace N1 { namespace N2 { [|class Class|] { int Method() { int x = 0; } } } }"; var expected = $@" using System; namespace N1 {{ namespace N2 {{ [System.Diagnostics.CodeAnalysis.SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"")] class Class {{ int Method() {{ int x = 0; }} }} }} }}"; await TestAsync(initial, expected); // Also verify that the added attribute does indeed suppress the diagnostic. expected = expected.Replace("class Class", "[|class Class|]"); await TestMissingAsync(expected); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestSuppressionOnNestedType() { var initial = @" using System; namespace N { class Generic { [|class Class|] { int Method() { int x = 0; } } } }"; var expected = $@" using System; namespace N {{ class Generic {{ [System.Diagnostics.CodeAnalysis.SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"")] class Class {{ int Method() {{ int x = 0; }} }} }} }}"; await TestAsync(initial, expected); // Also verify that the added attribute does indeed suppress the diagnostic. expected = expected.Replace("class Class", "[|class Class|]"); await TestMissingAsync(expected); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] public async Task TestSuppressionOnMethod() { var initial = @" using System; namespace N { class Generic { class Class { [|int Method()|] { int x = 0; } } } }"; var expected = $@" using System; namespace N {{ class Generic {{ class Class {{ [System.Diagnostics.CodeAnalysis.SuppressMessage(""InfoDiagnostic"", ""InfoDiagnostic:InfoDiagnostic"", Justification = ""{FeaturesResources.Pending}"")] int Method() {{ int x = 0; }} }} }} }}"; await TestAsync(initial, expected); // Also verify that the added attribute does indeed suppress the diagnostic. expected = expected.Replace("int Method()", "[|int Method()|]"); await TestMissingAsync(expected); } } } #endregion #region NoLocation Diagnostics tests public partial class CSharpDiagnosticWithoutLocationSuppressionTests : CSharpSuppressionTests { private class UserDiagnosticAnalyzer : DiagnosticAnalyzer { public static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor("NoLocationDiagnostic", "NoLocationDiagnostic", "NoLocationDiagnostic", "NoLocationDiagnostic", DiagnosticSeverity.Info, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(Descriptor); } } public override void Initialize(AnalysisContext context) => context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ClassDeclaration); public void AnalyzeNode(SyntaxNodeAnalysisContext context) => context.ReportDiagnostic(Diagnostic.Create(Descriptor, Location.None)); } internal override Tuple CreateDiagnosticProviderAndFixer(Workspace workspace) { return new Tuple( new UserDiagnosticAnalyzer(), new CSharpSuppressionCodeFixProvider()); } protected override int CodeActionIndex { get { return 0; } } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSuppression)] [WorkItem(1073825, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1073825")] public async Task TestDiagnosticWithoutLocationCanBeSuppressed() { await TestAsync( @"[||] using System; class Class { int Method() { int x = 0; } }", $@"// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage(""NoLocationDiagnostic"", ""NoLocationDiagnostic:NoLocationDiagnostic"", Justification = ""{FeaturesResources.Pending}"")] "); } } #endregion } }