99using Analyzer . Utilities ;
1010using Analyzer . Utilities . Extensions ;
1111using Microsoft . CodeAnalysis ;
12- using Microsoft . CodeAnalysis . CSharp ;
13- using Microsoft . CodeAnalysis . CSharp . Syntax ;
1412using Microsoft . CodeAnalysis . Diagnostics ;
1513using Microsoft . CodeAnalysis . FlowAnalysis ;
1614using Microsoft . CodeAnalysis . Operations ;
1715
1816namespace Roslyn . Diagnostics . Analyzers
1917{
20- [ DiagnosticAnalyzer ( LanguageNames . CSharp , LanguageNames . VisualBasic ) ]
21- public sealed class DoNotCopyValue : DiagnosticAnalyzer
18+ public abstract class AbstractDoNotCopyValue : DiagnosticAnalyzer
2219 {
2320 private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString ( nameof ( RoslynDiagnosticsAnalyzersResources . DoNotCopyValueTitle ) , RoslynDiagnosticsAnalyzersResources . ResourceManager , typeof ( RoslynDiagnosticsAnalyzersResources ) ) ;
2421 private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString ( nameof ( RoslynDiagnosticsAnalyzersResources . DoNotCopyValueMessage ) , RoslynDiagnosticsAnalyzersResources . ResourceManager , typeof ( RoslynDiagnosticsAnalyzersResources ) ) ;
@@ -93,6 +90,8 @@ public sealed class DoNotCopyValue : DiagnosticAnalyzer
9390
9491 public override ImmutableArray < DiagnosticDescriptor > SupportedDiagnostics => ImmutableArray . Create ( Rule , UnsupportedUseRule , NoBoxingRule , NoUnboxingRule ) ;
9592
93+ protected abstract NonCopyableWalker CreateWalker ( OperationBlockAnalysisContext context , NonCopyableTypesCache cache ) ;
94+
9695 public override void Initialize ( AnalysisContext context )
9796 {
9897 context . EnableConcurrentExecution ( ) ;
@@ -105,9 +104,9 @@ public override void Initialize(AnalysisContext context)
105104 } ) ;
106105 }
107106
108- private static void AnalyzeOperationBlock ( OperationBlockAnalysisContext context , NonCopyableTypesCache cache )
107+ private void AnalyzeOperationBlock ( OperationBlockAnalysisContext context , NonCopyableTypesCache cache )
109108 {
110- var walker = new NonCopyableWalker ( context , cache ) ;
109+ var walker = CreateWalker ( context , cache ) ;
111110 foreach ( var operation in context . OperationBlocks )
112111 {
113112 walker . Visit ( operation ) ;
@@ -149,18 +148,21 @@ public void Dispose()
149148 }
150149 }
151150
152- private sealed class NonCopyableWalker : OperationWalker
151+ protected abstract class NonCopyableWalker : OperationWalker
153152 {
154153 private readonly OperationBlockAnalysisContext _context ;
155- private readonly NonCopyableTypesCache _cache ;
156154 private readonly HashSet < IOperation > _handledOperations = new HashSet < IOperation > ( ) ;
157155
158- public NonCopyableWalker ( OperationBlockAnalysisContext context , NonCopyableTypesCache cache )
156+ protected NonCopyableWalker ( OperationBlockAnalysisContext context , NonCopyableTypesCache cache )
159157 {
160158 _context = context ;
161- _cache = cache ;
159+ Cache = cache ;
162160 }
163161
162+ protected NonCopyableTypesCache Cache { get ; }
163+
164+ protected abstract bool CheckForEachGetEnumerator ( IForEachLoopOperation operation , [ DisallowNull ] ref IConversionOperation ? conversion , [ DisallowNull ] ref IOperation ? instance ) ;
165+
164166 public override void VisitAddressOf ( IAddressOfOperation operation )
165167 {
166168 CheckTypeInUnsupportedContext ( operation ) ;
@@ -222,11 +224,11 @@ public override void VisitAwait(IAwaitOperation operation)
222224 {
223225 // Treat await of ValueTask<T> the same way handling of a return
224226 if ( operation . Type is { } type
225- && _cache . IsNonCopyableType ( type )
227+ && Cache . IsNonCopyableType ( type )
226228 && operation . Operation . Type is INamedTypeSymbol { OriginalDefinition : var taskType } )
227229 {
228- if ( ! SymbolEqualityComparer . Default . Equals ( taskType , _cache . ValueTaskT )
229- && ! SymbolEqualityComparer . Default . Equals ( taskType , _cache . ConfiguredValueTaskAwaitableT ) )
230+ if ( ! SymbolEqualityComparer . Default . Equals ( taskType , Cache . ValueTaskT )
231+ && ! SymbolEqualityComparer . Default . Equals ( taskType , Cache . ConfiguredValueTaskAwaitableT ) )
230232 {
231233 CheckTypeInUnsupportedContext ( operation ) ;
232234 }
@@ -566,21 +568,7 @@ public override void VisitForEachLoop(IForEachLoopOperation operation)
566568 else
567569 {
568570 // Treat this as an invocation of the GetEnumerator method.
569- if ( operation . Syntax is CommonForEachStatementSyntax syntax
570- && operation . SemanticModel . GetForEachStatementInfo ( syntax ) . GetEnumeratorMethod is { } getEnumeratorMethod )
571- {
572- CheckMethodSymbolInUnsupportedContext ( operation , getEnumeratorMethod ) ;
573-
574- if ( instance2 is not null
575- && _cache . IsNonCopyableType ( getEnumeratorMethod . ReceiverType )
576- && ! getEnumeratorMethod . IsReadOnly
577- && Acquire ( instance ) == RefKind . In )
578- {
579- // mark the instance as not checked by this method
580- instance2 = null ;
581- }
582- }
583- else
571+ if ( ! CheckForEachGetEnumerator ( operation , ref instance , ref instance2 ) )
584572 {
585573 // Not supported
586574 instance = null ;
@@ -669,7 +657,7 @@ public override void VisitInvocation(IInvocationOperation operation)
669657
670658 var instance = operation . Instance ;
671659 if ( instance is object
672- && _cache . IsNonCopyableType ( operation . TargetMethod . ReceiverType )
660+ && Cache . IsNonCopyableType ( operation . TargetMethod . ReceiverType )
673661 && ! operation . TargetMethod . IsReadOnly
674662 && Acquire ( instance ) == RefKind . In )
675663 {
@@ -837,7 +825,7 @@ public override void VisitPropertyReference(IPropertyReferenceOperation operatio
837825
838826 var instance = operation . Instance ;
839827 if ( instance is object
840- && _cache . IsNonCopyableType ( operation . Property . ContainingType )
828+ && Cache . IsNonCopyableType ( operation . Property . ContainingType )
841829 && Acquire ( instance ) == RefKind . In )
842830 {
843831 if ( operation . IsSetMethodInvocation ( ) )
@@ -1144,7 +1132,7 @@ private static bool CanAssign(RefKind sourceRefKind, RefKind targetRefKind)
11441132 } ;
11451133 }
11461134
1147- private RefKind Acquire ( IOperation ? operation )
1135+ protected RefKind Acquire ( IOperation ? operation )
11481136 {
11491137 if ( operation is null )
11501138 return RefKind . RefReadOnly ;
@@ -1338,7 +1326,7 @@ private void CheckTypeSymbolInUnsupportedContext(IOperation operation, ITypeSymb
13381326 if ( type . OriginalDefinition . SpecialType == SpecialType . System_Nullable_T )
13391327 {
13401328 var nullableUnderlyingType = ( ( INamedTypeSymbol ) type ) . TypeArguments . FirstOrDefault ( ) ;
1341- if ( _cache . IsNonCopyableType ( nullableUnderlyingType ) )
1329+ if ( Cache . IsNonCopyableType ( nullableUnderlyingType ) )
13421330 {
13431331 _context . ReportDiagnostic ( operation . Syntax . CreateDiagnostic ( AvoidNullableWrapperRule , type , operation . Kind ) ) ;
13441332 }
@@ -1380,7 +1368,7 @@ private void CheckLocalSymbolInUnsupportedContext(IOperation operation, ILocalSy
13801368 CheckTypeSymbolInUnsupportedContext ( operation , local . Type ) ;
13811369 }
13821370
1383- private void CheckMethodSymbolInUnsupportedContext ( IOperation operation , IMethodSymbol ? symbol )
1371+ protected void CheckMethodSymbolInUnsupportedContext ( IOperation operation , IMethodSymbol ? symbol )
13841372 {
13851373 if ( symbol is null )
13861374 return ;
@@ -1424,7 +1412,7 @@ private void CheckTypeInUnsupportedContext(IOperation operation)
14241412 return ;
14251413 }
14261414
1427- if ( ! _cache . IsNonCopyableType ( symbol ) )
1415+ if ( ! Cache . IsNonCopyableType ( symbol ) )
14281416 {
14291417 // Copies of this type are allowed
14301418 return ;
@@ -1434,7 +1422,7 @@ private void CheckTypeInUnsupportedContext(IOperation operation)
14341422 }
14351423 }
14361424
1437- private sealed class NonCopyableTypesCache
1425+ protected sealed class NonCopyableTypesCache
14381426 {
14391427 private readonly ConcurrentDictionary < INamedTypeSymbol , bool > _typesToNonCopyable
14401428 = new ConcurrentDictionary < INamedTypeSymbol , bool > ( ) ;
0 commit comments