Support EnumeratorCancellationAttribute in local functions#40959
Conversation
| { | ||
| private readonly ImmutableArray<CustomModifier> _refCustomModifiers; | ||
| private readonly ImmutableArray<CSharpAttributeData> _attributes; | ||
| private readonly ParameterSymbol _baseParameter; |
There was a problem hiding this comment.
nit: consider enabling nullability analysis in this file, or asserting that baseParameter is not null in constructor. #Resolved
jcouv
left a comment
There was a problem hiding this comment.
Done with review pass (iteration 1). Thanks!
|
@RikkiGibson Do you know the reason for CI failures? #Closed |
|
EE was using the old signatures of the internal SynthesizedParameterSymbol.Create methods. Working on how to rationalize the design/hierarchy of the synthesized parameter symbols in light of this. #Resolved |
src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs
Outdated
Show resolved
Hide resolved
src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs
Outdated
Show resolved
Hide resolved
src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs
Outdated
Show resolved
Hide resolved
|
Done with review pass (iteration 1). #Closed |
src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs
Outdated
Show resolved
Hide resolved
src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs
Outdated
Show resolved
Hide resolved
| } | ||
|
|
||
| // Called after all method attributes, return attributes, and parameter attributes have been initialized on this method. | ||
| protected void PostDecodeAllMethodAttributes(DiagnosticBag diagnostics) |
There was a problem hiding this comment.
PostDecodeAllMethodAttributes [](start = 23, length = 29)
nit: could be given a more specific name at the moment, since it only checks EnumeratorCancellation attribute #Resolved
src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs
Outdated
Show resolved
Hide resolved
| if (enumeratorCancellationCount == 0 && | ||
| ParameterTypesWithAnnotations.Any(p => p.Type.Equals(cancellationTokenType))) | ||
| { | ||
| // Warn for CancellationToken parameters in async-iterators with no parameter decorated with [EnumeratorCancellation] |
There was a problem hiding this comment.
async-iterators [](start = 68, length = 15)
@jcouv I assume you added this code originally, where do we make sure this is an iterator?
There was a problem hiding this comment.
There was a problem hiding this comment.
@AlekseyTs From our discussion, there are three factors to make an async-iterator: (1) async, (2) the return type, and (3) a yield in the body.
But we enforce that you cannot have (1) and (2) without having (3). We produce an error in that case (see test below). So in other places in the code, we can just check for (1) and (2).
I have a vague recollection that there was some technical benefit to this approach, but I'm not longer sure.
@RikkiGibson Would you mind adding a comment in the code that you're moving to capture this? I'd add it in master, but that would only create a conflict for when you'll merge...
[Fact]
[WorkItem(31552, "https://github.com/dotnet/roslyn/issues/31552")]
public void AsyncIterator_WithThrowOnly()
{
string source = @"
class C
{
public static async System.Collections.Generic.IAsyncEnumerable<int> M()
{
throw new System.NotImplementedException();
}
}";
var comp = CreateCompilationWithAsyncIterator(source);
comp.VerifyDiagnostics(
// (4,74): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
// public static async System.Collections.Generic.IAsyncEnumerable<int> M()
Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 74)
);
comp.VerifyEmitDiagnostics(
// (4,74): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
// public static async System.Collections.Generic.IAsyncEnumerable<int> M()
Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 74),
// (4,74): error CS8420: The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.
// public static async System.Collections.Generic.IAsyncEnumerable<int> M()
Diagnostic(ErrorCode.ERR_PossibleAsyncIteratorWithoutYieldOrAwait, "M").WithArguments("System.Collections.Generic.IAsyncEnumerable<int>").WithLocation(4, 74)
);
var m = comp.SourceModule.GlobalNamespace.GetMember<MethodSymbol>("C.M");
Assert.False(m.IsIterator);
Assert.True(m.IsAsync);
}| } | ||
| } | ||
|
|
||
| PostDecodeAllMethodAttributes(diagnostics); |
There was a problem hiding this comment.
PostDecodeAllMethodAttributes [](start = 12, length = 29)
It feels like "AfterReturnTypeBoundAndParametersAreCreated" is more precise name. However, I don't think we need a dedicated method for this operation. In fact, I think that this is not the right place for this kind of checks. We haven't finalized the signature of the method yet and are likely to mutate it below. It feels like this check belongs in LazyAsyncMethodChecks, which is called in the right time. Also, it looks like for local functions the checks performed by LazyAsyncMethodChecks are duplicated. I think it would be good to consolidate that and avoid code duplication. #Closed
| GetAttributes(); | ||
| GetReturnTypeAttributes(); | ||
|
|
||
| PostDecodeAllMethodAttributes(_declarationDiagnostics); |
There was a problem hiding this comment.
PostDecodeAllMethodAttributes(_declarationDiagnostics); [](start = 12, length = 55)
Should consolidate with other async-related checks performed for local functions and probably share code with regular methods so that there was one, well defined place where we perform async-related checks. #Closed
|
Done with review pass (iteration 5) #Closed |
|
@AlekseyTs Please let me know if I have adequately addressed your comments. |
| { | ||
| foreach (var parameter in Parameters) | ||
| { | ||
| var loc = parameter.Locations.FirstOrDefault() ?? location; |
There was a problem hiding this comment.
parameter.Locations.FirstOrDefault() ?? location [](start = 26, length = 48)
Consider calculating this lazily, i.e. only when we have something to report.
Related to #38801