1616 */
1717package org .sonar .python .checks ;
1818
19- import java .util .Collection ;
19+ import java .util .List ;
2020import java .util .Map ;
2121import java .util .Optional ;
2222import javax .annotation .Nullable ;
2626import org .sonar .plugins .python .api .symbols .FunctionSymbol ;
2727import org .sonar .plugins .python .api .symbols .Symbol ;
2828import org .sonar .plugins .python .api .tree .ArgList ;
29+ import org .sonar .plugins .python .api .tree .Argument ;
2930import org .sonar .plugins .python .api .tree .CallExpression ;
3031import org .sonar .plugins .python .api .tree .ClassDef ;
3132import org .sonar .plugins .python .api .tree .Expression ;
@@ -77,7 +78,25 @@ private static boolean isValidSubscriptionSymbol(Symbol symbol, Expression subsc
7778 @ Nullable String classRequiredMethod ) {
7879 LocationInFile locationInFile = symbol .is (FUNCTION ) ? ((FunctionSymbol ) symbol ).definitionLocation () : ((ClassSymbol ) symbol ).definitionLocation ();
7980 secondaries .put (locationInFile , SECONDARY_MESSAGE .formatted (symbol .name ()));
80- return isSubscriptionInClassArg (subscriptionObject ) || canHaveMethod (symbol , requiredMethod , classRequiredMethod );
81+ return isSubscriptionInClassArg (subscriptionObject )
82+ || canHaveMethod (symbol , requiredMethod , classRequiredMethod )
83+ || isValidGenericUsage (symbol , subscriptionObject , requiredMethod );
84+ }
85+
86+ private static boolean isValidGenericUsage (Symbol symbol , Expression subscriptionObject , String requiredMethod ) {
87+ return "__getitem__" .equals (requiredMethod ) && symbol .is (CLASS ) && !areSomeSubscriptsSuspicious (subscriptionObject );
88+ }
89+
90+ private static boolean areSomeSubscriptsSuspicious (Expression subscriptionObject ) {
91+ var subscriptionExprTree = TreeUtils .firstAncestorOfKind (subscriptionObject , Tree .Kind .SUBSCRIPTION );
92+ return subscriptionExprTree instanceof SubscriptionExpression subscriptionExpr
93+ && subscriptionExpr .subscripts ().expressions ().stream ()
94+ .allMatch (ItemOperationsTypeCheck ::isSubscriptSuspicious );
95+ }
96+
97+ private static boolean isSubscriptSuspicious (Expression expr ) {
98+ // a subscript used as a generic should be a name of a class, alias, or a class as a string literal; Anything else is suspicious
99+ return !expr .is (Tree .Kind .NAME , Tree .Kind .STRING_LITERAL );
81100 }
82101
83102 private static boolean isInvalidSubscriptionCallExpr (Expression expression , Map <LocationInFile , String > secondaries ) {
@@ -91,14 +110,23 @@ private static boolean isInvalidSubscriptionCallExpr(Expression expression, Map<
91110 }
92111
93112 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 )
113+ var classDefOptional = Optional .ofNullable (TreeUtils .firstAncestorOfKind (subscriptionObject , Tree .Kind .CLASSDEF ))
114+ .map (ClassDef .class ::cast );
115+
116+ List <Argument > classArguments = classDefOptional
117+ .map (ClassDef ::args )
118+ .map (ArgList ::arguments )
119+ .orElse (List .of ());
120+
121+ var onlyRegularArgumentExpressions = classArguments .stream ()
97122 .flatMap (TreeUtils .toStreamInstanceOfMapper (RegularArgument .class ))
98- .map (RegularArgument ::expression )
123+ .map (RegularArgument ::expression );
124+
125+ var subscriptionObjectStream = onlyRegularArgumentExpressions
99126 .flatMap (TreeUtils .toStreamInstanceOfMapper (SubscriptionExpression .class ))
100- .map (SubscriptionExpression ::object )
101- .anyMatch (subscriptionObject ::equals );
127+ .map (SubscriptionExpression ::object );
128+
129+ return subscriptionObjectStream .anyMatch (subscriptionObject ::equals );
102130 }
103131
104132 @ Override
0 commit comments