Skip to content

Commit

Permalink
Delete CompletedTask, FromResult, and Fix Generator Cast Error (#895)
Browse files Browse the repository at this point in the history
  • Loading branch information
AnaCoda authored Jul 11, 2023
1 parent 50cec73 commit 549b42c
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public TransformResult<MethodDeclarationSyntax> Transform( MethodDeclarationSynt
decl = decl.WithAttributeLists( ReplaceGenerateSyncAttribute( decl.AttributeLists ) )
.WithModifiers( RemoveAsyncModifier( decl.Modifiers ) )
.WithIdentifier( RemoveAsyncSuffix( decl.Identifier ) )
.WithReturnType( TransformType( decl.ReturnType, optional: false ) )
.WithReturnType( TransformType( decl.ReturnType, isReturnType: true ) )
.WithExpressionBody( MaybeTransform( decl.ExpressionBody, Transform ) )
.WithBody( MaybeTransform( decl.Body, Transform ) );
return GetResult( decl );
Expand Down Expand Up @@ -75,7 +75,7 @@ private SyntaxToken RemoveAsyncSuffix( SyntaxToken ident, bool optional = false
).WithTriviaFrom( ident );
}

private TypeSyntax TransformType( TypeSyntax typeSynt, bool optional = true ) {
private TypeSyntax TransformType( TypeSyntax typeSynt, bool isReturnType = false ) {
var returnTypeInfo = Model.GetTypeInfo( typeSynt, Token );

if( returnTypeInfo.Type == null ) {
Expand All @@ -86,8 +86,7 @@ private TypeSyntax TransformType( TypeSyntax typeSynt, bool optional = true ) {
if( returnTypeInfo.Type.ContainingNamespace.ToString() == "System.Threading.Tasks" ) {
switch( returnTypeInfo.Type.MetadataName ) {
case "Task":
return SyntaxFactory.ParseTypeName( "void" )
.WithTriviaFrom( typeSynt );
return isReturnType ? SyntaxFactory.ParseTypeName( "void" ).WithTriviaFrom( typeSynt ) : typeSynt;
case "Task`1":
return ( (GenericNameSyntax)typeSynt )
.TypeArgumentList.Arguments.First()
Expand All @@ -102,7 +101,7 @@ private TypeSyntax TransformType( TypeSyntax typeSynt, bool optional = true ) {
}
}

if(!optional) { ReportDiagnostic( Diagnostics.NonTaskReturnType, typeSynt.GetLocation() ); }
if( isReturnType ) { ReportDiagnostic( Diagnostics.NonTaskReturnType, typeSynt.GetLocation() ); }
return typeSynt;
}

Expand Down Expand Up @@ -264,20 +263,36 @@ private StatementSyntax Transform( ExpressionStatementSyntax exprStmt ) {
return exprStmt.WithExpression( result );
}

private InvocationExpressionSyntax Transform( InvocationExpressionSyntax invocationExpr) {
private ExpressionSyntax Transform( InvocationExpressionSyntax invocationExpr) {
ExpressionSyntax newExpr = invocationExpr;
var memberAccess = invocationExpr.Expression as MemberAccessExpressionSyntax;

if( string.Equals( memberAccess?.Name?.Identifier.ValueText, "ConfigureAwait", StringComparison.Ordinal ) ) {
if( memberAccess?.Expression is not null ) {
invocationExpr = (InvocationExpressionSyntax)memberAccess.Expression;
newExpr = memberAccess.Expression;
return Transform( newExpr );
}
}
return invocationExpr
.WithExpression( Transform( invocationExpr.Expression ) )
.WithArgumentList( TransformAll( invocationExpr.ArgumentList, Transform ) );
}

private MemberAccessExpressionSyntax Transform( MemberAccessExpressionSyntax memberAccessExpr ) {
bool ShouldRemoveReturnedMemberAccess( MemberAccessExpressionSyntax memberAccessExpr )
=> memberAccessExpr.Name.Identifier.ValueText switch {
"FromResult" => true,
"CompletedTask" => true,
_ => false
};

private ExpressionSyntax Transform( MemberAccessExpressionSyntax memberAccessExpr ) {
if( memberAccessExpr.IsKind( SyntaxKind.SimpleMemberAccessExpression ) ) {
if( ShouldRemoveReturnedMemberAccess( memberAccessExpr ) &&
( memberAccessExpr.Parent.IsKind( SyntaxKind.ReturnStatement ) ||
( memberAccessExpr.Parent?.Parent?.IsKind( SyntaxKind.ReturnStatement ) ?? false ) ) ) {
return SyntaxFactory.ParseExpression( "" );
}

return memberAccessExpr
.WithExpression( Transform( memberAccessExpr.Expression ) )
.WithName( Transform( memberAccessExpr.Name ) );
Expand Down
8 changes: 0 additions & 8 deletions src/D2L.CodeStyle.Analyzers/Async/Generator/SyncGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,6 @@ CancellationToken _
return false;
}

// Require the async keyword if applicable.
// Interface and abstract methods won't have bodies and thus won't have the
// keyword but we still want to translate their signatures.
// TODO: we could probably relax this, or we should have an error.
if( method.Body is not null && !method.Modifiers.Any( t => t.IsKind( SyntaxKind.AsyncKeyword ) ) ) {
return false;
}

// The ones we care about have to have a particular attribute -- so
// perk up when we see _any_ attributes (we don't have a SemanticModel
// in this method.)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,43 @@ public void ConfigureAwait() {
Assert.AreEqual( "[Blocking] void Bar() { Foo fooFirst = m_foo.Bar( barrer ); }", actual.Value.ToFullString() );
}

[Test]
public void CompletedTaskConfigureAwait() {
var actual = Transform( @"[GenerateSync] Task BarAsync() { return Task.CompletedTask; }" );

Assert.IsTrue( actual.Success );
Assert.IsEmpty( actual.Diagnostics );
Assert.AreEqual( "[Blocking] void Bar() { return ; }", actual.Value.ToFullString() );
}

[Test]
public void FromResult() {
var actual = Transform( @"[GenerateSync] Task BarAsync() { return Task.FromResult( baz ); }" );

Assert.IsTrue( actual.Success );
Assert.IsEmpty( actual.Diagnostics );
Assert.AreEqual( "[Blocking] void Bar() { return ( baz ); }", actual.Value.ToFullString() );
}

[Test]
public void FromResultGeneric() {
var actual = Transform( @"[GenerateSync] Task<Baz> BarAsync() { return Task.FromResult<Baz>( null ); }" );

Assert.IsTrue( actual.Success );
Assert.IsEmpty( actual.Diagnostics );
Assert.AreEqual( "[Blocking] Baz Bar() { return ( null ); }", actual.Value.ToFullString() );
}

// Not the best thing to be doing but unlikely this will happen and it should be easily caught
[Test]
public void DontRemoveUnreturnedFromResult() {
var actual = Transform( @"[GenerateSync] Task BarAsync() { var Baz = Task.CompletedTask; return Baz; }" );

Assert.IsTrue( actual.Success );
Assert.IsEmpty( actual.Diagnostics );
Assert.AreEqual( "[Blocking] void Bar() { var Baz = Task.CompletedTask; return Baz; }", actual.Value.ToFullString() );
}

[Test]
public void Using() {
var actual = Transform( @"[GenerateSync] async Task BarAsync() { using( var foo = await m_bar.GetAsync( qux ).ConfigureAwait( false ) ) { return await( this as IBarProvider ).BarsAsync().ConfigureAwait( false ); }" );
Expand Down

0 comments on commit 549b42c

Please sign in to comment.