Skip to content

Commit

Permalink
[24] Local instantiation in static context allowed by ECJ
Browse files Browse the repository at this point in the history
+ allocation must compare "nearest enclosing static scope
+ search for enclosing static should not go beyond the target enclosing
+ LocalEnumTest: corrected some expected outcomes
  + extracted subset from one test for specific comparison with javac

Fixes eclipse-jdt#3687
  • Loading branch information
stephan-herrmann committed Feb 6, 2025
1 parent d19b89e commit 30f05c7
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2000, 2024 IBM Corporation and others.
* Copyright (c) 2000, 2025 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -368,8 +368,8 @@ public TypeBinding resolveType(BlockScope scope) {
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())
MethodScope allocationStaticEnclosing = scope.nearestEnclosingStaticScope();
if (allocationStaticEnclosing != null && allocationStaticEnclosing != local.scope.nearestEnclosingStaticScope())
scope.problemReporter().allocationInStaticContext(this, local);
}
if (this.type != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -978,7 +978,18 @@ public Object[] getEmulationPath(ReferenceBinding targetEnclosingType, boolean o
|| (!onlyExactMatch && currentType.findSuperTypeOriginatingFrom(targetEnclosingType) != null)) break;

if (currentMethodScope != null) {
currentMethodScope = currentMethodScope.enclosingMethodScope();
// search for an enclosing method scope still inside targetEnclosingType
Scope enclosingScope = currentMethodScope.parent;
currentMethodScope = null;
while (enclosingScope != null) {
if (enclosingScope instanceof ClassScope cs && TypeBinding.equalsEquals(cs.referenceContext.binding, targetEnclosingType)) {
break; // any scopes outward from here are irrelevant
} else if (enclosingScope instanceof MethodScope ms) {
currentMethodScope = ms; // found, check this scope below
break;
}
enclosingScope = enclosingScope.parent;
}
if (currentMethodScope != null && currentMethodScope.isConstructorCall){
return BlockScope.NoEnclosingInstanceInConstructorCall;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1196,6 +1196,16 @@ public final MethodScope enclosingLambdaScope() {
}
return null; // may answer null if no method around
}
public final MethodScope nearestEnclosingStaticScope() {
Scope current = this;
while (current != null) {
MethodScope methodScope = current.methodScope();
if (methodScope != null && methodScope.isStatic)
return methodScope;
current = current.parent;
}
return null;
}

/* Answer the scope receiver type (could be parameterized)
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2020, 2021 IBM Corporation and others.
* Copyright (c) 2020, 2025 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -2797,7 +2797,7 @@ public void test076() { // bridge method needed
}

public void test077() {
this.runConformTest(
this.runNegativeTest(
new String[] {
"X.java",
"public class X {\n" +
Expand All @@ -2821,12 +2821,24 @@ public void test077() {
" }\n" +
"}\n"
},
"SUCCESS"
"""
----------
1. WARNING in X.java (at line 6)
void bar() {
^^^^^
The method bar() of type new E(){} should be tagged with @Override since it actually overrides a superclass method
----------
2. ERROR in X.java (at line 7)
new M();
^^^^^^^
Cannot instantiate local class 'E.M' in a static context
----------
"""
);
}

public void test078() {
this.runConformTest(
this.runNegativeTest(
new String[] {
"X.java",
"public class X {\n" +
Expand Down Expand Up @@ -2854,7 +2866,19 @@ public void test078() {
" }\n" +
"}\n"
},
"SUCCESS"
"""
----------
1. WARNING in X.java (at line 6)
void bar() {
^^^^^
The method bar() of type new E(){} should be tagged with @Override since it actually overrides a superclass method
----------
2. ERROR in X.java (at line 9)
new M();
^^^^^^^
Cannot instantiate local class 'E.M' in a static context
----------
"""
);
}

Expand Down Expand Up @@ -6664,27 +6688,66 @@ public void test168() {
" ^^\n" +
"Cannot reference a field before it is defined\n" +
"----------\n" +
"4. WARNING in X.java (at line 19)\n" +
"4. ERROR in X.java (at line 11)\n" +
" static A X2 = new A(A.X2);//2 - OK\n" +
" ^^^^^^^^^^^\n" +
"Cannot instantiate local class \'A\' in a static context\n" +
"----------\n" +
"5. WARNING in X.java (at line 19)\n" +
" A x1 = new A(x1);//6 - WRONG\n" +
" ^^\n" +
"The field Y.x1 is hiding a field from type A\n" +
"----------\n" +
"5. ERROR in X.java (at line 19)\n" +
"6. ERROR in X.java (at line 19)\n" +
" A x1 = new A(x1);//6 - WRONG\n" +
" ^^\n" +
"Cannot reference a field before it is defined\n" +
"----------\n" +
"6. WARNING in X.java (at line 20)\n" +
"7. WARNING in X.java (at line 20)\n" +
" static A X2 = new A(Y.X2);//7 - OK\n" +
" ^^\n" +
"The field Y.X2 is hiding a field from type A\n" +
"----------\n" +
"7. WARNING in X.java (at line 21)\n" +
"8. ERROR in X.java (at line 20)\n" +
" static A X2 = new A(Y.X2);//7 - OK\n" +
" ^^^^^^^^^^^\n" +
"Cannot instantiate local class \'A\' in a static context\n" +
"----------\n" +
"9. WARNING in X.java (at line 21)\n" +
" A x3 = new A(this.x3);//8 - OK\n" +
" ^^\n" +
"The field Y.x3 is hiding a field from type A\n" +
"----------\n");
}
public void test168_extract() {
this.runNegativeTest(
new String[] {
"X.java", // =================
"public class X { \n" +
" public static void main(String[] args) {\n" +
" class A {\n" +
" A(A a) {}\n" +
" static A X2 = new A(A.X2);\n" +
" }\n" +
" class Y extends A {\n" +
" static A X3 = new A(Y.X3);\n" +
" Y(Y y) { super(y); }\n" +
" }\n" +
" }\n" +
"}\n", // =================
},
"----------\n" +
"1. ERROR in X.java (at line 5)\n" +
" static A X2 = new A(A.X2);\n" +
" ^^^^^^^^^^^\n" +
"Cannot instantiate local class \'A\' in a static context\n" +
"----------\n" +
"2. ERROR in X.java (at line 8)\n" +
" static A X3 = new A(Y.X3);\n" +
" ^^^^^^^^^^^\n" +
"Cannot instantiate local class \'A\' in a static context\n" +
"----------\n");
}
//https://bugs.eclipse.org/bugs/show_bug.cgi?id=255452 - variation
public void test169() {
this.runNegativeTest(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3041,4 +3041,62 @@ public static void main(String[] args) {
},
"Outer$Inner1$1.m() Outer.g()");
}
public void testGH3687a() {
runNegativeTest(new String[] {
"X.java",
"""
public class X {
public void main(String[] args) {
System.out.println(foo() != 0);
}
static int foo() {
class Local {
int value = 0;
class Local2 {
public static int bar() {
return new Local().value;
}
}
}
return Local.Local2.bar();
}
}
"""
},
"""
----------
1. ERROR in X.java (at line 10)
return new Local().value;
^^^^^^^^^^^
Cannot instantiate local class 'Local' in a static context
----------
""");
}
public void testGH3687b() {
runConformTest(new String[] {
"X.java",
"""
public class X {
@SuppressWarnings("unused")
static int INT_FIELD = new Object() {
class Local {
int value = 10;
class Local2 {
public int bar() {
int v = new Local().value;
System.out.print(v);
return v;
}
}
}
int l = new Local().new Local2().bar();
}.hashCode();
public static void main(String... args) {
int h = INT_FIELD / 2;
}
}
"""
},
"10");
}
}

0 comments on commit 30f05c7

Please sign in to comment.