|
19 | 19 | import java.util.List; |
20 | 20 | import org.sonar.check.Rule; |
21 | 21 | import org.sonar.plugins.python.api.PythonSubscriptionCheck; |
| 22 | +import org.sonar.plugins.python.api.SubscriptionContext; |
22 | 23 | import org.sonar.plugins.python.api.symbols.v2.SymbolV2; |
23 | 24 | import org.sonar.plugins.python.api.symbols.v2.UsageV2; |
| 25 | +import org.sonar.plugins.python.api.tree.Argument; |
| 26 | +import org.sonar.plugins.python.api.tree.CallExpression; |
24 | 27 | import org.sonar.plugins.python.api.tree.Expression; |
25 | 28 | import org.sonar.plugins.python.api.tree.Name; |
26 | 29 | import org.sonar.plugins.python.api.tree.RaiseStatement; |
| 30 | +import org.sonar.plugins.python.api.tree.RegularArgument; |
27 | 31 | import org.sonar.plugins.python.api.tree.Tree; |
28 | 32 | import org.sonar.plugins.python.api.tree.Tree.Kind; |
29 | 33 | import org.sonar.plugins.python.api.types.v2.matchers.TypeMatcher; |
|
36 | 40 | @Rule(key = "S112") |
37 | 41 | public class GenericExceptionRaisedCheck extends PythonSubscriptionCheck { |
38 | 42 |
|
| 43 | + private static final String MESSAGE = "Replace this generic exception class with a more specific one."; |
| 44 | + |
39 | 45 | private final TypeMatcher isExceptionOrBaseExceptionMatcher = TypeMatchers.any( |
40 | 46 | TypeMatchers.isObjectOfType(EXCEPTION), |
41 | 47 | TypeMatchers.isObjectOfType(BASE_EXCEPTION), |
42 | 48 | TypeMatchers.isType(EXCEPTION), |
43 | 49 | TypeMatchers.isType(BASE_EXCEPTION) |
44 | 50 | ); |
45 | 51 |
|
| 52 | + private final TypeMatcher isObjectOfTypeExceptionOrBaseExceptionMatcher = TypeMatchers.any( |
| 53 | + TypeMatchers.isObjectOfType(EXCEPTION), |
| 54 | + TypeMatchers.isObjectOfType(BASE_EXCEPTION) |
| 55 | + ); |
| 56 | + |
46 | 57 | @Override |
47 | 58 | public void initialize(Context context) { |
48 | | - context.registerSyntaxNodeConsumer(Kind.RAISE_STMT, ctx -> { |
49 | | - RaiseStatement raise = (RaiseStatement) ctx.syntaxNode(); |
50 | | - List<Expression> expressions = raise.expressions(); |
51 | | - if (expressions.isEmpty()) { |
52 | | - return; |
53 | | - } |
| 59 | + context.registerSyntaxNodeConsumer(Kind.RAISE_STMT, this::checkRaise); |
| 60 | + context.registerSyntaxNodeConsumer(Kind.CALL_EXPR, this::checkFunctionCall); |
| 61 | + } |
| 62 | + |
| 63 | + private void checkRaise(SubscriptionContext ctx) { |
| 64 | + RaiseStatement raise = (RaiseStatement) ctx.syntaxNode(); |
| 65 | + List<Expression> expressions = raise.expressions(); |
| 66 | + if (expressions.isEmpty()) { |
| 67 | + return; |
| 68 | + } |
54 | 69 |
|
55 | | - Expression expression = expressions.get(0); |
56 | | - if (!isExceptionOrBaseExceptionMatcher.isTrueFor(expression, ctx)) { |
57 | | - return; |
| 70 | + Expression expression = expressions.get(0); |
| 71 | + if (!isExceptionOrBaseExceptionMatcher.isTrueFor(expression, ctx)) { |
| 72 | + return; |
| 73 | + } |
| 74 | + if (!isExceptionFunctionLocal(expression, raise)) { |
| 75 | + return; |
| 76 | + } |
| 77 | + ctx.addIssue(expression, MESSAGE); |
| 78 | + } |
| 79 | + |
| 80 | + private void checkFunctionCall(SubscriptionContext ctx) { |
| 81 | + CallExpression call = (CallExpression) ctx.syntaxNode(); |
| 82 | + List<Argument> arguments = call.arguments(); |
| 83 | + for (Argument arg : arguments) { |
| 84 | + if (!(arg instanceof RegularArgument regArg) || regArg.keywordArgument() != null) { |
| 85 | + continue; |
58 | 86 | } |
59 | | - if (!isExceptionFunctionLocal(expression, raise)) { |
60 | | - return; |
| 87 | + Expression argExpr = regArg.expression(); |
| 88 | + if (isObjectOfTypeExceptionOrBaseExceptionMatcher.isTrueFor(argExpr, ctx) && isExceptionFunctionLocal(argExpr, call)) { |
| 89 | + ctx.addIssue(argExpr, MESSAGE); |
61 | 90 | } |
62 | | - |
63 | | - ctx.addIssue(expression, "Replace this generic exception class with a more specific one."); |
64 | | - }); |
| 91 | + } |
65 | 92 | } |
66 | 93 |
|
67 | | - private static boolean isExceptionFunctionLocal(Expression expression, RaiseStatement raise) { |
| 94 | + private static boolean isExceptionFunctionLocal(Expression expression, Tree contextTree) { |
68 | 95 | if (!(expression instanceof Name name)) return true; |
69 | 96 | SymbolV2 symbolV2 = name.symbolV2(); |
70 | | - return symbolV2 == null || isLocalVariable(symbolV2, raise); |
| 97 | + return symbolV2 == null || isLocalVariable(symbolV2, contextTree); |
71 | 98 | } |
72 | 99 |
|
73 | | - private static boolean isLocalVariable(SymbolV2 symbol, Tree raiseStatement) { |
74 | | - Tree function = TreeUtils.firstAncestorOfKind(raiseStatement, Kind.FUNCDEF); |
| 100 | + private static boolean isLocalVariable(SymbolV2 symbol, Tree contextTree) { |
| 101 | + Tree function = TreeUtils.firstAncestorOfKind(contextTree, Kind.FUNCDEF); |
75 | 102 | if (function == null) { |
76 | 103 | return false; |
77 | 104 | } |
|
0 commit comments