1616 */
1717package org .sonar .python .checks ;
1818
19- import java .util .ArrayList ;
20- import java .util .Arrays ;
21- import java .util .List ;
19+ import java .util .Map ;
20+ import java .util .Optional ;
2221import org .sonar .check .Rule ;
2322import org .sonar .plugins .python .api .PythonSubscriptionCheck ;
23+ import org .sonar .plugins .python .api .quickfix .PythonQuickFix ;
2424import org .sonar .plugins .python .api .tree .Argument ;
2525import org .sonar .plugins .python .api .tree .CallExpression ;
2626import org .sonar .plugins .python .api .tree .Expression ;
2727import org .sonar .plugins .python .api .tree .RegularArgument ;
2828import org .sonar .plugins .python .api .tree .Tree ;
2929import org .sonar .plugins .python .api .types .v2 .TriBool ;
30+ import org .sonar .python .quickfix .TextEditUtils ;
3031import org .sonar .python .types .v2 .TypeCheckBuilder ;
32+ import org .sonar .python .types .v2 .TypeCheckMap ;
3133
3234@ Rule (key = "S7498" )
3335public class EmptyCollectionConstructorCheck extends PythonSubscriptionCheck {
3436
3537 private static final String MESSAGE = "Replace this constructor call with a literal." ;
36- private static final List <String > COLLECTION_CONSTRUCTORS = Arrays .asList ("dict" , "list" , "tuple" );
38+ private static final Map <String , String > COLLECTION_CONSTRUCTORS = Map .ofEntries (
39+ Map .entry ("list" , "[]" ),
40+ Map .entry ("tuple" , "()" ),
41+ Map .entry ("dict" , "{}" )
42+ );
3743
38- private List < TypeCheckBuilder > collectionConstructorTypeCheckers = null ;
44+ private TypeCheckMap < String > collectionConstructorTypeCheckers = null ;
3945 private TypeCheckBuilder dictChecker = null ;
4046
4147 @ Override
4248 public void initialize (Context context ) {
4349 context .registerSyntaxNodeConsumer (Tree .Kind .FILE_INPUT , ctx -> {
4450 dictChecker = ctx .typeChecker ().typeCheckBuilder ().isTypeWithFqn ("dict" );
4551
46- collectionConstructorTypeCheckers = new ArrayList <>();
47- for (String constructor : COLLECTION_CONSTRUCTORS ) {
48- collectionConstructorTypeCheckers .add (ctx .typeChecker ().typeCheckBuilder ().isTypeWithFqn (constructor ));
52+ collectionConstructorTypeCheckers = new TypeCheckMap <>();
53+ for (var constructorEntry : COLLECTION_CONSTRUCTORS .entrySet ()) {
54+ TypeCheckBuilder constructorTypeChecker = ctx .typeChecker ().typeCheckBuilder ().isTypeWithFqn (constructorEntry .getKey ());
55+ collectionConstructorTypeCheckers .put (constructorTypeChecker , constructorEntry .getValue ());
4956 }
5057 });
5158
5259 context .registerSyntaxNodeConsumer (Tree .Kind .CALL_EXPR , ctx -> {
5360 CallExpression callExpression = (CallExpression ) ctx .syntaxNode ();
5461
5562 if (isUnnecessaryCollectionConstructor (callExpression )) {
56- ctx .addIssue (callExpression .callee (), MESSAGE );
63+ var issue = ctx .addIssue (callExpression .callee (), MESSAGE );
64+ createQuickFix (callExpression ).ifPresent (issue ::addQuickFix );
5765 }
5866 });
5967 }
@@ -65,7 +73,7 @@ private boolean isUnnecessaryCollectionConstructor(CallExpression callExpression
6573
6674 private boolean isCollectionConstructor (Expression calleeExpression ) {
6775 var type = calleeExpression .typeV2 ();
68- return collectionConstructorTypeCheckers .stream (). map ( checker -> checker . check ( type )). anyMatch ( TriBool . TRUE :: equals );
76+ return collectionConstructorTypeCheckers .getOptionalForType ( type ). isPresent ( );
6977 }
7078
7179 private static boolean isEmptyCall (CallExpression callExpression ) {
@@ -87,4 +95,11 @@ private static boolean hasOnlyKeywordArguments(CallExpression callExpression) {
8795 private static boolean isKeywordArg (Argument arg ) {
8896 return arg instanceof RegularArgument regularArg && regularArg .keywordArgument () != null ;
8997 }
98+
99+ private Optional <PythonQuickFix > createQuickFix (CallExpression callExpression ) {
100+ return Optional .of (callExpression )
101+ .filter (EmptyCollectionConstructorCheck ::isEmptyCall )
102+ .flatMap (callExpr -> collectionConstructorTypeCheckers .getOptionalForType (callExpression .callee ().typeV2 ()))
103+ .map (replacementStr -> PythonQuickFix .newQuickFix ("Replace with literal" , TextEditUtils .replace (callExpression , replacementStr )));
104+ }
90105}
0 commit comments