From d0976ca4c91802663b8e452be9fc1808ecfe303f Mon Sep 17 00:00:00 2001 From: Stephan Herrmann Date: Mon, 15 Jul 2024 23:24:59 +0200 Subject: [PATCH] More tests (from issues #2464, #2468 and #2466 (variation) + improve error messages for misplaced constructor calls + illegally nested + duplicate --- .../eclipse/jdt/core/compiler/IProblem.java | 6 + .../compiler/ast/ExplicitConstructorCall.java | 15 ++- .../compiler/problem/ProblemReporter.java | 8 ++ .../compiler/problem/messages.properties | 1 + .../regression/CompilerInvocationTests.java | 2 + .../regression/SuperAfterStatementsTest.java | 106 +++++++++++++++++- 6 files changed, 129 insertions(+), 9 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/IProblem.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/IProblem.java index 76fdf1433cf..994334c640a 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/IProblem.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/IProblem.java @@ -2675,4 +2675,10 @@ public interface IProblem { * @noreference preview feature */ int AssignFieldWithInitializerInEarlyConstructionContext = PreviewRelated + 2030; + + /** + * @since 3.39 + * @noreference preview feature + */ + int ConstructorCallNotAllowedHere = PreviewRelated + 2031; } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java index a88e2936b68..55d4209d640 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java @@ -39,6 +39,8 @@ import static org.eclipse.jdt.internal.compiler.ast.ExpressionContext.INVOCATION_CONTEXT; +import java.util.Arrays; + import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.codegen.CodeStream; @@ -325,9 +327,10 @@ public void resolve(BlockScope scope) { if (methodDeclaration == null || !methodDeclaration.isConstructor()) { hasError = true; } else { + // is it the first constructor call? ExplicitConstructorCall constructorCall = constructorDeclaration.constructorCall; if (constructorCall == null) { - constructorCall = constructorDeclaration.getLateConstructorCall(); + constructorCall = constructorDeclaration.getLateConstructorCall(); // JEP 482 } if (constructorCall != null && constructorCall != this) { hasError = true; @@ -335,10 +338,12 @@ public void resolve(BlockScope scope) { } if (hasError) { if (!(methodDeclaration instanceof CompactConstructorDeclaration)) {// already flagged for CCD - ExplicitConstructorCall lateConstructorCall = constructorDeclaration.getLateConstructorCall(); - if (lateConstructorCall != null && lateConstructorCall != this - && JavaFeature.FLEXIBLE_CONSTRUCTOR_BODIES.isSupported(scope.compilerOptions())) { - scope.problemReporter().duplicateExplicitConstructorCall(this); + if (JavaFeature.FLEXIBLE_CONSTRUCTOR_BODIES.isSupported(scope.compilerOptions())) { + boolean isTopLevel = Arrays.stream(constructorDeclaration.statements).anyMatch(this::equals); + if (isTopLevel) + scope.problemReporter().duplicateExplicitConstructorCall(this); + else // otherwise it's illegally nested in some control structure: + scope.problemReporter().misplacedConstructorCall(this); } else { scope.problemReporter().invalidExplicitConstructorCall(this); } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java index bd42de84d80..31dab1b028a 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java @@ -4248,6 +4248,14 @@ public void duplicateExplicitConstructorCall(ASTNode location) { location.sourceStart, location.sourceEnd); } +public void misplacedConstructorCall(ASTNode location) { + this.handle( + IProblem.ConstructorCallNotAllowedHere, + NoArgument, + NoArgument, + location.sourceStart, + location.sourceEnd); +} public void invalidExpressionAsStatement(Expression expression){ this.handle( IProblem.InvalidExpressionAsStatement, diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties index 770a8d4ef67..a469dca4bc9 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties @@ -1190,6 +1190,7 @@ 2028 = Constructor cannot have more than one explicit constructor call 2029 = Cannot assign field ''{0}'' from class ''{1}'' in an early construction context 2030 = Cannot assign field ''{0}'' in an early construction context, because it has an initializer +2031 = Constructor call is not allowed here ### ELABORATIONS ## Access restrictions diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java index ccca4ff9673..c5de2816e25 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java @@ -1356,6 +1356,7 @@ class ProblemAttributes { expectedProblemAttributes.put("DuplicateExplicitConstructorCall", new ProblemAttributes(CategorizedProblem.CAT_PREVIEW_RELATED)); expectedProblemAttributes.put("SuperFieldAssignInEarlyConstructionContext", new ProblemAttributes(CategorizedProblem.CAT_PREVIEW_RELATED)); expectedProblemAttributes.put("AssignFieldWithInitializerInEarlyConstructionContext", new ProblemAttributes(CategorizedProblem.CAT_PREVIEW_RELATED)); + expectedProblemAttributes.put("ConstructorCallNotAllowedHere", new ProblemAttributes(CategorizedProblem.CAT_PREVIEW_RELATED)); expectedProblemAttributes.put("NamedPatternVariablesDisallowedHere", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL)); expectedProblemAttributes.put("OperandStackExceeds64KLimit", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL)); expectedProblemAttributes.put("OperandStackSizeInappropriate", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL)); @@ -2485,6 +2486,7 @@ class ProblemAttributes { expectedProblemAttributes.put("DuplicateExplicitConstructorCall", SKIP); expectedProblemAttributes.put("SuperFieldAssignInEarlyConstructionContext", SKIP); expectedProblemAttributes.put("AssignFieldWithInitializerInEarlyConstructionContext", SKIP); + expectedProblemAttributes.put("ConstructorCallNotAllowedHere", SKIP); expectedProblemAttributes.put("NamedPatternVariablesDisallowedHere", SKIP); expectedProblemAttributes.put("OperandStackExceeds64KLimit", SKIP); expectedProblemAttributes.put("OperandStackSizeInappropriate", SKIP); diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SuperAfterStatementsTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SuperAfterStatementsTest.java index ad98d66a0d5..fe5f7f35242 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SuperAfterStatementsTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SuperAfterStatementsTest.java @@ -1280,7 +1280,7 @@ public static void main(String argv[]) { "1. ERROR in X.java (at line 10)\n" + " this(0);\n" + " ^^^^^^^^\n" + - "Constructor call must be the first statement in a constructor\n" + + "Constructor cannot have more than one explicit constructor call\n" + "----------\n" ); } @@ -1319,7 +1319,7 @@ public static void main(String argv[]) { "3. ERROR in X.java (at line 9)\n" + " this(0);\n" + " ^^^^^^^^\n" + - "Constructor call must be the first statement in a constructor\n" + + "Constructor cannot have more than one explicit constructor call\n" + "----------\n" ); } @@ -1353,7 +1353,7 @@ public static void main(String argv[]) { "2. ERROR in X.java (at line 9)\n" + " this(0);\n" + " ^^^^^^^^\n" + - "Constructor call must be the first statement in a constructor\n" + + "Constructor cannot have more than one explicit constructor call\n" + "----------\n" ); } @@ -1380,7 +1380,7 @@ public static void main(String argv[]) { "1. ERROR in X.java (at line 7)\n" + " this();\n" + " ^^^^^^^\n" + - "Constructor call must be the first statement in a constructor\n" + + "Constructor cannot have more than one explicit constructor call\n" + "----------\n" ); } @@ -2089,4 +2089,102 @@ class X { "----------\n"; runner.runNegativeTest(); } + + public void testGH2464() { + Runner runner = new Runner(false); + runner.testFiles = new String[] { + "Test.java", + """ + public class Test { + String name = "Test"; + Test() { + Runnable r = new Runnable() { + @Override + public void run() { + System.out.println(Test.this); + System.out.println(name); + } + }; + r.run(); + super(); + } + public static void main(String[] args) { + new Test(); + } + } + """ + }; + runner.expectedCompilerLog = """ + ---------- + 1. ERROR in Test.java (at line 7) + System.out.println(Test.this); + ^^^^^^^^^ + Cannot use 'Test.this' in an early construction context + ---------- + 2. ERROR in Test.java (at line 8) + System.out.println(name); + ^^^^ + Cannot read field name in an early construction context + ---------- + """; + runner.runNegativeTest(); + } + + public void testGH2468() { + Runner runner = new Runner(false); + runner.testFiles = new String[] { + "TestFlow.java", + """ + public class TestFlow { + TestFlow() {} + TestFlow(boolean f) { + if (f) + super(); + else + this(); + } + } + """ + }; + runner.expectedCompilerLog = """ + ---------- + 1. ERROR in TestFlow.java (at line 5) + super(); + ^^^^^^^^ + Constructor call is not allowed here + ---------- + 2. ERROR in TestFlow.java (at line 7) + this(); + ^^^^^^^ + Constructor call is not allowed here + ---------- + """; + runner.runNegativeTest(); + } + + public void testGH2466() { + Runner runner = new Runner(false); + runner.testFiles = new String[] { + "TestFlow.java", + """ + public class TestFlow { + TestFlow() {} + TestFlow(boolean f) { + if (f) + super(); + this(); + } + } + """ + }; + runner.expectedCompilerLog = """ + ---------- + 1. ERROR in TestFlow.java (at line 5) + super(); + ^^^^^^^^ + Constructor call is not allowed here + ---------- + """; + runner.runNegativeTest(); + } }