Skip to content

Commit

Permalink
SONARJAVA-5080 S1659: Quickfix breaks syntax when multiple arrays are…
Browse files Browse the repository at this point in the history
… declared
  • Loading branch information
leonardo-pilastri-sonarsource authored Sep 13, 2024
1 parent 3acc73e commit 2d5d294
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,29 @@
package checks;

import java.util.Collections;

class OneDeclarationPerLineCheckSample {

int i = 0, j[] = null, k[][] = new int[0][0]; // Noncompliant [[quickfixes=qf_indentation1]]
// ^
// fix@qf_indentation1 {{Declare on separated lines}}
// edit@qf_indentation1 [[sc=12;ec=17]] {{;\n int[] j }}
// edit@qf_indentation1 [[sc=24;ec=31]] {{;\n int[][] k }}

int one, two; // Noncompliant
// ^^^

int no; int spaceBefore; // Noncompliant [[quickfixes=qf_indentation2]]
// ^^^^^^^^^^^
// ^^^^^^^^^^^
// fix@qf_indentation2 {{Declare on separated lines}}
// edit@qf_indentation2 [[sc=10;ec=11]] {{\n }}

int arr1[] = null, arr2[] = null; // Noncompliant [[quickfixes=qf_indentation3]]
// ^^^^
// fix@qf_indentation3 {{Declare on separated lines}}
// edit@qf_indentation3 [[sc=20;ec=28]] {{;\n int[] arr2 }}

public int three, four; // Noncompliant [[quickfixes=qf_indentation4]]
// ^^^^
// fix@qf_indentation4 {{Declare on separated lines}}
// edit@qf_indentation4 [[sc=19;ec=21]] {{;\n public int }}

}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
import org.sonar.plugins.java.api.tree.CaseGroupTree;
import org.sonar.plugins.java.api.tree.ClassTree;
import org.sonar.plugins.java.api.tree.IdentifierTree;
import org.sonar.plugins.java.api.tree.ModifiersTree;
import org.sonar.plugins.java.api.tree.SyntaxToken;
import org.sonar.plugins.java.api.tree.Tree;
import org.sonar.plugins.java.api.tree.Tree.Kind;
Expand Down Expand Up @@ -120,20 +119,34 @@ private JavaQuickFix getQuickFixes(List<VariableTree> nodesToReport) {

private JavaTextEdit getEditForVariable(VariableTree variableTree, SyntaxToken previousToken, String indentationOfLine) {
if (",".equals(previousToken.text())) {
return JavaTextEdit.replaceTextSpan(textSpanBetween(previousToken, true, variableTree.simpleName(), false),
String.format(";\n%s%s ", indentationOfLine, modifiersAndType(variableTree)));
boolean isTypeFragmented = isTypeFragmented(variableTree);
Tree endTree = isTypeFragmented ? variableTree.type().lastToken() : variableTree.simpleName();
return JavaTextEdit.replaceTextSpan(
textSpanBetween(previousToken, true, endTree, isTypeFragmented),
String.format(";\n%s%s ", indentationOfLine, modifiersAndType(variableTree, isTypeFragmented)));
} else {
return JavaTextEdit.replaceTextSpan(textSpanBetween(previousToken, false, variableTree, false),
String.format("\n%s", indentationOfLine));
}
}

private String modifiersAndType(VariableTree variableTree) {
ModifiersTree modifiers = variableTree.modifiers();
if (modifiers.isEmpty()) {
return QuickFixHelper.contentForTree(variableTree.type(), context);
private String modifiersAndType(VariableTree variableTree, boolean isTypeFragmented) {
StringBuilder sb = new StringBuilder();
if (!variableTree.modifiers().isEmpty()) {
sb.append(QuickFixHelper.contentOfTreeTokens(variableTree.modifiers(), context));
sb.append(" ");
}
return QuickFixHelper.contentForRange(variableTree.modifiers().firstToken(), variableTree.type().lastToken(), context);
sb.append(QuickFixHelper.contentOfTreeTokens(variableTree.type(), context));
sb.append(" ");
if (isTypeFragmented) {
sb.append(variableTree.simpleName().name());
}
return sb.toString();
}

private static boolean isTypeFragmented(VariableTree variableTree) {
var lastToken = variableTree.type().lastToken();
return lastToken.range().end().isAfter(variableTree.simpleName().identifierToken().range().start());
}

private String indentationOfLine(Tree tree) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,16 @@ public static String contentForRange(SyntaxToken firstToken, SyntaxToken lastTok
return sb.toString();
}

public static String contentOfTreeTokens(Tree tree, JavaFileScannerContext context) {
StringBuilder sb = new StringBuilder();
JavaTree javaTree = (JavaTree) tree;
for (SyntaxToken st : javaTree.allTokens()) {
sb.append(contentForRange(st, st, context));
}
return sb.toString();
}


/**
* Check if a given type "requiredType" is available in the current "context". Imports are cached to not have to recompute order all the time.
* A new import is required if the given type:
Expand Down
11 changes: 11 additions & 0 deletions java-frontend/src/main/java/org/sonar/java/model/JavaTree.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,17 @@ public abstract class JavaTree implements Tree {

private List<Tree> children;

public List<SyntaxToken> allTokens() {
List<SyntaxToken> list = new ArrayList<>();
if (this instanceof SyntaxToken st) {
list.add(st);
} else {
for (Tree tree : children) {
list.addAll(((JavaTree) tree).allTokens());
}
}
return list;
}

@Override
@Nullable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@

class JavaTreeModelTest {

@Test
void test_all_tokens() {
JavaTree tree = (JavaTree) JParserTestUtils.parse("class A { void m() { int i = 0; } }");
assertThat(tree.allTokens()).hasSize(16);
}

@Test
void line_of_tree() {
CompilationUnitTree empty = compilationUnit("");
Expand Down

0 comments on commit 2d5d294

Please sign in to comment.