[java-highlighting] ambiguous method errors migrated

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

GitOrigin-RevId: f8ba887237b6c273e9b111501da16288e930bc49
This commit is contained in:
Tagir Valeev
2025-01-29 11:35:29 +00:00
committed by intellij-monorepo-bot
parent a157ee801e
commit 8d746a5a24
13 changed files with 255 additions and 191 deletions
@@ -289,6 +289,15 @@ call.constructor.must.be.top.level.statement=Call to ''{0}'' must be top-level s
call.constructor.duplicate=Only one explicit constructor call allowed in constructor
call.constructor.record.in.canonical=Canonical constructor cannot delegate to another constructor
call.constructor.recursive=Recursive constructor call
call.unresolved=Cannot resolve method ''{0}''
call.ambiguous=Ambiguous method call: both ''{0}'' and ''{1}'' match
# {0} - colspan, {1} - method1, {2} - class1, {3} - method2, {4} - class2
call.ambiguous.tooltip=\
<html><body><table border=0>\
<tr><td colspan={0}>Ambiguous method call. Both</td></tr>\
<tr>{1}<td>in <b>{2}</b>\\&nbsp;and</td></tr>\
<tr>{3}<td>in <b>{4}</b>\\&nbsp;match</td></tr>\
</table></body></html>
array.illegal.initializer=Illegal initializer for ''{0}''
array.initializer.not.allowed=Array initializer is not allowed here
@@ -3,10 +3,7 @@ package com.intellij.java.codeserver.highlighting;
import com.intellij.codeInsight.ExceptionUtil;
import com.intellij.core.JavaPsiBundle;
import com.intellij.java.codeserver.highlighting.errors.JavaErrorKind;
import com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds;
import com.intellij.java.codeserver.highlighting.errors.JavaIncompatibleTypeErrorContext;
import com.intellij.java.codeserver.highlighting.errors.JavaMismatchedCallContext;
import com.intellij.java.codeserver.highlighting.errors.*;
import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.util.Pair;
import com.intellij.pom.java.JavaFeature;
@@ -947,9 +944,8 @@ final class ExpressionChecker {
}
boolean isDummyConstructorCall(@NotNull PsiMethodCallExpression methodCall,
@NotNull PsiExpressionList list,
@NotNull PsiReferenceExpression referenceToMethod) {
boolean isDummy = false;
@NotNull PsiExpressionList list,
@NotNull PsiReferenceExpression referenceToMethod) {
boolean isThisOrSuper = referenceToMethod.getReferenceNameElement() instanceof PsiKeyword;
if (isThisOrSuper) {
// super(..) or this(..)
@@ -957,11 +953,11 @@ final class ExpressionChecker {
CandidateInfo[] candidates = PsiResolveHelper.getInstance(myVisitor.project())
.getReferencedMethodCandidates(methodCall, true);
if (candidates.length == 1 && !candidates[0].getElement().isPhysical()) {
isDummy = true;// dummy constructor
return true; // dummy constructor
}
}
}
return isDummy;
return false;
}
void checkConstructorCall(@NotNull PsiClassType.ClassResolveResult typeResolveResult,
@@ -1080,4 +1076,52 @@ final class ExpressionChecker {
}
}
}
private static @NotNull Pair<MethodCandidateInfo, MethodCandidateInfo> findCandidates(JavaResolveResult @NotNull [] resolveResults) {
MethodCandidateInfo methodCandidate1 = null;
MethodCandidateInfo methodCandidate2 = null;
for (JavaResolveResult result : resolveResults) {
if (!(result instanceof MethodCandidateInfo candidate)) continue;
if (candidate.isApplicable() && !candidate.getElement().isConstructor()) {
if (methodCandidate1 == null) {
methodCandidate1 = candidate;
}
else {
methodCandidate2 = candidate;
break;
}
}
}
return Pair.pair(methodCandidate1, methodCandidate2);
}
void checkAmbiguousMethodCallArguments(JavaResolveResult @NotNull [] resolveResults,
@NotNull JavaResolveResult resolveResult,
@NotNull PsiMethodCallExpression methodCall) {
PsiExpressionList list = methodCall.getArgumentList();
Pair<MethodCandidateInfo, MethodCandidateInfo> pair = findCandidates(resolveResults);
MethodCandidateInfo methodCandidate1 = pair.first;
MethodCandidateInfo methodCandidate2 = pair.second;
PsiExpression[] expressions = list.getExpressions();
if (PsiTreeUtil.hasErrorElements(list)) return;
if (methodCandidate2 != null) {
if (IncompleteModelUtil.isIncompleteModel(list) &&
ContainerUtil.exists(expressions, e -> IncompleteModelUtil.mayHaveUnknownTypeDueToPendingReference(e))) {
return;
}
myVisitor.report(JavaErrorKinds.CALL_AMBIGUOUS.create(
methodCall, new JavaAmbiguousCallContext(resolveResults, methodCandidate1, methodCandidate2)));
}
else {
if (resolveResult.getElement() != null && (!resolveResult.isAccessible() || !resolveResult.isStaticsScopeCorrect())) return;
if (!ContainerUtil.exists(resolveResults, result -> result instanceof MethodCandidateInfo && result.isAccessible())) return;
if (IncompleteModelUtil.isIncompleteModel(list) &&
ContainerUtil.exists(expressions, IncompleteModelUtil::mayHaveUnknownTypeDueToPendingReference)) {
return;
}
if (ContainerUtil.exists(expressions, e -> e.getType() == null)) return;
myVisitor.report(JavaErrorKinds.CALL_UNRESOLVED.create(methodCall, resolveResults));
}
}
}
@@ -17,10 +17,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;
import com.intellij.psi.util.*;
import com.intellij.refactoring.util.RefactoringChangeUtil;
import com.intellij.util.ObjectUtils;
import org.jetbrains.annotations.Contract;
@@ -569,6 +566,19 @@ final class JavaErrorVisitor extends JavaElementVisitor {
PsiElement resolved = result.getElement();
PsiElement parent = expression.getParent();
PsiExpression qualifierExpression = expression.getQualifierExpression();
if (parent instanceof PsiMethodCallExpression methodCallExpression &&
methodCallExpression.getMethodExpression() == expression &&
(!result.isAccessible() || !result.isStaticsScopeCorrect())) {
PsiExpressionList list = methodCallExpression.getArgumentList();
if (!myExpressionChecker.isDummyConstructorCall(methodCallExpression, list, expression)) {
if (!PsiTreeUtil.findChildrenOfType(methodCallExpression.getArgumentList(), PsiLambdaExpression.class).isEmpty()) {
PsiElement nameElement = expression.getReferenceNameElement();
if (nameElement != null) {
myExpressionChecker.checkAmbiguousMethodCallArguments(results, result, methodCallExpression);
}
}
}
}
if (!hasErrorResults() && myJavaModule == null && qualifierExpression != null) {
if (parent instanceof PsiMethodCallExpression) {
PsiClass psiClass = RefactoringChangeUtil.getQualifierClass(expression);
@@ -775,6 +785,26 @@ final class JavaErrorVisitor extends JavaElementVisitor {
if (!hasErrorResults()) myStatementChecker.checkForeachExpressionTypeIsIterable(expression);
}
@Override
public void visitExpressionList(@NotNull PsiExpressionList list) {
super.visitExpressionList(list);
PsiElement parent = list.getParent();
if (parent instanceof PsiMethodCallExpression expression && expression.getArgumentList() == list) {
PsiReferenceExpression referenceExpression = expression.getMethodExpression();
JavaResolveResult[] results = resolveOptimised(referenceExpression);
if (results == null) return;
JavaResolveResult result = results.length == 1 ? results[0] : JavaResolveResult.EMPTY;
if ((!result.isAccessible() || !result.isStaticsScopeCorrect()) &&
!myExpressionChecker.isDummyConstructorCall(expression, list, referenceExpression) &&
// this check is for fake expression from JspMethodCallImpl
referenceExpression.getParent() == expression &&
PsiTreeUtil.findChildrenOfType(expression.getArgumentList(), PsiLambdaExpression.class).isEmpty()) {
myExpressionChecker.checkAmbiguousMethodCallArguments(results, result, expression);
}
}
}
@Override
public void visitAnnotationArrayInitializer(@NotNull PsiArrayInitializerMemberValue initializer) {
super.visitAnnotationArrayInitializer(initializer);
@@ -0,0 +1,85 @@
// 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.errors;
import com.intellij.java.codeserver.highlighting.JavaCompilationErrorBundle;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.HtmlBuilder;
import com.intellij.openapi.util.text.HtmlChunk;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.psi.util.PsiFormatUtilBase;
import com.intellij.psi.util.PsiUtilCore;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import static com.intellij.openapi.util.text.HtmlChunk.raw;
import static com.intellij.openapi.util.text.HtmlChunk.tag;
/**
* Context for ambiguous method call error
*
* @param results all results of multiple resolve
* @param methodCandidate1 first good candidate from results
* @param methodCandidate2 second good candidate from results
*/
public record JavaAmbiguousCallContext(@NotNull JavaResolveResult @NotNull [] results,
@NotNull MethodCandidateInfo methodCandidate1,
@NotNull MethodCandidateInfo methodCandidate2) {
@NotNull
@Nls
String description() {
PsiMethod element1 = methodCandidate1.getElement();
String m1 = PsiFormatUtil.formatMethod(element1,
methodCandidate1.getSubstitutor(false),
PsiFormatUtilBase.SHOW_CONTAINING_CLASS | PsiFormatUtilBase.SHOW_NAME |
PsiFormatUtilBase.SHOW_PARAMETERS,
PsiFormatUtilBase.SHOW_TYPE);
PsiMethod element2 = methodCandidate2.getElement();
String m2 = PsiFormatUtil.formatMethod(element2,
methodCandidate2.getSubstitutor(false),
PsiFormatUtilBase.SHOW_CONTAINING_CLASS | PsiFormatUtilBase.SHOW_NAME |
PsiFormatUtilBase.SHOW_PARAMETERS,
PsiFormatUtilBase.SHOW_TYPE);
VirtualFile virtualFile1 = PsiUtilCore.getVirtualFile(element1);
VirtualFile virtualFile2 = PsiUtilCore.getVirtualFile(element2);
if (!Comparing.equal(virtualFile1, virtualFile2)) {
if (virtualFile1 != null) m1 += " (In " + virtualFile1.getPresentableUrl() + ")";
if (virtualFile2 != null) m2 += " (In " + virtualFile2.getPresentableUrl() + ")";
}
return JavaCompilationErrorBundle.message("call.ambiguous", m1, m2);
}
@NotNull HtmlChunk tooltip() {
return raw(JavaCompilationErrorBundle.message("call.ambiguous.tooltip",
methodCandidate1.getElement().getParameterList().getParametersCount() + 2,
createAmbiguousMethodHtmlTooltipMethodRow(methodCandidate1),
getContainingClassName(methodCandidate1),
createAmbiguousMethodHtmlTooltipMethodRow(methodCandidate2),
getContainingClassName(methodCandidate2)));
}
private static @NotNull String getContainingClassName(@NotNull MethodCandidateInfo methodCandidate) {
PsiMethod method = methodCandidate.getElement();
PsiClass containingClass = method.getContainingClass();
return containingClass == null ? method.getContainingFile().getName() : JavaErrorFormatUtil.formatClass(containingClass, false);
}
private static @NotNull HtmlChunk createAmbiguousMethodHtmlTooltipMethodRow(@NotNull MethodCandidateInfo methodCandidate) {
PsiMethod method = methodCandidate.getElement();
PsiParameter[] parameters = method.getParameterList().getParameters();
PsiSubstitutor substitutor = methodCandidate.getSubstitutor();
HtmlBuilder row = new HtmlBuilder().append(tag("td").child(tag("b").addText(method.getName())));
for (int j = 0; j < parameters.length; j++) {
PsiParameter parameter = parameters[j];
PsiType type = substitutor.substitute(parameter.getType());
String typeText = (j == 0 ? "(" : "") + type.getPresentableText() + (j == parameters.length - 1 ? ")" : ",");
row.append(tag("td").child(tag("b").addText(typeText)));
}
if (parameters.length == 0) {
row.append(tag("td").child(tag("b").addText("()")));
}
return row.toFragment();
}
}
@@ -870,6 +870,18 @@ public final class JavaErrorKinds {
parameterized(PsiElement.class, JavaMismatchedCallContext.class, "call.wrong.arguments")
.withTooltip((psi, ctx) -> ctx.createTooltip())
.withDescription((psi, ctx) -> ctx.createDescription());
public static final Parameterized<PsiMethodCallExpression, JavaResolveResult[]> CALL_UNRESOLVED =
parameterized(PsiMethodCallExpression.class, JavaResolveResult[].class, "call.unresolved")
.withAnchor((call, results) -> requireNonNullElse(call.getMethodExpression().getReferenceNameElement(), call))
.withRawDescription((call, results) -> message(
"call.unresolved", call.getMethodExpression().getReferenceName() + formatArgumentTypes(call.getArgumentList(), true)));
public static final Parameterized<PsiMethodCallExpression, JavaAmbiguousCallContext> CALL_AMBIGUOUS =
parameterized(PsiMethodCallExpression.class, JavaAmbiguousCallContext.class, "call.ambiguous")
.withAnchor((call, ctx) -> call.getArgumentList())
.withRawDescription((call, ctx) -> ctx.description())
.withTooltip((call, ctx) -> ctx.tooltip());
public static final Parameterized<PsiMethodCallExpression, PsiMethod> CALL_DIRECT_ABSTRACT_METHOD_ACCESS =
parameterized(PsiMethodCallExpression.class, PsiMethod.class, "call.direct.abstract.method.access")
.withRawDescription((call, method) -> message("call.direct.abstract.method.access", formatMethod(method)));
@@ -16,6 +16,14 @@ import org.jetbrains.annotations.Nullable;
import static com.intellij.openapi.util.text.HtmlChunk.*;
/**
* Incompatible type context
*
* @param lType left type (left-side of assignment, return type of the method if return value is incompatible, etc.)
* @param rType right type (right-side of assignment, type of incompatible method return value, etc.; might be null)
* @param reasonForIncompatibleTypes textual reason for incompatible type (probably inference error)
* @see JavaErrorKinds#TYPE_INCOMPATIBLE
*/
public record JavaIncompatibleTypeErrorContext(@NotNull PsiType lType, @Nullable PsiType rType,
@Nullable @Nls String reasonForIncompatibleTypes) {
private static final @NlsSafe String ANONYMOUS = "anonymous ";
@@ -20,6 +20,13 @@ import java.util.List;
import static com.intellij.java.codeserver.highlighting.errors.JavaIncompatibleTypeErrorContext.*;
/**
* Context for mismatched call error
* @param list expression list of mismatched call
* @param candidate call candidate
* @param mismatchedExpressions list of arguments having mismatched types
* @see JavaErrorKinds#CALL_WRONG_ARGUMENTS
*/
public record JavaMismatchedCallContext(@NotNull PsiExpressionList list,
@NotNull MethodCandidateInfo candidate,
@NotNull List<PsiExpression> mismatchedExpressions) {
@@ -164,28 +164,23 @@ public final class HighlightFixUtil {
info.accept(quickFixFactory.createSurroundWithTryCatchFix(element));
}
static void registerStaticProblemQuickFixAction(@Nullable HighlightInfo.Builder info, @NotNull PsiElement refElement, @NotNull PsiJavaCodeReferenceElement place) {
static void registerStaticProblemQuickFixAction(@NotNull Consumer<? super CommonIntentionAction> info, @NotNull PsiElement refElement,
@NotNull PsiJavaCodeReferenceElement place) {
if (place instanceof PsiReferenceExpression && place.getParent() instanceof PsiMethodCallExpression) {
ReplaceGetClassWithClassLiteralFix.registerFix((PsiMethodCallExpression)place.getParent(), info);
}
if (refElement instanceof PsiJvmModifiersOwner) {
List<IntentionAction> fixes =
JvmElementActionFactories.createModifierActions((PsiJvmModifiersOwner)refElement, MemberRequestsKt.modifierRequest(JvmModifier.STATIC, true));
QuickFixAction.registerQuickFixActions(info, null, fixes);
fixes.forEach(info);
}
// make context non-static
PsiModifierListOwner staticParent = PsiUtil.getEnclosingStaticElement(place, null);
if (staticParent != null && isInstanceReference(place)) {
IntentionAction action = QuickFixFactory.getInstance().createModifierListFix(staticParent, PsiModifier.STATIC, false, false);
if (info != null) {
info.registerFix(action, null, null, null, null);
}
info.accept(QuickFixFactory.getInstance().createModifierListFix(staticParent, PsiModifier.STATIC, false, false));
}
if (place instanceof PsiReferenceExpression && refElement instanceof PsiField) {
IntentionAction action = QuickFixFactory.getInstance().createCreateFieldFromUsageFix((PsiReferenceExpression)place);
if (info != null) {
info.registerFix(action, null, null, null, null);
}
info.accept(QuickFixFactory.getInstance().createCreateFieldFromUsageFix((PsiReferenceExpression)place));
}
}
@@ -633,12 +628,11 @@ public final class HighlightFixUtil {
}
static MethodCandidateInfo @NotNull [] toMethodCandidates(JavaResolveResult @NotNull [] resolveResults) {
List<MethodCandidateInfo> candidateList = new ArrayList<>(resolveResults.length);
for (JavaResolveResult result : resolveResults) {
if (!(result instanceof MethodCandidateInfo candidate)) continue;
if (candidate.isAccessible()) candidateList.add(candidate);
}
return candidateList.toArray(new MethodCandidateInfo[0]);
return Arrays.stream(resolveResults)
.filter(result -> result instanceof MethodCandidateInfo)
.map(result -> (MethodCandidateInfo)result)
.filter(CandidateInfo::isAccessible)
.toArray(MethodCandidateInfo[]::new);
}
static void registerFixesOnInvalidConstructorCall(@NotNull Consumer<? super CommonIntentionAction> info,
@@ -809,6 +803,19 @@ public final class HighlightFixUtil {
return requireNonNullElse(GenericsUtil.getLeastUpperBound(currentType, valueType, manager), requireNonNullElse(currentType, valueType));
}
static void registerAmbiguousCallFixes(@NotNull Consumer<? super @Nullable CommonIntentionAction> sink,
@NotNull PsiMethodCallExpression methodCall,
@NotNull JavaResolveResult @NotNull [] resolveResults) {
PsiExpressionList list = methodCall.getArgumentList();
MethodCandidateInfo[] candidates = toMethodCandidates(resolveResults);
CastMethodArgumentFix.REGISTRAR.registerCastActions(candidates, methodCall, sink);
WrapWithAdapterMethodCallFix.registerCastActions(candidates, methodCall, sink);
WrapObjectWithOptionalOfNullableFix.REGISTAR.registerCastActions(candidates, methodCall, sink);
WrapExpressionFix.registerWrapAction(candidates, list.getExpressions(), sink);
PermuteArgumentsFix.registerFix(sink, methodCall, candidates);
registerChangeParameterClassFix(methodCall, list, sink);
}
private static final class ReturnModel {
final PsiReturnStatement myStatement;
final PsiType myType;
@@ -11,11 +11,9 @@ import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.codeInsight.quickfix.UnresolvedReferenceQuickFixUpdater;
import com.intellij.codeInspection.LocalQuickFixOnPsiElementAsIntentionAdapter;
import com.intellij.core.JavaPsiBundle;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.java.JavaFeature;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
@@ -27,9 +25,7 @@ import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.util.*;
import com.intellij.refactoring.util.RefactoringChangeUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.xml.util.XmlStringUtil;
import org.intellij.lang.annotations.Language;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -377,7 +373,7 @@ public final class HighlightMethodUtil {
HighlightInfo.Builder builder =
HighlightInfo.newHighlightInfo(highlightInfoType).range(elementToHighlight).description(description).escapedToolTip(toolTip);
if (element != null && !resolveResult.isStaticsScopeCorrect()) {
HighlightFixUtil.registerStaticProblemQuickFixAction(builder, element, referenceToMethod);
HighlightFixUtil.registerStaticProblemQuickFixAction(asConsumer(builder), element, referenceToMethod);
}
HighlightFixUtil.registerMethodCallIntentions(asConsumer(builder), methodCall, list);
@@ -398,88 +394,6 @@ public final class HighlightMethodUtil {
return builder;
}
static HighlightInfo.Builder checkAmbiguousMethodCallArguments(@NotNull PsiReferenceExpression referenceToMethod,
JavaResolveResult @NotNull [] resolveResults,
@NotNull PsiExpressionList list,
PsiElement element,
@NotNull JavaResolveResult resolveResult,
@NotNull PsiMethodCallExpression methodCall,
@NotNull PsiElement elementToHighlight) {
Pair<MethodCandidateInfo, MethodCandidateInfo> pair = findCandidates(resolveResults);
MethodCandidateInfo methodCandidate1 = pair.first;
MethodCandidateInfo methodCandidate2 = pair.second;
MethodCandidateInfo[] candidates = HighlightFixUtil.toMethodCandidates(resolveResults);
String description;
String toolTip;
PsiExpression[] expressions = list.getExpressions();
if (methodCandidate2 != null) {
if (IncompleteModelUtil.isIncompleteModel(list) &&
ContainerUtil.exists(expressions, e -> IncompleteModelUtil.mayHaveUnknownTypeDueToPendingReference(e))) {
return null;
}
PsiMethod element1 = methodCandidate1.getElement();
String m1 = PsiFormatUtil.formatMethod(element1,
methodCandidate1.getSubstitutor(false),
PsiFormatUtilBase.SHOW_CONTAINING_CLASS | PsiFormatUtilBase.SHOW_NAME |
PsiFormatUtilBase.SHOW_PARAMETERS,
PsiFormatUtilBase.SHOW_TYPE);
PsiMethod element2 = methodCandidate2.getElement();
String m2 = PsiFormatUtil.formatMethod(element2,
methodCandidate2.getSubstitutor(false),
PsiFormatUtilBase.SHOW_CONTAINING_CLASS | PsiFormatUtilBase.SHOW_NAME |
PsiFormatUtilBase.SHOW_PARAMETERS,
PsiFormatUtilBase.SHOW_TYPE);
VirtualFile virtualFile1 = PsiUtilCore.getVirtualFile(element1);
VirtualFile virtualFile2 = PsiUtilCore.getVirtualFile(element2);
if (!Comparing.equal(virtualFile1, virtualFile2)) {
if (virtualFile1 != null) m1 += " (In " + virtualFile1.getPresentableUrl() + ")";
if (virtualFile2 != null) m2 += " (In " + virtualFile2.getPresentableUrl() + ")";
}
description = JavaErrorBundle.message("ambiguous.method.call", m1, m2);
toolTip = createAmbiguousMethodHtmlTooltip(new MethodCandidateInfo[]{methodCandidate1, methodCandidate2});
}
else {
if (element != null && (!resolveResult.isAccessible() || !resolveResult.isStaticsScopeCorrect())) {
return null;
}
if (candidates.length == 0) {
return null;
}
if (IncompleteModelUtil.isIncompleteModel(list) &&
ContainerUtil.exists(expressions, IncompleteModelUtil::mayHaveUnknownTypeDueToPendingReference)) {
return null;
}
if (ContainerUtil.exists(expressions, e -> e.getType() == null)) {
return null;
}
String methodName = referenceToMethod.getReferenceName() + buildArgTypesList(list, true);
description = JavaErrorBundle.message("cannot.resolve.method", methodName);
toolTip = XmlStringUtil.escapeString(description);
}
if (PsiTreeUtil.hasErrorElements(list)) {
return null;
}
HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(elementToHighlight).description(description).escapedToolTip(toolTip);
if (!resolveResult.isAccessible() && resolveResult.isStaticsScopeCorrect() && methodCandidate2 != null) {
HighlightFixUtil.registerAccessQuickFixAction(asConsumer(builder), (PsiJvmMember)element, referenceToMethod,
resolveResult.getCurrentFileResolveScope());
}
if (methodCandidate2 == null) {
HighlightFixUtil.registerMethodCallIntentions(asConsumer(builder), methodCall, list);
}
if (element != null && !resolveResult.isStaticsScopeCorrect()) {
HighlightFixUtil.registerStaticProblemQuickFixAction(builder, element, referenceToMethod);
}
CastMethodArgumentFix.REGISTRAR.registerCastActions(candidates, methodCall, asConsumer(builder));
WrapWithAdapterMethodCallFix.registerCastActions(candidates, methodCall, asConsumer(builder));
WrapObjectWithOptionalOfNullableFix.REGISTAR.registerCastActions(candidates, methodCall, asConsumer(builder));
WrapExpressionFix.registerWrapAction(candidates, expressions, asConsumer(builder));
PermuteArgumentsFix.registerFix(asConsumer(builder), methodCall, candidates);
HighlightFixUtil.registerChangeParameterClassFix(methodCall, list, asConsumer(builder));
return builder;
}
private static @NotNull Pair<MethodCandidateInfo, MethodCandidateInfo> findCandidates(JavaResolveResult @NotNull [] resolveResults) {
MethodCandidateInfo methodCandidate1 = null;
MethodCandidateInfo methodCandidate2 = null;
@@ -498,39 +412,6 @@ public final class HighlightMethodUtil {
return Pair.pair(methodCandidate1, methodCandidate2);
}
private static @NotNull @NlsContexts.Tooltip String createAmbiguousMethodHtmlTooltip(MethodCandidateInfo @NotNull [] methodCandidates) {
return JavaErrorBundle.message("ambiguous.method.html.tooltip",
methodCandidates[0].getElement().getParameterList().getParametersCount() + 2,
createAmbiguousMethodHtmlTooltipMethodRow(methodCandidates[0]),
getContainingClassName(methodCandidates[0]),
createAmbiguousMethodHtmlTooltipMethodRow(methodCandidates[1]),
getContainingClassName(methodCandidates[1]));
}
private static @NotNull String getContainingClassName(@NotNull MethodCandidateInfo methodCandidate) {
PsiMethod method = methodCandidate.getElement();
PsiClass containingClass = method.getContainingClass();
return containingClass == null ? method.getContainingFile().getName() : HighlightUtil.formatClass(containingClass, false);
}
@Language("HTML")
private static @NotNull String createAmbiguousMethodHtmlTooltipMethodRow(@NotNull MethodCandidateInfo methodCandidate) {
PsiMethod method = methodCandidate.getElement();
PsiParameter[] parameters = method.getParameterList().getParameters();
PsiSubstitutor substitutor = methodCandidate.getSubstitutor();
StringBuilder ms = new StringBuilder("<td><b>" + method.getName() + "</b></td>");
for (int j = 0; j < parameters.length; j++) {
PsiParameter parameter = parameters[j];
PsiType type = substitutor.substitute(parameter.getType());
ms.append("<td><b>").append(j == 0 ? "(" : "").append(XmlStringUtil.escapeString(type.getPresentableText()))
.append(j == parameters.length - 1 ? ")" : ",").append("</b></td>");
}
if (parameters.length == 0) {
ms.append("<td><b>()</b></td>");
}
return ms.toString();
}
static HighlightInfo.Builder checkAbstractMethodInConcreteClass(@NotNull PsiMethod method, @NotNull PsiElement elementToHighlight) {
HighlightInfo.Builder errorResult = null;
PsiClass aClass = method.getContainingClass();
@@ -1291,7 +1291,7 @@ public final class HighlightUtil {
String description = staticContextProblemDescription(resolved);
HighlightInfo.Builder info =
HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF).range(refName).descriptionAndTooltip(description);
HighlightFixUtil.registerStaticProblemQuickFixAction(info, resolved, ref);
HighlightFixUtil.registerStaticProblemQuickFixAction(asConsumer(info), resolved, ref);
if (ref instanceof PsiReferenceExpression expression) {
IntentionAction action = getFixFactory().createRenameWrongRefFix(expression);
info.registerFix(action, null, null, null, null);
@@ -480,33 +480,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
if (!hasErrorResults()) add(HighlightUtil.checkConditionalExpressionBranchTypesMatch(expression, type));
}
@Override
public void visitExpressionList(@NotNull PsiExpressionList list) {
super.visitExpressionList(list);
PsiElement parent = list.getParent();
if (parent instanceof PsiMethodCallExpression expression && expression.getArgumentList() == list) {
PsiReferenceExpression referenceExpression = expression.getMethodExpression();
JavaResolveResult[] results = resolveOptimised(referenceExpression);
if (results == null) return;
JavaResolveResult result = results.length == 1 ? results[0] : JavaResolveResult.EMPTY;
if ((!result.isAccessible() || !result.isStaticsScopeCorrect()) &&
!HighlightMethodUtil.isDummyConstructorCall(expression, getResolveHelper(), list, referenceExpression) &&
// this check is for fake expression from JspMethodCallImpl
referenceExpression.getParent() == expression) {
try {
if (PsiTreeUtil.findChildrenOfType(expression.getArgumentList(), PsiLambdaExpression.class).isEmpty()) {
PsiElement resolved = result.getElement();
add(HighlightMethodUtil.checkAmbiguousMethodCallArguments(referenceExpression, results, list, resolved, result, expression,
list));
}
}
catch (IndexNotReadyException ignored) {
}
}
}
}
@Override
public void visitField(@NotNull PsiField field) {
super.visitField(field);
@@ -853,14 +826,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
try {
add(HighlightMethodUtil.checkAmbiguousMethodCallIdentifier(
expression, results, list, resolved, result, methodCallExpression, myLanguageLevel, myFile));
if (!PsiTreeUtil.findChildrenOfType(methodCallExpression.getArgumentList(), PsiLambdaExpression.class).isEmpty()) {
PsiElement nameElement = expression.getReferenceNameElement();
if (nameElement != null) {
add(HighlightMethodUtil.checkAmbiguousMethodCallArguments(
expression, results, list, resolved, result, methodCallExpression, nameElement));
}
}
}
catch (IndexNotReadyException ignored) {
}
@@ -405,6 +405,23 @@ final class JavaErrorFixProvider {
}
}
});
fixes(CALL_AMBIGUOUS, (error, sink) -> {
PsiMethodCallExpression methodCall = error.psi();
JavaResolveResult[] resolveResults = error.context().results();
HighlightFixUtil.registerAmbiguousCallFixes(sink, methodCall, resolveResults);
});
fixes(CALL_UNRESOLVED, (error, sink) -> {
PsiMethodCallExpression methodCall = error.psi();
HighlightFixUtil.registerMethodCallIntentions(sink, methodCall, methodCall.getArgumentList());
JavaResolveResult[] resolveResults = error.context();
if (resolveResults.length == 1) {
PsiElement element = resolveResults[0].getElement();
if (element != null && !resolveResults[0].isStaticsScopeCorrect()) {
HighlightFixUtil.registerStaticProblemQuickFixAction(sink, element, methodCall.getMethodExpression());
}
}
HighlightFixUtil.registerAmbiguousCallFixes(sink, methodCall, resolveResults);
});
fix(CALL_DIRECT_ABSTRACT_METHOD_ACCESS, error -> {
PsiMethod method = error.context();
int options = PsiFormatUtilBase.SHOW_NAME | PsiFormatUtilBase.SHOW_CONTAINING_CLASS;
@@ -1,7 +1,7 @@
// 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.codeInspection.CommonQuickFixBundle;
import com.intellij.java.analysis.JavaAnalysisBundle;
@@ -17,6 +17,8 @@ import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Consumer;
public class ReplaceGetClassWithClassLiteralFix extends PsiUpdateModCommandAction<PsiMethodCallExpression> {
public ReplaceGetClassWithClassLiteralFix(PsiMethodCallExpression expression) {
super(expression);
@@ -45,14 +47,11 @@ public class ReplaceGetClassWithClassLiteralFix extends PsiUpdateModCommandActio
return JavaAnalysisBundle.message("replace.get.class.with.class.literal");
}
public static void registerFix(PsiMethodCallExpression callExpression, HighlightInfo.Builder errorResult) {
public static void registerFix(PsiMethodCallExpression callExpression, @NotNull Consumer<? super CommonIntentionAction> info) {
if (callExpression.getMethodExpression().getQualifierExpression() == null) {
PsiMethod method = callExpression.resolveMethod();
if (method != null && PsiTypesUtil.isGetClass(method)) {
var action = new ReplaceGetClassWithClassLiteralFix(callExpression);
if (errorResult != null) {
errorResult.registerFix(action, null, null, null, null);
}
info.accept(new ReplaceGetClassWithClassLiteralFix(callExpression));
}
}
}