diff --git a/src/Analyzers/MethodSetupShouldSpecifyReturnValueAnalyzer.cs b/src/Analyzers/MethodSetupShouldSpecifyReturnValueAnalyzer.cs
index 595d09d35..57b76748b 100644
--- a/src/Analyzers/MethodSetupShouldSpecifyReturnValueAnalyzer.cs
+++ b/src/Analyzers/MethodSetupShouldSpecifyReturnValueAnalyzer.cs
@@ -153,7 +153,7 @@ private static bool HasReturnValueSpecification(
}
SyntaxNode? current = setupSyntax;
- while (current?.Parent is MemberAccessExpressionSyntax memberAccess)
+ while (current?.GetParentSkippingParentheses() is MemberAccessExpressionSyntax memberAccess)
{
cancellationToken.ThrowIfCancellationRequested();
diff --git a/src/Common/SyntaxNodeExtensions.cs b/src/Common/SyntaxNodeExtensions.cs
index 39c6173e7..6b448a047 100644
--- a/src/Common/SyntaxNodeExtensions.cs
+++ b/src/Common/SyntaxNodeExtensions.cs
@@ -25,4 +25,21 @@ internal static class SyntaxNodeExtensions
? Location.Create(syntax.SyntaxTree, node.Span)
: null;
}
+
+ ///
+ /// Returns the parent node, skipping any intermediate wrappers.
+ /// This handles cases like (mock.Setup(...)).Returns() where parentheses wrap an invocation.
+ ///
+ /// The node whose logical parent to find.
+ /// The first non-parenthesized ancestor, or if none exists.
+ internal static SyntaxNode? GetParentSkippingParentheses(this SyntaxNode node)
+ {
+ SyntaxNode? parent = node.Parent;
+ while (parent is ParenthesizedExpressionSyntax)
+ {
+ parent = parent.Parent;
+ }
+
+ return parent;
+ }
}
diff --git a/tests/Moq.Analyzers.Test/MethodSetupShouldSpecifyReturnValueAnalyzerTests.cs b/tests/Moq.Analyzers.Test/MethodSetupShouldSpecifyReturnValueAnalyzerTests.cs
index 866db27ab..cbfa9e61a 100644
--- a/tests/Moq.Analyzers.Test/MethodSetupShouldSpecifyReturnValueAnalyzerTests.cs
+++ b/tests/Moq.Analyzers.Test/MethodSetupShouldSpecifyReturnValueAnalyzerTests.cs
@@ -135,6 +135,55 @@ public static IEnumerable