Skip to content

Commit

Permalink
SONARPY-2407 Refactor Token#line and pythonLine to avoid mixing the two
Browse files Browse the repository at this point in the history
  • Loading branch information
ghislainpiot committed Dec 5, 2024
1 parent f251712 commit 54281fb
Show file tree
Hide file tree
Showing 11 changed files with 54 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.List;
import java.util.Set;
import org.sonar.check.Rule;
import org.sonar.plugins.python.api.PythonLine;
import org.sonar.plugins.python.api.PythonSubscriptionCheck;
import org.sonar.plugins.python.api.SubscriptionContext;
import org.sonar.plugins.python.api.quickfix.PythonQuickFix;
Expand Down Expand Up @@ -130,7 +131,7 @@ private PythonQuickFix createQuickFix(BinaryExpression binaryExpression, String
quickFix.addTextEdit(TextEditUtils.replace(binaryExpression, quickFixTextWithModuleName));

if (MATH_MODULE.equals(isCloseModuleName) && !isMathImported) {
quickFix.addTextEdit(TextEditUtils.insertAtPosition(0, 0, "import math\n"));
quickFix.addTextEdit(TextEditUtils.insertAtPosition(new PythonLine(0), 0, "import math\n"));
}

return quickFix.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

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

if (type == DeclarationType.MODULE) {
quickFix.addTextEdit(TextEditUtils.insertAtPosition(1, 0, EMPTY_DOCSTRING));
quickFix.addTextEdit(TextEditUtils.insertAtPosition(new PythonLine(1), 0, EMPTY_DOCSTRING));
} else if (type == DeclarationType.CLASS) {
ClassDef classDef = (ClassDef) tree;
quickFix.addTextEdit(TextEditUtils.insertLineAfter(classDef.colon(), classDef.body(), EMPTY_DOCSTRING));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ private static PythonTextEdit createRemoveStatementTextEdit(List<Statement> stat
var removeFrom = TreeUtils.getTreeSeparatorOrLastToken(previous);
var removeTo = TreeUtils.getTreeSeparatorOrLastToken(toRemove);
return TextEditUtils.removeRange(
removeFrom.line(),
removeFrom.pythonLine(),
removeFrom.column(),
removeTo.line(),
removeTo.pythonLine(),
removeTo.column());
} else {
return TextEditUtils.removeUntil(toRemove, statements.get(removeIndex + 1));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public void initialize(Context context) {
String comment = commentToken.value();
if (!pattern.matcher(comment).matches()) {
var issue = ctx.addIssue(commentToken, MESSAGE);
String line = getLines(ctx).get(commentToken.pythonLine() - 1);
String line = getLines(ctx).get(commentToken.pythonLine().line() - 1);
addQuickFix(issue, commentToken, line);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.sonar.check.Rule;
import org.sonar.plugins.python.api.IssueLocation;
import org.sonar.plugins.python.api.PythonCheck;
import org.sonar.plugins.python.api.PythonLine;
import org.sonar.plugins.python.api.PythonVisitorContext;
import org.sonar.plugins.python.api.quickfix.PythonQuickFix;
import org.sonar.python.quickfix.TextEditUtils;
Expand All @@ -37,11 +38,11 @@ public void scanFile(PythonVisitorContext ctx) {
for (int i = 0; i < lines.length; i++) {
Matcher matcher = TRAILING_WS.matcher(lines[i]);
if (matcher.find()) {
int lineNumber = i + 1;
PreciseIssue issue = new PreciseIssue(this, IssueLocation.atLineLevel(MESSAGE, lineNumber, ctx.pythonFile()));
var pythonLineNumber = new PythonLine(i + 1);
PreciseIssue issue = new PreciseIssue(this, IssueLocation.atLineLevel(MESSAGE, pythonLineNumber.line(), ctx.pythonFile()));

issue.addQuickFix(PythonQuickFix.newQuickFix("Remove trailing whitespaces")
.addTextEdit(TextEditUtils.removeRange(lineNumber, matcher.start(), lineNumber, matcher.end()))
.addTextEdit(TextEditUtils.removeRange(pythonLineNumber, matcher.start(), pythonLineNumber, matcher.end()))
.build());

ctx.addIssue(issue);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* SonarQube Python Plugin
* Copyright (C) 2011-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the Sonar Source-Available License for more details.
*
* You should have received a copy of the Sonar Source-Available License
* along with this program; if not, see https://sonarsource.com/license/ssal/
*/
package org.sonar.plugins.python.api;

public record PythonLine(int line) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import com.sonar.sslr.api.TokenType;
import java.util.List;
import org.sonar.plugins.python.api.PythonLine;

public interface Token extends Tree {

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

TokenType type();

int pythonLine();
PythonLine pythonLine();

int pythonColumn();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ public static Set<Integer> tokenLineNumbers(Token token) {
if (!token.type().equals(PythonTokenType.DEDENT) && !token.type().equals(PythonTokenType.INDENT) && !token.type().equals(PythonTokenType.NEWLINE)) {
// Handle all the lines of the token
String[] tokenLines = token.value().split("\n", -1);
int tokenLine = token.pythonLine();
int tokenLine = token.pythonLine().line();
for (int line = tokenLine; line < tokenLine + tokenLines.length; line++) {
lines.add(line);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.sonar.plugins.python.api.PythonLine;
import org.sonar.plugins.python.api.quickfix.PythonTextEdit;
import org.sonar.plugins.python.api.symbols.Symbol;
import org.sonar.plugins.python.api.symbols.Usage;
Expand Down Expand Up @@ -55,22 +56,22 @@ public static PythonTextEdit insertLineAfter(Tree tree, Tree indentReference, St
}

private static String offset(Tree referenceTree) {
return " ".repeat(referenceTree.firstToken().column());
return " ".repeat(referenceTree.firstToken().pythonColumn());
}

public static PythonTextEdit insertBefore(Tree tree, String textToInsert) {
Token token = tree.firstToken();
return insertAtPosition(token.line(), token.column(), textToInsert);
return insertAtPosition(token.pythonLine(), token.pythonColumn(), textToInsert);
}

public static PythonTextEdit insertAfter(Tree tree, String textToInsert) {
Token token = tree.firstToken();
int lengthToken = token.value().length();
return insertAtPosition(token.line(), token.column() + lengthToken, textToInsert);
return insertAtPosition(token.pythonLine(), token.pythonColumn() + lengthToken, textToInsert);
}

public static PythonTextEdit insertAtPosition(int line, int column, String textToInsert) {
return new PythonTextEdit(textToInsert, line, column, line, column);
public static PythonTextEdit insertAtPosition(PythonLine pythonLine, int column, String textToInsert) {
return new PythonTextEdit(textToInsert, pythonLine.line(), column, pythonLine.line(), column);
}

public static PythonTextEdit replace(Tree toReplace, String replacementText) {
Expand Down Expand Up @@ -103,22 +104,22 @@ public static List<PythonTextEdit> shiftLeft(StatementList statementList) {
public static List<PythonTextEdit> shiftLeft(Tree tree, int offset) {
return TreeUtils.tokens(tree).stream()
.filter(token -> token.column() >= offset)
.map(Token::line)
.map(Token::pythonLine)
.distinct()
.map(line -> removeRange(line, 0, line, offset))
.toList();
}

public static PythonTextEdit removeRange(int startLine, int startColumn, int endLine, int endColumn) {
return new PythonTextEdit("", startLine, startColumn, endLine, endColumn);
public static PythonTextEdit removeRange(PythonLine startLine, int startColumn, PythonLine endLine, int endColumn) {
return new PythonTextEdit("", startLine.line(), startColumn, endLine.line(), endColumn);
}

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

public static PythonTextEdit removeStatement(Statement statement) {
Expand All @@ -143,17 +144,17 @@ public static PythonTextEdit removeStatement(Statement statement) {
// Statement is first on the line or between at least two statements
// Remove from first token to last toke of statement
Token firstNextToken = next.firstToken();
return removeRange(firstTokenOfStmt.line(), firstTokenOfStmt.column(), firstNextToken.line(), firstNextToken.column());
return removeRange(firstTokenOfStmt.pythonLine(), firstTokenOfStmt.column(), firstNextToken.pythonLine(), firstNextToken.column());
} else if (hasPreviousSiblingOnLine) {
// Statement is last on the line and has one or more previous statement on the line
// Remove from last token or separator of previous statement to avoid trailing white spaces
// Keep the line break to ensure elements on the next line don't get pushed to the current line
Token lastPreviousToken = TreeUtils.getTreeSeparatorOrLastToken(previous);
return removeRange(lastPreviousToken.line(), getEndColumn(lastPreviousToken), lastPreviousToken.line(), getEndColumn(lastTokenOfStmt) - 1);
return removeRange(lastPreviousToken.pythonLine(), getEndColumn(lastPreviousToken), lastPreviousToken.pythonLine(), getEndColumn(lastTokenOfStmt) - 1);
} else {
// Statement is single on the line
// Remove the entire line including indent and line break
return removeRange(firstTokenOfStmt.line(), 0, lastTokenOfStmt.line(), getEndColumn(lastTokenOfStmt));
return removeRange(firstTokenOfStmt.pythonLine(), 0, lastTokenOfStmt.pythonLine(), getEndColumn(lastTokenOfStmt));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.sonar.plugins.python.api.PythonLine;
import org.sonar.plugins.python.api.tree.Token;
import org.sonar.plugins.python.api.tree.Tree;
import org.sonar.plugins.python.api.tree.TreeVisitor;
Expand Down Expand Up @@ -56,7 +57,7 @@ public String value() {

@Override
public int line() {
return line != null ? line : pythonLine();
return line != null ? line : pythonLine().line();
}

@Override
Expand All @@ -65,8 +66,8 @@ public int column() {
}

@Override
public int pythonLine() {
return token.getLine();
public PythonLine pythonLine() {
return new PythonLine(token.getLine());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.List;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.sonar.plugins.python.api.PythonLine;
import org.sonar.plugins.python.api.quickfix.PythonTextEdit;
import org.sonar.plugins.python.api.tree.CallExpression;
import org.sonar.plugins.python.api.tree.FileInput;
Expand Down Expand Up @@ -237,7 +238,9 @@ private static Token mockToken(String value, int line, int column) {

when(token.value()).thenReturn(value);
when(token.line()).thenReturn(line);
when(token.pythonLine()).thenReturn(new PythonLine(line));
when(token.column()).thenReturn(column);
when(token.pythonColumn()).thenReturn(column);

return token;
}
Expand Down

0 comments on commit 54281fb

Please sign in to comment.