mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-08 15:09:39 +07:00
[java-highlighting] Implicit constructor call problems migrated
Part of IDEA-365344 Create a new Java error highlighter with minimal dependencies (PSI only) GitOrigin-RevId: eb4befa6b9d6d9b5b8c968fed54abb11c419f578
This commit is contained in:
committed by
intellij-monorepo-bot
parent
8f7935d584
commit
41b2c21759
@@ -125,6 +125,9 @@ type.parameter.duplicate=Duplicate type parameter: ''{0}''
|
||||
|
||||
method.throws.class.name.expected=Class name expected
|
||||
|
||||
constructor.ambiguous.implicit.call=Ambiguous implicit constructor call: both ''{0}'' and ''{1}'' match
|
||||
constructor.no.default=There is no parameterless constructor available in ''{0}''
|
||||
|
||||
type.incompatible=Incompatible types. Found: ''{1}'', required: ''{0}''
|
||||
# {0} - left raw type, {1} - required type arguments row, {2} - right raw type, {3} - found type arguments row, {4} - reason, {5} - greyed title color
|
||||
type.incompatible.html.tooltip=\
|
||||
@@ -158,3 +161,8 @@ literal.string.illegal.line.end=Line end not allowed in string literals
|
||||
literal.string.illegal.escape=Illegal escape character in string literal
|
||||
literal.text.block.unclosed=Unclosed text block
|
||||
literal.text.block.no.new.line=Illegal text block start: missing new line after opening quotes
|
||||
|
||||
#{0} - exceptions list (comma separated), {1} - exceptions count in the list
|
||||
exception.unhandled=Unhandled {1, choice, 0#exception|2#exceptions}: {0}
|
||||
#{0} - exceptions list (comma separated), {1} - exceptions count in the list, {2} - exception source
|
||||
exception.unhandled.close=Unhandled {1, choice, 0#exception|2#exceptions} from {2}: {0}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package com.intellij.java.codeserver.highlighting;
|
||||
|
||||
import com.intellij.codeInsight.ClassUtil;
|
||||
import com.intellij.codeInsight.ExceptionUtil;
|
||||
import com.intellij.java.codeserver.highlighting.errors.JavaErrorKind;
|
||||
import com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds;
|
||||
import com.intellij.openapi.module.Module;
|
||||
@@ -13,6 +14,7 @@ import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.pom.java.JavaFeature;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.searches.DirectClassInheritorsSearch;
|
||||
import com.intellij.psi.search.searches.ImplicitClassSearch;
|
||||
@@ -658,6 +660,73 @@ final class ClassChecker {
|
||||
}
|
||||
}
|
||||
|
||||
void checkClassDoesNotCallSuperConstructorOrHandleExceptions(PsiClass aClass) {
|
||||
if (aClass.isEnum()) return;
|
||||
// check only no-ctr classes. Problem with specific constructor will be highlighted inside it
|
||||
if (aClass.getConstructors().length != 0) return;
|
||||
// find no-args base class ctr
|
||||
checkBaseClassDefaultConstructorProblem(aClass, aClass, PsiClassType.EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
void checkBaseClassDefaultConstructorProblem(@NotNull PsiClass aClass,
|
||||
@NotNull PsiMember anchor,
|
||||
PsiClassType @NotNull [] handledExceptions) {
|
||||
if (aClass instanceof PsiAnonymousClass) return;
|
||||
PsiClass baseClass = aClass.getSuperClass();
|
||||
if (baseClass == null) return;
|
||||
PsiMethod[] constructors = baseClass.getConstructors();
|
||||
if (constructors.length == 0) return;
|
||||
|
||||
PsiElement resolved = JavaResolveUtil.resolveImaginarySuperCallInThisPlace(aClass, aClass.getProject(), baseClass);
|
||||
List<PsiMethod> constructorCandidates = (resolved != null ? Collections.singletonList((PsiMethod)resolved)
|
||||
: Arrays.asList(constructors))
|
||||
.stream()
|
||||
.filter(constructor -> {
|
||||
PsiParameter[] parameters = constructor.getParameterList().getParameters();
|
||||
return (parameters.length == 0 || parameters.length == 1 && parameters[0].isVarArgs()) &&
|
||||
PsiResolveHelper.getInstance(aClass.getProject()).isAccessible(constructor, aClass, null);
|
||||
})
|
||||
.limit(2).toList();
|
||||
|
||||
if (constructorCandidates.size() >= 2) {// two ambiguous var-args-only constructors
|
||||
var context =
|
||||
new JavaErrorKinds.AmbiguousImplicitConstructorCallContext(aClass, constructorCandidates.get(0), constructorCandidates.get(1));
|
||||
myVisitor.report(JavaErrorKinds.CONSTRUCTOR_AMBIGUOUS_IMPLICIT_CALL.create(anchor, context));
|
||||
} else if (!constructorCandidates.isEmpty()) {
|
||||
checkDefaultConstructorThrowsException(constructorCandidates.get(0), anchor, handledExceptions);
|
||||
} else {
|
||||
// no need to distract with missing constructor error when there is already a "Cannot inherit from final class" error message
|
||||
if (baseClass.hasModifierProperty(PsiModifier.FINAL)) return;
|
||||
myVisitor.report(JavaErrorKinds.CONSTRUCTOR_NO_DEFAULT.create(anchor, baseClass));
|
||||
}
|
||||
}
|
||||
|
||||
private void checkDefaultConstructorThrowsException(@NotNull PsiMethod constructor, @NotNull PsiMember anchor, PsiClassType[] handledExceptions) {
|
||||
PsiClassType[] referencedTypes = constructor.getThrowsList().getReferencedTypes();
|
||||
List<PsiClassType> exceptions = new ArrayList<>();
|
||||
for (PsiClassType referencedType : referencedTypes) {
|
||||
if (!ExceptionUtil.isUncheckedException(referencedType) && !ExceptionUtil.isHandledBy(referencedType, handledExceptions)) {
|
||||
exceptions.add(referencedType);
|
||||
}
|
||||
}
|
||||
if (!exceptions.isEmpty()) {
|
||||
myVisitor.report(JavaErrorKinds.EXCEPTION_UNHANDLED.create(anchor, exceptions));
|
||||
}
|
||||
}
|
||||
|
||||
void checkConstructorCallsBaseClassConstructor(@NotNull PsiMethod constructor) {
|
||||
if (!constructor.isConstructor()) return;
|
||||
PsiClass aClass = constructor.getContainingClass();
|
||||
if (aClass == null) return;
|
||||
if (aClass.isEnum()) return;
|
||||
PsiCodeBlock body = constructor.getBody();
|
||||
if (body == null) return;
|
||||
|
||||
if (JavaPsiConstructorUtil.findThisOrSuperCallInConstructor(constructor) != null) return;
|
||||
PsiClassType[] handledExceptions = constructor.getThrowsList().getReferencedTypes();
|
||||
checkBaseClassDefaultConstructorProblem(aClass, constructor, handledExceptions);
|
||||
}
|
||||
|
||||
private static @Unmodifiable @NotNull Map<PsiJavaCodeReferenceElement, PsiClass> getPermittedClassesRefs(@NotNull PsiClass psiClass) {
|
||||
PsiReferenceList permitsList = psiClass.getPermitsList();
|
||||
if (permitsList == null) return Collections.emptyMap();
|
||||
|
||||
@@ -204,6 +204,9 @@ final class JavaErrorVisitor extends JavaElementVisitor {
|
||||
super.visitModifierList(list);
|
||||
PsiElement parent = list.getParent();
|
||||
if (parent instanceof PsiMethod method) {
|
||||
if (!hasErrorResults()) {
|
||||
myClassChecker.checkConstructorCallsBaseClassConstructor(method);
|
||||
}
|
||||
|
||||
}
|
||||
else if (parent instanceof PsiClass aClass) {
|
||||
@@ -213,7 +216,7 @@ final class JavaErrorVisitor extends JavaElementVisitor {
|
||||
myClassChecker.checkClassMustBeAbstract(aClass);
|
||||
}
|
||||
if (!hasErrorResults()) {
|
||||
//myClassChecker.checkClassDoesNotCallSuperConstructorOrHandleExceptions(aClass, getResolveHelper(getProject()));
|
||||
myClassChecker.checkClassDoesNotCallSuperConstructorOrHandleExceptions(aClass);
|
||||
}
|
||||
//if (!hasErrorResults()) add(HighlightMethodUtil.checkOverrideEquivalentInheritedMethods(aClass, myFile, myLanguageLevel));
|
||||
if (!hasErrorResults()) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.intellij.codeInsight.highlighting.HighlightUsagesDescriptionLocation;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.util.NlsSafe;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.tree.ElementType;
|
||||
import com.intellij.psi.impl.source.tree.TreeUtil;
|
||||
@@ -14,6 +15,8 @@ import com.intellij.psi.util.PsiTypesUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
final class JavaErrorFormatUtil {
|
||||
static @NotNull @NlsSafe String formatMethod(@NotNull PsiMethod method) {
|
||||
return PsiFormatUtil.formatMethod(method, PsiSubstitutor.EMPTY, PsiFormatUtilBase.SHOW_NAME | PsiFormatUtilBase.SHOW_PARAMETERS,
|
||||
@@ -41,6 +44,13 @@ final class JavaErrorFormatUtil {
|
||||
return new TextRange(start, end).shiftLeft(method.getTextRange().getStartOffset());
|
||||
}
|
||||
|
||||
static @Nullable TextRange getMemberDeclarationTextRange(@NotNull PsiMember member) {
|
||||
return member instanceof PsiClass psiClass ? getClassDeclarationTextRange(psiClass) :
|
||||
member instanceof PsiMethod psiMethod ? getMethodDeclarationTextRange(psiMethod) :
|
||||
member instanceof PsiField psiField ? getFieldDeclarationTextRange(psiField) :
|
||||
null;
|
||||
}
|
||||
|
||||
static @NotNull TextRange getFieldDeclarationTextRange(@NotNull PsiField field) {
|
||||
PsiModifierList modifierList = field.getModifierList();
|
||||
TextRange range = field.getTextRange();
|
||||
@@ -91,6 +101,10 @@ final class JavaErrorFormatUtil {
|
||||
return textRange.getStartOffset();
|
||||
}
|
||||
|
||||
static @NotNull String formatTypes(@NotNull Collection<? extends PsiClassType> unhandled) {
|
||||
return StringUtil.join(unhandled, JavaErrorFormatUtil::formatType, ", ");
|
||||
}
|
||||
|
||||
static @NotNull String formatType(@Nullable PsiType type) {
|
||||
return type == null ? PsiKeyword.NULL : PsiTypesUtil.removeExternalAnnotations(type).getInternalCanonicalText();
|
||||
}
|
||||
|
||||
@@ -11,20 +11,18 @@ import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.pom.java.JavaFeature;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiLiteralUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiTypesUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.*;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.PropertyKey;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.intellij.java.codeserver.highlighting.JavaCompilationErrorBundle.message;
|
||||
import static com.intellij.java.codeserver.highlighting.errors.JavaErrorFormatUtil.formatClass;
|
||||
import static com.intellij.java.codeserver.highlighting.errors.JavaErrorFormatUtil.formatMethod;
|
||||
import static com.intellij.java.codeserver.highlighting.errors.JavaErrorFormatUtil.*;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static java.util.Objects.requireNonNullElse;
|
||||
|
||||
@@ -355,6 +353,21 @@ public final class JavaErrorKinds {
|
||||
|
||||
public static final Simple<PsiJavaCodeReferenceElement> METHOD_THROWS_CLASS_NAME_EXPECTED =
|
||||
error("method.throws.class.name.expected");
|
||||
|
||||
public static final Parameterized<PsiMember, AmbiguousImplicitConstructorCallContext> CONSTRUCTOR_AMBIGUOUS_IMPLICIT_CALL =
|
||||
parameterized(PsiMember.class, AmbiguousImplicitConstructorCallContext.class, "constructor.ambiguous.implicit.call")
|
||||
.withRawDescription((member, ctx) -> ctx.description())
|
||||
.withRange((member, ctx) -> getMemberDeclarationTextRange(member));
|
||||
public static final Parameterized<PsiMember, PsiClass> CONSTRUCTOR_NO_DEFAULT =
|
||||
parameterized(PsiMember.class, PsiClass.class, "constructor.no.default")
|
||||
.withRawDescription((member, cls) -> message("constructor.no.default", formatClass(requireNonNull(cls))))
|
||||
.withRange((member, ctx) -> getMemberDeclarationTextRange(member));
|
||||
|
||||
public static final Parameterized<PsiElement, Collection<PsiClassType>> EXCEPTION_UNHANDLED =
|
||||
error(PsiElement.class, "exception.unhandled")
|
||||
.withRange(psi -> psi instanceof PsiMember member ? getMemberDeclarationTextRange(member) : null)
|
||||
.<Collection<PsiClassType>>parameterized()
|
||||
.withRawDescription((psi, unhandled) -> message("exception.unhandled", formatTypes(unhandled), unhandled.size()));
|
||||
|
||||
public static final Parameterized<PsiElement, JavaIncompatibleTypeError> TYPE_INCOMPATIBLE =
|
||||
parameterized(PsiElement.class, JavaIncompatibleTypeError.class, "type.incompatible")
|
||||
@@ -447,4 +460,29 @@ public final class JavaErrorKinds {
|
||||
return PsiUtil.getEnclosingStaticElement(place, outerClass);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A context for {@link #CONSTRUCTOR_AMBIGUOUS_IMPLICIT_CALL} error kind
|
||||
* @param psiClass a class where ambiguous call is performed
|
||||
* @param candidate1 first constructor candidate in super class
|
||||
* @param candidate2 second constructor candidate in super class
|
||||
*/
|
||||
public record AmbiguousImplicitConstructorCallContext(@NotNull PsiClass psiClass,
|
||||
@NotNull PsiMethod candidate1,
|
||||
@NotNull PsiMethod candidate2) {
|
||||
@Nls String description() {
|
||||
String m1 = PsiFormatUtil.formatMethod(candidate1, PsiSubstitutor.EMPTY,
|
||||
PsiFormatUtilBase.SHOW_CONTAINING_CLASS |
|
||||
PsiFormatUtilBase.SHOW_NAME |
|
||||
PsiFormatUtilBase.SHOW_PARAMETERS,
|
||||
PsiFormatUtilBase.SHOW_TYPE);
|
||||
String m2 = PsiFormatUtil.formatMethod(candidate2, PsiSubstitutor.EMPTY,
|
||||
PsiFormatUtilBase.SHOW_CONTAINING_CLASS |
|
||||
PsiFormatUtilBase.SHOW_NAME |
|
||||
PsiFormatUtilBase.SHOW_PARAMETERS,
|
||||
PsiFormatUtilBase.SHOW_TYPE);
|
||||
return message("constructor.ambiguous.implicit.call", m1, m2);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -105,6 +105,9 @@ public abstract class QuickFixFactory {
|
||||
public abstract @NotNull IntentionAction createAddExceptionToCatchFix();
|
||||
|
||||
public abstract @NotNull IntentionAction createAddExceptionToThrowsFix(@NotNull PsiElement element);
|
||||
|
||||
public abstract @NotNull IntentionAction createAddExceptionToThrowsFix(@NotNull PsiElement element,
|
||||
@NotNull Collection<PsiClassType> exceptionsToAdd);
|
||||
|
||||
public abstract @NotNull IntentionAction createAddExceptionFromFieldInitializerToConstructorThrowsFix(@NotNull PsiElement element);
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2000-2024 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.ExceptionUtil;
|
||||
import com.intellij.codeInsight.daemon.JavaErrorBundle;
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
|
||||
@@ -9,12 +8,9 @@ import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction;
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.codeInsight.intention.QuickFixFactory;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.NlsContexts;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
|
||||
import com.intellij.psi.search.searches.DirectClassInheritorsSearch;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.util.JavaPsiConstructorUtil;
|
||||
@@ -22,10 +18,6 @@ import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
@@ -53,97 +45,6 @@ public final class HighlightClassUtil {
|
||||
}
|
||||
}
|
||||
|
||||
private static @NlsContexts.DetailedDescription String checkDefaultConstructorThrowsException(@NotNull PsiMethod constructor, PsiClassType @NotNull [] handledExceptions) {
|
||||
PsiClassType[] referencedTypes = constructor.getThrowsList().getReferencedTypes();
|
||||
List<PsiClassType> exceptions = new ArrayList<>();
|
||||
for (PsiClassType referencedType : referencedTypes) {
|
||||
if (!ExceptionUtil.isUncheckedException(referencedType) && !ExceptionUtil.isHandledBy(referencedType, handledExceptions)) {
|
||||
exceptions.add(referencedType);
|
||||
}
|
||||
}
|
||||
if (!exceptions.isEmpty()) {
|
||||
return HighlightUtil.getUnhandledExceptionsDescriptor(exceptions);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static HighlightInfo.Builder checkClassDoesNotCallSuperConstructorOrHandleExceptions(@NotNull PsiClass aClass,
|
||||
@NotNull PsiResolveHelper resolveHelper) {
|
||||
if (aClass.isEnum()) return null;
|
||||
// check only no-ctr classes. Problem with specific constructor will be highlighted inside it
|
||||
if (aClass.getConstructors().length != 0) return null;
|
||||
// find no-args base class ctr
|
||||
TextRange textRange = HighlightNamesUtil.getClassDeclarationTextRange(aClass);
|
||||
return checkBaseClassDefaultConstructorProblem(aClass, resolveHelper, textRange, PsiClassType.EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
static HighlightInfo.Builder checkBaseClassDefaultConstructorProblem(@NotNull PsiClass aClass,
|
||||
@NotNull PsiResolveHelper resolveHelper,
|
||||
@NotNull TextRange range,
|
||||
PsiClassType @NotNull [] handledExceptions) {
|
||||
if (aClass instanceof PsiAnonymousClass) return null;
|
||||
PsiClass baseClass = aClass.getSuperClass();
|
||||
if (baseClass == null) return null;
|
||||
PsiMethod[] constructors = baseClass.getConstructors();
|
||||
if (constructors.length == 0) return null;
|
||||
|
||||
PsiElement resolved = JavaResolveUtil.resolveImaginarySuperCallInThisPlace(aClass, aClass.getProject(), baseClass);
|
||||
List<PsiMethod> constructorCandidates = (resolved != null ? Collections.singletonList((PsiMethod)resolved)
|
||||
: Arrays.asList(constructors))
|
||||
.stream()
|
||||
.filter(constructor -> {
|
||||
PsiParameter[] parameters = constructor.getParameterList().getParameters();
|
||||
return (parameters.length == 0 || parameters.length == 1 && parameters[0].isVarArgs()) &&
|
||||
resolveHelper.isAccessible(constructor, aClass, null);
|
||||
})
|
||||
.limit(2).toList();
|
||||
|
||||
if (constructorCandidates.size() >= 2) {// two ambiguous var-args-only constructors
|
||||
String m1 = PsiFormatUtil.formatMethod(constructorCandidates.get(0), PsiSubstitutor.EMPTY,
|
||||
PsiFormatUtilBase.SHOW_CONTAINING_CLASS |
|
||||
PsiFormatUtilBase.SHOW_NAME |
|
||||
PsiFormatUtilBase.SHOW_PARAMETERS,
|
||||
PsiFormatUtilBase.SHOW_TYPE);
|
||||
String m2 = PsiFormatUtil.formatMethod(constructorCandidates.get(1), PsiSubstitutor.EMPTY,
|
||||
PsiFormatUtilBase.SHOW_CONTAINING_CLASS |
|
||||
PsiFormatUtilBase.SHOW_NAME |
|
||||
PsiFormatUtilBase.SHOW_PARAMETERS,
|
||||
PsiFormatUtilBase.SHOW_TYPE);
|
||||
HighlightInfo.Builder info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
|
||||
.range(range)
|
||||
.descriptionAndTooltip(JavaErrorBundle.message("ambiguous.method.call", m1, m2));
|
||||
|
||||
IntentionAction action1 = QuickFixFactory.getInstance().createCreateConstructorMatchingSuperFix(aClass);
|
||||
info.registerFix(action1, null, null, null, null);
|
||||
IntentionAction action = QuickFixFactory.getInstance().createAddDefaultConstructorFix(baseClass);
|
||||
info.registerFix(action, null, null, null, null);
|
||||
return info;
|
||||
}
|
||||
|
||||
if (!constructorCandidates.isEmpty()) {
|
||||
PsiMethod constructor = constructorCandidates.get(0);
|
||||
String description = checkDefaultConstructorThrowsException(constructor, handledExceptions);
|
||||
if (description != null) {
|
||||
HighlightInfo.Builder info =
|
||||
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range).descriptionAndTooltip(description);
|
||||
IntentionAction action = QuickFixFactory.getInstance().createCreateConstructorMatchingSuperFix(aClass);
|
||||
info.registerFix(action, null, null, null, null);
|
||||
return info;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// no need to distract with missing constructor error when there is already a "Cannot inherit from final class" error message
|
||||
if (baseClass.hasModifierProperty(PsiModifier.FINAL)) return null;
|
||||
|
||||
String description = JavaErrorBundle.message("no.default.constructor.available", HighlightUtil.formatClass(baseClass));
|
||||
HighlightInfo.Builder info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range).descriptionAndTooltip(description);
|
||||
IntentionAction action = QuickFixFactory.getInstance().createCreateConstructorMatchingSuperFix(aClass);
|
||||
info.registerFix(action, null, null, null, null);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
public static HighlightInfo.Builder checkCreateInnerClassFromStaticContext(@NotNull PsiElement element,
|
||||
@Nullable PsiExpression qualifier,
|
||||
@NotNull PsiClass aClass) {
|
||||
|
||||
@@ -1488,33 +1488,6 @@ public final class HighlightMethodUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
static HighlightInfo.Builder checkConstructorCallsBaseClassConstructor(@NotNull PsiMethod constructor,
|
||||
@NotNull PsiResolveHelper resolveHelper) {
|
||||
if (!constructor.isConstructor()) return null;
|
||||
PsiClass aClass = constructor.getContainingClass();
|
||||
if (aClass == null) return null;
|
||||
if (aClass.isEnum()) return null;
|
||||
PsiCodeBlock body = constructor.getBody();
|
||||
if (body == null) return null;
|
||||
|
||||
if (JavaPsiConstructorUtil.findThisOrSuperCallInConstructor(constructor) != null) return null;
|
||||
TextRange textRange = HighlightNamesUtil.getMethodDeclarationTextRange(constructor);
|
||||
PsiClassType[] handledExceptions = constructor.getThrowsList().getReferencedTypes();
|
||||
HighlightInfo.Builder info = HighlightClassUtil.checkBaseClassDefaultConstructorProblem(aClass, resolveHelper, textRange, handledExceptions);
|
||||
if (info != null) {
|
||||
IntentionAction action2 = QuickFixFactory.getInstance().createInsertSuperFix(constructor);
|
||||
info.registerFix(action2, null, null, null, null);
|
||||
IntentionAction action1 = QuickFixFactory.getInstance().createInsertThisFix(constructor);
|
||||
info.registerFix(action1, null, null, null, null);
|
||||
PsiClass superClass = aClass.getSuperClass();
|
||||
if (superClass != null) {
|
||||
IntentionAction action = QuickFixFactory.getInstance().createAddDefaultConstructorFix(superClass);
|
||||
info.registerFix(action, null, null, null, null);
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return error if static method overrides instance method or
|
||||
|
||||
@@ -50,7 +50,6 @@ import com.intellij.ui.NewUI;
|
||||
import com.intellij.util.JavaPsiConstructorUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.MostlySingularMultiMap;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import com.intellij.util.ui.NamedColorUtil;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
@@ -98,7 +97,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
private final Map<String, Pair<PsiImportStaticReferenceElement, PsiField>> mySingleImportedFields = new HashMap<>();
|
||||
private final @NotNull Consumer<? super HighlightInfo.Builder> myErrorSink = builder -> add(builder);
|
||||
|
||||
private final Map<PsiClass, MostlySingularMultiMap<MethodSignature, PsiMethod>> myDuplicateMethods = new HashMap<>();
|
||||
private final Set<PsiClass> myOverrideEquivalentMethodsVisitedClasses = new HashSet<>();
|
||||
// stored "clashing signatures" errors for the method (if the key is a PsiModifierList of the method), or the class (if the key is a PsiModifierList of the class)
|
||||
private final Map<PsiMember, HighlightInfo.Builder> myOverrideEquivalentMethodsErrors = new HashMap<>();
|
||||
@@ -107,8 +105,8 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
private final Map<PsiElement, PsiMethod> myInsideConstructorOfClassCache = new HashMap<>(); // null value means "cached but no corresponding ctr found"
|
||||
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.
|
||||
|
||||
protected @NotNull PsiResolveHelper getResolveHelper(@NotNull Project project) {
|
||||
return PsiResolveHelper.getInstance(project);
|
||||
private @NotNull PsiResolveHelper getResolveHelper() {
|
||||
return PsiResolveHelper.getInstance(getProject());
|
||||
}
|
||||
|
||||
protected HighlightVisitorImpl() {
|
||||
@@ -118,7 +116,8 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
private boolean hasErrorResults() {
|
||||
return myHasError;
|
||||
}
|
||||
private @NotNull Project getProject() {
|
||||
|
||||
private @Contract(pure = true) @NotNull Project getProject() {
|
||||
return myHolder.getProject();
|
||||
}
|
||||
|
||||
@@ -144,7 +143,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #HighlightVisitorImpl()} and {@link #getResolveHelper(Project)}
|
||||
* @deprecated use {@link #HighlightVisitorImpl()} and {@link #getResolveHelper()}
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
protected HighlightVisitorImpl(@NotNull PsiResolveHelper psiResolveHelper) {
|
||||
@@ -378,7 +377,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
if (parentInferenceErrorMessage != null && (returnErrors == null || !returnErrors.containsValue(parentInferenceErrorMessage))) {
|
||||
if (returnErrors == null) return;
|
||||
HighlightInfo.Builder info =
|
||||
HighlightMethodUtil.createIncompatibleTypeHighlightInfo(callExpression, getResolveHelper(getProject()),
|
||||
HighlightMethodUtil.createIncompatibleTypeHighlightInfo(callExpression, getResolveHelper(),
|
||||
parentCallResolveResult, expression);
|
||||
if (info != null) {
|
||||
for (PsiElement errorElement : returnErrors.keySet()) {
|
||||
@@ -592,7 +591,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
|
||||
PsiResolveHelper resolveHelper;
|
||||
if ((!result.isAccessible() || !result.isStaticsScopeCorrect()) &&
|
||||
!HighlightMethodUtil.isDummyConstructorCall(expression, resolveHelper = getResolveHelper(getProject()), list, referenceExpression) &&
|
||||
!HighlightMethodUtil.isDummyConstructorCall(expression, resolveHelper = getResolveHelper(), list, referenceExpression) &&
|
||||
// this check is for fake expression from JspMethodCallImpl
|
||||
referenceExpression.getParent() == expression) {
|
||||
try {
|
||||
@@ -850,7 +849,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
if (!hasErrorResults()) add(HighlightClassUtil.checkSuperQualifierType(myFile.getProject(), expression));
|
||||
if (!hasErrorResults()) {
|
||||
try {
|
||||
HighlightMethodUtil.checkMethodCall(expression, getResolveHelper(getProject()), myLanguageLevel, myJavaSdkVersion, myFile,
|
||||
HighlightMethodUtil.checkMethodCall(expression, getResolveHelper(), myLanguageLevel, myJavaSdkVersion, myFile,
|
||||
myErrorSink);
|
||||
}
|
||||
catch (IndexNotReadyException ignored) {
|
||||
@@ -896,10 +895,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
}
|
||||
}
|
||||
if (!hasErrorResults()) add(HighlightMethodUtil.checkMethodMustHaveBody(method, aClass));
|
||||
if (!hasErrorResults()) {
|
||||
add(
|
||||
HighlightMethodUtil.checkConstructorCallsBaseClassConstructor(method, getResolveHelper(getProject())));
|
||||
}
|
||||
if (!hasErrorResults()) add(HighlightMethodUtil.checkStaticMethodOverride(method, myFile));
|
||||
if (!hasErrorResults() && aClass != null) {
|
||||
GenericsHighlightUtil.computeOverrideEquivalentMethodErrors(aClass, myOverrideEquivalentMethodsVisitedClasses, myOverrideEquivalentMethodsErrors);
|
||||
@@ -908,9 +903,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
}
|
||||
else if (parent instanceof PsiClass aClass) {
|
||||
try {
|
||||
if (!hasErrorResults()) {
|
||||
add(HighlightClassUtil.checkClassDoesNotCallSuperConstructorOrHandleExceptions(aClass, getResolveHelper(getProject())));
|
||||
}
|
||||
if (!hasErrorResults()) add(HighlightMethodUtil.checkOverrideEquivalentInheritedMethods(aClass, myFile, myLanguageLevel));
|
||||
if (!hasErrorResults()) {
|
||||
GenericsHighlightUtil.computeOverrideEquivalentMethodErrors(aClass, myOverrideEquivalentMethodsVisitedClasses, myOverrideEquivalentMethodsErrors);
|
||||
@@ -925,7 +917,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
@Override
|
||||
public void visitNewExpression(@NotNull PsiNewExpression expression) {
|
||||
PsiType type = expression.getType();
|
||||
PsiClass aClass = PsiUtil.resolveClassInType(type);
|
||||
add(HighlightUtil.checkUnhandledExceptions(expression));
|
||||
if (!hasErrorResults()) add(GenericsHighlightUtil.checkTypeParameterInstantiation(expression));
|
||||
if (!hasErrorResults()) add(GenericsHighlightUtil.checkGenericArrayCreation(expression, type));
|
||||
@@ -1198,7 +1189,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
methodCallExpression.getMethodExpression() == expression &&
|
||||
(!result.isAccessible() || !result.isStaticsScopeCorrect())) {
|
||||
PsiExpressionList list = methodCallExpression.getArgumentList();
|
||||
PsiResolveHelper resolveHelper = getResolveHelper(getProject());
|
||||
PsiResolveHelper resolveHelper = getResolveHelper();
|
||||
if (!HighlightMethodUtil.isDummyConstructorCall(methodCallExpression, resolveHelper, list, expression)) {
|
||||
try {
|
||||
add(HighlightMethodUtil.checkAmbiguousMethodCallIdentifier(
|
||||
|
||||
@@ -78,6 +78,7 @@ final class JavaErrorFixProvider {
|
||||
}
|
||||
|
||||
createClassFixes();
|
||||
createConstructorFixes();
|
||||
createExpressionFixes();
|
||||
createGenericFixes();
|
||||
createRecordFixes();
|
||||
@@ -89,6 +90,35 @@ final class JavaErrorFixProvider {
|
||||
public static JavaErrorFixProvider getInstance() {
|
||||
return ApplicationManager.getApplication().getService(JavaErrorFixProvider.class);
|
||||
}
|
||||
|
||||
private void createConstructorFixes() {
|
||||
JavaFixesProvider<PsiMember, Object> constructorCallFixes = error -> {
|
||||
if (error.psi() instanceof PsiClass cls) {
|
||||
return List.of(myFactory.createCreateConstructorMatchingSuperFix(cls));
|
||||
}
|
||||
else if (error.psi() instanceof PsiMethod method) {
|
||||
return List.of(myFactory.createInsertSuperFix(method), myFactory.createInsertThisFix(method));
|
||||
}
|
||||
return List.of();
|
||||
};
|
||||
multi(CONSTRUCTOR_AMBIGUOUS_IMPLICIT_CALL, constructorCallFixes);
|
||||
multi(CONSTRUCTOR_NO_DEFAULT, constructorCallFixes);
|
||||
fix(CONSTRUCTOR_AMBIGUOUS_IMPLICIT_CALL, error -> myFactory.createAddDefaultConstructorFix(
|
||||
requireNonNull(error.context().psiClass().getSuperClass())));
|
||||
fix(CONSTRUCTOR_NO_DEFAULT, error -> myFactory.createAddDefaultConstructorFix(error.context()));
|
||||
fix(EXCEPTION_UNHANDLED, error -> {
|
||||
PsiClass psiClass = error.psi() instanceof PsiClass cls ? cls :
|
||||
error.psi() instanceof PsiMethod method ? method.getContainingClass() :
|
||||
null;
|
||||
return psiClass != null ? myFactory.createCreateConstructorMatchingSuperFix(psiClass) : null;
|
||||
});
|
||||
fix(EXCEPTION_UNHANDLED, error -> {
|
||||
if (error.psi() instanceof PsiMethod method) {
|
||||
return myFactory.createAddExceptionToThrowsFix(method, error.context());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private void createExpressionFixes() {
|
||||
fix(NEW_EXPRESSION_QUALIFIED_MALFORMED, error -> myFactory.createRemoveNewQualifierFix(error.psi(), null));
|
||||
|
||||
@@ -24,13 +24,21 @@ import java.util.*;
|
||||
|
||||
public final class AddExceptionToThrowsFix extends PsiBasedModCommandAction<PsiElement> {
|
||||
private final @NotNull ThreeState myProcessHierarchy;
|
||||
private final Collection<PsiClassType> myExceptionsToAdd;
|
||||
|
||||
public AddExceptionToThrowsFix(@NotNull PsiElement wrongElement) {
|
||||
this(wrongElement, ThreeState.UNSURE);
|
||||
}
|
||||
|
||||
public AddExceptionToThrowsFix(@NotNull PsiElement wrongElement, @NotNull Collection<PsiClassType> exceptionsToAdd) {
|
||||
super(wrongElement);
|
||||
myExceptionsToAdd = exceptionsToAdd;
|
||||
myProcessHierarchy = ThreeState.UNSURE;
|
||||
}
|
||||
|
||||
public AddExceptionToThrowsFix(@NotNull PsiElement wrongElement, @NotNull ThreeState hierarchy) {
|
||||
super(wrongElement);
|
||||
myExceptionsToAdd = List.of();
|
||||
myProcessHierarchy = hierarchy;
|
||||
}
|
||||
|
||||
@@ -139,12 +147,12 @@ public final class AddExceptionToThrowsFix extends PsiBasedModCommandAction<PsiE
|
||||
return ContainerUtil.or(getSuperMethods(targetMethod), method -> method instanceof PsiCompiledElement || method instanceof SyntheticElement);
|
||||
}
|
||||
|
||||
private static @Nullable PsiMethod collectExceptions(Set<? super PsiClassType> unhandled, PsiElement element) {
|
||||
private @Nullable PsiMethod collectExceptions(Set<? super PsiClassType> unhandled, PsiElement element) {
|
||||
PsiElement targetElement = null;
|
||||
PsiMethod targetMethod = null;
|
||||
|
||||
final PsiElement psiElement;
|
||||
if (element instanceof PsiMethodReferenceExpression) {
|
||||
if (element instanceof PsiMethodReferenceExpression || element instanceof PsiMethod) {
|
||||
psiElement = element;
|
||||
}
|
||||
else {
|
||||
@@ -169,7 +177,14 @@ public final class AddExceptionToThrowsFix extends PsiBasedModCommandAction<PsiE
|
||||
|
||||
if (targetElement == null || targetMethod == null) return null;
|
||||
if (!ExceptionUtil.canDeclareThrownExceptions(targetMethod)) return null;
|
||||
List<PsiClassType> exceptions = getUnhandledExceptions(element, targetElement, targetMethod);
|
||||
Collection<PsiClassType> exceptions;
|
||||
if (!myExceptionsToAdd.isEmpty()) {
|
||||
if (ContainerUtil.exists(myExceptionsToAdd, e -> !e.isValid())) return null;
|
||||
exceptions = myExceptionsToAdd;
|
||||
}
|
||||
else {
|
||||
exceptions = getUnhandledExceptions(element, targetElement, targetMethod);
|
||||
}
|
||||
if (exceptions == null || exceptions.isEmpty()) return null;
|
||||
unhandled.addAll(exceptions);
|
||||
return targetMethod;
|
||||
@@ -181,9 +196,9 @@ public final class AddExceptionToThrowsFix extends PsiBasedModCommandAction<PsiE
|
||||
}
|
||||
|
||||
private static @Nullable List<PsiClassType> getUnhandledExceptions(@Nullable PsiElement element, PsiElement topElement, PsiMethod targetMethod) {
|
||||
if (element == null || element == topElement && !(topElement instanceof PsiMethodReferenceExpression)) return null;
|
||||
if (element == null || element == topElement && !(topElement instanceof PsiMethodReferenceExpression) && !(topElement instanceof PsiMethod)) return null;
|
||||
List<PsiClassType> unhandledExceptions = ExceptionUtil.getUnhandledExceptions(element);
|
||||
if (!filterInProjectExceptions(targetMethod, unhandledExceptions).isEmpty()) {
|
||||
if (!filterInProjectExceptions(targetMethod, unhandledExceptions).isEmpty()) {
|
||||
return unhandledExceptions;
|
||||
}
|
||||
if (topElement instanceof PsiMethodReferenceExpression) {
|
||||
|
||||
@@ -203,6 +203,12 @@ public final class QuickFixFactoryImpl extends QuickFixFactory {
|
||||
return new AddExceptionToThrowsFix(element).asIntention();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull IntentionAction createAddExceptionToThrowsFix(@NotNull PsiElement element,
|
||||
@NotNull Collection<PsiClassType> exceptionsToAdd) {
|
||||
return new AddExceptionToThrowsFix(element, exceptionsToAdd).asIntention();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull IntentionAction createAddExceptionFromFieldInitializerToConstructorThrowsFix(@NotNull PsiElement element) {
|
||||
return new AddExceptionFromFieldInitializerToConstructorThrowsFix(element).asIntention();
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// "Add exception to method signature" "true-preview"
|
||||
import java.io.IOException;
|
||||
|
||||
class Super {
|
||||
Super() throws IOException {
|
||||
|
||||
}
|
||||
}
|
||||
class Sub extends Super {
|
||||
Sub(int x) throws IOException {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// "Add exception to method signature" "true-preview"
|
||||
import java.io.IOException;
|
||||
|
||||
class Super {
|
||||
Super() throws IOException {
|
||||
|
||||
}
|
||||
}
|
||||
class Sub extends Super {
|
||||
Sub<caret>(int x) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -7,11 +7,8 @@ import com.intellij.codeInsight.daemon.impl.analysis.HighlightVisitorImpl;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.dev.codeInsight.internal.GoodCodeRedVisitor;
|
||||
import com.intellij.lang.annotation.HighlightSeverity;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.JavaPsiFacade;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiElementVisitor;
|
||||
import com.intellij.psi.PsiResolveHelper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -19,18 +16,11 @@ final class JavaGoodCodeRedVisitor implements GoodCodeRedVisitor {
|
||||
|
||||
@Override
|
||||
public @NotNull PsiElementVisitor createVisitor(@NotNull ProblemsHolder holder) {
|
||||
PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(holder.getProject()).getResolveHelper();
|
||||
return new MyHighlightVisitorImpl(holder, resolveHelper);
|
||||
return new MyHighlightVisitorImpl(holder);
|
||||
}
|
||||
|
||||
private static final class MyHighlightVisitorImpl extends HighlightVisitorImpl {
|
||||
|
||||
private final @NotNull PsiResolveHelper myResolveHelper;
|
||||
|
||||
private MyHighlightVisitorImpl(@NotNull ProblemsHolder holder,
|
||||
@NotNull PsiResolveHelper resolveHelper) {
|
||||
|
||||
myResolveHelper = resolveHelper;
|
||||
private MyHighlightVisitorImpl(@NotNull ProblemsHolder holder) {
|
||||
prepareToRunAsInspection(new HighlightInfoHolder(holder.getFile()) {
|
||||
@Override
|
||||
public boolean add(@Nullable HighlightInfo info) {
|
||||
@@ -54,10 +44,5 @@ final class JavaGoodCodeRedVisitor implements GoodCodeRedVisitor {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull PsiResolveHelper getResolveHelper(@NotNull Project project) {
|
||||
return myResolveHelper;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user