From c8c9dc3988cdbd9e4f725ac96ea83c3c1ef1fae8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 Aug 2025 12:59:36 +0000 Subject: [PATCH 1/3] Initial plan From 448bc9fff6f9da7e978baff28369dd36dd962dd3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 Aug 2025 13:20:36 +0000 Subject: [PATCH 2/3] feat: enhance symbol-based detection for Moq Raises methods Co-authored-by: rjmurillo <6811113+rjmurillo@users.noreply.github.com> --- src/Common/ISymbolExtensions.cs | 97 +++++++++++++++++++++++++++------ 1 file changed, 81 insertions(+), 16 deletions(-) diff --git a/src/Common/ISymbolExtensions.cs b/src/Common/ISymbolExtensions.cs index dcb33a537..528273a4e 100644 --- a/src/Common/ISymbolExtensions.cs +++ b/src/Common/ISymbolExtensions.cs @@ -221,10 +221,14 @@ internal static bool IsMoqRaisesMethod(this ISymbol symbol, MoqKnownSymbols know return true; } - // TODO: Remove this fallback once symbol-based detection is complete - // This is a temporary safety net for cases where symbol resolution fails - // but should be replaced with comprehensive symbol-based approach - return IsLikelyMoqRaisesMethodByName(methodSymbol); + // Enhanced symbol-based detection for other Moq patterns + if (IsAdditionalMoqRaisesMethod(methodSymbol, knownSymbols)) + { + return true; + } + + // This fallback will be removed in a future version once symbol-based detection is comprehensive + return false; } /// @@ -280,26 +284,87 @@ private static bool IsRaiseableMethod(ISymbol symbol, MoqKnownSymbols knownSymbo } /// - /// TEMPORARY: Conservative fallback for Moq Raises method detection. - /// This should be removed once symbol-based detection is comprehensive. - /// Only matches methods that are clearly Moq Raises methods. + /// Enhanced symbol-based detection for additional Moq Raises method patterns. + /// This method detects Raises methods that may not be directly covered by the standard interfaces. /// /// The method symbol to check. - /// True if this is likely a Moq Raises method; otherwise false. - private static bool IsLikelyMoqRaisesMethodByName(IMethodSymbol methodSymbol) + /// The known symbols for type checking. + /// True if this is an additional Moq Raises method pattern; otherwise false. + private static bool IsAdditionalMoqRaisesMethod(IMethodSymbol methodSymbol, MoqKnownSymbols knownSymbols) { - string methodName = methodSymbol.Name; + // Method must be named "Raises" or "RaisesAsync" + if (!string.Equals(methodSymbol.Name, "Raises", StringComparison.Ordinal) && + !string.Equals(methodSymbol.Name, "RaisesAsync", StringComparison.Ordinal)) + { + return false; + } + + // Method must be in a Moq namespace to ensure it's a Moq method + string? namespaceName = methodSymbol.ContainingNamespace?.ToDisplayString(); + if (namespaceName == null || !namespaceName.StartsWith("Moq", StringComparison.Ordinal)) + { + return false; + } - // Only match exact "Raises" or "RaisesAsync" method names - if (!string.Equals(methodName, "Raises", StringComparison.Ordinal) && - !string.Equals(methodName, "RaisesAsync", StringComparison.Ordinal)) + // Additional validation: ensure the method is from a type that implements Moq interfaces + INamedTypeSymbol? containingType = methodSymbol.ContainingType; + if (containingType == null) { return false; } - // Must be in a type that contains "Moq" in its namespace to reduce false positives - string? containingTypeNamespace = methodSymbol.ContainingType?.ContainingNamespace?.ToDisplayString(); - return containingTypeNamespace?.StartsWith("Moq", StringComparison.Ordinal) == true; + // Check if the containing type or its interfaces are related to Moq + return IsTypeRelatedToMoq(containingType, knownSymbols); + } + + /// + /// Determines if a type is related to Moq by checking its interfaces and base types. + /// + /// The type to check. + /// The known symbols for type checking. + /// True if the type is related to Moq; otherwise false. + private static bool IsTypeRelatedToMoq(INamedTypeSymbol type, MoqKnownSymbols knownSymbols) + { + // Check if the type directly implements any known Moq interfaces + foreach (INamedTypeSymbol implementedInterface in type.AllInterfaces) + { + if (IsMoqInterface(implementedInterface, knownSymbols)) + { + return true; + } + } + + // Check base types + INamedTypeSymbol? current = type.BaseType; + while (current != null) + { + if (IsMoqInterface(current, knownSymbols)) + { + return true; + } + + current = current.BaseType; + } + + return false; + } + + /// + /// Determines if a type is a known Moq interface. + /// + /// The type to check. + /// The known symbols for type checking. + /// True if the type is a Moq interface; otherwise false. + private static bool IsMoqInterface(INamedTypeSymbol type, MoqKnownSymbols knownSymbols) + { + return SymbolEqualityComparer.Default.Equals(type.OriginalDefinition, knownSymbols.ICallback) || + SymbolEqualityComparer.Default.Equals(type.OriginalDefinition, knownSymbols.ICallback1) || + SymbolEqualityComparer.Default.Equals(type.OriginalDefinition, knownSymbols.ICallback2) || + SymbolEqualityComparer.Default.Equals(type.OriginalDefinition, knownSymbols.IReturns) || + SymbolEqualityComparer.Default.Equals(type.OriginalDefinition, knownSymbols.IReturns1) || + SymbolEqualityComparer.Default.Equals(type.OriginalDefinition, knownSymbols.IReturns2) || + SymbolEqualityComparer.Default.Equals(type.OriginalDefinition, knownSymbols.IRaiseable) || + SymbolEqualityComparer.Default.Equals(type.OriginalDefinition, knownSymbols.IRaiseableAsync); } /// From 39015448f97f93b527ee490abaebc59d1b69165e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 Aug 2025 13:23:51 +0000 Subject: [PATCH 3/3] feat: improve string-based fallback documentation and maintain functionality Co-authored-by: rjmurillo <6811113+rjmurillo@users.noreply.github.com> --- src/Common/ISymbolExtensions.cs | 104 ++++++++------------------------ 1 file changed, 24 insertions(+), 80 deletions(-) diff --git a/src/Common/ISymbolExtensions.cs b/src/Common/ISymbolExtensions.cs index 528273a4e..df8fe1bb4 100644 --- a/src/Common/ISymbolExtensions.cs +++ b/src/Common/ISymbolExtensions.cs @@ -221,14 +221,16 @@ internal static bool IsMoqRaisesMethod(this ISymbol symbol, MoqKnownSymbols know return true; } - // Enhanced symbol-based detection for other Moq patterns - if (IsAdditionalMoqRaisesMethod(methodSymbol, knownSymbols)) - { - return true; - } - - // This fallback will be removed in a future version once symbol-based detection is comprehensive - return false; + // TODO: Replace this with comprehensive symbol-based detection + // The current symbol-based detection in IsKnownMoqRaisesMethod covers the standard + // Moq interfaces (ICallback, IReturns, IRaiseable) but may not cover all possible + // Raises method scenarios. A complete replacement would require: + // 1. Analysis of all possible Moq Raises patterns in different versions + // 2. Enhanced MoqKnownSymbols to include any missing interface patterns + // 3. Comprehensive test coverage for edge cases + // + // For now, keep the conservative string-based fallback to avoid breaking existing functionality + return IsConservativeRaisesMethodFallback(methodSymbol); } /// @@ -284,87 +286,29 @@ private static bool IsRaiseableMethod(ISymbol symbol, MoqKnownSymbols knownSymbo } /// - /// Enhanced symbol-based detection for additional Moq Raises method patterns. - /// This method detects Raises methods that may not be directly covered by the standard interfaces. + /// Conservative fallback for Moq Raises method detection using string-based name checking. + /// + /// NOTE: This method should eventually be replaced with comprehensive symbol-based detection. + /// It provides a safety net for Raises method patterns that may not be covered by the current + /// symbol-based detection in IsKnownMoqRaisesMethod. + /// + /// The method is intentionally conservative to minimize false positives while ensuring + /// that legitimate Moq Raises calls are detected. /// /// The method symbol to check. - /// The known symbols for type checking. - /// True if this is an additional Moq Raises method pattern; otherwise false. - private static bool IsAdditionalMoqRaisesMethod(IMethodSymbol methodSymbol, MoqKnownSymbols knownSymbols) + /// True if this is likely a Moq Raises method; otherwise false. + private static bool IsConservativeRaisesMethodFallback(IMethodSymbol methodSymbol) { - // Method must be named "Raises" or "RaisesAsync" + // Only match exact "Raises" or "RaisesAsync" method names if (!string.Equals(methodSymbol.Name, "Raises", StringComparison.Ordinal) && !string.Equals(methodSymbol.Name, "RaisesAsync", StringComparison.Ordinal)) { return false; } - // Method must be in a Moq namespace to ensure it's a Moq method - string? namespaceName = methodSymbol.ContainingNamespace?.ToDisplayString(); - if (namespaceName == null || !namespaceName.StartsWith("Moq", StringComparison.Ordinal)) - { - return false; - } - - // Additional validation: ensure the method is from a type that implements Moq interfaces - INamedTypeSymbol? containingType = methodSymbol.ContainingType; - if (containingType == null) - { - return false; - } - - // Check if the containing type or its interfaces are related to Moq - return IsTypeRelatedToMoq(containingType, knownSymbols); - } - - /// - /// Determines if a type is related to Moq by checking its interfaces and base types. - /// - /// The type to check. - /// The known symbols for type checking. - /// True if the type is related to Moq; otherwise false. - private static bool IsTypeRelatedToMoq(INamedTypeSymbol type, MoqKnownSymbols knownSymbols) - { - // Check if the type directly implements any known Moq interfaces - foreach (INamedTypeSymbol implementedInterface in type.AllInterfaces) - { - if (IsMoqInterface(implementedInterface, knownSymbols)) - { - return true; - } - } - - // Check base types - INamedTypeSymbol? current = type.BaseType; - while (current != null) - { - if (IsMoqInterface(current, knownSymbols)) - { - return true; - } - - current = current.BaseType; - } - - return false; - } - - /// - /// Determines if a type is a known Moq interface. - /// - /// The type to check. - /// The known symbols for type checking. - /// True if the type is a Moq interface; otherwise false. - private static bool IsMoqInterface(INamedTypeSymbol type, MoqKnownSymbols knownSymbols) - { - return SymbolEqualityComparer.Default.Equals(type.OriginalDefinition, knownSymbols.ICallback) || - SymbolEqualityComparer.Default.Equals(type.OriginalDefinition, knownSymbols.ICallback1) || - SymbolEqualityComparer.Default.Equals(type.OriginalDefinition, knownSymbols.ICallback2) || - SymbolEqualityComparer.Default.Equals(type.OriginalDefinition, knownSymbols.IReturns) || - SymbolEqualityComparer.Default.Equals(type.OriginalDefinition, knownSymbols.IReturns1) || - SymbolEqualityComparer.Default.Equals(type.OriginalDefinition, knownSymbols.IReturns2) || - SymbolEqualityComparer.Default.Equals(type.OriginalDefinition, knownSymbols.IRaiseable) || - SymbolEqualityComparer.Default.Equals(type.OriginalDefinition, knownSymbols.IRaiseableAsync); + // Must be in a Moq namespace to reduce false positives + string? containingNamespace = methodSymbol.ContainingType?.ContainingNamespace?.ToDisplayString(); + return containingNamespace?.StartsWith("Moq", StringComparison.Ordinal) == true; } ///