From b6980e6eabe08cf5076cce6dee87b5417a54c3c3 Mon Sep 17 00:00:00 2001 From: Stephan Herrmann Date: Wed, 4 Dec 2024 00:24:41 +0100 Subject: [PATCH] [24] don't request access to enclosing class if it's never used Fixes after re-opening: + essentially revert previous change + replace with specific detection wrt local type alloc in static context Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3194 --- .../eclipse/jdt/core/compiler/IProblem.java | 9 +++ .../compiler/ast/AllocationExpression.java | 9 +++ .../compiler/ast/ReferenceExpression.java | 10 +++ .../compiler/ast/SingleNameReference.java | 32 ++++---- .../compiler/ast/TypeDeclaration.java | 11 ++- .../compiler/lookup/NestedTypeBinding.java | 17 +--- .../jdt/internal/compiler/lookup/Scope.java | 9 +++ .../compiler/problem/ProblemReporter.java | 13 ++++ .../compiler/problem/messages.properties | 1 + .../regression/CompilerInvocationTests.java | 2 + .../regression/SuperAfterStatementsTest.java | 77 ++++++++++++++----- 11 files changed, 133 insertions(+), 57 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 289c958a32c..a4eeb3be263 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 @@ -8,6 +8,10 @@ * * SPDX-License-Identifier: EPL-2.0 * + * This is an implementation of an early-draft specification developed under the Java + * Community Process (JCP) and is made available for testing and evaluation purposes + * only. The code is not compatible with any specification of the JCP. + * * Contributors: * IBM Corporation - initial API and implementation * IBM Corporation - added the following constants @@ -2759,6 +2763,11 @@ public interface IProblem { */ int ConstructorCallNotAllowedHere = PreviewRelated + 2031; + /** + * @since 3.40 + */ + int AllocatingLocalInStaticContext = TypeRelated + 2032; + /** * @since 3.40 * @noreference preview feature diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java index fdd29789637..f3fd2742982 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java @@ -8,6 +8,10 @@ * * SPDX-License-Identifier: EPL-2.0 * + * This is an implementation of an early-draft specification developed under the Java + * Community Process (JCP) and is made available for testing and evaluation purposes + * only. The code is not compatible with any specification of the JCP. + * * Contributors: * IBM Corporation - initial API and implementation * Stephan Herrmann - Contributions for @@ -363,6 +367,11 @@ public TypeBinding resolveType(BlockScope scope) { } else { this.resolvedType = this.type.resolveType(scope, true /* check bounds*/); } + if (this.resolvedType instanceof LocalTypeBinding local && this.enumConstant == null) { + MethodScope enclosingMethodScope = local.scope.enclosingMethodScope(); + if (enclosingMethodScope != null && !enclosingMethodScope.isStatic && scope.isInStaticContext()) + scope.problemReporter().allocationInStaticContext(this, local); + } if (this.type != null) { checkIllegalNullAnnotation(scope, this.resolvedType); checkParameterizedAllocation: { diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java index 0263439e0a2..b65c932112f 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java @@ -12,6 +12,10 @@ * * SPDX-License-Identifier: EPL-2.0 * + * This is an implementation of an early-draft specification developed under the Java + * Community Process (JCP) and is made available for testing and evaluation purposes + * only. The code is not compatible with any specification of the JCP. + * * Contributors: * IBM Corporation - initial API and implementation * Jesper S Moller - Contributions for @@ -718,6 +722,12 @@ enclosing instance of this (8.1.3)", we will actually implement this check in co if (isMethodReference) { someMethod = scope.getMethod(this.receiverType, this.selector, descriptorParameters, this); } else { + if (this.receiverType instanceof LocalTypeBinding local) { + MethodScope enclosingMethodScope = local.scope.enclosingMethodScope(); + if (enclosingMethodScope != null && !enclosingMethodScope.isStatic && scope.isInStaticContext()) { + scope.problemReporter().allocationInStaticContext(this, local); + } + } if (argumentsTypeElided() && this.receiverType.isRawType()) { boolean[] inferredReturnType = new boolean[1]; someMethod = AllocationExpression.inferDiamondConstructor(scope, this, this.receiverType, this.descriptor.parameters, inferredReturnType); diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java index f466e132259..fd6d4580dcc 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java @@ -8,6 +8,10 @@ * * SPDX-License-Identifier: EPL-2.0 * + * This is an implementation of an early-draft specification developed under the Java + * Community Process (JCP) and is made available for testing and evaluation purposes + * only. The code is not compatible with any specification of the JCP. + * * Contributors: * IBM Corporation - initial API and implementation * Stephan Herrmann - Contributions for @@ -921,22 +925,18 @@ public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo f if ((this.bits & Binding.FIELD) != 0) { FieldBinding fieldBinding = (FieldBinding) this.binding; FieldBinding codegenField = fieldBinding.original(); - if ((this.bits & ASTNode.DepthMASK) != 0) { - if ((codegenField.isPrivate() // private access - && !currentScope.enclosingSourceType().isNestmateOf(codegenField.declaringClass)) - || (codegenField.isProtected() // implicit protected access - && codegenField.declaringClass.getPackage() != currentScope.enclosingSourceType().getPackage())) { - if (this.syntheticAccessors == null) - this.syntheticAccessors = new MethodBinding[2]; - this.syntheticAccessors[isReadAccess ? SingleNameReference.READ : SingleNameReference.WRITE] = - ((SourceTypeBinding) currentScope.enclosingSourceType(). - enclosingTypeAt((this.bits & ASTNode.DepthMASK) >> ASTNode.DepthSHIFT)).addSyntheticMethod(codegenField, isReadAccess, false /* not super access */); - currentScope.problemReporter().needToEmulateFieldAccess(codegenField, this, isReadAccess); - return; - } - if (!codegenField.isStatic() && currentScope.enclosingSourceType() instanceof NestedTypeBinding nestedType) { - nestedType.requestEnclosingInstancePathTo(codegenField.declaringClass); - } + if (((this.bits & ASTNode.DepthMASK) != 0) + && ((codegenField.isPrivate() // private access + && !currentScope.enclosingSourceType().isNestmateOf(codegenField.declaringClass) ) + || (codegenField.isProtected() // implicit protected access + && codegenField.declaringClass.getPackage() != currentScope.enclosingSourceType().getPackage()))) { + if (this.syntheticAccessors == null) + this.syntheticAccessors = new MethodBinding[2]; + this.syntheticAccessors[isReadAccess ? SingleNameReference.READ : SingleNameReference.WRITE] = + ((SourceTypeBinding)currentScope.enclosingSourceType(). + enclosingTypeAt((this.bits & ASTNode.DepthMASK) >> ASTNode.DepthSHIFT)).addSyntheticMethod(codegenField, isReadAccess, false /*not super access*/); + currentScope.problemReporter().needToEmulateFieldAccess(codegenField, this, isReadAccess); + return; } } } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java index 370765a3775..9023c584837 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java @@ -8,6 +8,10 @@ * * SPDX-License-Identifier: EPL-2.0 * + * This is an implementation of an early-draft specification developed under the Java + * Community Process (JCP) and is made available for testing and evaluation purposes + * only. The code is not compatible with any specification of the JCP. + * * Contributors: * IBM Corporation - initial API and implementation * Stephan Herrmann - Contributions for @@ -1072,14 +1076,9 @@ public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, Fl outerScope = outerScope.enclosingInstanceScope(); earlySeen = methodScope.isInsideEarlyConstructionContext(nestedType.enclosingType(), false); } - if (JavaFeature.FLEXIBLE_CONSTRUCTOR_BODIES.isSupported(currentScope.compilerOptions()) - && nestedType.enclosingInstances != null) // only if access to enclosing instance has already been requested - { + if (JavaFeature.FLEXIBLE_CONSTRUCTOR_BODIES.isSupported(currentScope.compilerOptions())) { // JEP 482: this is the central location for organizing synthetic arguments and fields // to serve far outer instances even in inner early construction context. - // Only SingleNameReference.manageSyntheticAccessIfNecessary() may add more synth outers later on - // (using NestedTypeBinding.requestEnclosingInstancePathTo()). - // // Locations MethodBinding.computeSignature() and BlockScope.getEmulationPath() will faithfully // use the information generated here, to decide about signature and call sequence. while (outerScope != null) { diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/NestedTypeBinding.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/NestedTypeBinding.java index 071ccb43d99..6ae4f749718 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/NestedTypeBinding.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/NestedTypeBinding.java @@ -126,27 +126,14 @@ public SyntheticArgumentBinding addSyntheticArgumentAndField(ReferenceBinding ta if (!isPrototype()) throw new IllegalStateException(); if (this.scope.isInsideEarlyConstructionContext(targetEnclosingType, false)) return null; - return doAddSyntheticArgumentAndField(targetEnclosingType); -} - -private SyntheticArgumentBinding doAddSyntheticArgumentAndField(ReferenceBinding targetEnclosingType) { SyntheticArgumentBinding synthLocal = addSyntheticArgument(targetEnclosingType); - if (synthLocal == null) - return null; + if (synthLocal == null) return null; + if (synthLocal.matchingField == null) synthLocal.matchingField = addSyntheticFieldForInnerclass(targetEnclosingType); return synthLocal; } -public void requestEnclosingInstancePathTo(ReferenceBinding targetEnclosingClass) { - if (isCompatibleWith(targetEnclosingClass)) - return; - if (getSyntheticField(targetEnclosingClass, false) == null) - doAddSyntheticArgumentAndField(targetEnclosingClass); - if (this.enclosingType instanceof NestedTypeBinding nestedType) - nestedType.requestEnclosingInstancePathTo(targetEnclosingClass); -} - /* Answer the receiver's enclosing type... null if the receiver is a top level type. */ @Override diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/Scope.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/Scope.java index fe5bbebe33b..2a32526946e 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/Scope.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/Scope.java @@ -8,6 +8,10 @@ * * SPDX-License-Identifier: EPL-2.0 * + * This is an implementation of an early-draft specification developed under the Java + * Community Process (JCP) and is made available for testing and evaluation purposes + * only. The code is not compatible with any specification of the JCP. + * * Contributors: * IBM Corporation - initial API and implementation * Stephan Herrmann - Contributions for @@ -5797,6 +5801,11 @@ public List collectClassesBeingInitialized() { return list; } + public boolean isInStaticContext() { + MethodScope methodScope = methodScope(); + return methodScope != null && methodScope.isStatic; + } + public void include(LocalVariableBinding[] bindings) { // `this` is assumed to be populated with bindings. if (bindings != null) { 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 d44a5e6dcb5..8d90c80dc14 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 @@ -8,6 +8,10 @@ * * SPDX-License-Identifier: EPL-2.0 * + * This is an implementation of an early-draft specification developed under the Java + * Community Process (JCP) and is made available for testing and evaluation purposes + * only. The code is not compatible with any specification of the JCP. + * * Contributors: * IBM Corporation - initial API and implementation * Benjamin Muskalla - Contribution for bug 239066 @@ -12425,6 +12429,15 @@ public void allocationInEarlyConstructionContext(Expression expr, TypeBinding al expr.sourceStart, expr.sourceEnd); } +public void allocationInStaticContext(ASTNode location, LocalTypeBinding allocatedType) { + this.handle( + IProblem.AllocatingLocalInStaticContext, + new String[] { String.valueOf(allocatedType.readableName()) }, + new String[] { String.valueOf(allocatedType.shortReadableName()) }, + location.sourceStart, + location.sourceEnd); +} + public void fieldReadInEarlyConstructionContext(char[] token, int sourceStart, int sourceEnd) { String[] arguments = new String[] {String.valueOf(token)}; this.handle( 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 f928348c096..e8c98af60f1 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 @@ -1201,6 +1201,7 @@ 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 +2032 = Cannot instantiate local class ''{0}'' in a static context # JEP 455 Primitive Types in Patterns, instanceof, and switch (Preview) 2100 = Case constants in a switch on ''{0}'' must have type ''{1}'' 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 96c45a4075c..dba8d1ef09a 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 @@ -342,6 +342,7 @@ class ProblemAttributes { expectedProblemAttributes.put("AbstractMethodMustBeImplementedOverConcreteMethod", new ProblemAttributes(CategorizedProblem.CAT_MEMBER)); expectedProblemAttributes.put("AbstractMethodsInConcreteClass", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); expectedProblemAttributes.put("AbstractServiceImplementation", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); + expectedProblemAttributes.put("AllocatingLocalInStaticContext", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); expectedProblemAttributes.put("AmbiguousConstructor", new ProblemAttributes(CategorizedProblem.CAT_MEMBER)); expectedProblemAttributes.put("AmbiguousConstructorInDefaultConstructor", new ProblemAttributes(CategorizedProblem.CAT_MEMBER)); expectedProblemAttributes.put("AmbiguousConstructorInImplicitConstructorCall", new ProblemAttributes(CategorizedProblem.CAT_MEMBER)); @@ -1480,6 +1481,7 @@ class ProblemAttributes { expectedProblemAttributes.put("AbstractMethodMustBeImplementedOverConcreteMethod", SKIP); expectedProblemAttributes.put("AbstractMethodsInConcreteClass", SKIP); expectedProblemAttributes.put("AbstractServiceImplementation", SKIP); + expectedProblemAttributes.put("AllocatingLocalInStaticContext", SKIP); expectedProblemAttributes.put("AmbiguousConstructor", SKIP); expectedProblemAttributes.put("AmbiguousConstructorInDefaultConstructor", SKIP); expectedProblemAttributes.put("AmbiguousConstructorInImplicitConstructorCall", 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 be0a284564f..970a370daab 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 @@ -2074,12 +2074,7 @@ class C4 { // not early for C3 ^^ Cannot read field f2 in an early construction context ---------- - 2. ERROR in C1.java (at line 15) - new C4(); - ^^^^^^^^ - No enclosing instance of the type C1 is accessible in scope - ---------- - 3. WARNING in C1.java (at line 18) + 2. WARNING in C1.java (at line 18) super(); ^^^^^^^^ You are using a preview language feature that may or may not be supported in a future release @@ -2378,14 +2373,16 @@ class Local {} ""); } - public void testCtorRef_neg () { - runNegativeTest(new String[] { + public void testLocalAccesToOuter () { + runConformTest(new String[] { "Outer.java", """ import java.util.function.Supplier; @SuppressWarnings("unused") class Outer { - void m() { } + void m() { + System.out.print("m"); + } class Inner { Inner() { class Foo { @@ -2394,54 +2391,94 @@ void g() { } } super(); + new Foo().g(); + } + } + public static void main(String... args) { + new Outer().new Inner(); + } + } + """ + }, + "m"); + } + + public void testCtorRef_staticContext () { + runNegativeTest(new String[] { + "Outer.java", + """ + import java.util.function.Supplier; + class Outer { + class Inner { + Inner() { + class Foo { + void g() { + System.out.print("g"); + } + } + super(); class Bar { + static void p() { + new Foo().g(); + } static void r() { Supplier sfoo = Foo::new; + sfoo.get().g(); } }; + Bar.r(); } } + public static void main(String... args) { + new Outer().new Inner(); + } } """ }, """ ---------- - 1. ERROR in Outer.java (at line 9) - m(); - ^^^ - No enclosing instance of the type Outer is accessible in scope - ---------- - 2. WARNING in Outer.java (at line 12) + 1. WARNING in Outer.java (at line 10) super(); ^^^^^^^^ You are using a preview language feature that may or may not be supported in a future release ---------- + 2. ERROR in Outer.java (at line 13) + new Foo().g(); + ^^^^^^^^^ + Cannot instantiate local class 'Foo' in a static context + ---------- + 3. ERROR in Outer.java (at line 16) + Supplier sfoo = Foo::new; + ^^^^^^^^ + Cannot instantiate local class 'Foo' in a static context + ---------- """); } - public void testCtorRef_pos () { + public void testCtorRef_nonStatic () { runConformTest(new String[] { "Outer.java", """ import java.util.function.Supplier; class Outer { - void m() { } class Inner { Inner() { class Foo { void g() { System.out.print("g"); - // m(); } } super(); class Bar { - static void r() { + void p() { + new Foo().g(); + } + void r() { Supplier sfoo = Foo::new; sfoo.get().g(); } }; - Bar.r(); + new Bar().r(); } } public static void main(String... args) {