1313using Microsoft . CodeAnalysis . CodeQuality ;
1414using Microsoft . CodeAnalysis . CodeStyle ;
1515using Microsoft . CodeAnalysis . Diagnostics ;
16+ using Microsoft . CodeAnalysis . LanguageService ;
1617using Microsoft . CodeAnalysis . Operations ;
1718using Microsoft . CodeAnalysis . PooledObjects ;
1819using Microsoft . CodeAnalysis . Shared . Extensions ;
@@ -25,8 +26,11 @@ internal abstract class AbstractRemoveUnusedMembersDiagnosticAnalyzer<
2526 TDocumentationCommentTriviaSyntax ,
2627 TIdentifierNameSyntax ,
2728 TTypeDeclarationSyntax ,
28- TMemberDeclarationSyntax >
29- : AbstractCodeQualityDiagnosticAnalyzer
29+ TMemberDeclarationSyntax > ( )
30+ : AbstractCodeQualityDiagnosticAnalyzer (
31+ [ s_removeUnusedMembersRule , s_removeUnreadMembersRule ] ,
32+ // We want to analyze references in generated code, but not report unused members in generated code.
33+ GeneratedCodeAnalysisFlags . Analyze )
3034 where TDocumentationCommentTriviaSyntax : SyntaxNode
3135 where TIdentifierNameSyntax : SyntaxNode
3236 where TTypeDeclarationSyntax : TMemberDeclarationSyntax
@@ -56,11 +60,7 @@ internal abstract class AbstractRemoveUnusedMembersDiagnosticAnalyzer<
5660 new LocalizableResourceString ( nameof ( AnalyzersResources . Private_member_0_can_be_removed_as_the_value_assigned_to_it_is_never_read ) , AnalyzersResources . ResourceManager , typeof ( AnalyzersResources ) ) ,
5761 hasAnyCodeStyleOption : false , isUnnecessary : true ) ;
5862
59- protected AbstractRemoveUnusedMembersDiagnosticAnalyzer ( )
60- : base ( [ s_removeUnusedMembersRule , s_removeUnreadMembersRule ] ,
61- GeneratedCodeAnalysisFlags . Analyze ) // We want to analyze references in generated code, but not report unused members in generated code.
62- {
63- }
63+ protected abstract ISemanticFacts SemanticFacts { get ; }
6464
6565 protected abstract IEnumerable < TTypeDeclarationSyntax > GetTypeDeclarations ( INamedTypeSymbol namedType , CancellationToken cancellationToken ) ;
6666 protected abstract SyntaxList < TMemberDeclarationSyntax > GetMembers ( TTypeDeclarationSyntax typeDeclaration ) ;
@@ -86,7 +86,8 @@ protected virtual void HandleNamedTypeSymbolStart(SymbolStartAnalysisContext con
8686
8787 private sealed class CompilationAnalyzer
8888 {
89- private readonly object _gate ;
89+ private readonly object _gate = new ( ) ;
90+
9091 /// <summary>
9192 /// State map for candidate member symbols, with the value indicating how each symbol is used in executable code.
9293 /// </summary>
@@ -114,7 +115,6 @@ private CompilationAnalyzer(
114115 Compilation compilation ,
115116 AbstractRemoveUnusedMembersDiagnosticAnalyzer < TDocumentationCommentTriviaSyntax , TIdentifierNameSyntax , TTypeDeclarationSyntax , TMemberDeclarationSyntax > analyzer )
116117 {
117- _gate = new object ( ) ;
118118 _analyzer = analyzer ;
119119
120120 _taskType = compilation . TaskType ( ) ;
@@ -204,15 +204,17 @@ private void RegisterActions(CompilationStartAnalysisContext compilationStartCon
204204 symbolStartContext . RegisterOperationAction ( AnalyzeInvocationOperation , OperationKind . Invocation ) ;
205205 symbolStartContext . RegisterOperationAction ( AnalyzeNameOfOperation , OperationKind . NameOf ) ;
206206 symbolStartContext . RegisterOperationAction ( AnalyzeObjectCreationOperation , OperationKind . ObjectCreation ) ;
207+ symbolStartContext . RegisterOperationAction ( AnalyzeLoopOperation , OperationKind . Loop ) ;
207208
208209 // We bail out reporting diagnostics for named types if it contains following kind of operations:
209210 // 1. Invalid operations, i.e. erroneous code:
210211 // We do so to ensure that we don't report false positives during editing scenarios in the IDE, where the user
211212 // is still editing code and fixing unresolved references to symbols, such as overload resolution errors.
212213 // 2. Dynamic operations, where we do not know the exact member being referenced at compile time.
213214 // 3. Operations with OperationKind.None.
214- symbolStartContext . RegisterOperationAction ( _ => hasUnsupportedOperation = true , OperationKind . Invalid , OperationKind . None ,
215- OperationKind . DynamicIndexerAccess , OperationKind . DynamicInvocation , OperationKind . DynamicMemberReference , OperationKind . DynamicObjectCreation ) ;
215+ symbolStartContext . RegisterOperationAction (
216+ _ => hasUnsupportedOperation = true ,
217+ OperationKind . Invalid , OperationKind . None , OperationKind . DynamicIndexerAccess , OperationKind . DynamicInvocation , OperationKind . DynamicMemberReference , OperationKind . DynamicObjectCreation ) ;
216218
217219 symbolStartContext . RegisterSymbolEndAction ( symbolEndContext => OnSymbolEnd ( symbolEndContext , hasUnsupportedOperation ) ) ;
218220
@@ -369,6 +371,18 @@ memberReference.Parent is IIncrementOrDecrementOperation ||
369371 }
370372 }
371373
374+ private void AnalyzeLoopOperation ( OperationAnalysisContext operationContext )
375+ {
376+ var operation = operationContext . Operation ;
377+ if ( operation is not IForEachLoopOperation loopOperation )
378+ return ;
379+
380+ var symbols = _analyzer . SemanticFacts . GetForEachSymbols ( operation . SemanticModel ! , loopOperation . Syntax ) ;
381+ OnSymbolUsage ( symbols . CurrentProperty , ValueUsageInfo . Read ) ;
382+ OnSymbolUsage ( symbols . GetEnumeratorMethod , ValueUsageInfo . Read ) ;
383+ OnSymbolUsage ( symbols . MoveNextMethod , ValueUsageInfo . Read ) ;
384+ }
385+
372386 private void AnalyzeInvocationOperation ( OperationAnalysisContext operationContext )
373387 {
374388 var targetMethod = ( ( IInvocationOperation ) operationContext . Operation ) . TargetMethod . OriginalDefinition ;
@@ -380,9 +394,7 @@ private void AnalyzeInvocationOperation(OperationAnalysisContext operationContex
380394 // If the invoked method is a reduced extension method, also mark the original
381395 // method from which it was reduced as "used".
382396 if ( targetMethod . ReducedFrom != null )
383- {
384397 OnSymbolUsage ( targetMethod . ReducedFrom , ValueUsageInfo . Read ) ;
385- }
386398 }
387399
388400 private void AnalyzeNameOfOperation ( OperationAnalysisContext operationContext )
0 commit comments