diff --git a/src/main/java/spoon/support/compiler/jdt/ParentExiter.java b/src/main/java/spoon/support/compiler/jdt/ParentExiter.java index a430d819495..d65d783f72d 100644 --- a/src/main/java/spoon/support/compiler/jdt/ParentExiter.java +++ b/src/main/java/spoon/support/compiler/jdt/ParentExiter.java @@ -445,10 +445,11 @@ public void visitCtBinaryOperator(CtBinaryOperator operator) { } else if (jdtTreeBuilder.getContextBuilder().stack.peek().node instanceof StringLiteralConcatenation) { CtBinaryOperator op = operator.getFactory().Core().createBinaryOperator(); op.setKind(BinaryOperatorKind.PLUS); - op.setLeftHandOperand(operator.getRightHandOperand()); - op.setRightHandOperand((CtExpression) child); + op.setLeftHandOperand(operator.getLeftHandOperand()); + op.setRightHandOperand(operator.getRightHandOperand()); op.setType((CtTypeReference) operator.getFactory().Type().STRING.clone()); - operator.setRightHandOperand(op); + operator.setLeftHandOperand(op); + operator.setRightHandOperand(((CtExpression) child)); int[] lineSeparatorPositions = jdtTreeBuilder.getContextBuilder().getCompilationUnitLineSeparatorPositions(); SourcePosition leftPosition = op.getLeftHandOperand().getPosition(); SourcePosition rightPosition = op.getRightHandOperand().getPosition(); diff --git a/src/test/java/spoon/reflect/ast/AstCheckerTest.java b/src/test/java/spoon/reflect/ast/AstCheckerTest.java index bc7e2f284bf..c085dc15f2f 100644 --- a/src/test/java/spoon/reflect/ast/AstCheckerTest.java +++ b/src/test/java/spoon/reflect/ast/AstCheckerTest.java @@ -22,13 +22,14 @@ import spoon.reflect.declaration.CtClass; import spoon.reflect.reference.CtExecutableReference; import spoon.support.modelobs.FineModelChangeListener; +import spoon.reflect.code.CtBinaryOperator; import spoon.reflect.code.CtBlock; +import spoon.reflect.code.CtExpression; import spoon.reflect.code.CtFieldRead; import spoon.reflect.code.CtIf; import spoon.reflect.code.CtInvocation; import spoon.reflect.code.CtReturn; import spoon.reflect.code.CtThrow; -import spoon.reflect.declaration.CtExecutable; import spoon.reflect.declaration.CtMethod; import spoon.reflect.declaration.ModifierKind; import spoon.reflect.factory.Factory; @@ -50,10 +51,35 @@ import java.util.Set; import java.util.stream.Collectors; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; import static org.junit.jupiter.api.Assertions.assertEquals; public class AstCheckerTest { + @Test + void leftOperandShouldBeGivenPriorityForStoringTheNestedOperator_stringLiteralConcatenation() { + // contract: string concatenation should be left associative. + // arrange + Launcher launcher = new Launcher(); + Factory factory = launcher.getFactory(); + CtClass classContainingStringLiteral = Launcher.parseClass("class A { private String x = \"a\" + \"b\" + \"c\" }"); + + // act + CtBinaryOperator binaryOperator = classContainingStringLiteral + .filterChildren(element -> element instanceof CtBinaryOperator) + .first(); + + // assert + CtExpression firstOperand = ((CtBinaryOperator)binaryOperator.getLeftHandOperand()).getLeftHandOperand(); + CtExpression secondOperand = ((CtBinaryOperator)binaryOperator.getLeftHandOperand()).getRightHandOperand(); + CtExpression thirdOperand = binaryOperator.getRightHandOperand(); + + assertThat(firstOperand, equalTo(factory.createLiteral("a"))); + assertThat(secondOperand, equalTo(factory.createLiteral("b"))); + assertThat(thirdOperand, equalTo(factory.createLiteral("c"))); + } + @Test public void testExecutableReference() { diff --git a/src/test/java/spoon/reflect/visitor/DefaultJavaPrettyPrinterTest.java b/src/test/java/spoon/reflect/visitor/DefaultJavaPrettyPrinterTest.java index e5b509b90c8..95d4c98375c 100644 --- a/src/test/java/spoon/reflect/visitor/DefaultJavaPrettyPrinterTest.java +++ b/src/test/java/spoon/reflect/visitor/DefaultJavaPrettyPrinterTest.java @@ -57,7 +57,8 @@ public void testParenOptimizationCorrectlyPrintsParenthesesForExpressions(String @ValueSource(strings = { "int sum = 1 + 2 + 3", "java.lang.String s = \"Sum: \" + (1 + 2)", - "java.lang.String s = \"Sum: \" + 1 + 2" + "java.lang.String s = \"Sum: \" + 1 + 2", + "java.lang.System.out.println(\"1\" + \"2\" + \"3\" + \"4\")" }) public void testParenOptimizationCorrectlyPrintsParenthesesForStatements(String rawStatement) { // contract: When input expressions as part of statements are minimally parenthesized, diff --git a/src/test/java/spoon/test/comment/CommentTest.java b/src/test/java/spoon/test/comment/CommentTest.java index 946ebab0cc9..afb5e767084 100644 --- a/src/test/java/spoon/test/comment/CommentTest.java +++ b/src/test/java/spoon/test/comment/CommentTest.java @@ -465,9 +465,9 @@ public void testInLineComment() { CtLocalVariable ctLocalVariableString = m1.getBody().getStatement(12); - assertEquals(createFakeComment(f, "comment multi line string"), ((CtBinaryOperator) ((CtBinaryOperator) ctLocalVariableString.getDefaultExpression()).getRightHandOperand()).getLeftHandOperand().getComments().get(0)); - assertEquals("\"\" + (\"\"// comment multi line string" + newLine - + " + \"\")", ctLocalVariableString.getDefaultExpression().toString()); + assertEquals(createFakeComment(f, "comment multi line string"), (((CtBinaryOperator) ctLocalVariableString.getDefaultExpression()).getLeftHandOperand()).getComments().get(0)); + assertEquals("(\"\" + \"\")// comment multi line string" + newLine + + " + \"\"", ctLocalVariableString.getDefaultExpression().toString()); ctLocalVariable1 = m1.getBody().getStatement(13); assertEquals("boolean c = (i == 1) ? // comment before then boolean CtConditional" + newLine