Skip to content

Commit 2f155cd

Browse files
authored
Add awaits and identifiers to SyncGenerator (#872)
1 parent 83fbd2c commit 2f155cd

File tree

3 files changed

+81
-27
lines changed

3 files changed

+81
-27
lines changed

src/D2L.CodeStyle.Analyzers/Async/Generator/AsyncToSyncMethodTransformer.cs

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Microsoft.CodeAnalysis;
1+
using D2L.CodeStyle.Analyzers.Extensions;
2+
using Microsoft.CodeAnalysis;
23
using Microsoft.CodeAnalysis.CSharp;
34
using Microsoft.CodeAnalysis.CSharp.Syntax;
45

@@ -53,8 +54,12 @@ private static SyntaxTokenList RemoveAsyncModifier( SyntaxTokenList modifiers )
5354
static token => token.IsKind( SyntaxKind.AsyncKeyword ) ? null : token
5455
);
5556

56-
private SyntaxToken RemoveAsyncSuffix( SyntaxToken ident ) {
57-
if( !ident.ValueText.EndsWith( "Async", StringComparison.Ordinal ) || ident.ValueText == "Async" ) {
57+
private SyntaxToken RemoveAsyncSuffix( SyntaxToken ident, bool optional = false ) {
58+
if( !ident.ValueText.EndsWith( "Async", StringComparison.Ordinal ) || ident.ValueText == "Async") {
59+
if( optional ) {
60+
return ident;
61+
}
62+
5863
ReportDiagnostic(
5964
Diagnostics.ExpectedAsyncSuffix,
6065
ident.GetLocation(),
@@ -122,8 +127,7 @@ private StatementSyntax Transform( StatementSyntax stmt )
122127

123128
EmptyStatementSyntax => stmt,
124129

125-
ExpressionStatementSyntax exprStmt => exprStmt
126-
.WithExpression( Transform( exprStmt.Expression) ),
130+
ExpressionStatementSyntax exprStmt => Transform( exprStmt ),
127131

128132
GotoStatementSyntax => stmt,
129133

@@ -154,11 +158,14 @@ private ExpressionSyntax Transform( ExpressionSyntax expr )
154158
.WithLeft( Transform( asgnExpr.Left ) )
155159
.WithRight( Transform( asgnExpr.Right ) ),
156160

161+
AwaitExpressionSyntax awaitExpr =>
162+
// Extract the inner expression, removing the "await"
163+
Transform( awaitExpr.Expression ).ConcatLeadingTriviaFrom( awaitExpr ),
164+
157165
BinaryExpressionSyntax binExpr => binExpr
158166
.WithLeft( Transform( binExpr.Left ) )
159167
.WithRight( Transform( binExpr.Right ) ),
160168

161-
162169
ConditionalExpressionSyntax condExpr => condExpr
163170
.WithCondition( Transform( condExpr.Condition ) )
164171
.WithWhenTrue( Transform( condExpr.WhenTrue ) )
@@ -170,6 +177,9 @@ private ExpressionSyntax Transform( ExpressionSyntax expr )
170177
.WithExpression( Transform( eaExpr.Expression ) )
171178
.WithArgumentList( TransformAll( eaExpr.ArgumentList, Transform ) ),
172179

180+
IdentifierNameSyntax identExpr => identExpr
181+
.WithIdentifier( RemoveAsyncSuffix( identExpr.Identifier, optional: true ) ),
182+
173183
LiteralExpressionSyntax => expr,
174184

175185
ObjectCreationExpressionSyntax newExpr => newExpr
@@ -193,6 +203,18 @@ private ExpressionSyntax Transform( ExpressionSyntax expr )
193203
_ => UnhandledSyntax( expr )
194204
};
195205

206+
private StatementSyntax Transform( ExpressionStatementSyntax exprStmt ) {
207+
var result = Transform( exprStmt.Expression );
208+
209+
// "await foo;" is redundant in sync land, so turn it into an
210+
// EmptyStatementSyntax (just a bare ";".)
211+
if( exprStmt.Expression is AwaitExpressionSyntax && result is IdentifierNameSyntax ) {
212+
return SyntaxFactory.EmptyStatement().WithTriviaFrom( exprStmt );
213+
}
214+
215+
return exprStmt.WithExpression( result );
216+
}
217+
196218
private ElseClauseSyntax Transform( ElseClauseSyntax clause )
197219
=> clause.WithStatement( Transform( clause.Statement ) );
198220

src/D2L.CodeStyle.Analyzers/Extensions/Microsoft.CodeAnalysis.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,5 +206,13 @@ public static ImmutableArray<INamedTypeSymbol> GetAllContainingTypes( this ISymb
206206
builder.Reverse();
207207
return builder.ToImmutable();
208208
}
209+
210+
public static TSyntax ConcatLeadingTriviaFrom<TSyntax>(
211+
this TSyntax syntax,
212+
SyntaxNode other
213+
) where TSyntax : SyntaxNode =>
214+
syntax.WithLeadingTrivia(
215+
other.GetLeadingTrivia().Concat( syntax.GetLeadingTrivia() )
216+
);
209217
}
210218
}

tests/D2L.CodeStyle.Analyzers.Test/Async/Generator/AsyncToSyncMethodTransformerTests.cs

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -39,59 +39,83 @@ public void BasicTaskArrow() {
3939
}
4040

4141
[Test]
42-
public void Silly() {
42+
public void Await() {
43+
var actual = Transform( @"[GenerateSync] async Task<int> HelloAsync() { return await q; }" );
44+
45+
Assert.IsTrue( actual.Success );
46+
Assert.IsEmpty( actual.Diagnostics );
47+
Assert.AreEqual( "[Blocking] int Hello() { return q; }", actual.Value.ToFullString() );
48+
}
49+
50+
[Test]
51+
public void AwaitIdentifier() {
52+
var actual = Transform( @"[GenerateSync] async Task HelloAsync() { await q; }" );
53+
54+
Assert.IsTrue( actual.Success );
55+
Assert.IsEmpty( actual.Diagnostics );
56+
Assert.AreEqual( "[Blocking] void Hello() { ; }", actual.Value.ToFullString() );
57+
}
58+
59+
[Test]
60+
public void Silly() {
4361
var actual = Transform( @"[GenerateSync]
44-
async Task<int> HelloAsync( int[] q ) {
45-
if( 6 == (7 - 1)*2 ) {
46-
return sizeof( ""hello"" );
47-
} else if( this[0]++ == ++this[1] ) {
48-
throw new NotImplementedException( ""foo"" );
49-
} else return 8;
62+
async Task<int> HelloAsync() {
63+
if( (await q) == (7 - (await q))*2 ) {
64+
return sizeof( int );
65+
} else if( this[0]++ == ++this[await q] ) {
66+
throw new NotImplementedException( await q );
67+
throw new X{ Y = await q };
68+
} else return await q;
5069
5170
{
5271
{
53-
{ ;;; }
72+
{ ;;; q; }
5473
}
5574
}
5675
5776
goto lol;
5877
5978
do {
60-
while( true ) {
79+
while( await q ) {
6180
continue;
6281
lol: break;
6382
}
64-
} while( false );
83+
} while( await q );
84+
85+
this = await q;
6586
66-
this = this;
87+
await q;
6788
6889
return 123;
6990
}" );
7091

7192
var expected = @"[Blocking]
72-
int Hello( int[] q ) {
73-
if( 6 == (7 - 1)*2 ) {
74-
return sizeof( ""hello"" );
75-
} else if( this[0]++ == ++this[1] ) {
76-
throw new NotImplementedException( ""foo"" );
77-
} else return 8;
93+
int Hello() {
94+
if( (q) == (7 - (q))*2 ) {
95+
return sizeof( int );
96+
} else if( this[0]++ == ++this[q] ) {
97+
throw new NotImplementedException( q );
98+
throw new X{ Y = q };
99+
} else return q;
78100
79101
{
80102
{
81-
{ ;;; }
103+
{ ;;; q; }
82104
}
83105
}
84106
85107
goto lol;
86108
87109
do {
88-
while( true ) {
110+
while( q ) {
89111
continue;
90112
lol: break;
91113
}
92-
} while( false );
114+
} while( q );
115+
116+
this = q;
93117
94-
this = this;
118+
;
95119
96120
return 123;
97121
}";

0 commit comments

Comments
 (0)