@@ -759,62 +759,78 @@ private async Task<bool> ProcessSuppressMessageAttributesAsync(
759
759
continue ;
760
760
}
761
761
762
- var symbols = SemanticFacts . GetDeclaredSymbols ( semanticModel , node , cancellationToken ) ;
763
- foreach ( var symbol in symbols )
762
+ // In the case of declaration nodes that can have more than one symbol e.g. fields and events,
763
+ // the attributes are shared between then. Given this, we only need to inspect the first symbol
764
+ // of the node.
765
+ var symbol = SemanticFacts
766
+ . GetDeclaredSymbols ( semanticModel , node , cancellationToken )
767
+ . FirstOrDefault ( ) ;
768
+
769
+ // If we somehow do not have a symbol, we can't do anything. Otherwise, check if our symbol is
770
+ // a partial definition. If it is, skip it in favor of checking the implementation.
771
+ if ( symbol is null or
772
+ IMethodSymbol { IsPartialDefinition : true } or
773
+ IPropertySymbol { IsPartialDefinition : true } )
764
774
{
765
- switch ( symbol ? . Kind )
766
- {
767
- // Local SuppressMessageAttributes are only applicable for types and members.
768
- case SymbolKind . NamedType :
769
- case SymbolKind . Method :
770
- case SymbolKind . Field :
771
- case SymbolKind . Property :
772
- case SymbolKind . Event :
773
- break ;
774
-
775
- default :
776
- continue ;
777
- }
775
+ continue ;
776
+ }
778
777
779
- // Skip already processed symbols from partial declarations
780
- var isPartial = symbol . Locations . Length > 1 ;
781
- if ( isPartial && ! processedPartialSymbols . Add ( symbol ) )
782
- {
778
+ switch ( symbol ? . Kind )
779
+ {
780
+ // Local SuppressMessageAttributes are only applicable for types and members.
781
+ case SymbolKind . NamedType :
782
+ case SymbolKind . Method :
783
+ case SymbolKind . Field :
784
+ case SymbolKind . Property :
785
+ case SymbolKind . Event :
786
+ break ;
787
+
788
+ default :
783
789
continue ;
784
- }
790
+ }
791
+
792
+ // Skip already processed symbols from partial declarations
793
+ var isPartial = symbol . Locations . Length > 1 ;
785
794
786
- foreach ( var attribute in symbol . GetAttributes ( ) )
795
+ if ( isPartial && ! processedPartialSymbols . Add ( symbol ) )
796
+ {
797
+ continue ;
798
+ }
799
+
800
+ foreach ( var attribute in symbol . GetAttributes ( ) )
801
+ {
802
+ if ( attribute . ApplicationSyntaxReference != null &&
803
+ TryGetSuppressedDiagnosticId ( attribute , suppressMessageAttributeType , out var id , out var category ) )
787
804
{
788
- if ( attribute . ApplicationSyntaxReference != null &&
789
- TryGetSuppressedDiagnosticId ( attribute , suppressMessageAttributeType , out var id , out var category ) )
805
+ // Ignore unsupported IDs and those excluded through user option.
806
+ if ( ! IsSupportedAnalyzerDiagnosticId ( id ) ||
807
+ userIdExclusions . Contains ( id , StringComparer . OrdinalIgnoreCase ) ||
808
+ category ? . Length > 0 && userCategoryExclusions . Contains ( category , StringComparer . OrdinalIgnoreCase ) )
809
+ {
810
+ continue ;
811
+ }
812
+
813
+ if ( ! idToSuppressMessageAttributesMap . TryGetValue ( id , out var nodesForId ) )
790
814
{
791
- // Ignore unsupported IDs and those excluded through user option.
792
- if ( ! IsSupportedAnalyzerDiagnosticId ( id ) ||
793
- userIdExclusions . Contains ( id , StringComparer . OrdinalIgnoreCase ) ||
794
- category ? . Length > 0 && userCategoryExclusions . Contains ( category , StringComparer . OrdinalIgnoreCase ) )
795
- {
796
- continue ;
797
- }
798
-
799
- if ( ! idToSuppressMessageAttributesMap . TryGetValue ( id , out var nodesForId ) )
800
- {
801
- nodesForId = [ ] ;
802
- idToSuppressMessageAttributesMap . Add ( id , nodesForId ) ;
803
- }
804
-
805
- var attributeNode = await attribute . ApplicationSyntaxReference . GetSyntaxAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
806
- nodesForId . Add ( attributeNode ) ;
807
-
808
- // Initialize the attribute node as unnecessary at the start of the algorithm.
809
- // Later processing will identify attributes which are indeed responsible for suppressing diagnostics
810
- // and mark them as used.
811
- // NOTE: For attributes on partial symbols with multiple declarations, we conservatively
812
- // consider them as used and avoid unnecessary attribute analysis because that would potentially
813
- // require analysis across multiple files, which can be expensive from a performance standpoint.
814
- suppressMessageAttributesToIsUsedMap . Add ( attributeNode , isPartial ) ;
815
+ nodesForId = [ ] ;
816
+ idToSuppressMessageAttributesMap . Add ( id , nodesForId ) ;
815
817
}
818
+
819
+ var attributeNode = await attribute . ApplicationSyntaxReference . GetSyntaxAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
820
+ nodesForId . Add ( attributeNode ) ;
821
+
822
+ // Initialize the attribute node as unnecessary at the start of the algorithm.
823
+ // Later processing will identify attributes which are indeed responsible for suppressing diagnostics
824
+ // and mark them as used.
825
+ // NOTE: For attributes on partial symbols with multiple declarations, we conservatively
826
+ // consider them as used and avoid unnecessary attribute analysis because that would potentially
827
+ // require analysis across multiple files, which can be expensive from a performance standpoint.
828
+ suppressMessageAttributesToIsUsedMap . Add ( attributeNode , isPartial ) ;
816
829
}
817
830
}
831
+
832
+ // Individual variables within a variable declaration cannot be decorated with distinct attributes, so we
833
+ // should avoid looking at any of the subsequent symbols for this node as they will be the same.
818
834
}
819
835
}
820
836
0 commit comments