diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java
index 6c3f2c9545b9..71ded6325b9e 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java
@@ -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;
diff --git a/java/java-psi-impl/resources/messages/JavaErrorBundle.properties b/java/java-psi-impl/resources/messages/JavaErrorBundle.properties
index bec4a4d5b6fc..bb1399e321d1 100644
--- a/java/java-psi-impl/resources/messages/JavaErrorBundle.properties
+++ b/java/java-psi-impl/resources/messages/JavaErrorBundle.properties
@@ -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
diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/ConstantReferencedViaInstance.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/ConstantReferencedViaInstance.java
index edf1db0c714c..5f52786a6499 100644
--- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/ConstantReferencedViaInstance.java
+++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/ConstantReferencedViaInstance.java
@@ -4,7 +4,7 @@ class Example extends Zuper{
}
Example() {
- super(new Inner().i);
+ super(new Inner().i);
}
}
diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/FlexibleConstructorBodies.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/FlexibleConstructorBodies.java
new file mode 100644
index 000000000000..a152b11fa494
--- /dev/null
+++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/FlexibleConstructorBodies.java
@@ -0,0 +1,183 @@
+import java.util.List;
+
+class A {
+ int i;
+
+ A() {
+ i = 10;
+ (this).i = 11;
+ (A.this).i = 12;
+ this.i++; // Error
+ this.hashCode(); // Error
+ System.out.print(this); // Error
+ super();
+ }
+ A(int i) {}
+}
+
+class B extends A {
+ B() {
+ int i = 3;
+ super();
+ }
+ B(int i) {
+ this();
+ super(2);
+ }
+ B(char i) {
+ super(4);
+ this();
+ }
+
+ B(String s) {
+ try {
+ super(2);
+ }
+ finally {
+ }
+ }
+ B(String s, int i) {
+ {
+ super(2);
+ }
+ }
+ B(boolean b, int i) {
+ super.i = i;
+ this.i = i;
+ if (false) return;
+ super(i);
+ }
+
+ void f() {
+ super();
+ }
+ void g() {
+ this();
+ }
+
+}
+class D {
+ int i;
+}
+
+class E extends D {
+
+ E() {
+ super.i++; // Error
+ super();
+ }
+
+}
+class F {
+
+ int i;
+
+ F() {
+ i++; // Error
+ hashCode(); // Error
+ super();
+ }
+
+}
+class G {
+
+ int b;
+
+ class C {
+
+ int c;
+
+ C() {
+ G.this.b++; // Allowed - enclosing instance
+ C.this.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 Inner(); // Error - 'this' is enclosing instance
+ super();
+ }
+
+}
+class X {
+
+ class S {
+ }
+
+ X() {
+ var tmp = new S() { }; // 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 extends Y {
+
+ Z() {
+ super(this); // 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) {}
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/IDEADEV11919.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/IDEADEV11919.java
index 8181b03009c5..43fa1233f429 100644
--- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/IDEADEV11919.java
+++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/IDEADEV11919.java
@@ -13,7 +13,7 @@ class Inh extends Foo {
public Inh() {
super(new Bar() {
public void DoBar() {
- Inh.this.myField.toString();
+ Inh.this.myField.toString();
}
});
@@ -49,7 +49,7 @@ class Base {
class Inhertior extends Base {
public Inhertior() {
- super("", field.length());
+ super("", field.length());
}
}
//end of IDEADEV-14306
\ No newline at end of file
diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/QualifiedSuper.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/QualifiedSuper.java
index 24c34449db3e..0c2724ea12b7 100644
--- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/QualifiedSuper.java
+++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/QualifiedSuper.java
@@ -15,17 +15,17 @@ class Outer {
o.super(Outer.this);
}
public Inner2(Outer o, int par) {
- o.super(this);
+ o.super(this);
}
public Inner2(Outer o, Object par) {
- this.super(o);
+ this.super(o);
}
}
class BadInner extends Inner1 {
- BadInner() {}
+ BadInner() {}
}
- class BadInner2 extends Inner1 {
+ class BadInner2 extends Inner1 {
}
class s {
diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/QualifiedThisBeforeCtrCalled.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/QualifiedThisBeforeCtrCalled.java
index 70076e208517..5f3eefbb9f1f 100644
--- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/QualifiedThisBeforeCtrCalled.java
+++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/QualifiedThisBeforeCtrCalled.java
@@ -9,7 +9,7 @@ class FirstLevel {
ThirdLevel(int i) {
super(new ThirdLevel(1) {
public void a() {
- ThirdLevel.this.hashCode();
+ ThirdLevel.this.hashCode();
}
});
}
diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/ReferenceMemberBeforeCtrCalled.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/ReferenceMemberBeforeCtrCalled.java
index 1a8b82dfb15f..6bde8214f51f 100644
--- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/ReferenceMemberBeforeCtrCalled.java
+++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/ReferenceMemberBeforeCtrCalled.java
@@ -13,21 +13,21 @@ class A {
class B extends A {
int bi;
B(int h) {
- super(bi);
+ super(bi);
}
B() {
- this(bi);
+ this(bi);
}
B(String s) {
- super(db(1) );
+ super(db(1) );
}
B(int i, int j) {
- super(f());
+ super(f());
}
B(int i, int j, int k) {
- super(super.f());
+ super(super.f());
}
B(String s, int i) {
@@ -35,11 +35,11 @@ class B extends A {
}
B(int s, int i, char j) {
- super(super.fi );
+ super(super.fi );
}
B(double d) {
- super(new Inner() );
+ super(new Inner() );
}
class Inner extends A {
Inner(){
@@ -63,7 +63,7 @@ class Enc {
}
Bb(int i, int j) {
- super(Enc.Bb.this.ibb );
+ super(Enc.Bb.this.ibb );
}
Bb(int i, String s) {
@@ -71,12 +71,12 @@ class Enc {
}
Bb(int i, char j) {
- super(this );
+ super(this );
}
}
Enc() {
- this(new Bb());
+ this(new Bb());
}
Enc(Bb b) {}
}
@@ -117,7 +117,7 @@ class YellinBug extends Base {
super(new Callback() {
public void call() {
- YellinBug.this.f();
+ YellinBug.this.f();
}
});
}
@@ -139,17 +139,17 @@ class Outer {
class UseIt extends Inner{
Outer o;
UseIt() {
- o.super();
+ o.super();
}
Outer geto() {
return null;
}
UseIt(int x) {
- geto().super();
+ geto().super();
}
UseIt(Outer x) {
- this.super();
+ this.super();
}
}
}
@@ -188,14 +188,14 @@ class InnerClassRefInsideAnonymous {
Child(Foo foo) {
super(new Foo() {
public String toString() {
- AFoo afoo = new AFoo();
+ AFoo afoo = new AFoo();
return super.toString();
}
});
}
Child(String s, Foo foo) {
- super(s, new AFoo());
+ super(s, new AFoo());
}
class AFoo extends Foo {}
diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/StatementsBeforeSuper.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/StatementsBeforeSuper.java
index b29dd6248afe..cc487d3dee93 100644
--- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/StatementsBeforeSuper.java
+++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/StatementsBeforeSuper.java
@@ -4,9 +4,9 @@ class A {
int i;
A() {
- this.i++; // Error
- this.hashCode(); // Error
- System.out.print(this); // Error
+ this.i++; // Error
+ this.hashCode(); // Error
+ System.out.print(this); // Error
super();
}
A(int i) {}
@@ -58,7 +58,7 @@ class D {
class E extends D {
E() {
- super.i++; // Error
+ super.i++; // Error
super();
}
@@ -68,8 +68,8 @@ class F {
int i;
F() {
- i++; // Error
- hashCode(); // Error
+ i++; // Error
+ hashCode(); // Error
super();
}
@@ -84,7 +84,7 @@ class G {
C() {
G.this.b++; // Allowed - enclosing instance
- C.this.c++; // Error - same instance
+ C.this.c++; // Error - same instance
super();
}
@@ -113,7 +113,7 @@ class Outer2 {
}
Outer2() {
- new Inner(); // Error - 'this' is enclosing instance
+ new Inner(); // Error - 'this' is enclosing instance
super();
}
@@ -124,7 +124,7 @@ class X {
}
X() {
- var tmp = new S() { }; // Error
+ var tmp = new S() { }; // Error
super();
}
@@ -153,7 +153,7 @@ class Y {
class Z extends Y {
Z() {
- super(this); // Error - refers to 'this'
+ super(this); // Error - refers to 'this'
}
Z(List> list) {
diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/ThisReferencedInnerClass.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/ThisReferencedInnerClass.java
index 98869fb1da67..0b2794a93ca5 100644
--- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/ThisReferencedInnerClass.java
+++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/ThisReferencedInnerClass.java
@@ -6,7 +6,7 @@ class Outer {
class Inner extends Outer {
Inner() {
- super(hello());
+ super(hello());
}
}
}
diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/LightAdvHighlightingTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/LightAdvHighlightingTest.java
index 259bd31332b9..08b7b090d3e0 100644
--- a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/LightAdvHighlightingTest.java
+++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/LightAdvHighlightingTest.java
@@ -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); }