Skip to content

Commit 672cdd9

Browse files
thomas-serre-sonarsourcesonartech
authored andcommitted
SONARPY-3423 S112: Do not raise when exception is not instantiated in the function (#897)
GitOrigin-RevId: 501565ebf7df5fa9f9a83d924f48a023576f863f
1 parent ed683b0 commit 672cdd9

2 files changed

Lines changed: 47 additions & 6 deletions

File tree

python-checks/src/main/java/org/sonar/python/checks/GenericExceptionRaisedCheck.java

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,31 @@
1919
import java.util.List;
2020
import org.sonar.check.Rule;
2121
import org.sonar.plugins.python.api.PythonSubscriptionCheck;
22-
import org.sonar.plugins.python.api.TriBool;
22+
import org.sonar.plugins.python.api.symbols.v2.SymbolV2;
23+
import org.sonar.plugins.python.api.symbols.v2.UsageV2;
2324
import org.sonar.plugins.python.api.tree.Expression;
25+
import org.sonar.plugins.python.api.tree.Name;
2426
import org.sonar.plugins.python.api.tree.RaiseStatement;
27+
import org.sonar.plugins.python.api.tree.Tree;
2528
import org.sonar.plugins.python.api.tree.Tree.Kind;
2629
import org.sonar.plugins.python.api.types.v2.PythonType;
30+
import org.sonar.plugins.python.api.types.v2.matchers.TypeMatcher;
31+
import org.sonar.plugins.python.api.types.v2.matchers.TypeMatchers;
32+
import org.sonar.python.tree.TreeUtils;
2733

2834
import static org.sonar.plugins.python.api.types.BuiltinTypes.BASE_EXCEPTION;
2935
import static org.sonar.plugins.python.api.types.BuiltinTypes.EXCEPTION;
3036

3137
@Rule(key = "S112")
3238
public class GenericExceptionRaisedCheck extends PythonSubscriptionCheck {
3339

40+
private final TypeMatcher isExceptionOrBaseExceptionMatcher = TypeMatchers.any(
41+
TypeMatchers.isObjectOfType(EXCEPTION),
42+
TypeMatchers.isObjectOfType(BASE_EXCEPTION),
43+
TypeMatchers.isType(EXCEPTION),
44+
TypeMatchers.isType(BASE_EXCEPTION)
45+
);
46+
3447
@Override
3548
public void initialize(Context context) {
3649
context.registerSyntaxNodeConsumer(Kind.RAISE_STMT, ctx -> {
@@ -39,13 +52,34 @@ public void initialize(Context context) {
3952
if (expressions.isEmpty()) {
4053
return;
4154
}
55+
4256
Expression expression = expressions.get(0);
43-
PythonType pythonType = expression.typeV2();
44-
TriBool isException = ctx.typeChecker().typeCheckBuilder().isBuiltinWithName(EXCEPTION).check(pythonType);
45-
TriBool isBaseException = ctx.typeChecker().typeCheckBuilder().isBuiltinWithName(BASE_EXCEPTION).check(pythonType);
46-
if (isException == TriBool.TRUE || isBaseException == TriBool.TRUE) {
47-
ctx.addIssue(expression, "Replace this generic exception class with a more specific one.");
57+
if (!isExceptionOrBaseExceptionMatcher.isTrueFor(expression, ctx)) {
58+
return;
4859
}
60+
if (!isExceptionFunctionLocal(expression, raise)) {
61+
return;
62+
}
63+
64+
ctx.addIssue(expression, "Replace this generic exception class with a more specific one.");
4965
});
5066
}
67+
68+
private static boolean isExceptionFunctionLocal(Expression expression, RaiseStatement raise) {
69+
if (!(expression instanceof Name name)) return true;
70+
SymbolV2 symbolV2 = name.symbolV2();
71+
return symbolV2 == null || isLocalVariable(symbolV2, raise);
72+
}
73+
74+
private static boolean isLocalVariable(SymbolV2 symbol, Tree raiseStatement) {
75+
Tree function = TreeUtils.firstAncestorOfKind(raiseStatement, Kind.FUNCDEF);
76+
if (function == null) {
77+
return false;
78+
}
79+
80+
return symbol.getSingleBindingUsage()
81+
.filter(u -> !u.kind().equals(UsageV2.Kind.PARAMETER))
82+
.map(usage -> TreeUtils.firstAncestor(usage.tree(), t -> t == function) != null)
83+
.orElse(false);
84+
}
5185
}

python-checks/src/test/resources/checks/genericException/genericExceptionRaised.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,10 @@ def python2_multiple_expressions(cond):
4848

4949
def no_issue_with_self_return_type():
5050
raise MyException().with_traceback("foo")
51+
52+
def raised_exception_is_the_parameter(exception: BaseException):
53+
raise exception
54+
55+
global_exception = BaseException()
56+
def raise_global_exception():
57+
raise global_exception

0 commit comments

Comments
 (0)