[java-highlighting] Overridden method problems -> MethodChecker

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

GitOrigin-RevId: 2d04cceca2e5984f725590f69edc2ef2641619b0
This commit is contained in:
Tagir Valeev
2025-01-17 16:02:19 +00:00
committed by intellij-monorepo-bot
parent bff363ddcb
commit 811dafd743
8 changed files with 405 additions and 405 deletions
@@ -136,6 +136,9 @@ method.instance.overrides.static=Instance method ''{0}'' in ''{1}'' cannot overr
method.static.overrides.instance=Static method ''{0}'' in ''{1}'' cannot override instance method ''{2}'' in ''{3}''
method.overrides.final=''{0}'' cannot override ''{1}'' in ''{2}''; overridden method is final
method.inheritance.weaker.privileges={0}; attempting to assign weaker access privileges (''{1}''); was ''{2}''
method.inheritance.clash.unrelated.return.types={0}; methods have unrelated return types
method.inheritance.clash.incompatible.return.types={0}; attempting to use incompatible return type
method.inheritance.clash.does.not.throw={0}; overridden method does not throw ''{1}''
clash.methods.message=''{0}'' clashes with ''{1}''
clash.methods.message.show.classes=''{0}'' in ''{2}'' clashes with ''{1}'' in ''{3}''
@@ -14,6 +14,7 @@ import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.util.MethodSignatureBackedByPsiMethod;
import com.intellij.psi.util.PsiTypesUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.PsiUtilCore;
@@ -21,6 +22,7 @@ import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.function.Consumer;
/**
@@ -214,6 +216,21 @@ final class JavaErrorVisitor extends JavaElementVisitor {
if (!hasErrorResults()) myMethodChecker.checkMethodCanHaveBody(method);
if (!hasErrorResults()) myMethodChecker.checkMethodMustHaveBody(method);
if (!hasErrorResults()) myMethodChecker.checkStaticMethodOverride(method);
MethodSignatureBackedByPsiMethod methodSignature = MethodSignatureBackedByPsiMethod.create(method, PsiSubstitutor.EMPTY);
PsiClass aClass = method.getContainingClass();
if (!method.isConstructor()) {
List<HierarchicalMethodSignature> superMethodSignatures = method.getHierarchicalMethodSignature().getSuperSignatures();
if (!superMethodSignatures.isEmpty()) {
if (!method.hasModifierProperty(PsiModifier.STATIC)) {
if (!hasErrorResults()) myMethodChecker.checkMethodWeakerPrivileges(method, methodSignature, superMethodSignatures);
if (!hasErrorResults()) myMethodChecker.checkMethodOverridesFinal(methodSignature, superMethodSignatures);
}
if (!hasErrorResults()) myMethodChecker.checkMethodIncompatibleReturnType(method, methodSignature, superMethodSignatures);
if (aClass != null && !hasErrorResults()) {
myMethodChecker.checkMethodIncompatibleThrows(method, methodSignature, superMethodSignatures, aClass);
}
}
}
}
else if (parent instanceof PsiClass aClass) {
if (!hasErrorResults()) myClassChecker.checkDuplicateNestedClass(aClass);
@@ -230,6 +247,7 @@ final class JavaErrorVisitor extends JavaElementVisitor {
//myErrorSink.accept(myOverrideEquivalentMethodsErrors.get(aClass));
}
if (!hasErrorResults()) myClassChecker.checkCyclicInheritance(aClass);
if (!hasErrorResults()) myMethodChecker.checkOverrideEquivalentInheritedMethods(aClass);
}
}
@@ -1,10 +1,13 @@
// 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.ExceptionUtil;
import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil;
import com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds;
import com.intellij.java.codeserver.highlighting.errors.JavaIncompatibleTypeErrorContext;
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.IncompleteModelUtil;
import com.intellij.psi.impl.PsiSuperMethodImplUtil;
@@ -12,10 +15,9 @@ import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.*;
import com.intellij.util.containers.MostlySingularMultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
final class MethodChecker {
private final @NotNull JavaErrorVisitor myVisitor;
@@ -137,13 +139,13 @@ final class MethodChecker {
boolean isSuperMethodStatic = superModifierList.hasModifierProperty(PsiModifier.STATIC);
if (isMethodStatic != isSuperMethodStatic) {
var errorKind = isMethodStatic ? JavaErrorKinds.METHOD_STATIC_OVERRIDES_INSTANCE : JavaErrorKinds.METHOD_INSTANCE_OVERRIDES_STATIC;
myVisitor.report(errorKind.create(method, superMethod));
myVisitor.report(errorKind.create(method, new JavaErrorKinds.OverrideClashContext(method, superMethod)));
return;
}
if (isMethodStatic) {
if (superClass.isInterface()) return;
checkIsWeaker(method, superMethod);
checkIsWeaker(method, method, superMethod);
if (!myVisitor.hasErrorResults()) checkSuperMethodIsFinal(method, superMethod);
}
}
@@ -155,15 +157,275 @@ final class MethodChecker {
}
}
private void checkIsWeaker(@NotNull PsiMethod method, @NotNull PsiMethod superMethod) {
private void checkIsWeaker(@NotNull PsiMember anchor, @NotNull PsiMethod method, @NotNull PsiMethod superMethod) {
PsiModifierList modifierList = method.getModifierList();
int accessLevel = PsiUtil.getAccessLevel(modifierList);
int superAccessLevel = PsiUtil.getAccessLevel(superMethod.getModifierList());
if (accessLevel < superAccessLevel) {
myVisitor.report(JavaErrorKinds.METHOD_INHERITANCE_WEAKER_PRIVILEGES.create(method, superMethod));
myVisitor.report(JavaErrorKinds.METHOD_INHERITANCE_WEAKER_PRIVILEGES.create(
anchor, new JavaErrorKinds.OverrideClashContext(method, superMethod)));
}
}
void checkOverrideEquivalentInheritedMethods(@NotNull PsiClass aClass) {
Collection<HierarchicalMethodSignature> visibleSignatures = aClass.getVisibleSignatures();
if (aClass.getImplementsListTypes().length == 0 && aClass.getExtendsListTypes().length == 0) {
// optimization: do not analyze unrelated methods from Object: in case of no inheritance they can't conflict
return;
}
PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(aClass.getProject()).getResolveHelper();
for (HierarchicalMethodSignature signature : visibleSignatures) {
PsiMethod method = signature.getMethod();
if (!resolveHelper.isAccessible(method, aClass, null)) continue;
List<HierarchicalMethodSignature> superSignatures = signature.getSuperSignatures();
boolean allAbstracts = method.hasModifierProperty(PsiModifier.ABSTRACT);
PsiClass containingClass = method.getContainingClass();
if (containingClass == null || aClass.equals(containingClass)) continue; //to be checked at method level
if (aClass.isInterface() && !containingClass.isInterface()) continue;
if (allAbstracts) {
superSignatures = new ArrayList<>(superSignatures);
superSignatures.add(0, signature);
checkInterfaceInheritedMethodsReturnTypes(aClass, superSignatures);
}
else {
checkMethodIncompatibleReturnType(aClass, signature, superSignatures);
}
if (method.hasModifierProperty(PsiModifier.STATIC) &&
//jsl 8, chapter 9.4.1
//chapter 8.4.8.2 speaks about a class that "declares or inherits a static method",
// at the same time the rule from chapter 9.4.1 speaks only about an interface that "declares a static method"
//There is no point to add java version check, because static methods in interfaces are allowed from java 8 too.
(!aClass.isInterface() ||
aClass.getManager().areElementsEquivalent(aClass, method.getContainingClass()))) {
for (HierarchicalMethodSignature superSignature : superSignatures) {
PsiMethod superMethod = superSignature.getMethod();
if (!superMethod.hasModifierProperty(PsiModifier.STATIC)) {
myVisitor.report(JavaErrorKinds.METHOD_STATIC_OVERRIDES_INSTANCE.create(
aClass, new JavaErrorKinds.OverrideClashContext(method, superMethod)));
return;
}
}
continue;
}
if (!myVisitor.hasErrorResults()) {
checkMethodIncompatibleThrows(aClass, signature, superSignatures, aClass);
}
if (!myVisitor.hasErrorResults()) {
checkMethodWeakerPrivileges(aClass, signature, superSignatures);
}
}
}
void checkMethodWeakerPrivileges(@NotNull PsiMember anchor,
@NotNull MethodSignatureBackedByPsiMethod methodSignature,
@NotNull List<? extends HierarchicalMethodSignature> superMethodSignatures) {
PsiMethod method = methodSignature.getMethod();
PsiModifierList modifierList = method.getModifierList();
if (modifierList.hasModifierProperty(PsiModifier.PUBLIC)) return;
for (MethodSignatureBackedByPsiMethod superMethodSignature : superMethodSignatures) {
PsiMethod superMethod = superMethodSignature.getMethod();
if (method.hasModifierProperty(PsiModifier.ABSTRACT) && !MethodSignatureUtil.isSuperMethod(superMethod, method)) continue;
if (!PsiUtil.isAccessible(myVisitor.project(), superMethod, method, null)) continue;
if (anchor instanceof PsiClass && MethodSignatureUtil.isSuperMethod(superMethod, method)) continue;
checkIsWeaker(anchor, method, superMethod);
}
}
void checkMethodIncompatibleThrows(@NotNull PsiMember anchor,
@NotNull MethodSignatureBackedByPsiMethod methodSignature,
@NotNull List<? extends HierarchicalMethodSignature> superMethodSignatures,
@NotNull PsiClass analyzedClass) {
PsiMethod method = methodSignature.getMethod();
PsiClass aClass = method.getContainingClass();
if (aClass == null) return;
PsiSubstitutor superSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(aClass, analyzedClass, PsiSubstitutor.EMPTY);
PsiClassType[] exceptions = method.getThrowsList().getReferencedTypes();
PsiJavaCodeReferenceElement[] referenceElements = anchor == method ? method.getThrowsList().getReferenceElements() : null;
List<PsiJavaCodeReferenceElement> exceptionContexts = new ArrayList<>();
List<PsiClassType> checkedExceptions = new ArrayList<>();
for (int i = 0; i < exceptions.length; i++) {
PsiClassType exception = exceptions[i];
if (!ExceptionUtil.isUncheckedException(exception)) {
checkedExceptions.add(exception);
if (referenceElements != null && i < referenceElements.length) {
PsiJavaCodeReferenceElement exceptionRef = referenceElements[i];
exceptionContexts.add(exceptionRef);
}
}
}
for (MethodSignatureBackedByPsiMethod superMethodSignature : superMethodSignatures) {
PsiMethod superMethod = superMethodSignature.getMethod();
int index = getExtraExceptionNum(methodSignature, superMethodSignature, checkedExceptions, superSubstitutor);
if (index != -1) {
if (aClass.isInterface()) {
PsiClass superContainingClass = superMethod.getContainingClass();
if (superContainingClass != null && !superContainingClass.isInterface()) continue;
if (superContainingClass != null && !aClass.isInheritor(superContainingClass, true)) continue;
}
PsiClassType exception = checkedExceptions.get(index);
myVisitor.report(JavaErrorKinds.METHOD_INHERITANCE_CLASH_DOES_NOT_THROW.create(
anchor,
new JavaErrorKinds.IncompatibleOverrideExceptionContext(method, superMethod, exception,
exceptionContexts.isEmpty() ? null : exceptionContexts.get(index))));
return;
}
}
}
void checkMethodIncompatibleReturnType(@NotNull PsiMember anchorClass, @NotNull MethodSignatureBackedByPsiMethod methodSignature,
@NotNull List<? extends HierarchicalMethodSignature> superMethodSignatures) {
PsiMethod method = methodSignature.getMethod();
PsiType returnType = methodSignature.getSubstitutor().substitute(method.getReturnType());
PsiClass aClass = method.getContainingClass();
if (aClass == null) return;
for (MethodSignatureBackedByPsiMethod superMethodSignature : superMethodSignatures) {
PsiMethod superMethod = superMethodSignature.getMethod();
PsiType declaredReturnType = superMethod.getReturnType();
PsiType superReturnType = declaredReturnType;
if (superMethodSignature.isRaw()) superReturnType = TypeConversionUtil.erasure(declaredReturnType);
if (returnType == null || superReturnType == null || method == superMethod) continue;
PsiClass superClass = superMethod.getContainingClass();
if (superClass == null) continue;
checkSuperMethodSignature(
anchorClass, superMethod, superMethodSignature, superReturnType, method, methodSignature, returnType);
}
}
private void checkSuperMethodSignature(@NotNull PsiMember anchorClass,
@NotNull PsiMethod superMethod,
@NotNull MethodSignatureBackedByPsiMethod superMethodSignature,
@NotNull PsiType superReturnType,
@NotNull PsiMethod method,
@NotNull MethodSignatureBackedByPsiMethod methodSignature,
@NotNull PsiType returnType) {
PsiClass superContainingClass = superMethod.getContainingClass();
if (superContainingClass != null &&
CommonClassNames.JAVA_LANG_OBJECT.equals(superContainingClass.getQualifiedName()) &&
!superMethod.hasModifierProperty(PsiModifier.PUBLIC)) {
PsiClass containingClass = method.getContainingClass();
if (containingClass != null && containingClass.isInterface() && !superContainingClass.isInterface()) {
return;
}
}
PsiType substitutedSuperReturnType;
// Important: we should use the language level of the file where the method is declared,
// not the language level of the current file, so myVisitor.isApplicable() doesn't work here.
boolean hasGenerics = PsiUtil.isAvailable(JavaFeature.GENERICS, method);
if (hasGenerics && !superMethodSignature.isRaw() && superMethodSignature.equals(methodSignature)) { //see 8.4.5
PsiSubstitutor unifyingSubstitutor = MethodSignatureUtil.getSuperMethodSignatureSubstitutor(methodSignature,
superMethodSignature);
substitutedSuperReturnType = unifyingSubstitutor == null
? superReturnType
: unifyingSubstitutor.substitute(superReturnType);
}
else {
substitutedSuperReturnType = TypeConversionUtil.erasure(superMethodSignature.getSubstitutor().substitute(superReturnType));
}
if (returnType.equals(substitutedSuperReturnType)) return;
if (!(returnType instanceof PsiPrimitiveType) && substitutedSuperReturnType.getDeepComponentType() instanceof PsiClassType) {
if (hasGenerics && LambdaUtil.performWithSubstitutedParameterBounds(
methodSignature.getTypeParameters(), methodSignature.getSubstitutor(),
() -> TypeConversionUtil.isAssignable(substitutedSuperReturnType, returnType))) {
return;
}
}
myVisitor.report(JavaErrorKinds.METHOD_INHERITANCE_CLASH_INCOMPATIBLE_RETURN_TYPES.create(
anchorClass, new JavaErrorKinds.IncompatibleOverrideReturnTypeContext(method, returnType, superMethod, substitutedSuperReturnType)));
}
private void checkInterfaceInheritedMethodsReturnTypes(@NotNull PsiClass aClass,
@NotNull List<HierarchicalMethodSignature> superMethodSignatures) {
if (superMethodSignatures.size() < 2) return;
MethodSignatureBackedByPsiMethod[] returnTypeSubstitutable = {superMethodSignatures.get(0)};
for (int i = 1; i < superMethodSignatures.size(); i++) {
PsiMethod currentMethod = returnTypeSubstitutable[0].getMethod();
PsiType currentType = returnTypeSubstitutable[0].getSubstitutor().substitute(currentMethod.getReturnType());
MethodSignatureBackedByPsiMethod otherSuperSignature = superMethodSignatures.get(i);
PsiMethod otherSuperMethod = otherSuperSignature.getMethod();
PsiSubstitutor otherSubstitutor = otherSuperSignature.getSubstitutor();
PsiType otherSuperReturnType = otherSubstitutor.substitute(otherSuperMethod.getReturnType());
PsiSubstitutor unifyingSubstitutor = MethodSignatureUtil.getSuperMethodSignatureSubstitutor(returnTypeSubstitutable[0],
otherSuperSignature);
if (unifyingSubstitutor != null) {
otherSuperReturnType = unifyingSubstitutor.substitute(otherSuperReturnType);
currentType = unifyingSubstitutor.substitute(currentType);
}
if (otherSuperReturnType == null || currentType == null || otherSuperReturnType.equals(currentType)) continue;
PsiType otherReturnType = otherSuperReturnType;
PsiType curType = currentType;
LambdaUtil.performWithSubstitutedParameterBounds(otherSuperMethod.getTypeParameters(), otherSubstitutor, () -> {
if (myVisitor.languageLevel().isAtLeast(LanguageLevel.JDK_1_5)) {
//http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.8 Example 8.1.5-3
if (!(otherReturnType instanceof PsiPrimitiveType || curType instanceof PsiPrimitiveType)) {
if (otherReturnType.isAssignableFrom(curType)) return null;
if (curType.isAssignableFrom(otherReturnType)) {
returnTypeSubstitutable[0] = otherSuperSignature;
return null;
}
}
if (otherSuperMethod.getTypeParameters().length > 0 && JavaGenericsUtil.isRawToGeneric(otherReturnType, curType)) return null;
}
myVisitor.report(JavaErrorKinds.METHOD_INHERITANCE_CLASH_UNRELATED_RETURN_TYPES
.create(aClass, new JavaErrorKinds.OverrideClashContext(currentMethod, otherSuperMethod)));
return null;
});
}
}
void checkMethodOverridesFinal(@NotNull MethodSignatureBackedByPsiMethod methodSignature,
@NotNull List<? extends HierarchicalMethodSignature> superMethodSignatures) {
PsiMethod method = methodSignature.getMethod();
for (MethodSignatureBackedByPsiMethod superMethodSignature : superMethodSignatures) {
PsiMethod superMethod = superMethodSignature.getMethod();
checkSuperMethodIsFinal(method, superMethod);
if (myVisitor.hasErrorResults()) return;
}
}
// return number of exception which was not declared in super method or -1
private static int getExtraExceptionNum(@NotNull MethodSignature methodSignature,
@NotNull MethodSignatureBackedByPsiMethod superSignature,
@NotNull List<? extends PsiClassType> checkedExceptions,
@NotNull PsiSubstitutor substitutorForDerivedClass) {
PsiMethod superMethod = superSignature.getMethod();
PsiSubstitutor substitutorForMethod = MethodSignatureUtil.getSuperMethodSignatureSubstitutor(methodSignature, superSignature);
for (int i = 0; i < checkedExceptions.size(); i++) {
PsiClassType checkedEx = checkedExceptions.get(i);
PsiType substituted =
substitutorForMethod == null ? TypeConversionUtil.erasure(checkedEx) : substitutorForMethod.substitute(checkedEx);
PsiType exception = substitutorForDerivedClass.substitute(substituted);
if (!isMethodThrows(superMethod, substitutorForMethod, exception, substitutorForDerivedClass)) {
return i;
}
}
return -1;
}
private static boolean isMethodThrows(@NotNull PsiMethod method,
@Nullable PsiSubstitutor substitutorForMethod,
@NotNull PsiType exception,
@NotNull PsiSubstitutor substitutorForDerivedClass) {
PsiClassType[] thrownExceptions = method.getThrowsList().getReferencedTypes();
for (PsiClassType thrownException1 : thrownExceptions) {
PsiType thrownException =
substitutorForMethod != null ? substitutorForMethod.substitute(thrownException1) : TypeConversionUtil.erasure(thrownException1);
thrownException = substitutorForDerivedClass.substitute(thrownException);
if (TypeConversionUtil.isAssignable(thrownException, exception)) return true;
}
return false;
}
private static boolean isEnumSyntheticMethod(@NotNull MethodSignature methodSignature, @NotNull Project project) {
if (methodSignature.equals(ourValuesEnumSyntheticMethod)) return true;
PsiType javaLangString = PsiType.getJavaLangString(PsiManager.getInstance(project), GlobalSearchScope.allScope(project));
@@ -372,22 +372,22 @@ public final class JavaErrorKinds {
error(PsiMethod.class, "method.should.have.body").withRange(JavaErrorFormatUtil::getMethodDeclarationTextRange);
public static final Simple<PsiMethod> METHOD_SHOULD_HAVE_BODY_OR_ABSTRACT =
error(PsiMethod.class, "method.should.have.body.or.abstract").withRange(JavaErrorFormatUtil::getMethodDeclarationTextRange);
public static final Parameterized<PsiMethod, PsiMethod> METHOD_STATIC_OVERRIDES_INSTANCE =
parameterized(PsiMethod.class, PsiMethod.class, "method.static.overrides.instance")
.withRange((method, superMethod) -> getMethodDeclarationTextRange(method))
.withRawDescription((method, superMethod) -> message("method.static.overrides.instance",
formatMethod(method),
formatClass(requireNonNull(method.getContainingClass())),
formatMethod(superMethod),
formatClass(requireNonNull(superMethod.getContainingClass()))));
public static final Parameterized<PsiMethod, PsiMethod> METHOD_INSTANCE_OVERRIDES_STATIC =
parameterized(PsiMethod.class, PsiMethod.class, "method.instance.overrides.static")
.withRange((method, superMethod) -> getMethodDeclarationTextRange(method))
.withRawDescription((method, superMethod) -> message("method.instance.overrides.static",
formatMethod(method),
formatClass(requireNonNull(method.getContainingClass())),
formatMethod(superMethod),
formatClass(requireNonNull(superMethod.getContainingClass()))));
public static final Parameterized<PsiMember, @NotNull OverrideClashContext> METHOD_STATIC_OVERRIDES_INSTANCE =
parameterized(PsiMember.class, OverrideClashContext.class, "method.static.overrides.instance")
.withRange((member, ctx) -> getMemberDeclarationTextRange(member))
.withRawDescription((method, ctx) -> message("method.static.overrides.instance",
formatMethod(ctx.method()),
formatClass(requireNonNull(ctx.method().getContainingClass())),
formatMethod(ctx.superMethod()),
formatClass(requireNonNull(ctx.superMethod().getContainingClass()))));
public static final Parameterized<PsiMember, @NotNull OverrideClashContext> METHOD_INSTANCE_OVERRIDES_STATIC =
parameterized(PsiMember.class, OverrideClashContext.class, "method.instance.overrides.static")
.withRange((method, ctx) -> getMemberDeclarationTextRange(method))
.withRawDescription((method, ctx) -> message("method.instance.overrides.static",
formatMethod(ctx.method()),
formatClass(requireNonNull(ctx.method().getContainingClass())),
formatMethod(ctx.superMethod()),
formatClass(requireNonNull(ctx.superMethod().getContainingClass()))));
public static final Parameterized<PsiMethod, PsiMethod> METHOD_OVERRIDES_FINAL =
parameterized(PsiMethod.class, PsiMethod.class, "method.overrides.final")
.withRange((method, superMethod) -> getMethodDeclarationTextRange(method))
@@ -398,26 +398,56 @@ public final class JavaErrorKinds {
formatMethod(superMethod),
superClass != null ? formatClass(superClass) : "<unknown>");
});
public static final Parameterized<PsiMethod, PsiMethod> METHOD_INHERITANCE_WEAKER_PRIVILEGES =
parameterized(PsiMethod.class, PsiMethod.class, "method.inheritance.weaker.privileges")
.withRange((method, superMethod) -> {
PsiModifierList modifierList = method.getModifierList();
PsiElement keyword = PsiUtil.findModifierInList(modifierList, PsiUtil.getAccessModifier(PsiUtil.getAccessLevel(modifierList)));
if (keyword != null) {
return keyword.getTextRange().shiftLeft(method.getTextRange().getStartOffset());
public static final Parameterized<PsiMember, @NotNull OverrideClashContext> METHOD_INHERITANCE_WEAKER_PRIVILEGES =
parameterized(PsiMember.class, OverrideClashContext.class, "method.inheritance.weaker.privileges")
.withRange((psi, ctx) -> {
if (psi instanceof PsiMethod method) {
PsiModifierList modifierList = method.getModifierList();
PsiElement keyword = PsiUtil.findModifierInList(modifierList, PsiUtil.getAccessModifier(PsiUtil.getAccessLevel(modifierList)));
if (keyword != null) {
return keyword.getTextRange().shiftLeft(method.getTextRange().getStartOffset());
}
// in case of package-private or some crazy third-party plugin where some access modifier implied even if it's absent
PsiIdentifier identifier = method.getNameIdentifier();
if (identifier != null) {
return identifier.getTextRangeInParent();
}
}
// in case of package-private or some crazy third-party plugin where some access modifier implied even if it's absent
PsiIdentifier identifier = method.getNameIdentifier();
if (identifier != null) {
return identifier.getTextRangeInParent();
}
return getMethodDeclarationTextRange(method);
return getMemberDeclarationTextRange(psi);
})
.withRawDescription((method, superMethod) -> message(
.withRawDescription((psi, ctx) -> message(
"method.inheritance.weaker.privileges",
formatClashMethodMessage(method, superMethod, true),
VisibilityUtil.toPresentableText(PsiUtil.getAccessModifier(PsiUtil.getAccessLevel(method.getModifierList()))),
VisibilityUtil.toPresentableText(PsiUtil.getAccessModifier(PsiUtil.getAccessLevel(superMethod.getModifierList())))));
formatClashMethodMessage(ctx.method(), ctx.superMethod(), true),
VisibilityUtil.toPresentableText(PsiUtil.getAccessModifier(PsiUtil.getAccessLevel(ctx.method().getModifierList()))),
VisibilityUtil.toPresentableText(PsiUtil.getAccessModifier(PsiUtil.getAccessLevel(ctx.superMethod().getModifierList())))));
public static final Parameterized<PsiClass, @NotNull OverrideClashContext> METHOD_INHERITANCE_CLASH_UNRELATED_RETURN_TYPES =
parameterized(PsiClass.class, OverrideClashContext.class, "method.inheritance.clash.unrelated.return.types")
.withRange((cls, ctx) -> getClassDeclarationTextRange(cls))
.withRawDescription((cls, ctx) -> message("method.inheritance.clash.unrelated.return.types",
formatClashMethodMessage(ctx.superMethod(), ctx.method(), true)));
public static final Parameterized<PsiMember, @NotNull IncompatibleOverrideReturnTypeContext>
METHOD_INHERITANCE_CLASH_INCOMPATIBLE_RETURN_TYPES =
parameterized(PsiMember.class, IncompatibleOverrideReturnTypeContext.class, "method.inheritance.clash.incompatible.return.types")
.withRange((psi, ctx) -> {
if (psi instanceof PsiMethod method) {
PsiTypeElement returnTypeElement = method.getReturnTypeElement();
if (returnTypeElement != null) {
return returnTypeElement.getTextRangeInParent();
}
}
return getMemberDeclarationTextRange(psi);
})
.withRawDescription((cls, ctx) -> message("method.inheritance.clash.incompatible.return.types",
formatClashMethodMessage(ctx.method(), ctx.superMethod(), true)));
public static final Parameterized<PsiMember, @NotNull IncompatibleOverrideExceptionContext>
METHOD_INHERITANCE_CLASH_DOES_NOT_THROW =
parameterized(PsiMember.class, IncompatibleOverrideExceptionContext.class, "method.inheritance.clash.does.not.throw")
.withRange((psi, ctx) ->
ctx.exceptionReference() != null ? ctx.exceptionReference().getTextRange().shiftLeft(psi.getTextRange().getStartOffset()) :
getMemberDeclarationTextRange(psi))
.withRawDescription((cls, ctx) -> message("method.inheritance.clash.does.not.throw",
formatClashMethodMessage(ctx.method(), ctx.superMethod(), true),
formatType(ctx.exceptionType())));
public static final Parameterized<PsiMember, AmbiguousImplicitConstructorCallContext> CONSTRUCTOR_AMBIGUOUS_IMPLICIT_CALL =
parameterized(PsiMember.class, AmbiguousImplicitConstructorCallContext.class, "constructor.ambiguous.implicit.call")
@@ -554,5 +584,20 @@ public final class JavaErrorKinds {
return message("constructor.ambiguous.implicit.call", m1, m2);
}
}
public record OverrideClashContext(@NotNull PsiMethod method, @NotNull PsiMethod superMethod) {
}
public record IncompatibleOverrideReturnTypeContext(@NotNull PsiMethod method,
@NotNull PsiType methodReturnType,
@NotNull PsiMethod superMethod,
@NotNull PsiType superMethodReturnType) {
}
public record IncompatibleOverrideExceptionContext(@NotNull PsiMethod method,
@NotNull PsiMethod superMethod,
@NotNull PsiClassType exceptionType,
@Nullable PsiJavaCodeReferenceElement exceptionReference) {
}
}
@@ -16,7 +16,6 @@ import com.intellij.java.analysis.JavaAnalysisBundle;
import com.intellij.lang.jvm.JvmModifier;
import com.intellij.lang.jvm.actions.JvmElementActionFactories;
import com.intellij.lang.jvm.actions.MemberRequestsKt;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.DefaultLanguageHighlighterColors;
import com.intellij.openapi.editor.colors.EditorColorsUtil;
import com.intellij.openapi.project.IndexNotReadyException;
@@ -57,7 +56,6 @@ import java.util.List;
import java.util.function.Consumer;
public final class HighlightMethodUtil {
private static final Logger LOG = Logger.getInstance(HighlightMethodUtil.class);
private HighlightMethodUtil() { }
@@ -79,70 +77,6 @@ public final class HighlightMethodUtil {
JavaHighlightUtil.formatMethod(method2));
}
static HighlightInfo.Builder checkMethodWeakerPrivileges(@NotNull MethodSignatureBackedByPsiMethod methodSignature,
@NotNull List<? extends HierarchicalMethodSignature> superMethodSignatures,
boolean includeRealPositionInfo,
@NotNull PsiFile containingFile, @Nullable Ref<? super String> description) {
PsiMethod method = methodSignature.getMethod();
PsiModifierList modifierList = method.getModifierList();
if (modifierList.hasModifierProperty(PsiModifier.PUBLIC)) return null;
for (MethodSignatureBackedByPsiMethod superMethodSignature : superMethodSignatures) {
PsiMethod superMethod = superMethodSignature.getMethod();
if (method.hasModifierProperty(PsiModifier.ABSTRACT) && !MethodSignatureUtil.isSuperMethod(superMethod, method)) continue;
if (!PsiUtil.isAccessible(containingFile.getProject(), superMethod, method, null)) continue;
if (!includeRealPositionInfo && MethodSignatureUtil.isSuperMethod(superMethod, method)) continue;
HighlightInfo.Builder info = isWeaker(method, superMethod, includeRealPositionInfo, description);
if (info != null) return info;
}
return null;
}
private static HighlightInfo.Builder isWeaker(@NotNull PsiMethod method,
@NotNull PsiMethod superMethod,
boolean includeRealPositionInfo, @Nullable Ref<? super String> descriptionH) {
PsiModifierList modifierList = method.getModifierList();
int accessLevel = PsiUtil.getAccessLevel(modifierList);
String accessModifier = PsiUtil.getAccessModifier(accessLevel);
int superAccessLevel = PsiUtil.getAccessLevel(superMethod.getModifierList());
if (accessLevel < superAccessLevel) {
String description = JavaErrorBundle.message("weaker.privileges",
createClashMethodMessage(method, superMethod, true),
VisibilityUtil.toPresentableText(accessModifier),
PsiUtil.getAccessModifier(superAccessLevel));
if (descriptionH != null) {
descriptionH.set(description);
}
TextRange textRange = TextRange.EMPTY_RANGE;
if (includeRealPositionInfo) {
PsiElement keyword = PsiUtil.findModifierInList(modifierList, accessModifier);
if (keyword != null) {
textRange = keyword.getTextRange();
}
else {
// in case of package-private or some crazy third-party plugin where some access modifier implied even if it's absent
PsiIdentifier identifier = method.getNameIdentifier();
if (identifier != null) {
textRange = identifier.getTextRange();
}
}
}
HighlightInfo.Builder info =
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description);
IntentionAction action = QuickFixFactory.getInstance().createChangeModifierFix();
info.registerFix(action, null, null, null, null);
return info;
}
return null;
}
static HighlightInfo.Builder checkMethodIncompatibleReturnType(@NotNull MethodSignatureBackedByPsiMethod methodSignature,
@NotNull List<? extends HierarchicalMethodSignature> superMethodSignatures,
boolean includeRealPositionInfo,
@Nullable Ref<? super String> description) {
return checkMethodIncompatibleReturnType(methodSignature, superMethodSignatures, includeRealPositionInfo, null, description);
}
static HighlightInfo.Builder checkMethodIncompatibleReturnType(@NotNull MethodSignatureBackedByPsiMethod methodSignature,
@NotNull List<? extends HierarchicalMethodSignature> superMethodSignatures,
@@ -171,7 +105,7 @@ public final class HighlightMethodUtil {
}
HighlightInfo.Builder info = checkSuperMethodSignature(
superMethod, superMethodSignature, superReturnType, method, methodSignature, returnType,
JavaErrorBundle.message("incompatible.return.type"), textRange, PsiUtil.getLanguageLevel(aClass), description);
textRange, PsiUtil.getLanguageLevel(aClass), description);
if (info != null) {
return info;
}
@@ -186,7 +120,6 @@ public final class HighlightMethodUtil {
@NotNull PsiMethod method,
@NotNull MethodSignatureBackedByPsiMethod methodSignature,
@NotNull PsiType returnType,
@NotNull @Nls String detailMessage,
@NotNull TextRange range,
@NotNull LanguageLevel languageLevel,
@Nullable Ref<? super String> description) {
@@ -222,7 +155,8 @@ public final class HighlightMethodUtil {
}
}
return createIncompatibleReturnTypeMessage(method, superMethod, substitutedSuperReturnType, returnType, detailMessage, range,
return createIncompatibleReturnTypeMessage(method, superMethod, substitutedSuperReturnType, returnType,
JavaErrorBundle.message("incompatible.return.type"), range,
description);
}
@@ -261,133 +195,6 @@ public final class HighlightMethodUtil {
}
static HighlightInfo.Builder checkMethodOverridesFinal(@NotNull MethodSignatureBackedByPsiMethod methodSignature,
@NotNull List<? extends HierarchicalMethodSignature> superMethodSignatures) {
PsiMethod method = methodSignature.getMethod();
for (MethodSignatureBackedByPsiMethod superMethodSignature : superMethodSignatures) {
PsiMethod superMethod = superMethodSignature.getMethod();
HighlightInfo.Builder info = checkSuperMethodIsFinal(method, superMethod);
if (info != null) return info;
}
return null;
}
private static HighlightInfo.Builder checkSuperMethodIsFinal(@NotNull PsiMethod method, @NotNull PsiMethod superMethod) {
// strange things happen when super method is from Object and method from interface
if (superMethod.hasModifierProperty(PsiModifier.FINAL)) {
PsiClass superClass = superMethod.getContainingClass();
String description = JavaErrorBundle.message("final.method.override",
JavaHighlightUtil.formatMethod(method),
JavaHighlightUtil.formatMethod(superMethod),
superClass != null ? HighlightUtil.formatClass(superClass) : "<unknown>");
TextRange textRange = HighlightNamesUtil.getMethodDeclarationTextRange(method);
HighlightInfo.Builder errorResult = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description);
QuickFixAction.registerQuickFixActions(errorResult, null, JvmElementActionFactories.createModifierActions(superMethod, MemberRequestsKt.modifierRequest(JvmModifier.FINAL, false)));
return errorResult;
}
return null;
}
static HighlightInfo.Builder checkMethodIncompatibleThrows(@NotNull MethodSignatureBackedByPsiMethod methodSignature,
@NotNull List<? extends HierarchicalMethodSignature> superMethodSignatures,
boolean includeRealPositionInfo,
@NotNull PsiClass analyzedClass, @Nullable Ref<? super String> descriptionH) {
PsiMethod method = methodSignature.getMethod();
PsiClass aClass = method.getContainingClass();
if (aClass == null) return null;
PsiSubstitutor superSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(aClass, analyzedClass, PsiSubstitutor.EMPTY);
PsiClassType[] exceptions = method.getThrowsList().getReferencedTypes();
PsiJavaCodeReferenceElement[] referenceElements;
List<PsiElement> exceptionContexts;
if (includeRealPositionInfo) {
exceptionContexts = new ArrayList<>();
referenceElements = method.getThrowsList().getReferenceElements();
}
else {
exceptionContexts = null;
referenceElements = null;
}
List<PsiClassType> checkedExceptions = new ArrayList<>();
for (int i = 0; i < exceptions.length; i++) {
PsiClassType exception = exceptions[i];
if (exception == null) {
LOG.error("throws: " + method.getThrowsList().getText() + "; method: " + method);
}
else if (!ExceptionUtil.isUncheckedException(exception)) {
checkedExceptions.add(exception);
if (includeRealPositionInfo && i < referenceElements.length) {
PsiJavaCodeReferenceElement exceptionRef = referenceElements[i];
exceptionContexts.add(exceptionRef);
}
}
}
for (MethodSignatureBackedByPsiMethod superMethodSignature : superMethodSignatures) {
PsiMethod superMethod = superMethodSignature.getMethod();
int index = getExtraExceptionNum(methodSignature, superMethodSignature, checkedExceptions, superSubstitutor);
if (index != -1) {
if (aClass.isInterface()) {
PsiClass superContainingClass = superMethod.getContainingClass();
if (superContainingClass != null && !superContainingClass.isInterface()) continue;
if (superContainingClass != null && !aClass.isInheritor(superContainingClass, true)) continue;
}
PsiClassType exception = checkedExceptions.get(index);
String description = JavaErrorBundle.message("overridden.method.does.not.throw",
createClashMethodMessage(method, superMethod, true),
JavaHighlightUtil.formatType(exception));
if (descriptionH != null) {
descriptionH.set(description);
}
TextRange textRange;
if (includeRealPositionInfo) {
PsiElement exceptionContext = exceptionContexts.get(index);
textRange = exceptionContext.getTextRange();
}
else {
textRange = TextRange.EMPTY_RANGE;
}
HighlightInfo.Builder errorResult =
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description);
IntentionAction action1 = QuickFixFactory.getInstance().createMethodThrowsFix(method, exception, false, false);
errorResult.registerFix(action1, null, null, null, null);
IntentionAction action = QuickFixFactory.getInstance().createMethodThrowsFix(superMethod, exception, true, true);
errorResult.registerFix(action, null, null, null, null);
return errorResult;
}
}
return null;
}
// return number of exception which was not declared in super method or -1
private static int getExtraExceptionNum(@NotNull MethodSignature methodSignature,
@NotNull MethodSignatureBackedByPsiMethod superSignature,
@NotNull List<? extends PsiClassType> checkedExceptions,
@NotNull PsiSubstitutor substitutorForDerivedClass) {
PsiMethod superMethod = superSignature.getMethod();
PsiSubstitutor substitutorForMethod = MethodSignatureUtil.getSuperMethodSignatureSubstitutor(methodSignature, superSignature);
for (int i = 0; i < checkedExceptions.size(); i++) {
PsiClassType checkedEx = checkedExceptions.get(i);
PsiType substituted = substitutorForMethod == null ? TypeConversionUtil.erasure(checkedEx) : substitutorForMethod.substitute(checkedEx);
PsiType exception = substitutorForDerivedClass.substitute(substituted);
if (!isMethodThrows(superMethod, substitutorForMethod, exception, substitutorForDerivedClass)) {
return i;
}
}
return -1;
}
private static boolean isMethodThrows(@NotNull PsiMethod method,
@Nullable PsiSubstitutor substitutorForMethod,
@NotNull PsiType exception,
@NotNull PsiSubstitutor substitutorForDerivedClass) {
PsiClassType[] thrownExceptions = method.getThrowsList().getReferencedTypes();
for (PsiClassType thrownException1 : thrownExceptions) {
PsiType thrownException = substitutorForMethod != null ? substitutorForMethod.substitute(thrownException1) : TypeConversionUtil.erasure(thrownException1);
thrownException = substitutorForDerivedClass.substitute(thrownException);
if (TypeConversionUtil.isAssignable(thrownException, exception)) return true;
}
return false;
}
static void checkMethodCall(@NotNull PsiMethodCallExpression methodCall,
@NotNull PsiResolveHelper resolveHelper,
@NotNull LanguageLevel languageLevel,
@@ -1382,138 +1189,6 @@ public final class HighlightMethodUtil {
}
private static String checkInterfaceInheritedMethodsReturnTypesDescription(@NotNull List<? extends MethodSignatureBackedByPsiMethod> superMethodSignatures,
@NotNull LanguageLevel languageLevel) {
if (superMethodSignatures.size() < 2) return null;
MethodSignatureBackedByPsiMethod[] returnTypeSubstitutable = {superMethodSignatures.get(0)};
for (int i = 1; i < superMethodSignatures.size(); i++) {
PsiMethod currentMethod = returnTypeSubstitutable[0].getMethod();
PsiType currentType = returnTypeSubstitutable[0].getSubstitutor().substitute(currentMethod.getReturnType());
MethodSignatureBackedByPsiMethod otherSuperSignature = superMethodSignatures.get(i);
PsiMethod otherSuperMethod = otherSuperSignature.getMethod();
PsiSubstitutor otherSubstitutor = otherSuperSignature.getSubstitutor();
PsiType otherSuperReturnType = otherSubstitutor.substitute(otherSuperMethod.getReturnType());
PsiSubstitutor unifyingSubstitutor = MethodSignatureUtil.getSuperMethodSignatureSubstitutor(returnTypeSubstitutable[0],
otherSuperSignature);
if (unifyingSubstitutor != null) {
otherSuperReturnType = unifyingSubstitutor.substitute(otherSuperReturnType);
currentType = unifyingSubstitutor.substitute(currentType);
}
if (otherSuperReturnType == null || currentType == null || otherSuperReturnType.equals(currentType)) continue;
PsiType otherReturnType = otherSuperReturnType;
PsiType curType = currentType;
String info =
LambdaUtil.performWithSubstitutedParameterBounds(otherSuperMethod.getTypeParameters(), otherSubstitutor, () -> {
if (languageLevel.isAtLeast(LanguageLevel.JDK_1_5)) {
//http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.8 Example 8.1.5-3
if (!(otherReturnType instanceof PsiPrimitiveType || curType instanceof PsiPrimitiveType)) {
if (otherReturnType.isAssignableFrom(curType)) return null;
if (curType.isAssignableFrom(otherReturnType)) {
returnTypeSubstitutable[0] = otherSuperSignature;
return null;
}
}
if (otherSuperMethod.getTypeParameters().length > 0 && JavaGenericsUtil.isRawToGeneric(otherReturnType, curType)) return null;
}
return MessageFormat.format("{0}; {1}", createClashMethodMessage(otherSuperMethod, currentMethod, true), JavaErrorBundle.message("unrelated.overriding.methods.return.types"));
});
if (info != null) return info;
}
return null;
}
static HighlightInfo.Builder checkOverrideEquivalentInheritedMethods(@NotNull PsiClass aClass,
@NotNull PsiFile containingFile,
@NotNull LanguageLevel languageLevel) {
Collection<HierarchicalMethodSignature> visibleSignatures = aClass.getVisibleSignatures();
if (aClass.getImplementsListTypes().length == 0 && aClass.getExtendsListTypes().length == 0) {
// optimization: do not analyze unrelated methods from Object: in case of no inheritance they can't conflict
return null;
}
PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(aClass.getProject()).getResolveHelper();
String description = null;
boolean appendImplementMethodFix = true;
Ultimate:
for (HierarchicalMethodSignature signature : visibleSignatures) {
PsiMethod method = signature.getMethod();
if (!resolveHelper.isAccessible(method, aClass, null)) continue;
List<HierarchicalMethodSignature> superSignatures = signature.getSuperSignatures();
boolean allAbstracts = method.hasModifierProperty(PsiModifier.ABSTRACT);
PsiClass containingClass = method.getContainingClass();
if (containingClass == null || aClass.equals(containingClass)) continue; //to be checked at method level
if (aClass.isInterface() && !containingClass.isInterface()) continue;
String error;
if (allAbstracts) {
superSignatures = new ArrayList<>(superSignatures);
superSignatures.add(0, signature);
error = checkInterfaceInheritedMethodsReturnTypesDescription(superSignatures, languageLevel);
}
else {
Ref<String> descriptionH = new Ref<>();
checkMethodIncompatibleReturnType(signature, superSignatures, false, descriptionH);
error = descriptionH.get();
}
if (error != null) {
description = error;
}
if (method.hasModifierProperty(PsiModifier.STATIC) &&
//jsl 8, chapter 9.4.1
//chapter 8.4.8.2 speaks about a class that "declares or inherits a static method",
// at the same time the rule from chapter 9.4.1 speaks only about an interface that "declares a static method"
//There is no point to add java version check, because static methods in interfaces are allowed from java 8 too.
(!aClass.isInterface() ||
aClass.getManager().areElementsEquivalent(aClass, method.getContainingClass()))) {
for (HierarchicalMethodSignature superSignature : superSignatures) {
PsiMethod superMethod = superSignature.getMethod();
if (!superMethod.hasModifierProperty(PsiModifier.STATIC)) {
PsiClass superClass = superMethod.getContainingClass();
description = JavaErrorBundle.message("static.method.cannot.override.instance.method",
JavaHighlightUtil.formatMethod(method),
HighlightUtil.formatClass(containingClass),
JavaHighlightUtil.formatMethod(superMethod),
superClass != null ? HighlightUtil.formatClass(superClass) : "<unknown>");
appendImplementMethodFix = false;
break Ultimate;
}
}
continue;
}
if (description == null) {
Ref<@Nls String> descriptionH = new Ref<>();
checkMethodIncompatibleThrows(signature, superSignatures, false, aClass, descriptionH);
description = descriptionH.get();
}
if (description == null) {
Ref<@Nls String> descriptionH = new Ref<>();
checkMethodWeakerPrivileges(signature, superSignatures, false, containingFile, descriptionH);
description = descriptionH.get();
}
if (description != null) break;
}
if (description != null) {
// show error info at the class level
TextRange textRange = HighlightNamesUtil.getClassDeclarationTextRange(aClass);
HighlightInfo.Builder highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description);
if (appendImplementMethodFix) {
IntentionAction action = QuickFixFactory.getInstance().createImplementMethodsFix(aClass);
highlightInfo.registerFix(action, null, null, null, null);
}
return highlightInfo;
}
return null;
}
static HighlightInfo.Builder checkConstructorHandleSuperClassExceptions(@NotNull PsiMethod method) {
if (!method.isConstructor()) {
return null;
@@ -61,7 +61,6 @@ import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.util.*;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.regex.Matcher;
@@ -869,43 +868,16 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
super.visitModifierList(list);
PsiElement parent = list.getParent();
if (parent instanceof PsiMethod method) {
MethodSignatureBackedByPsiMethod methodSignature = MethodSignatureBackedByPsiMethod.create(method, PsiSubstitutor.EMPTY);
PsiClass aClass = method.getContainingClass();
if (!method.isConstructor()) {
try {
List<HierarchicalMethodSignature> superMethodSignatures = method.getHierarchicalMethodSignature().getSuperSignatures();
if (!superMethodSignatures.isEmpty()) {
if (!method.hasModifierProperty(PsiModifier.STATIC)) {
if (!hasErrorResults()) {
add(HighlightMethodUtil.checkMethodWeakerPrivileges(methodSignature, superMethodSignatures, true, myFile, null));
}
if (!hasErrorResults()) add(HighlightMethodUtil.checkMethodOverridesFinal(methodSignature, superMethodSignatures));
}
if (!hasErrorResults()) {
add(HighlightMethodUtil.checkMethodIncompatibleReturnType(methodSignature, superMethodSignatures, true, null));
}
if (aClass != null && !hasErrorResults()) {
add(HighlightMethodUtil.checkMethodIncompatibleThrows(methodSignature, superMethodSignatures, true, aClass, null));
}
}
}
catch (IndexNotReadyException ignored) {
}
}
if (!hasErrorResults() && aClass != null) {
GenericsHighlightUtil.computeOverrideEquivalentMethodErrors(aClass, myOverrideEquivalentMethodsVisitedClasses, myOverrideEquivalentMethodsErrors);
myErrorSink.accept(myOverrideEquivalentMethodsErrors.get(method));
}
}
else if (parent instanceof PsiClass aClass) {
try {
if (!hasErrorResults()) add(HighlightMethodUtil.checkOverrideEquivalentInheritedMethods(aClass, myFile, myLanguageLevel));
if (!hasErrorResults()) {
GenericsHighlightUtil.computeOverrideEquivalentMethodErrors(aClass, myOverrideEquivalentMethodsVisitedClasses, myOverrideEquivalentMethodsErrors);
myErrorSink.accept(myOverrideEquivalentMethodsErrors.get(aClass));
}
}
catch (IndexNotReadyException ignored) {
if (!hasErrorResults()) {
GenericsHighlightUtil.computeOverrideEquivalentMethodErrors(aClass, myOverrideEquivalentMethodsVisitedClasses, myOverrideEquivalentMethodsErrors);
myErrorSink.accept(myOverrideEquivalentMethodsErrors.get(aClass));
}
}
}
@@ -22,6 +22,7 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.Service;
import com.intellij.pom.java.JavaFeature;
import com.intellij.psi.*;
import com.intellij.psi.impl.light.LightRecordMethod;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
@@ -118,12 +119,36 @@ final class JavaErrorFixProvider {
});
fix(METHOD_ABSTRACT_BODY, error -> myFactory.createPushDownMethodFix());
fix(METHOD_NATIVE_BODY, error -> myFactory.createPushDownMethodFix());
fix(METHOD_STATIC_OVERRIDES_INSTANCE, error -> removeModifierFix(error.psi(), PsiModifier.STATIC));
fix(METHOD_INSTANCE_OVERRIDES_STATIC, error -> maybeAddModifierFix(error.psi(), PsiModifier.STATIC));
fix(METHOD_STATIC_OVERRIDES_INSTANCE, error -> maybeAddModifierFix(error.context(), PsiModifier.STATIC));
fix(METHOD_INSTANCE_OVERRIDES_STATIC, error -> removeModifierFix(error.context(), PsiModifier.STATIC));
fix(METHOD_STATIC_OVERRIDES_INSTANCE, error -> removeModifierFix(error.context().method(), PsiModifier.STATIC));
fix(METHOD_INSTANCE_OVERRIDES_STATIC, error -> maybeAddModifierFix(error.context().method(), PsiModifier.STATIC));
fix(METHOD_STATIC_OVERRIDES_INSTANCE, error -> maybeAddModifierFix(error.context().superMethod(), PsiModifier.STATIC));
fix(METHOD_INSTANCE_OVERRIDES_STATIC, error -> removeModifierFix(error.context().superMethod(), PsiModifier.STATIC));
fix(METHOD_OVERRIDES_FINAL, error -> removeModifierFix(error.context(), PsiModifier.FINAL));
fix(METHOD_INHERITANCE_WEAKER_PRIVILEGES, error -> myFactory.createChangeModifierFix());
fix(METHOD_INHERITANCE_WEAKER_PRIVILEGES,
error -> error.psi() instanceof PsiMethod ? myFactory.createChangeModifierFix() :
error.psi() instanceof PsiClass cls ? myFactory.createImplementMethodsFix(cls) : null);
multi(METHOD_INHERITANCE_CLASH_DOES_NOT_THROW, error -> List.of(
myFactory.createMethodThrowsFix(error.context().method(), error.context().exceptionType(), false, false),
myFactory.createMethodThrowsFix(error.context().superMethod(), error.context().exceptionType(), true, true)
));
multi(METHOD_INHERITANCE_CLASH_INCOMPATIBLE_RETURN_TYPES, error -> {
IncompatibleOverrideReturnTypeContext context = error.context();
PsiMethod method = context.method();
List<CommonIntentionAction> registrar = new ArrayList<>();
if (method instanceof LightRecordMethod recordMethod) {
registrar.addAll(
HighlightFixUtil.getChangeVariableTypeFixes(recordMethod.getRecordComponent(), context.superMethodReturnType()));
}
else {
registrar.add(myFactory.createMethodReturnFix(method, context.superMethodReturnType(), false));
}
registrar.add(myFactory.createSuperMethodReturnFix(context.superMethod(), context.methodReturnType()));
PsiClass returnClass = PsiUtil.resolveClassInClassTypeOnly(context.methodReturnType());
if (returnClass != null && context.superMethodReturnType() instanceof PsiClassType classType) {
registrar.add(myFactory.createChangeParameterClassFix(returnClass, classType));
}
return registrar;
});
}
private void createConstructorFixes() {
@@ -1,6 +1,6 @@
interface ConflictWithObject {
<error descr="'notify()' cannot override 'notify()' in 'java.lang.Object'; overridden method is final">public Object notify()</error>;
<error descr="'notify()' cannot override 'notify()' in 'java.lang.Object'; overridden method is final">public Object notify();</error>
}
//--override final-------------------------------------------------------------------------