Skip to content

Commit 807db56

Browse files
committed
Add support for skipping analyzing banned API analysis in generated files
Adds a new `.editorconfig`/`.globalconfig` options for the banned API analyzer to skip analyzing generated code. Closes #82114.
1 parent 06bff44 commit 807db56

7 files changed

Lines changed: 595 additions & 18 deletions

File tree

src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/CSharpSymbolIsBannedInAnalyzersAnalyzer.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,8 @@ public sealed class CSharpSymbolIsBannedInAnalyzersAnalyzer : SymbolIsBannedInAn
2323
protected override SyntaxNode GetReferenceSyntaxNodeFromXmlCref(SyntaxNode syntaxNode) => ((XmlCrefAttributeSyntax)syntaxNode).Cref;
2424

2525
protected override IEnumerable<SyntaxNode> GetTypeSyntaxNodesFromBaseType(SyntaxNode syntaxNode) => ((BaseListSyntax)syntaxNode).Types.Select(t => (SyntaxNode)t.Type);
26+
27+
protected override bool IsRegularCommentOrDocumentationComment(SyntaxTrivia trivia)
28+
=> trivia.Kind() is SyntaxKind.SingleLineCommentTrivia or SyntaxKind.MultiLineCommentTrivia or SyntaxKind.ShebangDirectiveTrivia or SyntaxKind.SingleLineDocumentationCommentTrivia or SyntaxKind.MultiLineDocumentationCommentTrivia;
2629
}
2730
}

src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/BasicSymbolIsBannedInAnalyzersAnalyzer.vb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Analyzers
4444
End If
4545
End Function
4646

47+
Protected Overrides Function IsRegularCommentOrDocumentationComment(trivia As SyntaxTrivia) As Boolean
48+
Return trivia.Kind() = SyntaxKind.CommentTrivia OrElse trivia.Kind() = SyntaxKind.DocumentationCommentTrivia
49+
End Function
50+
4751
End Class
4852
End Namespace

src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ This can be done by:
1616
</ItemGroup>
1717
```
1818

19+
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`:
20+
21+
```ini
22+
is_global = true
23+
24+
banned_api_analyzer.exclude_generated_code = true
25+
```
26+
1927
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):
2028

2129
```txt

src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/CSharp/CSharpSymbolIsBannedAnalyzer.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ public sealed class CSharpSymbolIsBannedAnalyzer : SymbolIsBannedAnalyzer<Syntax
2020

2121
protected override SymbolDisplayFormat SymbolDisplayFormat => SymbolDisplayFormat.CSharpShortErrorMessageFormat;
2222

23+
protected override bool IsRegularCommentOrDocumentationComment(SyntaxTrivia trivia)
24+
=> trivia.Kind() is SyntaxKind.SingleLineCommentTrivia or SyntaxKind.MultiLineCommentTrivia or SyntaxKind.ShebangDirectiveTrivia or SyntaxKind.SingleLineDocumentationCommentTrivia or SyntaxKind.MultiLineDocumentationCommentTrivia;
25+
2326
protected override SyntaxNode GetReferenceSyntaxNodeFromXmlCref(SyntaxNode syntaxNode) => ((XmlCrefAttributeSyntax)syntaxNode).Cref;
2427

2528
protected override IEnumerable<SyntaxNode> GetTypeSyntaxNodesFromBaseType(SyntaxNode syntaxNode) => ((BaseListSyntax)syntaxNode).Types.Select(t => (SyntaxNode)t.Type);

src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/SymbolIsBannedAnalyzerBase.cs

Lines changed: 67 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System;
6+
using System.Collections.Concurrent;
67
using System.Collections.Generic;
78
using System.Collections.Immutable;
89
using System.Diagnostics.CodeAnalysis;
@@ -13,12 +14,15 @@
1314
using Microsoft.CodeAnalysis.Operations;
1415
using Microsoft.CodeAnalysis.Shared.Extensions;
1516
using Microsoft.CodeAnalysis.Text;
17+
using Roslyn.Utilities;
1618

1719
namespace Microsoft.CodeAnalysis.BannedApiAnalyzers
1820
{
1921
public abstract class SymbolIsBannedAnalyzerBase<TSyntaxKind> : DiagnosticAnalyzer
2022
where TSyntaxKind : struct
2123
{
24+
private const string ExcludeGeneratedCodeOptionName = "banned_api_analyzer.exclude_generated_code";
25+
2226
protected abstract Dictionary<(string ContainerName, string SymbolName), ImmutableArray<BanFileEntry>>? ReadBannedApis(CompilationStartAnalysisContext compilationContext);
2327

2428
protected abstract DiagnosticDescriptor SymbolIsBannedRule { get; }
@@ -33,6 +37,8 @@ public abstract class SymbolIsBannedAnalyzerBase<TSyntaxKind> : DiagnosticAnalyz
3337

3438
protected abstract SymbolDisplayFormat SymbolDisplayFormat { get; }
3539

40+
protected abstract bool IsRegularCommentOrDocumentationComment(SyntaxTrivia trivia);
41+
3642
public override void Initialize(AnalysisContext context)
3743
{
3844
context.EnableConcurrentExecution();
@@ -49,6 +55,8 @@ private void OnCompilationStart(CompilationStartAnalysisContext compilationConte
4955
if (bannedApis == null || bannedApis.Count == 0)
5056
return;
5157

58+
var excludeGeneratedCodeMap = new ConcurrentDictionary<SyntaxTree, bool>();
59+
5260
if (ShouldAnalyzeAttributes())
5361
{
5462
compilationContext.RegisterCompilationEndAction(
@@ -59,7 +67,10 @@ private void OnCompilationStart(CompilationStartAnalysisContext compilationConte
5967
});
6068

6169
compilationContext.RegisterSymbolAction(
62-
context => VerifyAttributes(context.ReportDiagnostic, context.Symbol.GetAttributes(), context.CancellationToken),
70+
context =>
71+
{
72+
VerifyAttributes(context.ReportDiagnostic, context.Symbol.GetAttributes(), context.CancellationToken);
73+
},
6374
SymbolKind.NamedType,
6475
SymbolKind.Method,
6576
SymbolKind.Field,
@@ -71,6 +82,9 @@ private void OnCompilationStart(CompilationStartAnalysisContext compilationConte
7182
context =>
7283
{
7384
context.CancellationToken.ThrowIfCancellationRequested();
85+
if (ShouldSkipOperationAnalysis(context))
86+
return;
87+
7488
switch (context.Operation)
7589
{
7690
case IObjectCreationOperation objectCreation:
@@ -153,11 +167,23 @@ private void OnCompilationStart(CompilationStartAnalysisContext compilationConte
153167
OperationKind.TypeOf);
154168

155169
compilationContext.RegisterSyntaxNodeAction(
156-
context => VerifyDocumentationSyntax(context.ReportDiagnostic, GetReferenceSyntaxNodeFromXmlCref(context.Node), context),
170+
context =>
171+
{
172+
if (ShouldSkipSyntaxNodeAnalysis(context))
173+
return;
174+
175+
VerifyDocumentationSyntax(context.ReportDiagnostic, GetReferenceSyntaxNodeFromXmlCref(context.Node), context);
176+
},
157177
XmlCrefSyntaxKind);
158178

159179
compilationContext.RegisterSyntaxNodeAction(
160-
context => VerifyBaseTypesSyntax(context.ReportDiagnostic, GetTypeSyntaxNodesFromBaseType(context.Node), context),
180+
context =>
181+
{
182+
if (ShouldSkipSyntaxNodeAnalysis(context))
183+
return;
184+
185+
VerifyBaseTypesSyntax(context.ReportDiagnostic, GetTypeSyntaxNodesFromBaseType(context.Node), context);
186+
},
161187
BaseTypeSyntaxKinds);
162188

163189
return;
@@ -219,32 +245,55 @@ bool ContainsAttributeSymbol(ISymbol symbol)
219245
};
220246
}
221247

248+
bool ShouldSkipOperationAnalysis(OperationAnalysisContext context)
249+
=> context.IsGeneratedCode && ExcludesGeneratedCode(context.Operation.Syntax.SyntaxTree);
250+
251+
bool ShouldSkipSyntaxNodeAnalysis(SyntaxNodeAnalysisContext context)
252+
=> context.IsGeneratedCode && ExcludesGeneratedCode(context.Node.SyntaxTree);
253+
254+
bool ShouldSkipTreeAnalysis(SyntaxTree tree, CancellationToken cancellationToken)
255+
=> ExcludesGeneratedCode(tree) && IsGeneratedCode(tree, cancellationToken);
256+
257+
bool ExcludesGeneratedCode(SyntaxTree tree)
258+
=> excludeGeneratedCodeMap.GetOrAdd(
259+
tree,
260+
tree =>
261+
{
262+
var options = compilationContext.Options.AnalyzerConfigOptionsProvider.GetOptions(tree);
263+
return options.TryGetValue(ExcludeGeneratedCodeOptionName, out var optionValue) &&
264+
bool.TryParse(optionValue, out var excludeGeneratedCode) &&
265+
excludeGeneratedCode;
266+
});
267+
268+
bool IsGeneratedCode(SyntaxTree tree, CancellationToken cancellationToken)
269+
=> GeneratedCodeUtilities.GetGeneratedCodeKindFromOptions(compilationContext.Options.AnalyzerConfigOptionsProvider.GetOptions(tree)).ToNullable() ??
270+
GeneratedCodeUtilities.IsGeneratedCode(tree, IsRegularCommentOrDocumentationComment, cancellationToken);
271+
222272
void VerifyAttributes(Action<Diagnostic> reportDiagnostic, ImmutableArray<AttributeData> attributes, CancellationToken cancellationToken)
223273
{
224274
cancellationToken.ThrowIfCancellationRequested();
225275
foreach (var attribute in attributes)
226276
{
277+
var applicationSyntaxReference = attribute.ApplicationSyntaxReference;
278+
if (applicationSyntaxReference == null)
279+
continue;
280+
281+
var node = applicationSyntaxReference.GetSyntax(cancellationToken);
282+
if (ShouldSkipTreeAnalysis(node.SyntaxTree, cancellationToken))
283+
continue;
284+
227285
if (IsBannedSymbol(attribute.AttributeClass, out var entry))
228286
{
229-
var node = attribute.ApplicationSyntaxReference?.GetSyntax(cancellationToken);
230-
if (node != null)
231-
{
232-
reportDiagnostic(
233-
node.CreateDiagnostic(
234-
SymbolIsBannedRule,
235-
attribute.AttributeClass.ToDisplayString(),
236-
string.IsNullOrWhiteSpace(entry.Message) ? "" : ": " + entry.Message));
237-
}
287+
reportDiagnostic(
288+
node.CreateDiagnostic(
289+
SymbolIsBannedRule,
290+
attribute.AttributeClass.ToDisplayString(),
291+
string.IsNullOrWhiteSpace(entry.Message) ? "" : ": " + entry.Message));
238292
}
239293

240294
if (attribute.AttributeConstructor != null)
241295
{
242-
var syntaxNode = attribute.ApplicationSyntaxReference?.GetSyntax(cancellationToken);
243-
244-
if (syntaxNode != null)
245-
{
246-
VerifySymbol(reportDiagnostic, attribute.AttributeConstructor, syntaxNode);
247-
}
296+
VerifySymbol(reportDiagnostic, attribute.AttributeConstructor, node);
248297
}
249298
}
250299
}

0 commit comments

Comments
 (0)