Skip to content

Commit b4f87e5

Browse files
committed
BUGFIX IDISP017 when DisposeAsync()
Fix #204.
1 parent c246c5a commit b4f87e5

File tree

5 files changed

+90
-5
lines changed

5 files changed

+90
-5
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
namespace IDisposableAnalyzers.NetCoreTests.IDISP017PreferUsingTests
2+
{
3+
using Gu.Roslyn.Asserts;
4+
using Microsoft.CodeAnalysis.Diagnostics;
5+
using NUnit.Framework;
6+
7+
public static class Valid
8+
{
9+
private static readonly DiagnosticAnalyzer Analyzer = new DisposeCallAnalyzer();
10+
11+
[TestCase("c.DisposeAsync()")]
12+
[TestCase("c.DisposeAsync().ConfigureAwait(false)")]
13+
public static void DisposeAsync(string expression)
14+
{
15+
var code = @"
16+
namespace N
17+
{
18+
using System;
19+
using System.IO;
20+
using System.Threading.Tasks;
21+
22+
class Issue204
23+
{
24+
public async ValueTask M()
25+
{
26+
var c = new C();
27+
try
28+
{
29+
30+
}
31+
finally
32+
{
33+
await c.DisposeAsync().ConfigureAwait(false);
34+
}
35+
}
36+
37+
public class C : IAsyncDisposable
38+
{
39+
private readonly IAsyncDisposable disposable = File.OpenRead(string.Empty);
40+
41+
public async ValueTask DisposeAsync()
42+
{
43+
await this.disposable.DisposeAsync().ConfigureAwait(false);
44+
}
45+
}
46+
}
47+
}".AssertReplace("c.DisposeAsync().ConfigureAwait(false)", expression);
48+
RoslynAssert.Valid(Analyzer, code);
49+
}
50+
}
51+
}

IDisposableAnalyzers/Analyzers/DisposeCallAnalyzer.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ private static void Handle(SyntaxNodeAnalysisContext context)
2929
{
3030
if (!context.IsExcludedFromAnalysis() &&
3131
context.Node is InvocationExpressionSyntax invocation &&
32-
DisposeCall.IsMatch(invocation, context.SemanticModel, context.CancellationToken) &&
32+
DisposeCall.IsMatchAny(invocation, context.SemanticModel, context.CancellationToken) &&
3333
!invocation.TryFirstAncestorOrSelf<AnonymousFunctionExpressionSyntax>(out _) &&
3434
DisposeCall.TryGetDisposedRootMember(invocation, context.SemanticModel, context.CancellationToken, out var root))
3535
{
@@ -117,7 +117,8 @@ bool IsReassigned(ExpressionSyntax location)
117117

118118
private static bool IsPreferUsing(ILocalSymbol local, InvocationExpressionSyntax invocation, SyntaxNodeAnalysisContext context)
119119
{
120-
return local.TrySingleDeclaration(context.CancellationToken, out var declaration) &&
120+
return invocation.IsSymbol(KnownSymbol.IDisposable.Dispose, context.SemanticModel, context.CancellationToken) &&
121+
local.TrySingleDeclaration(context.CancellationToken, out var declaration) &&
121122
declaration is VariableDeclaratorSyntax declarator &&
122123
declaration.TryFirstAncestor(out LocalDeclarationStatementSyntax? localDeclarationStatement) &&
123124
invocation.TryFirstAncestor(out ExpressionStatementSyntax? expressionStatement) &&

IDisposableAnalyzers/Helpers/Disposable.Disposes.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ when recursion.Target(argument) is { } target
180180

181181
bool IsDisposeOrReturnValueDisposed(InvocationExpressionSyntax invocation)
182182
{
183-
if (DisposeCall.IsMatch(invocation, recursion.SemanticModel, recursion.CancellationToken))
183+
if (DisposeCall.IsMatchAny(invocation, recursion.SemanticModel, recursion.CancellationToken))
184184
{
185185
return true;
186186
}

IDisposableAnalyzers/Helpers/DisposeCall.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ internal static class DisposeCall
1111
internal static bool TryGetDisposed(InvocationExpressionSyntax disposeCall, SemanticModel semanticModel, CancellationToken cancellationToken, [NotNullWhen(true)] out ISymbol? disposed)
1212
{
1313
disposed = null;
14-
return IsMatch(disposeCall, semanticModel, cancellationToken) &&
14+
return IsMatchAny(disposeCall, semanticModel, cancellationToken) &&
1515
MemberPath.TrySingle(disposeCall, out var expression) &&
1616
semanticModel.TryGetSymbol(expression, cancellationToken, out disposed);
1717
}
@@ -71,7 +71,7 @@ internal static bool IsDisposing(InvocationExpressionSyntax disposeCall, ISymbol
7171
return false;
7272
}
7373

74-
internal static bool IsMatch(InvocationExpressionSyntax candidate, SemanticModel semanticModel, CancellationToken cancellationToken)
74+
internal static bool IsMatchAny(InvocationExpressionSyntax candidate, SemanticModel semanticModel, CancellationToken cancellationToken)
7575
{
7676
return candidate.ArgumentList is { Arguments: { Count: 0 } } &&
7777
(candidate.IsSymbol(KnownSymbol.IDisposable.Dispose, semanticModel, cancellationToken) ||
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
namespace ValidCode.NetCore.AsyncDisposable
2+
{
3+
using System;
4+
using System.IO;
5+
using System.Threading.Tasks;
6+
7+
class Issue204
8+
{
9+
public async ValueTask M()
10+
{
11+
var c = new C();
12+
try
13+
{
14+
15+
}
16+
finally
17+
{
18+
await c.DisposeAsync()
19+
.ConfigureAwait(false);
20+
}
21+
}
22+
23+
public class C : IAsyncDisposable
24+
{
25+
private readonly IAsyncDisposable disposable = File.OpenRead(string.Empty);
26+
27+
public async ValueTask DisposeAsync()
28+
{
29+
await this.disposable.DisposeAsync().ConfigureAwait(false);
30+
}
31+
}
32+
}
33+
}

0 commit comments

Comments
 (0)