1919import java .util .List ;
2020import org .sonar .check .Rule ;
2121import 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 ;
2324import org .sonar .plugins .python .api .tree .Expression ;
25+ import org .sonar .plugins .python .api .tree .Name ;
2426import org .sonar .plugins .python .api .tree .RaiseStatement ;
27+ import org .sonar .plugins .python .api .tree .Tree ;
2528import org .sonar .plugins .python .api .tree .Tree .Kind ;
2629import 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
2834import static org .sonar .plugins .python .api .types .BuiltinTypes .BASE_EXCEPTION ;
2935import static org .sonar .plugins .python .api .types .BuiltinTypes .EXCEPTION ;
3036
3137@ Rule (key = "S112" )
3238public 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}
0 commit comments