From 8d746a5a24ddd6a7b74f86fbb9bf8235304feee3 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Tue, 28 Jan 2025 12:54:59 +0100 Subject: [PATCH] [java-highlighting] ambiguous method errors migrated Part of IDEA-365344 Create a new Java error highlighter with minimal dependencies (PSI only) GitOrigin-RevId: f8ba887237b6c273e9b111501da16288e930bc49 --- .../JavaCompilationErrorBundle.properties | 9 ++ .../highlighting/ExpressionChecker.java | 62 +++++++-- .../highlighting/JavaErrorVisitor.java | 38 +++++- .../errors/JavaAmbiguousCallContext.java | 85 ++++++++++++ .../highlighting/errors/JavaErrorKinds.java | 12 ++ .../JavaIncompatibleTypeErrorContext.java | 8 ++ .../errors/JavaMismatchedCallContext.java | 7 + .../impl/analysis/HighlightFixUtil.java | 39 +++--- .../impl/analysis/HighlightMethodUtil.java | 121 +----------------- .../daemon/impl/analysis/HighlightUtil.java | 2 +- .../impl/analysis/HighlightVisitorImpl.java | 35 ----- .../impl/analysis/JavaErrorFixProvider.java | 17 +++ .../ReplaceGetClassWithClassLiteralFix.java | 11 +- 13 files changed, 255 insertions(+), 191 deletions(-) create mode 100644 java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaAmbiguousCallContext.java diff --git a/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties b/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties index 053972a22bdc..01d2d8215cc3 100644 --- a/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties +++ b/java/codeserver/highlighting/resources/messages/JavaCompilationErrorBundle.properties @@ -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=\ + \ + \ + {1}\ + {3}\ +
Ambiguous method call. Both
in {2}\\ and
in {4}\\ match
array.illegal.initializer=Illegal initializer for ''{0}'' array.initializer.not.allowed=Array initializer is not allowed here diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ExpressionChecker.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ExpressionChecker.java index f73cea6e3b80..50499e6a60ef 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ExpressionChecker.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ExpressionChecker.java @@ -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 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 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)); + } + } } diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/JavaErrorVisitor.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/JavaErrorVisitor.java index 7055cd7016cd..b9d3cb5f92b8 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/JavaErrorVisitor.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/JavaErrorVisitor.java @@ -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); diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaAmbiguousCallContext.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaAmbiguousCallContext.java new file mode 100644 index 000000000000..b660bc436ea3 --- /dev/null +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaAmbiguousCallContext.java @@ -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(); + } +} diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKinds.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKinds.java index 077de0c4a197..89d8221cb3e4 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKinds.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaErrorKinds.java @@ -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 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 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 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))); diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaIncompatibleTypeErrorContext.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaIncompatibleTypeErrorContext.java index 1f603f712d0d..68143b21e087 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaIncompatibleTypeErrorContext.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaIncompatibleTypeErrorContext.java @@ -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 "; diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaMismatchedCallContext.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaMismatchedCallContext.java index f80ef7920555..96586bc1cf66 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaMismatchedCallContext.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/errors/JavaMismatchedCallContext.java @@ -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 mismatchedExpressions) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightFixUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightFixUtil.java index b551ca215b04..09fd10478e08 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightFixUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightFixUtil.java @@ -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 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 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 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 info, @@ -809,6 +803,19 @@ public final class HighlightFixUtil { return requireNonNullElse(GenericsUtil.getLeastUpperBound(currentType, valueType, manager), requireNonNullElse(currentType, valueType)); } + static void registerAmbiguousCallFixes(@NotNull Consumer 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; diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java index 6a35d030ccad..729adbfed910 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java @@ -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 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 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("" + method.getName() + ""); - for (int j = 0; j < parameters.length; j++) { - PsiParameter parameter = parameters[j]; - PsiType type = substitutor.substitute(parameter.getType()); - ms.append("").append(j == 0 ? "(" : "").append(XmlStringUtil.escapeString(type.getPresentableText())) - .append(j == parameters.length - 1 ? ")" : ",").append(""); - } - if (parameters.length == 0) { - ms.append("()"); - } - return ms.toString(); - } - static HighlightInfo.Builder checkAbstractMethodInConcreteClass(@NotNull PsiMethod method, @NotNull PsiElement elementToHighlight) { HighlightInfo.Builder errorResult = null; PsiClass aClass = method.getContainingClass(); diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java index b19ff579b12c..f6571d3aa853 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java @@ -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); diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index ffce1d98e853..24e8be6ea52c 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -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) { } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorFixProvider.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorFixProvider.java index 49b4922d89fd..ba663377eddb 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorFixProvider.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorFixProvider.java @@ -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; diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ReplaceGetClassWithClassLiteralFix.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ReplaceGetClassWithClassLiteralFix.java index fa0121b6b3ae..e7017a077e1c 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ReplaceGetClassWithClassLiteralFix.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ReplaceGetClassWithClassLiteralFix.java @@ -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 { 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 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)); } } }