[java-highlighting] checkClassReferenceAfterQualifier migrated

Part of IDEA-365344 Create a new Java error highlighter with minimal dependencies (PSI only)

GitOrigin-RevId: f11a0b224c8cdee5519452f61bcf8daf4470133e
This commit is contained in:
Tagir Valeev
2025-02-12 20:34:11 +00:00
committed by intellij-monorepo-bot
parent 61f7252105
commit f58076f23f
7 changed files with 41 additions and 84 deletions
@@ -128,6 +128,7 @@ class.generic.extends.exception=Generic class may not extend 'java.lang.Throwabl
class.initializer.must.complete.normally=Initializer must be able to complete normally
class.permitted.not.direct.subclass=Invalid permits clause: ''{0}'' must directly {1, choice, 1#extend|2#implement} ''{2}''
class.permitted.must.have.modifier=All sealed class subclasses must either be final, sealed or non-sealed
class.or.package.expected=Expected class or package
class.implicit.no.main.method=Implicitly declared class contains no 'main' method
class.implicit.invalid.file.name=The file name of an implicitly declared class is not a valid identifier
@@ -871,6 +871,32 @@ final class ExpressionChecker {
}
}
void checkClassReferenceAfterQualifier(@NotNull PsiReferenceExpression expression, @Nullable PsiElement resolved) {
if (!(resolved instanceof PsiClass psiClass)) return;
PsiExpression qualifier = expression.getQualifierExpression();
if (qualifier == null) return;
if (qualifier instanceof PsiReferenceExpression qExpression) {
PsiElement qualifierResolved = qExpression.resolve();
if (qualifierResolved instanceof PsiClass || qualifierResolved instanceof PsiPackage) return;
if (qualifierResolved == null) {
while (true) {
PsiElement qResolve = qExpression.resolve();
if (qResolve == null || qResolve instanceof PsiClass || qResolve instanceof PsiPackage) {
PsiExpression qualifierExpression = qExpression.getQualifierExpression();
if (qualifierExpression == null) return;
if (qualifierExpression instanceof PsiReferenceExpression ref) {
qExpression = ref;
continue;
}
}
break;
}
}
}
myVisitor.report(JavaErrorKinds.CLASS_OR_PACKAGE_EXPECTED.create(expression, psiClass));
}
private static boolean hasYield(@NotNull PsiSwitchExpression switchExpression, @NotNull PsiElement scope) {
class YieldFinder extends JavaRecursiveElementWalkingVisitor {
private boolean hasYield;
@@ -475,11 +475,6 @@ final class JavaErrorVisitor extends JavaElementVisitor {
if (!hasErrorResults()) {
myClassChecker.checkClassDoesNotCallSuperConstructorOrHandleExceptions(aClass);
}
//if (!hasErrorResults()) add(HighlightMethodUtil.checkOverrideEquivalentInheritedMethods(aClass, myFile, myLanguageLevel));
if (!hasErrorResults()) {
//GenericsHighlightUtil.computeOverrideEquivalentMethodErrors(aClass, myOverrideEquivalentMethodsVisitedClasses, myOverrideEquivalentMethodsErrors);
//myErrorSink.accept(myOverrideEquivalentMethodsErrors.get(aClass));
}
if (!hasErrorResults()) myClassChecker.checkCyclicInheritance(aClass);
if (!hasErrorResults()) myMethodChecker.checkOverrideEquivalentInheritedMethods(aClass);
if (!hasErrorResults()) {
@@ -828,6 +823,7 @@ final class JavaErrorVisitor extends JavaElementVisitor {
}
if (!hasErrorResults()) myGenericsChecker.checkAccessStaticFieldFromEnumConstructor(expression, result);
myExpressionChecker.checkUnqualifiedSuperInDefaultMethod(expression, qualifierExpression);
if (!hasErrorResults()) myExpressionChecker.checkClassReferenceAfterQualifier(expression, resolved);
if (!hasErrorResults() && resolved instanceof PsiModifierListOwner) checkPreviewFeature(expression);
}
@@ -430,6 +430,9 @@ public final class JavaErrorKinds {
ctx.subClass().getName(),
ctx.subClass().isInterface() == ctx.superClass().isInterface() ? 1 : 2,
ctx.superClass().getName()));
public static final Parameterized<PsiReferenceExpression, PsiClass> CLASS_OR_PACKAGE_EXPECTED =
parameterized(PsiReferenceExpression.class, PsiClass.class, "class.or.package.expected")
.withAnchor(ref -> ref.getQualifierExpression());
public static final Simple<PsiJavaCodeReferenceElement> VALUE_CLASS_EXTENDS_NON_ABSTRACT = error("value.class.extends.non.abstract");
@@ -525,55 +525,6 @@ public final class HighlightUtil {
return ElementDescriptionUtil.getElementDescription(element, HighlightUsagesDescriptionLocation.INSTANCE);
}
static HighlightInfo.Builder checkClassReferenceAfterQualifier(@NotNull PsiReferenceExpression expression, @Nullable PsiElement resolved) {
if (!(resolved instanceof PsiClass)) return null;
PsiExpression qualifier = expression.getQualifierExpression();
if (qualifier == null) return null;
if (qualifier instanceof PsiReferenceExpression qExpression) {
PsiElement qualifierResolved = qExpression.resolve();
if (qualifierResolved instanceof PsiClass || qualifierResolved instanceof PsiPackage) return null;
if (qualifierResolved == null) {
while (true) {
PsiElement qResolve = qExpression.resolve();
if (qResolve == null || qResolve instanceof PsiClass || qResolve instanceof PsiPackage) {
PsiExpression qualifierExpression = qExpression.getQualifierExpression();
if (qualifierExpression == null) return null;
if (qualifierExpression instanceof PsiReferenceExpression) {
qExpression = (PsiReferenceExpression)qualifierExpression;
continue;
}
}
break;
}
}
}
String description = JavaErrorBundle.message("expected.class.or.package");
HighlightInfo.Builder info =
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(qualifier).descriptionAndTooltip(description);
IntentionAction action = getFixFactory().createRemoveQualifierFix(qualifier, expression, (PsiClass)resolved);
info.registerFix(action, null, null, null, null);
return info;
}
private static @NotNull LanguageLevel getApplicableLevel(@NotNull PsiFile file, @NotNull JavaFeature feature) {
LanguageLevel standardLevel = feature.getStandardLevel();
LanguageLevel featureLevel = feature.getMinimumLevel();
if (featureLevel.isPreview()) {
JavaSdkVersion sdkVersion = JavaSdkVersionUtil.getJavaSdkVersion(file);
if (sdkVersion != null) {
if (standardLevel != null && sdkVersion.isAtLeast(JavaSdkVersion.fromLanguageLevel(standardLevel))) {
return standardLevel;
}
LanguageLevel previewLevel = sdkVersion.getMaxLanguageLevel().getPreviewLevel();
if (previewLevel != null && previewLevel.isAtLeast(featureLevel)) {
return previewLevel;
}
}
}
return featureLevel;
}
static @Nullable HighlightInfo.Builder checkFeature(@NotNull PsiElement element,
@NotNull JavaFeature feature,
@NotNull LanguageLevel level,
@@ -246,13 +246,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
if (!hasErrorResults()) add(ModuleHighlightUtil.checkModuleReference(statement));
}
private void doVisitReferenceElement(@NotNull PsiJavaCodeReferenceElement ref) {
JavaResolveResult result = resolveOptimised(ref, myFile);
if (result != null) {
add(HighlightUtil.checkReference(ref, result));
}
}
static @Nullable JavaResolveResult resolveOptimised(@NotNull PsiJavaCodeReferenceElement ref, @NotNull PsiFile containingFile) {
try {
if (ref instanceof PsiReferenceExpressionImpl) {
@@ -267,36 +260,18 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
}
}
private JavaResolveResult @Nullable [] resolveOptimised(@NotNull PsiReferenceExpression expression) {
try {
if (expression instanceof PsiReferenceExpressionImpl) {
PsiReferenceExpressionImpl.OurGenericsResolver resolver = PsiReferenceExpressionImpl.OurGenericsResolver.INSTANCE;
return JavaResolveUtil.resolveWithContainingFile(expression, resolver, true, true, myFile);
}
else {
return expression.multiResolve(true);
}
}
catch (IndexNotReadyException e) {
return null;
}
}
@Override
public void visitReferenceExpression(@NotNull PsiReferenceExpression expression) {
doVisitReferenceElement(expression);
JavaResolveResult result = resolveOptimised(expression, myFile);
if (result != null) {
add(HighlightUtil.checkReference(expression, result));
}
if (!hasErrorResults()) {
visitElement(expression);
if (hasErrorResults()) return;
}
JavaResolveResult[] results = resolveOptimised(expression);
if (results == null) return;
JavaResolveResult result = results.length == 1 ? results[0] : JavaResolveResult.EMPTY;
PsiElement resolved = result.getElement();
if (!hasErrorResults()) add(HighlightUtil.checkClassReferenceAfterQualifier(expression, resolved));
PsiExpression qualifierExpression = expression.getQualifierExpression();
if (!hasErrorResults() && myJavaModule == null && qualifierExpression != null) {
add(GenericsHighlightUtil.checkMemberSignatureTypesAccessibility(expression));
@@ -386,7 +361,10 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
public void visitReferenceElement(@NotNull PsiJavaCodeReferenceElement ref) {
super.visitReferenceElement(ref);
if (!(ref instanceof PsiExpression)) {
doVisitReferenceElement(ref);
JavaResolveResult result = resolveOptimised(ref, myFile);
if (result != null) {
add(HighlightUtil.checkReference(ref, result));
}
}
}
@@ -658,6 +658,8 @@ final class JavaErrorFixProvider {
};
fix(REFERENCE_MEMBER_BEFORE_CONSTRUCTOR, qualifyFix);
fix(CALL_MEMBER_BEFORE_CONSTRUCTOR, qualifyFix);
fix(CLASS_OR_PACKAGE_EXPECTED, error -> myFactory.createRemoveQualifierFix(
requireNonNull(error.psi().getQualifierExpression()), error.psi(), error.context()));
}
private void createAccessFixes() {