Skip to content

Commit 6191010

Browse files
author
Jeremi Do Dinh
authored
SONARPY-1490: Add a quickfix for rule S6741. (#1594)
1 parent 5e73728 commit 6191010

File tree

2 files changed

+81
-3
lines changed

2 files changed

+81
-3
lines changed

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,19 @@
2323
import org.sonar.check.Rule;
2424
import org.sonar.plugins.python.api.PythonSubscriptionCheck;
2525
import org.sonar.plugins.python.api.SubscriptionContext;
26+
import org.sonar.plugins.python.api.quickfix.PythonQuickFix;
2627
import org.sonar.plugins.python.api.symbols.Symbol;
2728
import org.sonar.plugins.python.api.tree.QualifiedExpression;
2829
import org.sonar.plugins.python.api.tree.Tree;
30+
import org.sonar.python.quickfix.TextEditUtils;
2931

3032
@Rule(key = "S6741")
3133
public class PandasDataFrameToNumpyCheck extends PythonSubscriptionCheck {
3234

35+
private static final String DATAFRAME_VALUES_FQN = "pandas.core.frame.DataFrame.values";
3336
private static final String MESSAGE = "Do not use \"DataFrame.values\".";
37+
private static final String QUICK_FIX_MESSAGE = "Replace with DataFrame.to_numpy()";
38+
3439
@Override
3540
public void initialize(Context context) {
3641
context.registerSyntaxNodeConsumer(Tree.Kind.QUALIFIED_EXPR, this::checkForDataFrameValues);
@@ -46,7 +51,12 @@ private void checkForDataFrameValues(SubscriptionContext ctx) {
4651
expr.qualifier().type()
4752
.resolveMember("values")
4853
.map(Symbol::fullyQualifiedName)
49-
.filter("pandas.core.frame.DataFrame.values"::equals)
50-
.ifPresent(str -> ctx.addIssue(expr.name(), MESSAGE));
54+
.filter(DATAFRAME_VALUES_FQN::equals)
55+
.ifPresent(str -> {
56+
PreciseIssue issue = ctx.addIssue(expr.name(), MESSAGE);
57+
issue.addQuickFix(PythonQuickFix.newQuickFix(QUICK_FIX_MESSAGE)
58+
.addTextEdit(TextEditUtils.replace(expr.name(), "to_numpy()"))
59+
.build());
60+
});
5161
}
5262
}

python-checks/src/test/java/org/sonar/python/checks/PandasDataFrameToNumpyCheckTest.java

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,81 @@
2020
package org.sonar.python.checks;
2121

2222
import org.junit.jupiter.api.Test;
23+
import org.sonar.python.checks.quickfix.PythonQuickFixVerifier;
2324
import org.sonar.python.checks.utils.PythonCheckVerifier;
2425

2526
class PandasDataFrameToNumpyCheckTest {
2627

28+
PandasDataFrameToNumpyCheck check = new PandasDataFrameToNumpyCheck();
29+
30+
private static final String QUICK_FIX_MESSAGE = "Replace this with a call to DataFrame.to_numpy()";
31+
2732
@Test
2833
void test() {
29-
PythonCheckVerifier.verify("src/test/resources/checks/pandasDataFrameToNumpy.py", new PandasDataFrameToNumpyCheck());
34+
PythonCheckVerifier.verify("src/test/resources/checks/pandasDataFrameToNumpy.py", check);
35+
}
36+
37+
@Test
38+
void quick_fix_test_1() {
39+
final String non_compliant = "def non_compliant_1(xx):\n" +
40+
" import pandas as pd\n" +
41+
"\n" +
42+
" df = pd.DataFrame({\n" +
43+
" 'X': ['A', 'B', 'A', 'C'],\n" +
44+
" 'Y': [10, 7, 12, 5]\n" +
45+
" })\n" +
46+
"\n" +
47+
" _ = df.values";
48+
final String compliant = "def non_compliant_1(xx):\n" +
49+
" import pandas as pd\n" +
50+
"\n" +
51+
" df = pd.DataFrame({\n" +
52+
" 'X': ['A', 'B', 'A', 'C'],\n" +
53+
" 'Y': [10, 7, 12, 5]\n" +
54+
" })\n" +
55+
"\n" +
56+
" _ = df.to_numpy()";
57+
performVerification(non_compliant, compliant);
58+
}
59+
60+
@Test
61+
void quick_fix_test_2() {
62+
63+
final String non_compliant = "def non_compliant_1(xx):\n" +
64+
" import pandas as pd\n" +
65+
"\n" +
66+
" _ = pd.DataFrame({\n" +
67+
" 'X': ['A', 'B', 'A', 'C'],\n" +
68+
" 'Y': [10, 7, 12, 5]\n" +
69+
" }).values";
70+
final String compliant = "def non_compliant_1(xx):\n" +
71+
" import pandas as pd\n" +
72+
"\n" +
73+
" _ = pd.DataFrame({\n" +
74+
" 'X': ['A', 'B', 'A', 'C'],\n" +
75+
" 'Y': [10, 7, 12, 5]\n" +
76+
" }).to_numpy()";
77+
performVerification(non_compliant, compliant);
3078
}
3179

80+
@Test
81+
void quick_fix_test_3() {
82+
83+
final String non_compliant = "def dataframe_from_read_csv():\n" +
84+
" import pandas as pd\n" +
85+
"\n" +
86+
" my_df = pd.read_csv(\"some_csv.csv\")\n" +
87+
" my_df.values.astype(str)";
88+
final String compliant = "def dataframe_from_read_csv():\n" +
89+
" import pandas as pd\n" +
90+
"\n" +
91+
" my_df = pd.read_csv(\"some_csv.csv\")\n" +
92+
" my_df.to_numpy().astype(str)";
93+
performVerification(non_compliant, compliant);
94+
}
95+
96+
private void performVerification(String non_compliant, String compliant) {
97+
PythonQuickFixVerifier.verify(check, non_compliant, compliant);
98+
PythonQuickFixVerifier.verifyQuickFixMessages(check, non_compliant, "Replace with DataFrame.to_numpy()");
99+
}
32100
}

0 commit comments

Comments
 (0)