Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ private TypeBinding internalResolveLeafType(Scope scope, boolean checkBounds) {
if (this.annotations != null)
rejectAnnotationsOnStaticMemberQualififer(scope, currentType, this.annotations[i-1]);
if (typeIsConsistent && currentType.isStatic()
&& (qualifyingType.isParameterizedTypeWithActualArguments() || qualifyingType.isGenericType())) {
&& (qualifyingType.isParameterizedType() || qualifyingType.isGenericType())) {
scope.problemReporter().staticMemberOfParameterizedType(this, currentType, qualifyingType, i);
typeIsConsistent = false;
qualifyingType = qualifyingType.actualType(); // avoid raw/parameterized enclosing of static member
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ protected TypeBinding getTypeBinding(Scope scope) {
rejectAnnotationsOnPackageQualifiers(scope, packageBinding);

boolean isClassScope = scope.kind == Scope.CLASS_SCOPE;
ReferenceBinding qualifiedType = null;
ReferenceBinding qualifyingType = null;
for (int i = typeStart, max = this.tokens.length, last = max-1; i < max; i++) {
findNextTypeBinding(i, scope, packageBinding);
if (!this.resolvedType.isValidBinding())
Expand All @@ -155,34 +155,41 @@ protected TypeBinding getTypeBinding(Scope scope) {
if (((ClassScope) scope).detectHierarchyCycle(this.resolvedType, this)) // must connect hierarchy to find inherited member types
return null;
ReferenceBinding currentType = (ReferenceBinding) this.resolvedType;
if (qualifiedType != null) {
if (qualifyingType != null) {
if (this.annotations != null) {
rejectAnnotationsOnStaticMemberQualififer(scope, currentType, this.annotations[i-1]);
}
if (currentType.isStatic() && (qualifyingType.isParameterizedType() || qualifyingType.isGenericType())) {
scope.problemReporter().staticMemberOfParameterizedType(this, currentType, qualifyingType, i);
qualifyingType = qualifyingType.actualType(); // avoid raw/parameterized enclosing of static member
}
ReferenceBinding enclosingType = currentType.enclosingType();
if (enclosingType != null && TypeBinding.notEquals(enclosingType.erasure(), qualifiedType.erasure())) {
qualifiedType = enclosingType; // inherited member type, leave it associated with its enclosing rather than subtype
if (enclosingType != null && TypeBinding.notEquals(enclosingType.erasure(), qualifyingType.erasure())) {
qualifyingType = enclosingType; // inherited member type, leave it associated with its enclosing rather than subtype
}
if (currentType.isGenericType()) {
qualifiedType = scope.environment().createRawType(currentType, qualifiedType);
qualifyingType = scope.environment().createRawType(currentType, qualifyingType);
} else if (!currentType.hasEnclosingInstanceContext()) {
qualifiedType = currentType; // parameterization of enclosing is irrelevant in this case
qualifyingType = currentType; // parameterization of enclosing is irrelevant in this case
} else {
boolean rawQualified = qualifiedType.isRawType();
boolean rawQualified = qualifyingType.isRawType();
if (rawQualified) {
qualifiedType = scope.environment().createRawType((ReferenceBinding)currentType.erasure(), qualifiedType);
} else if (qualifiedType.isParameterizedType() && TypeBinding.equalsEquals(qualifiedType.erasure(), currentType.enclosingType().erasure())) {
qualifiedType = scope.environment().createParameterizedType((ReferenceBinding)currentType.erasure(), null, qualifiedType);
qualifyingType = scope.environment().createRawType((ReferenceBinding)currentType.erasure(), qualifyingType);
} else if (qualifyingType.isParameterizedType() && TypeBinding.equalsEquals(qualifyingType.erasure(), currentType.enclosingType().erasure())) {
qualifyingType = scope.environment().createParameterizedType((ReferenceBinding)currentType.erasure(), null, qualifyingType);
} else {
qualifiedType = currentType;
qualifyingType = currentType;
}
}
} else {
qualifiedType = currentType.isGenericType() ? (ReferenceBinding)scope.environment().convertToRawType(currentType, false /*do not force conversion of enclosing types*/) : currentType;
qualifyingType = currentType.isGenericType() ?
(ReferenceBinding)scope.environment().convertToRawType(currentType, false /*do not force conversion of enclosing types*/) :
scope.environment().convertToParameterizedType(currentType);

}
recordResolution(scope.environment(), qualifiedType);
recordResolution(scope.environment(), qualifyingType);
}
this.resolvedType = qualifiedType;
this.resolvedType = qualifyingType;
return this.resolvedType;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2034,6 +2034,113 @@ interface MyCallable<U, F extends Exception> extends Callable<U> {
});
}

// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/4702
// ecj ignores error "cannot select a static class from a parameterized type"
public void testIssue4702() {
if (this.complianceLevel < ClassFileConstants.JDK16)
return;
runNegativeTest(new String[] {
"OuterStaticNestedDemo.java",
"""
public class OuterStaticNestedDemo<E> {
class Outer {
static class StaticNested {}
}

void qualifiedNew(Outer outer) {
new OuterStaticNestedDemo<String>.Outer.StaticNested();
}
}
"""
},
"----------\n" +
"1. ERROR in OuterStaticNestedDemo.java (at line 7)\n" +
" new OuterStaticNestedDemo<String>.Outer.StaticNested();\n" +
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
"The member type OuterStaticNestedDemo.Outer.StaticNested cannot be qualified with a parameterized type, since it is static. Remove arguments from qualifying type OuterStaticNestedDemo<String>.Outer\n" +
"----------\n");
}
// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/4702
// ecj ignores error "cannot select a static class from a parameterized type"
public void testIssue4702_1() {
if (this.complianceLevel < ClassFileConstants.JDK16)
return;
runNegativeTest(new String[] {
"OuterStaticNestedDemo.java",
"""
public class OuterStaticNestedDemo<E> {
class Outer {
static class StaticNested {}
}

void qualifiedNew(Outer outer) {
new Outer.StaticNested();
}
}
"""
},
"----------\n" +
"1. ERROR in OuterStaticNestedDemo.java (at line 7)\n" +
" new Outer.StaticNested();\n" +
" ^^^^^^^^^^^^^^^^^^\n" +
"The member type OuterStaticNestedDemo.Outer.StaticNested cannot be qualified with a parameterized type, since it is static. Remove arguments from qualifying type OuterStaticNestedDemo<E>.Outer\n" +
Comment on lines +2083 to +2086
Copy link
Copy Markdown
Contributor

@stephan-herrmann stephan-herrmann Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I don't understand what's going on. The programmer did not specify any type arguments, yet the compiler requires arguments to be removed? How could that code be corrected?

I realize that javac raises a similar error, but if there is any chance that this code is legal per JLS then rejecting this now sounds like a change too disruptive for RC1.

@srikanth-sankaran have you checked this against JLS?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The programmer did not specify any type arguments, yet the compiler requires arguments to be removed? How could that code be corrected?

I realize that javac raises a similar error

By saying new OuterStaticNestedDemo.Outer.StaticNested(); ?

Javac as well as the patched ECJ accept this.

"----------\n");
}
// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/4702
// ecj ignores error "cannot select a static class from a parameterized type"
public void testIssue4702_2() {
if (this.complianceLevel < ClassFileConstants.JDK16)
return;
runNegativeTest(new String[] {
"OuterStaticNestedDemo.java",
"""
class OuterStaticNestedBase<E> {
class Outer {
static class StaticNested {}
}
}
class OuterStaticNestedDemo<E> extends OuterStaticNestedBase<E> {


void qualifiedNew(Outer outer) {
new Outer.StaticNested();
}
}
"""
},
"----------\n" +
"1. ERROR in OuterStaticNestedDemo.java (at line 10)\n" +
" new Outer.StaticNested();\n" +
" ^^^^^^^^^^^^^^^^^^\n" +
"The member type OuterStaticNestedBase.Outer.StaticNested cannot be qualified with a parameterized type, since it is static. Remove arguments from qualifying type OuterStaticNestedBase<E>.Outer\n" +
"----------\n");
}
// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/4702
// ecj ignores error "cannot select a static class from a parameterized type"
public void testIssue4702_3() {
if (this.complianceLevel < ClassFileConstants.JDK16)
return;
runNegativeTest(new String[] {
"OuterStaticNestedDemo.java",
"""
public class OuterStaticNestedDemo<E> {
class Outer<T> {
static class StaticNested {}
}

void qualifiedNew(Outer outer) {
new Outer.StaticNested();
}
}
"""
},
"----------\n" +
"1. WARNING in OuterStaticNestedDemo.java (at line 6)\n" +
" void qualifiedNew(Outer outer) {\n" +
" ^^^^^\n" +
"OuterStaticNestedDemo.Outer is a raw type. References to generic type OuterStaticNestedDemo<E>.Outer<T> should be parameterized\n" +
"----------\n");
}
public static Class<GenericsRegressionTest_9> testClass() {
return GenericsRegressionTest_9.class;
}
Expand Down
Loading