diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/DisposableFieldsShouldBeDisposedTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/DisposableFieldsShouldBeDisposedTests.cs index b76092366e..3b8aa6afc1 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/DisposableFieldsShouldBeDisposedTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/DisposableFieldsShouldBeDisposedTests.cs @@ -727,6 +727,45 @@ function DisposeAsync() as ValueTask implements IAsyncDisposable.DisposeAsync return stream.DisposeAsync() end function end class +" + }.RunAsync(); + + await new VerifyVB.Test + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithAsyncInterfaces, + TestCode = @" +Imports System +Imports System.IO +Imports System.Net.Http +Imports System.Threading.Tasks + +class FileStream2 + implements IAsyncDisposable + public function DisposeAsync() as ValueTask implements IAsyncDisposable.DisposeAsync + return nothing + end function +end class + +public class Test + implements IAsyncDisposable, IDisposable + + private readonly client as HttpClient + private readonly stream as FileStream2 + + public sub new() + client = new HttpClient + stream = new FileStream2 + end sub + + public sub Dispose() implements IDisposable.Dispose + client.Dispose() + end sub + + rem arbitrary implementation name + function DisposeOtherAsync() as ValueTask implements IAsyncDisposable.DisposeAsync + return stream.DisposeAsync() + end function +end class " }.RunAsync(); } diff --git a/src/Utilities/Compiler/Extensions/IMethodSymbolExtensions.cs b/src/Utilities/Compiler/Extensions/IMethodSymbolExtensions.cs index 37c015a102..3c9cd76252 100644 --- a/src/Utilities/Compiler/Extensions/IMethodSymbolExtensions.cs +++ b/src/Utilities/Compiler/Extensions/IMethodSymbolExtensions.cs @@ -219,19 +219,12 @@ public static bool IsAsyncDisposeImplementation([NotNullWhen(returnValue: true)] method.IsImplementationOfInterfaceMethod(null, iAsyncDisposable, "DisposeAsync"); } - /// - /// Checks the name of the method of an implicit interface implementation (so, as is) or an explicit interface implementation (which also contains the interface as a prefix). - /// - private static bool IsImplicitOrExplicitInterfaceImplementationName(this IMethodSymbol method, string name, string interfacePrefix) - => (method.Name == name && method.MethodKind is MethodKind.Ordinary) || - (method.Name == $"{interfacePrefix}.{name}" && method.MethodKind is MethodKind.ExplicitInterfaceImplementation); - /// /// Checks if the given method has the signature "void Dispose()". /// private static bool HasDisposeMethodSignature(this IMethodSymbol method) { - return method.IsImplicitOrExplicitInterfaceImplementationName("Dispose", "System.IDisposable") && + return method.Name == "Dispose" && method.MethodKind == MethodKind.Ordinary && method.ReturnsVoid && method.Parameters.IsEmpty; } @@ -250,7 +243,7 @@ public static bool HasDisposeSignatureByConvention(this IMethodSymbol method) /// public static bool HasDisposeBoolMethodSignature(this IMethodSymbol method) { - if (method.IsImplicitOrExplicitInterfaceImplementationName("Dispose", "System.IDisposable") && + if (method.Name == "Dispose" && method.MethodKind == MethodKind.Ordinary && method.ReturnsVoid && method.Parameters.Length == 1) { IParameterSymbol parameter = method.Parameters[0]; @@ -267,8 +260,7 @@ public static bool HasDisposeBoolMethodSignature(this IMethodSymbol method) /// private static bool HasDisposeCloseMethodSignature(this IMethodSymbol method) { - return method.Name == "Close" && - method.MethodKind is MethodKind.Ordinary && + return method.Name == "Close" && method.MethodKind == MethodKind.Ordinary && method.ReturnsVoid && method.Parameters.IsEmpty; } @@ -286,7 +278,8 @@ private static bool HasDisposeAsyncMethodSignature(this IMethodSymbol method, INamedTypeSymbol? task, INamedTypeSymbol? valueTask) { - return method.IsImplicitOrExplicitInterfaceImplementationName("DisposeAsync", "System.IAsyncDisposable") && + return method.Name == "DisposeAsync" && + method.MethodKind == MethodKind.Ordinary && method.Parameters.IsEmpty && (SymbolEqualityComparer.Default.Equals(method.ReturnType, task) || SymbolEqualityComparer.Default.Equals(method.ReturnType, valueTask)); @@ -298,7 +291,7 @@ private static bool HasDisposeAsyncMethodSignature(this IMethodSymbol method, private static bool HasOverriddenDisposeCoreAsyncMethodSignature(this IMethodSymbol method, [NotNullWhen(returnValue: true)] INamedTypeSymbol? task) { return method.Name == "DisposeCoreAsync" && - method.MethodKind is MethodKind.Ordinary && + method.MethodKind == MethodKind.Ordinary && method.IsOverride && SymbolEqualityComparer.Default.Equals(method.ReturnType, task) && method.Parameters.Length == 1 && @@ -344,7 +337,7 @@ public static DisposeMethodKind GetDisposeMethodKind( { return DisposeMethodKind.DisposeBool; } - else if (method.HasDisposeAsyncMethodSignature(task, valueTask)) + else if (method.IsAsyncDisposeImplementation(iAsyncDisposable, valueTask) || method.HasDisposeAsyncMethodSignature(task, valueTask)) { return DisposeMethodKind.DisposeAsync; }