Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MSTEST0029 do not report on implementation of interface #4803

Merged
merged 5 commits into from
Jan 28, 2025
Merged
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 @@ -64,7 +64,9 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo
return;
}

if (!methodSymbol.HasValidTestMethodSignature(taskSymbol, valueTaskSymbol, canDiscoverInternals))
if (!methodSymbol.HasValidTestMethodSignature(taskSymbol, valueTaskSymbol, canDiscoverInternals)
|| methodSymbol.IsVirtual
|| methodSymbol.IsOverride)
Evangelink marked this conversation as resolved.
Show resolved Hide resolved
{
return;
}
Expand All @@ -85,6 +87,13 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo
return;
}

// We consider that if the method implements an interface member, it is not a test method.
// Explicit implementations are not public so they are discarded earlier.
if (methodSymbol.IsImplementationOfAnyInterfaceMember())
{
return;
}

ImmutableArray<AttributeData> methodAttributes = methodSymbol.GetAttributes();
// check if the method has testMethod, testInitialize or testCleanup attribute
bool hasValidAttribute = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,38 @@ namespace Analyzer.Utilities.Extensions;

internal static class IMethodSymbolExtensions
{
public static bool IsImplementationOfAnyInterfaceMember(this ISymbol symbol)
=> IsImplementationOfAnyInterfaceMember<ISymbol>(symbol);

/// <summary>
/// Checks if a given symbol implements an interface member implicitly
/// </summary>
public static bool IsImplementationOfAnyInterfaceMember<TSymbol>(this ISymbol symbol)
where TSymbol : ISymbol
{
if (symbol.ContainingType == null)
{
return false;
}

foreach (INamedTypeSymbol interfaceSymbol in symbol.ContainingType.AllInterfaces)
{
foreach (TSymbol interfaceMember in interfaceSymbol.GetMembers().OfType<TSymbol>())
{
if (IsImplementationOfInterfaceMember(symbol, interfaceMember))
{
return true;
}
}
}

return false;
}

public static bool IsImplementationOfInterfaceMember(this ISymbol symbol, [NotNullWhen(returnValue: true)] ISymbol? interfaceMember)
=> interfaceMember != null
&& SymbolEqualityComparer.Default.Equals(symbol, symbol.ContainingType.FindImplementationForInterfaceMember(interfaceMember));

/// <summary>
/// Checks if the given method is an implementation of the given interface method
/// Substituted with the given typeargument.
Expand Down
Evangelink marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -252,4 +252,94 @@ public void TestCleanup()
""";
await VerifyCS.VerifyCodeFixAsync(code, code);
}

[TestMethod]
public async Task WhenMethodIsPublicAndImplementsDispose_NoDiagnostic()
{
string code = """
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;

[TestClass]
public class MyTestClass : IDisposable
Evangelink marked this conversation as resolved.
Show resolved Hide resolved
{
public void Dispose()
{
}
}
""";
await VerifyCS.VerifyAnalyzerAsync(code);
}

[TestMethod]
public async Task WhenMethodIsPublicAndImplementsUserDefinedInterface_NoDiagnostic()
{
string code = """
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;

public interface IMyInterface
{
void MyMethod();
}

[TestClass]
public class MyTestClass : IMyInterface
{
public void MyMethod()
{
}
}
""";
await VerifyCS.VerifyAnalyzerAsync(code);
}

[TestMethod]
public async Task WhenMethodIsPublicAndImplementsExplicitlyUserDefinedInterface_NoDiagnostic()
{
string code = """
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;

public interface IMyInterface
{
void MyMethod();
}

[TestClass]
public class MyTestClass : IMyInterface
{
void IMyInterface.MyMethod()
{
}
}
""";
await VerifyCS.VerifyAnalyzerAsync(code);
}

[TestMethod]
public async Task WhenMethodIsPublicAndImplementsDisposeAsVirtual_NoDiagnostic()
{
string code = """
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;

[TestClass]
public class MyTestClass : IDisposable
{
public virtual void Dispose()
{
}
}

[TestClass]
public class SubTestClass : MyTestClass
{
public override void Dispose()
{
}
}
""";
await VerifyCS.VerifyAnalyzerAsync(code);
}
}