Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,29 @@ void IDisposable.Dispose()
this.disposable.Dispose();
}
}
}";
RoslynAssert.Valid(Analyzer, Disposable, code);
}

[Test]
public static void AsyncExplicitImplementation()
{
var code = @"
namespace N
{
using System;
using System.Threading.Tasks;

public sealed class C : IAsyncDisposable
{
private readonly Disposable disposable = new Disposable();

ValueTask IAsyncDisposable.DisposeAsync()
{
this.disposable.Dispose();
return ValueTask.CompletedTask;
}
}
}";
RoslynAssert.Valid(Analyzer, Disposable, code);
}
Expand Down
45 changes: 32 additions & 13 deletions IDisposableAnalyzers/Helpers/DisposeMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,20 @@ internal static class DisposeMethod
{
return topLevel;
}
else
{
return null;
}

return null;
}

return type.TryFindFirstMethodRecursive("Dispose", x => IsMatch(x), out var recursive)
? recursive
: null;
if (type.TryFindFirstMethodRecursive("Dispose", x => IsMatch(x), out var recursive))
{
return recursive;
}
else if (type.TryFindFirstMethodRecursive("System.IDisposable.Dispose", out recursive))
{
return recursive;
}

return null;

static bool IsMatch(IMethodSymbol candidate)
{
Expand Down Expand Up @@ -101,14 +106,28 @@ static bool IsMatch(IMethodSymbol candidate)

if (search == Search.TopLevel)
{
return type.TryFindFirstMethod("DisposeAsync", x => IsMatch(x), out var topLevel)
? topLevel
: null;
if (type.TryFindFirstMethod("DisposeAsync", x => IsMatch(x), out var topLevel))
{
return topLevel;
}
else if (type.TryFindFirstMethod("System.IAsyncDisposable.DisposeAsync", out topLevel))
{
return topLevel;
}

return null;
}

return type.TryFindFirstMethodRecursive("DisposeAsync", x => IsMatch(x), out var recursive)
? recursive
: null;
if (type.TryFindFirstMethodRecursive("DisposeAsync", x => IsMatch(x), out var recursive))
{
return recursive;
}
else if (type.TryFindFirstMethodRecursive("System.IAsyncDisposable.DisposeAsync", out recursive))
{
return recursive;
}

return null;

static bool IsMatch(IMethodSymbol candidate)
{
Expand Down
6 changes: 3 additions & 3 deletions IDisposableAnalyzers/Helpers/Walkers/DisposeWalker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ internal static DisposeWalker Borrow(INamedTypeSymbol type, SemanticModel semant
}

if (type.IsAssignableTo(KnownSymbols.IAsyncDisposable, semanticModel.Compilation) &&
type.TryFindFirstMethod(x => x is { Parameters.Length: 0 } && x == KnownSymbols.IAsyncDisposable.DisposeAsync, out var disposeAsync) &&
disposeAsync.TrySingleDeclaration(cancellationToken, out declaration))
DisposeMethod.FindDisposeAsync(type, semanticModel.Compilation, Search.Recursive) is { } asyncDisposeMethod &&
asyncDisposeMethod.TrySingleDeclaration(cancellationToken, out MethodDeclarationSyntax? asyncDeclaration))
{
return BorrowAndVisit(declaration, SearchScope.Instance, type, semanticModel, () => new DisposeWalker(), cancellationToken);
return BorrowAndVisit(asyncDeclaration, SearchScope.Instance, type, semanticModel, () => new DisposeWalker(), cancellationToken);
}

return Borrow(() => new DisposeWalker());
Expand Down