diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/CSharpSymbolIsBannedInAnalyzersAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/CSharpSymbolIsBannedInAnalyzersAnalyzer.cs index 3224024b6332f..b9bf7a0ecb016 100644 --- a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/CSharpSymbolIsBannedInAnalyzersAnalyzer.cs +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/CSharpSymbolIsBannedInAnalyzersAnalyzer.cs @@ -23,5 +23,8 @@ public sealed class CSharpSymbolIsBannedInAnalyzersAnalyzer : SymbolIsBannedInAn protected override SyntaxNode GetReferenceSyntaxNodeFromXmlCref(SyntaxNode syntaxNode) => ((XmlCrefAttributeSyntax)syntaxNode).Cref; protected override IEnumerable GetTypeSyntaxNodesFromBaseType(SyntaxNode syntaxNode) => ((BaseListSyntax)syntaxNode).Types.Select(t => (SyntaxNode)t.Type); + + protected override bool IsRegularCommentOrDocumentationComment(SyntaxTrivia trivia) + => trivia.Kind() is SyntaxKind.SingleLineCommentTrivia or SyntaxKind.MultiLineCommentTrivia or SyntaxKind.ShebangDirectiveTrivia or SyntaxKind.SingleLineDocumentationCommentTrivia or SyntaxKind.MultiLineDocumentationCommentTrivia; } } diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/BasicSymbolIsBannedInAnalyzersAnalyzer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/BasicSymbolIsBannedInAnalyzersAnalyzer.vb index 10e6384925e7c..5ad773750ba10 100644 --- a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/BasicSymbolIsBannedInAnalyzersAnalyzer.vb +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/BasicSymbolIsBannedInAnalyzersAnalyzer.vb @@ -44,5 +44,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Analyzers End If End Function + Protected Overrides Function IsRegularCommentOrDocumentationComment(trivia As SyntaxTrivia) As Boolean + Return trivia.Kind() = SyntaxKind.CommentTrivia OrElse trivia.Kind() = SyntaxKind.DocumentationCommentTrivia + End Function + End Class End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md index cf031e98eb1bd..2a95432ba7db5 100644 --- a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md @@ -16,6 +16,14 @@ This can be done by: ``` +Generated code is analyzed by default. To exclude generated code from banned API analysis, add the following to an analyzer config file such as `.globalconfig`: + +```ini +is_global = true + +banned_api_analyzer.exclude_generated_code = true +``` + To add a symbol to the banned list, just add an entry in the format below to one of the configuration files (Description Text will be displayed as description in diagnostics, which is optional): ```txt diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/CSharp/CSharpSymbolIsBannedAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/CSharp/CSharpSymbolIsBannedAnalyzer.cs index 7a8262626fd09..9a99d881521f9 100644 --- a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/CSharp/CSharpSymbolIsBannedAnalyzer.cs +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/CSharp/CSharpSymbolIsBannedAnalyzer.cs @@ -20,6 +20,9 @@ public sealed class CSharpSymbolIsBannedAnalyzer : SymbolIsBannedAnalyzer SymbolDisplayFormat.CSharpShortErrorMessageFormat; + protected override bool IsRegularCommentOrDocumentationComment(SyntaxTrivia trivia) + => trivia.Kind() is SyntaxKind.SingleLineCommentTrivia or SyntaxKind.MultiLineCommentTrivia or SyntaxKind.ShebangDirectiveTrivia or SyntaxKind.SingleLineDocumentationCommentTrivia or SyntaxKind.MultiLineDocumentationCommentTrivia; + protected override SyntaxNode GetReferenceSyntaxNodeFromXmlCref(SyntaxNode syntaxNode) => ((XmlCrefAttributeSyntax)syntaxNode).Cref; protected override IEnumerable GetTypeSyntaxNodesFromBaseType(SyntaxNode syntaxNode) => ((BaseListSyntax)syntaxNode).Types.Select(t => (SyntaxNode)t.Type); diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/SymbolIsBannedAnalyzerBase.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/SymbolIsBannedAnalyzerBase.cs index 8d6e96c742b64..4bc53a639b41c 100644 --- a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/SymbolIsBannedAnalyzerBase.cs +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/SymbolIsBannedAnalyzerBase.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; @@ -13,12 +14,15 @@ using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.BannedApiAnalyzers { public abstract class SymbolIsBannedAnalyzerBase : DiagnosticAnalyzer where TSyntaxKind : struct { + private const string ExcludeGeneratedCodeOptionName = "banned_api_analyzer.exclude_generated_code"; + protected abstract Dictionary<(string ContainerName, string SymbolName), ImmutableArray>? ReadBannedApis(CompilationStartAnalysisContext compilationContext); protected abstract DiagnosticDescriptor SymbolIsBannedRule { get; } @@ -33,6 +37,8 @@ public abstract class SymbolIsBannedAnalyzerBase : DiagnosticAnalyz protected abstract SymbolDisplayFormat SymbolDisplayFormat { get; } + protected abstract bool IsRegularCommentOrDocumentationComment(SyntaxTrivia trivia); + public override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); @@ -49,17 +55,22 @@ private void OnCompilationStart(CompilationStartAnalysisContext compilationConte if (bannedApis == null || bannedApis.Count == 0) return; + var generatedCodeOptionsMap = new ConcurrentDictionary(); + if (ShouldAnalyzeAttributes()) { compilationContext.RegisterCompilationEndAction( context => { - VerifyAttributes(context.ReportDiagnostic, compilationContext.Compilation.Assembly.GetAttributes(), context.CancellationToken); - VerifyAttributes(context.ReportDiagnostic, compilationContext.Compilation.SourceModule.GetAttributes(), context.CancellationToken); + VerifyAttributes(context.ReportDiagnostic, compilationContext.Compilation.Assembly.GetAttributes(), isGeneratedCode: null, context.CancellationToken); + VerifyAttributes(context.ReportDiagnostic, compilationContext.Compilation.SourceModule.GetAttributes(), isGeneratedCode: null, context.CancellationToken); }); compilationContext.RegisterSymbolAction( - context => VerifyAttributes(context.ReportDiagnostic, context.Symbol.GetAttributes(), context.CancellationToken), + context => + { + VerifyAttributes(context.ReportDiagnostic, context.Symbol.GetAttributes(), context.IsGeneratedCode, context.CancellationToken); + }, SymbolKind.NamedType, SymbolKind.Method, SymbolKind.Field, @@ -71,6 +82,9 @@ private void OnCompilationStart(CompilationStartAnalysisContext compilationConte context => { context.CancellationToken.ThrowIfCancellationRequested(); + if (ShouldSkipOperationAnalysis(context)) + return; + switch (context.Operation) { case IObjectCreationOperation objectCreation: @@ -153,11 +167,23 @@ private void OnCompilationStart(CompilationStartAnalysisContext compilationConte OperationKind.TypeOf); compilationContext.RegisterSyntaxNodeAction( - context => VerifyDocumentationSyntax(context.ReportDiagnostic, GetReferenceSyntaxNodeFromXmlCref(context.Node), context), + context => + { + if (ShouldSkipSyntaxNodeAnalysis(context)) + return; + + VerifyDocumentationSyntax(context.ReportDiagnostic, GetReferenceSyntaxNodeFromXmlCref(context.Node), context); + }, XmlCrefSyntaxKind); compilationContext.RegisterSyntaxNodeAction( - context => VerifyBaseTypesSyntax(context.ReportDiagnostic, GetTypeSyntaxNodesFromBaseType(context.Node), context), + context => + { + if (ShouldSkipSyntaxNodeAnalysis(context)) + return; + + VerifyBaseTypesSyntax(context.ReportDiagnostic, GetTypeSyntaxNodesFromBaseType(context.Node), context); + }, BaseTypeSyntaxKinds); return; @@ -219,32 +245,64 @@ bool ContainsAttributeSymbol(ISymbol symbol) }; } - void VerifyAttributes(Action reportDiagnostic, ImmutableArray attributes, CancellationToken cancellationToken) + bool ShouldSkipOperationAnalysis(OperationAnalysisContext context) + => !ShouldAnalyzeInTree(context.Operation.Syntax.SyntaxTree, context.IsGeneratedCode, context.CancellationToken); + + bool ShouldSkipSyntaxNodeAnalysis(SyntaxNodeAnalysisContext context) + => !ShouldAnalyzeInTree(context.Node.SyntaxTree, context.IsGeneratedCode, context.CancellationToken); + + bool ShouldSkipTreeAnalysis(SyntaxTree tree, bool? isGeneratedCode, CancellationToken cancellationToken) + => !ShouldAnalyzeInTree(tree, isGeneratedCode, cancellationToken); + + bool ShouldAnalyzeInTree(SyntaxTree tree, bool? isGeneratedCode, CancellationToken cancellationToken) + { + var generatedCodeOptions = generatedCodeOptionsMap.GetOrAdd( + tree, + tree => + { + var options = compilationContext.Options.AnalyzerConfigOptionsProvider.GetOptions(tree); + var excludesGeneratedCode = + options.TryGetValue(ExcludeGeneratedCodeOptionName, out var optionValue) && + bool.TryParse(optionValue, out var excludeGeneratedCode) && + excludeGeneratedCode; + return (excludesGeneratedCode, GeneratedCodeUtilities.GetGeneratedCodeKindFromOptions(options).ToNullable()); + }); + + return !generatedCodeOptions.ExcludesGeneratedCode || + !(isGeneratedCode ?? + generatedCodeOptions.IsGeneratedCodeFromOptions ?? + GeneratedCodeUtilities.IsGeneratedCode(tree, IsRegularCommentOrDocumentationComment, cancellationToken)); + } + + void VerifyAttributes(Action reportDiagnostic, ImmutableArray attributes, bool? isGeneratedCode, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); foreach (var attribute in attributes) { + var applicationSyntaxReference = attribute.ApplicationSyntaxReference; + if (applicationSyntaxReference == null) + continue; + + if (applicationSyntaxReference.SyntaxTree == null) + continue; + + if (ShouldSkipTreeAnalysis(applicationSyntaxReference.SyntaxTree, isGeneratedCode, cancellationToken)) + continue; + + var node = applicationSyntaxReference.GetSyntax(cancellationToken); + if (IsBannedSymbol(attribute.AttributeClass, out var entry)) { - var node = attribute.ApplicationSyntaxReference?.GetSyntax(cancellationToken); - if (node != null) - { - reportDiagnostic( - node.CreateDiagnostic( - SymbolIsBannedRule, - attribute.AttributeClass.ToDisplayString(), - string.IsNullOrWhiteSpace(entry.Message) ? "" : ": " + entry.Message)); - } + reportDiagnostic( + node.CreateDiagnostic( + SymbolIsBannedRule, + attribute.AttributeClass.ToDisplayString(), + string.IsNullOrWhiteSpace(entry.Message) ? "" : ": " + entry.Message)); } if (attribute.AttributeConstructor != null) { - var syntaxNode = attribute.ApplicationSyntaxReference?.GetSyntax(cancellationToken); - - if (syntaxNode != null) - { - VerifySymbol(reportDiagnostic, attribute.AttributeConstructor, syntaxNode); - } + VerifySymbol(reportDiagnostic, attribute.AttributeConstructor, node); } } } diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/UnitTests/SymbolIsBannedAnalyzerTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/UnitTests/SymbolIsBannedAnalyzerTests.cs index 876acc5ed77c9..64ca88a1d8ea8 100644 --- a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/UnitTests/SymbolIsBannedAnalyzerTests.cs +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/UnitTests/SymbolIsBannedAnalyzerTests.cs @@ -63,6 +63,706 @@ private static async Task VerifyCSharpAnalyzerAsync(string source, string banned await test.RunAsync(); } + [Fact] + [WorkItem(82114, "https://github.com/dotnet/roslyn/issues/82114")] + public async Task DiagnosticReportedInGeneratedFileByDefaultAsync() + { + const string bannedText = "T:N.Banned"; + + var csharpTest = new VerifyCS.Test + { + TestState = + { + Sources = + { + ("Generated.g.cs", """ + namespace N + { + class Banned + { + } + + class C + { + void M() + { + var c = {|#0:new Banned()|}; + } + } + } + """), + }, + AdditionalFiles = { (BannedSymbolsFileName, bannedText) }, + }, + }; + + csharpTest.ExpectedDiagnostics.Add(GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", "")); + await csharpTest.RunAsync(); + + var basicTest = new VerifyVB.Test + { + TestState = + { + Sources = + { + ("Generated.g.vb", """ + Namespace N + Class Banned + End Class + + Class C + Sub M() + Dim c = {|#0:New Banned()|} + End Sub + End Class + End Namespace + """), + }, + AdditionalFiles = { (BannedSymbolsFileName, bannedText) }, + }, + }; + + basicTest.ExpectedDiagnostics.Add(GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", "")); + await basicTest.RunAsync(); + } + + [Fact] + [WorkItem(82114, "https://github.com/dotnet/roslyn/issues/82114")] + public async Task GeneratedFileSkippedWhenConfiguredAsync() + { + const string bannedText = "T:N.Banned"; + + var csharpTest = new VerifyCS.Test + { + TestState = + { + Sources = + { + ("/0/Shared.cs", """ + namespace N + { + class Banned + { + } + } + """), + ("/0/Generated.g.cs", """ + namespace N + { + class GeneratedUsage + { + void M() + { + var c = {|#0:new Banned()|}; + } + } + } + """), + ("/0/Test0.cs", """ + namespace N + { + class NongeneratedUsage + { + void M() + { + var c = {|#1:new Banned()|}; + } + } + } + """), + }, + AdditionalFiles = { (BannedSymbolsFileName, bannedText) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", """ + is_global = true + + banned_api_analyzer.exclude_generated_code = true + """), + }, + }, + }; + + csharpTest.ExpectedDiagnostics.Add(GetCSharpResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", "")); + await csharpTest.RunAsync(); + + var basicTest = new VerifyVB.Test + { + TestState = + { + Sources = + { + ("/0/Shared.vb", """ + Namespace N + Class Banned + End Class + End Namespace + """), + ("/0/Generated.g.vb", """ + Namespace N + Class GeneratedUsage + Sub M() + Dim c = {|#0:New Banned()|} + End Sub + End Class + End Namespace + """), + ("/0/Test0.vb", """ + Namespace N + Class NongeneratedUsage + Sub M() + Dim c = {|#1:New Banned()|} + End Sub + End Class + End Namespace + """), + }, + AdditionalFiles = { (BannedSymbolsFileName, bannedText) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", """ + is_global = true + + banned_api_analyzer.exclude_generated_code = true + """), + }, + }, + }; + + basicTest.ExpectedDiagnostics.Add(GetBasicResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", "")); + await basicTest.RunAsync(); + } + + [Fact] + [WorkItem(82114, "https://github.com/dotnet/roslyn/issues/82114")] + public async Task GeneratedFileSkippedWhenConfiguredInEditorConfigAsync() + { + const string bannedText = "T:N.Banned"; + + var csharpTest = new VerifyCS.Test + { + TestState = + { + Sources = + { + ("/0/Shared.cs", """ + namespace N + { + class Banned + { + } + } + """), + ("/0/Generated.g.cs", """ + namespace N + { + class GeneratedUsage + { + void M() + { + var c = {|#0:new Banned()|}; + } + } + } + """), + ("/0/Test0.cs", """ + namespace N + { + class NongeneratedUsage + { + void M() + { + var c = {|#1:new Banned()|}; + } + } + } + """), + }, + AdditionalFiles = { (BannedSymbolsFileName, bannedText) }, + AnalyzerConfigFiles = + { + ("/.editorconfig", """ + root = true + + [*] + banned_api_analyzer.exclude_generated_code = true + """), + }, + }, + }; + + csharpTest.ExpectedDiagnostics.Add(GetCSharpResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", "")); + await csharpTest.RunAsync(); + + var basicTest = new VerifyVB.Test + { + TestState = + { + Sources = + { + ("/0/Shared.vb", """ + Namespace N + Class Banned + End Class + End Namespace + """), + ("/0/Generated.g.vb", """ + Namespace N + Class GeneratedUsage + Sub M() + Dim c = {|#0:New Banned()|} + End Sub + End Class + End Namespace + """), + ("/0/Test0.vb", """ + Namespace N + Class NongeneratedUsage + Sub M() + Dim c = {|#1:New Banned()|} + End Sub + End Class + End Namespace + """), + }, + AdditionalFiles = { (BannedSymbolsFileName, bannedText) }, + AnalyzerConfigFiles = + { + ("/.editorconfig", """ + root = true + + [*] + banned_api_analyzer.exclude_generated_code = true + """), + }, + }, + }; + + basicTest.ExpectedDiagnostics.Add(GetBasicResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", "")); + await basicTest.RunAsync(); + } + + [Fact] + [WorkItem(82114, "https://github.com/dotnet/roslyn/issues/82114")] + public async Task GeneratedFileSkippedWhenConfiguredInEditorConfigForSpecificPathAsync() + { + const string bannedText = "T:N.Banned"; + + var csharpTest = new VerifyCS.Test + { + TestState = + { + Sources = + { + ("/0/Shared.cs", """ + namespace N + { + class Banned + { + } + } + """), + ("/0/Generated/Matched.g.cs", """ + namespace N + { + class MatchedGeneratedUsage + { + void M() + { + var c = {|#0:new Banned()|}; + } + } + } + """), + ("/0/Generated/Other.g.cs", """ + namespace N + { + class OtherGeneratedUsage + { + void M() + { + var c = {|#1:new Banned()|}; + } + } + } + """), + ("/0/Generated/Test0.cs", """ + namespace N + { + class NongeneratedUsage + { + void M() + { + var c = {|#2:new Banned()|}; + } + } + } + """), + }, + AdditionalFiles = { (BannedSymbolsFileName, bannedText) }, + AnalyzerConfigFiles = + { + ("/.editorconfig", """ + root = true + + [*] + banned_api_analyzer.exclude_generated_code = false + + [/0/Generated/Matched.g.cs] + banned_api_analyzer.exclude_generated_code = true + """), + }, + }, + }; + + csharpTest.ExpectedDiagnostics.Add(GetCSharpResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", "")); + csharpTest.ExpectedDiagnostics.Add(GetCSharpResultAt(2, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", "")); + await csharpTest.RunAsync(); + + var basicTest = new VerifyVB.Test + { + TestState = + { + Sources = + { + ("/0/Shared.vb", """ + Namespace N + Class Banned + End Class + End Namespace + """), + ("/0/Generated/Matched.g.vb", """ + Namespace N + Class MatchedGeneratedUsage + Sub M() + Dim c = {|#0:New Banned()|} + End Sub + End Class + End Namespace + """), + ("/0/Generated/Other.g.vb", """ + Namespace N + Class OtherGeneratedUsage + Sub M() + Dim c = {|#1:New Banned()|} + End Sub + End Class + End Namespace + """), + ("/0/Generated/Test0.vb", """ + Namespace N + Class NongeneratedUsage + Sub M() + Dim c = {|#2:New Banned()|} + End Sub + End Class + End Namespace + """), + }, + AdditionalFiles = { (BannedSymbolsFileName, bannedText) }, + AnalyzerConfigFiles = + { + ("/.editorconfig", """ + root = true + + [*] + banned_api_analyzer.exclude_generated_code = false + + [/0/Generated/Matched.g.vb] + banned_api_analyzer.exclude_generated_code = true + """), + }, + }, + }; + + basicTest.ExpectedDiagnostics.Add(GetBasicResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", "")); + basicTest.ExpectedDiagnostics.Add(GetBasicResultAt(2, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", "")); + await basicTest.RunAsync(); + } + + [Fact] + [WorkItem(82114, "https://github.com/dotnet/roslyn/issues/82114")] + public async Task GeneratedDeclarationDoesNotSkipNonGeneratedPartialSymbolAttributeAnalysisAsync() + { + const string bannedText = "T:BannedAttribute"; + + var csharpTest = new VerifyCS.Test + { + TestState = + { + Sources = + { + ("Generated.g.cs", """ + partial class C + { + } + """), + ("Shared.cs", """ + using System; + + [AttributeUsage(AttributeTargets.All, Inherited = true)] + class BannedAttribute : Attribute + { + } + """), + ("Test0.cs", """ + [{|#0:Banned|}] + partial class C + { + } + """), + }, + AdditionalFiles = { (BannedSymbolsFileName, bannedText) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", """ + is_global = true + + banned_api_analyzer.exclude_generated_code = true + """), + }, + }, + }; + + csharpTest.ExpectedDiagnostics.Add(GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedAttribute", "")); + csharpTest.ExpectedDiagnostics.Add(GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedAttribute", "")); + await csharpTest.RunAsync(); + + var basicTest = new VerifyVB.Test + { + TestState = + { + Sources = + { + ("Generated.g.vb", """ + Partial Class C + End Class + """), + ("Shared.vb", """ + Imports System + + + Class BannedAttribute + Inherits Attribute + End Class + """), + ("Test0.vb", """ + <{|#0:Banned|}> + Partial Class C + End Class + """), + }, + AdditionalFiles = { (BannedSymbolsFileName, bannedText) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", """ + is_global = true + + banned_api_analyzer.exclude_generated_code = true + """), + }, + }, + }; + + basicTest.ExpectedDiagnostics.Add(GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedAttribute", "")); + basicTest.ExpectedDiagnostics.Add(GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedAttribute", "")); + await basicTest.RunAsync(); + } + + [Fact] + [WorkItem(82114, "https://github.com/dotnet/roslyn/issues/82114")] + public async Task GeneratedDeclarationDoesNotSkipNonGeneratedPartialSymbolUsageAnalysisAsync() + { + const string bannedText = "T:N.Banned"; + + var csharpTest = new VerifyCS.Test + { + TestState = + { + Sources = + { + ("Generated.g.cs", """ + namespace N + { + partial class C + { + } + } + """), + ("Shared.cs", """ + namespace N + { + class Banned + { + } + } + """), + ("Test0.cs", """ + namespace N + { + partial class C + { + void M() + { + var c = {|#0:new Banned()|}; + } + } + } + """), + }, + AdditionalFiles = { (BannedSymbolsFileName, bannedText) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", """ + is_global = true + + banned_api_analyzer.exclude_generated_code = true + """), + }, + }, + }; + + csharpTest.ExpectedDiagnostics.Add(GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", "")); + await csharpTest.RunAsync(); + + var basicTest = new VerifyVB.Test + { + TestState = + { + Sources = + { + ("Generated.g.vb", """ + Namespace N + Partial Class C + End Class + End Namespace + """), + ("Shared.vb", """ + Namespace N + Class Banned + End Class + End Namespace + """), + ("Test0.vb", """ + Namespace N + Partial Class C + Sub M() + Dim c = {|#0:New Banned()|} + End Sub + End Class + End Namespace + """), + }, + AdditionalFiles = { (BannedSymbolsFileName, bannedText) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", """ + is_global = true + + banned_api_analyzer.exclude_generated_code = true + """), + }, + }, + }; + + basicTest.ExpectedDiagnostics.Add(GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", "")); + await basicTest.RunAsync(); + } + + [Fact] + [WorkItem(82114, "https://github.com/dotnet/roslyn/issues/82114")] + public async Task GeneratedDeclarationDoesNotSkipNonGeneratedPartialSymbolBaseTypeAnalysisAsync() + { + const string bannedText = "T:N.Banned"; + + var csharpTest = new VerifyCS.Test + { + TestState = + { + Sources = + { + ("Generated.g.cs", """ + namespace N + { + partial class C + { + } + } + """), + ("Shared.cs", """ + namespace N + { + class Banned + { + } + } + """), + ("Test0.cs", """ + namespace N + { + partial class C : {|#0:Banned|} + { + } + } + """), + }, + AdditionalFiles = { (BannedSymbolsFileName, bannedText) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", """ + is_global = true + + banned_api_analyzer.exclude_generated_code = true + """), + }, + }, + }; + + csharpTest.ExpectedDiagnostics.Add(GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", "")); + await csharpTest.RunAsync(); + + var basicTest = new VerifyVB.Test + { + TestState = + { + Sources = + { + ("Generated.g.vb", """ + Namespace N + Partial Class C + End Class + End Namespace + """), + ("Shared.vb", """ + Namespace N + Class Banned + End Class + End Namespace + """), + ("Test0.vb", """ + Namespace N + Partial Class C + Inherits {|#0:Banned|} + End Class + End Namespace + """), + }, + AdditionalFiles = { (BannedSymbolsFileName, bannedText) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", """ + is_global = true + + banned_api_analyzer.exclude_generated_code = true + """), + }, + }, + }; + + basicTest.ExpectedDiagnostics.Add(GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", "")); + await basicTest.RunAsync(); + } + #region Diagnostic tests [Fact] diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/VisualBasic/BasicSymbolIsBannedAnalyzer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/VisualBasic/BasicSymbolIsBannedAnalyzer.vb index 79db7b962f4fb..aa0a214d7cdd1 100644 --- a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/VisualBasic/BasicSymbolIsBannedAnalyzer.vb +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/VisualBasic/BasicSymbolIsBannedAnalyzer.vb @@ -30,6 +30,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.BannedApiAnalyzers End Get End Property + Protected Overrides Function IsRegularCommentOrDocumentationComment(trivia As SyntaxTrivia) As Boolean + Return trivia.Kind() = SyntaxKind.CommentTrivia OrElse trivia.Kind() = SyntaxKind.DocumentationCommentTrivia + End Function + Protected Overrides Function GetReferenceSyntaxNodeFromXmlCref(syntaxNode As SyntaxNode) As SyntaxNode Return CType(syntaxNode, XmlCrefAttributeSyntax).Reference End Function