33// See the LICENSE file in the project root for more information.
44
55using System ;
6+ using System . Collections . Concurrent ;
67using System . Collections . Generic ;
78using System . Collections . Immutable ;
89using System . Diagnostics . CodeAnalysis ;
1314using Microsoft . CodeAnalysis . Operations ;
1415using Microsoft . CodeAnalysis . Shared . Extensions ;
1516using Microsoft . CodeAnalysis . Text ;
17+ using Roslyn . Utilities ;
1618
1719namespace 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