mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-20 13:31:28 +07:00
Java: statements before super() part III - highlight illegal this access before super() call (IDEA-340403)
GitOrigin-RevId: 96f15a88940eabe10c7d907c203f7b58dfbc00c7
This commit is contained in:
committed by
intellij-monorepo-bot
parent
59ac6e04ee
commit
b156a18110
@@ -2673,13 +2673,21 @@ public final class HighlightUtil {
|
||||
}
|
||||
|
||||
static HighlightInfo.Builder checkMemberReferencedBeforeConstructorCalled(@NotNull PsiElement expression,
|
||||
@Nullable PsiElement resolved,
|
||||
@NotNull PsiFile containingFile,
|
||||
@NotNull Function<? super PsiElement, ? extends PsiClass> insideConstructorOfClass) {
|
||||
if (insideConstructorOfClass.apply(expression) == null) {
|
||||
@Nullable PsiElement resolved,
|
||||
@NotNull Function<? super PsiElement, ? extends PsiMethod> surroundingConstructor) {
|
||||
PsiMethod constructor = surroundingConstructor.apply(expression);
|
||||
if (constructor == null) {
|
||||
// not inside expression inside constructor
|
||||
return null;
|
||||
}
|
||||
PsiMethodCallExpression constructorCall = JavaPsiConstructorUtil.findThisOrSuperCallInConstructor(constructor);
|
||||
if (constructorCall == null) {
|
||||
return null;
|
||||
}
|
||||
if (expression.getTextOffset() > constructorCall.getTextOffset() + constructorCall.getTextLength()) {
|
||||
return null;
|
||||
}
|
||||
// is in or before this() or super() call
|
||||
|
||||
PsiClass referencedClass;
|
||||
String resolvedName;
|
||||
@@ -2784,80 +2792,73 @@ public final class HighlightUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
PsiElement element = parent;
|
||||
while (element != null) {
|
||||
// check if expression inside super()/this() call
|
||||
PsiClass parentClass = constructor.getContainingClass();
|
||||
if (parentClass == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (JavaPsiConstructorUtil.isConstructorCall(element)) {
|
||||
PsiClass parentClass = insideConstructorOfClass.apply(element);
|
||||
if (parentClass == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// references to private methods from the outer class are not calls to super methods
|
||||
// even if the outer class is the super class
|
||||
if (resolved instanceof PsiMember member && member.hasModifierProperty(PsiModifier.PRIVATE) && referencedClass != parentClass) {
|
||||
return null;
|
||||
}
|
||||
// field or method should be declared in this class or super
|
||||
if (!InheritanceUtil.isInheritorOrSelf(parentClass, referencedClass, true)) return null;
|
||||
// and point to our instance
|
||||
if (expression instanceof PsiReferenceExpression ref) {
|
||||
PsiExpression qualifier = ref.getQualifierExpression();
|
||||
if (!isThisOrSuperReference(qualifier, parentClass)) {
|
||||
return null;
|
||||
}
|
||||
else if (qualifier instanceof PsiThisExpression || qualifier instanceof PsiSuperExpression) {
|
||||
if (((PsiQualifiedExpression)qualifier).getQualifier() != null) return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (expression instanceof PsiThisExpression || expression instanceof PsiSuperExpression) {
|
||||
if (referencedClass != parentClass) return null;
|
||||
}
|
||||
|
||||
if (expression instanceof PsiJavaCodeReferenceElement) {
|
||||
if (!parentClass.equals(PsiTreeUtil.getParentOfType(expression, PsiClass.class)) &&
|
||||
PsiTreeUtil.getParentOfType(expression, PsiTypeElement.class) != null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (PsiTreeUtil.getParentOfType(expression, PsiClassObjectAccessExpression.class) != null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (parent instanceof PsiNewExpression newExpression &&
|
||||
newExpression.isArrayCreation() &&
|
||||
newExpression.getClassOrAnonymousClassReference() == expression) {
|
||||
return null;
|
||||
}
|
||||
if (parent instanceof PsiThisExpression || parent instanceof PsiSuperExpression) return null;
|
||||
}
|
||||
|
||||
HighlightInfo.Builder builder = createMemberReferencedError(resolvedName, expression.getTextRange());
|
||||
if (expression instanceof PsiReferenceExpression ref && PsiUtil.isInnerClass(parentClass)) {
|
||||
String referenceName = ref.getReferenceName();
|
||||
PsiClass containingClass = parentClass.getContainingClass();
|
||||
LOG.assertTrue(containingClass != null);
|
||||
PsiField fieldInContainingClass = containingClass.findFieldByName(referenceName, true);
|
||||
if (fieldInContainingClass != null && ref.getQualifierExpression() == null) {
|
||||
builder.registerFix(new QualifyWithThisFix(containingClass, ref), null, null, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
element = element.getParent();
|
||||
if (element instanceof PsiClass && InheritanceUtil.isInheritorOrSelf((PsiClass)element, referencedClass, true)) {
|
||||
if ((expression instanceof PsiThisExpression || expression instanceof PsiSuperExpression) &&
|
||||
((PsiQualifiedExpression)expression).getQualifier() != null) {
|
||||
continue;
|
||||
}
|
||||
// references to private methods from the outer class are not calls to super methods
|
||||
// even if the outer class is the super class
|
||||
if (resolved instanceof PsiMember member && member.hasModifierProperty(PsiModifier.PRIVATE) && referencedClass != parentClass) {
|
||||
return null;
|
||||
}
|
||||
// field or method should be declared in this class or super
|
||||
if (!InheritanceUtil.isInheritorOrSelf(parentClass, referencedClass, true)) return null;
|
||||
// and point to our instance
|
||||
if (expression instanceof PsiReferenceExpression ref) {
|
||||
PsiExpression qualifier = ref.getQualifierExpression();
|
||||
if (!isThisOrSuperReference(qualifier, parentClass)) {
|
||||
return null;
|
||||
}
|
||||
else if (qualifier instanceof PsiThisExpression || qualifier instanceof PsiSuperExpression) {
|
||||
if (((PsiQualifiedExpression)qualifier).getQualifier() != null) return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
if (expression instanceof PsiThisExpression || expression instanceof PsiSuperExpression) {
|
||||
if (referencedClass != parentClass) return null;
|
||||
}
|
||||
|
||||
if (expression instanceof PsiJavaCodeReferenceElement) {
|
||||
if (!parentClass.equals(PsiTreeUtil.getParentOfType(expression, PsiClass.class)) &&
|
||||
PsiTreeUtil.getParentOfType(expression, PsiTypeElement.class) != null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (PsiTreeUtil.getParentOfType(expression, PsiClassObjectAccessExpression.class) != null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (parent instanceof PsiNewExpression newExpression &&
|
||||
newExpression.isArrayCreation() &&
|
||||
newExpression.getClassOrAnonymousClassReference() == expression) {
|
||||
return null;
|
||||
}
|
||||
if (parent instanceof PsiThisExpression || parent instanceof PsiSuperExpression) return null;
|
||||
}
|
||||
if (!(expression instanceof PsiThisExpression) && !(expression instanceof PsiSuperExpression) ||
|
||||
((PsiQualifiedExpression)expression).getQualifier() == null) {
|
||||
PsiClass expressionClass = PsiTreeUtil.getParentOfType(expression, PsiClass.class, true);
|
||||
while (expressionClass != null && parentClass != expressionClass) {
|
||||
if (InheritanceUtil.isInheritorOrSelf(expressionClass, referencedClass, true)) {
|
||||
return null;
|
||||
}
|
||||
expressionClass = PsiTreeUtil.getParentOfType(expressionClass, PsiClass.class, true);
|
||||
}
|
||||
}
|
||||
|
||||
HighlightInfo.Builder builder = createMemberReferencedError(resolvedName, expression.getTextRange());
|
||||
if (expression instanceof PsiReferenceExpression ref && PsiUtil.isInnerClass(parentClass)) {
|
||||
String referenceName = ref.getReferenceName();
|
||||
PsiClass containingClass = parentClass.getContainingClass();
|
||||
LOG.assertTrue(containingClass != null);
|
||||
PsiField fieldInContainingClass = containingClass.findFieldByName(referenceName, true);
|
||||
if (fieldInContainingClass != null && ref.getQualifierExpression() == null) {
|
||||
builder.registerFix(new QualifyWithThisFix(containingClass, ref), null, null, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -90,8 +90,8 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
private final Map<PsiClass, MostlySingularMultiMap<MethodSignature, PsiMethod>> myDuplicateMethods = new HashMap<>();
|
||||
private final Set<PsiClass> myOverrideEquivalentMethodsVisitedClasses = new HashSet<>();
|
||||
private final Map<PsiMethod, PsiType> myExpectedReturnTypes = new HashMap<>();
|
||||
private final Function<? super PsiElement, ? extends PsiClass> myInsideConstructorOfClass = entry -> findInsideConstructorClass(entry);
|
||||
private final Map<PsiElement, PsiClass> myInsideConstructorOfClassCache = new HashMap<>(); // null value means "cached but no corresponding ctr found"
|
||||
private final Function<? super PsiElement, ? extends PsiMethod> mySurroundingConstructor = entry -> findSurroundingConstructor(entry);
|
||||
private final Map<PsiElement, PsiMethod> myInsideConstructorOfClassCache = new HashMap<>(); // null value means "cached but no corresponding ctr found"
|
||||
|
||||
@NotNull
|
||||
protected PsiResolveHelper getResolveHelper(@NotNull Project project) {
|
||||
@@ -109,30 +109,17 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
return myHolder.getProject();
|
||||
}
|
||||
|
||||
// element -> class inside which there is a constructor inside which this element is contained
|
||||
private PsiClass findInsideConstructorClass(@NotNull PsiElement entry) {
|
||||
PsiClass result = null;
|
||||
PsiElement parent;
|
||||
// element -> a constructor inside which this element is contained
|
||||
private PsiMethod findSurroundingConstructor(@NotNull PsiElement entry) {
|
||||
PsiMethod result = null;
|
||||
PsiElement element;
|
||||
for (element = entry; element != null && !(element instanceof PsiFile); element = parent) {
|
||||
result = myInsideConstructorOfClassCache.get(entry);
|
||||
if (result != null || myInsideConstructorOfClassCache.containsKey(entry)) {
|
||||
return result;
|
||||
for (element = entry; element != null && !(element instanceof PsiFile); element = element.getParent()) {
|
||||
result = myInsideConstructorOfClassCache.get(element);
|
||||
if (result != null || myInsideConstructorOfClassCache.containsKey(element)) {
|
||||
break;
|
||||
}
|
||||
parent = element.getParent();
|
||||
if (parent instanceof PsiExpressionStatement) {
|
||||
PsiElement p2 = parent.getParent();
|
||||
if (p2 instanceof PsiCodeBlock) {
|
||||
PsiElement p3 = p2.getParent();
|
||||
if (p3 instanceof PsiMethod && ((PsiMethod)p3).isConstructor()) {
|
||||
PsiElement p4 = p3.getParent();
|
||||
if (p4 instanceof PsiClass) {
|
||||
result = (PsiClass)p4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result != null) {
|
||||
if (element instanceof PsiMethod method && method.isConstructor()) {
|
||||
result = method;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1342,7 +1329,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
add(HighlightUtil.checkRestrictedIdentifierReference(ref, (PsiClass)resolved, myLanguageLevel));
|
||||
}
|
||||
if (!hasErrorResults()) {
|
||||
add(HighlightUtil.checkMemberReferencedBeforeConstructorCalled(ref, resolved, myFile, myInsideConstructorOfClass));
|
||||
add(HighlightUtil.checkMemberReferencedBeforeConstructorCalled(ref, resolved, mySurroundingConstructor));
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -1786,7 +1773,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
if (!(expr.getParent() instanceof PsiReceiverParameter)) {
|
||||
add(HighlightUtil.checkThisOrSuperExpressionInIllegalContext(expr, expr.getQualifier(), myLanguageLevel));
|
||||
if (!hasErrorResults()) {
|
||||
add(HighlightUtil.checkMemberReferencedBeforeConstructorCalled(expr, null, myFile, myInsideConstructorOfClass));
|
||||
add(HighlightUtil.checkMemberReferencedBeforeConstructorCalled(expr, null, mySurroundingConstructor));
|
||||
}
|
||||
if (!hasErrorResults()) visitExpression(expr);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
import java.util.List;
|
||||
|
||||
class A {
|
||||
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
|
||||
super();
|
||||
}
|
||||
A(int i) {}
|
||||
}
|
||||
|
||||
@@ -41,4 +50,129 @@ class B extends A {
|
||||
<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 supertype constructor has been called">super.i</error>++; // Error
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
||||
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
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
||||
class G {
|
||||
|
||||
int b;
|
||||
|
||||
class C {
|
||||
|
||||
int c;
|
||||
|
||||
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
|
||||
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 supertype constructor has been called">Inner</error>(); // Error - 'this' is enclosing instance
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
||||
class X {
|
||||
|
||||
class S {
|
||||
}
|
||||
|
||||
X() {
|
||||
var tmp = new <error descr="Cannot reference 'S' before supertype constructor has been 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 supertype constructor has been 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) {}
|
||||
}
|
||||
Reference in New Issue
Block a user