diff --git a/analyzers/its/expected/akka.net/S3247-Akka-netstandard2.0.json b/analyzers/its/expected/akka.net/S3247-Akka-netstandard2.0.json index d1f12bf14e3..5f251c5cf85 100644 --- a/analyzers/its/expected/akka.net/S3247-Akka-netstandard2.0.json +++ b/analyzers/its/expected/akka.net/S3247-Akka-netstandard2.0.json @@ -1,11 +1,5 @@ { "Issues": [ - { - "Id": "S3247", - "Message": "Replace this type-check-and-cast sequence with an \u0027as\u0027 and a null check.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/akka.net/src/core/Akka/Actor/ActorRefProvider.cs#L516", - "Location": "Line 516 Position 17-54" - }, { "Id": "S3247", "Message": "Replace this type-check-and-cast sequence with an \u0027as\u0027 and a null check.", diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/CastShouldNotBeDuplicated.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/CastShouldNotBeDuplicated.cs index 89b1eff0e53..f34dc41a66e 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/CastShouldNotBeDuplicated.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/CastShouldNotBeDuplicated.cs @@ -107,7 +107,8 @@ bool IsDuplicatedCastOnSameSymbol(ExpressionSyntax expression, SyntaxNode type) && !CSharpFacade.Instance.Syntax.IsInExpressionTree(context.SemanticModel, expression); // see https://github.com/SonarSource/sonar-dotnet/issues/8735#issuecomment-1943419398 bool IsCastOnSameSymbol(ExpressionSyntax expression) => - Equals(context.SemanticModel.GetSymbolInfo(expression).Symbol, typeExpressionSymbol); + IsEquivalentVariable(expression, typedVariable) + && Equals(context.SemanticModel.GetSymbolInfo(expression).Symbol, typeExpressionSymbol); } private static void ProcessPatternExpression(SonarSyntaxNodeReportingContext analysisContext, SyntaxNode isPattern, SyntaxNode mainVariableExpression, SyntaxNode parentStatement) @@ -228,4 +229,24 @@ private static void ReportPatternAtCastLocation(SonarSyntaxNodeReportingContext context.ReportIssue(Rule, castLocation, [patternLocation.ToSecondary()], message); } } + + private static bool IsEquivalentVariable(ExpressionSyntax expression, SyntaxNode typedVariable) + { + var left = RemoveThisExpression(typedVariable).WithoutTrivia(); + var right = RemoveThisExpression(expression).WithoutTrivia(); + + return left.IsEquivalentTo(right) + || (StandaloneIdentifier(left) is { } leftIdentifier && leftIdentifier == StandaloneIdentifier(right)); + + static string StandaloneIdentifier(SyntaxNode node) => + node switch + { + IdentifierNameSyntax name => name.Identifier.ValueText, + _ when node.IsKind(SyntaxKindEx.SingleVariableDesignation) => ((SingleVariableDesignationSyntaxWrapper)node).Identifier.ValueText, + _ => null + }; + + static SyntaxNode RemoveThisExpression(SyntaxNode node) => + 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 eb51f1afadc..6f75a08ff17 100644 --- a/analyzers/tests/SonarAnalyzer.Test/TestCases/CastShouldNotBeDuplicated.cs +++ b/analyzers/tests/SonarAnalyzer.Test/TestCases/CastShouldNotBeDuplicated.cs @@ -1,13 +1,19 @@ using System; using System.Collections.Generic; -class Fruit { } +class Fruit +{ + public object Property => null; +} + struct SomeStruct { } class Program { private object someField; + private object LocalProperty => null; + public void Foo(Object x) { if (x is Fruit) // Noncompliant @@ -25,6 +31,32 @@ public void Foo(Object x) } } + public void IgnoreMemberAccess(Fruit arg) + { + var differentInstance = new Fruit(); + var f = new Fruit(); + + if (arg.Property is Fruit) // Compliant, the cast is on a different instance + { + _ = (Fruit)differentInstance.Property; + } + + if (f.Property is Fruit) // Compliant, the cast is on a different instance + { + _ = (Fruit)differentInstance.Property; + } + + if (f.Property is Fruit) // Noncompliant + { + _ = (Fruit)f.Property; // Secondary + } + + if (LocalProperty is Fruit) // Noncompliant + { + _ = (Fruit)LocalProperty; // Secondary + } + } + public void Bar(object x) { if (!(x is Fruit))