mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[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:
committed by
intellij-monorepo-bot
parent
a157ee801e
commit
8d746a5a24
@@ -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>\\ and</td></tr>\
|
||||
<tr>{3}<td>in <b>{4}</b>\\ match</td></tr>\
|
||||
</table></body></html>
|
||||
|
||||
array.illegal.initializer=Illegal initializer for ''{0}''
|
||||
array.initializer.not.allowed=Array initializer is not allowed here
|
||||
|
||||
+53
-9
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+34
-4
@@ -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);
|
||||
|
||||
+85
@@ -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();
|
||||
}
|
||||
}
|
||||
+12
@@ -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)));
|
||||
|
||||
+8
@@ -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 ";
|
||||
|
||||
+7
@@ -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) {
|
||||
|
||||
+23
-16
@@ -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;
|
||||
|
||||
+1
-120
@@ -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();
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
-35
@@ -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) {
|
||||
}
|
||||
|
||||
+17
@@ -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;
|
||||
|
||||
+5
-6
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user