31
31
import org .sonar .plugins .python .api .symbols .Usage ;
32
32
import org .sonar .plugins .python .api .tree .ClassDef ;
33
33
import org .sonar .plugins .python .api .tree .FunctionDef ;
34
- import org .sonar .plugins .python .api .tree .Name ;
35
34
import org .sonar .plugins .python .api .tree .Parameter ;
36
35
import org .sonar .plugins .python .api .tree .Tree ;
36
+ import org .sonar .python .quickfix .IssueWithQuickFix ;
37
+ import org .sonar .python .quickfix .PythonQuickFix ;
37
38
import org .sonar .python .tree .TreeUtils ;
38
39
40
+ import static org .sonar .python .quickfix .PythonTextEdit .insertBefore ;
41
+
39
42
@ Rule (key ="S5719" )
40
43
public class InstanceAndClassMethodsAtLeastOnePositionalCheck extends PythonSubscriptionCheck {
41
44
42
45
private static final List <String > KNOWN_CLASS_METHODS = Arrays .asList ("__new__" , "__init_subclass__" );
43
46
47
+ private enum MethodIssueType {
48
+ CLASS_METHOD ("Add a class parameter" , "cls" ),
49
+ REGULAR_METHOD ("Add a \" self\" or class parameter" , "self" , "cls" );
50
+
51
+ private final String message ;
52
+ private final List <String > insertions ;
53
+
54
+ MethodIssueType (String message , String ... insertions ) {
55
+ this .message = message ;
56
+ this .insertions = Arrays .asList (insertions );
57
+ }
58
+ }
59
+
44
60
private static boolean isUsageInClassBody (Usage usage , ClassDef classDef ) {
45
61
// We want all usages that are not function declarations and their closes parent is the class definition
46
62
return usage .kind () != Usage .Kind .FUNC_DECLARATION
@@ -65,19 +81,30 @@ private static void handleFunctionDef(SubscriptionContext ctx, ClassDef classDef
65
81
66
82
List <String > decoratorNames = functionDef .decorators ()
67
83
.stream ()
68
- .map (decorator ->
69
- TreeUtils .decoratorNameFromExpression (decorator .expression ())
70
- ).filter (Objects ::nonNull ).collect (Collectors .toList ());
84
+ .map (decorator -> TreeUtils .decoratorNameFromExpression (decorator .expression ()))
85
+ .filter (Objects ::nonNull ).collect (Collectors .toList ());
71
86
72
87
if (decoratorNames .contains ("staticmethod" )) {
73
88
return ;
74
89
}
75
90
76
91
String name = functionSymbol .name ();
77
92
if (KNOWN_CLASS_METHODS .contains (name ) || decoratorNames .contains ("classmethod" )) {
78
- ctx . addIssue (functionDef . defKeyword () , functionDef . rightPar (), "Add a class parameter" );
93
+ addIssue (ctx , functionDef , MethodIssueType . CLASS_METHOD );
79
94
} else {
80
- ctx .addIssue (functionDef .defKeyword (), functionDef .rightPar (), "Add a \" self\" or class parameter" );
95
+ addIssue (ctx , functionDef , MethodIssueType .REGULAR_METHOD );
96
+ }
97
+ }
98
+
99
+ private static void addIssue (SubscriptionContext ctx , FunctionDef functionDef , MethodIssueType type ) {
100
+ IssueWithQuickFix issue = (IssueWithQuickFix ) ctx .addIssue (functionDef .defKeyword (), functionDef .rightPar (),
101
+ type .message );
102
+
103
+ for (String insertion : type .insertions ) {
104
+ PythonQuickFix quickFix = PythonQuickFix .newQuickFix (String .format ("Add '%s' as the first argument." , insertion ))
105
+ .addTextEdit (insertBefore (functionDef .rightPar (), insertion ))
106
+ .build ();
107
+ issue .addQuickFix (quickFix );
81
108
}
82
109
}
83
110
0 commit comments