11// Licensed to the .NET Foundation under one or more agreements.
22// The .NET Foundation licenses this file to you under the MIT license.
33
4- using System ;
54using System . Collections . Immutable ;
6- using System . Linq ;
75using Analyzer . Utilities ;
86using Analyzer . Utilities . Extensions ;
97using Microsoft . CodeAnalysis ;
@@ -54,7 +52,10 @@ public override void Initialize(AnalysisContext context)
5452 return ;
5553 }
5654
55+ var iAsyncDisposable = wellKnownTypeProvider . GetOrCreateTypeByMetadataName ( WellKnownTypeNames . SystemIAsyncDisposable ) ;
5756 var configuredAsyncDisposable = wellKnownTypeProvider . GetOrCreateTypeByMetadataName ( WellKnownTypeNames . SystemRuntimeCompilerServicesConfiguredAsyncDisposable ) ;
57+
58+ var iAsyncEnumerable = wellKnownTypeProvider . GetOrCreateTypeByMetadataName ( WellKnownTypeNames . SystemCollectionsGenericIAsyncEnumerable1 ) ;
5859 var configuredAsyncEnumerable = wellKnownTypeProvider . GetOrCreateTypeByMetadataName ( WellKnownTypeNames . SystemRuntimeCompilerServicesConfiguredCancelableAsyncEnumerable ) ;
5960
6061 context . RegisterOperationBlockStartAction ( context =>
@@ -75,27 +76,33 @@ public override void Initialize(AnalysisContext context)
7576 }
7677
7778 context . RegisterOperationAction ( context => AnalyzeAwaitOperation ( context , taskTypes ) , OperationKind . Await ) ;
78- if ( configuredAsyncDisposable is not null )
79+
80+ if ( iAsyncDisposable is not null && configuredAsyncDisposable is not null )
7981 {
80- context . RegisterOperationAction ( context => AnalyzeUsingOperation ( context , configuredAsyncDisposable ) , OperationKind . Using ) ;
81- context . RegisterOperationAction ( context => AnalyzeUsingDeclarationOperation ( context , configuredAsyncDisposable ) , OperationKind . UsingDeclaration ) ;
82+ context . RegisterOperationAction ( context => AnalyzeUsingOperation ( context , iAsyncDisposable , configuredAsyncDisposable ) , OperationKind . Using ) ;
83+ context . RegisterOperationAction ( context => AnalyzeUsingDeclarationOperation ( context , iAsyncDisposable , configuredAsyncDisposable ) , OperationKind . UsingDeclaration ) ;
8284 }
8385
84- if ( configuredAsyncEnumerable is not null )
86+ if ( iAsyncEnumerable is not null && configuredAsyncEnumerable is not null )
8587 {
86- context . RegisterOperationAction ( ctx => AnalyzeAwaitForEachLoopOperation ( ctx , configuredAsyncEnumerable ) , OperationKind . Loop ) ;
88+ context . RegisterOperationAction ( ctx => AnalyzeAwaitForEachLoopOperation ( ctx , iAsyncEnumerable , configuredAsyncEnumerable ) , OperationKind . Loop ) ;
8789 }
8890 }
8991 } ) ;
9092 } ) ;
9193 }
9294
93- private static void AnalyzeAwaitForEachLoopOperation ( OperationAnalysisContext context , INamedTypeSymbol configuredAsyncEnumerable )
95+ private static void AnalyzeAwaitForEachLoopOperation ( OperationAnalysisContext context , INamedTypeSymbol iAsyncEnumerable , INamedTypeSymbol configuredAsyncEnumerable )
9496 {
95- if ( context . Operation is IForEachLoopOperation { IsAsynchronous : true , Collection . Type : not null } forEachOperation
96- && ! forEachOperation . Collection . Type . OriginalDefinition . Equals ( configuredAsyncEnumerable , SymbolEqualityComparer . Default ) )
97+ if ( context . Operation is IForEachLoopOperation { IsAsynchronous : true , Collection . Type : not null } forEachOperation )
9798 {
98- context . ReportDiagnostic ( forEachOperation . Collection . CreateDiagnostic ( Rule ) ) ;
99+ var collectionTypeOriginalDefinition = forEachOperation . Collection . Type . OriginalDefinition ;
100+
101+ if ( ! collectionTypeOriginalDefinition . Equals ( configuredAsyncEnumerable , SymbolEqualityComparer . Default ) &&
102+ context . Compilation . ClassifyCommonConversion ( collectionTypeOriginalDefinition , iAsyncEnumerable ) is { Exists : true , IsImplicit : true } )
103+ {
104+ context . ReportDiagnostic ( forEachOperation . Collection . CreateDiagnostic ( Rule ) ) ;
105+ }
99106 }
100107 }
101108
@@ -111,7 +118,7 @@ private static void AnalyzeAwaitOperation(OperationAnalysisContext context, Immu
111118 }
112119 }
113120
114- private static void AnalyzeUsingOperation ( OperationAnalysisContext context , INamedTypeSymbol configuredAsyncDisposable )
121+ private static void AnalyzeUsingOperation ( OperationAnalysisContext context , INamedTypeSymbol iAsyncDisposable , INamedTypeSymbol configuredAsyncDisposable )
115122 {
116123 var usingExpression = ( IUsingOperation ) context . Operation ;
117124 if ( ! usingExpression . IsAsynchronous )
@@ -121,12 +128,16 @@ private static void AnalyzeUsingOperation(OperationAnalysisContext context, INam
121128
122129 if ( usingExpression . Resources is IVariableDeclarationGroupOperation variableDeclarationGroup )
123130 {
131+ var compilation = context . Compilation ;
132+
124133 foreach ( var declaration in variableDeclarationGroup . Declarations )
125134 {
126135 foreach ( var declarator in declaration . Declarators )
127136 {
128- // Get the type of the expression being awaited and check it's a task type.
129- if ( declarator . Symbol . Type != configuredAsyncDisposable )
137+ var declaratorSymbolType = declarator . Symbol . Type ;
138+
139+ if ( ! declaratorSymbolType . Equals ( configuredAsyncDisposable , SymbolEqualityComparer . Default ) &&
140+ compilation . ClassifyCommonConversion ( declaratorSymbolType , iAsyncDisposable ) is { Exists : true , IsImplicit : true } )
130141 {
131142 var reportingOperation = declarator . Initializer ? . Value ?? declarator ;
132143 context . ReportDiagnostic ( reportingOperation . CreateDiagnostic ( Rule ) ) ;
@@ -136,20 +147,24 @@ private static void AnalyzeUsingOperation(OperationAnalysisContext context, INam
136147 }
137148 }
138149
139- private static void AnalyzeUsingDeclarationOperation ( OperationAnalysisContext context , INamedTypeSymbol configuredAsyncDisposable )
150+ private static void AnalyzeUsingDeclarationOperation ( OperationAnalysisContext context , INamedTypeSymbol iAsyncDisposable , INamedTypeSymbol configuredAsyncDisposable )
140151 {
141152 var usingExpression = ( IUsingDeclarationOperation ) context . Operation ;
142153 if ( ! usingExpression . IsAsynchronous )
143154 {
144155 return ;
145156 }
146157
158+ var compilation = context . Compilation ;
159+
147160 foreach ( var declaration in usingExpression . DeclarationGroup . Declarations )
148161 {
149162 foreach ( var declarator in declaration . Declarators )
150163 {
151- // Get the type of the expression being awaited and check it's a task type.
152- if ( declarator . Symbol . Type != configuredAsyncDisposable )
164+ var declaratorSymbolType = declarator . Symbol . Type ;
165+
166+ if ( ! declaratorSymbolType . Equals ( configuredAsyncDisposable , SymbolEqualityComparer . Default ) &&
167+ compilation . ClassifyCommonConversion ( declaratorSymbolType , iAsyncDisposable ) is { Exists : true , IsImplicit : true } )
153168 {
154169 var reportingOperation = declarator . Initializer ? . Value ?? declarator ;
155170 context . ReportDiagnostic ( reportingOperation . CreateDiagnostic ( Rule ) ) ;
0 commit comments