1717package org .sonar .python .checks ;
1818
1919import java .util .List ;
20+ import java .util .Map ;
2021import java .util .Objects ;
2122import java .util .Optional ;
2223import org .sonar .check .Rule ;
2324import org .sonar .plugins .python .api .PythonSubscriptionCheck ;
2425import org .sonar .plugins .python .api .SubscriptionContext ;
26+ import org .sonar .plugins .python .api .quickfix .PythonQuickFix ;
2527import org .sonar .plugins .python .api .tree .ComprehensionExpression ;
2628import org .sonar .plugins .python .api .tree .ComprehensionFor ;
2729import org .sonar .plugins .python .api .tree .DictCompExpression ;
2830import org .sonar .plugins .python .api .tree .Name ;
2931import org .sonar .plugins .python .api .tree .Tree ;
3032import org .sonar .plugins .python .api .tree .Tuple ;
33+ import org .sonar .python .quickfix .TextEditUtils ;
3134import org .sonar .python .tree .TreeUtils ;
3235
3336@ Rule (key = "S7500" )
3437public class UnnecessaryComprehensionCheck extends PythonSubscriptionCheck {
3538
39+ private static final Map <Tree .Kind , String > COMPREHENSION_TO_QUICK_FIX_FORMAT_MAPPING = Map .of (
40+ Tree .Kind .GENERATOR_EXPR , "%s" ,
41+ Tree .Kind .LIST_COMPREHENSION , "list(%s)" ,
42+ Tree .Kind .SET_COMPREHENSION , "set(%s)" ,
43+ Tree .Kind .DICT_COMPREHENSION , "dict(%s)"
44+ );
45+
3646 @ Override
3747 public void initialize (Context context ) {
3848 context .registerSyntaxNodeConsumer (Tree .Kind .GENERATOR_EXPR , UnnecessaryComprehensionCheck ::checkComprehensionExpression );
@@ -54,7 +64,8 @@ private static void checkComprehensionExpression(SubscriptionContext ctx) {
5464 && loopExpression instanceof Name loopValueName
5565 && valueName .name ().equals (loopValueName .name ())
5666 ) {
57- ctx .addIssue (comprehension , "Replace this comprehension with passing the iterable to the collection constructor call" );
67+ var issue = ctx .addIssue (comprehension , "Replace this comprehension with passing the iterable to the collection constructor call" );
68+ createQuickFix (comprehension , comprehension .comprehensionFor ()).ifPresent (issue ::addQuickFix );
5869 }
5970 }
6071
@@ -78,7 +89,17 @@ private static void checkDictComprehensionExpression(SubscriptionContext ctx) {
7889 && keyName .name ().equals (loopKeyName .name ())
7990 && valueName .name ().equals (loopValueName .name ())
8091 ) {
81- ctx .addIssue (comprehension , "Replace this comprehension with passing the iterable to the dict constructor call" );
92+ var issue = ctx .addIssue (comprehension , "Replace this comprehension with passing the iterable to the dict constructor call" );
93+ createQuickFix (comprehension , comprehension .comprehensionFor ()).ifPresent (issue ::addQuickFix );
8294 }
8395 }
96+
97+ private static Optional <PythonQuickFix > createQuickFix (Tree comprehension , ComprehensionFor comprehensionFor ) {
98+ return Optional .ofNullable (TreeUtils .treeToString (comprehensionFor .iterable (), false ))
99+ .flatMap (iterableString -> Optional .ofNullable (COMPREHENSION_TO_QUICK_FIX_FORMAT_MAPPING .getOrDefault (comprehension .getKind (), null ))
100+ .map (format -> format .formatted (iterableString ))
101+ .map (replacementText -> TextEditUtils .replace (comprehension , replacementText ))
102+ .map (textEdit -> PythonQuickFix .newQuickFix ("Replace with collection constructor call" ).addTextEdit (textEdit ))
103+ .map (PythonQuickFix .Builder ::build ));
104+ }
84105}
0 commit comments