Java: support for JEP 382: Flexible constructor bodies (IDEA-355054)

GitOrigin-RevId: f6146f9e06b0f618f1512b7b864532e126d7beda
This commit is contained in:
Bas Leijdekkers
2024-07-19 20:23:55 +00:00
committed by intellij-monorepo-bot
parent 5b1dbe4383
commit 7f47f19875
11 changed files with 260 additions and 47 deletions
@@ -2788,11 +2788,18 @@ public final class HighlightUtil {
referencedClass = PsiUtil.resolveClassInType(((PsiThisExpression)qualifier).getType());
}
}
if (resolved instanceof PsiField referencedField) {
if (referencedField.hasModifierProperty(PsiModifier.STATIC)) return null;
resolvedName = PsiFormatUtil
.formatVariable(referencedField, PsiFormatUtilBase.SHOW_CONTAINING_CLASS | PsiFormatUtilBase.SHOW_NAME, PsiSubstitutor.EMPTY);
referencedClass = referencedField.getContainingClass();
if (resolved instanceof PsiField field) {
if (field.hasModifierProperty(PsiModifier.STATIC)) return null;
LanguageLevel languageLevel = PsiUtil.getLanguageLevel(expression);
if (JavaFeature.STATEMENTS_BEFORE_SUPER.isSufficient(languageLevel) &&
languageLevel != LanguageLevel.JDK_22_PREVIEW &&
isOnSimpleAssignmentLeftHand(expression) &&
field.getContainingClass() == PsiTreeUtil.getParentOfType(expression, PsiClass.class, true)) {
return null;
}
resolvedName =
PsiFormatUtil.formatVariable(field, PsiFormatUtilBase.SHOW_CONTAINING_CLASS | PsiFormatUtilBase.SHOW_NAME, PsiSubstitutor.EMPTY);
referencedClass = field.getContainingClass();
}
else if (resolved instanceof PsiMethod method) {
if (method.hasModifierProperty(PsiModifier.STATIC)) return null;
@@ -2913,7 +2920,19 @@ public final class HighlightUtil {
}
}
HighlightInfo.Builder builder = createMemberReferencedError(resolvedName, expression.getTextRange());
if (expression instanceof PsiThisExpression) {
LanguageLevel languageLevel = PsiUtil.getLanguageLevel(expression);
if (JavaFeature.STATEMENTS_BEFORE_SUPER.isSufficient(languageLevel) && languageLevel != LanguageLevel.JDK_22_PREVIEW) {
parent = PsiUtil.skipParenthesizedExprUp(parent);
if (isOnSimpleAssignmentLeftHand(parent) &&
parent instanceof PsiReferenceExpression ref &&
ref.resolve() instanceof PsiField field &&
field.getContainingClass() == PsiTreeUtil.getParentOfType(expression, PsiClass.class, true)) {
return null;
}
}
}
HighlightInfo.Builder builder = createMemberReferencedError(resolvedName, expression.getTextRange(), resolved instanceof PsiMethod);
if (expression instanceof PsiReferenceExpression ref && PsiUtil.isInnerClass(parentClass)) {
String referenceName = ref.getReferenceName();
PsiClass containingClass = parentClass.getContainingClass();
@@ -2927,9 +2946,18 @@ public final class HighlightUtil {
return builder;
}
private static boolean isOnSimpleAssignmentLeftHand(@NotNull PsiElement expr) {
PsiElement parent = PsiTreeUtil.skipParentsOfType(expr, PsiParenthesizedExpression.class);
return parent instanceof PsiAssignmentExpression assignment &&
JavaTokenType.EQ == assignment.getOperationTokenType() &&
PsiTreeUtil.isAncestor(assignment.getLExpression(), expr, false);
}
@NotNull
private static HighlightInfo.Builder createMemberReferencedError(@NotNull String resolvedName, @NotNull TextRange textRange) {
String description = JavaErrorBundle.message("member.referenced.before.constructor.called", resolvedName);
private static HighlightInfo.Builder createMemberReferencedError(@NotNull String resolvedName, @NotNull TextRange textRange, boolean methodCall) {
String description = methodCall
? JavaErrorBundle.message("method.called.before.constructor.called", resolvedName)
: JavaErrorBundle.message("member.referenced.before.constructor.called", resolvedName);
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description);
}
@@ -2946,12 +2974,12 @@ public final class HighlightUtil {
PsiMethod[] constructors = aClass.getConstructors();
if (constructors.length == 0) {
TextRange range = HighlightNamesUtil.getClassDeclarationTextRange(aClass);
return createMemberReferencedError(aClass.getName() + ".this", range);
return createMemberReferencedError(aClass.getName() + ".this", range, false);
}
for (PsiMethod constructor : constructors) {
PsiMethodCallExpression call = JavaPsiConstructorUtil.findThisOrSuperCallInConstructor(constructor);
if (!JavaPsiConstructorUtil.isSuperConstructorCall(call)) {
return createMemberReferencedError(aClass.getName() + ".this", HighlightNamesUtil.getMethodDeclarationTextRange(constructor));
return createMemberReferencedError(aClass.getName() + ".this", HighlightNamesUtil.getMethodDeclarationTextRange(constructor), false);
}
}
return null;
@@ -309,7 +309,8 @@ illegal.self.reference.enum=Cannot refer to enum constant ''{0}'' from inside it
unknown.class=Unknown class: ''{0}''
illegal.type.void=Illegal type: 'void'
member.referenced.before.constructor.called=Cannot reference ''{0}'' before supertype constructor has been called
member.referenced.before.constructor.called=Cannot reference ''{0}'' before superclass constructor is called
method.called.before.constructor.called=Cannot call ''{0}'' before superclass constructor is called
label.without.statement=Label without statement
duplicate.label=Label ''{0}'' already in use
unclosed.comment=Unclosed comment
@@ -4,7 +4,7 @@ class Example extends Zuper{
}
Example() {
super(new <error descr="Cannot reference 'Inner' before supertype constructor has been called">Inner</error>().i);
super(new <error descr="Cannot reference 'Inner' before superclass constructor is called">Inner</error>().i);
}
}
@@ -0,0 +1,183 @@
import java.util.List;
class A {
int i;
A() {
i = 10;
(this).i = 11;
(A.this).i = 12;
<error descr="Cannot reference 'this' before superclass constructor is called">this</error>.i++; // Error
<error descr="Cannot reference 'this' before superclass constructor is called">this</error>.hashCode(); // Error
System.out.print(<error descr="Cannot reference 'this' before superclass constructor is called">this</error>); // Error
super();
}
A(int i) {}
}
class B extends A {
B() {
int i = 3;
super();
}
B(int i) {
this();
<error descr="Only one explicit constructor call allowed in constructor">super(2)</error>;
}
B(char i) {
super(4);
<error descr="Only one explicit constructor call allowed in constructor">this()</error>;
}
B(String s) {
try {
<error descr="Call to 'super()' must be a top level statement in constructor body">super(2)</error>;
}
finally {
}
}
B(String s, int i) {
{
<error descr="Call to 'super()' must be a top level statement in constructor body">super(2)</error>;
}
}
B(boolean b, int i) {
<error descr="Cannot reference 'A.i' before superclass constructor is called">super.i</error> = i;
<error descr="Cannot reference 'this' before superclass constructor is called">this</error>.i = i;
if (false) <error descr="return not allowed before 'super()' call">return;</error>
super(i);
}
void f() {
<error descr="Call to 'super()' only allowed in constructor body">super()</error>;
}
void g() {
<error descr="Call to 'this()' only allowed in constructor body">this()</error>;
}
}
class D {
int i;
}
class E extends D {
E() {
<error descr="Cannot reference 'D.i' before superclass constructor is called">super.i</error>++; // Error
super();
}
}
class F {
int i;
F() {
<error descr="Cannot reference 'F.i' before superclass constructor is called">i</error>++; // Error
<error descr="Cannot call 'Object.hashCode()' before superclass constructor is called">hashCode</error>(); // Error
super();
}
}
class G {
int b;
class C {
int c;
C() {
G.this.b++; // Allowed - enclosing instance
<error descr="Cannot reference 'C.this' before superclass constructor is called">C.this</error>.c++; // Error - same instance
super();
}
}
}
class Outer {
void hello() {
System.out.println("Hello");
}
class Inner {
Inner() {
hello(); // Allowed - enclosing instance method
super();
}
}
}
class Outer2 {
class Inner {
}
Outer2() {
new <error descr="Cannot reference 'Inner' before superclass constructor is called">Inner</error>(); // Error - 'this' is enclosing instance
super();
}
}
class X {
class S {
}
X() {
var tmp = new <error descr="Cannot reference 'S' before superclass constructor is called">S</error>() { }; // Error
super();
}
}
class O {
class S {
}
class U {
U() {
var tmp = new S() { }; // Allowed
super();
}
}
}
class Y {
Y(Object o) {
if (o == null) throw new NullPointerException();
super();
}
}
class Z<T> extends Y {
Z() {
super(<error descr="Cannot reference 'this' before superclass constructor is called">this</error>); // Error - refers to 'this'
}
Z(List<?> list) {
super((T)list.get(0)); // Allowed - refers to 'T' but not 'this'
}
}
record R(int x, int y) {
R(int x, int y, int z) {
if (z > 1000) throw new IllegalArgumentException();
this(x, y);
}
}
enum EE {
A, B;
EE() {
System.out.println(1);
this(1);
}
EE(int i) {}
}
@@ -13,7 +13,7 @@ class Inh extends Foo {
public Inh() {
super(new Bar() {
public void DoBar() {
<error descr="Cannot reference 'Inh.this' before supertype constructor has been called">Inh.this</error>.myField.toString();
<error descr="Cannot reference 'Inh.this' before superclass constructor is called">Inh.this</error>.myField.toString();
}
});
@@ -49,7 +49,7 @@ class Base {
class Inhertior extends Base {
public Inhertior() {
super("", <error descr="Cannot reference 'Base.field' before supertype constructor has been called">field</error>.length());
super("", <error descr="Cannot reference 'Base.field' before superclass constructor is called">field</error>.length());
}
}
//end of IDEADEV-14306
@@ -15,17 +15,17 @@ class Outer {
o.super(Outer.this);
}
public Inner2(Outer o, int par) {
o.super(<error descr="Cannot reference 'this' before supertype constructor has been called">this</error>);
o.super(<error descr="Cannot reference 'this' before superclass constructor is called">this</error>);
}
public Inner2(Outer o, Object par) {
<error descr="Cannot reference 'this' before supertype constructor has been called">this</error>.super(o);
<error descr="Cannot reference 'this' before superclass constructor is called">this</error>.super(o);
}
}
class BadInner extends Inner1 {
<error descr="Cannot reference 'BadInner.this' before supertype constructor has been called">BadInner()</error> {}
<error descr="Cannot reference 'BadInner.this' before superclass constructor is called">BadInner()</error> {}
}
<error descr="Cannot reference 'BadInner2.this' before supertype constructor has been called">class BadInner2 extends Inner1</error> {
<error descr="Cannot reference 'BadInner2.this' before superclass constructor is called">class BadInner2 extends Inner1</error> {
}
class s {
@@ -9,7 +9,7 @@ class FirstLevel {
ThirdLevel(int i) {
super(new ThirdLevel(1) {
public void a() {
<error descr="Cannot reference 'ThirdLevel.this' before supertype constructor has been called">ThirdLevel.this</error>.hashCode();
<error descr="Cannot reference 'ThirdLevel.this' before superclass constructor is called">ThirdLevel.this</error>.hashCode();
}
});
}
@@ -13,21 +13,21 @@ class A {
class B extends A {
int bi;
B(int h) {
super(<error descr="Cannot reference 'B.bi' before supertype constructor has been called">bi</error>);
super(<error descr="Cannot reference 'B.bi' before superclass constructor is called">bi</error>);
}
B() {
this(<error descr="Cannot reference 'B.bi' before supertype constructor has been called">bi</error>);
this(<error descr="Cannot reference 'B.bi' before superclass constructor is called">bi</error>);
}
B(String s) {
super(<error descr="Cannot reference 'B.db()' before supertype constructor has been called">db</error>(1) );
super(<error descr="Cannot call 'B.db()' before superclass constructor is called">db</error>(1) );
}
B(int i, int j) {
super(<error descr="Cannot reference 'A.f()' before supertype constructor has been called">f</error>());
super(<error descr="Cannot call 'A.f()' before superclass constructor is called">f</error>());
}
B(int i, int j, int k) {
super(<error descr="Cannot reference 'A.f()' before supertype constructor has been called">super.f</error>());
super(<error descr="Cannot call 'A.f()' before superclass constructor is called">super.f</error>());
}
B(String s, int i) {
@@ -35,11 +35,11 @@ class B extends A {
}
B(int s, int i, char j) {
super(<error descr="Cannot reference 'A.fi' before supertype constructor has been called">super.fi</error> );
super(<error descr="Cannot reference 'A.fi' before superclass constructor is called">super.fi</error> );
}
B(double d) {
super(new <error descr="Cannot reference 'Inner' before supertype constructor has been called">Inner</error>() );
super(new <error descr="Cannot reference 'Inner' before superclass constructor is called">Inner</error>() );
}
class Inner extends A {
Inner(){
@@ -63,7 +63,7 @@ class Enc {
}
Bb(int i, int j) {
super(<error descr="Cannot reference 'Bb.this' before supertype constructor has been called">Enc.Bb.this</error>.ibb );
super(<error descr="Cannot reference 'Bb.this' before superclass constructor is called">Enc.Bb.this</error>.ibb );
}
Bb(int i, String s) {
@@ -71,12 +71,12 @@ class Enc {
}
Bb(int i, char j) {
super(<error descr="Cannot reference 'this' before supertype constructor has been called">this</error> );
super(<error descr="Cannot reference 'this' before superclass constructor is called">this</error> );
}
}
Enc() {
this(new <error descr="Cannot reference 'Bb' before supertype constructor has been called">Bb</error>());
this(new <error descr="Cannot reference 'Bb' before superclass constructor is called">Bb</error>());
}
Enc(Bb b) {}
}
@@ -117,7 +117,7 @@ class YellinBug extends Base {
super(new Callback() {
public void call() {
<error descr="Cannot reference 'YellinBug.this' before supertype constructor has been called">YellinBug.this</error>.f();
<error descr="Cannot reference 'YellinBug.this' before superclass constructor is called">YellinBug.this</error>.f();
}
});
}
@@ -139,17 +139,17 @@ class Outer {
class UseIt extends Inner{
Outer o;
UseIt() {
<error descr="Cannot reference 'UseIt.o' before supertype constructor has been called">o</error>.super();
<error descr="Cannot reference 'UseIt.o' before superclass constructor is called">o</error>.super();
}
Outer geto() {
return null;
}
UseIt(int x) {
<error descr="Cannot reference 'UseIt.geto()' before supertype constructor has been called">geto</error>().super();
<error descr="Cannot call 'UseIt.geto()' before superclass constructor is called">geto</error>().super();
}
UseIt(Outer x) {
<error descr="Cannot reference 'this' before supertype constructor has been called">this</error>.super();
<error descr="Cannot reference 'this' before superclass constructor is called">this</error>.super();
}
}
}
@@ -188,14 +188,14 @@ class InnerClassRefInsideAnonymous {
Child(Foo foo) {
super(new Foo() {
public String toString() {
AFoo afoo = new <error descr="Cannot reference 'AFoo' before supertype constructor has been called">AFoo</error>();
AFoo afoo = new <error descr="Cannot reference 'AFoo' before superclass constructor is called">AFoo</error>();
return super.toString();
}
});
}
Child(String s, Foo foo) {
super(s, new <error descr="Cannot reference 'AFoo' before supertype constructor has been called">AFoo</error>());
super(s, new <error descr="Cannot reference 'AFoo' before superclass constructor is called">AFoo</error>());
}
class AFoo extends Foo {}
@@ -4,9 +4,9 @@ class A {
int i;
A() {
<error descr="Cannot reference 'this' before supertype constructor has been called">this</error>.i++; // Error
<error descr="Cannot reference 'this' before supertype constructor has been called">this</error>.hashCode(); // Error
System.out.print(<error descr="Cannot reference 'this' before supertype constructor has been called">this</error>); // Error
<error descr="Cannot reference 'this' before superclass constructor is called">this</error>.i++; // Error
<error descr="Cannot reference 'this' before superclass constructor is called">this</error>.hashCode(); // Error
System.out.print(<error descr="Cannot reference 'this' before superclass constructor is called">this</error>); // Error
super();
}
A(int i) {}
@@ -58,7 +58,7 @@ class D {
class E extends D {
E() {
<error descr="Cannot reference 'D.i' before supertype constructor has been called">super.i</error>++; // Error
<error descr="Cannot reference 'D.i' before superclass constructor is called">super.i</error>++; // Error
super();
}
@@ -68,8 +68,8 @@ class F {
int i;
F() {
<error descr="Cannot reference 'F.i' before supertype constructor has been called">i</error>++; // Error
<error descr="Cannot reference 'Object.hashCode()' before supertype constructor has been called">hashCode</error>(); // Error
<error descr="Cannot reference 'F.i' before superclass constructor is called">i</error>++; // Error
<error descr="Cannot call 'Object.hashCode()' before superclass constructor is called">hashCode</error>(); // Error
super();
}
@@ -84,7 +84,7 @@ class G {
C() {
G.this.b++; // Allowed - enclosing instance
<error descr="Cannot reference 'C.this' before supertype constructor has been called">C.this</error>.c++; // Error - same instance
<error descr="Cannot reference 'C.this' before superclass constructor is called">C.this</error>.c++; // Error - same instance
super();
}
@@ -113,7 +113,7 @@ class Outer2 {
}
Outer2() {
new <error descr="Cannot reference 'Inner' before supertype constructor has been called">Inner</error>(); // Error - 'this' is enclosing instance
new <error descr="Cannot reference 'Inner' before superclass constructor is called">Inner</error>(); // Error - 'this' is enclosing instance
super();
}
@@ -124,7 +124,7 @@ class X {
}
X() {
var tmp = new <error descr="Cannot reference 'S' before supertype constructor has been called">S</error>() { }; // Error
var tmp = new <error descr="Cannot reference 'S' before superclass constructor is called">S</error>() { }; // Error
super();
}
@@ -153,7 +153,7 @@ class Y {
class Z<T> extends Y {
Z() {
super(<error descr="Cannot reference 'this' before supertype constructor has been called">this</error>); // Error - refers to 'this'
super(<error descr="Cannot reference 'this' before superclass constructor is called">this</error>); // Error - refers to 'this'
}
Z(List<?> list) {
@@ -6,7 +6,7 @@ class Outer {
class Inner extends Outer {
Inner() {
super(<error descr="Cannot reference 'Outer.hello()' before supertype constructor has been called">hello</error>());
super(<error descr="Cannot call 'Outer.hello()' before superclass constructor is called">hello</error>());
}
}
}
@@ -1,4 +1,4 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.java.codeInsight.daemon;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzerSettings;
@@ -242,6 +242,7 @@ public class LightAdvHighlightingTest extends LightDaemonAnalyzerTestCase {
public void testIgnoreImplicitThisReferenceBeforeSuperSinceJdk7() { doTest(false); }
public void testStatementsBeforeSuper() { IdeaTestUtil.withLevel(getModule(), LanguageLevel.JDK_22_PREVIEW, () -> doTest(false)); }
public void testFlexibleConstructorBodies() { IdeaTestUtil.withLevel(getModule(), LanguageLevel.JDK_23_PREVIEW, () -> doTest(false)); }
public void testCastFromVoid() { doTest(false); }
public void testCatchUnknownMethod() { doTest(false); }
public void testIDEADEV8822() { doTest(false); }