@@ -19,6 +19,10 @@ internal static class IDisposableFactory
1919 SyntaxFactory . QualifiedName ( SyntaxFactory . IdentifierName ( "System" ) , SyntaxFactory . IdentifierName ( "IDisposable" ) )
2020 . WithAdditionalAnnotations ( Simplifier . Annotation ) ;
2121
22+ internal static readonly TypeSyntax SystemIAsyncDisposable =
23+ SyntaxFactory . QualifiedName ( SyntaxFactory . IdentifierName ( "System" ) , SyntaxFactory . IdentifierName ( "IAsyncDisposable" ) )
24+ . WithAdditionalAnnotations ( Simplifier . Annotation ) ;
25+
2226 internal static readonly StatementSyntax GcSuppressFinalizeThis =
2327 SyntaxFactory . ExpressionStatement (
2428 SyntaxFactory . InvocationExpression (
@@ -105,58 +109,71 @@ ExpressionSyntax Normalize(ExpressionSyntax e)
105109 }
106110 }
107111
108- internal static ExpressionStatementSyntax DisposeStatement ( FieldOrProperty disposable , SemanticModel semanticModel , CancellationToken cancellationToken )
112+ internal static ExpressionStatementSyntax DisposeAsyncStatement ( FieldOrProperty disposable , SemanticModel semanticModel , CancellationToken cancellationToken )
109113 {
110- using ( var walker = MutationWalker . For ( disposable , semanticModel , cancellationToken ) )
114+ switch ( MemberAccessContext . Create ( disposable , semanticModel , cancellationToken ) )
111115 {
112- if ( IsNeverNull ( out var neverNull ) )
113- {
114- if ( disposable . Type . IsAssignableTo ( KnownSymbol . IDisposable , semanticModel . Compilation ) &&
115- DisposeMethod . Find ( disposable . Type , semanticModel . Compilation , Search . Recursive ) is { ExplicitInterfaceImplementations : { IsEmpty : true } } )
116+ case { NeverNull : { } neverNull } :
117+ if ( disposable . Type . IsAssignableTo ( KnownSymbol . IAsyncDisposable , semanticModel . Compilation ) &&
118+ DisposeMethod . FindDisposeAsync ( disposable . Type , semanticModel . Compilation , Search . Recursive ) is { ExplicitInterfaceImplementations : { IsEmpty : true } } )
116119 {
117- return DisposeStatement ( neverNull . WithoutTrivia ( ) ) . WithLeadingElasticLineFeed ( ) ;
120+ return AsyncDisposeStatement ( neverNull . WithoutTrivia ( ) ) . WithLeadingElasticLineFeed ( ) ;
118121 }
119122
120- return DisposeStatement (
121- SyntaxFactory . CastExpression (
122- SystemIDisposable ,
123- neverNull . WithoutTrivia ( ) ) )
123+ return AsyncDisposeStatement (
124+ SyntaxFactory . CastExpression (
125+ SystemIAsyncDisposable ,
126+ neverNull . WithoutTrivia ( ) ) )
124127 . WithLeadingElasticLineFeed ( ) ;
125- }
126128
127- bool IsNeverNull ( out ExpressionSyntax memberAccess )
128- {
129- if ( walker . TrySingle ( out var mutation ) &&
130- mutation is AssignmentExpressionSyntax { Left : { } single , Right : ObjectCreationExpressionSyntax _, Parent : ExpressionStatementSyntax { Parent : BlockSyntax { Parent : ConstructorDeclarationSyntax _ } } } &&
131- disposable . Symbol . ContainingType . Constructors . Length == 1 )
129+ static ExpressionStatementSyntax AsyncDisposeStatement ( ExpressionSyntax expression )
132130 {
133- memberAccess = single ;
134- return true ;
131+ return SyntaxFactory . ExpressionStatement (
132+ SyntaxFactory . AwaitExpression (
133+ expression : SyntaxFactory . InvocationExpression (
134+ expression : SyntaxFactory . MemberAccessExpression (
135+ kind : SyntaxKind . SimpleMemberAccessExpression ,
136+ expression : expression ,
137+ name : SyntaxFactory . IdentifierName ( "DisposeAsync" ) ) ,
138+ argumentList : SyntaxFactory . ArgumentList ( ) ) ) ) ;
135139 }
136140
137- if ( walker . IsEmpty &&
138- disposable . Initializer ( cancellationToken ) is { Value : ObjectCreationExpressionSyntax _ } )
141+ default :
142+ throw new InvalidOperationException ( "Error generating DisposeAsyncStatement." ) ;
143+ }
144+ }
145+
146+ internal static ExpressionStatementSyntax DisposeStatement ( FieldOrProperty disposable , SemanticModel semanticModel , CancellationToken cancellationToken )
147+ {
148+ switch ( MemberAccessContext . Create ( disposable , semanticModel , cancellationToken ) )
149+ {
150+ case { NeverNull : { } neverNull } :
151+ if ( disposable . Type . IsAssignableTo ( KnownSymbol . IDisposable , semanticModel . Compilation ) &&
152+ DisposeMethod . Find ( disposable . Type , semanticModel . Compilation , Search . Recursive ) is { ExplicitInterfaceImplementations : { IsEmpty : true } } )
139153 {
140- memberAccess = MemberAccess ( disposable , semanticModel , cancellationToken ) ;
141- return true ;
154+ return DisposeStatement ( neverNull . WithoutTrivia ( ) ) . WithLeadingElasticLineFeed ( ) ;
142155 }
143156
144- memberAccess = null ! ;
145- return false ;
146- }
147- }
157+ return DisposeStatement (
158+ SyntaxFactory . CastExpression (
159+ SystemIDisposable ,
160+ neverNull . WithoutTrivia ( ) ) )
161+ . WithLeadingElasticLineFeed ( ) ;
162+ case { MaybeNull : { } maybeNull } :
163+ if ( DisposeMethod . IsAccessibleOn ( disposable . Type , semanticModel . Compilation ) )
164+ {
165+ return ConditionalDisposeStatement ( maybeNull ) . WithLeadingElasticLineFeed ( ) ;
166+ }
148167
149- if ( DisposeMethod . IsAccessibleOn ( disposable . Type , semanticModel . Compilation ) )
150- {
151- return ConditionalDisposeStatement ( MemberAccess ( disposable , semanticModel , cancellationToken ) ) . WithLeadingElasticLineFeed ( ) ;
168+ return ConditionalDisposeStatement (
169+ SyntaxFactory . BinaryExpression (
170+ SyntaxKind . AsExpression ,
171+ maybeNull ,
172+ SystemIDisposable ) )
173+ . WithLeadingElasticLineFeed ( ) ;
174+ default :
175+ throw new InvalidOperationException ( "Error generating DisposeStatement." ) ;
152176 }
153-
154- return ConditionalDisposeStatement (
155- SyntaxFactory . BinaryExpression (
156- SyntaxKind . AsExpression ,
157- MemberAccess ( disposable , semanticModel , cancellationToken ) ,
158- SystemIDisposable ) )
159- . WithLeadingElasticLineFeed ( ) ;
160177 }
161178
162179 internal static ExpressionSyntax MemberAccess ( SyntaxToken memberIdentifier , SemanticModel semanticModel , CancellationToken cancellationToken )
@@ -260,5 +277,58 @@ internal static ArgumentListSyntax Arguments(ExpressionSyntax expression)
260277 {
261278 return SyntaxFactory . ArgumentList ( SyntaxFactory . SingletonSeparatedList ( SyntaxFactory . Argument ( expression ) ) ) ;
262279 }
280+
281+ private struct MemberAccessContext
282+ {
283+ internal readonly ExpressionSyntax ? NeverNull ;
284+ internal readonly ExpressionSyntax ? MaybeNull ;
285+
286+ private MemberAccessContext ( ExpressionSyntax ? neverNull , ExpressionSyntax ? maybeNull )
287+ {
288+ this . NeverNull = neverNull ;
289+ this . MaybeNull = maybeNull ;
290+ }
291+
292+ internal static MemberAccessContext Create ( FieldOrProperty disposable , SemanticModel semanticModel , CancellationToken cancellationToken )
293+ {
294+ using ( var walker = MutationWalker . For ( disposable , semanticModel , cancellationToken ) )
295+ {
296+ if ( IsNeverNull ( out var neverNull ) )
297+ {
298+ return new MemberAccessContext ( neverNull . WithoutTrivia ( ) , null ) ;
299+ }
300+
301+ if ( walker . Assignments . TryFirst ( out var first ) )
302+ {
303+ return new MemberAccessContext ( null , first . Left . WithoutTrivia ( ) ) ;
304+ }
305+
306+ bool IsNeverNull ( out ExpressionSyntax memberAccess )
307+ {
308+ if ( walker . TrySingle ( out var mutation ) &&
309+ mutation is AssignmentExpressionSyntax { Left : { } single , Right : ObjectCreationExpressionSyntax _, Parent : ExpressionStatementSyntax { Parent : BlockSyntax { Parent : ConstructorDeclarationSyntax _ } } } &&
310+ disposable . Symbol . ContainingType . Constructors . Length == 1 )
311+ {
312+ memberAccess = single ;
313+ return true ;
314+ }
315+
316+ if ( walker . IsEmpty &&
317+ disposable . Initializer ( cancellationToken ) is { Value : ObjectCreationExpressionSyntax _ } )
318+ {
319+ memberAccess = MemberAccess ( disposable , semanticModel , cancellationToken ) ;
320+ return true ;
321+ }
322+
323+ memberAccess = null ! ;
324+ return false ;
325+ }
326+ }
327+
328+ return new MemberAccessContext (
329+ null ,
330+ MemberAccess ( disposable , semanticModel , cancellationToken ) ) ;
331+ }
332+ }
263333 }
264334}
0 commit comments