[java-highlighting] checkAbstractInstantiation, checkExtendsDuplicate, checkClassExtendsForeignInnerClass -> ClassChecker

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

GitOrigin-RevId: 2a8fd18d616644166ac14c31f1abdbf10c074c8e
This commit is contained in:
Tagir Valeev
2025-01-09 18:51:18 +00:00
committed by intellij-monorepo-bot
parent 7103151e05
commit 6035671a2a
11 changed files with 301 additions and 144 deletions
@@ -57,3 +57,11 @@ receiver.name.mismatch=The receiver name does not match the enclosing class type
override.on.static.method=Static methods cannot be annotated with @Override
override.on.non-overriding.method=Method does not override method from its superclass
class.must.implement.method=Class ''{0}'' must implement abstract method ''{1}'' in ''{2}''
class.must.implement.method.or.abstract=Class ''{0}'' must either be declared abstract or implement abstract method ''{1}'' in ''{2}''
class.must.implement.method.enum.constant=Enum constant ''{0}'' must implement abstract method ''{1}'' in ''{2}''
class.reference.list.duplicate=Duplicate reference to ''{0}'' in ''{1}'' list
class.reference.list.name.expected=Class name expected
class.reference.list.inner.private=''{0}'' has private access in ''{1}''
class.reference.list.no.enclosing.instance=No enclosing instance of type ''{0}'' is in scope
@@ -0,0 +1,143 @@
// 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.highlighting;
import com.intellij.codeInsight.ClassUtil;
import com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.util.*;
import com.intellij.util.JavaPsiConstructorUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
final class ClassChecker {
private final @NotNull JavaErrorVisitor myVisitor;
ClassChecker(@NotNull JavaErrorVisitor visitor) { myVisitor = visitor; }
/**
* new ref(...) or new ref(...) { ... } where ref is abstract class
*/
void checkAbstractInstantiation(@NotNull PsiJavaCodeReferenceElement ref) {
PsiElement parent = ref.getParent();
if (parent instanceof PsiAnonymousClass aClass
&& parent.getParent() instanceof PsiNewExpression
&& !PsiUtilCore.hasErrorElementChild(parent.getParent())) {
checkClassWithAbstractMethods(aClass, aClass, ref.getTextRange());
}
}
private void checkClassWithAbstractMethods(@NotNull PsiClass aClass, @NotNull PsiMember implementsFixElement, @NotNull TextRange range) {
PsiMethod abstractMethod = ClassUtil.getAnyAbstractMethod(aClass);
if (abstractMethod == null) {
return;
}
PsiClass containingClass = abstractMethod.getContainingClass();
if (containingClass == null ||
containingClass == aClass ||
implementsFixElement instanceof PsiEnumConstant && !hasEnumConstantsWithInitializer(aClass)) {
return;
}
myVisitor.report(JavaErrorKinds.CLASS_NO_ABSTRACT_METHOD.create(implementsFixElement, abstractMethod));
}
void checkExtendsDuplicate(@NotNull PsiJavaCodeReferenceElement element, PsiElement resolved) {
if (!(element.getParent() instanceof PsiReferenceList list)) return;
if (!(list.getParent() instanceof PsiClass)) return;
if (!(resolved instanceof PsiClass aClass)) return;
PsiManager manager = myVisitor.file().getManager();
PsiJavaCodeReferenceElement sibling = PsiTreeUtil.getPrevSiblingOfType(element, PsiJavaCodeReferenceElement.class);
while (true) {
if (sibling == null) return;
PsiElement target = sibling.resolve();
if (manager.areElementsEquivalent(target, aClass)) break;
sibling = PsiTreeUtil.getPrevSiblingOfType(sibling, PsiJavaCodeReferenceElement.class);
}
myVisitor.report(JavaErrorKinds.CLASS_REFERENCE_LIST_DUPLICATE.create(element, aClass));
}
void checkClassExtendsForeignInnerClass(@NotNull PsiJavaCodeReferenceElement extendRef, @Nullable PsiElement resolved) {
PsiElement parent = extendRef.getParent();
if (!(parent instanceof PsiReferenceList)) return;
PsiElement grand = parent.getParent();
if (!(grand instanceof PsiClass aClass)) return;
PsiClass containerClass;
if (aClass instanceof PsiTypeParameter typeParameter) {
if (!(typeParameter.getOwner() instanceof PsiClass cls)) return;
containerClass = cls;
}
else {
containerClass = aClass;
}
if (aClass.getExtendsList() != parent && aClass.getImplementsList() != parent) return;
if (resolved != null && !(resolved instanceof PsiClass)) {
myVisitor.report(JavaErrorKinds.CLASS_REFERENCE_LIST_NAME_EXPECTED.create(extendRef));
return;
}
extendRef.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitReferenceElement(@NotNull PsiJavaCodeReferenceElement reference) {
super.visitReferenceElement(reference);
PsiElement resolve = reference.resolve();
if (resolve instanceof PsiClass base) {
PsiClass baseClass = base.getContainingClass();
if (baseClass != null &&
base.hasModifierProperty(PsiModifier.PRIVATE) &&
baseClass == containerClass &&
baseClass.getContainingClass() == null) {
myVisitor.report(JavaErrorKinds.CLASS_REFERENCE_LIST_INNER_PRIVATE.create(reference, base));
stopWalking();
return;
}
// must be inner class
if (!PsiUtil.isInnerClass(base)) return;
if (resolve == resolved &&
baseClass != null &&
(!PsiTreeUtil.isAncestor(baseClass, extendRef, true) || aClass.hasModifierProperty(PsiModifier.STATIC)) &&
!InheritanceUtil.hasEnclosingInstanceInScope(baseClass, extendRef, psiClass -> psiClass != aClass, true) &&
!qualifiedNewCalledInConstructors(aClass)) {
myVisitor.report(JavaErrorKinds.CLASS_REFERENCE_LIST_NO_ENCLOSING_INSTANCE.create(extendRef, baseClass));
stopWalking();
}
}
}
});
}
/**
* 15.9 Class Instance Creation Expressions | 15.9.2 Determining Enclosing Instances
*/
private static boolean qualifiedNewCalledInConstructors(@NotNull PsiClass aClass) {
PsiMethod[] constructors = aClass.getConstructors();
if (constructors.length == 0) return false;
for (PsiMethod constructor : constructors) {
PsiMethodCallExpression methodCallExpression = JavaPsiConstructorUtil.findThisOrSuperCallInConstructor(constructor);
if (methodCallExpression == null) return false;
if (JavaPsiConstructorUtil.isChainedConstructorCall(methodCallExpression)) continue;
PsiReferenceExpression referenceExpression = methodCallExpression.getMethodExpression();
PsiExpression qualifierExpression = PsiUtil.skipParenthesizedExprDown(referenceExpression.getQualifierExpression());
//If the class instance creation expression is qualified, then the immediately
//enclosing instance of i is the object that is the value of the Primary expression or the ExpressionName,
//otherwise aClass needs to be a member of a class enclosing the class in which the class instance creation expression appears
//already excluded by InheritanceUtil.hasEnclosingInstanceInScope
if (qualifierExpression == null) return false;
}
return true;
}
private static boolean hasEnumConstantsWithInitializer(@NotNull PsiClass aClass) {
return CachedValuesManager.getCachedValue(aClass, () -> {
PsiField[] fields = aClass.getFields();
for (PsiField field : fields) {
if (field instanceof PsiEnumConstant constant && constant.getInitializingClass() != null) {
return new CachedValueProvider.Result<>(true, PsiModificationTracker.MODIFICATION_COUNT);
}
}
return new CachedValueProvider.Result<>(false, PsiModificationTracker.MODIFICATION_COUNT);
});
}
}
@@ -3,13 +3,17 @@ package com.intellij.java.codeserver.highlighting;
import com.intellij.java.codeserver.highlighting.errors.JavaCompilationError;
import com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds;
import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.project.Project;
import com.intellij.pom.java.JavaFeature;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Consumer;
@@ -23,6 +27,7 @@ final class JavaErrorVisitor extends JavaElementVisitor {
private final @NotNull PsiFile myFile;
private final @NotNull LanguageLevel myLanguageLevel;
private final @NotNull AnnotationChecker myAnnotationChecker = new AnnotationChecker(this);
private final @NotNull ClassChecker myClassChecker = new ClassChecker(this);
private final @NotNull MethodChecker myMethodChecker = new MethodChecker(this);
private final @NotNull ReceiverChecker myReceiverChecker = new ReceiverChecker(this);
private boolean myHasError; // true if myHolder.add() was called with HighlightInfo of >=ERROR severity. On each .visit(PsiElement) call this flag is reset. Useful to determine whether the error was already reported while visiting this PsiElement.
@@ -86,13 +91,47 @@ final class JavaErrorVisitor extends JavaElementVisitor {
@Override
public void visitReferenceExpression(@NotNull PsiReferenceExpression expression) {
super.visitReferenceExpression(expression);
JavaResolveResult resultForIncompleteCode = doVisitReferenceElement(expression);
if (!hasErrorResults()) {
visitExpression(expression);
if (hasErrorResults()) return;
}
}
@Override
public void visitReferenceElement(@NotNull PsiJavaCodeReferenceElement ref) {
JavaResolveResult result = ref instanceof PsiExpression ? resolveOptimised(ref, myFile) : doVisitReferenceElement(ref);
if (result != null) {
PsiElement resolved = result.getElement();
}
}
private JavaResolveResult doVisitReferenceElement(@NotNull PsiJavaCodeReferenceElement ref) {
JavaResolveResult result = resolveOptimised(ref, myFile);
if (result == null) return null;
PsiElement resolved = result.getElement();
if (!hasErrorResults()) myClassChecker.checkAbstractInstantiation(ref);
if (!hasErrorResults()) myClassChecker.checkExtendsDuplicate(ref, resolved);
if (!hasErrorResults()) myClassChecker.checkClassExtendsForeignInnerClass(ref, resolved);
return result;
}
static @Nullable JavaResolveResult resolveOptimised(@NotNull PsiJavaCodeReferenceElement ref, @NotNull PsiFile containingFile) {
try {
if (ref instanceof PsiReferenceExpressionImpl) {
PsiReferenceExpressionImpl.OurGenericsResolver resolver = PsiReferenceExpressionImpl.OurGenericsResolver.INSTANCE;
JavaResolveResult[] results = JavaResolveUtil.resolveWithContainingFile(ref, resolver, true, true, containingFile);
return results.length == 1 ? results[0] : JavaResolveResult.EMPTY;
}
return ref.advancedResolve(true);
}
catch (IndexNotReadyException e) {
return null;
}
}
@Override
public void visitReferenceList(@NotNull PsiReferenceList list) {
super.visitReferenceList(list);
@@ -38,6 +38,32 @@ final class JavaErrorFormatUtil {
return new TextRange(start, end).shiftLeft(method.getTextRange().getStartOffset());
}
static @NotNull TextRange getFieldDeclarationTextRange(@NotNull PsiField field) {
PsiModifierList modifierList = field.getModifierList();
TextRange range = field.getTextRange();
int start = modifierList == null ? range.getStartOffset() : stripAnnotationsFromModifierList(modifierList);
int end = field.getNameIdentifier().getTextRange().getEndOffset();
return new TextRange(start, end).shiftLeft(range.getStartOffset());
}
static @NotNull TextRange getClassDeclarationTextRange(@NotNull PsiClass aClass) {
if (aClass instanceof PsiEnumConstantInitializer) {
throw new IllegalArgumentException();
}
PsiElement psiElement = aClass instanceof PsiAnonymousClass anonymousClass
? anonymousClass.getBaseClassReference()
: aClass.getModifierList() == null ? aClass.getNameIdentifier() : aClass.getModifierList();
if(psiElement == null) return new TextRange(0, 0);
int start = stripAnnotationsFromModifierList(psiElement);
PsiElement endElement = aClass instanceof PsiAnonymousClass anonymousClass ?
anonymousClass.getBaseClassReference() :
aClass.getImplementsList();
if (endElement == null) endElement = aClass.getNameIdentifier();
TextRange endTextRange = endElement == null ? null : endElement.getTextRange();
int end = endTextRange == null ? start : endTextRange.getEndOffset();
return new TextRange(start, end).shiftLeft(aClass.getTextRange().getStartOffset());
}
private static int stripAnnotationsFromModifierList(@NotNull PsiElement element) {
TextRange textRange = element.getTextRange();
if (textRange == null) return 0;
@@ -3,6 +3,7 @@ package com.intellij.java.codeserver.highlighting.errors;
import com.intellij.codeInsight.AnnotationTargetUtil;
import com.intellij.core.JavaPsiBundle;
import com.intellij.java.codeserver.highlighting.JavaCompilationErrorBundle;
import com.intellij.java.codeserver.highlighting.errors.JavaErrorKind.Parameterized;
import com.intellij.java.codeserver.highlighting.errors.JavaErrorKind.Simple;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
@@ -13,6 +14,7 @@ import com.intellij.psi.util.PsiTypesUtil;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.PropertyKey;
import java.util.List;
import java.util.stream.Collectors;
@@ -176,6 +178,43 @@ public final class JavaErrorKinds {
.withAnchor(parameter -> requireNonNullElse(parameter.getTypeElement(), parameter)).withContext();
public static final Parameterized<PsiReceiverParameter, @Nullable String> RECEIVER_NAME_MISMATCH =
error(PsiReceiverParameter.class, "receiver.name.mismatch").withAnchor(PsiReceiverParameter::getIdentifier).withContext();
// PsiMember = PsiClass | PsiEnumConstant
public static final Parameterized<PsiMember, PsiMethod> CLASS_NO_ABSTRACT_METHOD =
error(PsiMember.class, "class.must.implement.method")
.withRange(member ->
member instanceof PsiEnumConstant enumConstant ? enumConstant.getNameIdentifier().getTextRange() :
member instanceof PsiClass aClass ? JavaErrorFormatUtil.getClassDeclarationTextRange(aClass) : null)
.<PsiMethod>withContext()
.withRawDescription((member, abstractMethod) -> {
PsiClass aClass = member instanceof PsiClass cls ? cls : requireNonNull(member.getContainingClass());
@PropertyKey(resourceBundle = JavaCompilationErrorBundle.BUNDLE) String messageKey;
String referenceName;
if (aClass instanceof PsiEnumConstantInitializer enumConstant) {
messageKey = "class.must.implement.method.enum.constant";
referenceName = enumConstant.getEnumConstant().getName();
}
else {
messageKey = aClass.isEnum() || aClass.isRecord() || aClass instanceof PsiAnonymousClass
? "class.must.implement.method"
: "class.must.implement.method.or.abstract";
referenceName = formatClass(aClass, false);
}
return message(messageKey, referenceName, formatMethod(abstractMethod),
formatClass(requireNonNull(abstractMethod.getContainingClass()), false));
});
public static final Parameterized<PsiJavaCodeReferenceElement, PsiClass> CLASS_REFERENCE_LIST_DUPLICATE =
parameterized(PsiJavaCodeReferenceElement.class, PsiClass.class, "class.reference.list.duplicate")
.withRawDescription(
(ref, target) -> message("class.reference.list.duplicate", formatClass(target), ref.getParent().getFirstChild().getText()));
public static final Simple<PsiJavaCodeReferenceElement> CLASS_REFERENCE_LIST_NAME_EXPECTED =
error("class.reference.list.name.expected");
public static final Parameterized<PsiJavaCodeReferenceElement, PsiClass> CLASS_REFERENCE_LIST_INNER_PRIVATE =
parameterized(PsiJavaCodeReferenceElement.class, PsiClass.class, "class.reference.list.inner.private")
.withRawDescription((ref, target) -> message("class.reference.list.inner.private",
formatClass(target), formatClass(requireNonNull(target.getContainingClass()))));
public static final Parameterized<PsiJavaCodeReferenceElement, PsiClass> CLASS_REFERENCE_LIST_NO_ENCLOSING_INSTANCE =
parameterized(PsiJavaCodeReferenceElement.class, PsiClass.class, "class.reference.list.no.enclosing.instance")
.withRawDescription((ref, target) -> message("class.reference.list.no.enclosing.instance", formatClass(target)));
private static @NotNull <Psi extends PsiElement> Simple<Psi> error(@NotNull String key) {
return new Simple<>(key);
@@ -55,18 +55,6 @@ import java.util.function.Consumer;
* Generates HighlightInfoType.ERROR-only HighlightInfos at PsiClass level.
*/
public final class HighlightClassUtil {
/**
* new ref(...) or new ref(...) { ... } where ref is abstract class
*/
static HighlightInfo.Builder checkAbstractInstantiation(@NotNull PsiJavaCodeReferenceElement ref) {
PsiElement parent = ref.getParent();
if (parent instanceof PsiAnonymousClass aClass
&& parent.getParent() instanceof PsiNewExpression
&& !PsiUtilCore.hasErrorElementChild(parent.getParent())) {
return checkClassWithAbstractMethods(aClass, aClass, ref.getTextRange());
}
return null;
}
static HighlightInfo.Builder checkClassWithAbstractMethods(@NotNull PsiClass aClass, @NotNull PsiElement implementsFixElement, @NotNull TextRange range) {
PsiMethod abstractMethod = ClassUtil.getAnyAbstractMethod(aClass);
@@ -739,29 +727,6 @@ public final class HighlightClassUtil {
return null;
}
static HighlightInfo.Builder checkExtendsDuplicate(@NotNull PsiJavaCodeReferenceElement element,
@Nullable PsiElement resolved,
@NotNull PsiFile containingFile) {
if (!(element.getParent() instanceof PsiReferenceList list)) return null;
if (!(list.getParent() instanceof PsiClass)) return null;
if (!(resolved instanceof PsiClass aClass)) return null;
PsiManager manager = containingFile.getManager();
PsiJavaCodeReferenceElement sibling = PsiTreeUtil.getPrevSiblingOfType(element, PsiJavaCodeReferenceElement.class);
while (true) {
if (sibling == null) return null;
PsiElement target = sibling.resolve();
if (manager.areElementsEquivalent(target, aClass)) break;
sibling = PsiTreeUtil.getPrevSiblingOfType(sibling, PsiJavaCodeReferenceElement.class);
}
String name = HighlightUtil.formatClass(aClass);
String description = JavaErrorBundle.message("duplicate.reference.in.list", name, list.getFirstChild().getText());
HighlightInfo.Builder info =
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element).descriptionAndTooltip(description);
IntentionAction action = QuickFixFactory.getInstance().createRemoveDuplicateExtendsAction(name);
info.registerFix(action, null, null, null, null);
return info;
}
static HighlightInfo.Builder checkClassAlreadyImported(@NotNull PsiClass aClass, @NotNull PsiElement elementToHighlight) {
PsiFile file = aClass.getContainingFile();
if (!(file instanceof PsiJavaFile javaFile)) return null;
@@ -861,107 +826,6 @@ public final class HighlightClassUtil {
return info;
}
/**
* class c extends foreign.inner {}
*
* @param extendRef points to the class in the extends list
* @param resolved extendRef resolved
*/
static HighlightInfo.Builder checkClassExtendsForeignInnerClass(@NotNull PsiJavaCodeReferenceElement extendRef, @Nullable PsiElement resolved) {
PsiElement parent = extendRef.getParent();
if (!(parent instanceof PsiReferenceList)) {
return null;
}
PsiElement grand = parent.getParent();
if (!(grand instanceof PsiClass aClass)) {
return null;
}
PsiClass containerClass;
if (aClass instanceof PsiTypeParameter typeParameter) {
if (!(typeParameter.getOwner() instanceof PsiClass cls)) {
return null;
}
containerClass = cls;
}
else {
containerClass = aClass;
}
if (aClass.getExtendsList() != parent && aClass.getImplementsList() != parent) {
return null;
}
if (resolved != null && !(resolved instanceof PsiClass)) {
String description = JavaErrorBundle.message("class.name.expected");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(extendRef).descriptionAndTooltip(description);
}
HighlightInfo.Builder[] infos = new HighlightInfo.Builder[1];
extendRef.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitElement(@NotNull PsiElement element) {
if (infos[0] != null) return;
super.visitElement(element);
}
@Override
public void visitReferenceElement(@NotNull PsiJavaCodeReferenceElement reference) {
super.visitReferenceElement(reference);
PsiElement resolve = reference.resolve();
if (resolve instanceof PsiClass base) {
PsiClass baseClass = base.getContainingClass();
if (baseClass != null && base.hasModifierProperty(PsiModifier.PRIVATE) && baseClass == containerClass && baseClass.getContainingClass() == null) {
String description = JavaErrorBundle.message("private.symbol",
HighlightUtil.formatClass(base),
HighlightUtil.formatClass(baseClass));
HighlightInfo.Builder info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(extendRef)
.descriptionAndTooltip(description);
IntentionAction action1 = QuickFixFactory.getInstance().createModifierListFix(base, PsiModifier.PUBLIC, true, false);
info.registerFix(action1, null, null, null, null);
IntentionAction action = QuickFixFactory.getInstance().createModifierListFix(base, PsiModifier.PROTECTED, true, false);
info.registerFix(action, null, null, null, null);
infos[0] = info;
return;
}
// must be inner class
if (!PsiUtil.isInnerClass(base)) return;
if (resolve == resolved && baseClass != null && (!PsiTreeUtil.isAncestor(baseClass, extendRef, true) || aClass.hasModifierProperty(PsiModifier.STATIC)) &&
!InheritanceUtil.hasEnclosingInstanceInScope(baseClass, extendRef, psiClass -> psiClass != aClass, true) &&
!qualifiedNewCalledInConstructors(aClass)) {
String description = JavaErrorBundle.message("no.enclosing.instance.in.scope", HighlightUtil.formatClass(baseClass));
infos[0] = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(extendRef).descriptionAndTooltip(description);
}
}
}
});
return infos[0];
}
/**
* 15.9 Class Instance Creation Expressions | 15.9.2 Determining Enclosing Instances
*/
private static boolean qualifiedNewCalledInConstructors(@NotNull PsiClass aClass) {
PsiMethod[] constructors = aClass.getConstructors();
if (constructors.length == 0) return false;
for (PsiMethod constructor : constructors) {
PsiMethodCallExpression methodCallExpression = JavaPsiConstructorUtil.findThisOrSuperCallInConstructor(constructor);
if (methodCallExpression == null) return false;
if (JavaPsiConstructorUtil.isChainedConstructorCall(methodCallExpression)) continue;
PsiReferenceExpression referenceExpression = methodCallExpression.getMethodExpression();
PsiExpression qualifierExpression = PsiUtil.skipParenthesizedExprDown(referenceExpression.getQualifierExpression());
//If the class instance creation expression is qualified, then the immediately
//enclosing instance of i is the object that is the value of the Primary expression or the ExpressionName,
//otherwise aClass needs to be a member of a class enclosing the class in which the class instance creation expression appears
//already excluded by InheritanceUtil.hasEnclosingInstanceInScope
if (qualifierExpression == null) return false;
}
return true;
}
static HighlightInfo.Builder checkCreateInnerClassFromStaticContext(@NotNull PsiNewExpression expression, @NotNull PsiType type, @NotNull PsiClass aClass) {
if (type instanceof PsiArrayType || type instanceof PsiPrimitiveType) return null;
if (aClass instanceof PsiAnonymousClass anonymousClass) {
@@ -1046,6 +1046,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
@Override
public void visitReferenceElement(@NotNull PsiJavaCodeReferenceElement ref) {
super.visitReferenceElement(ref);
JavaResolveResult result = ref instanceof PsiExpression ? resolveOptimised(ref, myFile) : doVisitReferenceElement(ref);
if (result != null) {
PsiElement resolved = result.getElement();
@@ -1090,9 +1091,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
}
}
if (!hasErrorResults()) add(HighlightClassUtil.checkAbstractInstantiation(ref));
if (!hasErrorResults()) add(HighlightClassUtil.checkExtendsDuplicate(ref, resolved, myFile));
if (!hasErrorResults()) add(HighlightClassUtil.checkClassExtendsForeignInnerClass(ref, resolved));
if (!hasErrorResults()) add(GenericsHighlightUtil.checkSelectStaticClassFromParameterizedType(resolved, ref));
if (!hasErrorResults() && parent instanceof PsiNewExpression newExpression) {
add(GenericsHighlightUtil.checkDiamondTypeNotAllowed(newExpression));
@@ -1,6 +1,7 @@
// 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.codeInsight.daemon.impl.analysis;
import com.intellij.codeInsight.ClassUtil;
import com.intellij.codeInsight.daemon.impl.quickfix.MoveAnnotationOnStaticMemberQualifyingTypeFix;
import com.intellij.codeInsight.daemon.impl.quickfix.MoveAnnotationToPackageInfoFileFix;
import com.intellij.codeInsight.daemon.impl.quickfix.ReplaceVarWithExplicitTypeFix;
@@ -10,16 +11,21 @@ import com.intellij.codeInsight.intention.impl.BaseIntentionAction;
import com.intellij.java.analysis.JavaAnalysisBundle;
import com.intellij.java.codeserver.highlighting.errors.JavaCompilationError;
import com.intellij.java.codeserver.highlighting.errors.JavaErrorKind;
import com.intellij.lang.jvm.JvmModifier;
import com.intellij.lang.jvm.actions.JvmElementActionFactories;
import com.intellij.lang.jvm.actions.MemberRequestsKt;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.ig.psiutils.TypeUtils;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.stream.Collectors;
import static com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds.*;
@@ -53,11 +59,45 @@ final class JavaErrorFixProvider {
ANNOTATION_NOT_ALLOWED_EXTENDS, RECEIVER_STATIC_CONTEXT, RECEIVER_WRONG_POSITION)) {
single(kind, genericRemover);
}
createClassFixes(factory);
createAnnotationFixes(factory);
createReceiverParameterFixes(factory);
}
private static void createClassFixes(QuickFixFactory factory) {
single(CLASS_NO_ABSTRACT_METHOD, error -> {
if (error.psi() instanceof PsiClass aClass && !(aClass instanceof PsiAnonymousClass) && !aClass.isEnum()
&& aClass.getModifierList() != null
&& HighlightUtil.getIncompatibleModifier(PsiModifier.ABSTRACT, aClass.getModifierList()) == null) {
return factory.createModifierListFix(aClass, PsiModifier.ABSTRACT, true, false);
}
return null;
});
multi(CLASS_NO_ABSTRACT_METHOD, error -> {
PsiClass aClass = error.psi() instanceof PsiClass cls ? cls : Objects.requireNonNull(error.psi().getContainingClass());
PsiClass containingClass = Objects.requireNonNull(error.context().getContainingClass());
PsiMethod anyMethodToImplement = ClassUtil.getAnyMethodToImplement(aClass);
if (anyMethodToImplement == null) return List.of();
if (!anyMethodToImplement.hasModifierProperty(PsiModifier.PACKAGE_LOCAL) ||
JavaPsiFacade.getInstance(aClass.getProject()).arePackagesTheSame(aClass, containingClass)) {
return List.of(factory.createImplementMethodsFix(error.psi()));
}
else {
return StreamEx.of(JvmModifier.PROTECTED, JvmModifier.PUBLIC)
.flatCollection(modifier ->
JvmElementActionFactories.createModifierActions(anyMethodToImplement, MemberRequestsKt.modifierRequest(modifier, true)))
.collect(Collectors.toUnmodifiableList());
}
});
single(CLASS_REFERENCE_LIST_DUPLICATE,
error -> factory.createRemoveDuplicateExtendsAction(HighlightUtil.formatClass(error.context())));
multi(CLASS_REFERENCE_LIST_INNER_PRIVATE, error ->
ContainerUtil.map(List.of(PsiModifier.PUBLIC, PsiModifier.PROTECTED),
(@PsiModifier.ModifierConstant String modifier) ->
factory.createModifierListFix(error.context(), modifier, true, false)));
}
private static void createReceiverParameterFixes(@NotNull QuickFixFactory factory) {
single(RECEIVER_TYPE_MISMATCH, error -> factory.createReceiverParameterTypeFix(error.psi(), error.context()));
single(RECEIVER_NAME_MISMATCH,
@@ -4,7 +4,7 @@ class A<T extends <error descr="'A.B' has private access in 'A'">A<T>.B</error>>
private class B {}
}
abstract class Outer implements <error descr="'Outer.Inner' has private access in 'Outer'">List<Outer.Inner></error> {
abstract class Outer implements List<<error descr="'Outer.Inner' has private access in 'Outer'">Outer.Inner</error>> {
private static abstract class Inner implements List<Inner.Key> {
private static final class Key {}
}
@@ -1,6 +1,6 @@
import java.util.Comparator;
abstract class A implements <error descr="'A.B' has private access in 'A'">Comparator<A.B></error> {
abstract class A implements Comparator<<error descr="'A.B' has private access in 'A'">A.B</error>> {
private static class B {
}
@@ -1,6 +1,6 @@
import java.util.Comparator;
abstract class A implements <error descr="'A.B' has private access in 'A'">Comparator<A.B></error> {
abstract class A implements Comparator<<error descr="'A.B' has private access in 'A'">A.B</error>> {
private static class B {
}