2424import org .sonar .plugins .python .api .tree .BaseTreeVisitor ;
2525import org .sonar .plugins .python .api .tree .ForStatement ;
2626import org .sonar .plugins .python .api .tree .FunctionDef ;
27+ import org .sonar .plugins .python .api .tree .ReturnStatement ;
2728import org .sonar .plugins .python .api .tree .Statement ;
2829import org .sonar .plugins .python .api .tree .StatementList ;
2930import org .sonar .plugins .python .api .tree .Token ;
3334import org .sonar .plugins .python .api .tree .YieldStatement ;
3435import org .sonar .plugins .python .api .types .v2 .ClassType ;
3536import org .sonar .plugins .python .api .types .v2 .FunctionType ;
37+ import org .sonar .plugins .python .api .types .v2 .TriBool ;
3638import org .sonar .python .checks .utils .CheckUtils ;
39+ import org .sonar .python .types .v2 .TypeCheckBuilder ;
3740
3841@ Rule (key = "S7503" )
3942public class AsyncFunctionNotAsyncCheck extends PythonSubscriptionCheck {
4043
4144 private static final String MESSAGE = "Use asynchronous features in this function or remove the `async` keyword." ;
45+ private TypeCheckBuilder notImplementedTypeChecker ;
4246
4347 @ Override
4448 public void initialize (Context context ) {
45- context .registerSyntaxNodeConsumer (Tree .Kind .FUNCDEF , AsyncFunctionNotAsyncCheck ::checkAsyncFunction );
49+ context .registerSyntaxNodeConsumer (Tree .Kind .FILE_INPUT , this ::setupTypeChecks );
50+ context .registerSyntaxNodeConsumer (Tree .Kind .FUNCDEF , this ::checkAsyncFunction );
4651 }
4752
48- private static void checkAsyncFunction (SubscriptionContext ctx ) {
53+ private void setupTypeChecks (SubscriptionContext ctx ) {
54+ notImplementedTypeChecker = ctx .typeChecker ().typeCheckBuilder ().isTypeWithName ("NotImplemented" );
55+ }
56+
57+ private void checkAsyncFunction (SubscriptionContext ctx ) {
4958 FunctionDef functionDef = (FunctionDef ) ctx .syntaxNode ();
5059
5160 Token asyncKeyword = functionDef .asyncKeyword ();
@@ -60,9 +69,9 @@ private static void checkAsyncFunction(SubscriptionContext ctx) {
6069 }
6170 }
6271
63- private static boolean isException (FunctionDef functionDef ) {
72+ private boolean isException (FunctionDef functionDef ) {
6473 return CheckUtils .isAbstract (functionDef ) ||
65- isEmptyFunction (functionDef .body ()) ||
74+ isTrivialFunction (functionDef .body ()) ||
6675 isDunderMethod (functionDef ) ||
6776 !functionDef .decorators ().isEmpty () ||
6877 mightBeOverridingMethod (functionDef );
@@ -73,15 +82,20 @@ private static boolean isDunderMethod(FunctionDef functionDef) {
7382 return methodName .startsWith ("__" );
7483 }
7584
76- private static boolean isEmptyFunction (StatementList body ) {
85+ private boolean isTrivialFunction (StatementList body ) {
7786 for (Statement statement : body .statements ()) {
78- if (!CheckUtils .isEmptyStatement (statement )) {
87+ if (!CheckUtils .isEmptyStatement (statement ) && ! statement . is ( Tree . Kind . RAISE_STMT ) && ! isReturnNotImplemented ( statement ) ) {
7988 return false ;
8089 }
8190 }
8291 return true ;
8392 }
8493
94+ private boolean isReturnNotImplemented (Statement statement ) {
95+ return statement .is (Tree .Kind .RETURN_STMT ) &&
96+ ((ReturnStatement ) statement ).expressions ().stream ().allMatch (e -> notImplementedTypeChecker .check (e .typeV2 ()) == TriBool .TRUE );
97+ }
98+
8599 private static boolean mightBeOverridingMethod (FunctionDef functionDef ) {
86100 FunctionType functionType = (FunctionType ) functionDef .name ().typeV2 ();
87101 return functionType .owner () instanceof ClassType classType && (classType .hasUnresolvedHierarchy () || classType .inheritedMember (functionType .name ()).isPresent ());
0 commit comments