diff --git a/java-checks-test-sources/default/src/main/java/checks/OneDeclarationPerLineCheckSample.java b/java-checks-test-sources/default/src/main/java/checks/OneDeclarationPerLineCheckSample.java index 3b970193f10..a0d611589b4 100644 --- a/java-checks-test-sources/default/src/main/java/checks/OneDeclarationPerLineCheckSample.java +++ b/java-checks-test-sources/default/src/main/java/checks/OneDeclarationPerLineCheckSample.java @@ -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 }} + } diff --git a/java-checks/src/main/java/org/sonar/java/checks/OneDeclarationPerLineCheck.java b/java-checks/src/main/java/org/sonar/java/checks/OneDeclarationPerLineCheck.java index d1951c093db..6e0fd854607 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/OneDeclarationPerLineCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/OneDeclarationPerLineCheck.java @@ -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; @@ -120,20 +119,34 @@ private JavaQuickFix getQuickFixes(List 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) { diff --git a/java-checks/src/main/java/org/sonar/java/checks/helpers/QuickFixHelper.java b/java-checks/src/main/java/org/sonar/java/checks/helpers/QuickFixHelper.java index 9a1a81af79f..2ecee7bf47d 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/helpers/QuickFixHelper.java +++ b/java-checks/src/main/java/org/sonar/java/checks/helpers/QuickFixHelper.java @@ -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: diff --git a/java-frontend/src/main/java/org/sonar/java/model/JavaTree.java b/java-frontend/src/main/java/org/sonar/java/model/JavaTree.java index 725ff56fa2d..d860557dec7 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/JavaTree.java +++ b/java-frontend/src/main/java/org/sonar/java/model/JavaTree.java @@ -72,6 +72,17 @@ public abstract class JavaTree implements Tree { private List children; + public List allTokens() { + List 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 diff --git a/java-frontend/src/test/java/org/sonar/java/model/JavaTreeModelTest.java b/java-frontend/src/test/java/org/sonar/java/model/JavaTreeModelTest.java index 0da9cf586e2..cb7b1b5da3d 100644 --- a/java-frontend/src/test/java/org/sonar/java/model/JavaTreeModelTest.java +++ b/java-frontend/src/test/java/org/sonar/java/model/JavaTreeModelTest.java @@ -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("");