Skip to content

Commit a57149b

Browse files
ghislainpiotsonartech
authored andcommitted
SONARPY-2967 Implement quickfix for S7501 (#294)
GitOrigin-RevId: aa295aedc41cb4da318c6a4d9e21ede340222a1c
1 parent 901cc74 commit a57149b

2 files changed

Lines changed: 334 additions & 16 deletions

File tree

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

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,26 @@
1616
*/
1717
package org.sonar.python.checks;
1818

19-
import java.util.HashMap;
19+
import java.util.ArrayList;
20+
import java.util.LinkedHashMap;
2021
import java.util.List;
2122
import java.util.Map;
23+
import java.util.Objects;
24+
import java.util.stream.Collectors;
2225

2326
import org.sonar.check.Rule;
2427
import org.sonar.plugins.python.api.PythonSubscriptionCheck;
2528
import org.sonar.plugins.python.api.SubscriptionContext;
29+
import org.sonar.plugins.python.api.quickfix.PythonQuickFix;
2630
import org.sonar.plugins.python.api.tree.AliasedName;
31+
import org.sonar.plugins.python.api.tree.Argument;
2732
import org.sonar.plugins.python.api.tree.CallExpression;
2833
import org.sonar.plugins.python.api.tree.Expression;
2934
import org.sonar.plugins.python.api.tree.ImportName;
3035
import org.sonar.plugins.python.api.tree.Name;
3136
import org.sonar.plugins.python.api.tree.Tree;
3237
import org.sonar.plugins.python.api.types.v2.TriBool;
38+
import org.sonar.python.quickfix.TextEditUtils;
3339
import org.sonar.python.tree.TreeUtils;
3440
import org.sonar.python.types.v2.TypeCheckBuilder;
3541

@@ -45,8 +51,11 @@ public class InputInAsyncCheck extends PythonSubscriptionCheck {
4551
private static final String LIB_TRIO = "trio";
4652
private static final String LIB_ANYIO = "anyio";
4753

54+
private static final String QUICK_FIX_TO_THREAD = "Wrap with await %s.to_thread(input%s)";
55+
private static final String QUICK_FIX_RUN_SYNC = "Wrap with await %s.to_thread.run_sync(input%s)";
56+
4857
private TypeCheckBuilder isInputCall;
49-
private final Map<String, String> asyncLibraryAliases = new HashMap<>();
58+
private final Map<String, String> asyncLibraryAliases = new LinkedHashMap<>();
5059

5160
@Override
5261
public void initialize(Context context) {
@@ -83,7 +92,8 @@ private void checkInputInAsync(SubscriptionContext context) {
8392
TreeUtils.asyncTokenOfEnclosingFunction(callExpression)
8493
.ifPresent(asyncKeyword -> {
8594
String message = getMessage();
86-
context.addIssue(callee, message).secondary(asyncKeyword, SECONDARY_MESSAGE);
95+
var issue = context.addIssue(callee, message).secondary(asyncKeyword, SECONDARY_MESSAGE);
96+
createQuickFixes(callExpression, callee).forEach(issue::addQuickFix);
8797
});
8898
}
8999

@@ -100,4 +110,30 @@ private String getMessage() {
100110
return String.format(MESSAGE_TO_THREAD_RUN_SYNC, asyncLibraryAliases.get(LIB_ANYIO));
101111
}
102112
}
113+
114+
private List<PythonQuickFix> createQuickFixes(CallExpression callExpression, Expression inputCallee) {
115+
if (inputCallee instanceof Name inputCalleeName && !"input".equals(inputCalleeName.name())) {
116+
return List.of();
117+
}
118+
119+
List<PythonQuickFix> fixes = new ArrayList<>();
120+
List<Argument> args = callExpression.arguments();
121+
122+
String argsString = args.stream()
123+
.map(arg -> TreeUtils.treeToString(arg, false))
124+
.filter(Objects::nonNull)
125+
.collect(Collectors.joining(", "));
126+
127+
var argsForTemplate = argsString.isEmpty() ? "" : (", " + argsString);
128+
129+
asyncLibraryAliases.forEach((library, alias) -> {
130+
var replacementCall = LIB_ASYNCIO.equals(library) ? (alias + ".to_thread(input" + argsForTemplate + ")") : (alias + ".to_thread.run_sync(input" + argsForTemplate + ")");
131+
var quickFixMsg = LIB_ASYNCIO.equals(library) ? String.format(QUICK_FIX_TO_THREAD, alias, argsForTemplate) : String.format(QUICK_FIX_RUN_SYNC, alias, argsForTemplate);
132+
var quickFix = PythonQuickFix.newQuickFix(quickFixMsg)
133+
.addTextEdit(TextEditUtils.replace(callExpression, "await " + replacementCall))
134+
.build();
135+
fixes.add(quickFix);
136+
});
137+
return fixes;
138+
}
103139
}

0 commit comments

Comments
 (0)