[java-highlighting] checkEnumSuperConstructorCall, checkSuperQualifierType converted

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

GitOrigin-RevId: 7b48c7780fb6990f01272e733ac54db93c43f5d4
This commit is contained in:
Tagir Valeev
2025-01-15 19:19:41 +00:00
committed by intellij-monorepo-bot
parent df02b97035
commit da92bfa562
17 changed files with 209 additions and 146 deletions
@@ -166,3 +166,6 @@ literal.text.block.no.new.line=Illegal text block start: missing new line after
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}
call.super.enum.constructor=Call to super is not allowed in enum constructor
call.super.qualifier.not.inner.class=Qualifier is not allowed because superclass ''{0}'' is not a non-static inner class
@@ -727,6 +727,51 @@ final class ClassChecker {
checkBaseClassDefaultConstructorProblem(aClass, constructor, handledExceptions);
}
void checkEnumSuperConstructorCall(@NotNull PsiMethodCallExpression expr) {
PsiReferenceExpression methodExpression = expr.getMethodExpression();
PsiElement refNameElement = methodExpression.getReferenceNameElement();
if (refNameElement != null && PsiKeyword.SUPER.equals(refNameElement.getText())) {
PsiMember constructor = PsiUtil.findEnclosingConstructorOrInitializer(expr);
if (constructor instanceof PsiMethod) {
PsiClass aClass = constructor.getContainingClass();
if (aClass != null && aClass.isEnum()) {
myVisitor.report(JavaErrorKinds.CALL_SUPER_ENUM_CONSTRUCTOR.create(expr));
}
}
}
}
void checkSuperQualifierType(@NotNull PsiMethodCallExpression superCall) {
if (!JavaPsiConstructorUtil.isSuperConstructorCall(superCall)) return;
PsiMethod ctr = PsiTreeUtil.getParentOfType(superCall, PsiMethod.class, true, PsiMember.class);
if (ctr == null) return;
PsiClass aClass = ctr.getContainingClass();
if (aClass == null) return;
PsiClass targetClass = aClass.getSuperClass();
if (targetClass == null) return;
PsiExpression qualifier = superCall.getMethodExpression().getQualifierExpression();
if (qualifier != null) {
if (isRealInnerClass(targetClass)) {
PsiClass outerClass = targetClass.getContainingClass();
if (outerClass != null) {
PsiClassType outerType = JavaPsiFacade.getElementFactory(myVisitor.project()).createType(outerClass);
myVisitor.myExpressionChecker.checkAssignability(outerType, null, qualifier, qualifier);
}
} else {
myVisitor.report(JavaErrorKinds.CALL_SUPER_QUALIFIER_NOT_INNER_CLASS.create(qualifier, targetClass));
}
}
}
/** JLS 8.1.3. Inner Classes and Enclosing Instances */
private static boolean isRealInnerClass(PsiClass aClass) {
if (PsiUtil.isInnerClass(aClass)) return true;
if (!PsiUtil.isLocalOrAnonymousClass(aClass)) return false;
if (aClass.hasModifierProperty(PsiModifier.STATIC)) return false; // check for implicit staticness
PsiMember member = PsiTreeUtil.getParentOfType(aClass, PsiMember.class, true);
return member != null && !member.hasModifierProperty(PsiModifier.STATIC);
}
private static @Unmodifiable @NotNull Map<PsiJavaCodeReferenceElement, PsiClass> getPermittedClassesRefs(@NotNull PsiClass psiClass) {
PsiReferenceList permitsList = psiClass.getPermitsList();
if (permitsList == null) return Collections.emptyMap();
@@ -2,10 +2,13 @@
package com.intellij.java.codeserver.highlighting;
import com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds;
import com.intellij.java.codeserver.highlighting.errors.JavaIncompatibleTypeError;
import com.intellij.psi.*;
import com.intellij.psi.impl.IncompleteModelUtil;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -88,4 +91,28 @@ final class ExpressionChecker {
myVisitor.report(JavaErrorKinds.CLASS_CANNOT_BE_REFERENCED_FROM_STATIC_CONTEXT.create(elementToHighlight, context));
}
}
void checkAssignability(@Nullable PsiType lType,
@Nullable PsiType rType,
@Nullable PsiExpression expression,
@NotNull PsiElement elementToHighlight) {
if (lType == rType) return;
if (expression == null) {
if (rType == null || lType == null || TypeConversionUtil.isAssignable(lType, rType)) return;
}
else if (TypeConversionUtil.areTypesAssignmentCompatible(lType, expression) || PsiTreeUtil.hasErrorElements(expression)) {
return;
}
if (rType == null) {
rType = expression.getType();
}
if (lType == null || lType == PsiTypes.nullType()) {
return;
}
if (expression != null && IncompleteModelUtil.isIncompleteModel(expression) &&
IncompleteModelUtil.isPotentiallyConvertible(lType, expression)) {
return;
}
myVisitor.report(JavaErrorKinds.TYPE_INCOMPATIBLE.create(elementToHighlight, new JavaIncompatibleTypeError(lType, rType)));
}
}
@@ -37,7 +37,7 @@ final class JavaErrorVisitor extends JavaElementVisitor {
private final @NotNull GenericsChecker myGenericsChecker = new GenericsChecker(this);
private final @NotNull MethodChecker myMethodChecker = new MethodChecker(this);
private final @NotNull ReceiverChecker myReceiverChecker = new ReceiverChecker(this);
private final @NotNull ExpressionChecker myExpressionChecker = new ExpressionChecker(this);
final @NotNull ExpressionChecker myExpressionChecker = new ExpressionChecker(this);
private final @NotNull LiteralChecker myLiteralChecker = new LiteralChecker(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.
@@ -62,6 +62,10 @@ final class JavaErrorVisitor extends JavaElementVisitor {
return myFile;
}
@NotNull Project project() {
return myProject;
}
@NotNull LanguageLevel languageLevel() {
return myLanguageLevel;
}
@@ -249,6 +253,13 @@ final class JavaErrorVisitor extends JavaElementVisitor {
if (!hasErrorResults()) myClassChecker.checkIllegalInstanceMemberInRecord(field);
}
@Override
public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) {
if (!hasErrorResults()) myClassChecker.checkEnumSuperConstructorCall(expression);
if (!hasErrorResults()) myClassChecker.checkSuperQualifierType(expression);
}
@Override
public void visitIdentifier(@NotNull PsiIdentifier identifier) {
PsiElement parent = identifier.getParent();
@@ -5,10 +5,7 @@ import com.intellij.java.codeserver.highlighting.JavaCompilationErrorBundle;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.HtmlChunk;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.PropertyKey;
import org.jetbrains.annotations.*;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
@@ -229,6 +226,7 @@ public sealed interface JavaErrorKind<Psi extends PsiElement, Context> {
* @param psi psi element to bind an error instance to
* @return an instance of this error
*/
@Contract(pure = true)
public @NotNull JavaCompilationError<Psi, Void> create(@NotNull Psi psi) {
return new JavaCompilationError<>(this, psi, null);
}
@@ -319,6 +317,7 @@ public sealed interface JavaErrorKind<Psi extends PsiElement, Context> {
* @param context context to bind an error instance to
* @return an instance of this error
*/
@Contract(pure = true)
public @NotNull JavaCompilationError<Psi, Context> create(@NotNull Psi psi, Context context) {
return new JavaCompilationError<>(this, psi, context);
}
@@ -408,6 +408,11 @@ public final class JavaErrorKinds {
error(PsiLiteralExpression.class, "literal.text.block.unclosed").withRange(e -> TextRange.from(e.getTextLength(), 0));
public static final Simple<PsiLiteralExpression> LITERAL_TEXT_BLOCK_NO_NEW_LINE =
error(PsiLiteralExpression.class, "literal.text.block.no.new.line").withRange(e -> TextRange.create(0, 3));
public static final Simple<PsiMethodCallExpression> CALL_SUPER_ENUM_CONSTRUCTOR = error("call.super.enum.constructor");
public static final Parameterized<PsiExpression, PsiClass> CALL_SUPER_QUALIFIER_NOT_INNER_CLASS =
parameterized(PsiExpression.class, PsiClass.class, "call.super.qualifier.not.inner.class")
.withRawDescription((psi, cls) -> message("call.super.qualifier.not.inner.class", formatClass(cls)));
private static @NotNull <Psi extends PsiElement> Simple<Psi> error(@NotNull String key) {
@@ -2,12 +2,9 @@
package com.intellij.codeInsight.daemon.impl.analysis;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.daemon.impl.quickfix.*;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.CommonIntentionAction;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.modcommand.ModCommandAction;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil;
import com.intellij.psi.infos.MethodCandidateInfo;
@@ -29,6 +26,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import static com.intellij.util.ObjectUtils.tryCast;
@@ -39,8 +37,7 @@ final class AdaptExpressionTypeFixUtil {
private AdaptExpressionTypeFixUtil() { }
private static void registerPatchParametersFixes(@NotNull HighlightInfo.Builder info,
@NotNull TextRange textRange,
private static void registerPatchParametersFixes(@NotNull Consumer<? super CommonIntentionAction> info,
@NotNull PsiMethodCallExpression call,
@NotNull PsiMethod method,
@NotNull PsiType expectedTypeByParent,
@@ -57,7 +54,7 @@ final class AdaptExpressionTypeFixUtil {
Set<PsiTypeParameter> set = Set.of(typeParameter);
if (!PsiTreeUtil.isAncestor(method, typeParameter, true)) {
registerPatchQualifierFixes(info, textRange, call, method, typeParameter, expectedTypeValue, parameters, set);
registerPatchQualifierFixes(info, call, method, typeParameter, expectedTypeValue, parameters, set);
return;
}
@@ -81,21 +78,20 @@ final class AdaptExpressionTypeFixUtil {
psiClassType.rawType().equalsToText(CommonClassNames.JAVA_LANG_CLASS) &&
typeParameter == getSoleTypeParameter(parameterType)) {
if (expectedTypeValue instanceof PsiClassType classType && JavaGenericsUtil.isReifiableType(expectedTypeValue)) {
ReplaceExpressionAction fix = new ReplaceExpressionAction(
info.accept(new ReplaceExpressionAction(
arg, classType.rawType().getCanonicalText() + ".class",
classType.rawType().getPresentableText() + ".class");
info.registerFix(fix, null, null, null, null);
classType.rawType().getPresentableText() + ".class"));
}
}
PsiType expectedArgType = desiredSubstitutor.substitute(parameterType);
if (arg instanceof PsiLambdaExpression && parameterType instanceof PsiClassType) {
registerLambdaReturnFixes(info, textRange, (PsiLambdaExpression)arg, (PsiClassType)parameterType, expectedArgType, typeParameter);
registerLambdaReturnFixes(info, (PsiLambdaExpression)arg, (PsiClassType)parameterType, expectedArgType, typeParameter);
return;
}
PsiType actualArgType = PsiPolyExpressionUtil.isPolyExpression(arg) ?
substitutor.put(typeParameter, substitution.myActualType).substitute(parameterType) :
arg.getType();
registerExpectedTypeFixes(info, textRange, arg, expectedArgType, actualArgType);
registerExpectedTypeFixes(info, false, arg, expectedArgType, actualArgType);
}
private static @Nullable PsiParameter findWrongParameter(@NotNull PsiMethodCallExpression call,
@@ -131,8 +127,7 @@ final class AdaptExpressionTypeFixUtil {
return ContainerUtil.getOnlyItem(candidates);
}
private static void registerPatchQualifierFixes(@NotNull HighlightInfo.Builder info,
@NotNull TextRange textRange,
private static void registerPatchQualifierFixes(@NotNull Consumer<? super CommonIntentionAction> info,
@NotNull PsiMethodCallExpression call,
@NotNull PsiMethod method,
@NotNull PsiTypeParameter typeParameter,
@@ -154,12 +149,11 @@ final class AdaptExpressionTypeFixUtil {
.createType(qualifierClass, classResolveResult.getSubstitutor().put(typeParameter, expectedTypeValue));
PsiType actualType = qualifierCall.getType();
if (actualType != null && !expectedQualifierType.equals(actualType)) {
registerPatchParametersFixes(info, textRange, qualifierCall, qualifierMethod, expectedQualifierType, actualType);
registerPatchParametersFixes(info, qualifierCall, qualifierMethod, expectedQualifierType, actualType);
}
}
private static void registerLambdaReturnFixes(@NotNull HighlightInfo.Builder info,
@NotNull TextRange textRange,
private static void registerLambdaReturnFixes(@NotNull Consumer<? super CommonIntentionAction> info,
@NotNull PsiLambdaExpression arg,
@NotNull PsiClassType parameterType,
@Nullable PsiType expectedArgType,
@@ -188,7 +182,7 @@ final class AdaptExpressionTypeFixUtil {
}
PsiType expectedFnReturnType = LambdaUtil.getFunctionalInterfaceReturnType(expectedArgType);
if (expectedFnReturnType == null) return;
registerExpectedTypeFixes(info, textRange, lambdaBody, expectedFnReturnType);
registerExpectedTypeFixes(info, false, lambdaBody, expectedFnReturnType);
}
/**
@@ -198,7 +192,16 @@ final class AdaptExpressionTypeFixUtil {
* @param expression expression whose type is incorrect
* @param expectedType desired expression type.
*/
static void registerExpectedTypeFixes(@NotNull HighlightInfo.Builder info, @NotNull TextRange textRange,@NotNull PsiExpression expression, @Nullable PsiType expectedType) {
static void registerExpectedTypeFixes(@NotNull Consumer<? super CommonIntentionAction> info,
@NotNull PsiExpression expression,
@Nullable PsiType expectedType) {
registerExpectedTypeFixes(info, true, expression, expectedType);
}
private static void registerExpectedTypeFixes(@NotNull Consumer<? super CommonIntentionAction> info,
boolean wholeRange,
@NotNull PsiExpression expression,
@Nullable PsiType expectedType) {
PsiType actualType;
if (PsiPolyExpressionUtil.isPolyExpression(expression)) {
actualType = ((PsiExpression)expression.copy()).getType();
@@ -206,7 +209,7 @@ final class AdaptExpressionTypeFixUtil {
else {
actualType = expression.getType();
}
registerExpectedTypeFixes(info, textRange, expression, expectedType, actualType);
registerExpectedTypeFixes(info, wholeRange, expression, expectedType, actualType);
}
/**
@@ -217,35 +220,36 @@ final class AdaptExpressionTypeFixUtil {
* @param expectedType desired expression type
* @param actualType actual expression type
*/
static void registerExpectedTypeFixes(@NotNull HighlightInfo.Builder info,
@NotNull TextRange textRange,
static void registerExpectedTypeFixes(@NotNull Consumer<? super CommonIntentionAction> info,
@NotNull PsiExpression expression,
@Nullable PsiType expectedType,
@Nullable PsiType actualType) {
registerExpectedTypeFixes(info, true, expression, expectedType, actualType);
}
private static void registerExpectedTypeFixes(@NotNull Consumer<? super CommonIntentionAction> info,
boolean wholeRange,
@NotNull PsiExpression expression,
@Nullable PsiType expectedType,
@Nullable PsiType actualType) {
if (actualType == null || expectedType == null) return;
boolean mentionsTypeArgument = mentionsTypeArgument(expression, actualType);
expectedType = GenericsUtil.getVariableTypeByExpressionType(expectedType);
TextRange range = expression.getTextRange();
String role = textRange.equals(range) ? null : getRole(expression);
String role = wholeRange ? null : getRole(expression);
if (!mentionsTypeArgument) {
IntentionAction action3 = new WrapWithAdapterMethodCallFix(expectedType, expression, role);
info.registerFix(action3, null, null, null, null);
IntentionAction action2 = QuickFixFactory.getInstance().createWrapWithOptionalFix(expectedType, expression);
info.registerFix(action2, null, null, null, null);
var action1 = new WrapExpressionFix(expectedType, expression, role);
info.registerFix(action1, null, null, null, null);
info.accept(new WrapWithAdapterMethodCallFix(expectedType, expression, role));
info.accept(QuickFixFactory.getInstance().createWrapWithOptionalFix(expectedType, expression));
info.accept(new WrapExpressionFix(expectedType, expression, role));
PsiType castToType = suggestCastTo(expression, expectedType, actualType);
if (castToType != null) {
ModCommandAction action = new AddTypeCastFix(castToType, expression, role);
info.registerFix(action, null, null, null, null);
info.accept(new AddTypeCastFix(castToType, expression, role));
}
}
if (expectedType instanceof PsiArrayType arrayType) {
PsiType erasedValueType = TypeConversionUtil.erasure(actualType);
if (erasedValueType != null &&
TypeConversionUtil.isAssignable(arrayType.getComponentType(), erasedValueType)) {
IntentionAction action = QuickFixFactory.getInstance().createSurroundWithArrayFix(null, expression);
info.registerFix(action, null, null, null, null);
info.accept(QuickFixFactory.getInstance().createSurroundWithArrayFix(null, expression));
}
}
HighlightFixUtil.registerCollectionToArrayFixAction(info, actualType, expectedType, expression);
@@ -254,12 +258,12 @@ final class AdaptExpressionTypeFixUtil {
if (qualifier != null) {
PsiType type = qualifier.getType();
if (type != null && expectedType.isAssignableFrom(type)) {
info.registerFix(new ReplaceWithQualifierFix(call, role), null, null, null, null);
info.accept(new ReplaceWithQualifierFix(call, role));
}
}
PsiMethod argMethod = call.resolveMethod();
if (argMethod != null) {
registerPatchParametersFixes(info, textRange, call, argMethod, expectedType, actualType);
registerPatchParametersFixes(info, call, argMethod, expectedType, actualType);
}
}
}
@@ -1043,22 +1043,6 @@ public final class GenericsHighlightUtil {
return null;
}
static HighlightInfo.Builder checkEnumSuperConstructorCall(@NotNull PsiMethodCallExpression expr) {
PsiReferenceExpression methodExpression = expr.getMethodExpression();
PsiElement refNameElement = methodExpression.getReferenceNameElement();
if (refNameElement != null && PsiKeyword.SUPER.equals(refNameElement.getText())) {
PsiMember constructor = PsiUtil.findEnclosingConstructorOrInitializer(expr);
if (constructor instanceof PsiMethod) {
PsiClass aClass = constructor.getContainingClass();
if (aClass != null && aClass.isEnum()) {
String message = JavaErrorBundle.message("call.to.super.is.not.allowed.in.enum.constructor");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expr).descriptionAndTooltip(message);
}
}
}
return null;
}
static HighlightInfo.Builder checkVarArgParameterIsLast(@NotNull PsiParameter parameter) {
PsiElement declarationScope = parameter.getDeclarationScope();
if (declarationScope instanceof PsiMethod psiMethod) {
@@ -7,13 +7,11 @@ import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
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.text.StringUtil;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.search.searches.DirectClassInheritorsSearch;
import com.intellij.psi.util.*;
import com.intellij.util.JavaPsiConstructorUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -76,39 +74,6 @@ public final class HighlightClassUtil {
return checkIllegalEnclosingUsage(placeToSearchEnclosingFrom, aClass, outerClass, element);
}
static HighlightInfo.Builder checkSuperQualifierType(@NotNull Project project, @NotNull PsiMethodCallExpression superCall) {
if (!JavaPsiConstructorUtil.isSuperConstructorCall(superCall)) return null;
PsiMethod ctr = PsiTreeUtil.getParentOfType(superCall, PsiMethod.class, true, PsiMember.class);
if (ctr == null) return null;
PsiClass aClass = ctr.getContainingClass();
if (aClass == null) return null;
PsiClass targetClass = aClass.getSuperClass();
if (targetClass == null) return null;
PsiExpression qualifier = superCall.getMethodExpression().getQualifierExpression();
if (qualifier != null) {
if (isRealInnerClass(targetClass)) {
PsiClass outerClass = targetClass.getContainingClass();
if (outerClass != null) {
PsiClassType outerType = JavaPsiFacade.getElementFactory(project).createType(outerClass);
return HighlightUtil.checkAssignability(outerType, null, qualifier, qualifier);
}
} else {
String description = JavaErrorBundle.message("not.inner.class", HighlightUtil.formatClass(targetClass));
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(qualifier).descriptionAndTooltip(description);
}
}
return null;
}
/** JLS 8.1.3. Inner Classes and Enclosing Instances */
private static boolean isRealInnerClass(PsiClass aClass) {
if (PsiUtil.isInnerClass(aClass)) return true;
if (!PsiUtil.isLocalOrAnonymousClass(aClass)) return false;
if (aClass.hasModifierProperty(PsiModifier.STATIC)) return false; // check for implicit staticness
PsiMember member = PsiTreeUtil.getParentOfType(aClass, PsiMember.class, true);
return member != null && !member.hasModifierProperty(PsiModifier.STATIC);
}
static HighlightInfo.Builder checkIllegalEnclosingUsage(@NotNull PsiElement place,
@Nullable PsiClass aClass,
@NotNull PsiClass outerClass,
@@ -7,6 +7,7 @@ import com.intellij.codeInsight.daemon.impl.quickfix.QualifyMethodCallFix;
import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction;
import com.intellij.codeInsight.daemon.impl.quickfix.ReplaceAssignmentFromVoidWithStatementIntentionAction;
import com.intellij.codeInsight.daemon.impl.quickfix.ReplaceGetClassWithClassLiteralFix;
import com.intellij.codeInsight.intention.CommonIntentionAction;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.codeInsight.intention.impl.PriorityIntentionActionWrapper;
@@ -35,13 +36,14 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
public final class HighlightFixUtil {
private static final Logger LOG = Logger.getInstance(HighlightFixUtil.class);
private static final CallMatcher COLLECTION_TO_ARRAY =
CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_COLLECTION, "toArray").parameterCount(0);
static void registerCollectionToArrayFixAction(@NotNull HighlightInfo.Builder info,
static void registerCollectionToArrayFixAction(@NotNull Consumer<? super CommonIntentionAction> info,
@Nullable PsiType fromType,
@Nullable PsiType toType,
@NotNull PsiExpression expression) {
@@ -60,8 +62,7 @@ public final class HighlightFixUtil {
InheritanceUtil.isInheritor(fromType, CommonClassNames.JAVA_UTIL_COLLECTION)) {
PsiType collectionItemType = JavaGenericsUtil.getCollectionItemType(fromType, expression.getResolveScope());
if (collectionItemType != null && arrayComponentType.isConvertibleFrom(collectionItemType)) {
IntentionAction action = QuickFixFactory.getInstance().createCollectionToArrayFix(collection, expression, (PsiArrayType)toType);
info.registerFix(action, null, null, null, null);
info.accept(QuickFixFactory.getInstance().createCollectionToArrayFix(collection, expression, (PsiArrayType)toType));
}
}
}
@@ -234,25 +235,25 @@ public final class HighlightFixUtil {
@Nullable PsiType itemType,
@Nullable PsiExpression expr,
@Nullable HighlightInfo.Builder highlightInfo) {
if (highlightInfo == null) return;
for (IntentionAction action : getChangeVariableTypeFixes(parameter, itemType)) {
if (highlightInfo != null) {
highlightInfo.registerFix(action, null, null, null, null);
}
highlightInfo.registerFix(action, null, null, null, null);
}
IntentionAction fix = createChangeReturnTypeFix(expr, parameter.getType());
if (fix != null) {
highlightInfo.registerFix(fix, null, null, null, null);
}
registerChangeReturnTypeFix(highlightInfo, expr, parameter.getType());
}
static void registerChangeReturnTypeFix(@Nullable HighlightInfo.Builder highlightInfo, @Nullable PsiExpression expr, @NotNull PsiType toType) {
static @Nullable IntentionAction createChangeReturnTypeFix(@Nullable PsiExpression expr, @NotNull PsiType toType) {
if (expr instanceof PsiMethodCallExpression) {
PsiMethod method = ((PsiMethodCallExpression)expr).resolveMethod();
if (method != null) {
IntentionAction action = PriorityIntentionActionWrapper
return PriorityIntentionActionWrapper
.lowPriority(QuickFixFactory.getInstance().createMethodReturnFix(method, toType, true));
if (highlightInfo != null) {
highlightInfo.registerFix(action, null, null, null, null);
}
}
}
return null;
}
/**
@@ -331,11 +332,10 @@ public final class HighlightFixUtil {
}
}
static void registerLambdaReturnTypeFixes(@Nullable HighlightInfo.Builder info, @NotNull TextRange range, PsiLambdaExpression lambda, PsiExpression expression) {
if (info == null) return;
static void registerLambdaReturnTypeFixes(@NotNull Consumer<? super CommonIntentionAction> info, PsiLambdaExpression lambda, PsiExpression expression) {
PsiType type = LambdaUtil.getFunctionalInterfaceReturnType(lambda);
if (type != null) {
AdaptExpressionTypeFixUtil.registerExpectedTypeFixes(info, range, expression, type);
AdaptExpressionTypeFixUtil.registerExpectedTypeFixes(info, expression, type);
}
}
@@ -455,7 +455,8 @@ public final class HighlightMethodUtil {
PsiType actualType = ((PsiExpression)methodCall.copy()).getType();
TextRange fixRange = getFixRange(list);
if (expectedTypeByParent != null && actualType != null && !expectedTypeByParent.isAssignableFrom(actualType)) {
AdaptExpressionTypeFixUtil.registerExpectedTypeFixes(builder, fixRange, methodCall, expectedTypeByParent, actualType);
AdaptExpressionTypeFixUtil.registerExpectedTypeFixes(
HighlightUtil.asConsumer(builder), methodCall, expectedTypeByParent, actualType);
}
HighlightFixUtil.registerQualifyMethodCallFix(resolveHelper.getReferencedMethodCandidates(methodCall, false), methodCall,
list, builder);
@@ -647,7 +648,8 @@ public final class HighlightMethodUtil {
builder = HighlightUtil.createIncompatibleTypeHighlightInfo(
expectedTypeByParent, actualType, fixRange, 0, XmlStringUtil.escapeString(errorMessage));
if (methodCall instanceof PsiExpression) {
AdaptExpressionTypeFixUtil.registerExpectedTypeFixes(builder, fixRange, (PsiExpression)methodCall, expectedTypeByParent, actualType);
AdaptExpressionTypeFixUtil.registerExpectedTypeFixes(HighlightUtil.asConsumer(builder),
(PsiExpression)methodCall, expectedTypeByParent, actualType);
}
PsiElement parent = PsiUtil.skipParenthesizedExprUp(methodCall.getParent());
if (parent instanceof PsiReturnStatement) {
@@ -19,6 +19,7 @@ import com.intellij.core.JavaPsiBundle;
import com.intellij.ide.IdeBundle;
import com.intellij.java.analysis.JavaAnalysisBundle;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.modcommand.ModCommandAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.LanguageLevelUtil;
import com.intellij.openapi.module.Module;
@@ -457,7 +458,7 @@ public final class HighlightUtil {
return highlightInfo;
}
private static boolean isCastIntentionApplicable(@NotNull PsiExpression expression, @Nullable PsiType toType) {
static boolean isCastIntentionApplicable(@NotNull PsiExpression expression, @Nullable PsiType toType) {
while (expression instanceof PsiTypeCastExpression || expression instanceof PsiParenthesizedExpression) {
if (expression instanceof PsiTypeCastExpression castExpression) {
expression = castExpression.getOperand();
@@ -610,21 +611,34 @@ public final class HighlightUtil {
return null;
}
HighlightInfo.Builder highlightInfo = createIncompatibleTypeHighlightInfo(lType, rType, textRange, navigationShift);
AddTypeArgumentsConditionalFix.register(highlightInfo, expression, lType);
AddTypeArgumentsConditionalFix.register(asConsumer(highlightInfo), expression, lType);
if (rType != null && expression != null && isCastIntentionApplicable(expression, lType)) {
IntentionAction action = getFixFactory().createAddTypeCastFix(lType, expression);
highlightInfo.registerFix(action, null, null, null, null);
}
if (expression != null) {
AdaptExpressionTypeFixUtil.registerExpectedTypeFixes(highlightInfo, textRange, expression, lType, rType);
AdaptExpressionTypeFixUtil.registerExpectedTypeFixes(asConsumer(highlightInfo), expression, lType, rType);
if (!(expression.getParent() instanceof PsiConditionalExpression && PsiTypes.voidType().equals(lType))) {
HighlightFixUtil.registerChangeReturnTypeFix(highlightInfo, expression, lType);
IntentionAction fix = HighlightFixUtil.createChangeReturnTypeFix(expression, lType);
if (fix != null) {
highlightInfo.registerFix(fix, null, null, null, null);
}
}
}
ChangeNewOperatorTypeFix.register(highlightInfo, expression, lType);
ModCommandAction fix = ChangeNewOperatorTypeFix.createFix(expression, lType);
if (fix != null) {
highlightInfo.registerFix(fix, null, null, null, null);
}
return highlightInfo;
}
static @NotNull Consumer<CommonIntentionAction> asConsumer(HighlightInfo.Builder highlightInfo) {
if (highlightInfo == null) {
return fix -> {};
}
return fix -> highlightInfo.registerFix(fix.asIntention(), null, null, null, null);
}
static HighlightInfo.Builder checkReturnFromSwitchExpr(@NotNull PsiReturnStatement statement) {
if (PsiImplUtil.findEnclosingSwitchExpression(statement) != null) {
String message = JavaErrorBundle.message("return.outside.switch.expr");
@@ -400,7 +400,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
info.registerFix(action, null, null, null, null);
}
if (element instanceof PsiExpression expr) {
HighlightFixUtil.registerLambdaReturnTypeFixes(info, element.getTextRange(), expression, expr);
HighlightFixUtil.registerLambdaReturnTypeFixes(HighlightUtil.asConsumer(info), expression, expr);
}
add(info);
}
@@ -845,8 +845,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
@Override
public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) {
if (!hasErrorResults()) add(GenericsHighlightUtil.checkEnumSuperConstructorCall(expression));
if (!hasErrorResults()) add(HighlightClassUtil.checkSuperQualifierType(myFile.getProject(), expression));
visitElement(expression);
if (!hasErrorResults()) {
try {
HighlightMethodUtil.checkMethodCall(expression, getResolveHelper(), myLanguageLevel, myJavaSdkVersion, myFile,
@@ -3,10 +3,7 @@ package com.intellij.codeInsight.daemon.impl.analysis;
import com.intellij.codeInsight.ClassUtil;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.daemon.impl.quickfix.MoveAnnotationOnStaticMemberQualifyingTypeFix;
import com.intellij.codeInsight.daemon.impl.quickfix.MoveAnnotationToPackageInfoFileFix;
import com.intellij.codeInsight.daemon.impl.quickfix.MoveMembersIntoClassFix;
import com.intellij.codeInsight.daemon.impl.quickfix.ReplaceVarWithExplicitTypeFix;
import com.intellij.codeInsight.daemon.impl.quickfix.*;
import com.intellij.codeInsight.intention.CommonIntentionAction;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.codeInsight.intention.impl.BaseIntentionAction;
@@ -36,6 +33,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.intellij.codeInsight.daemon.impl.analysis.HighlightUtil.isCastIntentionApplicable;
import static com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds.*;
import static java.util.Objects.requireNonNull;
import static java.util.Objects.requireNonNullElse;
@@ -146,6 +144,19 @@ final class JavaErrorFixProvider {
return List.of(myFactory.createExtendsListFix(usedClass, throwableType, true));
}
}
if (anchor instanceof PsiExpression expression) {
List<CommonIntentionAction> registrar = new ArrayList<>();
AddTypeArgumentsConditionalFix.register(registrar::add, expression, context.lType());
if (context.rType() != null && isCastIntentionApplicable(expression, context.lType())) {
ContainerUtil.addIfNotNull(registrar, myFactory.createAddTypeCastFix(context.lType(), expression));
}
AdaptExpressionTypeFixUtil.registerExpectedTypeFixes(registrar::add, expression, context.lType(), context.rType());
if (!(expression.getParent() instanceof PsiConditionalExpression && PsiTypes.voidType().equals(context.lType()))) {
ContainerUtil.addIfNotNull(registrar, HighlightFixUtil.createChangeReturnTypeFix(expression, context.lType()));
}
ContainerUtil.addIfNotNull(registrar, ChangeNewOperatorTypeFix.createFix(expression, context.lType()));
return registrar;
}
return List.of();
});
@@ -1,13 +1,10 @@
// 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.quickfix;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.intention.CommonIntentionAction;
import com.intellij.codeInsight.intention.PriorityAction;
import com.intellij.java.analysis.JavaAnalysisBundle;
import com.intellij.modcommand.ActionContext;
import com.intellij.modcommand.ModPsiUpdater;
import com.intellij.modcommand.Presentation;
import com.intellij.modcommand.PsiUpdateModCommandAction;
import com.intellij.modcommand.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
@@ -17,6 +14,8 @@ import com.intellij.psi.util.TypeConversionUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Consumer;
public class AddTypeArgumentsConditionalFix extends PsiUpdateModCommandAction<PsiMethodCallExpression> {
private static final Logger LOG = Logger.getInstance(AddTypeArgumentsConditionalFix.class);
@@ -73,7 +72,7 @@ public class AddTypeArgumentsConditionalFix extends PsiUpdateModCommandAction<Ps
return PsiUtil.getEnclosingStaticElement(element, aClass) != null;
}
public static void register(@NotNull HighlightInfo.Builder highlightInfo, @Nullable PsiExpression expression, @NotNull PsiType lType) {
public static void register(@NotNull Consumer<? super CommonIntentionAction> highlightInfo, @Nullable PsiExpression expression, @NotNull PsiType lType) {
if (lType != PsiTypes.nullType() && expression instanceof PsiConditionalExpression) {
final PsiExpression thenExpression = ((PsiConditionalExpression)expression).getThenExpression();
final PsiExpression elseExpression = ((PsiConditionalExpression)expression).getElseExpression();
@@ -94,8 +93,8 @@ public class AddTypeArgumentsConditionalFix extends PsiUpdateModCommandAction<Ps
}
}
private static void inferTypeArgs(@NotNull HighlightInfo.Builder highlightInfo, PsiType lType, PsiExpression thenExpression) {
final JavaResolveResult result = ((PsiMethodCallExpression)thenExpression).resolveMethodGenerics();
private static void inferTypeArgs(Consumer<? super ModCommandAction> fixConsumer, PsiType lType, PsiExpression expression) {
final JavaResolveResult result = ((PsiMethodCallExpression)expression).resolveMethodGenerics();
final PsiMethod method = (PsiMethod)result.getElement();
if (method != null) {
final PsiType returnType = method.getReturnType();
@@ -103,19 +102,18 @@ public class AddTypeArgumentsConditionalFix extends PsiUpdateModCommandAction<Ps
if (returnType != null && aClass != null && aClass.getQualifiedName() != null) {
final JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(method.getProject());
final PsiDeclarationStatement variableDeclarationStatement =
javaPsiFacade.getElementFactory().createVariableDeclarationStatement("xxx", lType, thenExpression, thenExpression);
javaPsiFacade.getElementFactory().createVariableDeclarationStatement("xxx", lType, expression, expression);
final PsiExpression initializer =
((PsiLocalVariable)variableDeclarationStatement.getDeclaredElements()[0]).getInitializer();
LOG.assertTrue(initializer != null);
final PsiSubstitutor substitutor = javaPsiFacade.getResolveHelper()
.inferTypeArguments(method.getTypeParameters(), method.getParameterList().getParameters(),
((PsiMethodCallExpression)thenExpression).getArgumentList().getExpressions(), PsiSubstitutor.EMPTY,
((PsiMethodCallExpression)expression).getArgumentList().getExpressions(), PsiSubstitutor.EMPTY,
initializer, DefaultParameterTypeInferencePolicy.INSTANCE);
PsiType substitutedType = substitutor.substitute(returnType);
if (substitutedType != null && TypeConversionUtil.isAssignable(lType, substitutedType)) {
highlightInfo.registerFix(new AddTypeArgumentsConditionalFix(substitutor, (PsiMethodCallExpression)thenExpression, method), null,
null, thenExpression.getTextRange(), null);
fixConsumer.accept(new AddTypeArgumentsConditionalFix(substitutor, (PsiMethodCallExpression)expression, method));
}
}
}
@@ -2,12 +2,8 @@
package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInspection.RemoveRedundantTypeArgumentsUtil;
import com.intellij.modcommand.ActionContext;
import com.intellij.modcommand.ModPsiUpdater;
import com.intellij.modcommand.Presentation;
import com.intellij.modcommand.PsiUpdateModCommandAction;
import com.intellij.modcommand.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.UnfairTextRange;
@@ -121,10 +117,10 @@ public final class ChangeNewOperatorTypeFix extends PsiUpdateModCommandAction<Ps
updater.moveCaretTo(newExpression.getTextRange().getEndOffset() + caretOffset);
}
public static void register(@NotNull HighlightInfo.Builder highlightInfo, PsiExpression expression, PsiType lType) {
if (PsiUtil.resolveClassInClassTypeOnly(lType) instanceof PsiAnonymousClass) return;
public static ModCommandAction createFix(PsiExpression expression, PsiType lType) {
if (PsiUtil.resolveClassInClassTypeOnly(lType) instanceof PsiAnonymousClass) return null;
expression = PsiUtil.deparenthesizeExpression(expression);
if (!(expression instanceof PsiNewExpression newExpression)) return;
if (!(expression instanceof PsiNewExpression newExpression)) return null;
final PsiType rType = expression.getType();
PsiType newType = lType;
if (rType instanceof PsiClassType rClassType && newType instanceof PsiClassType newClassType) {
@@ -144,10 +140,10 @@ public final class ChangeNewOperatorTypeFix extends PsiUpdateModCommandAction<Ps
}
}
}
if (rType == null || newType.getCanonicalText().equals(rType.getCanonicalText())) return;
if (rType == null || newType.getCanonicalText().equals(rType.getCanonicalText())) return null;
final PsiClass aClass = PsiTypesUtil.getPsiClass(newType);
if (aClass != null && (aClass.isEnum() || aClass.isAnnotationType())) return;
highlightInfo.registerFix(new ChangeNewOperatorTypeFix(newType, newExpression), null, null, null, null);
if (aClass != null && (aClass.isEnum() || aClass.isAnnotationType())) return null;
return new ChangeNewOperatorTypeFix(newType, newExpression);
}
/* Guesswork
@@ -4,9 +4,9 @@ class A {
A(Integer... i) {}
}
class B extends A {
<error descr="Ambiguous method call: both 'A.A(String...)' and 'A.A(Integer...)' match">public B()</error> {}
<error descr="Ambiguous implicit constructor call: both 'A.A(String...)' and 'A.A(Integer...)' match">public B()</error> {}
}
<error descr="Ambiguous method call: both 'A.A(String...)' and 'A.A(Integer...)' match">class C extends A</error> {}
<error descr="Ambiguous implicit constructor call: both 'A.A(String...)' and 'A.A(Integer...)' match">class C extends A</error> {}
class A1 {
A1(String... i){}