Skip to content

Commit

Permalink
MSTEST0029 do not report on implementation of interface (#4803)
Browse files Browse the repository at this point in the history
  • Loading branch information
Evangelink authored Jan 28, 2025
1 parent 3c7787e commit f492095
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 1 deletion.
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)
{
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
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
{
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);
}
}

0 comments on commit f492095

Please sign in to comment.