diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/CastShouldNotBeDuplicated.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/CastShouldNotBeDuplicated.cs index f34dc41a66e..8ab3e90dfd4 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/CastShouldNotBeDuplicated.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/CastShouldNotBeDuplicated.cs @@ -232,8 +232,8 @@ private static void ReportPatternAtCastLocation(SonarSyntaxNodeReportingContext private static bool IsEquivalentVariable(ExpressionSyntax expression, SyntaxNode typedVariable) { - var left = RemoveThisExpression(typedVariable).WithoutTrivia(); - var right = RemoveThisExpression(expression).WithoutTrivia(); + var left = CleanupExpression(typedVariable).WithoutTrivia(); + var right = CleanupExpression(expression).WithoutTrivia(); return left.IsEquivalentTo(right) || (StandaloneIdentifier(left) is { } leftIdentifier && leftIdentifier == StandaloneIdentifier(right)); @@ -245,8 +245,14 @@ static string StandaloneIdentifier(SyntaxNode node) => _ when node.IsKind(SyntaxKindEx.SingleVariableDesignation) => ((SingleVariableDesignationSyntaxWrapper)node).Identifier.ValueText, _ => null }; + } - static SyntaxNode RemoveThisExpression(SyntaxNode node) => - node is MemberAccessExpressionSyntax { Expression: ThisExpressionSyntax } memberAccess ? memberAccess.Name : node; + private static SyntaxNode CleanupExpression(SyntaxNode node) + { + while (node is ParenthesizedExpressionSyntax parenthesized) + { + node = parenthesized.Expression; + } + return node is MemberAccessExpressionSyntax { Expression: ThisExpressionSyntax } memberAccess ? memberAccess.Name : node; } } diff --git a/analyzers/tests/SonarAnalyzer.Test/TestCases/CastShouldNotBeDuplicated.cs b/analyzers/tests/SonarAnalyzer.Test/TestCases/CastShouldNotBeDuplicated.cs index 6f75a08ff17..834e40bde0d 100644 --- a/analyzers/tests/SonarAnalyzer.Test/TestCases/CastShouldNotBeDuplicated.cs +++ b/analyzers/tests/SonarAnalyzer.Test/TestCases/CastShouldNotBeDuplicated.cs @@ -41,6 +41,22 @@ public void IgnoreMemberAccess(Fruit arg) _ = (Fruit)differentInstance.Property; } + // https://github.com/SonarSource/sonar-dotnet/issues/9491 + if (arg.Property is Fruit) // Noncompliant + { + _ = ((Fruit)(arg.Property)).Property; // Secondary + } + + if (arg.Property is Fruit) // Noncompliant + { + _ = ((Fruit)((arg.Property))).Property; // Secondary + } + + if (arg.Property is Fruit) // Noncompliant + { + _ = ((Fruit)arg.Property).Property; // Secondary + } + if (f.Property is Fruit) // Compliant, the cast is on a different instance { _ = (Fruit)differentInstance.Property; @@ -128,6 +144,12 @@ public void TakeIdentifierIntoAccount(object x) var fruit = (Fruit)this.someField; // ^^^^^^^^^^^^^^^^^^^^^ Secondary } + + if (someField is Fruit) // Noncompliant + { + var fruit = ((Fruit)(this.someField)); +// ^^^^^^^^^^^^^^^^^^^^^^^ Secondary + } } public void UnknownFoo(object x)