[java-core] JavaPsiReferenceUtil moved to codeserver.core

GitOrigin-RevId: acad9e3fddb54a91d678aead628c2124128e93f0
This commit is contained in:
Tagir Valeev
2025-02-18 09:36:18 +00:00
committed by intellij-monorepo-bot
parent f659d57b70
commit 47ea843d98
4 changed files with 13 additions and 6 deletions
@@ -0,0 +1,116 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.java.codeserver.core;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ThreeState;
import org.jetbrains.annotations.NotNull;
/**
* Utility methods related to references in Java sources
*/
public final class JavaPsiReferenceUtil {
private JavaPsiReferenceUtil() { }
/**
* @param expression reference that potentially references the field
* @param referencedField the field referenced
* @param acceptQualified true if qualified references are accepted
* @return {@link ThreeState#UNSURE} if
*/
public static @NotNull ForwardReferenceProblem checkForwardReference(@NotNull PsiReferenceExpression expression,
@NotNull PsiField referencedField,
boolean acceptQualified) {
PsiClass containingClass = referencedField.getContainingClass();
if (containingClass == null) return ForwardReferenceProblem.LEGAL;
if (expression.getContainingFile() != referencedField.getContainingFile()) return ForwardReferenceProblem.LEGAL;
TextRange fieldRange = referencedField.getTextRange();
if (fieldRange == null || expression.getTextRange().getStartOffset() >= fieldRange.getEndOffset()) return ForwardReferenceProblem.LEGAL;
if (!acceptQualified) {
if (containingClass.isEnum()) {
if (isLegalForwardReferenceInEnum(expression, referencedField, containingClass)) return ForwardReferenceProblem.LEGAL;
}
// simple reference can be illegal (JLS 8.3.3)
else if (expression.getQualifierExpression() != null) return ForwardReferenceProblem.LEGAL;
}
PsiField initField = findEnclosingFieldInitializer(expression);
PsiClassInitializer classInitializer = findParentClassInitializer(expression);
if (initField == null && classInitializer == null) return ForwardReferenceProblem.LEGAL;
// instance initializers may access static fields
boolean isStaticClassInitializer = classInitializer != null && classInitializer.hasModifierProperty(PsiModifier.STATIC);
boolean isStaticInitField = initField != null && initField.hasModifierProperty(PsiModifier.STATIC);
boolean inStaticContext = isStaticInitField || isStaticClassInitializer;
if (!inStaticContext && referencedField.hasModifierProperty(PsiModifier.STATIC)) return ForwardReferenceProblem.LEGAL;
if (PsiUtil.isOnAssignmentLeftHand(expression) && !PsiUtil.isAccessedForReading(expression)) return ForwardReferenceProblem.LEGAL;
if (!containingClass.getManager().areElementsEquivalent(containingClass, PsiTreeUtil.getParentOfType(expression, PsiClass.class))) {
return ForwardReferenceProblem.LEGAL;
}
return initField == referencedField
? ForwardReferenceProblem.ILLEGAL_SELF_REFERENCE
: ForwardReferenceProblem.ILLEGAL_FORWARD_REFERENCE;
}
private static boolean isLegalForwardReferenceInEnum(@NotNull PsiReferenceExpression expression,
@NotNull PsiField referencedField,
@NotNull PsiClass containingClass) {
PsiExpression qualifierExpr = expression.getQualifierExpression();
// simple reference can be illegal (JLS 8.3.3)
if (qualifierExpr == null) return false;
if (!(qualifierExpr instanceof PsiReferenceExpression)) return true;
PsiElement qualifiedReference = ((PsiReferenceExpression)qualifierExpr).resolve();
if (containingClass.equals(qualifiedReference)) {
// static fields that are constant variables (4.12.4) are initialized before other static fields (12.4.2),
// so a qualified reference to the constant variable is possible.
return PsiUtil.isCompileTimeConstant(referencedField);
}
return true;
}
/**
* @return field that has initializer with this element as subexpression or null if not found
*/
private static PsiField findEnclosingFieldInitializer(@NotNull PsiElement entry) {
PsiElement element = entry;
while (element != null) {
PsiElement parent = element.getParent();
if (parent instanceof PsiField) {
if (element == ((PsiField)parent).getInitializer()) return (PsiField)parent;
if (parent instanceof PsiEnumConstant && element == ((PsiEnumConstant)parent).getArgumentList()) return (PsiField)parent;
}
if (element instanceof PsiClass || element instanceof PsiMethod) return null;
element = parent;
}
return null;
}
private static PsiClassInitializer findParentClassInitializer(@NotNull PsiElement root) {
PsiElement element = root;
while (element != null) {
if (element instanceof PsiClassInitializer initializer) return initializer;
if (element instanceof PsiClass || element instanceof PsiMethod) return null;
element = element.getParent();
}
return null;
}
/**
* A kind of problem with forward reference
*/
public enum ForwardReferenceProblem {
/**
* The reference has no problem in terms of forward kind
*/
LEGAL,
/**
* The reference refers to an object being initialized (e.g., field initializer refers to a field itself)
*/
ILLEGAL_SELF_REFERENCE,
/**
* The reference is an illegal reference to an object declared later
*/
ILLEGAL_FORWARD_REFERENCE
}
}