javaparser icon indicating copy to clipboard operation
javaparser copied to clipboard

Incorrect indentation by LexicalPreservingPrinter when removing/adding first statement of a block

Open andrecsilva opened this issue 3 years ago • 8 comments

Using JavaParser 3.24.7

Consider the following MWE and its output:

public class LexicalBugMWE{

	public static void main(String[] args) {
		CompilationUnit cu = StaticJavaParser.parse("class A {\n"
				+ "  void foo() {\n"
				+ "    int first = 1;\n"
				+ "    int second = 2;\n"
				+ "    int third = 3;\n"
				+ "    int fourth = 4;\n"
				+ "  }\n"
				+ "}"
				);
		LexicalPreservingPrinter.setup(cu);
		var block = cu.findAll(BlockStmt.class).get(0);
		var newStmt = new ExpressionStmt(new MethodCallExpr("foo"));
		//block.addStatement(0,newStmt);
		block.addStatement(1,newStmt);
		block.getStatement(0).remove();
		System.out.println(LexicalPreservingPrinter.print(cu));
	}
}

Output:

class A {
  void foo() {
    foo();
        int second = 2;
    int third = 3;
    int fourth = 4;
  }
}

Curiously, removing before adding outputs correctly.

If we simply add newStmt at the start instead, we get:

class A {
  void foo() {
  foo();
  int first = 1;
    int second = 2;
    int third = 3;
    int fourth = 4;
  }
}

This seems to be the related with #3677. I'm willing to spend some time on this. Any (non-obvious) idea where to start looking?

andrecsilva avatar Nov 03 '22 11:11 andrecsilva

I'm willing to spend some time on this. Any (non-obvious) idea where to start looking?

Difference.apply(..) It is often here in this part of the code that this type of problem occurs. The problem is that it has become very complex over time and it is poorly documented.

jlerbsc avatar Nov 03 '22 14:11 jlerbsc

@ftomassetti any clues here?

nahsra avatar Nov 04 '22 14:11 nahsra

@ftomassetti has not responded to messages for a long time. He has moved on. Is my answer irrelevant?

jlerbsc avatar Nov 04 '22 14:11 jlerbsc

In general, this kind of problem occurs because indentation should be derived, when possible, from context. When it is not possible instead, the standard indentation should be used.

By deriving from context, I mean that, ideally, when adding a statement within a block, the same indentation as preceding blocks should be used. If an element is instead the first in a block, then the indentation of the container should be taken, and the standard extra indentation (4 spaces, I think) should be added.

In this example, the indentation generated (8 spaces) seems to be the "standard/default" one because the statement is within a method, within a class, and each block adds 4 spaces.

Now, the problem could well be in the logic that identify the contextual indentation being used. So fixing that logic could be the first way to solve this problem.

A different way to tackle this could be adding a method to specify how much standard indentation to use instead (2 spaces instead of 4 in this case). In this specific case, I think both approaches should work, and the second one would be simpler and more reliable. The only drawback is that it would not help in cases in which the code is inconsistently indented. I.e., if each method is intended to use a different amount of spaces then only the first solution will help with that.

Other issues could appear when missing tabs and spaces.

ftomassetti avatar Nov 07 '22 12:11 ftomassetti

@jlerbsc hello, i use 3.26.2, the problem still exists. when simply add newStmt at the start instead, we get:

class A {
  void foo() {
  foo();
  int first = 1;
    int second = 2;
    int third = 3;
    int fourth = 4;
  }
}

the indentation of foo() and int first = 1is Incorrect.

ChenKangQiang avatar Oct 16 '24 09:10 ChenKangQiang

@jlerbsc Can I ask when is this issue planned to be resolved.

ChenKangQiang avatar Oct 17 '24 09:10 ChenKangQiang

@jlerbsc bro. How is it going?

ChenKangQiang avatar Oct 28 '24 13:10 ChenKangQiang

There are no plans to correct this issue.

jlerbsc avatar Oct 28 '24 13:10 jlerbsc