Skip to content

Commit 4760547

Browse files
Add Argument tracker and tests
1 parent 5631cb6 commit 4760547

File tree

19 files changed

+1817
-9
lines changed

19 files changed

+1817
-9
lines changed

analyzers/src/SonarAnalyzer.CSharp/Extensions/SyntaxNodeExtensions.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ public static SyntaxNode WalkUpParentheses(this SyntaxNode node)
199199
PointerTypeSyntax { ElementType: { } elementType } => GetIdentifier(elementType),
200200
PredefinedTypeSyntax { Keyword: var keyword } => keyword,
201201
QualifiedNameSyntax { Right.Identifier: var identifier } => identifier,
202+
SimpleBaseTypeSyntax { Type: { } type } => GetIdentifier(type),
202203
SimpleNameSyntax { Identifier: var identifier } => identifier,
203204
TypeParameterConstraintClauseSyntax { Name.Identifier: var identifier } => identifier,
204205
TypeParameterSyntax { Identifier: var identifier } => identifier,
@@ -363,13 +364,15 @@ static bool TakesExpressionTree(SymbolInfo info)
363364
}
364365
}
365366

366-
// based on Type="ArgumentListSyntax" in https://github.com/dotnet/roslyn/blob/main/src/Compilers/CSharp/Portable/Syntax/Syntax.xml
367-
public static ArgumentListSyntax ArgumentList(this SyntaxNode node) =>
367+
// based on Type="BaseArgumentListSyntax " in https://github.com/dotnet/roslyn/blob/main/src/Compilers/CSharp/Portable/Syntax/Syntax.xml
368+
public static BaseArgumentListSyntax ArgumentList(this SyntaxNode node) =>
368369
node switch
369370
{
370371
ObjectCreationExpressionSyntax creation => creation.ArgumentList,
371372
InvocationExpressionSyntax invocation => invocation.ArgumentList,
372373
ConstructorInitializerSyntax constructorInitializer => constructorInitializer.ArgumentList,
374+
ElementAccessExpressionSyntax x => x.ArgumentList,
375+
ElementBindingExpressionSyntax x => x.ArgumentList,
373376
null => null,
374377
_ when PrimaryConstructorBaseTypeSyntaxWrapper.IsInstance(node) => ((PrimaryConstructorBaseTypeSyntaxWrapper)node).ArgumentList,
375378
_ when ImplicitObjectCreationExpressionSyntaxWrapper.IsInstance(node) => ((ImplicitObjectCreationExpressionSyntaxWrapper)node).ArgumentList,

analyzers/src/SonarAnalyzer.CSharp/Facade/CSharpTrackerFacade.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ namespace SonarAnalyzer.Helpers.Facade
2424
{
2525
internal sealed class CSharpTrackerFacade : ITrackerFacade<SyntaxKind>
2626
{
27+
public ArgumentTracker<SyntaxKind> Argument => new CSharpArgumentTracker();
2728
public BaseTypeTracker<SyntaxKind> BaseType { get; } = new CSharpBaseTypeTracker();
2829
public ElementAccessTracker<SyntaxKind> ElementAccess { get; } = new CSharpElementAccessTracker();
2930
public FieldAccessTracker<SyntaxKind> FieldAccess { get; } = new CSharpFieldAccessTracker();

analyzers/src/SonarAnalyzer.CSharp/Helpers/CSharpAttributeParameterLookup.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,7 @@ protected override SyntaxNode Expression(AttributeArgumentSyntax argument) =>
3030

3131
protected override SyntaxToken? GetNameColonArgumentIdentifier(AttributeArgumentSyntax argument) =>
3232
argument.NameColon?.Name.Identifier;
33+
34+
protected override SyntaxToken? GetNameEqualsArgumentIdentifier(AttributeArgumentSyntax argument) =>
35+
argument.NameEquals?.Name.Identifier;
3336
}

analyzers/src/SonarAnalyzer.CSharp/Helpers/CSharpMethodParameterLookup.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,18 @@ public CSharpMethodParameterLookup(InvocationExpressionSyntax invocation, Semant
2828
public CSharpMethodParameterLookup(InvocationExpressionSyntax invocation, IMethodSymbol methodSymbol)
2929
: this(invocation.ArgumentList, methodSymbol) { }
3030

31-
public CSharpMethodParameterLookup(ArgumentListSyntax argumentList, SemanticModel semanticModel)
31+
public CSharpMethodParameterLookup(BaseArgumentListSyntax argumentList, SemanticModel semanticModel)
3232
: base(argumentList.Arguments, semanticModel.GetSymbolInfo(argumentList.Parent)) { }
3333

34-
public CSharpMethodParameterLookup(ArgumentListSyntax argumentList, IMethodSymbol methodSymbol)
34+
public CSharpMethodParameterLookup(BaseArgumentListSyntax argumentList, IMethodSymbol methodSymbol)
3535
: base(argumentList.Arguments, methodSymbol) { }
3636

3737
protected override SyntaxNode Expression(ArgumentSyntax argument) =>
3838
argument.Expression;
3939

4040
protected override SyntaxToken? GetNameColonArgumentIdentifier(ArgumentSyntax argument) =>
4141
argument.NameColon?.Name.Identifier;
42+
43+
protected override SyntaxToken? GetNameEqualsArgumentIdentifier(ArgumentSyntax argument) =>
44+
null;
4245
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* SonarAnalyzer for .NET
3+
* Copyright (C) 2015-2023 SonarSource SA
4+
* mailto: contact AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
21+
namespace SonarAnalyzer.Helpers.Trackers;
22+
23+
internal sealed class CSharpArgumentTracker : ArgumentTracker<SyntaxKind>
24+
{
25+
protected override SyntaxKind[] TrackedSyntaxKinds =>
26+
[
27+
SyntaxKind.AttributeArgument,
28+
SyntaxKind.Argument,
29+
];
30+
31+
protected override ILanguageFacade<SyntaxKind> Language => CSharpFacade.Instance;
32+
33+
protected override IReadOnlyCollection<SyntaxNode> ArgumentList(SyntaxNode argumentNode) =>
34+
argumentNode switch
35+
{
36+
AttributeArgumentSyntax { Parent: AttributeArgumentListSyntax { Arguments: { } list } } => list,
37+
ArgumentSyntax { Parent: BaseArgumentListSyntax { Arguments: { } list } } => list,
38+
_ => null,
39+
};
40+
41+
protected override int? Position(SyntaxNode argumentNode) =>
42+
argumentNode is ArgumentSyntax { NameColon: not null } or AttributeArgumentSyntax { NameColon: not null } or AttributeArgumentSyntax { NameEquals: not null }
43+
? null
44+
: ArgumentList(argumentNode).IndexOf(x => x == argumentNode);
45+
46+
protected override RefKind? ArgumentRefKind(SyntaxNode argumentNode) =>
47+
argumentNode switch
48+
{
49+
AttributeArgumentSyntax => null,
50+
ArgumentSyntax { RefOrOutKeyword: { } refOrOut } => refOrOut.Kind() switch { SyntaxKind.OutKeyword => RefKind.Out, SyntaxKind.RefKeyword => RefKind.Ref, _ => RefKind.None },
51+
_ => null,
52+
};
53+
54+
protected override bool InvocationFitsMemberKind(SyntaxNode invokedExpression, InvokedMemberKind memberKind) =>
55+
memberKind switch
56+
{
57+
InvokedMemberKind.Method => invokedExpression is InvocationExpressionSyntax,
58+
InvokedMemberKind.Constructor => invokedExpression is ObjectCreationExpressionSyntax
59+
or ConstructorInitializerSyntax
60+
|| ImplicitObjectCreationExpressionSyntaxWrapper.IsInstance(invokedExpression),
61+
InvokedMemberKind.Indexer => invokedExpression is ElementAccessExpressionSyntax or ElementBindingExpressionSyntax,
62+
InvokedMemberKind.Attribute => invokedExpression is AttributeSyntax,
63+
_ => false,
64+
};
65+
66+
protected override bool InvokedMemberFits(SemanticModel model, SyntaxNode invokedExpression, InvokedMemberKind memberKind, Func<string, bool> invokedMemberNameConstraint) =>
67+
memberKind switch
68+
{
69+
InvokedMemberKind.Method => invokedMemberNameConstraint(invokedExpression.GetName()),
70+
InvokedMemberKind.Constructor => invokedExpression switch
71+
{
72+
ObjectCreationExpressionSyntax { Type: { } typeName } => invokedMemberNameConstraint(typeName.GetName()),
73+
ConstructorInitializerSyntax x => FindClassNameFromConstructorInitializerSyntax(x) is not string name || invokedMemberNameConstraint(name),
74+
{ } x when ImplicitObjectCreationExpressionSyntaxWrapper.IsInstance(x) => invokedMemberNameConstraint(model.GetSymbolInfo(x).Symbol?.ContainingType?.Name),
75+
_ => false,
76+
},
77+
InvokedMemberKind.Indexer => invokedExpression switch
78+
{
79+
ElementAccessExpressionSyntax { Expression: { } accessedExpression } => invokedMemberNameConstraint(accessedExpression.GetName()),
80+
ElementBindingExpressionSyntax { } binding => binding.GetParentConditionalAccessExpression() is
81+
{ Expression: { } accessedExpression } && invokedMemberNameConstraint(accessedExpression.GetName()),
82+
_ => false,
83+
},
84+
InvokedMemberKind.Attribute => invokedExpression is AttributeSyntax { Name: { } typeName } && invokedMemberNameConstraint(typeName.GetName()),
85+
_ => false,
86+
};
87+
88+
private string FindClassNameFromConstructorInitializerSyntax(ConstructorInitializerSyntax initializerSyntax) =>
89+
initializerSyntax.ThisOrBaseKeyword.Kind() switch
90+
{
91+
SyntaxKind.ThisKeyword => initializerSyntax is { Parent: ConstructorDeclarationSyntax { Identifier.ValueText: { } typeName } } ? typeName : null,
92+
SyntaxKind.BaseKeyword => initializerSyntax is { Parent: ConstructorDeclarationSyntax { Parent: BaseTypeDeclarationSyntax { BaseList.Types: { Count: > 0 } baseListTypes } } }
93+
? baseListTypes.First().GetName() // Get the class name of the called constructor from the base types list of the type declaration
94+
: null,
95+
_ => null,
96+
};
97+
}

analyzers/src/SonarAnalyzer.Common/Facade/ITrackerFacade.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ namespace SonarAnalyzer.Helpers.Facade
2525
public interface ITrackerFacade<TSyntaxKind>
2626
where TSyntaxKind : struct
2727
{
28+
ArgumentTracker<TSyntaxKind> Argument { get; }
2829
BaseTypeTracker<TSyntaxKind> BaseType { get; }
2930
ElementAccessTracker<TSyntaxKind> ElementAccess { get; }
3031
FieldAccessTracker<TSyntaxKind> FieldAccess { get; }

0 commit comments

Comments
 (0)