diff --git a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ITypeSymbolExtensions.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ITypeSymbolExtensions.cs index a76b4c0dc4..12401c104c 100644 --- a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ITypeSymbolExtensions.cs +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ITypeSymbolExtensions.cs @@ -48,16 +48,19 @@ public static bool DerivesFrom([NotNullWhen(returnValue: true)] this ITypeSymbol if (!baseTypesOnly && candidateBaseType.TypeKind == TypeKind.Interface) { - IEnumerable allInterfaces = symbol.AllInterfaces.OfType(); - if (SymbolEqualityComparer.Default.Equals(candidateBaseType.OriginalDefinition, candidateBaseType)) + // Avoid OfType() + optional Select() + Contains() chain — each creates a heap-allocated + // LINQ iterator state machine. AllInterfaces is ImmutableArray; iterating it + // directly uses the struct enumerator (zero heap allocation). + bool useOriginalDefinition = SymbolEqualityComparer.Default.Equals(candidateBaseType.OriginalDefinition, candidateBaseType); + foreach (INamedTypeSymbol iface in symbol.AllInterfaces) { - // Candidate base type is not a constructed generic type, so use original definition for interfaces. - allInterfaces = allInterfaces.Select(i => i.OriginalDefinition); - } - - if (allInterfaces.Contains(candidateBaseType, SymbolEqualityComparer.Default)) - { - return true; + // When the candidate is not a constructed generic type, compare via OriginalDefinition + // (mirrors the original Select(i => i.OriginalDefinition) projection). + ITypeSymbol candidate = useOriginalDefinition ? iface.OriginalDefinition : iface; + if (SymbolEqualityComparer.Default.Equals(candidate, candidateBaseType)) + { + return true; + } } }