diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs index d2ee8497fcf..725179211b6 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs @@ -36,8 +36,7 @@ protected override ArgumentSyntax LastArgumentIfArrayCreation(SyntaxNode express LastArgumentIfArrayCreation(ArgumentList(expression)); protected override ITypeSymbol ArrayElementType(SyntaxNode expression, SemanticModel model) => - expression as ArrayCreationExpressionSyntax is { } argument - && model.GetTypeInfo(argument.Type.ElementType).Type is { } elementType + model.GetTypeInfo(((ArrayCreationExpressionSyntax)expression).Type.ElementType).Type is var elementType ? elementType : null; diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs index adb46b10b83..01ec9ed1c70 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -18,6 +18,10 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +using System.Xml.Linq; +using SonarAnalyzer.Json.Parsing; +using SonarAnalyzer.SymbolicExecution.Roslyn.OperationProcessors; + namespace SonarAnalyzer.Rules; public abstract class ArrayPassedAsParamsBase : SonarDiagnosticAnalyzer @@ -38,20 +42,16 @@ protected sealed override void Initialize(SonarAnalysisContext context) => context.RegisterNodeAction(Language.GeneratedCodeRecognizer, c => { if (LastArgumentIfArrayCreation(c.Node) is { } lastArgument - && c.SemanticModel.GetSymbolInfo(c.Node).Symbol is IMethodSymbol methodSymbol - && ParameterSymbol(methodSymbol, c.Node, lastArgument) is { IsParams: true } param - && !IsObjectOrArrayType(c.Node, methodSymbol, param, c.SemanticModel) - && !IsJaggedArrayParam(param)) + && c.SemanticModel.GetSymbolInfo(c.Node).Symbol is IMethodSymbol methodSymbol + && Language.MethodParameterLookup(c.Node, methodSymbol).TryGetSymbol(lastArgument, out var param) + && param is { IsParams: true } + && !IsObjectOrArrayType(c.Node, methodSymbol, param, c.SemanticModel) + && !IsJaggedArrayParam(param)) { c.ReportIssue(Rule, lastArgument.GetLocation()); } }, ExpressionKinds); - private IParameterSymbol ParameterSymbol(IMethodSymbol symbol, SyntaxNode invocation, TArgumentNode argument) => - Language.MethodParameterLookup(invocation, symbol).TryGetSymbol(argument, out var param) - ? param - : null; - private static bool IsJaggedArrayParam(IParameterSymbol param) => param.Type is IArrayTypeSymbol { ElementType: IArrayTypeSymbol }; @@ -68,6 +68,5 @@ bool ParamArgumentsAreReferenceTypeArrays(SyntaxNode node, IMethodSymbol symbol, && ArrayElementType(arguments.First(), model) is { } elementType && elementType.IsReferenceType && !elementType.Is(KnownType.System_Object); - } } diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs index db04c245dc7..27f91d1da75 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs @@ -35,8 +35,7 @@ protected override ArgumentSyntax LastArgumentIfArrayCreation(SyntaxNode express GetLastArgumentIfArrayCreation(GetArgumentListFromExpression(expression)); protected override ITypeSymbol ArrayElementType(SyntaxNode expression, SemanticModel model) => - expression as ArrayCreationExpressionSyntax is { } argument - && model.GetTypeInfo(argument.Type).Type is { } elementType + model.GetTypeInfo(((ArrayCreationExpressionSyntax)expression).Type).Type is var elementType ? elementType : null; diff --git a/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.cs b/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.cs index 6ad8db67cbd..a153c86a687 100644 --- a/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.cs +++ b/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.cs @@ -81,21 +81,22 @@ public void Method(params object[] args) { } public void MethodArray(params Array[] args) { } public void MethodJaggedArray(params int[][] args) { } - public void CallMethod() + public void CallMethod(dynamic d) { Method(new String[] { "1", "2" }); // Noncompliant, elements in args: ["1", "2"] // The argument given for a parameter array can be a single expression that is implicitly convertible (ยง10.2) to the parameter array type. // In this case, the parameter array acts precisely like a value parameter. // see: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/classes#14625-parameter-arrays - Method(new object[] { new int[] { 1, 2} }); // FN, elements in args: [System.Int32[]] + Method(new object[] { new int[] { 1, 2 } }); // FN, elements in args: [System.Int32[]] Method(new int[] { 1, 2, 3, }); // Compliant, Elements in args: [System.Int32[]] - Method(new String[] { "1", "2" }, new String[] { "1", "2"}); // Compliant, elements in args: [System.String[], System.String[]] + Method(new String[] { "1", "2" }, new String[] { "1", "2" }); // Compliant, elements in args: [System.String[], System.String[]] Method(new String[] { "1", "2"}, new int[] { 1, 2}); // Compliant, elements in args: pSystem.String[], System.Int32[]] MethodArray(new String[] { "1", "2" }, new String[] { "1", "2" }); // Compliant, elements in args: [System.String[], System.String[]] MethodArray(new int[] { 1, 2 }, new int[] { 1, 2 }); // Compliant, elements in args: [System.Int32[], System.Int32[]] MethodJaggedArray(new int[] { 1, 2 }); // Compliant: jagged array [System.Object[]] - + Method(d); // Compliant + Method("Hello", 2); // Compliant string.Format(CultureInfo.InvariantCulture, "{0}.{1}", new object[] { "", new object() }); } }