From d09dc95c25bd511c41215c8d978849856ac38af3 Mon Sep 17 00:00:00 2001 From: mary-georgiou-sonarsource Date: Tue, 4 Jun 2024 16:08:02 +0200 Subject: [PATCH 1/8] fix FPs --- .../Rules/ArrayPassedAsParamsBase.cs | 18 +++++++---- .../TestCases/ArrayPassedAsParams.cs | 30 +++++++++---------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs index 1fe3f0f0347..8fd42451231 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -40,18 +40,26 @@ protected sealed override void Initialize(SonarAnalysisContext context) => { if (LastArgumentIfArrayCreation(c.Node) is { } lastArgument && ParameterSymbol(c.SemanticModel, c.Node, lastArgument) is { IsParams: true } param - && !IsJaggedArrayParam(param)) + && !Exluded(param)) { c.ReportIssue(rule, lastArgument.GetLocation()); } }, ExpressionKinds); private IParameterSymbol ParameterSymbol(SemanticModel model, SyntaxNode invocation, TArgumentNode argument) => - model.GetSymbolInfo(invocation).Symbol is IMethodSymbol methodSymbol - && Language.MethodParameterLookup(invocation, methodSymbol).TryGetSymbol(argument, out var param) + model.GetSymbolInfo(invocation).Symbol is IMethodSymbol methodSymbol && Language.MethodParameterLookup(invocation, methodSymbol).TryGetSymbol(argument, out var param) ? param : null; - private static bool IsJaggedArrayParam(IParameterSymbol param) => - param.Type is IArrayTypeSymbol { ElementType: IArrayTypeSymbol }; + private static bool Exluded(IParameterSymbol param) + { + return IsJaggedArrayParam(param) || IsObjectOrArrayType(param); + + static bool IsJaggedArrayParam(IParameterSymbol param) => + param.Type is IArrayTypeSymbol { ElementType: IArrayTypeSymbol }; + + static bool IsObjectOrArrayType(IParameterSymbol param) => + param.Type is IArrayTypeSymbol array && array.ElementType.IsAny(KnownType.System_Object, KnownType.System_Array); + } + } diff --git a/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.cs b/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.cs index 54823b4e337..00185118a51 100644 --- a/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.cs +++ b/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.cs @@ -82,20 +82,18 @@ public void MethodJaggedArray(params int[][] args) { } public void CallMethod() { - Method(new String[] { "1", "2" }); // Noncompliant, TP. 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} }); // Noncompliant. TP Elements in args: [System.Int32[]] - Method(new int[] { 1, 2, 3, }); // Noncompliant, FP. Elements in args: [System.Int32[]] - Method(new String[] { "1", "2" }, new String[] { "1", "2"}); // Noncompliant, FP. Elements in args: [System.String[], System.String[]] - // ^^^^^^^^^^^^^^^^^^^^^^^^ - Method(new String[] { "1", "2"}, new int[] { 1, 2}); // Noncompliant, FP. Elements in args: pSystem.String[], System.Int32[]] - // ^^^^^^^^^^^^^^^^^ - MethodArray(new String[] { "1", "2" }, new String[] { "1", "2" }); // Noncompliant, FP. Elements in args: [System.String[], System.String[]] - MethodArray(new int[] { 1, 2 }, new int[] { 1, 2 }); // Noncompliant, FP. Elements in args: [System.Int32[], System.Int32[]] - - MethodJaggedArray(new int[] { 1, 2 }); // Compliant: jagged array [System.Object[]] + Method(new String[] { "1", "2" }); // FN. 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 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 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[]] } } @@ -103,10 +101,10 @@ public void CallMethod() public class Repro6893 { public void Method(int a, params object[] argumentArray) { } - + public void CallMethod() { - Method(a: 1, argumentArray: new int[] { 1, 2 }); // Noncompliant FP + Method(a: 1, argumentArray: new int[] { 1, 2 }); // Compliant } } From 8c7ed47913ec7da656b17432caa04a9d31b9bd1e Mon Sep 17 00:00:00 2001 From: mary-georgiou-sonarsource Date: Tue, 4 Jun 2024 16:47:14 +0200 Subject: [PATCH 2/8] some cleaup --- .../Rules/ArrayPassedAsParams.cs | 7 +++---- .../Rules/ArrayPassedAsParamsBase.cs | 14 ++++++-------- .../Rules/ArrayPassedAsParams.cs | 15 +++++++++------ 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs index c37342e0a08..1720a529b5b 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs @@ -26,11 +26,11 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase Language => CSharpFacade.Instance; protected override SyntaxKind[] ExpressionKinds { get; } = - { + [ SyntaxKind.ObjectCreationExpression, SyntaxKind.InvocationExpression, SyntaxKindEx.ImplicitObjectCreationExpression - }; + ]; protected override ArgumentSyntax LastArgumentIfArrayCreation(SyntaxNode expression) => LastArgumentIfArrayCreation(ArgumentList(expression)); @@ -47,8 +47,7 @@ private static BaseArgumentListSyntax ArgumentList(SyntaxNode expression) => { ObjectCreationExpressionSyntax { } creation => creation.ArgumentList, InvocationExpressionSyntax { } invocation => invocation.ArgumentList, - _ when ImplicitObjectCreationExpressionSyntaxWrapper.IsInstance(expression) => - ((ImplicitObjectCreationExpressionSyntaxWrapper)expression).ArgumentList, + _ when ImplicitObjectCreationExpressionSyntaxWrapper.IsInstance(expression) => ((ImplicitObjectCreationExpressionSyntaxWrapper)expression).ArgumentList, _ => null }; diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs index 8fd42451231..77d8248f9fc 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -25,15 +25,13 @@ public abstract class ArrayPassedAsParamsBase : Sona where TArgumentNode : SyntaxNode { private const string DiagnosticId = "S3878"; - protected override string MessageFormat => "Remove this array creation and simply pass the elements."; - - private readonly DiagnosticDescriptor rule; protected abstract TSyntaxKind[] ExpressionKinds { get; } protected abstract TArgumentNode LastArgumentIfArrayCreation(SyntaxNode expression); - protected ArrayPassedAsParamsBase() : base(DiagnosticId) => - rule = Language.CreateDescriptor(DiagnosticId, MessageFormat); + protected override string MessageFormat => "Remove this array creation and simply pass the elements."; + + protected ArrayPassedAsParamsBase() : base(DiagnosticId) {} protected sealed override void Initialize(SonarAnalysisContext context) => context.RegisterNodeAction(Language.GeneratedCodeRecognizer, c => @@ -42,12 +40,13 @@ protected sealed override void Initialize(SonarAnalysisContext context) => && ParameterSymbol(c.SemanticModel, c.Node, lastArgument) is { IsParams: true } param && !Exluded(param)) { - c.ReportIssue(rule, lastArgument.GetLocation()); + c.ReportIssue(Rule, lastArgument.GetLocation()); } }, ExpressionKinds); private IParameterSymbol ParameterSymbol(SemanticModel model, SyntaxNode invocation, TArgumentNode argument) => - model.GetSymbolInfo(invocation).Symbol is IMethodSymbol methodSymbol && Language.MethodParameterLookup(invocation, methodSymbol).TryGetSymbol(argument, out var param) + model.GetSymbolInfo(invocation).Symbol is IMethodSymbol methodSymbol + && Language.MethodParameterLookup(invocation, methodSymbol).TryGetSymbol(argument, out var param) ? param : null; @@ -61,5 +60,4 @@ static bool IsJaggedArrayParam(IParameterSymbol param) => static bool IsObjectOrArrayType(IParameterSymbol param) => param.Type is IArrayTypeSymbol array && array.ElementType.IsAny(KnownType.System_Object, KnownType.System_Array); } - } diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs index d0d699d4e45..bb78163352b 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs @@ -26,10 +26,10 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase Language => VisualBasicFacade.Instance; protected override SyntaxKind[] ExpressionKinds { get; } = - { + [ SyntaxKind.ObjectCreationExpression, SyntaxKind.InvocationExpression - }; + ]; protected override ArgumentSyntax LastArgumentIfArrayCreation(SyntaxNode expression) => GetLastArgumentIfArrayCreation(GetArgumentListFromExpression(expression)); @@ -50,8 +50,11 @@ private static ArgumentSyntax GetLastArgumentIfArrayCreation(ArgumentListSyntax : null; private static bool IsArrayCreation(ExpressionSyntax expression) => - expression - is ArrayCreationExpressionSyntax { Initializer.Initializers.Count: > 0 } - or ArrayCreationExpressionSyntax { ArrayBounds: null } - or CollectionInitializerSyntax; + expression switch + { + ArrayCreationExpressionSyntax { Initializer.Initializers.Count: > 0 } => true, + ArrayCreationExpressionSyntax { ArrayBounds: null } => true, + CollectionInitializerSyntax => true, + _ => false + }; } From 6a3858afa58c048f64ed2ddeef4f9e95e466766f Mon Sep 17 00:00:00 2001 From: mary-georgiou-sonarsource Date: Wed, 5 Jun 2024 10:38:49 +0200 Subject: [PATCH 3/8] update ITs --- .../Ember-MM/S3878-Ember Media Manager.json | 160 ------------------ .../Ember-MM/S3878-generic.EmberCore.NMT.json | 28 --- .../Ember-MM/S3878-multi.EmberExtras.json | 82 --------- .../Ember-MM/S3878-scraper.EmberCore.XML.json | 52 ------ ...ewEngines.Razor.BuildProviders-net452.json | 16 -- 5 files changed, 338 deletions(-) delete mode 100644 analyzers/its/expected/Ember-MM/S3878-Ember Media Manager.json delete mode 100644 analyzers/its/expected/Ember-MM/S3878-generic.EmberCore.NMT.json delete mode 100644 analyzers/its/expected/Ember-MM/S3878-multi.EmberExtras.json delete mode 100644 analyzers/its/expected/Ember-MM/S3878-scraper.EmberCore.XML.json delete mode 100644 analyzers/its/expected/Nancy/S3878-Nancy.ViewEngines.Razor.BuildProviders-net452.json diff --git a/analyzers/its/expected/Ember-MM/S3878-Ember Media Manager.json b/analyzers/its/expected/Ember-MM/S3878-Ember Media Manager.json deleted file mode 100644 index 6d1b789ee57..00000000000 --- a/analyzers/its/expected/Ember-MM/S3878-Ember Media Manager.json +++ /dev/null @@ -1,160 +0,0 @@ -{ - "Issues": [ - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L6583", - "Location": "Line 6583 Position 71-102" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L6881", - "Location": "Line 6881 Position 47-94" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L6882", - "Location": "Line 6882 Position 47-130" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L6883", - "Location": "Line 6883 Position 47-130" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L6884", - "Location": "Line 6884 Position 47-132" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L6885", - "Location": "Line 6885 Position 47-79" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7017", - "Location": "Line 7017 Position 47-93" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7018", - "Location": "Line 7018 Position 47-94" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7019", - "Location": "Line 7019 Position 47-83" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7020", - "Location": "Line 7020 Position 47-83" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7021", - "Location": "Line 7021 Position 47-80" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7022", - "Location": "Line 7022 Position 47-84" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7023", - "Location": "Line 7023 Position 47-80" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7024", - "Location": "Line 7024 Position 47-82" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7025", - "Location": "Line 7025 Position 47-80" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7026", - "Location": "Line 7026 Position 47-97" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7027", - "Location": "Line 7027 Position 47-101" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7028", - "Location": "Line 7028 Position 47-97" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7107", - "Location": "Line 7107 Position 43-137" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7109", - "Location": "Line 7109 Position 47-141" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7111", - "Location": "Line 7111 Position 43-75" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7193", - "Location": "Line 7193 Position 47-96" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7194", - "Location": "Line 7194 Position 47-130" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7195", - "Location": "Line 7195 Position 47-130" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7196", - "Location": "Line 7196 Position 47-134" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Ember%20Media%20Manager/frmMain.vb#L7197", - "Location": "Line 7197 Position 47-79" - } - ] -} \ No newline at end of file diff --git a/analyzers/its/expected/Ember-MM/S3878-generic.EmberCore.NMT.json b/analyzers/its/expected/Ember-MM/S3878-generic.EmberCore.NMT.json deleted file mode 100644 index 4695bde4c7c..00000000000 --- a/analyzers/its/expected/Ember-MM/S3878-generic.EmberCore.NMT.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "Issues": [ - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/generic.EmberCore.NMT/dlgNMTMovies.vb#L127", - "Location": "Line 127 Position 56-126" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/generic.EmberCore.NMT/dlgNMTMovies.vb#L133", - "Location": "Line 133 Position 56-129" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/generic.EmberCore.NMT/dlgNMTMovies.vb#L916", - "Location": "Line 916 Position 38-59" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/generic.EmberCore.NMT/dlgNMTMovies.vb#L948", - "Location": "Line 948 Position 40-62" - } - ] -} \ No newline at end of file diff --git a/analyzers/its/expected/Ember-MM/S3878-multi.EmberExtras.json b/analyzers/its/expected/Ember-MM/S3878-multi.EmberExtras.json deleted file mode 100644 index 430cd0ebe95..00000000000 --- a/analyzers/its/expected/Ember-MM/S3878-multi.EmberExtras.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "Issues": [ - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/multi.EmberExtras/frmAVCodecEditor.vb#L12", - "Location": "Line 12 Position 50-100" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/multi.EmberExtras/frmAVCodecEditor.vb#L24", - "Location": "Line 24 Position 50-100" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/multi.EmberExtras/frmAVCodecEditor.vb#L40", - "Location": "Line 40 Position 46-87" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/multi.EmberExtras/frmAVCodecEditor.vb#L47", - "Location": "Line 47 Position 46-87" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/multi.EmberExtras/frmGenresEditor.vb#L160", - "Location": "Line 160 Position 47-74" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/multi.EmberExtras/frmGenresEditor.vb#L173", - "Location": "Line 173 Position 45-68" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/multi.EmberExtras/frmGenresEditor.vb#L34", - "Location": "Line 34 Position 34-57" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/multi.EmberExtras/frmGenresEditor.vb#L48", - "Location": "Line 48 Position 59-91" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/multi.EmberExtras/frmGenresEditor.vb#L57", - "Location": "Line 57 Position 59-91" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/multi.EmberExtras/frmMediaSourcesEditor.vb#L127", - "Location": "Line 127 Position 47-88" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/multi.EmberExtras/frmMediaSourcesEditor.vb#L17", - "Location": "Line 17 Position 55-105" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/multi.EmberExtras/frmMediaSourcesEditor.vb#L30", - "Location": "Line 30 Position 52-109" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/multi.EmberExtras/frmMediaSourcesEditor.vb#L37", - "Location": "Line 37 Position 48-89" - } - ] -} \ No newline at end of file diff --git a/analyzers/its/expected/Ember-MM/S3878-scraper.EmberCore.XML.json b/analyzers/its/expected/Ember-MM/S3878-scraper.EmberCore.XML.json deleted file mode 100644 index 36cda30d1ec..00000000000 --- a/analyzers/its/expected/Ember-MM/S3878-scraper.EmberCore.XML.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "Issues": [ - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/scraper.EmberCore.XML/XMLScraper/MediaTags/AlbumTag.vb#L304", - "Location": "Line 304 Position 97-138" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/scraper.EmberCore.XML/XMLScraper/MediaTags/AlbumTag.vb#L411", - "Location": "Line 411 Position 97-138" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/scraper.EmberCore.XML/XMLScraper/MediaTags/ArtistTag.vb#L291", - "Location": "Line 291 Position 97-138" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/scraper.EmberCore.XML/XMLScraper/MediaTags/MovieTag.vb#L446", - "Location": "Line 446 Position 97-138" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/scraper.EmberCore.XML/XMLScraper/MediaTags/MusicVideoTag.vb#L160", - "Location": "Line 160 Position 97-138" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/scraper.EmberCore.XML/XMLScraper/MediaTags/PersonInfo.vb#L272", - "Location": "Line 272 Position 97-138" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/scraper.EmberCore.XML/XMLScraper/MediaTags/TVEpisodeTag.vb#L224", - "Location": "Line 224 Position 97-138" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Ember-MM/Addons/scraper.EmberCore.XML/XMLScraper/MediaTags/TVShow.vb#L261", - "Location": "Line 261 Position 97-138" - } - ] -} \ No newline at end of file diff --git a/analyzers/its/expected/Nancy/S3878-Nancy.ViewEngines.Razor.BuildProviders-net452.json b/analyzers/its/expected/Nancy/S3878-Nancy.ViewEngines.Razor.BuildProviders-net452.json deleted file mode 100644 index bdc4d32fa6c..00000000000 --- a/analyzers/its/expected/Nancy/S3878-Nancy.ViewEngines.Razor.BuildProviders-net452.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "Issues": [ - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Nancy/src/Nancy.ViewEngines.Razor.BuildProviders/NancyCSharpRazorBuildProvider.cs#L47", - "Location": "Line 47 Position 104-175" - }, - { - "Id": "S3878", - "Message": "Remove this array creation and simply pass the elements.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/Projects/Nancy/src/Nancy.ViewEngines.Razor.BuildProviders/NancyCSharpRazorBuildProvider.cs#L57", - "Location": "Line 57 Position 106-177" - } - ] -} \ No newline at end of file From 7067bdee81607b7fcef3d56ad9e00667efc5033d Mon Sep 17 00:00:00 2001 From: mary-georgiou-sonarsource Date: Thu, 6 Jun 2024 09:09:52 +0200 Subject: [PATCH 4/8] apply comments --- .../Rules/ArrayPassedAsParams.cs | 9 +++++- .../Rules/ArrayPassedAsParamsBase.cs | 32 ++++++++++++------- .../Rules/ArrayPassedAsParams.cs | 6 ++++ .../TestCases/ArrayPassedAsParams.cs | 7 ++-- .../TestCases/ArrayPassedAsParams.vb | 28 ++++++++++++++++ 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs index 1720a529b5b..d2ee8497fcf 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs @@ -35,6 +35,12 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase LastArgumentIfArrayCreation(ArgumentList(expression)); + protected override ITypeSymbol ArrayElementType(SyntaxNode expression, SemanticModel model) => + expression as ArrayCreationExpressionSyntax is { } argument + && model.GetTypeInfo(argument.Type.ElementType).Type is { } elementType + ? elementType + : null; + private static ArgumentSyntax LastArgumentIfArrayCreation(BaseArgumentListSyntax argumentList) => argumentList is { Arguments: { Count: > 0 } arguments } && arguments.Last() is var lastArgument @@ -47,7 +53,8 @@ private static BaseArgumentListSyntax ArgumentList(SyntaxNode expression) => { ObjectCreationExpressionSyntax { } creation => creation.ArgumentList, InvocationExpressionSyntax { } invocation => invocation.ArgumentList, - _ when ImplicitObjectCreationExpressionSyntaxWrapper.IsInstance(expression) => ((ImplicitObjectCreationExpressionSyntaxWrapper)expression).ArgumentList, + _ when ImplicitObjectCreationExpressionSyntaxWrapper.IsInstance(expression) => + ((ImplicitObjectCreationExpressionSyntaxWrapper)expression).ArgumentList, _ => null }; diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs index 77d8248f9fc..adb46b10b83 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -28,6 +28,7 @@ public abstract class ArrayPassedAsParamsBase : Sona protected abstract TSyntaxKind[] ExpressionKinds { get; } protected abstract TArgumentNode LastArgumentIfArrayCreation(SyntaxNode expression); + protected abstract ITypeSymbol ArrayElementType(SyntaxNode expression, SemanticModel model); protected override string MessageFormat => "Remove this array creation and simply pass the elements."; @@ -37,27 +38,36 @@ protected sealed override void Initialize(SonarAnalysisContext context) => context.RegisterNodeAction(Language.GeneratedCodeRecognizer, c => { if (LastArgumentIfArrayCreation(c.Node) is { } lastArgument - && ParameterSymbol(c.SemanticModel, c.Node, lastArgument) is { IsParams: true } param - && !Exluded(param)) + && 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.ReportIssue(Rule, lastArgument.GetLocation()); } }, ExpressionKinds); - private IParameterSymbol ParameterSymbol(SemanticModel model, SyntaxNode invocation, TArgumentNode argument) => - model.GetSymbolInfo(invocation).Symbol is IMethodSymbol methodSymbol - && Language.MethodParameterLookup(invocation, methodSymbol).TryGetSymbol(argument, out var param) + private IParameterSymbol ParameterSymbol(IMethodSymbol symbol, SyntaxNode invocation, TArgumentNode argument) => + Language.MethodParameterLookup(invocation, symbol).TryGetSymbol(argument, out var param) ? param : null; - private static bool Exluded(IParameterSymbol param) + private static bool IsJaggedArrayParam(IParameterSymbol param) => + param.Type is IArrayTypeSymbol { ElementType: IArrayTypeSymbol }; + + private bool IsObjectOrArrayType(SyntaxNode node, IMethodSymbol symbol, IParameterSymbol param, SemanticModel model) { - return IsJaggedArrayParam(param) || IsObjectOrArrayType(param); + return param.Type is IArrayTypeSymbol array + && (array.ElementType.Is(KnownType.System_Array) + || (array.ElementType.Is(KnownType.System_Object) && !ParamArgumentsAreReferenceTypeArrays(node, symbol, param, model))); - static bool IsJaggedArrayParam(IParameterSymbol param) => - param.Type is IArrayTypeSymbol { ElementType: IArrayTypeSymbol }; + bool ParamArgumentsAreReferenceTypeArrays(SyntaxNode node, IMethodSymbol symbol, IParameterSymbol param, SemanticModel model) => + Language.MethodParameterLookup(node, symbol) is { } parameterLookup + && parameterLookup.TryGetSyntax(param, out var arguments) + && arguments.Count() is 1 + && ArrayElementType(arguments.First(), model) is { } elementType + && elementType.IsReferenceType + && !elementType.Is(KnownType.System_Object); - static bool IsObjectOrArrayType(IParameterSymbol param) => - param.Type is IArrayTypeSymbol array && array.ElementType.IsAny(KnownType.System_Object, KnownType.System_Array); } } diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs index bb78163352b..db04c245dc7 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs @@ -34,6 +34,12 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase GetLastArgumentIfArrayCreation(GetArgumentListFromExpression(expression)); + protected override ITypeSymbol ArrayElementType(SyntaxNode expression, SemanticModel model) => + expression as ArrayCreationExpressionSyntax is { } argument + && model.GetTypeInfo(argument.Type).Type is { } elementType + ? elementType + : null; + private static ArgumentListSyntax GetArgumentListFromExpression(SyntaxNode expression) => expression switch { diff --git a/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.cs b/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.cs index 00185118a51..6ad8db67cbd 100644 --- a/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.cs +++ b/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; public class Program { @@ -82,11 +83,11 @@ public void MethodJaggedArray(params int[][] args) { } public void CallMethod() { - Method(new String[] { "1", "2" }); // FN. Elements in args: ["1", "2"] + 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 int[] { 1, 2}); // Compliant, elements in args: pSystem.String[], System.Int32[]] @@ -94,6 +95,8 @@ public void CallMethod() 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[]] + + string.Format(CultureInfo.InvariantCulture, "{0}.{1}", new object[] { "", new object() }); } } diff --git a/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.vb b/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.vb index 7156b9a9df3..e336eafdea9 100644 --- a/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.vb +++ b/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.vb @@ -64,3 +64,31 @@ Public Class MyOtherClass Public Sub New(ByVal a As Integer(), ParamArray args As Integer()) End Sub End Class + +Public Class Repro6894 + 'Reproducer for https://github.com/SonarSource/sonar-dotnet/issues/6894 + + Public Sub Method(ParamArray args As Object()) + End Sub + + Public Sub MethodArray(ParamArray args As Array()) + End Sub + + Public Sub MethodJaggedArray(ParamArray args As Integer()()) + End Sub + + Public Sub CallMethod() + 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 Integer() {1, 2}}) ' FN, elements in args: [System.Int32[]] + Method(New Integer() {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 Integer() {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 Integer() {1, 2}, New Integer() {1, 2}) ' Compliant, elements in args: [System.Int32[], System.Int32[]] + + MethodJaggedArray(New Integer() {1, 2}) ' Compliant: jagged array [System.Object[]] + End Sub +End Class From 80d2ec37a224cb8b41f18ce316d3220c635346fc Mon Sep 17 00:00:00 2001 From: mary-georgiou-sonarsource Date: Thu, 6 Jun 2024 14:06:49 +0200 Subject: [PATCH 5/8] code smells and coverage --- .../Rules/ArrayPassedAsParams.cs | 3 +-- .../Rules/ArrayPassedAsParamsBase.cs | 13 ++++++------- .../Rules/ArrayPassedAsParams.cs | 3 +-- .../TestCases/ArrayPassedAsParams.cs | 9 +++++---- 4 files changed, 13 insertions(+), 15 deletions(-) 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..49a4e5beb66 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 @@ -39,7 +43,8 @@ protected sealed override void Initialize(SonarAnalysisContext context) => { if (LastArgumentIfArrayCreation(c.Node) is { } lastArgument && c.SemanticModel.GetSymbolInfo(c.Node).Symbol is IMethodSymbol methodSymbol - && ParameterSymbol(methodSymbol, c.Node, lastArgument) is { IsParams: true } param + && Language.MethodParameterLookup(c.Node, methodSymbol).TryGetSymbol(lastArgument, out var param) + && param is { IsParams: true } && !IsObjectOrArrayType(c.Node, methodSymbol, param, c.SemanticModel) && !IsJaggedArrayParam(param)) { @@ -47,11 +52,6 @@ protected sealed override void Initialize(SonarAnalysisContext context) => } }, 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() }); } } From ff71bc1172aece74d8fdd083585517b6b9792408 Mon Sep 17 00:00:00 2001 From: mary-georgiou-sonarsource Date: Fri, 7 Jun 2024 17:05:15 +0200 Subject: [PATCH 6/8] remove unused usings --- .../src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs index 49a4e5beb66..27744a1d3bd 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -18,10 +18,6 @@ * 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 From e963f4f04eb8f1b28758a446df5b797930699a7e Mon Sep 17 00:00:00 2001 From: mary-georgiou-sonarsource Date: Mon, 17 Jun 2024 10:19:36 +0200 Subject: [PATCH 7/8] apply comments --- .../Rules/ArrayPassedAsParams.cs | 13 ++++-------- .../Rules/ArrayPassedAsParamsBase.cs | 21 +++++++++---------- .../Rules/ArrayPassedAsParams.cs | 21 +++++++------------ .../TestCases/ArrayPassedAsParams.cs | 4 +++- .../TestCases/ArrayPassedAsParams.vb | 2 +- 5 files changed, 26 insertions(+), 35 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs index 725179211b6..9b6f2d7ad63 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs @@ -33,20 +33,15 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase - LastArgumentIfArrayCreation(ArgumentList(expression)); - - protected override ITypeSymbol ArrayElementType(SyntaxNode expression, SemanticModel model) => - model.GetTypeInfo(((ArrayCreationExpressionSyntax)expression).Type.ElementType).Type is var elementType - ? elementType - : null; - - private static ArgumentSyntax LastArgumentIfArrayCreation(BaseArgumentListSyntax argumentList) => - argumentList is { Arguments: { Count: > 0 } arguments } + ArgumentList(expression) is { Arguments: { Count: > 0 } arguments } && arguments.Last() is var lastArgument && IsArrayCreation(lastArgument.Expression) ? lastArgument : null; + protected override ITypeSymbol ArrayElementType(ArgumentSyntax argument, SemanticModel model) => + model.GetTypeInfo(((ArrayCreationExpressionSyntax)argument.Expression).Type.ElementType).Type; + private static BaseArgumentListSyntax ArgumentList(SyntaxNode expression) => expression switch { diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs index 27744a1d3bd..3ba7652b576 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -28,7 +28,7 @@ public abstract class ArrayPassedAsParamsBase : Sona protected abstract TSyntaxKind[] ExpressionKinds { get; } protected abstract TArgumentNode LastArgumentIfArrayCreation(SyntaxNode expression); - protected abstract ITypeSymbol ArrayElementType(SyntaxNode expression, SemanticModel model); + protected abstract ITypeSymbol ArrayElementType(TArgumentNode argument, SemanticModel model); protected override string MessageFormat => "Remove this array creation and simply pass the elements."; @@ -39,9 +39,10 @@ protected sealed override void Initialize(SonarAnalysisContext context) => { if (LastArgumentIfArrayCreation(c.Node) is { } lastArgument && c.SemanticModel.GetSymbolInfo(c.Node).Symbol is IMethodSymbol methodSymbol - && Language.MethodParameterLookup(c.Node, methodSymbol).TryGetSymbol(lastArgument, out var param) + && Language.MethodParameterLookup(c.Node, methodSymbol) is { } parameterLookup + && parameterLookup.TryGetSymbol(lastArgument, out var param) && param is { IsParams: true } - && !IsObjectOrArrayType(c.Node, methodSymbol, param, c.SemanticModel) + && !IsArrayOfCandidateTypes(lastArgument, parameterLookup, param, c.SemanticModel) && !IsJaggedArrayParam(param)) { c.ReportIssue(Rule, lastArgument.GetLocation()); @@ -51,18 +52,16 @@ protected sealed override void Initialize(SonarAnalysisContext context) => private static bool IsJaggedArrayParam(IParameterSymbol param) => param.Type is IArrayTypeSymbol { ElementType: IArrayTypeSymbol }; - private bool IsObjectOrArrayType(SyntaxNode node, IMethodSymbol symbol, IParameterSymbol param, SemanticModel model) + private bool IsArrayOfCandidateTypes(TArgumentNode lastArgument, IMethodParameterLookup parameterLookup, IParameterSymbol param, SemanticModel model) { return param.Type is IArrayTypeSymbol array && (array.ElementType.Is(KnownType.System_Array) - || (array.ElementType.Is(KnownType.System_Object) && !ParamArgumentsAreReferenceTypeArrays(node, symbol, param, model))); + || (array.ElementType.Is(KnownType.System_Object) && !ParamArgumentsAreReferenceTypeArrays(lastArgument, parameterLookup, model))); - bool ParamArgumentsAreReferenceTypeArrays(SyntaxNode node, IMethodSymbol symbol, IParameterSymbol param, SemanticModel model) => - Language.MethodParameterLookup(node, symbol) is { } parameterLookup + bool ParamArgumentsAreReferenceTypeArrays(TArgumentNode lastArgument, IMethodParameterLookup parameterLookup, SemanticModel model) => + ArrayElementType(lastArgument, model) is { IsReferenceType: true } elementType + && !elementType.Is(KnownType.System_Object) && parameterLookup.TryGetSyntax(param, out var arguments) - && arguments.Count() is 1 - && ArrayElementType(arguments.First(), model) is { } elementType - && elementType.IsReferenceType - && !elementType.Is(KnownType.System_Object); + && arguments.Count() is 1; } } diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs index 27f91d1da75..d78b9a41689 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs @@ -32,14 +32,16 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase - GetLastArgumentIfArrayCreation(GetArgumentListFromExpression(expression)); - - protected override ITypeSymbol ArrayElementType(SyntaxNode expression, SemanticModel model) => - model.GetTypeInfo(((ArrayCreationExpressionSyntax)expression).Type).Type is var elementType - ? elementType + ArgumentList(expression) is { Arguments: { Count: > 0 } arguments } + && arguments.Last() is var lastArgument + && IsArrayCreation(lastArgument.GetExpression()) + ? lastArgument : null; - private static ArgumentListSyntax GetArgumentListFromExpression(SyntaxNode expression) => + protected override ITypeSymbol ArrayElementType(ArgumentSyntax argument, SemanticModel model) => + model.GetTypeInfo(((ArrayCreationExpressionSyntax)argument.GetExpression()).Type).Type; + + private static ArgumentListSyntax ArgumentList(SyntaxNode expression) => expression switch { ObjectCreationExpressionSyntax { } creation => creation.ArgumentList, @@ -47,13 +49,6 @@ private static ArgumentListSyntax GetArgumentListFromExpression(SyntaxNode expre _ => null }; - private static ArgumentSyntax GetLastArgumentIfArrayCreation(ArgumentListSyntax argumentList) => - argumentList is { Arguments: { Count: > 0 } arguments } - && arguments.Last() is var lastArgument - && IsArrayCreation(lastArgument.GetExpression()) - ? lastArgument - : null; - private static bool IsArrayCreation(ExpressionSyntax expression) => expression switch { diff --git a/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.cs b/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.cs index a153c86a687..b718a8fc1a3 100644 --- a/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.cs +++ b/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.cs @@ -78,6 +78,7 @@ public class Repro6894 //Reproducer for https://github.com/SonarSource/sonar-dotnet/issues/6894 public void Method(params object[] args) { } + public void MethodMixed(int i, params object[] args) { } public void MethodArray(params Array[] args) { } public void MethodJaggedArray(params int[][] args) { } @@ -90,7 +91,8 @@ public void CallMethod(dynamic d) 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 int[] { 1, 2}); // Compliant, elements in args: pSystem.String[], System.Int32[]] + Method(new String[] { "1", "2"}, new int[] { 1, 2}); // Compliant, elements in args: [System.String[], System.Int32[]] + MethodMixed(1, new String[] { "1", "2" }); // Noncompliant 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[]] diff --git a/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.vb b/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.vb index e336eafdea9..4208c5ceb14 100644 --- a/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.vb +++ b/analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.vb @@ -85,7 +85,7 @@ Public Class Repro6894 Method(New Object() {New Integer() {1, 2}}) ' FN, elements in args: [System.Int32[]] Method(New Integer() {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 Integer() {1, 2}) ' Compliant, elements in args: pSystem.String[], System.Int32[]] + Method(New String() {"1", "2"}, New Integer() {1, 2}) ' Compliant, elements in args: [System.String[], System.Int32[]] MethodArray(New String() {"1", "2"}, New String() {"1", "2"}) ' Compliant, elements in args: [System.String[], System.String[]] MethodArray(New Integer() {1, 2}, New Integer() {1, 2}) ' Compliant, elements in args: [System.Int32[], System.Int32[]] From a8f8e6e2d62b05bcd9a429392c56fdd9a33a5487 Mon Sep 17 00:00:00 2001 From: mary-georgiou-sonarsource Date: Thu, 20 Jun 2024 14:05:19 +0200 Subject: [PATCH 8/8] apply comment --- .../src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs index 3ba7652b576..79e397e2f03 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -62,6 +62,6 @@ bool ParamArgumentsAreReferenceTypeArrays(TArgumentNode lastArgument, IMethodPar ArrayElementType(lastArgument, model) is { IsReferenceType: true } elementType && !elementType.Is(KnownType.System_Object) && parameterLookup.TryGetSyntax(param, out var arguments) - && arguments.Count() is 1; + && arguments.Length is 1; } }