Skip to content

Commit fceee63

Browse files
authored
SONARPY-2407 Refactor Token#line and pythonLine to avoid mixing the two (#2218)
1 parent 74cb3f2 commit fceee63

File tree

11 files changed

+54
-25
lines changed

11 files changed

+54
-25
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.List;
2121
import java.util.Set;
2222
import org.sonar.check.Rule;
23+
import org.sonar.plugins.python.api.PythonLine;
2324
import org.sonar.plugins.python.api.PythonSubscriptionCheck;
2425
import org.sonar.plugins.python.api.SubscriptionContext;
2526
import org.sonar.plugins.python.api.quickfix.PythonQuickFix;
@@ -130,7 +131,7 @@ private PythonQuickFix createQuickFix(BinaryExpression binaryExpression, String
130131
quickFix.addTextEdit(TextEditUtils.replace(binaryExpression, quickFixTextWithModuleName));
131132

132133
if (MATH_MODULE.equals(isCloseModuleName) && !isMathImported) {
133-
quickFix.addTextEdit(TextEditUtils.insertAtPosition(0, 0, "import math\n"));
134+
quickFix.addTextEdit(TextEditUtils.insertAtPosition(new PythonLine(0), 0, "import math\n"));
134135
}
135136

136137
return quickFix.build();

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import javax.annotation.CheckForNull;
2020
import org.sonar.check.Rule;
21+
import org.sonar.plugins.python.api.PythonLine;
2122
import org.sonar.plugins.python.api.PythonSubscriptionCheck;
2223
import org.sonar.plugins.python.api.SubscriptionContext;
2324
import org.sonar.plugins.python.api.tree.ClassDef;
@@ -118,7 +119,7 @@ private static void addQuickFix(PreciseIssue issue, Tree tree, DeclarationType t
118119
PythonQuickFix.Builder quickFix = PythonQuickFix.newQuickFix("Add docstring");
119120

120121
if (type == DeclarationType.MODULE) {
121-
quickFix.addTextEdit(TextEditUtils.insertAtPosition(1, 0, EMPTY_DOCSTRING));
122+
quickFix.addTextEdit(TextEditUtils.insertAtPosition(new PythonLine(1), 0, EMPTY_DOCSTRING));
122123
} else if (type == DeclarationType.CLASS) {
123124
ClassDef classDef = (ClassDef) tree;
124125
quickFix.addTextEdit(TextEditUtils.insertLineAfter(classDef.colon(), classDef.body(), EMPTY_DOCSTRING));

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@ private static PythonTextEdit createRemoveStatementTextEdit(List<Statement> stat
6969
var removeFrom = TreeUtils.getTreeSeparatorOrLastToken(previous);
7070
var removeTo = TreeUtils.getTreeSeparatorOrLastToken(toRemove);
7171
return TextEditUtils.removeRange(
72-
removeFrom.line(),
72+
removeFrom.pythonLine(),
7373
removeFrom.column(),
74-
removeTo.line(),
74+
removeTo.pythonLine(),
7575
removeTo.column());
7676
} else {
7777
return TextEditUtils.removeUntil(toRemove, statements.get(removeIndex + 1));

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public void initialize(Context context) {
6161
String comment = commentToken.value();
6262
if (!pattern.matcher(comment).matches()) {
6363
var issue = ctx.addIssue(commentToken, MESSAGE);
64-
String line = getLines(ctx).get(commentToken.pythonLine() - 1);
64+
String line = getLines(ctx).get(commentToken.pythonLine().line() - 1);
6565
addQuickFix(issue, commentToken, line);
6666
}
6767
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.sonar.check.Rule;
2222
import org.sonar.plugins.python.api.IssueLocation;
2323
import org.sonar.plugins.python.api.PythonCheck;
24+
import org.sonar.plugins.python.api.PythonLine;
2425
import org.sonar.plugins.python.api.PythonVisitorContext;
2526
import org.sonar.plugins.python.api.quickfix.PythonQuickFix;
2627
import org.sonar.python.quickfix.TextEditUtils;
@@ -37,11 +38,11 @@ public void scanFile(PythonVisitorContext ctx) {
3738
for (int i = 0; i < lines.length; i++) {
3839
Matcher matcher = TRAILING_WS.matcher(lines[i]);
3940
if (matcher.find()) {
40-
int lineNumber = i + 1;
41-
PreciseIssue issue = new PreciseIssue(this, IssueLocation.atLineLevel(MESSAGE, lineNumber, ctx.pythonFile()));
41+
var pythonLineNumber = new PythonLine(i + 1);
42+
PreciseIssue issue = new PreciseIssue(this, IssueLocation.atLineLevel(MESSAGE, pythonLineNumber.line(), ctx.pythonFile()));
4243

4344
issue.addQuickFix(PythonQuickFix.newQuickFix("Remove trailing whitespaces")
44-
.addTextEdit(TextEditUtils.removeRange(lineNumber, matcher.start(), lineNumber, matcher.end()))
45+
.addTextEdit(TextEditUtils.removeRange(pythonLineNumber, matcher.start(), pythonLineNumber, matcher.end()))
4546
.build());
4647

4748
ctx.addIssue(issue);
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* SonarQube Python Plugin
3+
* Copyright (C) 2011-2024 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
* See the Sonar Source-Available License for more details.
13+
*
14+
* You should have received a copy of the Sonar Source-Available License
15+
* along with this program; if not, see https://sonarsource.com/license/ssal/
16+
*/
17+
package org.sonar.plugins.python.api;
18+
19+
public record PythonLine(int line) {
20+
}

python-frontend/src/main/java/org/sonar/plugins/python/api/tree/Token.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import com.sonar.sslr.api.TokenType;
2020
import java.util.List;
21+
import org.sonar.plugins.python.api.PythonLine;
2122

2223
public interface Token extends Tree {
2324

@@ -31,7 +32,7 @@ public interface Token extends Tree {
3132

3233
TokenType type();
3334

34-
int pythonLine();
35+
PythonLine pythonLine();
3536

3637
int pythonColumn();
3738

python-frontend/src/main/java/org/sonar/python/metrics/FileLinesVisitor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ public static Set<Integer> tokenLineNumbers(Token token) {
145145
if (!token.type().equals(PythonTokenType.DEDENT) && !token.type().equals(PythonTokenType.INDENT) && !token.type().equals(PythonTokenType.NEWLINE)) {
146146
// Handle all the lines of the token
147147
String[] tokenLines = token.value().split("\n", -1);
148-
int tokenLine = token.pythonLine();
148+
int tokenLine = token.pythonLine().line();
149149
for (int line = tokenLine; line < tokenLine + tokenLines.length; line++) {
150150
lines.add(line);
151151
}

python-frontend/src/main/java/org/sonar/python/quickfix/TextEditUtils.java

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.Collections;
2020
import java.util.LinkedList;
2121
import java.util.List;
22+
import org.sonar.plugins.python.api.PythonLine;
2223
import org.sonar.plugins.python.api.quickfix.PythonTextEdit;
2324
import org.sonar.plugins.python.api.symbols.Symbol;
2425
import org.sonar.plugins.python.api.symbols.Usage;
@@ -55,22 +56,22 @@ public static PythonTextEdit insertLineAfter(Tree tree, Tree indentReference, St
5556
}
5657

5758
private static String offset(Tree referenceTree) {
58-
return " ".repeat(referenceTree.firstToken().column());
59+
return " ".repeat(referenceTree.firstToken().pythonColumn());
5960
}
6061

6162
public static PythonTextEdit insertBefore(Tree tree, String textToInsert) {
6263
Token token = tree.firstToken();
63-
return insertAtPosition(token.line(), token.column(), textToInsert);
64+
return insertAtPosition(token.pythonLine(), token.pythonColumn(), textToInsert);
6465
}
6566

6667
public static PythonTextEdit insertAfter(Tree tree, String textToInsert) {
6768
Token token = tree.firstToken();
6869
int lengthToken = token.value().length();
69-
return insertAtPosition(token.line(), token.column() + lengthToken, textToInsert);
70+
return insertAtPosition(token.pythonLine(), token.pythonColumn() + lengthToken, textToInsert);
7071
}
7172

72-
public static PythonTextEdit insertAtPosition(int line, int column, String textToInsert) {
73-
return new PythonTextEdit(textToInsert, line, column, line, column);
73+
public static PythonTextEdit insertAtPosition(PythonLine pythonLine, int column, String textToInsert) {
74+
return new PythonTextEdit(textToInsert, pythonLine.line(), column, pythonLine.line(), column);
7475
}
7576

7677
public static PythonTextEdit replace(Tree toReplace, String replacementText) {
@@ -103,22 +104,22 @@ public static List<PythonTextEdit> shiftLeft(StatementList statementList) {
103104
public static List<PythonTextEdit> shiftLeft(Tree tree, int offset) {
104105
return TreeUtils.tokens(tree).stream()
105106
.filter(token -> token.column() >= offset)
106-
.map(Token::line)
107+
.map(Token::pythonLine)
107108
.distinct()
108109
.map(line -> removeRange(line, 0, line, offset))
109110
.toList();
110111
}
111112

112-
public static PythonTextEdit removeRange(int startLine, int startColumn, int endLine, int endColumn) {
113-
return new PythonTextEdit("", startLine, startColumn, endLine, endColumn);
113+
public static PythonTextEdit removeRange(PythonLine startLine, int startColumn, PythonLine endLine, int endColumn) {
114+
return new PythonTextEdit("", startLine.line(), startColumn, endLine.line(), endColumn);
114115
}
115116

116117
/**
117118
* Remove range including the start token until the beginning of the end tree's first token.
118119
* This is useful to remove and shift multiple statement over multiple lines.
119120
*/
120121
public static PythonTextEdit removeUntil(Tree start, Tree until) {
121-
return removeRange(start.firstToken().line(), start.firstToken().column(), until.firstToken().line(), until.firstToken().column());
122+
return removeRange(start.firstToken().pythonLine(), start.firstToken().column(), until.firstToken().pythonLine(), until.firstToken().column());
122123
}
123124

124125
public static PythonTextEdit removeStatement(Statement statement) {
@@ -143,17 +144,17 @@ public static PythonTextEdit removeStatement(Statement statement) {
143144
// Statement is first on the line or between at least two statements
144145
// Remove from first token to last toke of statement
145146
Token firstNextToken = next.firstToken();
146-
return removeRange(firstTokenOfStmt.line(), firstTokenOfStmt.column(), firstNextToken.line(), firstNextToken.column());
147+
return removeRange(firstTokenOfStmt.pythonLine(), firstTokenOfStmt.column(), firstNextToken.pythonLine(), firstNextToken.column());
147148
} else if (hasPreviousSiblingOnLine) {
148149
// Statement is last on the line and has one or more previous statement on the line
149150
// Remove from last token or separator of previous statement to avoid trailing white spaces
150151
// Keep the line break to ensure elements on the next line don't get pushed to the current line
151152
Token lastPreviousToken = TreeUtils.getTreeSeparatorOrLastToken(previous);
152-
return removeRange(lastPreviousToken.line(), getEndColumn(lastPreviousToken), lastPreviousToken.line(), getEndColumn(lastTokenOfStmt) - 1);
153+
return removeRange(lastPreviousToken.pythonLine(), getEndColumn(lastPreviousToken), lastPreviousToken.pythonLine(), getEndColumn(lastTokenOfStmt) - 1);
153154
} else {
154155
// Statement is single on the line
155156
// Remove the entire line including indent and line break
156-
return removeRange(firstTokenOfStmt.line(), 0, lastTokenOfStmt.line(), getEndColumn(lastTokenOfStmt));
157+
return removeRange(firstTokenOfStmt.pythonLine(), 0, lastTokenOfStmt.pythonLine(), getEndColumn(lastTokenOfStmt));
157158
}
158159
}
159160

python-frontend/src/main/java/org/sonar/python/tree/TokenImpl.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.Collections;
2121
import java.util.List;
2222
import java.util.stream.Collectors;
23+
import org.sonar.plugins.python.api.PythonLine;
2324
import org.sonar.plugins.python.api.tree.Token;
2425
import org.sonar.plugins.python.api.tree.Tree;
2526
import org.sonar.plugins.python.api.tree.TreeVisitor;
@@ -56,7 +57,7 @@ public String value() {
5657

5758
@Override
5859
public int line() {
59-
return line != null ? line : pythonLine();
60+
return line != null ? line : pythonLine().line();
6061
}
6162

6263
@Override
@@ -65,8 +66,8 @@ public int column() {
6566
}
6667

6768
@Override
68-
public int pythonLine() {
69-
return token.getLine();
69+
public PythonLine pythonLine() {
70+
return new PythonLine(token.getLine());
7071
}
7172

7273
@Override

0 commit comments

Comments
 (0)