16
16
*/
17
17
package org .sonar .python .checks ;
18
18
19
+ import java .util .Collection ;
19
20
import java .util .Map ;
21
+ import java .util .Optional ;
20
22
import javax .annotation .Nullable ;
21
23
import org .sonar .check .Rule ;
22
24
import org .sonar .plugins .python .api .LocationInFile ;
23
25
import org .sonar .plugins .python .api .symbols .ClassSymbol ;
24
26
import org .sonar .plugins .python .api .symbols .FunctionSymbol ;
25
27
import org .sonar .plugins .python .api .symbols .Symbol ;
28
+ import org .sonar .plugins .python .api .tree .ArgList ;
26
29
import org .sonar .plugins .python .api .tree .CallExpression ;
30
+ import org .sonar .plugins .python .api .tree .ClassDef ;
27
31
import org .sonar .plugins .python .api .tree .Expression ;
28
- import org .sonar .plugins .python .api .tree .HasSymbol ;
32
+ import org .sonar .plugins .python .api .tree .RegularArgument ;
33
+ import org .sonar .plugins .python .api .tree .SubscriptionExpression ;
29
34
import org .sonar .plugins .python .api .tree .Tree ;
30
35
import org .sonar .plugins .python .api .types .InferredType ;
36
+ import org .sonar .python .tree .TreeUtils ;
31
37
import org .sonar .python .types .InferredTypes ;
32
38
33
39
import static org .sonar .plugins .python .api .symbols .Symbol .Kind .CLASS ;
@@ -39,37 +45,62 @@ public class ItemOperationsTypeCheck extends ItemOperationsType {
39
45
40
46
@ Override
41
47
public boolean isValidSubscription (Expression subscriptionObject , String requiredMethod , @ Nullable String classRequiredMethod ,
42
- Map <LocationInFile , String > secondaries ) {
48
+ Map <LocationInFile , String > secondaries ) {
43
49
44
50
if (subscriptionObject .is (Tree .Kind .GENERATOR_EXPR )) {
45
51
return false ;
46
52
}
47
- if (subscriptionObject .is (Tree .Kind .CALL_EXPR )) {
48
- Symbol subscriptionCalleeSymbol = ((CallExpression ) subscriptionObject ).calleeSymbol ();
49
- if (subscriptionCalleeSymbol != null && subscriptionCalleeSymbol .is (FUNCTION ) && ((FunctionSymbol ) subscriptionCalleeSymbol ).isAsynchronous ()) {
50
- FunctionSymbol functionSymbol = (FunctionSymbol ) subscriptionCalleeSymbol ;
51
- secondaries .put (functionSymbol .definitionLocation (), String .format (SECONDARY_MESSAGE , functionSymbol .name ()));
52
- return false ;
53
- }
53
+ if (isInvalidSubscriptionCallExpr (subscriptionObject , secondaries )) {
54
+ return false ;
54
55
}
55
- if (subscriptionObject instanceof HasSymbol hasSymbol ) {
56
- Symbol symbol = hasSymbol .symbol ();
57
- if (symbol == null || isTypingOrCollectionsSymbol (symbol )) {
56
+
57
+ var symbolOptional = TreeUtils .getSymbolFromTree (subscriptionObject );
58
+
59
+ if (symbolOptional .isPresent ()) {
60
+ var symbol = symbolOptional .get ();
61
+ if (isTypingOrCollectionsSymbol (symbol )) {
58
62
return true ;
59
63
}
60
64
if (symbol .is (FUNCTION , CLASS )) {
61
- secondaries .put (symbol .is (FUNCTION ) ?
62
- ((FunctionSymbol ) symbol ).definitionLocation () : ((ClassSymbol ) symbol ).definitionLocation (), String .format (SECONDARY_MESSAGE , symbol .name ()));
63
- return canHaveMethod (symbol , requiredMethod , classRequiredMethod );
65
+ return isValidSubscriptionSymbol (symbol , subscriptionObject , secondaries , requiredMethod , classRequiredMethod );
64
66
}
65
67
}
68
+
66
69
InferredType type = subscriptionObject .type ();
67
70
String typeName = InferredTypes .typeName (type );
68
71
String secondaryMessage = typeName != null ? String .format (SECONDARY_MESSAGE , typeName ) : DEFAULT_SECONDARY_MESSAGE ;
69
72
secondaries .put (typeClassLocation (type ), secondaryMessage );
70
73
return type .canHaveMember (requiredMethod );
71
74
}
72
75
76
+ private static boolean isValidSubscriptionSymbol (Symbol symbol , Expression subscriptionObject , Map <LocationInFile , String > secondaries , String requiredMethod ,
77
+ @ Nullable String classRequiredMethod ) {
78
+ LocationInFile locationInFile = symbol .is (FUNCTION ) ? ((FunctionSymbol ) symbol ).definitionLocation () : ((ClassSymbol ) symbol ).definitionLocation ();
79
+ secondaries .put (locationInFile , SECONDARY_MESSAGE .formatted (symbol .name ()));
80
+ return isSubscriptionInClassArg (subscriptionObject ) || canHaveMethod (symbol , requiredMethod , classRequiredMethod );
81
+ }
82
+
83
+ private static boolean isInvalidSubscriptionCallExpr (Expression expression , Map <LocationInFile , String > secondaries ) {
84
+ if (expression instanceof CallExpression callExpression
85
+ && callExpression .calleeSymbol () instanceof FunctionSymbol functionSymbol
86
+ && functionSymbol .isAsynchronous ()) {
87
+ secondaries .put (functionSymbol .definitionLocation (), SECONDARY_MESSAGE .formatted (functionSymbol .name ()));
88
+ return true ;
89
+ }
90
+ return false ;
91
+ }
92
+
93
+ private static boolean isSubscriptionInClassArg (Expression subscriptionObject ) {
94
+ return Optional .ofNullable (((ClassDef ) TreeUtils .firstAncestorOfKind (subscriptionObject , Tree .Kind .CLASSDEF ))).map (ClassDef ::args ).map (ArgList ::arguments )
95
+ .stream ()
96
+ .flatMap (Collection ::stream )
97
+ .flatMap (TreeUtils .toStreamInstanceOfMapper (RegularArgument .class ))
98
+ .map (RegularArgument ::expression )
99
+ .flatMap (TreeUtils .toStreamInstanceOfMapper (SubscriptionExpression .class ))
100
+ .map (SubscriptionExpression ::object )
101
+ .anyMatch (subscriptionObject ::equals );
102
+ }
103
+
73
104
@ Override
74
105
public String message (@ Nullable String name , String missingMethod ) {
75
106
if (name != null ) {
0 commit comments