Skip to content

Commit 6101985

Browse files
mary-georgiou-sonarsourcesonartech
authored andcommitted
NET-1254 Fix S3878 FN: When params are passed as array throught an attribute
1 parent b60532e commit 6101985

File tree

3 files changed

+65
-16
lines changed

3 files changed

+65
-16
lines changed

analyzers/src/SonarAnalyzer.CSharp/Rules/ArrayPassedAsParams.cs

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,34 @@
1717
namespace SonarAnalyzer.CSharp.Rules;
1818

1919
[DiagnosticAnalyzer(LanguageNames.CSharp)]
20-
public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase<SyntaxKind, ArgumentSyntax>
20+
public sealed class ArrayPassedAsParams : ArrayPassedAsParamsBase<SyntaxKind, CSharpSyntaxNode>
2121
{
2222
protected override ILanguageFacade<SyntaxKind> Language => CSharpFacade.Instance;
2323

2424
protected override SyntaxKind[] ExpressionKinds { get; } =
2525
[
2626
SyntaxKind.ObjectCreationExpression,
2727
SyntaxKind.InvocationExpression,
28-
SyntaxKindEx.ImplicitObjectCreationExpression
28+
SyntaxKindEx.ImplicitObjectCreationExpression,
29+
SyntaxKind.Attribute
2930
];
3031

31-
protected override ArgumentSyntax LastArgumentIfArrayCreation(SyntaxNode expression) =>
32-
ArgumentList(expression) is { Arguments: { Count: > 0 } arguments }
33-
&& arguments.Last() is var lastArgument
34-
&& IsArrayCreation(lastArgument.Expression)
32+
protected override CSharpSyntaxNode LastArgumentIfArrayCreation(SyntaxNode expression)
33+
{
34+
CSharpSyntaxNode lastArgument = expression switch
35+
{
36+
AttributeSyntax { ArgumentList.Arguments: { Count: > 0 } args } => args.Last(),
37+
_ when ArgumentList(expression) is { Arguments: { Count: > 0 } args } => args.Last(),
38+
_ => null
39+
};
40+
41+
return IsArrayCreation(lastArgument)
3542
? lastArgument
3643
: null;
44+
}
3745

38-
protected override ITypeSymbol ArrayElementType(ArgumentSyntax argument, SemanticModel model) =>
39-
argument.Expression switch
46+
protected override ITypeSymbol ArrayElementType(CSharpSyntaxNode argument, SemanticModel model) =>
47+
ArgumentExpression(argument) switch
4048
{
4149
ArrayCreationExpressionSyntax arrayCreation => model.GetTypeInfo(arrayCreation.Type.ElementType).Type,
4250
ImplicitArrayCreationExpressionSyntax implicitArrayCreation => (model.GetTypeInfo(implicitArrayCreation).Type as IArrayTypeSymbol)?.ElementType,
@@ -53,16 +61,27 @@ _ when ImplicitObjectCreationExpressionSyntaxWrapper.IsInstance(expression) =>
5361
_ => null
5462
};
5563

56-
private static bool IsArrayCreation(ExpressionSyntax expression) =>
57-
expression switch
64+
private static bool IsArrayCreation(CSharpSyntaxNode argument)
65+
{
66+
var expression = ArgumentExpression(argument);
67+
return expression switch
5868
{
5969
ArrayCreationExpressionSyntax { Initializer: not null } => true,
6070
ImplicitArrayCreationExpressionSyntax => true,
6171
_ when CollectionExpressionSyntaxWrapper.IsInstance(expression) => !ContainsSpread((CollectionExpressionSyntaxWrapper)expression),
6272
_ => false
6373
};
74+
}
6475

6576
// [x, y, ..z] is not possible to be passed as params
6677
private static bool ContainsSpread(CollectionExpressionSyntaxWrapper expression) =>
6778
expression.Elements.Any(x => x.SyntaxNode.IsKind(SyntaxKindEx.SpreadElement));
79+
80+
private static ExpressionSyntax ArgumentExpression(CSharpSyntaxNode node) =>
81+
node switch
82+
{
83+
ArgumentSyntax arg => arg.Expression,
84+
AttributeArgumentSyntax arg => arg.Expression,
85+
_ => null
86+
};
6887
}

analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.Latest.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,22 @@ public struct MyImmutableArray<T> : IEnumerable<T>
7676
IEnumerator<T> IEnumerable<T>.GetEnumerator() => default!;
7777
IEnumerator IEnumerable.GetEnumerator() => default!;
7878
}
79+
80+
// Reproducer for https://github.com/SonarSource/sonar-dotnet/issues/6977
81+
public class Repro6977_CollectionExpression
82+
{
83+
class ParamsAttribute : Attribute
84+
{
85+
public ParamsAttribute(params string[] values) { }
86+
public ParamsAttribute(int a, string b, params string[] values) { }
87+
}
88+
89+
internal enum Foo
90+
{
91+
[Params(["1", "2" ])] // Noncompliant
92+
Red,
93+
94+
[Params("1", "2")]
95+
Yellow
96+
}
97+
}

analyzers/tests/SonarAnalyzer.Test/TestCases/ArrayPassedAsParams.cs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ public void Base(string[] myArray)
2929
Method3(new string[] { "s1", "s2" }, new string[] { "s1", "s2" }); // Noncompliant
3030
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
3131
Method3(null, null); // Compliant
32-
Method4(new [] { "s1" }); // Compliant
33-
Method4(new [] { "s1", "s2" }); // Compliant
32+
Method4(new[] { "s1" }); // Compliant
33+
Method4(new[] { "s1", "s2" }); // Compliant
3434

3535
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)
3636
Method3(args: new string[12], a: new string[] { "s1", "s2" }); // Compliant
@@ -92,7 +92,7 @@ public void CallMethod(dynamic d)
9292
Method(new object[] { new int[] { 1, 2 } }); // FN, elements in args: [System.Int32[]]
9393
Method(new int[] { 1, 2, 3, }); // Compliant, Elements in args: [System.Int32[]]
9494
Method(new String[] { "1", "2" }, new String[] { "1", "2" }); // Compliant, elements in args: [System.String[], System.String[]]
95-
Method(new String[] { "1", "2"}, new int[] { 1, 2}); // Compliant, elements in args: [System.String[], System.Int32[]]
95+
Method(new String[] { "1", "2" }, new int[] { 1, 2 }); // Compliant, elements in args: [System.String[], System.Int32[]]
9696
MethodMixed(1, new String[] { "1", "2" }); // Noncompliant
9797
MethodArray(new String[] { "1", "2" }, new String[] { "1", "2" }); // Compliant, elements in args: [System.String[], System.String[]]
9898
MethodArray(new int[] { 1, 2 }, new int[] { 1, 2 }); // Compliant, elements in args: [System.Int32[], System.Int32[]]
@@ -127,14 +127,25 @@ public class Repro6977
127127
class ParamsAttribute : Attribute
128128
{
129129
public ParamsAttribute(params string[] values) { }
130+
public ParamsAttribute(int a, string b, params string[] values) { }
131+
public ParamsAttribute() { }
130132
}
131133

132134
internal enum Foo
133135
{
134-
[Params(new[] { "1", "2" })] // FN
135-
Bar,
136+
[Params(new[] { "1", "2" })] // Noncompliant
137+
Red,
136138

137139
[Params("1", "2")]
138-
FooBar
140+
Yellow,
141+
142+
[Params(a: 1, b: "hello", values : new[] { "1", "2" })] // Noncompliant
143+
Blue,
144+
145+
[Params(a: 1, values: new[] { "1", "2" }, b: "hello")] // FN
146+
Green,
147+
148+
[Params]
149+
Violet
139150
}
140151
}

0 commit comments

Comments
 (0)