From 05a79ca2fad26f5d092be2a80ad803c466a0947f Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Mon, 23 Jan 2023 17:05:25 +0100 Subject: [PATCH 01/40] RSPEC baseline --- analyzers/rspec/cs/S3878_c#.html | 31 ++++++++++++++++++++ analyzers/rspec/cs/S3878_c#.json | 17 +++++++++++ analyzers/rspec/cs/Sonar_way_profile.json | 1 + analyzers/rspec/vbnet/S3878_vb.net.html | 30 +++++++++++++++++++ analyzers/rspec/vbnet/S3878_vb.net.json | 17 +++++++++++ analyzers/rspec/vbnet/Sonar_way_profile.json | 1 + 6 files changed, 97 insertions(+) create mode 100644 analyzers/rspec/cs/S3878_c#.html create mode 100644 analyzers/rspec/cs/S3878_c#.json create mode 100644 analyzers/rspec/vbnet/S3878_vb.net.html create mode 100644 analyzers/rspec/vbnet/S3878_vb.net.json diff --git a/analyzers/rspec/cs/S3878_c#.html b/analyzers/rspec/cs/S3878_c#.html new file mode 100644 index 00000000000..d9fc819cdc8 --- /dev/null +++ b/analyzers/rspec/cs/S3878_c#.html @@ -0,0 +1,31 @@ +

There’s no point in creating an array solely for the purpose of passing it as a params (...) argument; params keyword allow to pass a +variable number of parameters that will behave exactly like an array variable inside the method implementation. Simply pass the elements directly.

+

Noncompliant Code Example

+
+public void CallTheThing() {
+	//...
+	DoTheThing(new string[] { "s1", "s2"});  // Noncompliant: unnecessary
+	DoTheThing(new string[12]);  // Compliant
+	// ...
+}
+
+public void DoTheThing (params string[] args) {
+	// ...
+}
+
+

Compliant Solution

+
+public void CallTheThing()
+{
+	//...
+	DoTheThing("s1", "s2");
+	DoTheThing(new string[12]);
+	// ...
+}
+
+public void DoTheThing(params string[] args)
+{
+	// ...
+}
+
+ diff --git a/analyzers/rspec/cs/S3878_c#.json b/analyzers/rspec/cs/S3878_c#.json new file mode 100644 index 00000000000..9434c76d212 --- /dev/null +++ b/analyzers/rspec/cs/S3878_c#.json @@ -0,0 +1,17 @@ +{ + "title": "Arrays should not be created for params parameters", + "type": "CODE_SMELL", + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "2min" + }, + "tags": [ + "clumsy" + ], + "defaultSeverity": "Minor", + "ruleSpecification": "RSPEC-3878", + "sqKey": "S3878", + "scope": "All", + "quickfix": "unknown" +} diff --git a/analyzers/rspec/cs/Sonar_way_profile.json b/analyzers/rspec/cs/Sonar_way_profile.json index d16b8c34168..608da404352 100644 --- a/analyzers/rspec/cs/Sonar_way_profile.json +++ b/analyzers/rspec/cs/Sonar_way_profile.json @@ -186,6 +186,7 @@ "S3871", "S3875", "S3877", + "S3878", "S3881", "S3884", "S3885", diff --git a/analyzers/rspec/vbnet/S3878_vb.net.html b/analyzers/rspec/vbnet/S3878_vb.net.html new file mode 100644 index 00000000000..d64dc4f8527 --- /dev/null +++ b/analyzers/rspec/vbnet/S3878_vb.net.html @@ -0,0 +1,30 @@ +

There’s no point in creating an array solely for the purpose of passing it as a ParamArray (...) argument; ParamArray keyword allow to +pass a variable number of parameters that will behave exactly like an array variable inside the method implementation. Simply pass the elements +directly.

+

Noncompliant Code Example

+
+Class SurroundingClass
+	Public Sub CallTheThing()
+		DoTheThing(New String() {"s1", "s2"})
+		DoTheThing(New String(11) {})
+	End Sub
+
+	Public Sub DoTheThing(ParamArray args As String())
+		' Do something
+	End Sub
+End Class
+
+

Compliant Solution

+
+Class SurroundingClass
+	Public Sub CallTheThing()
+		DoTheThing("s1", "s2")
+		DoTheThing(New String(11) {})
+	End Sub
+
+	Public Sub DoTheThing(ParamArray args As String())
+		' Do something
+	End Sub
+End Class
+
+ diff --git a/analyzers/rspec/vbnet/S3878_vb.net.json b/analyzers/rspec/vbnet/S3878_vb.net.json new file mode 100644 index 00000000000..3cd2e2f382c --- /dev/null +++ b/analyzers/rspec/vbnet/S3878_vb.net.json @@ -0,0 +1,17 @@ +{ + "title": "Arrays should not be created for ParamArray parameters", + "type": "CODE_SMELL", + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "2min" + }, + "tags": [ + "clumsy" + ], + "defaultSeverity": "Minor", + "ruleSpecification": "RSPEC-3878", + "sqKey": "S3878", + "scope": "All", + "quickfix": "unknown" +} diff --git a/analyzers/rspec/vbnet/Sonar_way_profile.json b/analyzers/rspec/vbnet/Sonar_way_profile.json index 344b9bb365b..64c3988b67b 100644 --- a/analyzers/rspec/vbnet/Sonar_way_profile.json +++ b/analyzers/rspec/vbnet/Sonar_way_profile.json @@ -81,6 +81,7 @@ "S3776", "S3869", "S3871", + "S3878", "S3884", "S3889", "S3903", From ca10a136b97ed20a391fd50c2756773b3afe0820 Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Mon, 23 Jan 2023 17:05:48 +0100 Subject: [PATCH 02/40] Packaging test --- .../SonarAnalyzer.UnitTest/PackagingTests/RuleTypeMappingCS.cs | 2 +- .../SonarAnalyzer.UnitTest/PackagingTests/RuleTypeMappingVB.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/PackagingTests/RuleTypeMappingCS.cs b/analyzers/tests/SonarAnalyzer.UnitTest/PackagingTests/RuleTypeMappingCS.cs index b85c5fa1f6b..8a7a979b11d 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/PackagingTests/RuleTypeMappingCS.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/PackagingTests/RuleTypeMappingCS.cs @@ -3802,7 +3802,7 @@ internal static class RuleTypeMappingCS ["S3875"] = "CODE_SMELL", ["S3876"] = "CODE_SMELL", ["S3877"] = "CODE_SMELL", - // ["S3878"], + ["S3878"] = "CODE_SMELL", // ["S3879"], ["S3880"] = "CODE_SMELL", ["S3881"] = "CODE_SMELL", diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/PackagingTests/RuleTypeMappingVB.cs b/analyzers/tests/SonarAnalyzer.UnitTest/PackagingTests/RuleTypeMappingVB.cs index 3cab127083f..b86fec111e4 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/PackagingTests/RuleTypeMappingVB.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/PackagingTests/RuleTypeMappingVB.cs @@ -3802,7 +3802,7 @@ internal static class RuleTypeMappingVB // ["S3875"], // ["S3876"], // ["S3877"], - // ["S3878"], + ["S3878"] = "CODE_SMELL", // ["S3879"], // ["S3880"], // ["S3881"], From c67118ec0dafb6c6e07134f93dbc41e65ff78724 Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Mon, 23 Jan 2023 17:07:07 +0100 Subject: [PATCH 03/40] Rule file creation, baseline --- .../Rules/ArrayPassedAsParams.cs | 39 ++++++++++++++++++ .../Rules/ArrayPassedAsParamsBase.cs | 31 ++++++++++++++ .../Rules/ArrayPassedAsParams.cs | 39 ++++++++++++++++++ .../Rules/ArrayPassedAsParamsTest.cs | 40 +++++++++++++++++++ .../TestCases/ArrayPassedAsParams.cs | 5 +++ .../TestCases/ArrayPassedAsParams.vb | 7 ++++ 6 files changed, 161 insertions(+) create mode 100644 analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs create mode 100644 analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs create mode 100644 analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs create mode 100644 analyzers/tests/SonarAnalyzer.UnitTest/Rules/ArrayPassedAsParamsTest.cs create mode 100644 analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs create mode 100644 analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs new file mode 100644 index 00000000000..e9f28b51f4b --- /dev/null +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs @@ -0,0 +1,39 @@ +/* + * SonarAnalyzer for .NET + * Copyright (C) 2015-2022 SonarSource SA + * mailto: contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +namespace SonarAnalyzer.Rules.CSharp; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase +{ + + protected override ILanguageFacade Language => CSharpFacade.Instance; + + protected override void Initialize(SonarAnalysisContext context) => + context.RegisterSyntaxNodeActionInNonGenerated(c => + { + var node = c.Node; + if (true) + { + c.ReportIssue(Diagnostic.Create(Rule, node.GetLocation())); + } + }, + SyntaxKind.InvocationExpression); +} diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs new file mode 100644 index 00000000000..0d0f117b39b --- /dev/null +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -0,0 +1,31 @@ +/* + * SonarAnalyzer for .NET + * Copyright (C) 2015-2022 SonarSource SA + * mailto: contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +namespace SonarAnalyzer.Rules; + +public abstract class ArrayPassedAsParamsBase : SonarDiagnosticAnalyzer + where TSyntaxKind : struct +{ + private const string DiagnosticId = "S3878"; + + protected override string MessageFormat => "FIXME"; + + protected ArrayPassedAsParamsBase() : base(DiagnosticId) { } +} diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs new file mode 100644 index 00000000000..25968343ab2 --- /dev/null +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs @@ -0,0 +1,39 @@ +/* + * SonarAnalyzer for .NET + * Copyright (C) 2015-2022 SonarSource SA + * mailto: contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +namespace SonarAnalyzer.Rules.VisualBasic; + +[DiagnosticAnalyzer(LanguageNames.VisualBasic)] +public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase +{ + + protected override ILanguageFacade Language => VisualBasicFacade.Instance; + + protected override void Initialize(SonarAnalysisContext context) => + context.RegisterSyntaxNodeActionInNonGenerated(c => + { + var node = c.Node; + if (true) + { + c.ReportIssue(Diagnostic.Create(Rule, node.GetLocation())); + } + }, + SyntaxKind.InvocationExpression); +} diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/ArrayPassedAsParamsTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/ArrayPassedAsParamsTest.cs new file mode 100644 index 00000000000..0b75b12395a --- /dev/null +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/ArrayPassedAsParamsTest.cs @@ -0,0 +1,40 @@ +/* + * SonarAnalyzer for .NET + * Copyright (C) 2015-2022 SonarSource SA + * mailto: contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +using CS = SonarAnalyzer.Rules.CSharp; +using VB = SonarAnalyzer.Rules.VisualBasic; + +namespace SonarAnalyzer.UnitTest.Rules; + +[TestClass] +public class ArrayPassedAsParamsTest +{ + private readonly VerifierBuilder builderCS = new VerifierBuilder(); + private readonly VerifierBuilder builderVB = new VerifierBuilder(); + + [TestMethod] + public void ArrayPassedAsParams_CS() => + builderCS.AddPaths("ArrayPassedAsParams.cs").Verify(); + + [TestMethod] + public void ArrayPassedAsParams_VB() => + builderVB.AddPaths("ArrayPassedAsParams.vb").Verify(); +} diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs new file mode 100644 index 00000000000..a790d6d1f92 --- /dev/null +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs @@ -0,0 +1,5 @@ +using System; + +public class Program +{ +} diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb new file mode 100644 index 00000000000..f5b9e9276c6 --- /dev/null +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb @@ -0,0 +1,7 @@ +Public Class Program + + Public Sub Test() + + End Sub + +End Class From 9acb0386adf87fb37ec4f50da04bf93373c79c3d Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Tue, 24 Jan 2023 09:35:19 +0100 Subject: [PATCH 04/40] First batch of test cases --- .../TestCases/ArrayPassedAsParams.cs | 20 +++++++++++++++++++ .../TestCases/ArrayPassedAsParams.vb | 18 ++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs index a790d6d1f92..3a55ec44bd8 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs @@ -2,4 +2,24 @@ public class Program { + public void Base(string[] myArray) + { + Method(new string[] { "s1", "s2" }); // Noncompliant + + Method("s1"); // Compliant + Method("s1", "s2"); // Compliant + Method(myArray); // Compliant + Method(new string[12]); // Compliant + + Method2(1, new string[] { "s1", "s2" }); // Noncompliant + + Method2(1, "s1"); // Compliant + Method2(1, "s1", "s2"); // Compliant + Method2(1, myArray); // Compliant + Method2(1, new string[12]); // Compliant + } + + public void Method(params string[] args) { } + + public void Method2(int a, params string[] args) { } } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb index f5b9e9276c6..366a0d184ea 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb @@ -1,7 +1,23 @@ Public Class Program - Public Sub Test() + Public Sub Base(ByVal myArray As String()) + Method(New String() {"s1", "s2"}) ' Noncompliant: unnecessary + Method("s1") 'Compliant + Method("s1", "s2") 'Compliant + Method(myArray) 'Compliant + Method(New String(12) {}) 'Compliant + Method2(1, New String() {"s1", "s2"}) ' Noncompliant: unnecessary + Method2(1, "s1") 'Compliant + Method2(1, "s1", "s2") 'Compliant + Method2(1, myArray) 'Compliant + Method2(1, New String(11) {}) 'Compliant + End Sub + + Public Sub Method(ParamArray args As String()) + End Sub + + Public Sub Method2(ByVal a As Integer, ParamArray args As String()) End Sub End Class From 6f0f2e5731d31f018a5a07051e11e287d81c8031 Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Tue, 24 Jan 2023 12:23:03 +0100 Subject: [PATCH 05/40] First rule sketch with test cases (cs/vb) --- .../Rules/ArrayPassedAsParams.cs | 26 +++++++------ .../Rules/ArrayPassedAsParamsBase.cs | 27 +++++++++++-- .../Rules/ArrayPassedAsParams.cs | 26 +++++++------ .../TestCases/ArrayPassedAsParams.cs | 20 ++++++++-- .../TestCases/ArrayPassedAsParams.vb | 39 +++++++++++++------ 5 files changed, 95 insertions(+), 43 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs index e9f28b51f4b..bc5869fd3e6 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs @@ -21,19 +21,21 @@ namespace SonarAnalyzer.Rules.CSharp; [DiagnosticAnalyzer(LanguageNames.CSharp)] -public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase +public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase { - protected override ILanguageFacade Language => CSharpFacade.Instance; + protected override string ParameterKeyword => "params"; + protected override string MessageFormat => MessageBase; + + protected override bool ShouldReport(SonarSyntaxNodeReportingContext context, InvocationExpressionSyntax invocation) => + invocation.ArgumentList.Arguments.Count > 0 + && invocation.ArgumentList.Arguments.Last().Expression is ArrayCreationExpressionSyntax array + && array.Initializer is InitializerExpressionSyntax initializer + && initializer.Expressions.Count > 0 + && context.SemanticModel.GetSymbolInfo(invocation).Symbol is IMethodSymbol invokedMethodSymbol + && invokedMethodSymbol.Parameters.Any() + && invokedMethodSymbol.Parameters.Last().IsParams; // No additional parameters are permitted after the params keyword in a method declaration, and only one params keyword is permitted in a method declaration. - protected override void Initialize(SonarAnalysisContext context) => - context.RegisterSyntaxNodeActionInNonGenerated(c => - { - var node = c.Node; - if (true) - { - c.ReportIssue(Diagnostic.Create(Rule, node.GetLocation())); - } - }, - SyntaxKind.InvocationExpression); + protected override Location GetLocation(InvocationExpressionSyntax invocation) => + invocation.ArgumentList.Arguments.Last().Expression.GetLocation(); } diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs index 0d0f117b39b..1a0da2f80ed 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -20,12 +20,33 @@ namespace SonarAnalyzer.Rules; -public abstract class ArrayPassedAsParamsBase : SonarDiagnosticAnalyzer +public abstract class ArrayPassedAsParamsBase : SonarDiagnosticAnalyzer where TSyntaxKind : struct + where TInvocationExpressionSyntax : SyntaxNode { private const string DiagnosticId = "S3878"; - protected override string MessageFormat => "FIXME"; + public const string MessageBase = "Arrays should not be created for {0} parameters."; - protected ArrayPassedAsParamsBase() : base(DiagnosticId) { } + private readonly DiagnosticDescriptor rule; + protected abstract string ParameterKeyword { get; } + protected abstract bool ShouldReport(SonarSyntaxNodeReportingContext context, TInvocationExpressionSyntax invocation); + protected abstract Location GetLocation(TInvocationExpressionSyntax context); + + protected ArrayPassedAsParamsBase() : base(DiagnosticId) + { + rule = Language.CreateDescriptor(DiagnosticId, MessageFormat); + } + + protected sealed override void Initialize(SonarAnalysisContext context) => + context.RegisterNodeAction(Language.GeneratedCodeRecognizer, CheckInvocation, Language.SyntaxKind.InvocationExpression); + + private void CheckInvocation(SonarSyntaxNodeReportingContext context) + { + if ((TInvocationExpressionSyntax)context.Node is var invocation + && ShouldReport(context, invocation)) + { + context.ReportIssue(Diagnostic.Create(rule, GetLocation(invocation), ParameterKeyword)); + } + } } diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs index 25968343ab2..fc1551fa97c 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs @@ -21,19 +21,21 @@ namespace SonarAnalyzer.Rules.VisualBasic; [DiagnosticAnalyzer(LanguageNames.VisualBasic)] -public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase +public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase { - protected override ILanguageFacade Language => VisualBasicFacade.Instance; + protected override string ParameterKeyword => "ParamArray"; + protected override string MessageFormat => MessageBase; + + protected override bool ShouldReport(SonarSyntaxNodeReportingContext context, InvocationExpressionSyntax invocation) => + invocation.ArgumentList.Arguments.Count > 0 + && invocation.ArgumentList.Arguments.Last().GetExpression() is ArrayCreationExpressionSyntax array + && array.Initializer is CollectionInitializerSyntax initializer + && initializer.Initializers.Count > 0 + && context.SemanticModel.GetSymbolInfo(invocation).Symbol is IMethodSymbol invokedMethodSymbol + && invokedMethodSymbol.Parameters.Any() + && invokedMethodSymbol.Parameters.Last().IsParams; // No additional parameters are permitted after the params keyword in a method declaration, and only one params keyword is permitted in a method declaration. - protected override void Initialize(SonarAnalysisContext context) => - context.RegisterSyntaxNodeActionInNonGenerated(c => - { - var node = c.Node; - if (true) - { - c.ReportIssue(Diagnostic.Create(Rule, node.GetLocation())); - } - }, - SyntaxKind.InvocationExpression); + protected override Location GetLocation(InvocationExpressionSyntax invocation) => + invocation.ArgumentList.Arguments.Last().GetExpression().GetLocation(); } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs index 3a55ec44bd8..af5eec8750d 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs @@ -4,22 +4,34 @@ public class Program { public void Base(string[] myArray) { - Method(new string[] { "s1", "s2" }); // Noncompliant - + Method(new string[] { "s1", "s2" }); // Noncompliant {{Arrays should not be created for params parameters.}} +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Method(new string[] { "s1" }); // Noncompliant Method("s1"); // Compliant Method("s1", "s2"); // Compliant Method(myArray); // Compliant Method(new string[12]); // Compliant - Method2(1, new string[] { "s1", "s2" }); // Noncompliant - + Method2(1, new string[] { "s1", "s2" }); // Noncompliant {{Arrays should not be created for params parameters.}} +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Method2(1, new string[] { "s1" }); // Noncompliant Method2(1, "s1"); // Compliant Method2(1, "s1", "s2"); // Compliant Method2(1, myArray); // Compliant Method2(1, new string[12]); // Compliant + + Method3(new string[] { "s1", "s2" }, "s1"); // Compliant + Method3(new string[] { "s1", "s2" }, new string[12]); // Compliant + Method3(new string[] { "s1", "s2" }, new string[] { "s1", "s2" }); // Noncompliant +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Method3(null, null); // Compliant } public void Method(params string[] args) { } public void Method2(int a, params string[] args) { } + + public void Method3(string[] a, params string[] args) { } + + public void Method4(params string[] a, params string[] args) { } // Error [CS0231] } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb index 366a0d184ea..3bbc6a6b57e 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb @@ -1,17 +1,27 @@ -Public Class Program - + +Public Class Program Public Sub Base(ByVal myArray As String()) - Method(New String() {"s1", "s2"}) ' Noncompliant: unnecessary - Method("s1") 'Compliant - Method("s1", "s2") 'Compliant - Method(myArray) 'Compliant - Method(New String(12) {}) 'Compliant + Method(New String() {"s1", "s2"}) ' Noncompliant {{Arrays should not be created for ParamArray parameters.}} + ' ^^^^^^^^^^^^^^^^^^^^^^^^^ + Method(New String() {"s1"}) ' Noncompliant + Method("s1") ' Compliant + Method("s1", "s2") ' Compliant + Method(myArray) ' Compliant + Method(New String(11) {}) ' Compliant + + Method2(1, New String() {"s1", "s2"}) ' Noncompliant {{Arrays should not be created for ParamArray parameters.}} + ' ^^^^^^^^^^^^^^^^^^^^^^^^^ + Method2(1, New String() {"s1"}) ' Noncompliant + Method2(1, "s1") ' Compliant + Method2(1, "s1", "s2") ' Compliant + Method2(1, myArray) ' Compliant + Method2(1, New String(11) {}) ' Compliant - Method2(1, New String() {"s1", "s2"}) ' Noncompliant: unnecessary - Method2(1, "s1") 'Compliant - Method2(1, "s1", "s2") 'Compliant - Method2(1, myArray) 'Compliant - Method2(1, New String(11) {}) 'Compliant + Method3(New String() {"s1", "s2"}, "s1") ' Compliant + Method3(New String() {"s1", "s2"}, New String(11) {}) ' Compliant + Method3(New String() {"s1", "s2"}, New String() {"s1", "s2"}) ' Noncompliant + ' ^^^^^^^^^^^^^^^^^^^^^^^^^ + Method3(Nothing, Nothing) ' Compliant End Sub Public Sub Method(ParamArray args As String()) @@ -20,4 +30,9 @@ Public Sub Method2(ByVal a As Integer, ParamArray args As String()) End Sub + Public Sub Method3(ByVal a As String(), ParamArray args As String()) + End Sub + + Public Sub Method4(ParamArray a As String(), ParamArray args As String()) 'Error [CS0231] + End Sub End Class From 08d227d83b985d0866de1335dac814a982d3fbaa Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Tue, 24 Jan 2023 12:45:28 +0100 Subject: [PATCH 06/40] Common code to baseclass --- .../src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs | 7 ++----- .../SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs | 7 +++++-- .../SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs | 7 ++----- .../Rules/ArrayPassedAsParamsTest.cs | 7 ++----- .../TestCases/ArrayPassedAsParams.vb | 2 +- 5 files changed, 12 insertions(+), 18 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs index bc5869fd3e6..61694bec3ac 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs @@ -27,14 +27,11 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase "params"; protected override string MessageFormat => MessageBase; - protected override bool ShouldReport(SonarSyntaxNodeReportingContext context, InvocationExpressionSyntax invocation) => + protected override bool ShouldReport(InvocationExpressionSyntax invocation) => invocation.ArgumentList.Arguments.Count > 0 && invocation.ArgumentList.Arguments.Last().Expression is ArrayCreationExpressionSyntax array && array.Initializer is InitializerExpressionSyntax initializer - && initializer.Expressions.Count > 0 - && context.SemanticModel.GetSymbolInfo(invocation).Symbol is IMethodSymbol invokedMethodSymbol - && invokedMethodSymbol.Parameters.Any() - && invokedMethodSymbol.Parameters.Last().IsParams; // No additional parameters are permitted after the params keyword in a method declaration, and only one params keyword is permitted in a method declaration. + && initializer.Expressions.Count > 0; protected override Location GetLocation(InvocationExpressionSyntax invocation) => invocation.ArgumentList.Arguments.Last().Expression.GetLocation(); diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs index 1a0da2f80ed..bde778455fd 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -30,7 +30,7 @@ public abstract class ArrayPassedAsParamsBase "ParamArray"; protected override string MessageFormat => MessageBase; - protected override bool ShouldReport(SonarSyntaxNodeReportingContext context, InvocationExpressionSyntax invocation) => + protected override bool ShouldReport(InvocationExpressionSyntax invocation) => invocation.ArgumentList.Arguments.Count > 0 && invocation.ArgumentList.Arguments.Last().GetExpression() is ArrayCreationExpressionSyntax array && array.Initializer is CollectionInitializerSyntax initializer - && initializer.Initializers.Count > 0 - && context.SemanticModel.GetSymbolInfo(invocation).Symbol is IMethodSymbol invokedMethodSymbol - && invokedMethodSymbol.Parameters.Any() - && invokedMethodSymbol.Parameters.Last().IsParams; // No additional parameters are permitted after the params keyword in a method declaration, and only one params keyword is permitted in a method declaration. + && initializer.Initializers.Count > 0; protected override Location GetLocation(InvocationExpressionSyntax invocation) => invocation.ArgumentList.Arguments.Last().GetExpression().GetLocation(); diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/ArrayPassedAsParamsTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/ArrayPassedAsParamsTest.cs index 0b75b12395a..c1c007c32f3 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/ArrayPassedAsParamsTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/ArrayPassedAsParamsTest.cs @@ -27,14 +27,11 @@ namespace SonarAnalyzer.UnitTest.Rules; [TestClass] public class ArrayPassedAsParamsTest { - private readonly VerifierBuilder builderCS = new VerifierBuilder(); - private readonly VerifierBuilder builderVB = new VerifierBuilder(); - [TestMethod] public void ArrayPassedAsParams_CS() => - builderCS.AddPaths("ArrayPassedAsParams.cs").Verify(); + new VerifierBuilder().AddPaths("ArrayPassedAsParams.cs").Verify(); [TestMethod] public void ArrayPassedAsParams_VB() => - builderVB.AddPaths("ArrayPassedAsParams.vb").Verify(); + new VerifierBuilder().AddPaths("ArrayPassedAsParams.vb").Verify(); } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb index 3bbc6a6b57e..ca012c90989 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb @@ -15,7 +15,7 @@ Public Class Program Method2(1, "s1") ' Compliant Method2(1, "s1", "s2") ' Compliant Method2(1, myArray) ' Compliant - Method2(1, New String(11) {}) ' Compliant + Method2(1, New String(11) {}) ' Compliant Method3(New String() {"s1", "s2"}, "s1") ' Compliant Method3(New String() {"s1", "s2"}, New String(11) {}) ' Compliant From eebe4d4a3acf154815b6abe70ad90b7c2ad4e550 Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Tue, 24 Jan 2023 13:57:55 +0100 Subject: [PATCH 07/40] Move semanticModel queries after syntax checks --- .../src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs index bde778455fd..8ba80a27f6b 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -44,10 +44,10 @@ protected ArrayPassedAsParamsBase() : base(DiagnosticId) private void CheckInvocation(SonarSyntaxNodeReportingContext context) { if ((TInvocationExpressionSyntax)context.Node is var invocation + && ShouldReport(invocation) && context.SemanticModel.GetSymbolInfo(invocation).Symbol is IMethodSymbol invokedMethodSymbol && invokedMethodSymbol.Parameters.Any() - && invokedMethodSymbol.Parameters.Last().IsParams // No additional parameters are permitted after the params keyword in a method declaration, and only one params keyword is permitted in a method declaration. - && ShouldReport(invocation)) + && invokedMethodSymbol.Parameters.Last().IsParams) // No additional parameters are permitted after the params keyword in a method declaration, and only one params keyword is permitted in a method declaration. { context.ReportIssue(Diagnostic.Create(rule, GetLocation(invocation), ParameterKeyword)); } From 044fd0da596fa99e0cbd7a76c406a56853902eee Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Tue, 24 Jan 2023 14:21:17 +0100 Subject: [PATCH 08/40] Add test case --- .../TestCases/ArrayPassedAsParams.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs index af5eec8750d..4eb406945ab 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs @@ -24,7 +24,9 @@ public void Base(string[] myArray) Method3(new string[] { "s1", "s2" }, new string[12]); // Compliant Method3(new string[] { "s1", "s2" }, new string[] { "s1", "s2" }); // Noncompliant // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Method3(null, null); // Compliant + Method3(null, null); // Compliant + Method4(new [] { "s1" }); // Compliant + Method4(new [] { "s1", "s2" }); // Compliant } public void Method(params string[] args) { } @@ -33,5 +35,7 @@ public void Base(string[] myArray) public void Method3(string[] a, params string[] args) { } - public void Method4(params string[] a, params string[] args) { } // Error [CS0231] + public void Method4(object[] a, params object[] args) { } + + public void Method5(params string[] a, params string[] args) { } // Error [CS0231] } From 0916ce71510cad70c0051c63d5ac753a52dcf821 Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Tue, 24 Jan 2023 16:25:28 +0100 Subject: [PATCH 09/40] Fix some code smell --- .../src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs | 2 +- .../SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs | 6 +++--- .../SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs | 2 +- .../SonarAnalyzer.UnitTest/Rules/ArrayPassedAsParamsTest.cs | 1 - .../SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs | 3 +++ 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs index 61694bec3ac..86c3ccdef3f 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs @@ -1,6 +1,6 @@ /* * SonarAnalyzer for .NET - * Copyright (C) 2015-2022 SonarSource SA + * Copyright (C) 2015-2023 SonarSource SA * mailto: contact AT sonarsource DOT com * * This program is free software; you can redistribute it and/or diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs index 8ba80a27f6b..b2830caed6f 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -1,6 +1,6 @@ /* * SonarAnalyzer for .NET - * Copyright (C) 2015-2022 SonarSource SA + * Copyright (C) 2015-2023 SonarSource SA * mailto: contact AT sonarsource DOT com * * This program is free software; you can redistribute it and/or @@ -26,7 +26,7 @@ public abstract class ArrayPassedAsParamsBase Date: Wed, 25 Jan 2023 11:14:22 +0100 Subject: [PATCH 10/40] Update the list of expected issues --- ...AB-AF12-4012-B945-284C2448DC81}-S3878.json | 342 ++++++++++++++++++ ...5E-C6AE-4D2D-A9DD-B6EFD19A4279}-S3878.json | 69 ++++ ...3A-D04F-4262-923D-21AEDF86E2B7}-S3878.json | 56 +++ ...CF-D15A-4BF1-B980-395CEC0004A9}-S3878.json | 173 +++++++++ ...es.Razor.BuildProviders--net452-S3878.json | 30 ++ ...DistributedData--netstandard2.0-S3878.json | 30 ++ 6 files changed, 700 insertions(+) create mode 100644 analyzers/its/expected/Ember-MM/Ember Media Manager-{9B57D3AB-AF12-4012-B945-284C2448DC81}-S3878.json create mode 100644 analyzers/its/expected/Ember-MM/EmberAPI-{208AA35E-C6AE-4D2D-A9DD-B6EFD19A4279}-S3878.json create mode 100644 analyzers/its/expected/Ember-MM/generic.EmberCore.NMT-{84B2143A-D04F-4262-923D-21AEDF86E2B7}-S3878.json create mode 100644 analyzers/its/expected/Ember-MM/multi.EmberExtras-{62356BCF-D15A-4BF1-B980-395CEC0004A9}-S3878.json create mode 100644 analyzers/its/expected/Nancy/Nancy.ViewEngines.Razor.BuildProviders--net452-S3878.json create mode 100644 analyzers/its/expected/akka.net/Akka.DistributedData--netstandard2.0-S3878.json diff --git a/analyzers/its/expected/Ember-MM/Ember Media Manager-{9B57D3AB-AF12-4012-B945-284C2448DC81}-S3878.json b/analyzers/its/expected/Ember-MM/Ember Media Manager-{9B57D3AB-AF12-4012-B945-284C2448DC81}-S3878.json new file mode 100644 index 00000000000..604697bd481 --- /dev/null +++ b/analyzers/its/expected/Ember-MM/Ember Media Manager-{9B57D3AB-AF12-4012-B945-284C2448DC81}-S3878.json @@ -0,0 +1,342 @@ +{ +"issues": [ +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", +"region": { +"startLine": 6583, +"startColumn": 71, +"endLine": 6583, +"endColumn": 102 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", +"region": { +"startLine": 6881, +"startColumn": 47, +"endLine": 6881, +"endColumn": 94 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", +"region": { +"startLine": 6882, +"startColumn": 47, +"endLine": 6882, +"endColumn": 130 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", +"region": { +"startLine": 6883, +"startColumn": 47, +"endLine": 6883, +"endColumn": 130 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", +"region": { +"startLine": 6884, +"startColumn": 47, +"endLine": 6884, +"endColumn": 132 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", +"region": { +"startLine": 6885, +"startColumn": 47, +"endLine": 6885, +"endColumn": 79 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", +"region": { +"startLine": 7017, +"startColumn": 47, +"endLine": 7017, +"endColumn": 93 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", +"region": { +"startLine": 7018, +"startColumn": 47, +"endLine": 7018, +"endColumn": 94 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", +"region": { +"startLine": 7019, +"startColumn": 47, +"endLine": 7019, +"endColumn": 83 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", +"region": { +"startLine": 7020, +"startColumn": 47, +"endLine": 7020, +"endColumn": 83 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", +"region": { +"startLine": 7021, +"startColumn": 47, +"endLine": 7021, +"endColumn": 80 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", +"region": { +"startLine": 7022, +"startColumn": 47, +"endLine": 7022, +"endColumn": 84 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", +"region": { +"startLine": 7023, +"startColumn": 47, +"endLine": 7023, +"endColumn": 80 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", +"region": { +"startLine": 7024, +"startColumn": 47, +"endLine": 7024, +"endColumn": 82 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", +"region": { +"startLine": 7025, +"startColumn": 47, +"endLine": 7025, +"endColumn": 80 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", +"region": { +"startLine": 7026, +"startColumn": 47, +"endLine": 7026, +"endColumn": 97 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", +"region": { +"startLine": 7027, +"startColumn": 47, +"endLine": 7027, +"endColumn": 101 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", +"region": { +"startLine": 7028, +"startColumn": 47, +"endLine": 7028, +"endColumn": 97 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", +"region": { +"startLine": 7107, +"startColumn": 43, +"endLine": 7107, +"endColumn": 137 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", +"region": { +"startLine": 7109, +"startColumn": 47, +"endLine": 7109, +"endColumn": 141 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", +"region": { +"startLine": 7111, +"startColumn": 43, +"endLine": 7111, +"endColumn": 75 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", +"region": { +"startLine": 7193, +"startColumn": 47, +"endLine": 7193, +"endColumn": 96 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", +"region": { +"startLine": 7194, +"startColumn": 47, +"endLine": 7194, +"endColumn": 130 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", +"region": { +"startLine": 7195, +"startColumn": 47, +"endLine": 7195, +"endColumn": 130 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", +"region": { +"startLine": 7196, +"startColumn": 47, +"endLine": 7196, +"endColumn": 134 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", +"region": { +"startLine": 7197, +"startColumn": 47, +"endLine": 7197, +"endColumn": 79 +} +} +} +] +} diff --git a/analyzers/its/expected/Ember-MM/EmberAPI-{208AA35E-C6AE-4D2D-A9DD-B6EFD19A4279}-S3878.json b/analyzers/its/expected/Ember-MM/EmberAPI-{208AA35E-C6AE-4D2D-A9DD-B6EFD19A4279}-S3878.json new file mode 100644 index 00000000000..d38f229bf64 --- /dev/null +++ b/analyzers/its/expected/Ember-MM/EmberAPI-{208AA35E-C6AE-4D2D-A9DD-B6EFD19A4279}-S3878.json @@ -0,0 +1,69 @@ +{ +"issues": [ +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\EmberAPI\clsAPIMediaContainers.vb", +"region": { +"startLine": 1130, +"startColumn": 54, +"endLine": 1130, +"endColumn": 73 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\EmberAPI\clsAPIMediaContainers.vb", +"region": { +"startLine": 1148, +"startColumn": 54, +"endLine": 1148, +"endColumn": 73 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\EmberAPI\clsAPIMediaContainers.vb", +"region": { +"startLine": 1167, +"startColumn": 54, +"endLine": 1167, +"endColumn": 73 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\EmberAPI\clsAPIMediaContainers.vb", +"region": { +"startLine": 1186, +"startColumn": 54, +"endLine": 1186, +"endColumn": 73 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\EmberAPI\clsAPIMediaContainers.vb", +"region": { +"startLine": 1677, +"startColumn": 54, +"endLine": 1677, +"endColumn": 73 +} +} +} +] +} diff --git a/analyzers/its/expected/Ember-MM/generic.EmberCore.NMT-{84B2143A-D04F-4262-923D-21AEDF86E2B7}-S3878.json b/analyzers/its/expected/Ember-MM/generic.EmberCore.NMT-{84B2143A-D04F-4262-923D-21AEDF86E2B7}-S3878.json new file mode 100644 index 00000000000..5d25b12074a --- /dev/null +++ b/analyzers/its/expected/Ember-MM/generic.EmberCore.NMT-{84B2143A-D04F-4262-923D-21AEDF86E2B7}-S3878.json @@ -0,0 +1,56 @@ +{ +"issues": [ +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Addons\generic.EmberCore.NMT\dlgNMTMovies.vb", +"region": { +"startLine": 127, +"startColumn": 56, +"endLine": 127, +"endColumn": 126 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Addons\generic.EmberCore.NMT\dlgNMTMovies.vb", +"region": { +"startLine": 133, +"startColumn": 56, +"endLine": 133, +"endColumn": 129 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Addons\generic.EmberCore.NMT\dlgNMTMovies.vb", +"region": { +"startLine": 916, +"startColumn": 38, +"endLine": 916, +"endColumn": 59 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Addons\generic.EmberCore.NMT\dlgNMTMovies.vb", +"region": { +"startLine": 948, +"startColumn": 40, +"endLine": 948, +"endColumn": 62 +} +} +} +] +} diff --git a/analyzers/its/expected/Ember-MM/multi.EmberExtras-{62356BCF-D15A-4BF1-B980-395CEC0004A9}-S3878.json b/analyzers/its/expected/Ember-MM/multi.EmberExtras-{62356BCF-D15A-4BF1-B980-395CEC0004A9}-S3878.json new file mode 100644 index 00000000000..25f6eea7190 --- /dev/null +++ b/analyzers/its/expected/Ember-MM/multi.EmberExtras-{62356BCF-D15A-4BF1-B980-395CEC0004A9}-S3878.json @@ -0,0 +1,173 @@ +{ +"issues": [ +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Addons\multi.EmberExtras\frmAVCodecEditor.vb", +"region": { +"startLine": 12, +"startColumn": 50, +"endLine": 12, +"endColumn": 100 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Addons\multi.EmberExtras\frmAVCodecEditor.vb", +"region": { +"startLine": 24, +"startColumn": 50, +"endLine": 24, +"endColumn": 100 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Addons\multi.EmberExtras\frmAVCodecEditor.vb", +"region": { +"startLine": 40, +"startColumn": 46, +"endLine": 40, +"endColumn": 87 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Addons\multi.EmberExtras\frmAVCodecEditor.vb", +"region": { +"startLine": 47, +"startColumn": 46, +"endLine": 47, +"endColumn": 87 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Addons\multi.EmberExtras\frmGenresEditor.vb", +"region": { +"startLine": 34, +"startColumn": 34, +"endLine": 34, +"endColumn": 57 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Addons\multi.EmberExtras\frmGenresEditor.vb", +"region": { +"startLine": 48, +"startColumn": 59, +"endLine": 48, +"endColumn": 91 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Addons\multi.EmberExtras\frmGenresEditor.vb", +"region": { +"startLine": 57, +"startColumn": 59, +"endLine": 57, +"endColumn": 91 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Addons\multi.EmberExtras\frmGenresEditor.vb", +"region": { +"startLine": 160, +"startColumn": 47, +"endLine": 160, +"endColumn": 74 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Addons\multi.EmberExtras\frmGenresEditor.vb", +"region": { +"startLine": 173, +"startColumn": 45, +"endLine": 173, +"endColumn": 68 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Addons\multi.EmberExtras\frmMediaSourcesEditor.vb", +"region": { +"startLine": 17, +"startColumn": 55, +"endLine": 17, +"endColumn": 105 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Addons\multi.EmberExtras\frmMediaSourcesEditor.vb", +"region": { +"startLine": 30, +"startColumn": 52, +"endLine": 30, +"endColumn": 109 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Addons\multi.EmberExtras\frmMediaSourcesEditor.vb", +"region": { +"startLine": 37, +"startColumn": 48, +"endLine": 37, +"endColumn": 89 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Addons\multi.EmberExtras\frmMediaSourcesEditor.vb", +"region": { +"startLine": 127, +"startColumn": 47, +"endLine": 127, +"endColumn": 88 +} +} +} +] +} diff --git a/analyzers/its/expected/Nancy/Nancy.ViewEngines.Razor.BuildProviders--net452-S3878.json b/analyzers/its/expected/Nancy/Nancy.ViewEngines.Razor.BuildProviders--net452-S3878.json new file mode 100644 index 00000000000..1705303d238 --- /dev/null +++ b/analyzers/its/expected/Nancy/Nancy.ViewEngines.Razor.BuildProviders--net452-S3878.json @@ -0,0 +1,30 @@ +{ +"issues": [ +{ +"id": "S3878", +"message": "Arrays should not be created for params parameters.", +"location": { +"uri": "sources\Nancy\src\Nancy.ViewEngines.Razor.BuildProviders\NancyCSharpRazorBuildProvider.cs", +"region": { +"startLine": 47, +"startColumn": 104, +"endLine": 47, +"endColumn": 175 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for params parameters.", +"location": { +"uri": "sources\Nancy\src\Nancy.ViewEngines.Razor.BuildProviders\NancyCSharpRazorBuildProvider.cs", +"region": { +"startLine": 57, +"startColumn": 106, +"endLine": 57, +"endColumn": 177 +} +} +} +] +} diff --git a/analyzers/its/expected/akka.net/Akka.DistributedData--netstandard2.0-S3878.json b/analyzers/its/expected/akka.net/Akka.DistributedData--netstandard2.0-S3878.json new file mode 100644 index 00000000000..cfc7a1dcf76 --- /dev/null +++ b/analyzers/its/expected/akka.net/Akka.DistributedData--netstandard2.0-S3878.json @@ -0,0 +1,30 @@ +{ +"issues": [ +{ +"id": "S3878", +"message": "Arrays should not be created for params parameters.", +"location": { +"uri": "sources\akka.net\src\contrib\cluster\Akka.DistributedData\Replicator.cs", +"region": { +"startLine": 270, +"startColumn": 73, +"endLine": 270, +"endColumn": 89 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for params parameters.", +"location": { +"uri": "sources\akka.net\src\contrib\cluster\Akka.DistributedData\Replicator.cs", +"region": { +"startLine": 271, +"startColumn": 77, +"endLine": 271, +"endColumn": 95 +} +} +} +] +} From cdc92e95eaa733d7dc8e4ece276222ceb0ab0f1f Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Wed, 25 Jan 2023 12:22:36 +0100 Subject: [PATCH 11/40] Add support for ObjectCreationExpression --- .../Rules/ArrayPassedAsParams.cs | 21 +++++++++--- .../Rules/ArrayPassedAsParamsBase.cs | 33 +++++++++++++++---- .../Rules/ArrayPassedAsParams.cs | 21 +++++++++--- .../TestCases/ArrayPassedAsParams.cs | 13 ++++++-- 4 files changed, 70 insertions(+), 18 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs index 86c3ccdef3f..8fec0e2960e 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs @@ -21,18 +21,29 @@ namespace SonarAnalyzer.Rules.CSharp; [DiagnosticAnalyzer(LanguageNames.CSharp)] -public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase +public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase { protected override ILanguageFacade Language => CSharpFacade.Instance; protected override string ParameterKeyword => "params"; protected override string MessageFormat => MessageBase; - protected override bool ShouldReport(InvocationExpressionSyntax invocation) => + protected override bool ShouldReportInvocation(InvocationExpressionSyntax invocation) => invocation.ArgumentList.Arguments.Count > 0 && invocation.ArgumentList.Arguments.Last().Expression is ArrayCreationExpressionSyntax array - && array.Initializer is InitializerExpressionSyntax initializer - && initializer.Expressions.Count > 0; + && CheckArrayCreation(array); + + protected override bool ShouldReportCreation(ObjectCreationExpressionSyntax creation) => + creation.ArgumentList.Arguments.Count > 0 + && creation.ArgumentList.Arguments.Last().Expression is ArrayCreationExpressionSyntax array + && CheckArrayCreation(array); - protected override Location GetLocation(InvocationExpressionSyntax invocation) => + protected override Location GetInvocationLocation(InvocationExpressionSyntax invocation) => invocation.ArgumentList.Arguments.Last().Expression.GetLocation(); + + protected override Location GetCreationLocation(ObjectCreationExpressionSyntax creation) => + creation.ArgumentList.Arguments.Last().Expression.GetLocation(); + + private bool CheckArrayCreation(ArrayCreationExpressionSyntax array) => + array.Initializer is InitializerExpressionSyntax initializer + && initializer.Expressions.Count > 0; } diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs index b2830caed6f..04dad2bc91e 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -18,11 +18,15 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +using System.Xml.Linq; +using Microsoft.CodeAnalysis.CSharp.Syntax; + namespace SonarAnalyzer.Rules; -public abstract class ArrayPassedAsParamsBase : SonarDiagnosticAnalyzer +public abstract class ArrayPassedAsParamsBase : SonarDiagnosticAnalyzer where TSyntaxKind : struct where TInvocationExpressionSyntax : SyntaxNode + where TObjectCreationExpressionSyntax : SyntaxNode { private const string DiagnosticId = "S3878"; @@ -30,26 +34,43 @@ public abstract class ArrayPassedAsParamsBase + protected sealed override void Initialize(SonarAnalysisContext context) + { context.RegisterNodeAction(Language.GeneratedCodeRecognizer, CheckInvocation, Language.SyntaxKind.InvocationExpression); + context.RegisterNodeAction(Language.GeneratedCodeRecognizer, CheckObjectCreation, Language.SyntaxKind.ObjectCreationExpressions); + } private void CheckInvocation(SonarSyntaxNodeReportingContext context) { if ((TInvocationExpressionSyntax)context.Node is var invocation - && ShouldReport(invocation) + && ShouldReportInvocation(invocation) && context.SemanticModel.GetSymbolInfo(invocation).Symbol is IMethodSymbol invokedMethodSymbol && invokedMethodSymbol.Parameters.Any() && invokedMethodSymbol.Parameters.Last().IsParams) // params keyword should be only one and no additional parameters are permitted after that. { - context.ReportIssue(Diagnostic.Create(rule, GetLocation(invocation), ParameterKeyword)); + context.ReportIssue(Diagnostic.Create(rule, GetInvocationLocation(invocation), ParameterKeyword)); + } + } + + private void CheckObjectCreation(SonarSyntaxNodeReportingContext context) + { + if ((TObjectCreationExpressionSyntax)context.Node is var creation + && ShouldReportCreation(creation) + && context.SemanticModel.GetSymbolInfo(creation).Symbol is IMethodSymbol invokedMethodSymbol + && invokedMethodSymbol.Parameters.Any() + && invokedMethodSymbol.Parameters.Last().IsParams) // params keyword should be only one and no additional parameters are permitted after that. + { + context.ReportIssue(Diagnostic.Create(rule, GetCreationLocation(creation), ParameterKeyword)); } } } diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs index fe103600e1d..462d8aebdc0 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs @@ -21,18 +21,29 @@ namespace SonarAnalyzer.Rules.VisualBasic; [DiagnosticAnalyzer(LanguageNames.VisualBasic)] -public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase +public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase { protected override ILanguageFacade Language => VisualBasicFacade.Instance; protected override string ParameterKeyword => "ParamArray"; protected override string MessageFormat => MessageBase; - protected override bool ShouldReport(InvocationExpressionSyntax invocation) => + protected override bool ShouldReportInvocation(InvocationExpressionSyntax invocation) => invocation.ArgumentList.Arguments.Count > 0 && invocation.ArgumentList.Arguments.Last().GetExpression() is ArrayCreationExpressionSyntax array - && array.Initializer is CollectionInitializerSyntax initializer - && initializer.Initializers.Count > 0; + && CheckArrayCreation(array); + + protected override bool ShouldReportCreation(ObjectCreationExpressionSyntax creation) => + creation.ArgumentList.Arguments.Count > 0 + && creation.ArgumentList.Arguments.Last().GetExpression() is ArrayCreationExpressionSyntax array + && CheckArrayCreation(array); - protected override Location GetLocation(InvocationExpressionSyntax invocation) => + protected override Location GetInvocationLocation(InvocationExpressionSyntax invocation) => invocation.ArgumentList.Arguments.Last().GetExpression().GetLocation(); + + protected override Location GetCreationLocation(ObjectCreationExpressionSyntax creation) => + creation.ArgumentList.Arguments.Last().GetExpression().GetLocation(); + + private bool CheckArrayCreation(ArrayCreationExpressionSyntax array) => + array.Initializer is CollectionInitializerSyntax initializer + && initializer.Initializers.Count > 0; } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs index 6562642b514..02b254dfede 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs @@ -28,8 +28,12 @@ public void Base(string[] myArray) Method4(new [] { "s1" }); // Compliant Method4(new [] { "s1", "s2" }); // Compliant - Method2(args: new string[] { "s1", "s2" }, a: 1); // Noncompliant - Method3(args: new string[12], a: new string[] { "s1", "s2" }) // Compliant + Method2(args: new string[] { "s1", "s2" }, a: 1); // FN + Method3(args: new string[12], a: new string[] { "s1", "s2" }); // Noncompliant FP + + var s = new MyClass(1, new int[] { 2, 3 }); // Noncompliant +// ^^^^^^^^^^^^^^^^^^ + var s1 = new MyClass(1, 2, 3); // Compliant } public void Method(params string[] args) { } @@ -42,3 +46,8 @@ public void Base(string[] myArray) public void Method5(params string[] a, params string[] args) { } // Error [CS0231] } + +public class MyClass +{ + public MyClass(int a, params int[] args) { } +} From 41b040c413a1522fa0ac2ddfd66966910162fcb4 Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Wed, 25 Jan 2023 12:51:38 +0100 Subject: [PATCH 12/40] Add VB test cases --- .../TestCases/ArrayPassedAsParams.vb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb index ca012c90989..f8faab6de22 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb @@ -35,4 +35,12 @@ Public Class Program Public Sub Method4(ParamArray a As String(), ParamArray args As String()) 'Error [CS0231] End Sub + + Private a = New [MyClass](1, New Integer() {2, 3}) ' Noncompliant + Private b = New [MyClass](1, 2, 3) ' Compliant +End Class + +Public Class [MyClass] + Public Sub New(ByVal a As Integer, ParamArray args As Integer()) + End Sub End Class From c4f07c6f0f91db020a2cb7f1a15a71b040ac4af9 Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Wed, 25 Jan 2023 14:20:09 +0100 Subject: [PATCH 13/40] Defensive checks and fix ITs for ObjectCreation --- ...31-1F7B-4637-9B3A-806988DE50CF}-S3878.json | 108 ++++++++++++++++++ .../Akka.Streams--netstandard2.0-S3878.json | 17 +++ .../Rules/ArrayPassedAsParams.cs | 3 +- .../Rules/ArrayPassedAsParamsBase.cs | 4 +- .../Rules/ArrayPassedAsParams.cs | 3 +- 5 files changed, 131 insertions(+), 4 deletions(-) create mode 100644 analyzers/its/expected/Ember-MM/scraper.EmberCore.XML-{E567C031-1F7B-4637-9B3A-806988DE50CF}-S3878.json create mode 100644 analyzers/its/expected/akka.net/Akka.Streams--netstandard2.0-S3878.json diff --git a/analyzers/its/expected/Ember-MM/scraper.EmberCore.XML-{E567C031-1F7B-4637-9B3A-806988DE50CF}-S3878.json b/analyzers/its/expected/Ember-MM/scraper.EmberCore.XML-{E567C031-1F7B-4637-9B3A-806988DE50CF}-S3878.json new file mode 100644 index 00000000000..e3cc88bfba7 --- /dev/null +++ b/analyzers/its/expected/Ember-MM/scraper.EmberCore.XML-{E567C031-1F7B-4637-9B3A-806988DE50CF}-S3878.json @@ -0,0 +1,108 @@ +{ +"issues": [ +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Addons\scraper.EmberCore.XML\XMLScraper\MediaTags\AlbumTag.vb", +"region": { +"startLine": 304, +"startColumn": 97, +"endLine": 304, +"endColumn": 138 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Addons\scraper.EmberCore.XML\XMLScraper\MediaTags\AlbumTag.vb", +"region": { +"startLine": 411, +"startColumn": 97, +"endLine": 411, +"endColumn": 138 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Addons\scraper.EmberCore.XML\XMLScraper\MediaTags\ArtistTag.vb", +"region": { +"startLine": 291, +"startColumn": 97, +"endLine": 291, +"endColumn": 138 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Addons\scraper.EmberCore.XML\XMLScraper\MediaTags\MovieTag.vb", +"region": { +"startLine": 446, +"startColumn": 97, +"endLine": 446, +"endColumn": 138 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Addons\scraper.EmberCore.XML\XMLScraper\MediaTags\MusicVideoTag.vb", +"region": { +"startLine": 160, +"startColumn": 97, +"endLine": 160, +"endColumn": 138 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Addons\scraper.EmberCore.XML\XMLScraper\MediaTags\PersonInfo.vb", +"region": { +"startLine": 272, +"startColumn": 97, +"endLine": 272, +"endColumn": 138 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Addons\scraper.EmberCore.XML\XMLScraper\MediaTags\TVEpisodeTag.vb", +"region": { +"startLine": 224, +"startColumn": 97, +"endLine": 224, +"endColumn": 138 +} +} +}, +{ +"id": "S3878", +"message": "Arrays should not be created for ParamArray parameters.", +"location": { +"uri": "sources\Ember-MM\Addons\scraper.EmberCore.XML\XMLScraper\MediaTags\TVShow.vb", +"region": { +"startLine": 261, +"startColumn": 97, +"endLine": 261, +"endColumn": 138 +} +} +} +] +} diff --git a/analyzers/its/expected/akka.net/Akka.Streams--netstandard2.0-S3878.json b/analyzers/its/expected/akka.net/Akka.Streams--netstandard2.0-S3878.json new file mode 100644 index 00000000000..2495df7624a --- /dev/null +++ b/analyzers/its/expected/akka.net/Akka.Streams--netstandard2.0-S3878.json @@ -0,0 +1,17 @@ +{ +"issues": [ +{ +"id": "S3878", +"message": "Arrays should not be created for params parameters.", +"location": { +"uri": "sources\akka.net\src\core\Akka.Streams\ActorMaterializer.cs", +"region": { +"startLine": 633, +"startColumn": 35, +"endLine": 645, +"endColumn": 18 +} +} +} +] +} diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs index 8fec0e2960e..2a59d2d555d 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs @@ -33,7 +33,8 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase - creation.ArgumentList.Arguments.Count > 0 + creation.ArgumentList is not null + && creation.ArgumentList.Arguments.Count > 0 && creation.ArgumentList.Arguments.Last().Expression is ArrayCreationExpressionSyntax array && CheckArrayCreation(array); diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs index 04dad2bc91e..5f6940217a2 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -52,7 +52,7 @@ protected sealed override void Initialize(SonarAnalysisContext context) private void CheckInvocation(SonarSyntaxNodeReportingContext context) { - if ((TInvocationExpressionSyntax)context.Node is var invocation + if (context.Node is TInvocationExpressionSyntax invocation && ShouldReportInvocation(invocation) && context.SemanticModel.GetSymbolInfo(invocation).Symbol is IMethodSymbol invokedMethodSymbol && invokedMethodSymbol.Parameters.Any() @@ -64,7 +64,7 @@ private void CheckInvocation(SonarSyntaxNodeReportingContext context) private void CheckObjectCreation(SonarSyntaxNodeReportingContext context) { - if ((TObjectCreationExpressionSyntax)context.Node is var creation + if (context.Node is TObjectCreationExpressionSyntax creation && ShouldReportCreation(creation) && context.SemanticModel.GetSymbolInfo(creation).Symbol is IMethodSymbol invokedMethodSymbol && invokedMethodSymbol.Parameters.Any() diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs index 462d8aebdc0..55d21bd2a0e 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs @@ -33,7 +33,8 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase - creation.ArgumentList.Arguments.Count > 0 + creation.ArgumentList is not null + && creation.ArgumentList.Arguments.Count > 0 && creation.ArgumentList.Arguments.Last().GetExpression() is ArrayCreationExpressionSyntax array && CheckArrayCreation(array); From 1f4ecab55812c0616c815b064a7a031dfc89e125 Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Wed, 25 Jan 2023 14:37:57 +0100 Subject: [PATCH 14/40] Fix code smell --- .../src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs | 2 +- .../src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs | 3 --- .../src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs index 2a59d2d555d..fec369a695e 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs @@ -44,7 +44,7 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase creation.ArgumentList.Arguments.Last().Expression.GetLocation(); - private bool CheckArrayCreation(ArrayCreationExpressionSyntax array) => + private static bool CheckArrayCreation(ArrayCreationExpressionSyntax array) => array.Initializer is InitializerExpressionSyntax initializer && initializer.Expressions.Count > 0; } diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs index 5f6940217a2..35bc6c9e9aa 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -18,9 +18,6 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using System.Xml.Linq; -using Microsoft.CodeAnalysis.CSharp.Syntax; - namespace SonarAnalyzer.Rules; public abstract class ArrayPassedAsParamsBase : SonarDiagnosticAnalyzer diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs index 55d21bd2a0e..92386e57b4b 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs @@ -44,7 +44,7 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase creation.ArgumentList.Arguments.Last().GetExpression().GetLocation(); - private bool CheckArrayCreation(ArrayCreationExpressionSyntax array) => + private static bool CheckArrayCreation(ArrayCreationExpressionSyntax array) => array.Initializer is CollectionInitializerSyntax initializer && initializer.Initializers.Count > 0; } From 27b74e918bf16ee5cbc43e7c825523aeef1c912a Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Wed, 25 Jan 2023 15:18:13 +0100 Subject: [PATCH 15/40] Shared method --- .../SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs | 6 +++--- .../Rules/ArrayPassedAsParamsBase.cs | 11 ++++++----- .../Rules/ArrayPassedAsParams.cs | 6 +++--- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs index fec369a695e..3ad42957fac 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs @@ -30,13 +30,13 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase invocation.ArgumentList.Arguments.Count > 0 && invocation.ArgumentList.Arguments.Last().Expression is ArrayCreationExpressionSyntax array - && CheckArrayCreation(array); + && CheckArrayInitializer(array); protected override bool ShouldReportCreation(ObjectCreationExpressionSyntax creation) => creation.ArgumentList is not null && creation.ArgumentList.Arguments.Count > 0 && creation.ArgumentList.Arguments.Last().Expression is ArrayCreationExpressionSyntax array - && CheckArrayCreation(array); + && CheckArrayInitializer(array); protected override Location GetInvocationLocation(InvocationExpressionSyntax invocation) => invocation.ArgumentList.Arguments.Last().Expression.GetLocation(); @@ -44,7 +44,7 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase creation.ArgumentList.Arguments.Last().Expression.GetLocation(); - private static bool CheckArrayCreation(ArrayCreationExpressionSyntax array) => + private static bool CheckArrayInitializer(ArrayCreationExpressionSyntax array) => array.Initializer is InitializerExpressionSyntax initializer && initializer.Expressions.Count > 0; } diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs index 35bc6c9e9aa..6fdcb908253 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -36,10 +36,8 @@ public abstract class ArrayPassedAsParamsBase rule = Language.CreateDescriptor(DiagnosticId, MessageFormat); - } protected sealed override void Initialize(SonarAnalysisContext context) { @@ -55,7 +53,7 @@ private void CheckInvocation(SonarSyntaxNodeReportingContext context) && invokedMethodSymbol.Parameters.Any() && invokedMethodSymbol.Parameters.Last().IsParams) // params keyword should be only one and no additional parameters are permitted after that. { - context.ReportIssue(Diagnostic.Create(rule, GetInvocationLocation(invocation), ParameterKeyword)); + Report(context, GetInvocationLocation(invocation)); } } @@ -67,7 +65,10 @@ private void CheckObjectCreation(SonarSyntaxNodeReportingContext context) && invokedMethodSymbol.Parameters.Any() && invokedMethodSymbol.Parameters.Last().IsParams) // params keyword should be only one and no additional parameters are permitted after that. { - context.ReportIssue(Diagnostic.Create(rule, GetCreationLocation(creation), ParameterKeyword)); + Report(context, GetCreationLocation(creation)); } } + + private void Report(SonarSyntaxNodeReportingContext context, Location location) => + context.ReportIssue(Diagnostic.Create(rule, location, ParameterKeyword)); } diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs index 92386e57b4b..c624f9250e8 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs @@ -30,13 +30,13 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase invocation.ArgumentList.Arguments.Count > 0 && invocation.ArgumentList.Arguments.Last().GetExpression() is ArrayCreationExpressionSyntax array - && CheckArrayCreation(array); + && CheckArrayInitializer(array); protected override bool ShouldReportCreation(ObjectCreationExpressionSyntax creation) => creation.ArgumentList is not null && creation.ArgumentList.Arguments.Count > 0 && creation.ArgumentList.Arguments.Last().GetExpression() is ArrayCreationExpressionSyntax array - && CheckArrayCreation(array); + && CheckArrayInitializer(array); protected override Location GetInvocationLocation(InvocationExpressionSyntax invocation) => invocation.ArgumentList.Arguments.Last().GetExpression().GetLocation(); @@ -44,7 +44,7 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase creation.ArgumentList.Arguments.Last().GetExpression().GetLocation(); - private static bool CheckArrayCreation(ArrayCreationExpressionSyntax array) => + private static bool CheckArrayInitializer(ArrayCreationExpressionSyntax array) => array.Initializer is CollectionInitializerSyntax initializer && initializer.Initializers.Count > 0; } From 5ba1351b691ed6f64c0bfe0139ba269cc5abc791 Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Wed, 25 Jan 2023 18:40:26 +0100 Subject: [PATCH 16/40] Cheching method parameter to fix FN/FP --- .../Rules/ArrayPassedAsParams.cs | 14 +++++++++++--- .../Rules/ArrayPassedAsParamsBase.cs | 8 ++------ .../Helpers/VisualBasicMethodParameterLookup.cs | 3 +++ .../Rules/ArrayPassedAsParams.cs | 13 +++++++++---- .../TestCases/ArrayPassedAsParams.cs | 11 +++++++++-- .../TestCases/ArrayPassedAsParams.vb | 11 +++++++++++ 6 files changed, 45 insertions(+), 15 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs index 3ad42957fac..5e4ded5bc7a 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs @@ -18,6 +18,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +using Microsoft.CodeAnalysis; + namespace SonarAnalyzer.Rules.CSharp; [DiagnosticAnalyzer(LanguageNames.CSharp)] @@ -27,10 +29,16 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase "params"; protected override string MessageFormat => MessageBase; - protected override bool ShouldReportInvocation(InvocationExpressionSyntax invocation) => - invocation.ArgumentList.Arguments.Count > 0 + protected override bool ShouldReportInvocation(SonarSyntaxNodeReportingContext context, InvocationExpressionSyntax invocation) + { + var myLookup = new CSharpMethodParameterLookup(invocation, context.SemanticModel); + + return invocation.ArgumentList.Arguments.Count > 0 && invocation.ArgumentList.Arguments.Last().Expression is ArrayCreationExpressionSyntax array - && CheckArrayInitializer(array); + && CheckArrayInitializer(array) + && myLookup.TryGetSymbol(invocation.ArgumentList.Arguments.Last(), out var parameter) + && parameter.IsParams; + } protected override bool ShouldReportCreation(ObjectCreationExpressionSyntax creation) => creation.ArgumentList is not null diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs index 6fdcb908253..fd63cabff7d 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -31,7 +31,7 @@ public abstract class ArrayPassedAsParamsBase { + public VisualBasicMethodParameterLookup(InvocationExpressionSyntax invocation, SemanticModel semanticModel) + : this(invocation.ArgumentList, semanticModel) { } + public VisualBasicMethodParameterLookup(ArgumentListSyntax argumentList, IMethodSymbol methodSymbol) : base(argumentList?.Arguments, methodSymbol) { } diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs index c624f9250e8..257c8d98715 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs @@ -27,11 +27,16 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase "ParamArray"; protected override string MessageFormat => MessageBase; - protected override bool ShouldReportInvocation(InvocationExpressionSyntax invocation) => - invocation.ArgumentList.Arguments.Count > 0 - && invocation.ArgumentList.Arguments.Last().GetExpression() is ArrayCreationExpressionSyntax array - && CheckArrayInitializer(array); + protected override bool ShouldReportInvocation(SonarSyntaxNodeReportingContext context, InvocationExpressionSyntax invocation) + { + var myLookup = new VisualBasicMethodParameterLookup(invocation, context.SemanticModel); + return invocation.ArgumentList.Arguments.Count > 0 + && invocation.ArgumentList.Arguments.Last().GetExpression() is ArrayCreationExpressionSyntax array + && CheckArrayInitializer(array) + && myLookup.TryGetSymbol(invocation.ArgumentList.Arguments.Last(), out var parameter) + && parameter.IsParams; + } protected override bool ShouldReportCreation(ObjectCreationExpressionSyntax creation) => creation.ArgumentList is not null && creation.ArgumentList.Arguments.Count > 0 diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs index 02b254dfede..84c7834a6de 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs @@ -28,12 +28,14 @@ public void Base(string[] myArray) Method4(new [] { "s1" }); // Compliant Method4(new [] { "s1", "s2" }); // Compliant - Method2(args: new string[] { "s1", "s2" }, a: 1); // FN - Method3(args: new string[12], a: new string[] { "s1", "s2" }); // Noncompliant FP + Method3(args: new string[] { "s1", "s2" }, a: new string[12]); // Compliant (if you specifically require the arguments to be passed in this order there is no way of making this compliant, thus we shouldn't raise) + Method3(args: new string[12], a: new string[] { "s1", "s2" }); // Compliant var s = new MyClass(1, new int[] { 2, 3 }); // Noncompliant // ^^^^^^^^^^^^^^^^^^ var s1 = new MyClass(1, 2, 3); // Compliant + s1 = new MyClass(args: new int[] { 2, 3 }, a: 1); // Compliant (if you specifically require the arguments to be passed in this order there is no way of making this compliant, thus we shouldn't raise) + var s2 = new MyOtherClass(args: new int[12], a: new int[] { 2, 3 }); // Noncompliant FP } public void Method(params string[] args) { } @@ -51,3 +53,8 @@ public class MyClass { public MyClass(int a, params int[] args) { } } + +public class MyOtherClass +{ + public MyOtherClass(int[] a, params int[] args) { } +} diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb index f8faab6de22..7f4786a9899 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb @@ -22,6 +22,12 @@ Public Class Program Method3(New String() {"s1", "s2"}, New String() {"s1", "s2"}) ' Noncompliant ' ^^^^^^^^^^^^^^^^^^^^^^^^^ Method3(Nothing, Nothing) ' Compliant + + Dim s = New [MyClass](1, New Integer() {2, 3}) ' Noncompliant + ' ^^^^^^^^^^^^^^^^^^^^ + Dim s1 = New [MyClass](1, 2, 3) ' Compliant + s1 = New [MyClass](args:=New Integer() {2, 3}, a:=1) ' Error [BC30587] + Dim s2 = New MyOtherClass(args:=New Integer(11) {}, a:=New Integer() {2, 3}) ' Error [BC30587] End Sub Public Sub Method(ParamArray args As String()) @@ -44,3 +50,8 @@ Public Class [MyClass] Public Sub New(ByVal a As Integer, ParamArray args As Integer()) End Sub End Class + +Public Class MyOtherClass + Public Sub New(ByVal a As Integer(), ParamArray args As Integer()) + End Sub +End Class From a2db8b6df48261972b8a266a116038055f1ff976 Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Thu, 26 Jan 2023 09:31:34 +0100 Subject: [PATCH 17/40] Fixed CSharp constructor named argument FP --- .../CSharpConstructorParameterLookup.cs | 72 +++++++++++++++++++ .../Rules/ArrayPassedAsParams.cs | 24 ++++--- .../Rules/ArrayPassedAsParamsBase.cs | 8 +-- .../VisualBasicMethodParameterLookup.cs | 3 - .../Rules/ArrayPassedAsParams.cs | 21 +++--- .../TestCases/ArrayPassedAsParams.cs | 2 +- .../TestCases/ArrayPassedAsParams.vb | 7 +- 7 files changed, 106 insertions(+), 31 deletions(-) create mode 100644 analyzers/src/SonarAnalyzer.CSharp/Helpers/CSharpConstructorParameterLookup.cs diff --git a/analyzers/src/SonarAnalyzer.CSharp/Helpers/CSharpConstructorParameterLookup.cs b/analyzers/src/SonarAnalyzer.CSharp/Helpers/CSharpConstructorParameterLookup.cs new file mode 100644 index 00000000000..973494e1ac3 --- /dev/null +++ b/analyzers/src/SonarAnalyzer.CSharp/Helpers/CSharpConstructorParameterLookup.cs @@ -0,0 +1,72 @@ +/* + * SonarAnalyzer for .NET + * Copyright (C) 2015-2023 SonarSource SA + * mailto: contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +namespace SonarAnalyzer.Helpers +{ + internal class CSharpConstructorParameterLookup + { + private readonly SeparatedSyntaxList? argumentList; + public IMethodSymbol ConstructorSymbol { get; } + + protected CSharpConstructorParameterLookup(SeparatedSyntaxList? argumentList, IMethodSymbol constructorSymbol) + { + this.argumentList = argumentList; + ConstructorSymbol = constructorSymbol; + } + + public CSharpConstructorParameterLookup(ObjectCreationExpressionSyntax creation, SemanticModel semanticModel) + : this(creation.ArgumentList, semanticModel) { } + + public CSharpConstructorParameterLookup(ArgumentListSyntax argumentList, SemanticModel semanticModel) + : this(argumentList?.Arguments, argumentList == null ? null : semanticModel.GetSymbolInfo(argumentList.Parent).Symbol as IMethodSymbol) { } + + public CSharpConstructorParameterLookup(ArgumentListSyntax argumentList, IMethodSymbol constructorSymbol) + : this(argumentList?.Arguments, constructorSymbol) { } + + public bool TryGetSymbol(ArgumentSyntax argument, out IParameterSymbol parameter) + { + parameter = null; + + if (!argumentList.HasValue + || !argumentList.Value.Contains(argument) + || ConstructorSymbol == null + || ConstructorSymbol.IsVararg) + { + return false; + } + + if (argument.NameColon?.Name.Identifier is { } nameColonArgumentIdentifier) + { + parameter = ConstructorSymbol.Parameters.FirstOrDefault(symbol => symbol.Name == nameColonArgumentIdentifier.ValueText); + return parameter != null; + } + + var index = argumentList.Value.IndexOf(argument); + if (index >= ConstructorSymbol.Parameters.Length) + { + var lastParameter = ConstructorSymbol.Parameters.Last(); + parameter = lastParameter.IsParams ? lastParameter : null; + return parameter != null; + } + parameter = ConstructorSymbol.Parameters[index]; + return true; + } + } +} diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs index 5e4ded5bc7a..e7d4a195112 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs @@ -34,17 +34,23 @@ protected override bool ShouldReportInvocation(SonarSyntaxNodeReportingContext c var myLookup = new CSharpMethodParameterLookup(invocation, context.SemanticModel); return invocation.ArgumentList.Arguments.Count > 0 - && invocation.ArgumentList.Arguments.Last().Expression is ArrayCreationExpressionSyntax array - && CheckArrayInitializer(array) - && myLookup.TryGetSymbol(invocation.ArgumentList.Arguments.Last(), out var parameter) - && parameter.IsParams; + && invocation.ArgumentList.Arguments.Last().Expression is ArrayCreationExpressionSyntax array + && CheckArrayInitializer(array) + && myLookup.TryGetSymbol(invocation.ArgumentList.Arguments.Last(), out var parameter) + && parameter.IsParams; } - protected override bool ShouldReportCreation(ObjectCreationExpressionSyntax creation) => - creation.ArgumentList is not null - && creation.ArgumentList.Arguments.Count > 0 - && creation.ArgumentList.Arguments.Last().Expression is ArrayCreationExpressionSyntax array - && CheckArrayInitializer(array); + protected override bool ShouldReportCreation(SonarSyntaxNodeReportingContext context, ObjectCreationExpressionSyntax creation) + { + var myLookup = new CSharpConstructorParameterLookup(creation, context.SemanticModel); + + return creation.ArgumentList is not null + && creation.ArgumentList.Arguments.Count > 0 + && creation.ArgumentList.Arguments.Last().Expression is ArrayCreationExpressionSyntax array + && CheckArrayInitializer(array) + && myLookup.TryGetSymbol(creation.ArgumentList.Arguments.Last(), out var parameter) + && parameter.IsParams; + } protected override Location GetInvocationLocation(InvocationExpressionSyntax invocation) => invocation.ArgumentList.Arguments.Last().Expression.GetLocation(); diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs index fd63cabff7d..022bfc0ad44 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -32,7 +32,7 @@ public abstract class ArrayPassedAsParamsBase { - public VisualBasicMethodParameterLookup(InvocationExpressionSyntax invocation, SemanticModel semanticModel) - : this(invocation.ArgumentList, semanticModel) { } - public VisualBasicMethodParameterLookup(ArgumentListSyntax argumentList, IMethodSymbol methodSymbol) : base(argumentList?.Arguments, methodSymbol) { } diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs index 257c8d98715..7bfad8e0408 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs @@ -27,21 +27,22 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase "ParamArray"; protected override string MessageFormat => MessageBase; - protected override bool ShouldReportInvocation(SonarSyntaxNodeReportingContext context, InvocationExpressionSyntax invocation) - { - var myLookup = new VisualBasicMethodParameterLookup(invocation, context.SemanticModel); - - return invocation.ArgumentList.Arguments.Count > 0 + protected override bool ShouldReportInvocation(SonarSyntaxNodeReportingContext context, InvocationExpressionSyntax invocation) => + invocation.ArgumentList.Arguments.Count > 0 && invocation.ArgumentList.Arguments.Last().GetExpression() is ArrayCreationExpressionSyntax array && CheckArrayInitializer(array) - && myLookup.TryGetSymbol(invocation.ArgumentList.Arguments.Last(), out var parameter) - && parameter.IsParams; - } - protected override bool ShouldReportCreation(ObjectCreationExpressionSyntax creation) => + && context.SemanticModel.GetSymbolInfo(invocation).Symbol is IMethodSymbol invokedMethodSymbol + && invokedMethodSymbol.Parameters.Any() + && invokedMethodSymbol.Parameters.Last().IsParams; + + protected override bool ShouldReportCreation(SonarSyntaxNodeReportingContext context, ObjectCreationExpressionSyntax creation) => creation.ArgumentList is not null && creation.ArgumentList.Arguments.Count > 0 && creation.ArgumentList.Arguments.Last().GetExpression() is ArrayCreationExpressionSyntax array - && CheckArrayInitializer(array); + && CheckArrayInitializer(array) + && context.SemanticModel.GetSymbolInfo(creation).Symbol is IMethodSymbol invokedMethodSymbol + && invokedMethodSymbol.Parameters.Any() + && invokedMethodSymbol.Parameters.Last().IsParams; protected override Location GetInvocationLocation(InvocationExpressionSyntax invocation) => invocation.ArgumentList.Arguments.Last().GetExpression().GetLocation(); diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs index 84c7834a6de..e5c7403e17f 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs @@ -35,7 +35,7 @@ public void Base(string[] myArray) // ^^^^^^^^^^^^^^^^^^ var s1 = new MyClass(1, 2, 3); // Compliant s1 = new MyClass(args: new int[] { 2, 3 }, a: 1); // Compliant (if you specifically require the arguments to be passed in this order there is no way of making this compliant, thus we shouldn't raise) - var s2 = new MyOtherClass(args: new int[12], a: new int[] { 2, 3 }); // Noncompliant FP + var s2 = new MyOtherClass(args: new int[12], a: new int[] { 2, 3 }); // Compliant } public void Method(params string[] args) { } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb index 7f4786a9899..ac0a932ca89 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb @@ -23,11 +23,14 @@ Public Class Program ' ^^^^^^^^^^^^^^^^^^^^^^^^^ Method3(Nothing, Nothing) ' Compliant + Method3(args:=New String() {"s1", "s2"}, a:=New String(11) {}) ' Error [BC30587] Named argument cannot match a ParamArray parameter + Method3(args:=New String(11) {}, a:=New String() {"s1", "s2"}) ' Error [BC30587] Named argument cannot match a ParamArray parameter + Dim s = New [MyClass](1, New Integer() {2, 3}) ' Noncompliant ' ^^^^^^^^^^^^^^^^^^^^ Dim s1 = New [MyClass](1, 2, 3) ' Compliant - s1 = New [MyClass](args:=New Integer() {2, 3}, a:=1) ' Error [BC30587] - Dim s2 = New MyOtherClass(args:=New Integer(11) {}, a:=New Integer() {2, 3}) ' Error [BC30587] + s1 = New [MyClass](args:=New Integer() {2, 3}, a:=1) ' Error [BC30587] Named argument cannot match a ParamArray parameter + Dim s2 = New MyOtherClass(args:=New Integer(11) {}, a:=New Integer() {2, 3}) ' Error [BC30587] Named argument cannot match a ParamArray parameter End Sub Public Sub Method(ParamArray args As String()) From 469aacda4d134b462f3ad5cae508fcb9b6f3c785 Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Thu, 26 Jan 2023 09:38:13 +0100 Subject: [PATCH 18/40] Small fix --- .../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 022bfc0ad44..77985f5954f 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -27,7 +27,7 @@ public abstract class ArrayPassedAsParamsBase Date: Thu, 26 Jan 2023 19:58:32 +0100 Subject: [PATCH 19/40] Rule rewriting, semplifications --- .../Rules/ArrayPassedAsParams.cs | 66 +++++++++---------- .../Rules/ArrayPassedAsParamsBase.cs | 31 +++------ .../Rules/ArrayPassedAsParams.cs | 52 ++++++++------- .../TestCases/ArrayPassedAsParams.vb | 3 - 4 files changed, 66 insertions(+), 86 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs index e7d4a195112..50cf83e1ee9 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs @@ -18,47 +18,41 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using Microsoft.CodeAnalysis; - namespace SonarAnalyzer.Rules.CSharp; [DiagnosticAnalyzer(LanguageNames.CSharp)] -public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase +public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase { protected override ILanguageFacade Language => CSharpFacade.Instance; protected override string ParameterKeyword => "params"; - protected override string MessageFormat => MessageBase; - - protected override bool ShouldReportInvocation(SonarSyntaxNodeReportingContext context, InvocationExpressionSyntax invocation) - { - var myLookup = new CSharpMethodParameterLookup(invocation, context.SemanticModel); - - return invocation.ArgumentList.Arguments.Count > 0 - && invocation.ArgumentList.Arguments.Last().Expression is ArrayCreationExpressionSyntax array - && CheckArrayInitializer(array) - && myLookup.TryGetSymbol(invocation.ArgumentList.Arguments.Last(), out var parameter) - && parameter.IsParams; - } - - protected override bool ShouldReportCreation(SonarSyntaxNodeReportingContext context, ObjectCreationExpressionSyntax creation) - { - var myLookup = new CSharpConstructorParameterLookup(creation, context.SemanticModel); - - return creation.ArgumentList is not null - && creation.ArgumentList.Arguments.Count > 0 - && creation.ArgumentList.Arguments.Last().Expression is ArrayCreationExpressionSyntax array - && CheckArrayInitializer(array) - && myLookup.TryGetSymbol(creation.ArgumentList.Arguments.Last(), out var parameter) - && parameter.IsParams; - } - - protected override Location GetInvocationLocation(InvocationExpressionSyntax invocation) => - invocation.ArgumentList.Arguments.Last().Expression.GetLocation(); - - protected override Location GetCreationLocation(ObjectCreationExpressionSyntax creation) => - creation.ArgumentList.Arguments.Last().Expression.GetLocation(); - private static bool CheckArrayInitializer(ArrayCreationExpressionSyntax array) => - array.Initializer is InitializerExpressionSyntax initializer - && initializer.Expressions.Count > 0; + protected override bool ShouldReport(SonarSyntaxNodeReportingContext context, SyntaxNode expression) => + expression switch + { + ObjectCreationExpressionSyntax { } creation => + CheckLastArgument(creation.ArgumentList) && IsParamParameter(context, creation, creation.ArgumentList.Arguments.Last()), + InvocationExpressionSyntax { } invocation => + CheckLastArgument(invocation.ArgumentList) && IsParamParameter(context, invocation, invocation.ArgumentList.Arguments.Last()), + _ => false + }; + + protected override Location GetLocation(SyntaxNode expression) => + expression switch + { + ObjectCreationExpressionSyntax { } creation => creation.ArgumentList.Arguments.Last().Expression.GetLocation(), + InvocationExpressionSyntax { } invocation => invocation.ArgumentList.Arguments.Last().Expression.GetLocation(), + _ => expression.GetLocation() + }; + + private static bool CheckLastArgument(ArgumentListSyntax argumentList) => + argumentList is not null + && argumentList.Arguments.Any() + && argumentList.Arguments.Last().Expression is ArrayCreationExpressionSyntax invocationArray + && invocationArray.Initializer is InitializerExpressionSyntax { Expressions.Count: > 0 }; + + private bool IsParamParameter(SonarSyntaxNodeReportingContext context, SyntaxNode node, ArgumentSyntax argument) => + context.SemanticModel.GetSymbolInfo(node).Symbol is IMethodSymbol methodSymbol + && Language.MethodParameterLookup(node, methodSymbol) is CSharpMethodParameterLookup lookup + && lookup.TryGetSymbol(argument, out var param) + && param.IsParams; } diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs index 77985f5954f..ed053856e0f 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -20,44 +20,31 @@ namespace SonarAnalyzer.Rules; -public abstract class ArrayPassedAsParamsBase : SonarDiagnosticAnalyzer +public abstract class ArrayPassedAsParamsBase : SonarDiagnosticAnalyzer where TSyntaxKind : struct - where TInvocationExpressionSyntax : SyntaxNode - where TObjectCreationExpressionSyntax : SyntaxNode { private const string DiagnosticId = "S3878"; - - protected const string MessageBase = "Arrays should not be created for {0} parameters."; + protected override string MessageFormat => "Arrays should not be created for {0} parameters."; private readonly DiagnosticDescriptor rule; protected abstract string ParameterKeyword { get; } - protected abstract bool ShouldReportInvocation(SonarSyntaxNodeReportingContext context, TInvocationExpressionSyntax invocation); - protected abstract bool ShouldReportCreation(SonarSyntaxNodeReportingContext context, TObjectCreationExpressionSyntax creation); - protected abstract Location GetInvocationLocation(TInvocationExpressionSyntax context); - protected abstract Location GetCreationLocation(TObjectCreationExpressionSyntax context); + protected abstract bool ShouldReport(SonarSyntaxNodeReportingContext context, SyntaxNode expression); + protected abstract Location GetLocation(SyntaxNode expression); protected ArrayPassedAsParamsBase() : base(DiagnosticId) => rule = Language.CreateDescriptor(DiagnosticId, MessageFormat); protected sealed override void Initialize(SonarAnalysisContext context) { - context.RegisterNodeAction(Language.GeneratedCodeRecognizer, CheckInvocation, Language.SyntaxKind.InvocationExpression); - context.RegisterNodeAction(Language.GeneratedCodeRecognizer, CheckObjectCreation, Language.SyntaxKind.ObjectCreationExpressions); - } - - private void CheckInvocation(SonarSyntaxNodeReportingContext context) - { - if (context.Node is TInvocationExpressionSyntax invocation && ShouldReportInvocation(context, invocation)) - { - Report(context, GetInvocationLocation(invocation)); - } + context.RegisterNodeAction(Language.GeneratedCodeRecognizer, CheckExpression, Language.SyntaxKind.ObjectCreationExpressions); + context.RegisterNodeAction(Language.GeneratedCodeRecognizer, CheckExpression, Language.SyntaxKind.InvocationExpression); } - private void CheckObjectCreation(SonarSyntaxNodeReportingContext context) + private void CheckExpression(SonarSyntaxNodeReportingContext context) { - if (context.Node is TObjectCreationExpressionSyntax creation && ShouldReportCreation(context, creation)) + if (context.Node is { } expression && ShouldReport(context, expression)) { - Report(context, GetCreationLocation(creation)); + Report(context, GetLocation(expression)); } } diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs index 7bfad8e0408..51c8732c4cb 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs @@ -21,36 +21,38 @@ namespace SonarAnalyzer.Rules.VisualBasic; [DiagnosticAnalyzer(LanguageNames.VisualBasic)] -public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase +public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase { protected override ILanguageFacade Language => VisualBasicFacade.Instance; protected override string ParameterKeyword => "ParamArray"; - protected override string MessageFormat => MessageBase; - protected override bool ShouldReportInvocation(SonarSyntaxNodeReportingContext context, InvocationExpressionSyntax invocation) => - invocation.ArgumentList.Arguments.Count > 0 - && invocation.ArgumentList.Arguments.Last().GetExpression() is ArrayCreationExpressionSyntax array - && CheckArrayInitializer(array) - && context.SemanticModel.GetSymbolInfo(invocation).Symbol is IMethodSymbol invokedMethodSymbol - && invokedMethodSymbol.Parameters.Any() - && invokedMethodSymbol.Parameters.Last().IsParams; + protected override bool ShouldReport(SonarSyntaxNodeReportingContext context, SyntaxNode expression) => + expression switch + { + ObjectCreationExpressionSyntax { } creation => + CheckLastArgument(creation.ArgumentList) && IsParamParameter(context, creation, creation.ArgumentList.Arguments.Last()), + InvocationExpressionSyntax { } invocation => + CheckLastArgument(invocation.ArgumentList) && IsParamParameter(context, invocation, invocation.ArgumentList.Arguments.Last()), + _ => false, + }; - protected override bool ShouldReportCreation(SonarSyntaxNodeReportingContext context, ObjectCreationExpressionSyntax creation) => - creation.ArgumentList is not null - && creation.ArgumentList.Arguments.Count > 0 - && creation.ArgumentList.Arguments.Last().GetExpression() is ArrayCreationExpressionSyntax array - && CheckArrayInitializer(array) - && context.SemanticModel.GetSymbolInfo(creation).Symbol is IMethodSymbol invokedMethodSymbol - && invokedMethodSymbol.Parameters.Any() - && invokedMethodSymbol.Parameters.Last().IsParams; + protected override Location GetLocation(SyntaxNode expression) => + expression switch + { + ObjectCreationExpressionSyntax { } creation => creation.ArgumentList.Arguments.Last().GetExpression().GetLocation(), + InvocationExpressionSyntax { } invocation => invocation.ArgumentList.Arguments.Last().GetExpression().GetLocation(), + _ => expression.GetLocation() + }; - protected override Location GetInvocationLocation(InvocationExpressionSyntax invocation) => - invocation.ArgumentList.Arguments.Last().GetExpression().GetLocation(); + private static bool CheckLastArgument(ArgumentListSyntax argumentList) => + argumentList is not null + && argumentList.Arguments.Any() + && argumentList.Arguments.Last().GetExpression() is ArrayCreationExpressionSyntax invocationArray + && invocationArray.Initializer is CollectionInitializerSyntax { Initializers.Count: > 0 }; - protected override Location GetCreationLocation(ObjectCreationExpressionSyntax creation) => - creation.ArgumentList.Arguments.Last().GetExpression().GetLocation(); - - private static bool CheckArrayInitializer(ArrayCreationExpressionSyntax array) => - array.Initializer is CollectionInitializerSyntax initializer - && initializer.Initializers.Count > 0; + private bool IsParamParameter(SonarSyntaxNodeReportingContext context, SyntaxNode node, ArgumentSyntax argument) => + context.SemanticModel.GetSymbolInfo(node).Symbol is IMethodSymbol methodSymbol + && Language.MethodParameterLookup(node, methodSymbol) is VisualBasicMethodParameterLookup lookup + && lookup.TryGetSymbol(argument, out var param) + && param.IsParams; } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb index ac0a932ca89..71231cadf96 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb @@ -44,9 +44,6 @@ Public Class Program Public Sub Method4(ParamArray a As String(), ParamArray args As String()) 'Error [CS0231] End Sub - - Private a = New [MyClass](1, New Integer() {2, 3}) ' Noncompliant - Private b = New [MyClass](1, 2, 3) ' Compliant End Class Public Class [MyClass] From ee448f669ed3074dcc264bc348d0fbb326fe52d2 Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Thu, 26 Jan 2023 20:10:39 +0100 Subject: [PATCH 20/40] Removed unused helper class --- .../CSharpConstructorParameterLookup.cs | 72 ------------------- 1 file changed, 72 deletions(-) delete mode 100644 analyzers/src/SonarAnalyzer.CSharp/Helpers/CSharpConstructorParameterLookup.cs diff --git a/analyzers/src/SonarAnalyzer.CSharp/Helpers/CSharpConstructorParameterLookup.cs b/analyzers/src/SonarAnalyzer.CSharp/Helpers/CSharpConstructorParameterLookup.cs deleted file mode 100644 index 973494e1ac3..00000000000 --- a/analyzers/src/SonarAnalyzer.CSharp/Helpers/CSharpConstructorParameterLookup.cs +++ /dev/null @@ -1,72 +0,0 @@ -/* - * SonarAnalyzer for .NET - * Copyright (C) 2015-2023 SonarSource SA - * mailto: contact AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -namespace SonarAnalyzer.Helpers -{ - internal class CSharpConstructorParameterLookup - { - private readonly SeparatedSyntaxList? argumentList; - public IMethodSymbol ConstructorSymbol { get; } - - protected CSharpConstructorParameterLookup(SeparatedSyntaxList? argumentList, IMethodSymbol constructorSymbol) - { - this.argumentList = argumentList; - ConstructorSymbol = constructorSymbol; - } - - public CSharpConstructorParameterLookup(ObjectCreationExpressionSyntax creation, SemanticModel semanticModel) - : this(creation.ArgumentList, semanticModel) { } - - public CSharpConstructorParameterLookup(ArgumentListSyntax argumentList, SemanticModel semanticModel) - : this(argumentList?.Arguments, argumentList == null ? null : semanticModel.GetSymbolInfo(argumentList.Parent).Symbol as IMethodSymbol) { } - - public CSharpConstructorParameterLookup(ArgumentListSyntax argumentList, IMethodSymbol constructorSymbol) - : this(argumentList?.Arguments, constructorSymbol) { } - - public bool TryGetSymbol(ArgumentSyntax argument, out IParameterSymbol parameter) - { - parameter = null; - - if (!argumentList.HasValue - || !argumentList.Value.Contains(argument) - || ConstructorSymbol == null - || ConstructorSymbol.IsVararg) - { - return false; - } - - if (argument.NameColon?.Name.Identifier is { } nameColonArgumentIdentifier) - { - parameter = ConstructorSymbol.Parameters.FirstOrDefault(symbol => symbol.Name == nameColonArgumentIdentifier.ValueText); - return parameter != null; - } - - var index = argumentList.Value.IndexOf(argument); - if (index >= ConstructorSymbol.Parameters.Length) - { - var lastParameter = ConstructorSymbol.Parameters.Last(); - parameter = lastParameter.IsParams ? lastParameter : null; - return parameter != null; - } - parameter = ConstructorSymbol.Parameters[index]; - return true; - } - } -} From f7b83b1d50c1e2e2eb5e358a426d0e05db4fb320 Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Fri, 27 Jan 2023 08:46:51 +0100 Subject: [PATCH 21/40] Added Indexer(CS) and Properties(VB) FN --- .../TestCases/ArrayPassedAsParams.cs | 9 +++++++++ .../TestCases/ArrayPassedAsParams.vb | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs index e5c7403e17f..b9aa6c97805 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs @@ -36,6 +36,10 @@ public void Base(string[] myArray) var s1 = new MyClass(1, 2, 3); // Compliant s1 = new MyClass(args: new int[] { 2, 3 }, a: 1); // Compliant (if you specifically require the arguments to be passed in this order there is no way of making this compliant, thus we shouldn't raise) var s2 = new MyOtherClass(args: new int[12], a: new int[] { 2, 3 }); // Compliant + + var s3 = new IndexerClass(); + var indexer1 = s3[new int[] { 1, 2 }]; // FN + var indexer2 = s3[1, 2]; // Compliant } public void Method(params string[] args) { } @@ -58,3 +62,8 @@ public class MyOtherClass { public MyOtherClass(int[] a, params int[] args) { } } + +public class IndexerClass +{ + public int this[params int[] i] => 1; +} diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb index 71231cadf96..8cabc43ab5f 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb @@ -31,6 +31,9 @@ Public Class Program Dim s1 = New [MyClass](1, 2, 3) ' Compliant s1 = New [MyClass](args:=New Integer() {2, 3}, a:=1) ' Error [BC30587] Named argument cannot match a ParamArray parameter Dim s2 = New MyOtherClass(args:=New Integer(11) {}, a:=New Integer() {2, 3}) ' Error [BC30587] Named argument cannot match a ParamArray parameter + + Dim s3 = Prop(New String() {"s1", "s2"}) ' FN + Dim s4 = Prop("s1", "s2") ' Compliant End Sub Public Sub Method(ParamArray args As String()) @@ -44,6 +47,12 @@ Public Class Program Public Sub Method4(ParamArray a As String(), ParamArray args As String()) 'Error [CS0231] End Sub + + Public ReadOnly Property Prop(ParamArray param() As String) As Integer + Get + End Get + End Property + End Class Public Class [MyClass] From 5517d4f75629ae2d9ec774571be0d37bc6267d47 Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Fri, 27 Jan 2023 09:26:30 +0100 Subject: [PATCH 22/40] Fix UT --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 33bdb1ce666..31ba9bacb4d 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ languages in [SonarQube](http://www.sonarqube.org/), [SonarCloud](https://sonarc ## Features -* [370+ C# rules](https://rules.sonarsource.com/csharp) and [170+ VB.​NET rules](https://rules.sonarsource.com/vbnet) +* [380+ C# rules](https://rules.sonarsource.com/csharp) and [170+ VB.​NET rules](https://rules.sonarsource.com/vbnet) * Metrics (cognitive complexity, duplications, number of lines etc.) * Import of [test coverage reports](https://community.sonarsource.com/t/9871) from Visual Studio Code Coverage, dotCover, OpenCover, Coverlet, Altcover. * Import of third party Roslyn Analyzers results From 89d245fd87a0a3af3bbba5cdf4cfbf19fba687ed Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Fri, 27 Jan 2023 13:36:37 +0100 Subject: [PATCH 23/40] Second round of comments --- .../SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs | 7 ------- .../Helpers/MethodParameterLookupBase.cs | 4 +++- .../Rules/ArrayPassedAsParamsBase.cs | 10 +++++++--- .../Rules/ArrayPassedAsParams.cs | 7 ------- .../TestCases/ArrayPassedAsParams.cs | 4 ++-- .../TestCases/ArrayPassedAsParams.vb | 4 ++-- 6 files changed, 14 insertions(+), 22 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs index 50cf83e1ee9..c61308bb2d7 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs @@ -24,7 +24,6 @@ namespace SonarAnalyzer.Rules.CSharp; public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase { protected override ILanguageFacade Language => CSharpFacade.Instance; - protected override string ParameterKeyword => "params"; protected override bool ShouldReport(SonarSyntaxNodeReportingContext context, SyntaxNode expression) => expression switch @@ -49,10 +48,4 @@ expression switch && argumentList.Arguments.Any() && argumentList.Arguments.Last().Expression is ArrayCreationExpressionSyntax invocationArray && invocationArray.Initializer is InitializerExpressionSyntax { Expressions.Count: > 0 }; - - private bool IsParamParameter(SonarSyntaxNodeReportingContext context, SyntaxNode node, ArgumentSyntax argument) => - context.SemanticModel.GetSymbolInfo(node).Symbol is IMethodSymbol methodSymbol - && Language.MethodParameterLookup(node, methodSymbol) is CSharpMethodParameterLookup lookup - && lookup.TryGetSymbol(argument, out var param) - && param.IsParams; } diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/MethodParameterLookupBase.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/MethodParameterLookupBase.cs index 8bf4f80967c..a17134cdbea 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/MethodParameterLookupBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/MethodParameterLookupBase.cs @@ -22,6 +22,7 @@ namespace SonarAnalyzer.Helpers { public interface IMethodParameterLookup { + bool TryGetSymbol(SyntaxNode argument, out IParameterSymbol parameter); bool TryGetSyntax(IParameterSymbol parameter, out ImmutableArray expressions); bool TryGetSyntax(string parameterName, out ImmutableArray expressions); bool TryGetNonParamsSyntax(IParameterSymbol parameter, out SyntaxNode expression); @@ -44,9 +45,10 @@ protected MethodParameterLookupBase(SeparatedSyntaxList? argume MethodSymbol = methodSymbol; } - public bool TryGetSymbol(TArgumentSyntax argument, out IParameterSymbol parameter) + public bool TryGetSymbol(SyntaxNode arg, out IParameterSymbol parameter) { parameter = null; + var argument = (TArgumentSyntax)arg; if (!argumentList.HasValue || !argumentList.Value.Contains(argument) diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs index ed053856e0f..6c3072d8e21 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -24,10 +24,9 @@ public abstract class ArrayPassedAsParamsBase : SonarDiagnosticAnal where TSyntaxKind : struct { private const string DiagnosticId = "S3878"; - protected override string MessageFormat => "Arrays should not be created for {0} parameters."; + protected override string MessageFormat => "Remove this array creation and simply pass the elements."; private readonly DiagnosticDescriptor rule; - protected abstract string ParameterKeyword { get; } protected abstract bool ShouldReport(SonarSyntaxNodeReportingContext context, SyntaxNode expression); protected abstract Location GetLocation(SyntaxNode expression); @@ -49,5 +48,10 @@ private void CheckExpression(SonarSyntaxNodeReportingContext context) } private void Report(SonarSyntaxNodeReportingContext context, Location location) => - context.ReportIssue(Diagnostic.Create(rule, location, ParameterKeyword)); + context.ReportIssue(Diagnostic.Create(rule, location)); + + protected bool IsParamParameter(SonarSyntaxNodeReportingContext context, SyntaxNode node, SyntaxNode argument) => + context.SemanticModel.GetSymbolInfo(node).Symbol is IMethodSymbol methodSymbol + && Language.MethodParameterLookup(node, methodSymbol).TryGetSymbol(argument, out var param) + && param.IsParams; } diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs index 51c8732c4cb..2b59256028a 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs @@ -24,7 +24,6 @@ namespace SonarAnalyzer.Rules.VisualBasic; public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase { protected override ILanguageFacade Language => VisualBasicFacade.Instance; - protected override string ParameterKeyword => "ParamArray"; protected override bool ShouldReport(SonarSyntaxNodeReportingContext context, SyntaxNode expression) => expression switch @@ -49,10 +48,4 @@ expression switch && argumentList.Arguments.Any() && argumentList.Arguments.Last().GetExpression() is ArrayCreationExpressionSyntax invocationArray && invocationArray.Initializer is CollectionInitializerSyntax { Initializers.Count: > 0 }; - - private bool IsParamParameter(SonarSyntaxNodeReportingContext context, SyntaxNode node, ArgumentSyntax argument) => - context.SemanticModel.GetSymbolInfo(node).Symbol is IMethodSymbol methodSymbol - && Language.MethodParameterLookup(node, methodSymbol) is VisualBasicMethodParameterLookup lookup - && lookup.TryGetSymbol(argument, out var param) - && param.IsParams; } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs index b9aa6c97805..7d93f49a7bf 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs @@ -4,7 +4,7 @@ public class Program { public void Base(string[] myArray) { - Method(new string[] { "s1", "s2" }); // Noncompliant {{Arrays should not be created for params parameters.}} + Method(new string[] { "s1", "s2" }); // Noncompliant {{Remove this array creation and simply pass the elements.}} // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Method(new string[] { "s1" }); // Noncompliant Method("s1"); // Compliant @@ -12,7 +12,7 @@ public void Base(string[] myArray) Method(myArray); // Compliant Method(new string[12]); // Compliant - Method2(1, new string[] { "s1", "s2" }); // Noncompliant {{Arrays should not be created for params parameters.}} + Method2(1, new string[] { "s1", "s2" }); // Noncompliant {{Remove this array creation and simply pass the elements.}} // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Method2(1, new string[] { "s1" }); // Noncompliant Method2(1, "s1"); // Compliant diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb index 8cabc43ab5f..10c97981a37 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.vb @@ -1,7 +1,7 @@  Public Class Program Public Sub Base(ByVal myArray As String()) - Method(New String() {"s1", "s2"}) ' Noncompliant {{Arrays should not be created for ParamArray parameters.}} + Method(New String() {"s1", "s2"}) ' Noncompliant {{Remove this array creation and simply pass the elements.}} ' ^^^^^^^^^^^^^^^^^^^^^^^^^ Method(New String() {"s1"}) ' Noncompliant Method("s1") ' Compliant @@ -9,7 +9,7 @@ Public Class Program Method(myArray) ' Compliant Method(New String(11) {}) ' Compliant - Method2(1, New String() {"s1", "s2"}) ' Noncompliant {{Arrays should not be created for ParamArray parameters.}} + Method2(1, New String() {"s1", "s2"}) ' Noncompliant {{Remove this array creation and simply pass the elements.}} ' ^^^^^^^^^^^^^^^^^^^^^^^^^ Method2(1, New String() {"s1"}) ' Noncompliant Method2(1, "s1") ' Compliant From 58a200e26316f66ef02b4fc160c42d706f1bfa72 Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Fri, 27 Jan 2023 14:03:31 +0100 Subject: [PATCH 24/40] Fix ITs --- ...AB-AF12-4012-B945-284C2448DC81}-S3878.json | 52 +++++++++---------- ...5E-C6AE-4D2D-A9DD-B6EFD19A4279}-S3878.json | 10 ++-- ...3A-D04F-4262-923D-21AEDF86E2B7}-S3878.json | 8 +-- ...CF-D15A-4BF1-B980-395CEC0004A9}-S3878.json | 26 +++++----- ...31-1F7B-4637-9B3A-806988DE50CF}-S3878.json | 16 +++--- ...es.Razor.BuildProviders--net452-S3878.json | 4 +- ...DistributedData--netstandard2.0-S3878.json | 4 +- .../Akka.Streams--netstandard2.0-S3878.json | 2 +- 8 files changed, 61 insertions(+), 61 deletions(-) diff --git a/analyzers/its/expected/Ember-MM/Ember Media Manager-{9B57D3AB-AF12-4012-B945-284C2448DC81}-S3878.json b/analyzers/its/expected/Ember-MM/Ember Media Manager-{9B57D3AB-AF12-4012-B945-284C2448DC81}-S3878.json index 604697bd481..f170a3be41d 100644 --- a/analyzers/its/expected/Ember-MM/Ember Media Manager-{9B57D3AB-AF12-4012-B945-284C2448DC81}-S3878.json +++ b/analyzers/its/expected/Ember-MM/Ember Media Manager-{9B57D3AB-AF12-4012-B945-284C2448DC81}-S3878.json @@ -2,7 +2,7 @@ "issues": [ { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", "region": { @@ -15,7 +15,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", "region": { @@ -28,7 +28,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", "region": { @@ -41,7 +41,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", "region": { @@ -54,7 +54,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", "region": { @@ -67,7 +67,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", "region": { @@ -80,7 +80,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", "region": { @@ -93,7 +93,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", "region": { @@ -106,7 +106,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", "region": { @@ -119,7 +119,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", "region": { @@ -132,7 +132,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", "region": { @@ -145,7 +145,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", "region": { @@ -158,7 +158,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", "region": { @@ -171,7 +171,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", "region": { @@ -184,7 +184,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", "region": { @@ -197,7 +197,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", "region": { @@ -210,7 +210,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", "region": { @@ -223,7 +223,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", "region": { @@ -236,7 +236,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", "region": { @@ -249,7 +249,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", "region": { @@ -262,7 +262,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", "region": { @@ -275,7 +275,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", "region": { @@ -288,7 +288,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", "region": { @@ -301,7 +301,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", "region": { @@ -314,7 +314,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", "region": { @@ -327,7 +327,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Ember%20Media%20Manager\frmMain.vb", "region": { diff --git a/analyzers/its/expected/Ember-MM/EmberAPI-{208AA35E-C6AE-4D2D-A9DD-B6EFD19A4279}-S3878.json b/analyzers/its/expected/Ember-MM/EmberAPI-{208AA35E-C6AE-4D2D-A9DD-B6EFD19A4279}-S3878.json index d38f229bf64..0e29ccebc5f 100644 --- a/analyzers/its/expected/Ember-MM/EmberAPI-{208AA35E-C6AE-4D2D-A9DD-B6EFD19A4279}-S3878.json +++ b/analyzers/its/expected/Ember-MM/EmberAPI-{208AA35E-C6AE-4D2D-A9DD-B6EFD19A4279}-S3878.json @@ -2,7 +2,7 @@ "issues": [ { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\EmberAPI\clsAPIMediaContainers.vb", "region": { @@ -15,7 +15,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\EmberAPI\clsAPIMediaContainers.vb", "region": { @@ -28,7 +28,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\EmberAPI\clsAPIMediaContainers.vb", "region": { @@ -41,7 +41,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\EmberAPI\clsAPIMediaContainers.vb", "region": { @@ -54,7 +54,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\EmberAPI\clsAPIMediaContainers.vb", "region": { diff --git a/analyzers/its/expected/Ember-MM/generic.EmberCore.NMT-{84B2143A-D04F-4262-923D-21AEDF86E2B7}-S3878.json b/analyzers/its/expected/Ember-MM/generic.EmberCore.NMT-{84B2143A-D04F-4262-923D-21AEDF86E2B7}-S3878.json index 5d25b12074a..5fb3c01c722 100644 --- a/analyzers/its/expected/Ember-MM/generic.EmberCore.NMT-{84B2143A-D04F-4262-923D-21AEDF86E2B7}-S3878.json +++ b/analyzers/its/expected/Ember-MM/generic.EmberCore.NMT-{84B2143A-D04F-4262-923D-21AEDF86E2B7}-S3878.json @@ -2,7 +2,7 @@ "issues": [ { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Addons\generic.EmberCore.NMT\dlgNMTMovies.vb", "region": { @@ -15,7 +15,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Addons\generic.EmberCore.NMT\dlgNMTMovies.vb", "region": { @@ -28,7 +28,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Addons\generic.EmberCore.NMT\dlgNMTMovies.vb", "region": { @@ -41,7 +41,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Addons\generic.EmberCore.NMT\dlgNMTMovies.vb", "region": { diff --git a/analyzers/its/expected/Ember-MM/multi.EmberExtras-{62356BCF-D15A-4BF1-B980-395CEC0004A9}-S3878.json b/analyzers/its/expected/Ember-MM/multi.EmberExtras-{62356BCF-D15A-4BF1-B980-395CEC0004A9}-S3878.json index 25f6eea7190..ba4054d4eba 100644 --- a/analyzers/its/expected/Ember-MM/multi.EmberExtras-{62356BCF-D15A-4BF1-B980-395CEC0004A9}-S3878.json +++ b/analyzers/its/expected/Ember-MM/multi.EmberExtras-{62356BCF-D15A-4BF1-B980-395CEC0004A9}-S3878.json @@ -2,7 +2,7 @@ "issues": [ { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Addons\multi.EmberExtras\frmAVCodecEditor.vb", "region": { @@ -15,7 +15,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Addons\multi.EmberExtras\frmAVCodecEditor.vb", "region": { @@ -28,7 +28,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Addons\multi.EmberExtras\frmAVCodecEditor.vb", "region": { @@ -41,7 +41,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Addons\multi.EmberExtras\frmAVCodecEditor.vb", "region": { @@ -54,7 +54,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Addons\multi.EmberExtras\frmGenresEditor.vb", "region": { @@ -67,7 +67,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Addons\multi.EmberExtras\frmGenresEditor.vb", "region": { @@ -80,7 +80,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Addons\multi.EmberExtras\frmGenresEditor.vb", "region": { @@ -93,7 +93,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Addons\multi.EmberExtras\frmGenresEditor.vb", "region": { @@ -106,7 +106,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Addons\multi.EmberExtras\frmGenresEditor.vb", "region": { @@ -119,7 +119,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Addons\multi.EmberExtras\frmMediaSourcesEditor.vb", "region": { @@ -132,7 +132,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Addons\multi.EmberExtras\frmMediaSourcesEditor.vb", "region": { @@ -145,7 +145,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Addons\multi.EmberExtras\frmMediaSourcesEditor.vb", "region": { @@ -158,7 +158,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Addons\multi.EmberExtras\frmMediaSourcesEditor.vb", "region": { diff --git a/analyzers/its/expected/Ember-MM/scraper.EmberCore.XML-{E567C031-1F7B-4637-9B3A-806988DE50CF}-S3878.json b/analyzers/its/expected/Ember-MM/scraper.EmberCore.XML-{E567C031-1F7B-4637-9B3A-806988DE50CF}-S3878.json index e3cc88bfba7..46a9c63a7c3 100644 --- a/analyzers/its/expected/Ember-MM/scraper.EmberCore.XML-{E567C031-1F7B-4637-9B3A-806988DE50CF}-S3878.json +++ b/analyzers/its/expected/Ember-MM/scraper.EmberCore.XML-{E567C031-1F7B-4637-9B3A-806988DE50CF}-S3878.json @@ -2,7 +2,7 @@ "issues": [ { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Addons\scraper.EmberCore.XML\XMLScraper\MediaTags\AlbumTag.vb", "region": { @@ -15,7 +15,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Addons\scraper.EmberCore.XML\XMLScraper\MediaTags\AlbumTag.vb", "region": { @@ -28,7 +28,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Addons\scraper.EmberCore.XML\XMLScraper\MediaTags\ArtistTag.vb", "region": { @@ -41,7 +41,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Addons\scraper.EmberCore.XML\XMLScraper\MediaTags\MovieTag.vb", "region": { @@ -54,7 +54,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Addons\scraper.EmberCore.XML\XMLScraper\MediaTags\MusicVideoTag.vb", "region": { @@ -67,7 +67,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Addons\scraper.EmberCore.XML\XMLScraper\MediaTags\PersonInfo.vb", "region": { @@ -80,7 +80,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Addons\scraper.EmberCore.XML\XMLScraper\MediaTags\TVEpisodeTag.vb", "region": { @@ -93,7 +93,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for ParamArray parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Ember-MM\Addons\scraper.EmberCore.XML\XMLScraper\MediaTags\TVShow.vb", "region": { diff --git a/analyzers/its/expected/Nancy/Nancy.ViewEngines.Razor.BuildProviders--net452-S3878.json b/analyzers/its/expected/Nancy/Nancy.ViewEngines.Razor.BuildProviders--net452-S3878.json index 1705303d238..de08e308ee6 100644 --- a/analyzers/its/expected/Nancy/Nancy.ViewEngines.Razor.BuildProviders--net452-S3878.json +++ b/analyzers/its/expected/Nancy/Nancy.ViewEngines.Razor.BuildProviders--net452-S3878.json @@ -2,7 +2,7 @@ "issues": [ { "id": "S3878", -"message": "Arrays should not be created for params parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Nancy\src\Nancy.ViewEngines.Razor.BuildProviders\NancyCSharpRazorBuildProvider.cs", "region": { @@ -15,7 +15,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for params parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\Nancy\src\Nancy.ViewEngines.Razor.BuildProviders\NancyCSharpRazorBuildProvider.cs", "region": { diff --git a/analyzers/its/expected/akka.net/Akka.DistributedData--netstandard2.0-S3878.json b/analyzers/its/expected/akka.net/Akka.DistributedData--netstandard2.0-S3878.json index cfc7a1dcf76..f4579fd20b2 100644 --- a/analyzers/its/expected/akka.net/Akka.DistributedData--netstandard2.0-S3878.json +++ b/analyzers/its/expected/akka.net/Akka.DistributedData--netstandard2.0-S3878.json @@ -2,7 +2,7 @@ "issues": [ { "id": "S3878", -"message": "Arrays should not be created for params parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\akka.net\src\contrib\cluster\Akka.DistributedData\Replicator.cs", "region": { @@ -15,7 +15,7 @@ }, { "id": "S3878", -"message": "Arrays should not be created for params parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\akka.net\src\contrib\cluster\Akka.DistributedData\Replicator.cs", "region": { diff --git a/analyzers/its/expected/akka.net/Akka.Streams--netstandard2.0-S3878.json b/analyzers/its/expected/akka.net/Akka.Streams--netstandard2.0-S3878.json index 2495df7624a..527eb63c209 100644 --- a/analyzers/its/expected/akka.net/Akka.Streams--netstandard2.0-S3878.json +++ b/analyzers/its/expected/akka.net/Akka.Streams--netstandard2.0-S3878.json @@ -2,7 +2,7 @@ "issues": [ { "id": "S3878", -"message": "Arrays should not be created for params parameters.", +"message": "Remove this array creation and simply pass the elements.", "location": { "uri": "sources\akka.net\src\core\Akka.Streams\ActorMaterializer.cs", "region": { From e6b738623104867d81f0ce09e373cabbb2dc2155 Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Fri, 27 Jan 2023 14:31:46 +0100 Subject: [PATCH 25/40] Moving stuff to the base class --- .../Rules/ArrayPassedAsParams.cs | 26 +++++++------------ .../Rules/ArrayPassedAsParamsBase.cs | 20 +++++++------- .../Rules/ArrayPassedAsParams.cs | 26 +++++++------------ 3 files changed, 27 insertions(+), 45 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs index c61308bb2d7..9ec3c78d81f 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs @@ -21,31 +21,23 @@ namespace SonarAnalyzer.Rules.CSharp; [DiagnosticAnalyzer(LanguageNames.CSharp)] -public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase +public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase { protected override ILanguageFacade Language => CSharpFacade.Instance; - protected override bool ShouldReport(SonarSyntaxNodeReportingContext context, SyntaxNode expression) => + protected override ArgumentSyntax GetLastArgumentIfArrayCreation(SyntaxNode expression) => expression switch { - ObjectCreationExpressionSyntax { } creation => - CheckLastArgument(creation.ArgumentList) && IsParamParameter(context, creation, creation.ArgumentList.Arguments.Last()), - InvocationExpressionSyntax { } invocation => - CheckLastArgument(invocation.ArgumentList) && IsParamParameter(context, invocation, invocation.ArgumentList.Arguments.Last()), - _ => false + ObjectCreationExpressionSyntax { } creation => CheckLastArgument(creation.ArgumentList), + InvocationExpressionSyntax { } invocation => CheckLastArgument(invocation.ArgumentList), + _ => null }; - protected override Location GetLocation(SyntaxNode expression) => - expression switch - { - ObjectCreationExpressionSyntax { } creation => creation.ArgumentList.Arguments.Last().Expression.GetLocation(), - InvocationExpressionSyntax { } invocation => invocation.ArgumentList.Arguments.Last().Expression.GetLocation(), - _ => expression.GetLocation() - }; - - private static bool CheckLastArgument(ArgumentListSyntax argumentList) => + private static ArgumentSyntax CheckLastArgument(ArgumentListSyntax argumentList) => argumentList is not null && argumentList.Arguments.Any() && argumentList.Arguments.Last().Expression is ArrayCreationExpressionSyntax invocationArray - && invocationArray.Initializer is InitializerExpressionSyntax { Expressions.Count: > 0 }; + && invocationArray.Initializer is InitializerExpressionSyntax { Expressions.Count: > 0 } + ? argumentList.Arguments.Last() + : null; } diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs index 6c3072d8e21..b2e4d4341cd 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -20,15 +20,15 @@ namespace SonarAnalyzer.Rules; -public abstract class ArrayPassedAsParamsBase : SonarDiagnosticAnalyzer +public abstract class ArrayPassedAsParamsBase : SonarDiagnosticAnalyzer where TSyntaxKind : struct + 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 bool ShouldReport(SonarSyntaxNodeReportingContext context, SyntaxNode expression); - protected abstract Location GetLocation(SyntaxNode expression); + protected abstract TArgumentNode GetLastArgumentIfArrayCreation(SyntaxNode invocation); protected ArrayPassedAsParamsBase() : base(DiagnosticId) => rule = Language.CreateDescriptor(DiagnosticId, MessageFormat); @@ -41,17 +41,15 @@ protected sealed override void Initialize(SonarAnalysisContext context) private void CheckExpression(SonarSyntaxNodeReportingContext context) { - if (context.Node is { } expression && ShouldReport(context, expression)) + if (GetLastArgumentIfArrayCreation(context.Node) is { } lastArgument + && IsParamParameter(context, context.Node, lastArgument)) { - Report(context, GetLocation(expression)); + context.ReportIssue(Diagnostic.Create(rule, lastArgument.GetLocation())); } } - private void Report(SonarSyntaxNodeReportingContext context, Location location) => - context.ReportIssue(Diagnostic.Create(rule, location)); - - protected bool IsParamParameter(SonarSyntaxNodeReportingContext context, SyntaxNode node, SyntaxNode argument) => - context.SemanticModel.GetSymbolInfo(node).Symbol is IMethodSymbol methodSymbol - && Language.MethodParameterLookup(node, methodSymbol).TryGetSymbol(argument, out var param) + protected bool IsParamParameter(SonarSyntaxNodeReportingContext context, SyntaxNode invocation, SyntaxNode argument) => + context.SemanticModel.GetSymbolInfo(invocation).Symbol is IMethodSymbol methodSymbol + && Language.MethodParameterLookup(invocation, methodSymbol).TryGetSymbol(argument, out var param) && param.IsParams; } diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs index 2b59256028a..2cd2fe3701e 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs @@ -21,31 +21,23 @@ namespace SonarAnalyzer.Rules.VisualBasic; [DiagnosticAnalyzer(LanguageNames.VisualBasic)] -public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase +public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase { protected override ILanguageFacade Language => VisualBasicFacade.Instance; - protected override bool ShouldReport(SonarSyntaxNodeReportingContext context, SyntaxNode expression) => + protected override ArgumentSyntax GetLastArgumentIfArrayCreation(SyntaxNode expression) => expression switch { - ObjectCreationExpressionSyntax { } creation => - CheckLastArgument(creation.ArgumentList) && IsParamParameter(context, creation, creation.ArgumentList.Arguments.Last()), - InvocationExpressionSyntax { } invocation => - CheckLastArgument(invocation.ArgumentList) && IsParamParameter(context, invocation, invocation.ArgumentList.Arguments.Last()), - _ => false, + ObjectCreationExpressionSyntax { } creation => CheckLastArgument(creation.ArgumentList), + InvocationExpressionSyntax { } invocation => CheckLastArgument(invocation.ArgumentList), + _ => null }; - protected override Location GetLocation(SyntaxNode expression) => - expression switch - { - ObjectCreationExpressionSyntax { } creation => creation.ArgumentList.Arguments.Last().GetExpression().GetLocation(), - InvocationExpressionSyntax { } invocation => invocation.ArgumentList.Arguments.Last().GetExpression().GetLocation(), - _ => expression.GetLocation() - }; - - private static bool CheckLastArgument(ArgumentListSyntax argumentList) => + private static ArgumentSyntax CheckLastArgument(ArgumentListSyntax argumentList) => argumentList is not null && argumentList.Arguments.Any() && argumentList.Arguments.Last().GetExpression() is ArrayCreationExpressionSyntax invocationArray - && invocationArray.Initializer is CollectionInitializerSyntax { Initializers.Count: > 0 }; + && invocationArray.Initializer is CollectionInitializerSyntax { Initializers.Count: > 0 } + ? argumentList.Arguments.Last() + : null; } From 3b890f527e0c1eeed7ccd4ba30c00c250b243381 Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Fri, 27 Jan 2023 14:43:53 +0100 Subject: [PATCH 26/40] ParamsInvocationKinds --- .../SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs | 6 ++++++ .../Rules/ArrayPassedAsParamsBase.cs | 9 ++++----- .../Rules/ArrayPassedAsParams.cs | 6 ++++++ 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs index 9ec3c78d81f..eeced4c4e94 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs @@ -25,6 +25,12 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase Language => CSharpFacade.Instance; + protected override SyntaxKind[] ParamsInvocationKinds { get; } = + { + SyntaxKind.ObjectCreationExpression, + SyntaxKind.InvocationExpression + }; + protected override ArgumentSyntax GetLastArgumentIfArrayCreation(SyntaxNode expression) => expression switch { diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs index b2e4d4341cd..fe45c285941 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -28,16 +28,15 @@ public abstract class ArrayPassedAsParamsBase : Sona protected override string MessageFormat => "Remove this array creation and simply pass the elements."; private readonly DiagnosticDescriptor rule; + + protected abstract TSyntaxKind[] ParamsInvocationKinds { get; } protected abstract TArgumentNode GetLastArgumentIfArrayCreation(SyntaxNode invocation); protected ArrayPassedAsParamsBase() : base(DiagnosticId) => rule = Language.CreateDescriptor(DiagnosticId, MessageFormat); - protected sealed override void Initialize(SonarAnalysisContext context) - { - context.RegisterNodeAction(Language.GeneratedCodeRecognizer, CheckExpression, Language.SyntaxKind.ObjectCreationExpressions); - context.RegisterNodeAction(Language.GeneratedCodeRecognizer, CheckExpression, Language.SyntaxKind.InvocationExpression); - } + protected sealed override void Initialize(SonarAnalysisContext context) => + context.RegisterNodeAction(Language.GeneratedCodeRecognizer, CheckExpression, ParamsInvocationKinds); private void CheckExpression(SonarSyntaxNodeReportingContext context) { diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs index 2cd2fe3701e..fff6fee80c6 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs @@ -25,6 +25,12 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase Language => VisualBasicFacade.Instance; + protected override SyntaxKind[] ParamsInvocationKinds { get; } = + { + SyntaxKind.ObjectCreationExpression, + SyntaxKind.InvocationExpression + }; + protected override ArgumentSyntax GetLastArgumentIfArrayCreation(SyntaxNode expression) => expression switch { From 86f80c5e8b1b7d25a4c5cbe47e9848532816cbb8 Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Fri, 27 Jan 2023 14:49:34 +0100 Subject: [PATCH 27/40] Code smell fix --- .../src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs | 2 +- .../SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs | 6 +++--- .../SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs index eeced4c4e94..c2ffe4134ba 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs @@ -25,7 +25,7 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase Language => CSharpFacade.Instance; - protected override SyntaxKind[] ParamsInvocationKinds { get; } = + protected override SyntaxKind[] ExpressionKinds { get; } = { SyntaxKind.ObjectCreationExpression, SyntaxKind.InvocationExpression diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs index fe45c285941..c8e2f70f171 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -29,14 +29,14 @@ public abstract class ArrayPassedAsParamsBase : Sona private readonly DiagnosticDescriptor rule; - protected abstract TSyntaxKind[] ParamsInvocationKinds { get; } - protected abstract TArgumentNode GetLastArgumentIfArrayCreation(SyntaxNode invocation); + protected abstract TSyntaxKind[] ExpressionKinds { get; } + protected abstract TArgumentNode GetLastArgumentIfArrayCreation(SyntaxNode expression); protected ArrayPassedAsParamsBase() : base(DiagnosticId) => rule = Language.CreateDescriptor(DiagnosticId, MessageFormat); protected sealed override void Initialize(SonarAnalysisContext context) => - context.RegisterNodeAction(Language.GeneratedCodeRecognizer, CheckExpression, ParamsInvocationKinds); + context.RegisterNodeAction(Language.GeneratedCodeRecognizer, CheckExpression, ExpressionKinds); private void CheckExpression(SonarSyntaxNodeReportingContext context) { diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs index fff6fee80c6..4901676c972 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs @@ -25,7 +25,7 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase Language => VisualBasicFacade.Instance; - protected override SyntaxKind[] ParamsInvocationKinds { get; } = + protected override SyntaxKind[] ExpressionKinds { get; } = { SyntaxKind.ObjectCreationExpression, SyntaxKind.InvocationExpression From 055c76d7c89717b70c9f57a15789482dfe404b7d Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Fri, 27 Jan 2023 16:52:52 +0100 Subject: [PATCH 28/40] Addressed more PR comments --- .../Rules/ArrayPassedAsParams.cs | 16 +++++++--------- .../Rules/ArrayPassedAsParamsBase.cs | 6 +++--- .../Rules/ArrayPassedAsParams.cs | 15 +++++++-------- .../TestCases/ArrayPassedAsParams.cs | 3 ++- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs index c2ffe4134ba..1689028ffc4 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs @@ -34,16 +34,14 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase expression switch { - ObjectCreationExpressionSyntax { } creation => CheckLastArgument(creation.ArgumentList), - InvocationExpressionSyntax { } invocation => CheckLastArgument(invocation.ArgumentList), + ObjectCreationExpressionSyntax { } creation => GetLastArgumentIfArrayCreation(creation.ArgumentList), + InvocationExpressionSyntax { } invocation => GetLastArgumentIfArrayCreation(invocation.ArgumentList), _ => null }; - private static ArgumentSyntax CheckLastArgument(ArgumentListSyntax argumentList) => - argumentList is not null - && argumentList.Arguments.Any() - && argumentList.Arguments.Last().Expression is ArrayCreationExpressionSyntax invocationArray - && invocationArray.Initializer is InitializerExpressionSyntax { Expressions.Count: > 0 } - ? argumentList.Arguments.Last() - : null; + private static ArgumentSyntax GetLastArgumentIfArrayCreation(ArgumentListSyntax argumentList) => + argumentList is { Arguments: { Count: > 0 } arguments } + && arguments.Last() is { Expression: ArrayCreationExpressionSyntax { Initializer: InitializerExpressionSyntax { Expressions.Count: > 0 } } } lastArgument + ? lastArgument + : null; } diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs index c8e2f70f171..52a8494066a 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -41,14 +41,14 @@ public abstract class ArrayPassedAsParamsBase : Sona private void CheckExpression(SonarSyntaxNodeReportingContext context) { if (GetLastArgumentIfArrayCreation(context.Node) is { } lastArgument - && IsParamParameter(context, context.Node, lastArgument)) + && IsParamParameter(context.SemanticModel, context.Node, lastArgument)) { context.ReportIssue(Diagnostic.Create(rule, lastArgument.GetLocation())); } } - protected bool IsParamParameter(SonarSyntaxNodeReportingContext context, SyntaxNode invocation, SyntaxNode argument) => - context.SemanticModel.GetSymbolInfo(invocation).Symbol is IMethodSymbol methodSymbol + protected bool IsParamParameter(SemanticModel model, SyntaxNode invocation, SyntaxNode argument) => + model.GetSymbolInfo(invocation).Symbol is IMethodSymbol methodSymbol && Language.MethodParameterLookup(invocation, methodSymbol).TryGetSymbol(argument, out var param) && param.IsParams; } diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs index 4901676c972..9c9fa3959cc 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs @@ -34,16 +34,15 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase expression switch { - ObjectCreationExpressionSyntax { } creation => CheckLastArgument(creation.ArgumentList), - InvocationExpressionSyntax { } invocation => CheckLastArgument(invocation.ArgumentList), + ObjectCreationExpressionSyntax { } creation => GetLastArgumentIfArrayCreation(creation.ArgumentList), + InvocationExpressionSyntax { } invocation => GetLastArgumentIfArrayCreation(invocation.ArgumentList), _ => null }; - private static ArgumentSyntax CheckLastArgument(ArgumentListSyntax argumentList) => - argumentList is not null - && argumentList.Arguments.Any() - && argumentList.Arguments.Last().GetExpression() is ArrayCreationExpressionSyntax invocationArray + private static ArgumentSyntax GetLastArgumentIfArrayCreation(ArgumentListSyntax argumentList) => + argumentList is { Arguments: { Count: > 0 } arguments} + && arguments.Last().GetExpression() is ArrayCreationExpressionSyntax invocationArray && invocationArray.Initializer is CollectionInitializerSyntax { Initializers.Count: > 0 } - ? argumentList.Arguments.Last() - : null; + ? arguments.Last() + : null; } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs index 7d93f49a7bf..f21d44a06a7 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs @@ -39,7 +39,8 @@ public void Base(string[] myArray) var s3 = new IndexerClass(); var indexer1 = s3[new int[] { 1, 2 }]; // FN - var indexer2 = s3[1, 2]; // Compliant + var indexer2 = s3?[new int[] { 1, 2 }]; // FN + var indexer3 = s3[1, 2]; // Compliant } public void Method(params string[] args) { } From fcc244ff596c35ee17c405ba68de96c581eae10b Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Fri, 27 Jan 2023 18:19:02 +0100 Subject: [PATCH 29/40] More restrictive access modifier --- .../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 52a8494066a..814bcafa06a 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -47,7 +47,7 @@ private void CheckExpression(SonarSyntaxNodeReportingContext context) } } - protected bool IsParamParameter(SemanticModel model, SyntaxNode invocation, SyntaxNode argument) => + private bool IsParamParameter(SemanticModel model, SyntaxNode invocation, SyntaxNode argument) => model.GetSymbolInfo(invocation).Symbol is IMethodSymbol methodSymbol && Language.MethodParameterLookup(invocation, methodSymbol).TryGetSymbol(argument, out var param) && param.IsParams; From 1b4ed980bcd072bc048694a9412e73a7995ad87d Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Sun, 29 Jan 2023 18:34:51 +0100 Subject: [PATCH 30/40] Add support for implicit object creation expression --- .../Rules/ArrayPassedAsParams.cs | 7 +++++-- .../Helpers/MethodParameterLookupBase.cs | 10 +++++----- .../Rules/ArrayPassedAsParamsBase.cs | 16 +++++++--------- .../Rules/ArrayPassedAsParams.cs | 2 +- .../Rules/ArrayPassedAsParamsTest.cs | 14 +++++++++++++- .../TestCases/ArrayPassedAsParams.CSharp9.cs | 16 ++++++++++++++++ 6 files changed, 47 insertions(+), 18 deletions(-) create mode 100644 analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.CSharp9.cs diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs index 1689028ffc4..a3fbaa57e7c 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs @@ -28,7 +28,8 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase @@ -36,10 +37,12 @@ expression switch { ObjectCreationExpressionSyntax { } creation => GetLastArgumentIfArrayCreation(creation.ArgumentList), InvocationExpressionSyntax { } invocation => GetLastArgumentIfArrayCreation(invocation.ArgumentList), + _ when ImplicitObjectCreationExpressionSyntaxWrapper.IsInstance(expression) => + GetLastArgumentIfArrayCreation(((ImplicitObjectCreationExpressionSyntaxWrapper)expression).ArgumentList), _ => null }; - private static ArgumentSyntax GetLastArgumentIfArrayCreation(ArgumentListSyntax argumentList) => + private static ArgumentSyntax GetLastArgumentIfArrayCreation(BaseArgumentListSyntax argumentList) => argumentList is { Arguments: { Count: > 0 } arguments } && arguments.Last() is { Expression: ArrayCreationExpressionSyntax { Initializer: InitializerExpressionSyntax { Expressions.Count: > 0 } } } lastArgument ? lastArgument diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/MethodParameterLookupBase.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/MethodParameterLookupBase.cs index a17134cdbea..334c88e241f 100644 --- a/analyzers/src/SonarAnalyzer.Common/Helpers/MethodParameterLookupBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Helpers/MethodParameterLookupBase.cs @@ -45,26 +45,26 @@ protected MethodParameterLookupBase(SeparatedSyntaxList? argume MethodSymbol = methodSymbol; } - public bool TryGetSymbol(SyntaxNode arg, out IParameterSymbol parameter) + public bool TryGetSymbol(SyntaxNode argument, out IParameterSymbol parameter) { parameter = null; - var argument = (TArgumentSyntax)arg; + var arg = argument as TArgumentSyntax ?? throw new ArgumentException($"{nameof(argument)} must be of type {typeof(TArgumentSyntax)}", nameof(argument)); if (!argumentList.HasValue - || !argumentList.Value.Contains(argument) + || !argumentList.Value.Contains(arg) || MethodSymbol == null || MethodSymbol.IsVararg) { return false; } - if (GetNameColonArgumentIdentifier(argument) is { } nameColonArgumentIdentifier) + if (GetNameColonArgumentIdentifier(arg) is { } nameColonArgumentIdentifier) { parameter = MethodSymbol.Parameters.FirstOrDefault(symbol => symbol.Name == nameColonArgumentIdentifier.ValueText); return parameter != null; } - var index = argumentList.Value.IndexOf(argument); + var index = argumentList.Value.IndexOf(arg); if (index >= MethodSymbol.Parameters.Length) { var lastParameter = MethodSymbol.Parameters.Last(); diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs index 814bcafa06a..bdc0084dffb 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -36,16 +36,14 @@ public abstract class ArrayPassedAsParamsBase : Sona rule = Language.CreateDescriptor(DiagnosticId, MessageFormat); protected sealed override void Initialize(SonarAnalysisContext context) => - context.RegisterNodeAction(Language.GeneratedCodeRecognizer, CheckExpression, ExpressionKinds); - - private void CheckExpression(SonarSyntaxNodeReportingContext context) - { - if (GetLastArgumentIfArrayCreation(context.Node) is { } lastArgument - && IsParamParameter(context.SemanticModel, context.Node, lastArgument)) + context.RegisterNodeAction(Language.GeneratedCodeRecognizer, c => { - context.ReportIssue(Diagnostic.Create(rule, lastArgument.GetLocation())); - } - } + if (GetLastArgumentIfArrayCreation(c.Node) is { } lastArgument + && IsParamParameter(c.SemanticModel, c.Node, lastArgument)) + { + c.ReportIssue(Diagnostic.Create(rule, lastArgument.GetLocation())); + } + }, ExpressionKinds); private bool IsParamParameter(SemanticModel model, SyntaxNode invocation, SyntaxNode argument) => model.GetSymbolInfo(invocation).Symbol is IMethodSymbol methodSymbol diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs index 9c9fa3959cc..60111d621a2 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs @@ -40,7 +40,7 @@ expression switch }; private static ArgumentSyntax GetLastArgumentIfArrayCreation(ArgumentListSyntax argumentList) => - argumentList is { Arguments: { Count: > 0 } arguments} + argumentList is { Arguments: { Count: > 0 } arguments } && arguments.Last().GetExpression() is ArrayCreationExpressionSyntax invocationArray && invocationArray.Initializer is CollectionInitializerSyntax { Initializers.Count: > 0 } ? arguments.Last() diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/ArrayPassedAsParamsTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/ArrayPassedAsParamsTest.cs index 825af1c0dff..f1fc5b94eb4 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/ArrayPassedAsParamsTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/ArrayPassedAsParamsTest.cs @@ -26,9 +26,21 @@ namespace SonarAnalyzer.UnitTest.Rules; [TestClass] public class ArrayPassedAsParamsTest { + private readonly VerifierBuilder builderCS = new VerifierBuilder(); + [TestMethod] public void ArrayPassedAsParams_CS() => - new VerifierBuilder().AddPaths("ArrayPassedAsParams.cs").Verify(); + builderCS.AddPaths("ArrayPassedAsParams.cs").Verify(); + +#if NET + + [TestMethod] + public void ArrayPassedAsParams_CSharp9() => + builderCS.AddPaths("ArrayPassedAsParams.CSharp9.cs") + .WithOptions(ParseOptionsHelper.FromCSharp9) + .Verify(); + +#endif [TestMethod] public void ArrayPassedAsParams_VB() => diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.CSharp9.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.CSharp9.cs new file mode 100644 index 00000000000..811cc60662d --- /dev/null +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.CSharp9.cs @@ -0,0 +1,16 @@ +using System; + +public class Program +{ + public void Base() + { + MyClass s = new(1, new int[] { 2, 3 }); // Noncompliant +// ^^^^^^^^^^^^^^^^^^ + MyClass s1 = new(1, 2, 3); // Compliant + } +} + +public class MyClass +{ + public MyClass(int a, params int[] args) { } +} From b6e651ee3f784c9a85fec3222ad30f0c5a9d0818 Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Mon, 30 Jan 2023 08:30:16 +0100 Subject: [PATCH 31/40] CSharp9 test case with TopLevelStatements --- .../Rules/ArrayPassedAsParamsTest.cs | 2 +- .../TestCases/ArrayPassedAsParams.CSharp9.cs | 12 +++--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/ArrayPassedAsParamsTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/ArrayPassedAsParamsTest.cs index f1fc5b94eb4..9e03377acc4 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/ArrayPassedAsParamsTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/ArrayPassedAsParamsTest.cs @@ -37,7 +37,7 @@ public class ArrayPassedAsParamsTest [TestMethod] public void ArrayPassedAsParams_CSharp9() => builderCS.AddPaths("ArrayPassedAsParams.CSharp9.cs") - .WithOptions(ParseOptionsHelper.FromCSharp9) + .WithTopLevelStatements() .Verify(); #endif diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.CSharp9.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.CSharp9.cs index 811cc60662d..e51f307939c 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.CSharp9.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.CSharp9.cs @@ -1,14 +1,8 @@ using System; -public class Program -{ - public void Base() - { - MyClass s = new(1, new int[] { 2, 3 }); // Noncompliant -// ^^^^^^^^^^^^^^^^^^ - MyClass s1 = new(1, 2, 3); // Compliant - } -} +MyClass s = new(1, new int[] { 2, 3 }); // Noncompliant +// ^^^^^^^^^^^^^^^^^^ +MyClass s1 = new(1, 2, 3); // Compliant public class MyClass { From a9c65306ba27465030e98ec7e24a8e2f14a156bc Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Mon, 30 Jan 2023 10:11:20 +0100 Subject: [PATCH 32/40] Removed switch default case --- .../SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs | 3 +-- .../Rules/ArrayPassedAsParamsBase.cs | 4 ++-- .../Rules/ArrayPassedAsParams.cs | 9 ++++----- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs index a3fbaa57e7c..d30504e1a0e 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs @@ -38,8 +38,7 @@ expression switch ObjectCreationExpressionSyntax { } creation => GetLastArgumentIfArrayCreation(creation.ArgumentList), InvocationExpressionSyntax { } invocation => GetLastArgumentIfArrayCreation(invocation.ArgumentList), _ when ImplicitObjectCreationExpressionSyntaxWrapper.IsInstance(expression) => - GetLastArgumentIfArrayCreation(((ImplicitObjectCreationExpressionSyntaxWrapper)expression).ArgumentList), - _ => null + GetLastArgumentIfArrayCreation(((ImplicitObjectCreationExpressionSyntaxWrapper)expression).ArgumentList) }; private static ArgumentSyntax GetLastArgumentIfArrayCreation(BaseArgumentListSyntax argumentList) => diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs index bdc0084dffb..bc5c36e57c6 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ArrayPassedAsParamsBase.cs @@ -39,13 +39,13 @@ public abstract class ArrayPassedAsParamsBase : Sona context.RegisterNodeAction(Language.GeneratedCodeRecognizer, c => { if (GetLastArgumentIfArrayCreation(c.Node) is { } lastArgument - && IsParamParameter(c.SemanticModel, c.Node, lastArgument)) + && IsParamParameter(c.SemanticModel, c.Node, lastArgument)) { c.ReportIssue(Diagnostic.Create(rule, lastArgument.GetLocation())); } }, ExpressionKinds); - private bool IsParamParameter(SemanticModel model, SyntaxNode invocation, SyntaxNode argument) => + private bool IsParamParameter(SemanticModel model, SyntaxNode invocation, TArgumentNode argument) => model.GetSymbolInfo(invocation).Symbol is IMethodSymbol methodSymbol && Language.MethodParameterLookup(invocation, methodSymbol).TryGetSymbol(argument, out var param) && param.IsParams; diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs index 60111d621a2..0b90230e0ce 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs @@ -35,14 +35,13 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase GetLastArgumentIfArrayCreation(creation.ArgumentList), - InvocationExpressionSyntax { } invocation => GetLastArgumentIfArrayCreation(invocation.ArgumentList), - _ => null + InvocationExpressionSyntax { } invocation => GetLastArgumentIfArrayCreation(invocation.ArgumentList) }; private static ArgumentSyntax GetLastArgumentIfArrayCreation(ArgumentListSyntax argumentList) => argumentList is { Arguments: { Count: > 0 } arguments } - && arguments.Last().GetExpression() is ArrayCreationExpressionSyntax invocationArray - && invocationArray.Initializer is CollectionInitializerSyntax { Initializers.Count: > 0 } - ? arguments.Last() + && arguments.Last() is var argument + && argument.GetExpression() is ArrayCreationExpressionSyntax { Initializer: CollectionInitializerSyntax { Initializers.Count: > 0 } } + ? argument : null; } From 6bcca9ea3161e19e1e78afa492ba3a58607ce784 Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Mon, 30 Jan 2023 10:43:31 +0100 Subject: [PATCH 33/40] Add test case for ArgumentException --- .../SonarAnalyzer.UnitTest/Helpers/MethodParameterLookupTest.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/MethodParameterLookupTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/MethodParameterLookupTest.cs index 2326f502f28..9e9be29ddfc 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/MethodParameterLookupTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/MethodParameterLookupTest.cs @@ -207,6 +207,7 @@ private void InspectTryGetSyntax(MethodParameterLookupBase look private void InspectTryGetSymbol(MethodParameterLookupBase lookup, object expectedArguments, TArgumentSyntax[] arguments) { + Assert.ThrowsException(() => lookup.TryGetSymbol(SpecialArgument.Parent, out var parameter)); lookup.TryGetSymbol(SpecialArgument, out var parameter).Should().Be(false); foreach (var argument in arguments) From 926be7c89aab20da94dff76b17c89c14434e6aa0 Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Mon, 30 Jan 2023 15:17:24 +0100 Subject: [PATCH 34/40] Standalone ThrowsException tests --- .../Rules/ArrayPassedAsParams.cs | 10 ++- .../Rules/ArrayPassedAsParams.cs | 8 +- .../Helpers/MethodParameterLookupTest.cs | 77 +++++++++++-------- 3 files changed, 59 insertions(+), 36 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs index d30504e1a0e..fa886535de0 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs @@ -33,12 +33,16 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase + GetLastArgumentIfArrayCreation(GetArgumentListFromExpression(expression)); + + private static BaseArgumentListSyntax GetArgumentListFromExpression(SyntaxNode expression) => expression switch { - ObjectCreationExpressionSyntax { } creation => GetLastArgumentIfArrayCreation(creation.ArgumentList), - InvocationExpressionSyntax { } invocation => GetLastArgumentIfArrayCreation(invocation.ArgumentList), + ObjectCreationExpressionSyntax { } creation => creation.ArgumentList, + InvocationExpressionSyntax { } invocation => invocation.ArgumentList, _ when ImplicitObjectCreationExpressionSyntaxWrapper.IsInstance(expression) => - GetLastArgumentIfArrayCreation(((ImplicitObjectCreationExpressionSyntaxWrapper)expression).ArgumentList) + ((ImplicitObjectCreationExpressionSyntaxWrapper)expression).ArgumentList, + _ => null }; private static ArgumentSyntax GetLastArgumentIfArrayCreation(BaseArgumentListSyntax argumentList) => diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs index 0b90230e0ce..d6ead281668 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs @@ -32,10 +32,14 @@ public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase + GetLastArgumentIfArrayCreation(GetArgumentListFromExpression(expression)); + + private static ArgumentListSyntax GetArgumentListFromExpression(SyntaxNode expression) => expression switch { - ObjectCreationExpressionSyntax { } creation => GetLastArgumentIfArrayCreation(creation.ArgumentList), - InvocationExpressionSyntax { } invocation => GetLastArgumentIfArrayCreation(invocation.ArgumentList) + ObjectCreationExpressionSyntax { } creation => creation.ArgumentList, + InvocationExpressionSyntax { } invocation => invocation.ArgumentList, + _ => null }; private static ArgumentSyntax GetLastArgumentIfArrayCreation(ArgumentListSyntax argumentList) => diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/MethodParameterLookupTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/MethodParameterLookupTest.cs index 9e9be29ddfc..c7006201ab1 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/MethodParameterLookupTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/MethodParameterLookupTest.cs @@ -20,7 +20,9 @@ using System.Collections; using SonarAnalyzer.Common; +using CSharpCodeAnalysis = Microsoft.CodeAnalysis.CSharp; using CSharpSyntax = Microsoft.CodeAnalysis.CSharp.Syntax; +using VBCodeAnalysis = Microsoft.CodeAnalysis.VisualBasic; using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax; namespace SonarAnalyzer.UnitTest.Helpers @@ -28,10 +30,7 @@ namespace SonarAnalyzer.UnitTest.Helpers [TestClass] public class MethodParameterLookupTest { - [TestMethod] - public void TestMethodParameterLookup_CS() - { - const string Source = @" + private const string SourceCS = @" namespace Test { class TestClass @@ -74,28 +73,8 @@ void SpecialMethod(int specialParameter) } } }"; - var c = new CSharpInspection(Source); - c.CheckExpectedParameterMappings(0, "DoNothing", new { }); - c.CheckExpectedParameterMappings(1, "DoSomething", new { a = 1, b = true }); - c.CheckExpectedParameterMappings(2, "DoSomething", new { a = 1, b = true }); - c.CheckExpectedParameterMappings(3, "WithOptional", new { a = 1 }); - c.CheckExpectedParameterMappings(4, "WithOptional", new { a = 1, opt = "Ipsum" }); - c.CheckExpectedParameterMappings(5, "WithOptional", new { a = 1, opt = "Ipsum" }); - c.CheckExpectedParameterMappings(6, "WithParams", new { }); - c.CheckExpectedParameterMappings(7, "WithParams", new { arr = new[] { 1, 2, 3 } }); - c.MainInvocations.Length.Should().Be(8); // Self-Test of this test. If new Invocation is added to the Main(), this number has to be updated and test should be written for that case. - - // TryGetNonParamsSyntax throw scenario - var lookupThrow = c.CreateLookup(6, "WithParams"); - Action actionThrow = () => lookupThrow.TryGetNonParamsSyntax(lookupThrow.MethodSymbol.Parameters.Single(), out var argument); - actionThrow.Should().Throw(); - } - - [TestMethod] - public void TestMethodParameterLookup_VB() - { - const string Source = @" + private const string SourceVB = @" Module MainModule Sub Main() @@ -127,7 +106,27 @@ Sub SpecialMethod(SpecialParameter As Integer) End Sub End Module "; - var c = new VisualBasicInspection(Source); + + [TestMethod] + public void TestMethodParameterLookup_CS() + { + var c = new CSharpInspection(SourceCS); + c.CheckExpectedParameterMappings(0, "DoNothing", new { }); + c.CheckExpectedParameterMappings(1, "DoSomething", new { a = 1, b = true }); + c.CheckExpectedParameterMappings(2, "DoSomething", new { a = 1, b = true }); + c.CheckExpectedParameterMappings(3, "WithOptional", new { a = 1 }); + c.CheckExpectedParameterMappings(4, "WithOptional", new { a = 1, opt = "Ipsum" }); + c.CheckExpectedParameterMappings(5, "WithOptional", new { a = 1, opt = "Ipsum" }); + c.CheckExpectedParameterMappings(6, "WithParams", new { }); + c.CheckExpectedParameterMappings(7, "WithParams", new { arr = new[] { 1, 2, 3 } }); + + c.MainInvocations.Length.Should().Be(8); // Self-Test of this test. If new Invocation is added to the Main(), this number has to be updated and test should be written for that case. + } + + [TestMethod] + public void TestMethodParameterLookup_VB() + { + var c = new VisualBasicInspection(SourceVB); c.CheckExpectedParameterMappings(0, "DoNothing", new { }); c.CheckExpectedParameterMappings(1, "DoSomething", new { a = 1, b = true }); c.CheckExpectedParameterMappings(2, "WithOptional", new { a = 1 }); @@ -136,11 +135,28 @@ End Module c.CheckExpectedParameterMappings(5, "WithParams", new { arr = new[] { 1, 2, 3 } }); c.MainInvocations.Length.Should().Be(6); // Self-Test of this test. If new Invocation is added to the Main(), this number has to be updated and test should be written for that case. + } + + [TestMethod] + public void TestMethodParameterLookup_CS_ThrowsException() + { + var c = new CSharpInspection(SourceCS); + var lookupThrow = c.CreateLookup(1, "DoSomething"); + + Assert.ThrowsException(() => lookupThrow.TryGetNonParamsSyntax(lookupThrow.MethodSymbol.Parameters.Single(), out var argument)); + Assert.ThrowsException(() => + lookupThrow.TryGetSymbol(CSharpCodeAnalysis.SyntaxFactory.LiteralExpression(CSharpCodeAnalysis.SyntaxKind.StringLiteralExpression), out var parameter)); + } + + [TestMethod] + public void TestMethodParameterLookup_VB_ThrowsException() + { + var c = new VisualBasicInspection(SourceVB); + var lookupThrow = c.CreateLookup(1, "DoSomething"); - // TryGetNonParamsSyntax throw scenario - var lookupThrow = c.CreateLookup(4, "WithParams"); - Action actionThrow = () => lookupThrow.TryGetNonParamsSyntax(lookupThrow.MethodSymbol.Parameters.Single(), out var argument); - actionThrow.Should().Throw(); + Assert.ThrowsException(() => lookupThrow.TryGetNonParamsSyntax(lookupThrow.MethodSymbol.Parameters.Single(), out var argument)); + Assert.ThrowsException(() => + lookupThrow.TryGetSymbol(VBCodeAnalysis.SyntaxFactory.StringLiteralExpression(VBCodeAnalysis.SyntaxFactory.StringLiteralToken(string.Empty, string.Empty)), out var parameter)); } private abstract class InspectionBase @@ -207,7 +223,6 @@ private void InspectTryGetSyntax(MethodParameterLookupBase look private void InspectTryGetSymbol(MethodParameterLookupBase lookup, object expectedArguments, TArgumentSyntax[] arguments) { - Assert.ThrowsException(() => lookup.TryGetSymbol(SpecialArgument.Parent, out var parameter)); lookup.TryGetSymbol(SpecialArgument, out var parameter).Should().Be(false); foreach (var argument in arguments) From 51095bbb4c5972901926f5ae67c642484e7bcff0 Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Tue, 31 Jan 2023 11:22:40 +0100 Subject: [PATCH 35/40] Increased precision for VB --- .../Rules/ArrayPassedAsParams.cs | 12 ++++++- .../Helpers/MethodParameterLookupTest.cs | 14 +++++--- .../Rules/ArrayPassedAsParamsTest.cs | 33 ++++++++++++++++++- .../TestCases/ArrayPassedAsParams.cs | 2 ++ 4 files changed, 55 insertions(+), 6 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs index d6ead281668..ad182d0a1cf 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs @@ -45,7 +45,17 @@ expression switch private static ArgumentSyntax GetLastArgumentIfArrayCreation(ArgumentListSyntax argumentList) => argumentList is { Arguments: { Count: > 0 } arguments } && arguments.Last() is var argument - && argument.GetExpression() is ArrayCreationExpressionSyntax { Initializer: CollectionInitializerSyntax { Initializers.Count: > 0 } } + && IsArrayCreation(argument.GetExpression()) ? argument : null; + + private static bool IsArrayCreation(ExpressionSyntax expression) => + expression switch + { + ArrayCreationExpressionSyntax { } arrayCreation => + arrayCreation.Initializer is CollectionInitializerSyntax { Initializers.Count: > 0 } + || arrayCreation.ArrayBounds is not ArgumentListSyntax { }, + CollectionInitializerSyntax { } => true, + _ => false + }; } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/MethodParameterLookupTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/MethodParameterLookupTest.cs index c7006201ab1..d410caf78ec 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/MethodParameterLookupTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/MethodParameterLookupTest.cs @@ -143,9 +143,12 @@ public void TestMethodParameterLookup_CS_ThrowsException() var c = new CSharpInspection(SourceCS); var lookupThrow = c.CreateLookup(1, "DoSomething"); - Assert.ThrowsException(() => lookupThrow.TryGetNonParamsSyntax(lookupThrow.MethodSymbol.Parameters.Single(), out var argument)); - Assert.ThrowsException(() => + var invalidOperationEx = Assert.ThrowsException(() => lookupThrow.TryGetNonParamsSyntax(lookupThrow.MethodSymbol.Parameters.Single(), out var argument)); + invalidOperationEx.Message.Should().Be("Sequence contains more than one element"); + + var argumentEx = Assert.ThrowsException(() => lookupThrow.TryGetSymbol(CSharpCodeAnalysis.SyntaxFactory.LiteralExpression(CSharpCodeAnalysis.SyntaxKind.StringLiteralExpression), out var parameter)); + argumentEx.Message.Should().Be("argument must be of type Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax (Parameter 'argument')"); } [TestMethod] @@ -154,9 +157,12 @@ public void TestMethodParameterLookup_VB_ThrowsException() var c = new VisualBasicInspection(SourceVB); var lookupThrow = c.CreateLookup(1, "DoSomething"); - Assert.ThrowsException(() => lookupThrow.TryGetNonParamsSyntax(lookupThrow.MethodSymbol.Parameters.Single(), out var argument)); - Assert.ThrowsException(() => + var invalidOperationEx = Assert.ThrowsException(() => lookupThrow.TryGetNonParamsSyntax(lookupThrow.MethodSymbol.Parameters.Single(), out var argument)); + invalidOperationEx.Message.Should().Be("Sequence contains more than one element"); + + var argumentEx = Assert.ThrowsException(() => lookupThrow.TryGetSymbol(VBCodeAnalysis.SyntaxFactory.StringLiteralExpression(VBCodeAnalysis.SyntaxFactory.StringLiteralToken(string.Empty, string.Empty)), out var parameter)); + argumentEx.Message.Should().Be("argument must be of type Microsoft.CodeAnalysis.VisualBasic.Syntax.ArgumentSyntax (Parameter 'argument')"); } private abstract class InspectionBase diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/ArrayPassedAsParamsTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/ArrayPassedAsParamsTest.cs index 9e03377acc4..5e5fb174a23 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/ArrayPassedAsParamsTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/ArrayPassedAsParamsTest.cs @@ -27,6 +27,7 @@ namespace SonarAnalyzer.UnitTest.Rules; public class ArrayPassedAsParamsTest { private readonly VerifierBuilder builderCS = new VerifierBuilder(); + private readonly VerifierBuilder builderVB = new VerifierBuilder(); [TestMethod] public void ArrayPassedAsParams_CS() => @@ -44,5 +45,35 @@ public class ArrayPassedAsParamsTest [TestMethod] public void ArrayPassedAsParams_VB() => - new VerifierBuilder().AddPaths("ArrayPassedAsParams.vb").Verify(); + builderVB.AddPaths("ArrayPassedAsParams.vb").Verify(); + + [DataTestMethod] + [DataRow("{ }", false)] + [DataRow("{ \"s\", \"s\", \"s\", \"s\" }", false)] + [DataRow("New String(2) { }", true)] + [DataRow("New String(2) { \"s\", \"s\", \"s\" }", false)] + [DataRow("New String() { }", false)] + [DataRow("New String() { \"s\" }", false)] + public void ArrayPassedAsParams_VBCollectionInitializerSyntaxTests(string arrayCreation, bool compliant) + { + var code = $$""" + Public Class C + Public Sub M() + ParamsMethod({{arrayCreation}}) ' {{(compliant ? "Compliant" : "Noncompliant")}} + End Sub + + Public Sub ParamsMethod(ParamArray p As String()) + End Sub + End Class + """; + var builder = builderVB.AddSnippet(code); + if (compliant) + { + builder.VerifyNoIssueReported(); + } + else + { + builder.Verify(); + } + } } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs index f21d44a06a7..9bc15eca7d4 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs @@ -7,6 +7,7 @@ public void Base(string[] myArray) Method(new string[] { "s1", "s2" }); // Noncompliant {{Remove this array creation and simply pass the elements.}} // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Method(new string[] { "s1" }); // Noncompliant + Method(new string[] { }); // Compliant Method("s1"); // Compliant Method("s1", "s2"); // Compliant Method(myArray); // Compliant @@ -20,6 +21,7 @@ public void Base(string[] myArray) Method2(1, myArray); // Compliant Method2(1, new string[12]); // Compliant + Method3(new string[] { "s1", "s2" }); // Compliant Method3(new string[] { "s1", "s2" }, "s1"); // Compliant Method3(new string[] { "s1", "s2" }, new string[12]); // Compliant Method3(new string[] { "s1", "s2" }, new string[] { "s1", "s2" }); // Noncompliant From 0e31460578bf921e0ab37b442f0d12b3fcd18fa3 Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Tue, 31 Jan 2023 11:41:30 +0100 Subject: [PATCH 36/40] Add coverage for CS ImplicitArrayCreation --- .../Rules/ArrayPassedAsParams.cs | 13 +++++++++++-- .../TestCases/ArrayPassedAsParams.cs | 1 + 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs index fa886535de0..a689ff721ce 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs @@ -47,7 +47,16 @@ expression switch private static ArgumentSyntax GetLastArgumentIfArrayCreation(BaseArgumentListSyntax argumentList) => argumentList is { Arguments: { Count: > 0 } arguments } - && arguments.Last() is { Expression: ArrayCreationExpressionSyntax { Initializer: InitializerExpressionSyntax { Expressions.Count: > 0 } } } lastArgument - ? lastArgument + && IsArrayCreation(arguments.Last().Expression) + ? arguments.Last() : null; + + private static bool IsArrayCreation(ExpressionSyntax expression) => + expression switch + { + ArrayCreationExpressionSyntax { } arrayCreation => + arrayCreation.Initializer is InitializerExpressionSyntax { Expressions.Count: > 0 }, + ImplicitArrayCreationExpressionSyntax { } => true, + _ => false + }; } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs index 9bc15eca7d4..0eb09764748 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs @@ -7,6 +7,7 @@ public void Base(string[] myArray) Method(new string[] { "s1", "s2" }); // Noncompliant {{Remove this array creation and simply pass the elements.}} // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Method(new string[] { "s1" }); // Noncompliant + Method(new[] { "s1" }); // Noncompliant Method(new string[] { }); // Compliant Method("s1"); // Compliant Method("s1", "s2"); // Compliant From 5e61ae9cfe88cbaaec845bcd32f8462803dcd58d Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Tue, 31 Jan 2023 11:58:21 +0100 Subject: [PATCH 37/40] Fix UTs --- .../Helpers/MethodParameterLookupTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/MethodParameterLookupTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/MethodParameterLookupTest.cs index d410caf78ec..8cb20bf9d21 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/MethodParameterLookupTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Helpers/MethodParameterLookupTest.cs @@ -148,7 +148,7 @@ public void TestMethodParameterLookup_CS_ThrowsException() var argumentEx = Assert.ThrowsException(() => lookupThrow.TryGetSymbol(CSharpCodeAnalysis.SyntaxFactory.LiteralExpression(CSharpCodeAnalysis.SyntaxKind.StringLiteralExpression), out var parameter)); - argumentEx.Message.Should().Be("argument must be of type Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax (Parameter 'argument')"); + argumentEx.Message.Should().StartWith("argument must be of type Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax"); } [TestMethod] @@ -162,7 +162,7 @@ public void TestMethodParameterLookup_VB_ThrowsException() var argumentEx = Assert.ThrowsException(() => lookupThrow.TryGetSymbol(VBCodeAnalysis.SyntaxFactory.StringLiteralExpression(VBCodeAnalysis.SyntaxFactory.StringLiteralToken(string.Empty, string.Empty)), out var parameter)); - argumentEx.Message.Should().Be("argument must be of type Microsoft.CodeAnalysis.VisualBasic.Syntax.ArgumentSyntax (Parameter 'argument')"); + argumentEx.Message.Should().StartWith("argument must be of type Microsoft.CodeAnalysis.VisualBasic.Syntax.ArgumentSyntax"); } private abstract class InspectionBase From cfab8c121dfca8c2a85fd1c5d06c1d86af7faff5 Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Tue, 31 Jan 2023 12:29:07 +0100 Subject: [PATCH 38/40] Fix ITs --- .../expected/Nancy/Nancy--net452-S3878.json | 290 ++++++++++++++++++ .../Nancy/Nancy--netstandard2.0-S3878.json | 290 ++++++++++++++++++ .../Nancy/Nancy.Embedded--net452-S3878.json | 30 ++ .../Nancy.Embedded--netstandard2.0-S3878.json | 30 ++ ...y.ViewEngines.DotLiquid--net452-S3878.json | 17 + .../Akka.Cluster--netstandard2.0-S3878.json | 69 +++++ ...luster.Sharding--netstandard2.0-S3878.json | 17 + .../Akka.NodeTestRunner--net5.0-S3878.json | 30 ++ ...a.NodeTestRunner--netcoreapp3.1-S3878.json | 30 ++ ...Persistence.TCK--netstandard2.0-S3878.json | 30 ++ .../RemotePingPong--net471-S3878.json | 17 + .../RemotePingPong--net5.0-S3878.json | 17 + .../RemotePingPong--netcoreapp3.1-S3878.json | 17 + .../Samples.Cluster.Simple--net471-S3878.json | 17 + ....Cluster.Transformation--net471-S3878.json | 17 + 15 files changed, 918 insertions(+) create mode 100644 analyzers/its/expected/Nancy/Nancy--net452-S3878.json create mode 100644 analyzers/its/expected/Nancy/Nancy--netstandard2.0-S3878.json create mode 100644 analyzers/its/expected/Nancy/Nancy.Embedded--net452-S3878.json create mode 100644 analyzers/its/expected/Nancy/Nancy.Embedded--netstandard2.0-S3878.json create mode 100644 analyzers/its/expected/Nancy/Nancy.ViewEngines.DotLiquid--net452-S3878.json create mode 100644 analyzers/its/expected/akka.net/Akka.Cluster--netstandard2.0-S3878.json create mode 100644 analyzers/its/expected/akka.net/Akka.Cluster.Sharding--netstandard2.0-S3878.json create mode 100644 analyzers/its/expected/akka.net/Akka.NodeTestRunner--net5.0-S3878.json create mode 100644 analyzers/its/expected/akka.net/Akka.NodeTestRunner--netcoreapp3.1-S3878.json create mode 100644 analyzers/its/expected/akka.net/Akka.Persistence.TCK--netstandard2.0-S3878.json create mode 100644 analyzers/its/expected/akka.net/RemotePingPong--net471-S3878.json create mode 100644 analyzers/its/expected/akka.net/RemotePingPong--net5.0-S3878.json create mode 100644 analyzers/its/expected/akka.net/RemotePingPong--netcoreapp3.1-S3878.json create mode 100644 analyzers/its/expected/akka.net/Samples.Cluster.Simple--net471-S3878.json create mode 100644 analyzers/its/expected/akka.net/Samples.Cluster.Transformation--net471-S3878.json diff --git a/analyzers/its/expected/Nancy/Nancy--net452-S3878.json b/analyzers/its/expected/Nancy/Nancy--net452-S3878.json new file mode 100644 index 00000000000..723fb841320 --- /dev/null +++ b/analyzers/its/expected/Nancy/Nancy--net452-S3878.json @@ -0,0 +1,290 @@ +{ +"issues": [ +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\Bootstrapper\NancyBootstrapperLocator.cs", +"region": { +"startLine": 135, +"startColumn": 53, +"endLine": 141, +"endColumn": 14 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\Conventions\DefaultViewLocationConventions.cs", +"region": { +"startLine": 49, +"startColumn": 73, +"endLine": 49, +"endColumn": 86 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\Conventions\DefaultViewLocationConventions.cs", +"region": { +"startLine": 61, +"startColumn": 73, +"endLine": 61, +"endColumn": 86 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\Conventions\DefaultViewLocationConventions.cs", +"region": { +"startLine": 72, +"startColumn": 73, +"endLine": 72, +"endColumn": 86 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\Conventions\DefaultViewLocationConventions.cs", +"region": { +"startLine": 84, +"startColumn": 73, +"endLine": 84, +"endColumn": 86 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\Conventions\DefaultViewLocationConventions.cs", +"region": { +"startLine": 90, +"startColumn": 163, +"endLine": 90, +"endColumn": 176 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\Conventions\DefaultViewLocationConventions.cs", +"region": { +"startLine": 95, +"startColumn": 163, +"endLine": 95, +"endColumn": 174 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\Conventions\DefaultViewLocationConventions.cs", +"region": { +"startLine": 99, +"startColumn": 153, +"endLine": 99, +"endColumn": 166 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\Conventions\DefaultViewLocationConventions.cs", +"region": { +"startLine": 104, +"startColumn": 153, +"endLine": 104, +"endColumn": 166 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\Conventions\StaticContentConventionBuilder.cs", +"region": { +"startLine": 156, +"startColumn": 108, +"endLine": 156, +"endColumn": 120 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\Conventions\StaticContentConventionBuilder.cs", +"region": { +"startLine": 211, +"startColumn": 60, +"endLine": 211, +"endColumn": 73 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\Conventions\StaticContentConventionBuilder.cs", +"region": { +"startLine": 221, +"startColumn": 45, +"endLine": 221, +"endColumn": 56 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\HttpMultipartBoundary.cs", +"region": { +"startLine": 76, +"startColumn": 53, +"endLine": 76, +"endColumn": 66 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\ModelBinding\DefaultBinder.cs", +"region": { +"startLine": 202, +"startColumn": 80, +"endLine": 202, +"endColumn": 101 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\ModelBinding\DefaultConverters\CollectionConverter.cs", +"region": { +"startLine": 103, +"startColumn": 81, +"endLine": 103, +"endColumn": 102 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\ModelBinding\DefaultConverters\CollectionConverter.cs", +"region": { +"startLine": 104, +"startColumn": 87, +"endLine": 104, +"endColumn": 108 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\ModelBinding\DefaultConverters\CollectionConverter.cs", +"region": { +"startLine": 126, +"startColumn": 81, +"endLine": 126, +"endColumn": 102 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\ModelBinding\DefaultConverters\CollectionConverter.cs", +"region": { +"startLine": 127, +"startColumn": 85, +"endLine": 127, +"endColumn": 106 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\ViewEngines\FileSystemViewLocationProvider.cs", +"region": { +"startLine": 120, +"startColumn": 28, +"endLine": 120, +"endColumn": 65 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\ViewEngines\FileSystemViewLocationProvider.cs", +"region": { +"startLine": 123, +"startColumn": 26, +"endLine": 123, +"endColumn": 40 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\ViewEngines\ResourceViewLocationProvider.cs", +"region": { +"startLine": 130, +"startColumn": 23, +"endLine": 130, +"endColumn": 36 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\ViewEngines\ResourceViewLocationProvider.cs", +"region": { +"startLine": 142, +"startColumn": 30, +"endLine": 142, +"endColumn": 43 +} +} +} +] +} diff --git a/analyzers/its/expected/Nancy/Nancy--netstandard2.0-S3878.json b/analyzers/its/expected/Nancy/Nancy--netstandard2.0-S3878.json new file mode 100644 index 00000000000..723fb841320 --- /dev/null +++ b/analyzers/its/expected/Nancy/Nancy--netstandard2.0-S3878.json @@ -0,0 +1,290 @@ +{ +"issues": [ +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\Bootstrapper\NancyBootstrapperLocator.cs", +"region": { +"startLine": 135, +"startColumn": 53, +"endLine": 141, +"endColumn": 14 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\Conventions\DefaultViewLocationConventions.cs", +"region": { +"startLine": 49, +"startColumn": 73, +"endLine": 49, +"endColumn": 86 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\Conventions\DefaultViewLocationConventions.cs", +"region": { +"startLine": 61, +"startColumn": 73, +"endLine": 61, +"endColumn": 86 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\Conventions\DefaultViewLocationConventions.cs", +"region": { +"startLine": 72, +"startColumn": 73, +"endLine": 72, +"endColumn": 86 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\Conventions\DefaultViewLocationConventions.cs", +"region": { +"startLine": 84, +"startColumn": 73, +"endLine": 84, +"endColumn": 86 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\Conventions\DefaultViewLocationConventions.cs", +"region": { +"startLine": 90, +"startColumn": 163, +"endLine": 90, +"endColumn": 176 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\Conventions\DefaultViewLocationConventions.cs", +"region": { +"startLine": 95, +"startColumn": 163, +"endLine": 95, +"endColumn": 174 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\Conventions\DefaultViewLocationConventions.cs", +"region": { +"startLine": 99, +"startColumn": 153, +"endLine": 99, +"endColumn": 166 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\Conventions\DefaultViewLocationConventions.cs", +"region": { +"startLine": 104, +"startColumn": 153, +"endLine": 104, +"endColumn": 166 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\Conventions\StaticContentConventionBuilder.cs", +"region": { +"startLine": 156, +"startColumn": 108, +"endLine": 156, +"endColumn": 120 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\Conventions\StaticContentConventionBuilder.cs", +"region": { +"startLine": 211, +"startColumn": 60, +"endLine": 211, +"endColumn": 73 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\Conventions\StaticContentConventionBuilder.cs", +"region": { +"startLine": 221, +"startColumn": 45, +"endLine": 221, +"endColumn": 56 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\HttpMultipartBoundary.cs", +"region": { +"startLine": 76, +"startColumn": 53, +"endLine": 76, +"endColumn": 66 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\ModelBinding\DefaultBinder.cs", +"region": { +"startLine": 202, +"startColumn": 80, +"endLine": 202, +"endColumn": 101 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\ModelBinding\DefaultConverters\CollectionConverter.cs", +"region": { +"startLine": 103, +"startColumn": 81, +"endLine": 103, +"endColumn": 102 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\ModelBinding\DefaultConverters\CollectionConverter.cs", +"region": { +"startLine": 104, +"startColumn": 87, +"endLine": 104, +"endColumn": 108 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\ModelBinding\DefaultConverters\CollectionConverter.cs", +"region": { +"startLine": 126, +"startColumn": 81, +"endLine": 126, +"endColumn": 102 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\ModelBinding\DefaultConverters\CollectionConverter.cs", +"region": { +"startLine": 127, +"startColumn": 85, +"endLine": 127, +"endColumn": 106 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\ViewEngines\FileSystemViewLocationProvider.cs", +"region": { +"startLine": 120, +"startColumn": 28, +"endLine": 120, +"endColumn": 65 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\ViewEngines\FileSystemViewLocationProvider.cs", +"region": { +"startLine": 123, +"startColumn": 26, +"endLine": 123, +"endColumn": 40 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\ViewEngines\ResourceViewLocationProvider.cs", +"region": { +"startLine": 130, +"startColumn": 23, +"endLine": 130, +"endColumn": 36 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy\ViewEngines\ResourceViewLocationProvider.cs", +"region": { +"startLine": 142, +"startColumn": 30, +"endLine": 142, +"endColumn": 43 +} +} +} +] +} diff --git a/analyzers/its/expected/Nancy/Nancy.Embedded--net452-S3878.json b/analyzers/its/expected/Nancy/Nancy.Embedded--net452-S3878.json new file mode 100644 index 00000000000..3f028297926 --- /dev/null +++ b/analyzers/its/expected/Nancy/Nancy.Embedded--net452-S3878.json @@ -0,0 +1,30 @@ +{ +"issues": [ +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy.Embedded\Conventions\EmbeddedStaticContentConventionBuilder.cs", +"region": { +"startLine": 130, +"startColumn": 60, +"endLine": 130, +"endColumn": 73 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy.Embedded\Conventions\EmbeddedStaticContentConventionBuilder.cs", +"region": { +"startLine": 169, +"startColumn": 45, +"endLine": 169, +"endColumn": 58 +} +} +} +] +} diff --git a/analyzers/its/expected/Nancy/Nancy.Embedded--netstandard2.0-S3878.json b/analyzers/its/expected/Nancy/Nancy.Embedded--netstandard2.0-S3878.json new file mode 100644 index 00000000000..3f028297926 --- /dev/null +++ b/analyzers/its/expected/Nancy/Nancy.Embedded--netstandard2.0-S3878.json @@ -0,0 +1,30 @@ +{ +"issues": [ +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy.Embedded\Conventions\EmbeddedStaticContentConventionBuilder.cs", +"region": { +"startLine": 130, +"startColumn": 60, +"endLine": 130, +"endColumn": 73 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy.Embedded\Conventions\EmbeddedStaticContentConventionBuilder.cs", +"region": { +"startLine": 169, +"startColumn": 45, +"endLine": 169, +"endColumn": 58 +} +} +} +] +} diff --git a/analyzers/its/expected/Nancy/Nancy.ViewEngines.DotLiquid--net452-S3878.json b/analyzers/its/expected/Nancy/Nancy.ViewEngines.DotLiquid--net452-S3878.json new file mode 100644 index 00000000000..901b8765b55 --- /dev/null +++ b/analyzers/its/expected/Nancy/Nancy.ViewEngines.DotLiquid--net452-S3878.json @@ -0,0 +1,17 @@ +{ +"issues": [ +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\Nancy\src\Nancy.ViewEngines.DotLiquid\LiquidNancyFileSystem.cs", +"region": { +"startLine": 76, +"startColumn": 74, +"endLine": 76, +"endColumn": 96 +} +} +} +] +} diff --git a/analyzers/its/expected/akka.net/Akka.Cluster--netstandard2.0-S3878.json b/analyzers/its/expected/akka.net/Akka.Cluster--netstandard2.0-S3878.json new file mode 100644 index 00000000000..315e9fce514 --- /dev/null +++ b/analyzers/its/expected/akka.net/Akka.Cluster--netstandard2.0-S3878.json @@ -0,0 +1,69 @@ +{ +"issues": [ +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\akka.net\src\core\Akka.Cluster\AutoDown.cs", +"region": { +"startLine": 110, +"startColumn": 37, +"endLine": 110, +"endColumn": 86 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\akka.net\src\core\Akka.Cluster\ClusterHeartbeat.cs", +"region": { +"startLine": 122, +"startColumn": 38, +"endLine": 122, +"endColumn": 122 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\akka.net\src\core\Akka.Cluster\ClusterReadView.cs", +"region": { +"startLine": 173, +"startColumn": 42, +"endLine": 173, +"endColumn": 92 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\akka.net\src\core\Akka.Cluster\ClusterRemoteWatcher.cs", +"region": { +"startLine": 77, +"startColumn": 38, +"endLine": 77, +"endColumn": 79 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\akka.net\src\core\Akka.Cluster\Routing\ClusterRoutingConfig.cs", +"region": { +"startLine": 640, +"startColumn": 37, +"endLine": 644, +"endColumn": 14 +} +} +} +] +} diff --git a/analyzers/its/expected/akka.net/Akka.Cluster.Sharding--netstandard2.0-S3878.json b/analyzers/its/expected/akka.net/Akka.Cluster.Sharding--netstandard2.0-S3878.json new file mode 100644 index 00000000000..ff3ab6ef80e --- /dev/null +++ b/analyzers/its/expected/akka.net/Akka.Cluster.Sharding--netstandard2.0-S3878.json @@ -0,0 +1,17 @@ +{ +"issues": [ +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\akka.net\src\contrib\cluster\Akka.Cluster.Sharding\PersistentShardCoordinator.cs", +"region": { +"startLine": 1304, +"startColumn": 101, +"endLine": 1304, +"endColumn": 151 +} +} +} +] +} diff --git a/analyzers/its/expected/akka.net/Akka.NodeTestRunner--net5.0-S3878.json b/analyzers/its/expected/akka.net/Akka.NodeTestRunner--net5.0-S3878.json new file mode 100644 index 00000000000..5a57bf22468 --- /dev/null +++ b/analyzers/its/expected/akka.net/Akka.NodeTestRunner--net5.0-S3878.json @@ -0,0 +1,30 @@ +{ +"issues": [ +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\akka.net\src\core\Akka.NodeTestRunner\Program.cs", +"region": { +"startLine": 62, +"startColumn": 55, +"endLine": 62, +"endColumn": 68 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\akka.net\src\core\Akka.NodeTestRunner\Program.cs", +"region": { +"startLine": 164, +"startColumn": 54, +"endLine": 164, +"endColumn": 67 +} +} +} +] +} diff --git a/analyzers/its/expected/akka.net/Akka.NodeTestRunner--netcoreapp3.1-S3878.json b/analyzers/its/expected/akka.net/Akka.NodeTestRunner--netcoreapp3.1-S3878.json new file mode 100644 index 00000000000..5a57bf22468 --- /dev/null +++ b/analyzers/its/expected/akka.net/Akka.NodeTestRunner--netcoreapp3.1-S3878.json @@ -0,0 +1,30 @@ +{ +"issues": [ +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\akka.net\src\core\Akka.NodeTestRunner\Program.cs", +"region": { +"startLine": 62, +"startColumn": 55, +"endLine": 62, +"endColumn": 68 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\akka.net\src\core\Akka.NodeTestRunner\Program.cs", +"region": { +"startLine": 164, +"startColumn": 54, +"endLine": 164, +"endColumn": 67 +} +} +} +] +} diff --git a/analyzers/its/expected/akka.net/Akka.Persistence.TCK--netstandard2.0-S3878.json b/analyzers/its/expected/akka.net/Akka.Persistence.TCK--netstandard2.0-S3878.json new file mode 100644 index 00000000000..fb92c4e122c --- /dev/null +++ b/analyzers/its/expected/akka.net/Akka.Persistence.TCK--netstandard2.0-S3878.json @@ -0,0 +1,30 @@ +{ +"issues": [ +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\akka.net\src\core\Akka.Persistence.TCK\Performance\JournalPerfSpec.cs", +"region": { +"startLine": 299, +"startColumn": 29, +"endLine": 299, +"endColumn": 49 +} +} +}, +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\akka.net\src\core\Akka.Persistence.TCK\Performance\JournalPerfSpec.cs", +"region": { +"startLine": 337, +"startColumn": 30, +"endLine": 337, +"endColumn": 62 +} +} +} +] +} diff --git a/analyzers/its/expected/akka.net/RemotePingPong--net471-S3878.json b/analyzers/its/expected/akka.net/RemotePingPong--net471-S3878.json new file mode 100644 index 00000000000..f3e310780f6 --- /dev/null +++ b/analyzers/its/expected/akka.net/RemotePingPong--net471-S3878.json @@ -0,0 +1,17 @@ +{ +"issues": [ +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\akka.net\src\benchmark\RemotePingPong\Program.cs", +"region": { +"startLine": 202, +"startColumn": 32, +"endLine": 202, +"endColumn": 82 +} +} +} +] +} diff --git a/analyzers/its/expected/akka.net/RemotePingPong--net5.0-S3878.json b/analyzers/its/expected/akka.net/RemotePingPong--net5.0-S3878.json new file mode 100644 index 00000000000..f3e310780f6 --- /dev/null +++ b/analyzers/its/expected/akka.net/RemotePingPong--net5.0-S3878.json @@ -0,0 +1,17 @@ +{ +"issues": [ +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\akka.net\src\benchmark\RemotePingPong\Program.cs", +"region": { +"startLine": 202, +"startColumn": 32, +"endLine": 202, +"endColumn": 82 +} +} +} +] +} diff --git a/analyzers/its/expected/akka.net/RemotePingPong--netcoreapp3.1-S3878.json b/analyzers/its/expected/akka.net/RemotePingPong--netcoreapp3.1-S3878.json new file mode 100644 index 00000000000..f3e310780f6 --- /dev/null +++ b/analyzers/its/expected/akka.net/RemotePingPong--netcoreapp3.1-S3878.json @@ -0,0 +1,17 @@ +{ +"issues": [ +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\akka.net\src\benchmark\RemotePingPong\Program.cs", +"region": { +"startLine": 202, +"startColumn": 32, +"endLine": 202, +"endColumn": 82 +} +} +} +] +} diff --git a/analyzers/its/expected/akka.net/Samples.Cluster.Simple--net471-S3878.json b/analyzers/its/expected/akka.net/Samples.Cluster.Simple--net471-S3878.json new file mode 100644 index 00000000000..e57b7f089cd --- /dev/null +++ b/analyzers/its/expected/akka.net/Samples.Cluster.Simple--net471-S3878.json @@ -0,0 +1,17 @@ +{ +"issues": [ +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\akka.net\src\examples\Cluster\Samples.Cluster.Simple\SimpleClusterListener.cs", +"region": { +"startLine": 24, +"startColumn": 72, +"endLine": 24, +"endColumn": 155 +} +} +} +] +} diff --git a/analyzers/its/expected/akka.net/Samples.Cluster.Transformation--net471-S3878.json b/analyzers/its/expected/akka.net/Samples.Cluster.Transformation--net471-S3878.json new file mode 100644 index 00000000000..63a83ede154 --- /dev/null +++ b/analyzers/its/expected/akka.net/Samples.Cluster.Transformation--net471-S3878.json @@ -0,0 +1,17 @@ +{ +"issues": [ +{ +"id": "S3878", +"message": "Remove this array creation and simply pass the elements.", +"location": { +"uri": "sources\akka.net\src\examples\Cluster\Roles\Samples.Cluster.Transformation\TransformationBackend.cs", +"region": { +"startLine": 22, +"startColumn": 37, +"endLine": 22, +"endColumn": 76 +} +} +} +] +} From d5ce51b904e649abbc87a1cf1256a4ac4f622406 Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Wed, 1 Feb 2023 09:31:49 +0100 Subject: [PATCH 39/40] Stylistic fix + Noncompliant CS empty array --- .../SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs | 8 ++++---- .../Rules/ArrayPassedAsParams.cs | 10 +++++----- .../Rules/ArrayPassedAsParamsTest.cs | 6 +++--- .../TestCases/ArrayPassedAsParams.cs | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs index a689ff721ce..4b0d60f48f9 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs @@ -47,15 +47,15 @@ expression switch private static ArgumentSyntax GetLastArgumentIfArrayCreation(BaseArgumentListSyntax argumentList) => argumentList is { Arguments: { Count: > 0 } arguments } - && IsArrayCreation(arguments.Last().Expression) - ? arguments.Last() + && arguments.Last() is var lastArgument + && IsArrayCreation(lastArgument.Expression) + ? lastArgument : null; private static bool IsArrayCreation(ExpressionSyntax expression) => expression switch { - ArrayCreationExpressionSyntax { } arrayCreation => - arrayCreation.Initializer is InitializerExpressionSyntax { Expressions.Count: > 0 }, + ArrayCreationExpressionSyntax { } arrayCreation => arrayCreation.Initializer is not null, ImplicitArrayCreationExpressionSyntax { } => true, _ => false }; diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs index ad182d0a1cf..fe436cec925 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs @@ -44,9 +44,9 @@ expression switch private static ArgumentSyntax GetLastArgumentIfArrayCreation(ArgumentListSyntax argumentList) => argumentList is { Arguments: { Count: > 0 } arguments } - && arguments.Last() is var argument - && IsArrayCreation(argument.GetExpression()) - ? argument + && arguments.Last() is var lastArgument + && IsArrayCreation(lastArgument.GetExpression()) + ? lastArgument : null; private static bool IsArrayCreation(ExpressionSyntax expression) => @@ -54,8 +54,8 @@ expression switch { ArrayCreationExpressionSyntax { } arrayCreation => arrayCreation.Initializer is CollectionInitializerSyntax { Initializers.Count: > 0 } - || arrayCreation.ArrayBounds is not ArgumentListSyntax { }, - CollectionInitializerSyntax { } => true, + || arrayCreation.ArrayBounds is null, + CollectionInitializerSyntax => true, _ => false }; } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/ArrayPassedAsParamsTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/ArrayPassedAsParamsTest.cs index 5e5fb174a23..fd615a44737 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/ArrayPassedAsParamsTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/ArrayPassedAsParamsTest.cs @@ -49,11 +49,11 @@ public class ArrayPassedAsParamsTest [DataTestMethod] [DataRow("{ }", false)] - [DataRow("{ \"s\", \"s\", \"s\", \"s\" }", false)] + [DataRow("""{ "s", "s", "s", "s" }""", false)] [DataRow("New String(2) { }", true)] - [DataRow("New String(2) { \"s\", \"s\", \"s\" }", false)] + [DataRow("""New String(2) { "s", "s", "s" }""", false)] [DataRow("New String() { }", false)] - [DataRow("New String() { \"s\" }", false)] + [DataRow("""New String() { "s" }""", false)] public void ArrayPassedAsParams_VBCollectionInitializerSyntaxTests(string arrayCreation, bool compliant) { var code = $$""" diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs index 0eb09764748..b2516243853 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ArrayPassedAsParams.cs @@ -8,7 +8,7 @@ public void Base(string[] myArray) // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Method(new string[] { "s1" }); // Noncompliant Method(new[] { "s1" }); // Noncompliant - Method(new string[] { }); // Compliant + Method(new string[] { }); // Noncompliant Method("s1"); // Compliant Method("s1", "s2"); // Compliant Method(myArray); // Compliant From 500870ce8f703f64c5f8a1577af24adafd9a13ec Mon Sep 17 00:00:00 2001 From: Cristian Ambrosini Date: Wed, 1 Feb 2023 15:10:05 +0100 Subject: [PATCH 40/40] Addressed PR comments --- .../Rules/ArrayPassedAsParams.cs | 9 +++------ .../Rules/ArrayPassedAsParams.cs | 12 ++++-------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs index 4b0d60f48f9..abe7f01f536 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs @@ -53,10 +53,7 @@ expression switch : null; private static bool IsArrayCreation(ExpressionSyntax expression) => - expression switch - { - ArrayCreationExpressionSyntax { } arrayCreation => arrayCreation.Initializer is not null, - ImplicitArrayCreationExpressionSyntax { } => true, - _ => false - }; + expression + is ArrayCreationExpressionSyntax { Initializer: not null } + or ImplicitArrayCreationExpressionSyntax; } diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs index fe436cec925..634efcb7fae 100644 --- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs +++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ArrayPassedAsParams.cs @@ -50,12 +50,8 @@ expression switch : null; private static bool IsArrayCreation(ExpressionSyntax expression) => - expression switch - { - ArrayCreationExpressionSyntax { } arrayCreation => - arrayCreation.Initializer is CollectionInitializerSyntax { Initializers.Count: > 0 } - || arrayCreation.ArrayBounds is null, - CollectionInitializerSyntax => true, - _ => false - }; + expression + is ArrayCreationExpressionSyntax { Initializer.Initializers.Count: > 0 } + or ArrayCreationExpressionSyntax { ArrayBounds: null } + or CollectionInitializerSyntax; }