3131import org .sonar .plugins .python .api .tree .WithStatement ;
3232import org .sonar .plugins .python .api .tree .YieldExpression ;
3333import org .sonar .plugins .python .api .tree .YieldStatement ;
34+ import org .sonar .plugins .python .api .types .v2 .ClassType ;
35+ import org .sonar .plugins .python .api .types .v2 .FunctionType ;
3436import org .sonar .python .checks .utils .CheckUtils ;
3537
3638@ Rule (key = "S7503" )
3739public class AsyncFunctionNotAsyncCheck extends PythonSubscriptionCheck {
38-
40+
3941 private static final String MESSAGE = "Use asynchronous features in this function or remove the `async` keyword." ;
40-
42+
4143 @ Override
4244 public void initialize (Context context ) {
4345 context .registerSyntaxNodeConsumer (Tree .Kind .FUNCDEF , AsyncFunctionNotAsyncCheck ::checkAsyncFunction );
4446 }
45-
47+
4648 private static void checkAsyncFunction (SubscriptionContext ctx ) {
4749 FunctionDef functionDef = (FunctionDef ) ctx .syntaxNode ();
48-
50+
4951 Token asyncKeyword = functionDef .asyncKeyword ();
5052 if (asyncKeyword == null || isException (functionDef )) {
5153 return ;
5254 }
5355 AsyncFeatureVisitor visitor = new AsyncFeatureVisitor ();
5456 functionDef .body ().accept (visitor );
55-
57+
5658 if (!visitor .hasAsyncFeature ()) {
5759 ctx .addIssue (functionDef .name (), MESSAGE ).secondary (asyncKeyword , "This function is async." );
5860 }
5961 }
6062
6163 private static boolean isException (FunctionDef functionDef ) {
62- return CheckUtils .isAbstract (functionDef ) || isEmptyFunction (functionDef .body ());
64+ return CheckUtils .isAbstract (functionDef ) ||
65+ isEmptyFunction (functionDef .body ()) ||
66+ isDunderMethod (functionDef ) ||
67+ !functionDef .decorators ().isEmpty () ||
68+ mightBeOverridingMethod (functionDef );
6369 }
64-
70+
71+ private static boolean isDunderMethod (FunctionDef functionDef ) {
72+ String methodName = functionDef .name ().name ();
73+ return methodName .startsWith ("__" );
74+ }
75+
6576 private static boolean isEmptyFunction (StatementList body ) {
6677 for (Statement statement : body .statements ()) {
6778 if (!CheckUtils .isEmptyStatement (statement )) {
@@ -70,20 +81,25 @@ private static boolean isEmptyFunction(StatementList body) {
7081 }
7182 return true ;
7283 }
73-
84+
85+ private static boolean mightBeOverridingMethod (FunctionDef functionDef ) {
86+ FunctionType functionType = (FunctionType ) functionDef .name ().typeV2 ();
87+ return functionType .owner () instanceof ClassType classType && (classType .hasUnresolvedHierarchy () || classType .inheritedMember (functionType .name ()).isPresent ());
88+ }
89+
7490 private static class AsyncFeatureVisitor extends BaseTreeVisitor {
75-
91+
7692 private boolean asyncFeatureFound = false ;
77-
93+
7894 public boolean hasAsyncFeature () {
7995 return asyncFeatureFound ;
8096 }
81-
97+
8298 @ Override
8399 public void visitAwaitExpression (AwaitExpression awaitExpression ) {
84100 asyncFeatureFound = true ;
85101 }
86-
102+
87103 @ Override
88104 public void visitForStatement (ForStatement forStatement ) {
89105 if (forStatement .isAsync ()) {
@@ -94,7 +110,7 @@ public void visitForStatement(ForStatement forStatement) {
94110 super .visitForStatement (forStatement );
95111 }
96112 }
97-
113+
98114 @ Override
99115 public void visitWithStatement (WithStatement withStatement ) {
100116 if (withStatement .isAsync ()) {
@@ -104,22 +120,22 @@ public void visitWithStatement(WithStatement withStatement) {
104120 super .visitWithStatement (withStatement );
105121 }
106122 }
107-
123+
108124 @ Override
109125 public void visitYieldStatement (YieldStatement yieldStatement ) {
110126 asyncFeatureFound = true ;
111127 }
112-
128+
113129 @ Override
114130 public void visitYieldExpression (YieldExpression yieldExpression ) {
115131 asyncFeatureFound = true ;
116132 }
117-
133+
118134 @ Override
119135 public void visitFunctionDef (FunctionDef functionDef ) {
120136 // Skip nested functions
121137 }
122-
138+
123139 @ Override
124140 protected void scan (@ Nullable Tree tree ) {
125141 // Stop scanning if we've already found an async feature
0 commit comments