-
Notifications
You must be signed in to change notification settings - Fork 467
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Suggest CA2007 for await foreach without specified cancellation #6683
Conversation
public virtual (Action<OperationAnalysisContext> Analysis, OperationKind OperationKind) AnalyzeAwaitForEachLoopOperation(INamedTypeSymbol configuredAsyncEnumerable) | ||
{ | ||
return (_ => { }, OperationKind.None); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better to make this method return void
and do the actual registeration.
protected virtual void RegisterLanguageSpecificChecks(OperationBlockStartAnalysisContext context, INamedTypeSymbol configuredAsyncEnumerable)
{
}
and in C# implementation:
protected override void RegisterLanguageSpecificChecks(OperationBlockStartAnalysisContext context, INamedTypeSymbol configuredAsyncEnumerable)
{
context.RegisterOperationAction(context => AnalyzeAwaitForEachLoopOperation(context, configuredAsyncEnumerable), OperationKind.Loop);
}
Note: I'm writing directly in GitHub, so I could have made an error.
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] | ||
public sealed class DoNotDirectlyAwaitATaskAnalyzer : DiagnosticAnalyzer | ||
[DiagnosticAnalyzer(LanguageNames.VisualBasic)] | ||
public class DoNotDirectlyAwaitATaskAnalyzer : DiagnosticAnalyzer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd make this abstract, and have VB-derived class.
|
||
private static void AnalyzeAwaitForEachLoopOperation(OperationAnalysisContext context, INamedTypeSymbol configuredAsyncEnumerable) | ||
{ | ||
if (context.Operation is IForEachLoopOperation {Syntax: ForEachStatementSyntax {AwaitKeyword.RawKind: not (int)SyntaxKind.None}} forEachOperation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't recall what version of Roslyn we do use in this repo, but is this API not available? https://learn.microsoft.com/en-us/dotnet/api/microsoft.codeanalysis.operations.iforeachloopoperation.isasynchronous?view=roslyn-dotnet-3.9.0#microsoft-codeanalysis-operations-iforeachloopoperation-isasynchronous
Would it make sense to use it via lightup?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should upgrade to newer version of Microsoft.CodeAnalysis package reference for the NetAnalyzers project. We don't really need to support this analyzer package on very old versions of VS/compiler. I'll create a separate PR for this, and remove any old lightup code that was added to work around this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
if (context.Operation is IForEachLoopOperation {Syntax: ForEachStatementSyntax {AwaitKeyword.RawKind: not (int)SyntaxKind.None}} forEachOperation | ||
&& !forEachOperation.Collection.Type.OriginalDefinition.Equals(configuredAsyncEnumerable, SymbolEqualityComparer.Default)) | ||
{ | ||
context.ReportDiagnostic(forEachOperation.Collection.Syntax.CreateDiagnostic(Rule)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
context.ReportDiagnostic(forEachOperation.Collection.Syntax.CreateDiagnostic(Rule)); | |
context.ReportDiagnostic(forEachOperation.Collection.CreateDiagnostic(Rule)); |
@@ -52,7 +53,9 @@ public override void Initialize(AnalysisContext context) | |||
return; | |||
} | |||
|
|||
var configuredAsyncDisposable = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeCompilerServicesConfiguredAsyncDisposable); | |||
var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be a good idea to move this variable before TryGetTaskTypes
, and pass it instead of passing context.Compilation.
My preference is to take earlier pull request #5377 instead of this pull request, provided it remains viable |
# Conflicts: # src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif # src/Utilities/Compiler/WellKnownTypeNames.cs
Codecov Report
@@ Coverage Diff @@
## main #6683 +/- ##
==========================================
- Coverage 96.43% 96.42% -0.01%
==========================================
Files 1410 1410
Lines 336154 336238 +84
Branches 11103 11106 +3
==========================================
+ Hits 324158 324230 +72
- Misses 9200 9212 +12
Partials 2796 2796 |
# Conflicts: # src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif
protected override void RegisterLanguageSpecificChecks(OperationBlockStartAnalysisContext context, INamedTypeSymbol configuredAsyncEnumerable) | ||
{ | ||
context.RegisterOperationAction(ctx => AnalyzeAwaitForEachLoopOperation(ctx, configuredAsyncEnumerable), OperationKind.Loop); | ||
} | ||
|
||
private static void AnalyzeAwaitForEachLoopOperation(OperationAnalysisContext context, INamedTypeSymbol configuredAsyncEnumerable) | ||
{ | ||
if (context.Operation is IForEachLoopOperation { IsAsynchronous: true, Collection.Type: not null } forEachOperation | ||
&& !forEachOperation.Collection.Type.OriginalDefinition.Equals(configuredAsyncEnumerable, SymbolEqualityComparer.Default)) | ||
{ | ||
context.ReportDiagnostic(forEachOperation.Collection.CreateDiagnostic(Rule)); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@CollinAlpert all the code here seems to be compilable at the shared layer. I understand only C# supports async foreach loops, but even having this code in the shared layer will do the right thing by bailing out early for VB. I would move this code down to the shared layer and we can definitely conclude that this PR is preferable over #5377
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
Thanks! |
This PR extends the
DoNotDirectlyAwaitATask
analyzer forIAsyncEnumerable
. Let me know if there is an easier way to check for anawait foreach
loop, I found this method to be quite cumbersome.Fixes #6652.