|
35 | 35 | import org.sonar.plugins.python.api.tree.FunctionDef;
|
36 | 36 | import org.sonar.plugins.python.api.tree.Name;
|
37 | 37 | import org.sonar.plugins.python.api.tree.NumericLiteral;
|
| 38 | +import org.sonar.plugins.python.api.tree.Statement; |
38 | 39 | import org.sonar.plugins.python.api.tree.Token;
|
39 | 40 | import org.sonar.plugins.python.api.tree.Tree;
|
40 | 41 | import org.sonar.plugins.python.api.tree.UnaryExpression;
|
|
45 | 46 | import org.sonar.python.tree.TreeUtils;
|
46 | 47 |
|
47 | 48 | import static org.sonar.python.checks.DeadStoreUtils.isUsedInSubFunction;
|
48 |
| -import static org.sonar.python.quickfix.PythonTextEdit.remove; |
49 | 49 |
|
50 | 50 | @Rule(key = "S1854")
|
51 | 51 | public class DeadStoreCheck extends PythonSubscriptionCheck {
|
@@ -82,10 +82,11 @@ private static void verifyBlock(SubscriptionContext ctx, CfgBlock block, LiveVar
|
82 | 82 | .forEach(unnecessaryAssignment -> {
|
83 | 83 | Tree element = unnecessaryAssignment.element;
|
84 | 84 | String message = String.format(MESSAGE_TEMPLATE, unnecessaryAssignment.symbol.name());
|
85 |
| - IssueWithQuickFix issue = (IssueWithQuickFix) separatorToken(element) |
| 85 | + IssueWithQuickFix issue = (IssueWithQuickFix) separatorTokenForIssue(element) |
86 | 86 | .map(separator -> ctx.addIssue(element.firstToken(), separator, message))
|
87 | 87 | .orElseGet(() -> ctx.addIssue(element, message));
|
88 |
| - createQuickFix(issue, unnecessaryAssignment.element); |
| 88 | + |
| 89 | + createQuickFix(issue, element); |
89 | 90 | });
|
90 | 91 | }
|
91 | 92 |
|
@@ -152,37 +153,64 @@ private static boolean isFunctionDeclarationSymbol(Symbol symbol) {
|
152 | 153 | return symbol.usages().stream().anyMatch(u -> u.kind() == Usage.Kind.FUNC_DECLARATION);
|
153 | 154 | }
|
154 | 155 |
|
155 |
| - private static Optional<Token> separatorToken(Tree element) { |
156 |
| - if (element instanceof AssignmentStatement) { |
157 |
| - Token seperator = ((AssignmentStatement) element).separator(); |
158 |
| - return "\n".equals(seperator.value()) ? Optional.empty() : Optional.of(seperator); |
| 156 | + private static Optional<Token> separatorTokenForIssue(Tree element) { |
| 157 | + return separatorTokenOfElement(element) |
| 158 | + .filter(sep -> !"\n".equals(sep.value())); |
| 159 | + } |
| 160 | + |
| 161 | + private static Optional<Token> separatorTokenOfElement(Tree element) { |
| 162 | + if (element.is(Tree.Kind.ASSIGNMENT_STMT, Tree.Kind.EXPRESSION_STMT)) { |
| 163 | + Token separator = ((Statement) element).separator(); |
| 164 | + return Optional.ofNullable(separator); |
159 | 165 | }
|
160 | 166 | return Optional.empty();
|
161 | 167 | }
|
162 | 168 |
|
163 |
| - private static PythonTextEdit removeDeadStore(Tree tree) { |
164 |
| - List<Tree> childrenOfParent = tree.parent().children(); |
| 169 | + private static PythonTextEdit removeDeadStore(Tree currentTree) { |
| 170 | + Optional<Token> tokenSeparator = separatorTokenOfElement(currentTree); |
| 171 | + List<Tree> childrenOfParent = currentTree.parent().children(); |
| 172 | + |
165 | 173 | if (childrenOfParent.size() == 1) {
|
166 |
| - return remove(tree); |
| 174 | + return PythonTextEdit.replace(currentTree, "pass"); |
| 175 | + } |
| 176 | + |
| 177 | + Token currentFirstToken = currentTree.firstToken(); |
| 178 | + int indexOfCurrentTree = childrenOfParent.indexOf(currentTree); |
| 179 | + |
| 180 | + // If the tree to remove is the last one, we remove from the end of the previous tree until the end of the current one |
| 181 | + if (indexOfCurrentTree == childrenOfParent.size() - 1) { |
| 182 | + Tree previousTree = childrenOfParent.get(indexOfCurrentTree - 1); |
| 183 | + Optional<Token> previousSep = separatorTokenOfElement(previousTree); |
| 184 | + Token previous = previousSep.orElse(previousTree.lastToken()); |
| 185 | + currentFirstToken = tokenSeparator.orElse(currentTree.lastToken()); |
| 186 | + return removeFromEndOfTillEndOf(previous, currentFirstToken); |
167 | 187 | }
|
168 |
| - Token current = tree.firstToken(); |
169 |
| - int i = childrenOfParent.indexOf(tree); |
170 |
| - if (i == childrenOfParent.size() - 1) { |
171 |
| - Token previous = childrenOfParent.get(i - 1).lastToken(); |
172 |
| - current = tree.lastToken(); |
173 |
| - // Replace from the end of the previous token (will also remove the separator and trailing whitespaces) until the end of the current token |
174 |
| - return new PythonTextEdit("", previous.line(), previous.column() + previous.value().length(), current.line(), current.column() + current.value().length()); |
| 188 | + |
| 189 | + // If the next tree is more than 1 line after the currentFirstToken tree, we only remove the separator |
| 190 | + Token next; |
| 191 | + int currentLine = currentTree.lastToken().line(); |
| 192 | + int nextLine = childrenOfParent.get(indexOfCurrentTree + 1).firstToken().line(); |
| 193 | + if (nextLine > currentLine + 1) { |
| 194 | + next = tokenSeparator.orElse(currentTree.lastToken()); |
| 195 | + // Remove from the start of the currentFirstToken token until after the separator |
| 196 | + return new PythonTextEdit("", currentFirstToken.line(), currentFirstToken.column(), next.line(), next.column() + next.value().length()); |
| 197 | + } else { |
| 198 | + // Remove from the start of the currentFirstToken token until the next token |
| 199 | + next = childrenOfParent.get(indexOfCurrentTree + 1).firstToken(); |
| 200 | + return new PythonTextEdit("", currentFirstToken.line(), currentFirstToken.column(), next.line(), next.column()); |
175 | 201 | }
|
176 |
| - Token next = childrenOfParent.get(i + 1).firstToken(); |
177 |
| - // Remove from the start of the current tokenuntil the next token |
178 |
| - return new PythonTextEdit("", current.line(), current.column(), next.line(), next.column()); |
179 | 202 | }
|
180 | 203 |
|
181 | 204 | private static void createQuickFix(IssueWithQuickFix issue, Tree unnecessaryAssignment) {
|
182 | 205 | PythonTextEdit edit = removeDeadStore(unnecessaryAssignment);
|
183 |
| - PythonQuickFix quickFix = PythonQuickFix.newQuickFix("Remove the line(s)") |
| 206 | + PythonQuickFix quickFix = PythonQuickFix.newQuickFix("Remove the unused statement") |
184 | 207 | .addTextEdit(edit)
|
185 | 208 | .build();
|
186 | 209 | issue.addQuickFix(quickFix);
|
187 | 210 | }
|
| 211 | + |
| 212 | + private static PythonTextEdit removeFromEndOfTillEndOf(Token first, Token last) { |
| 213 | + return new PythonTextEdit("", first.line(), first.column() + first.value().length(), |
| 214 | + last.line(), last.column() + last.value().length()); |
| 215 | + } |
188 | 216 | }
|
0 commit comments