19
19
*/
20
20
package org .sonar .python .checks ;
21
21
22
+ import java .util .Collections ;
23
+ import java .util .HashSet ;
24
+ import java .util .List ;
22
25
import java .util .Optional ;
26
+ import java .util .Set ;
27
+ import java .util .SortedSet ;
28
+ import java .util .TreeSet ;
23
29
import java .util .function .Predicate ;
24
30
import org .sonar .check .Rule ;
25
31
import org .sonar .plugins .python .api .LocationInFile ;
26
32
import org .sonar .plugins .python .api .PythonSubscriptionCheck ;
27
- import org .sonar .plugins .python .api .SubscriptionContext ;
28
33
import org .sonar .plugins .python .api .symbols .FunctionSymbol ;
29
34
import org .sonar .plugins .python .api .symbols .Symbol ;
30
35
import org .sonar .plugins .python .api .tree .Argument ;
43
48
@ Rule (key = "S5655" )
44
49
public class ArgumentTypeCheck extends PythonSubscriptionCheck {
45
50
51
+ private static class IssueToReport {
52
+ SortedSet <Integer > nonCompliantArgs = new TreeSet <>();
53
+ String message ;
54
+ final Set <LocationInFile > secondaryLocations = new HashSet <>();
55
+ }
56
+
46
57
@ Override
47
58
public void initialize (Context context ) {
48
59
context .registerSyntaxNodeConsumer (Tree .Kind .CALL_EXPR , ctx -> {
@@ -51,46 +62,57 @@ public void initialize(Context context) {
51
62
if (calleeSymbol == null ) {
52
63
return ;
53
64
}
54
- if (!calleeSymbol .is (Symbol .Kind .FUNCTION )) {
55
- // We might want to support ambiguous symbols for which every definition is a function
56
- return ;
57
- }
58
- FunctionSymbol functionSymbol = (FunctionSymbol ) calleeSymbol ;
59
- if (functionSymbol .hasVariadicParameter ()) {
60
- return ;
65
+ Set <Symbol > symbols = SymbolUtils .flattenAmbiguousSymbols (Collections .singleton (calleeSymbol ));
66
+ if (symbols .stream ().anyMatch (s -> !s .is (Symbol .Kind .FUNCTION ))) return ;
67
+ IssueToReport issue = new IssueToReport ();
68
+ if (symbols .stream ().allMatch (symbol -> isNonCompliantFunctionCall (issue , ((FunctionSymbol ) symbol ), callExpression ))) {
69
+ List <Argument > args = callExpression .arguments ();
70
+ Integer firstArgIndex = issue .nonCompliantArgs .first ();
71
+ PreciseIssue preciseIssue = ctx .addIssue (args .get (firstArgIndex ), issue .message );
72
+ issue .nonCompliantArgs .forEach (index -> { if (!index .equals (firstArgIndex )) preciseIssue .secondary (args .get (index ), issue .message ); });
73
+ issue .secondaryLocations .forEach (locationInFile -> preciseIssue .secondary (locationInFile , "Function definition" ));
61
74
}
62
- boolean isStaticCall = callExpression .callee ().is (Tree .Kind .NAME ) || Optional .of (callExpression .callee ())
63
- .filter (c -> c .is (Tree .Kind .QUALIFIED_EXPR ))
64
- .flatMap (q -> TreeUtils .getSymbolFromTree (((QualifiedExpression ) q ).qualifier ()).filter (s -> s .is (Symbol .Kind .CLASS )))
65
- .isPresent ();
66
- checkFunctionCall (ctx , callExpression , functionSymbol , isStaticCall );
67
75
});
68
76
}
69
77
70
- private static void checkFunctionCall (SubscriptionContext ctx , CallExpression callExpression , FunctionSymbol functionSymbol , boolean isStaticCall ) {
78
+ private static boolean isNonCompliantFunctionCall (IssueToReport issue , FunctionSymbol functionSymbol , CallExpression callExpression ) {
79
+ if (functionSymbol .hasVariadicParameter ()) {
80
+ return false ;
81
+ }
82
+ boolean isStaticCall = callExpression .callee ().is (Tree .Kind .NAME ) || Optional .of (callExpression .callee ())
83
+ .filter (c -> c .is (Tree .Kind .QUALIFIED_EXPR ))
84
+ .flatMap (q -> TreeUtils .getSymbolFromTree (((QualifiedExpression ) q ).qualifier ()).filter (s -> s .is (Symbol .Kind .CLASS )))
85
+ .isPresent ();
86
+ return checkFunctionCall (issue , callExpression , functionSymbol , isStaticCall );
87
+ }
88
+
89
+ private static boolean checkFunctionCall (IssueToReport issue , CallExpression callExpression , FunctionSymbol functionSymbol , boolean isStaticCall ) {
71
90
72
91
boolean isKeyword = false ;
73
92
int firstParameterOffset = SymbolUtils .firstParameterOffset (functionSymbol , isStaticCall );
74
93
if (firstParameterOffset < 0 ) {
75
- return ;
94
+ return false ;
76
95
}
96
+ boolean hasIncompatibleArgumentType = false ;
77
97
for (int i = 0 ; i < callExpression .arguments ().size (); i ++) {
78
98
Argument argument = callExpression .arguments ().get (i );
79
99
int parameterIndex = i + firstParameterOffset ;
80
100
if (parameterIndex >= functionSymbol .parameters ().size ()) {
81
101
// S930 will raise the issue
82
- return ;
102
+ return false ;
83
103
}
84
104
if (argument .is (Tree .Kind .REGULAR_ARGUMENT )) {
85
105
RegularArgument regularArgument = (RegularArgument ) argument ;
86
106
isKeyword |= regularArgument .keywordArgument () != null ;
87
107
boolean shouldReport = isKeyword ? shouldReportKeywordArgument (regularArgument , functionSymbol )
88
108
: shouldReportPositionalArgument (regularArgument , functionSymbol , parameterIndex );
89
109
if (shouldReport ) {
90
- reportIssue (ctx , functionSymbol , regularArgument );
110
+ hasIncompatibleArgumentType = true ;
111
+ reportIssue (issue , functionSymbol , i );
91
112
}
92
113
}
93
114
}
115
+ return hasIncompatibleArgumentType ;
94
116
}
95
117
96
118
private static boolean shouldReportPositionalArgument (RegularArgument regularArgument , FunctionSymbol functionSymbol , int index ) {
@@ -122,11 +144,11 @@ private static boolean shouldReportKeywordArgument(RegularArgument regularArgume
122
144
.orElse (false );
123
145
}
124
146
125
- private static void reportIssue (SubscriptionContext ctx , FunctionSymbol functionSymbol , RegularArgument regularArgument ) {
126
- PreciseIssue issue = ctx . addIssue ( regularArgument , String . format ( "Change this argument; Function \" %s \" expects a different type" , functionSymbol . name ()) );
127
- LocationInFile locationInFile = functionSymbol .definitionLocation ( );
128
- if (locationInFile != null ) {
129
- issue .secondary ( locationInFile , "Function definition" );
147
+ private static void reportIssue (IssueToReport issue , FunctionSymbol functionSymbol , int argumentIndex ) {
148
+ issue . nonCompliantArgs . add ( argumentIndex );
149
+ issue . message = String . format ( "Change this argument; Function \" %s \" expects a different type" , functionSymbol .name () );
150
+ if (functionSymbol . definitionLocation () != null ) {
151
+ issue .secondaryLocations . add ( functionSymbol . definitionLocation () );
130
152
}
131
153
}
132
154
0 commit comments