[java-highlighting] Most of method-reference problems migrated

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

GitOrigin-RevId: 03528950826974c1875e89868dc335c88c2e8149
This commit is contained in:
Tagir Valeev
2025-02-04 11:32:57 +01:00
committed by intellij-monorepo-bot
parent 17651f8c19
commit 577de68f8a
14 changed files with 256 additions and 304 deletions

View File

@@ -55,7 +55,21 @@ lambda.inference.error={0}
lambda.return.type.error={0}
lambda.target.not.interface=Target type of a lambda conversion must be an interface
method.reference.not.expected=Method reference expression is not expected here
method.reference.sealed=Method reference cannot implement a sealed interface
method.reference.return.type.error={0}
method.reference.unresolved.constructor=Cannot resolve constructor ''{0}''
method.reference.unresolved.method=Cannot resolve method ''{0}''
method.reference.inference.error={0}
method.reference.raw.constructor=Raw constructor reference with explicit type parameters for constructor
method.reference.qualifier.class.unresolved=Cannot find class {0}
method.reference.qualifier.wildcard=Unexpected wildcard
method.reference.abstract.method=Abstract method ''{0}'' cannot be accessed directly
method.reference.non.static.method.in.static.context=Non-static method cannot be referenced from a static context
method.reference.static.method.non.static.qualifier=Static method referenced through non-static qualifier
method.reference.static.method.receiver=Static method referenced through receiver
method.reference.parameterized.qualifier=Parameterized qualifier on static method reference
method.reference.enclosing.instance.not.in.scope=An enclosing instance of type {0} is not in scope
safe.varargs.on.record.component=@SafeVarargs is not allowed on a record component
safe.varargs.on.fixed.arity=@SafeVarargs is not allowed on methods with fixed arity

View File

@@ -10,19 +10,12 @@ import com.intellij.psi.*;
import com.intellij.psi.impl.IncompleteModelUtil;
import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.util.MethodSignature;
import com.intellij.psi.util.PsiTypesUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.*;
import static com.intellij.psi.LambdaUtil.getFunction;
import static com.intellij.psi.LambdaUtil.getTargetMethod;
final class FunctionChecker {
private final @NotNull JavaErrorVisitor myVisitor;
@@ -47,6 +40,113 @@ final class FunctionChecker {
}
}
void checkMethodReferenceQualifier(@NotNull PsiMethodReferenceExpression expression) {
PsiElement referenceNameElement = expression.getReferenceNameElement();
PsiElement qualifier = expression.getQualifier();
if (referenceNameElement instanceof PsiKeyword) {
if (!PsiMethodReferenceUtil.isValidQualifier(expression) && qualifier != null) {
boolean pending = qualifier instanceof PsiJavaCodeReferenceElement ref &&
IncompleteModelUtil.isIncompleteModel(expression) &&
IncompleteModelUtil.canBePendingReference(ref);
if (!pending) {
myVisitor.report(JavaErrorKinds.METHOD_REFERENCE_QUALIFIER_CLASS_UNRESOLVED.create(qualifier));
}
}
}
if (qualifier instanceof PsiTypeElement typeElement) {
PsiType psiType = typeElement.getType();
if (psiType instanceof PsiClassType) {
final PsiJavaCodeReferenceElement referenceElement = typeElement.getInnermostComponentReferenceElement();
if (referenceElement != null) {
PsiType[] typeParameters = referenceElement.getTypeParameters();
for (PsiType typeParameter : typeParameters) {
if (typeParameter instanceof PsiWildcardType) {
myVisitor.report(JavaErrorKinds.METHOD_REFERENCE_QUALIFIER_WILDCARD.create(typeElement));
break;
}
}
}
}
}
}
void checkRawConstructorReference(@NotNull PsiMethodReferenceExpression expression) {
if (expression.isConstructor()) {
PsiType[] typeParameters = expression.getTypeParameters();
if (typeParameters.length > 0) {
PsiElement qualifier = expression.getQualifier();
if (qualifier instanceof PsiReferenceExpression) {
PsiElement resolve = ((PsiReferenceExpression)qualifier).resolve();
if (resolve instanceof PsiClass && ((PsiClass)resolve).hasTypeParameters()) {
myVisitor.report(JavaErrorKinds.METHOD_REFERENCE_RAW_CONSTRUCTOR.create(expression));
}
}
}
}
}
void checkMethodReferenceContext(@NotNull PsiMethodReferenceExpression methodRef, @NotNull PsiType functionalInterfaceType) {
PsiElement resolve = methodRef.resolve();
if (resolve == null) return;
PsiClass containingClass = resolve instanceof PsiMethod method ? method.getContainingClass() : (PsiClass)resolve;
boolean isStaticSelector = PsiMethodReferenceUtil.isStaticallyReferenced(methodRef);
PsiElement qualifier = methodRef.getQualifier();
boolean isConstructor = true;
if (resolve instanceof PsiMethod method) {
boolean isMethodStatic = method.hasModifierProperty(PsiModifier.STATIC);
isConstructor = method.isConstructor();
PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
boolean receiverReferenced = PsiMethodReferenceUtil.isResolvedBySecondSearch(
methodRef, interfaceMethod != null ? interfaceMethod.getSignature(LambdaUtil.getSubstitutor(interfaceMethod, resolveResult)) : null,
method.isVarArgs(), isMethodStatic, method.getParameterList().getParametersCount());
if (method.hasModifierProperty(PsiModifier.ABSTRACT) && qualifier instanceof PsiSuperExpression) {
myVisitor.report(JavaErrorKinds.METHOD_REFERENCE_ABSTRACT_METHOD.create(methodRef, method));
return;
}
if (!receiverReferenced && isStaticSelector && !isMethodStatic && !isConstructor) {
if (functionalInterfaceType instanceof PsiClassType classType && classType.hasParameters()) {
// Prefer surrounding error, as it could be more descriptive
if (hasSurroundingInferenceError(methodRef)) return;
}
myVisitor.report(JavaErrorKinds.METHOD_REFERENCE_NON_STATIC_METHOD_IN_STATIC_CONTEXT.create(methodRef, method));
return;
}
if (!receiverReferenced && !isStaticSelector && isMethodStatic) {
myVisitor.report(JavaErrorKinds.METHOD_REFERENCE_STATIC_METHOD_NON_STATIC_QUALIFIER.create(methodRef, method));
return;
}
if (receiverReferenced && isStaticSelector && isMethodStatic) {
myVisitor.report(JavaErrorKinds.METHOD_REFERENCE_STATIC_METHOD_RECEIVER.create(methodRef, method));
return;
}
if (isStaticSelector && isMethodStatic && qualifier instanceof PsiTypeElement) {
PsiJavaCodeReferenceElement referenceElement = PsiTreeUtil.getChildOfType(qualifier, PsiJavaCodeReferenceElement.class);
if (referenceElement != null) {
PsiReferenceParameterList parameterList = referenceElement.getParameterList();
if (parameterList != null && parameterList.getTypeArguments().length > 0) {
myVisitor.report(JavaErrorKinds.METHOD_REFERENCE_PARAMETERIZED_QUALIFIER.create(parameterList));
return;
}
}
}
}
if (isConstructor) {
if (containingClass != null && PsiUtil.isInnerClass(containingClass) && containingClass.isPhysical()) {
PsiClass outerClass = containingClass.getContainingClass();
if (outerClass != null && !InheritanceUtil.hasEnclosingInstanceInScope(outerClass, methodRef, true, false)) {
myVisitor.report(JavaErrorKinds.METHOD_REFERENCE_ENCLOSING_INSTANCE_NOT_IN_SCOPE.create(methodRef, outerClass));
}
}
}
}
private static @Nullable JavaCompilationError<?, ?> getFunctionalInterfaceError(
@NotNull PsiFunctionalExpression context, PsiType functionalInterfaceType) {
if (functionalInterfaceType instanceof PsiIntersectionType intersection) {
@@ -54,14 +154,14 @@ final class FunctionChecker {
Map<PsiType, MethodSignature> typeAndSignature = new HashMap<>();
for (PsiType type : intersection.getConjuncts()) {
if (getFunctionalInterfaceError(context, type) == null) {
MethodSignature signature = getFunction(PsiUtil.resolveClassInType(type));
MethodSignature signature = LambdaUtil.getFunction(PsiUtil.resolveClassInType(type));
signatures.add(signature);
typeAndSignature.put(type, signature);
}
}
PsiType baseType = typeAndSignature.entrySet().iterator().next().getKey();
MethodSignature baseSignature = typeAndSignature.get(baseType);
LambdaUtil.TargetMethodContainer baseContainer = getTargetMethod(baseType, baseSignature, baseType);
LambdaUtil.TargetMethodContainer baseContainer = LambdaUtil.getTargetMethod(baseType, baseSignature, baseType);
if (baseContainer == null) {
return JavaErrorKinds.LAMBDA_NO_TARGET_METHOD.create(context, baseType);
}
@@ -71,7 +171,7 @@ final class FunctionChecker {
if (baseType == entry.getKey()) {
continue;
}
LambdaUtil.TargetMethodContainer container = getTargetMethod(entry.getKey(), baseSignature, baseType);
LambdaUtil.TargetMethodContainer container = LambdaUtil.getTargetMethod(entry.getKey(), baseSignature, baseType);
if (container == null) {
return JavaErrorKinds.LAMBDA_MULTIPLE_TARGET_METHODS.create(context, functionalInterfaceType);
}
@@ -85,7 +185,7 @@ final class FunctionChecker {
if (typeAndSignature.containsKey(type)) {
continue;
}
LambdaUtil.TargetMethodContainer container = getTargetMethod(type, baseSignature, baseType);
LambdaUtil.TargetMethodContainer container = LambdaUtil.getTargetMethod(type, baseSignature, baseType);
if (container == null) {
continue;
}
@@ -101,7 +201,7 @@ final class FunctionChecker {
PsiClass aClass = resolveResult.getElement();
if (aClass != null) {
if (aClass instanceof PsiTypeParameter) return null; //should be logged as cyclic inference
MethodSignature functionalMethod = getFunction(aClass);
MethodSignature functionalMethod = LambdaUtil.getFunction(aClass);
if (functionalMethod != null && functionalMethod.getTypeParameters().length > 0) {
return JavaErrorKinds.LAMBDA_SAM_GENERIC.create(context);
}
@@ -191,4 +291,50 @@ final class FunctionChecker {
}
return false;
}
void checkMethodReferenceReturnType(@NotNull PsiMethodReferenceExpression expression, @NotNull JavaResolveResult result,
@Nullable PsiType functionalInterfaceType) {
String badReturnTypeMessage = PsiMethodReferenceUtil.checkReturnType(expression, result, functionalInterfaceType);
if (badReturnTypeMessage != null) {
myVisitor.report(JavaErrorKinds.METHOD_REFERENCE_RETURN_TYPE_ERROR.create(expression, badReturnTypeMessage));
}
}
void checkMethodReferenceResolve(@NotNull PsiMethodReferenceExpression expression,
@NotNull JavaResolveResult @NotNull [] results,
@Nullable PsiType functionalInterfaceType) {
boolean resolvedButNonApplicable = results.length == 1 && results[0] instanceof MethodCandidateInfo methodInfo &&
!methodInfo.isApplicable() &&
functionalInterfaceType != null;
if (results.length != 1 || resolvedButNonApplicable) {
if (results.length == 1 && ((MethodCandidateInfo)results[0]).getInferenceErrorMessage() != null) {
myVisitor.report(JavaErrorKinds.METHOD_REFERENCE_INFERENCE_ERROR.create(expression, (MethodCandidateInfo)results[0]));
return;
}
if (expression.isConstructor()) {
PsiClass containingClass = PsiMethodReferenceUtil.getQualifierResolveResult(expression).getContainingClass();
if (containingClass != null && containingClass.isPhysical()) {
myVisitor.report(JavaErrorKinds.METHOD_REFERENCE_UNRESOLVED_CONSTRUCTOR.create(expression, containingClass));
}
}
else if (results.length > 1) {
if (!IncompleteModelUtil.isIncompleteModel(expression) ||
!IncompleteModelUtil.isUnresolvedClassType(functionalInterfaceType)) {
myVisitor.report(JavaErrorKinds.REFERENCE_AMBIGUOUS.create(expression, Arrays.asList(results)));
}
}
else {
if (IncompleteModelUtil.isIncompleteModel(expression) && IncompleteModelUtil.canBePendingReference(expression)) {
PsiElement referenceNameElement = expression.getReferenceNameElement();
if (referenceNameElement != null) {
myVisitor.report(JavaErrorKinds.REFERENCE_PENDING.create(referenceNameElement));
}
}
else if (!(resolvedButNonApplicable && hasSurroundingInferenceError(expression))) {
myVisitor.report(JavaErrorKinds.METHOD_REFERENCE_UNRESOLVED_METHOD.create(expression));
}
}
}
}
}

View File

@@ -14,6 +14,8 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.java.JavaFeature;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.impl.IncompleteModelUtil;
import com.intellij.psi.impl.source.DummyHolder;
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil;
import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl;
@@ -334,12 +336,23 @@ final class JavaErrorVisitor extends JavaElementVisitor {
public void visitMethodReferenceExpression(@NotNull PsiMethodReferenceExpression expression) {
visitElement(expression);
checkFeature(expression, JavaFeature.METHOD_REFERENCES);
PsiElement parent = PsiUtil.skipParenthesizedExprUp(expression.getParent());
if (toReportFunctionalExpressionProblemOnParent(parent)) return;
PsiType functionalInterfaceType = expression.getFunctionalInterfaceType();
if (functionalInterfaceType != null && !PsiTypesUtil.allTypeParametersResolved(expression, functionalInterfaceType)) return;
JavaResolveResult[] results = expression.multiResolve(true);
JavaResolveResult result = results.length == 1 ? results[0] : JavaResolveResult.EMPTY;
PsiElement method = result.getElement();
if (method instanceof PsiJvmMember member && !result.isAccessible()) {
myModifierChecker.reportAccessProblem(expression, member, result);
return;
}
if (!(parent instanceof DummyHolder) && !LambdaUtil.isValidLambdaContext(parent)) {
report(JavaErrorKinds.METHOD_REFERENCE_NOT_EXPECTED.create(expression));
return;
}
if (!hasErrorResults()) myFunctionChecker.checkMethodReferenceQualifier(expression);
if (!hasErrorResults()) {
boolean resolvedButNonApplicable = results.length == 1 && results[0] instanceof MethodCandidateInfo methodInfo &&
!methodInfo.isApplicable() &&
@@ -372,9 +385,19 @@ final class JavaErrorVisitor extends JavaElementVisitor {
myExpressionChecker.checkStaticInterfaceCallQualifier(expression, result, containingClass);
}
}
if (!hasErrorResults()) myFunctionChecker.checkRawConstructorReference(expression);
if (functionalInterfaceType != null) {
if (!hasErrorResults()) myFunctionChecker.checkExtendsSealedClass(expression, functionalInterfaceType);
boolean isFunctional = LambdaUtil.isFunctionalType(functionalInterfaceType);
if (!hasErrorResults() && !isFunctional &&
!(IncompleteModelUtil.isIncompleteModel(expression) &&
IncompleteModelUtil.isUnresolvedClassType(functionalInterfaceType))) {
report(JavaErrorKinds.LAMBDA_NOT_FUNCTIONAL_INTERFACE.create(expression, functionalInterfaceType));
}
if (!hasErrorResults()) myFunctionChecker.checkMethodReferenceContext(expression, functionalInterfaceType);
}
if (!hasErrorResults()) myFunctionChecker.checkMethodReferenceResolve(expression, results, functionalInterfaceType);
if (!hasErrorResults()) myFunctionChecker.checkMethodReferenceReturnType(expression, result, functionalInterfaceType);
}
@Override

View File

@@ -142,8 +142,44 @@ public final class JavaErrorKinds {
public static final Parameterized<PsiElement, String> LAMBDA_RETURN_TYPE_ERROR =
parameterized(PsiElement.class, String.class, "lambda.return.type.error")
.withRawDescription((psi, message) -> message("lambda.return.type.error", message));
public static final Simple<PsiMethodReferenceExpression> METHOD_REFERENCE_SEALED = error("method.reference.sealed");
public static final Simple<PsiMethodReferenceExpression> METHOD_REFERENCE_NOT_EXPECTED = error("method.reference.not.expected");
public static final Simple<PsiMethodReferenceExpression> METHOD_REFERENCE_RAW_CONSTRUCTOR = error("method.reference.raw.constructor");
public static final Simple<PsiElement> METHOD_REFERENCE_QUALIFIER_CLASS_UNRESOLVED =
error(PsiElement.class, "method.reference.qualifier.class.unresolved")
.withRawDescription(qualifier -> message("method.reference.qualifier.class.unresolved", qualifier.getText()));
public static final Simple<PsiTypeElement> METHOD_REFERENCE_QUALIFIER_WILDCARD = error("method.reference.qualifier.wildcard");
public static final Parameterized<PsiMethodReferenceExpression, String> METHOD_REFERENCE_RETURN_TYPE_ERROR =
parameterized(PsiMethodReferenceExpression.class, String.class, "method.reference.return.type.error")
.withRawDescription((psi, message) -> message("method.reference.return.type.error", message));
public static final Parameterized<PsiMethodReferenceExpression, MethodCandidateInfo> METHOD_REFERENCE_INFERENCE_ERROR =
parameterized(PsiMethodReferenceExpression.class, MethodCandidateInfo.class, "method.reference.inference.error")
.withAnchor((ref, candidate) -> requireNonNullElse(ref.getReferenceNameElement(), ref))
.withRawDescription((ref, candidate) -> message("method.reference.inference.error", candidate.getInferenceErrorMessage()));
public static final Parameterized<PsiMethodReferenceExpression, PsiClass> METHOD_REFERENCE_UNRESOLVED_CONSTRUCTOR =
parameterized(PsiMethodReferenceExpression.class, PsiClass.class, "method.reference.unresolved.constructor")
.withAnchor((ref, cls) -> requireNonNullElse(ref.getReferenceNameElement(), ref))
.withRawDescription((ref, cls) -> message("method.reference.unresolved.constructor", cls.getName()));
public static final Simple<PsiMethodReferenceExpression> METHOD_REFERENCE_UNRESOLVED_METHOD =
error(PsiMethodReferenceExpression.class, "method.reference.unresolved.method")
.withAnchor(ref -> requireNonNullElse(ref.getReferenceNameElement(), ref))
.withHighlightType(ref -> JavaErrorHighlightType.WRONG_REF)
.withRawDescription(ref -> message("method.reference.unresolved.method", ref.getReferenceName()));
public static final Parameterized<PsiMethodReferenceExpression, PsiMethod> METHOD_REFERENCE_ABSTRACT_METHOD =
parameterized(PsiMethodReferenceExpression.class, PsiMethod.class, "method.reference.abstract.method")
.withRawDescription((psi, method) -> message("method.reference.abstract.method", method.getName()));
public static final Parameterized<PsiMethodReferenceExpression, PsiMethod> METHOD_REFERENCE_NON_STATIC_METHOD_IN_STATIC_CONTEXT =
parameterized(PsiMethodReferenceExpression.class, PsiMethod.class, "method.reference.non.static.method.in.static.context");
public static final Parameterized<PsiMethodReferenceExpression, PsiMethod> METHOD_REFERENCE_STATIC_METHOD_NON_STATIC_QUALIFIER =
parameterized(PsiMethodReferenceExpression.class, PsiMethod.class, "method.reference.static.method.non.static.qualifier");
public static final Parameterized<PsiMethodReferenceExpression, PsiMethod> METHOD_REFERENCE_STATIC_METHOD_RECEIVER =
parameterized(PsiMethodReferenceExpression.class, PsiMethod.class, "method.reference.static.method.receiver");
public static final Parameterized<PsiMethodReferenceExpression, PsiClass> METHOD_REFERENCE_ENCLOSING_INSTANCE_NOT_IN_SCOPE =
parameterized(PsiMethodReferenceExpression.class, PsiClass.class, "method.reference.enclosing.instance.not.in.scope")
.withRawDescription((psi, cls) -> message("method.reference.enclosing.instance.not.in.scope", formatClass(cls)));
public static final Simple<PsiReferenceParameterList> METHOD_REFERENCE_PARAMETERIZED_QUALIFIER =
error("method.reference.parameterized.qualifier");
public static final Parameterized<PsiAnnotation, @NotNull List<PsiAnnotation.@NotNull TargetType>> ANNOTATION_NOT_APPLICABLE =
error(PsiAnnotation.class, "annotation.not.applicable").<@NotNull List<PsiAnnotation.@NotNull TargetType>>parameterized()

View File

@@ -6,22 +6,13 @@ import com.intellij.codeInsight.daemon.impl.DefaultHighlightUtil;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
import com.intellij.codeInsight.daemon.impl.HighlightVisitor;
import com.intellij.codeInsight.daemon.impl.quickfix.AdjustFunctionContextFix;
import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.codeInsight.quickfix.UnresolvedReferenceQuickFixProvider;
import com.intellij.codeInspection.ex.GlobalInspectionContextBase;
import com.intellij.core.JavaPsiBundle;
import com.intellij.java.codeserver.highlighting.JavaErrorCollector;
import com.intellij.java.codeserver.highlighting.errors.JavaCompilationError;
import com.intellij.java.codeserver.highlighting.errors.JavaErrorHighlightType;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.lang.jvm.JvmModifier;
import com.intellij.lang.jvm.JvmModifiersOwner;
import com.intellij.lang.jvm.actions.JvmElementActionFactories;
import com.intellij.lang.jvm.actions.MemberRequestsKt;
import com.intellij.openapi.editor.DefaultLanguageHighlighterColors;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.colors.EditorColorsUtil;
@@ -30,17 +21,14 @@ import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.projectRoots.JavaVersionService;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.HtmlChunk;
import com.intellij.pom.java.JavaFeature;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.impl.IncompleteModelUtil;
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.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiTypesUtil;
@@ -60,7 +48,6 @@ import java.util.function.Consumer;
import java.util.function.Function;
import static com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds.*;
import static java.util.Objects.*;
// java highlighting: problems in java code like unresolved/incompatible symbols/methods etc.
public class HighlightVisitorImpl extends JavaElementVisitor implements HighlightVisitor {
@@ -605,160 +592,16 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
PsiType functionalInterfaceType = expression.getFunctionalInterfaceType();
if (functionalInterfaceType != null && !PsiTypesUtil.allTypeParametersResolved(expression, functionalInterfaceType)) return;
JavaResolveResult result;
JavaResolveResult[] results;
try {
results = expression.multiResolve(true);
result = results.length == 1 ? results[0] : JavaResolveResult.EMPTY;
}
catch (IndexNotReadyException e) {
return;
}
JavaResolveResult[] results = expression.multiResolve(true);
JavaResolveResult result = results.length == 1 ? results[0] : JavaResolveResult.EMPTY;
PsiElement method = result.getElement();
if (method instanceof PsiJvmMember && !result.isAccessible()) {
String accessProblem = HighlightUtil.accessProblemDescription(expression, method, result);
HighlightInfo.Builder info =
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(accessProblem);
HighlightFixUtil.registerAccessQuickFixAction(HighlightUtil.asConsumer(info), (PsiJvmMember)method, expression, result.getCurrentFileResolveScope());
add(info);
}
if (!LambdaUtil.isValidLambdaContext(parent)) {
String description = JavaErrorBundle.message("method.reference.expression.is.not.expected");
add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(description));
}
if (!hasErrorResults()) {
PsiElement referenceNameElement = expression.getReferenceNameElement();
if (referenceNameElement instanceof PsiKeyword) {
if (!PsiMethodReferenceUtil.isValidQualifier(expression)) {
PsiElement qualifier = expression.getQualifier();
if (qualifier != null) {
boolean pending = qualifier instanceof PsiJavaCodeReferenceElement ref &&
IncompleteModelUtil.isIncompleteModel(expression) &&
IncompleteModelUtil.canBePendingReference(ref);
if (!pending) {
String description = JavaErrorBundle.message("cannot.find.class", qualifier.getText());
add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(qualifier).descriptionAndTooltip(description));
}
}
}
}
}
if (functionalInterfaceType != null) {
if (!hasErrorResults()) {
boolean isFunctional = LambdaUtil.isFunctionalType(functionalInterfaceType);
if (!isFunctional && !(IncompleteModelUtil.isIncompleteModel(expression) &&
IncompleteModelUtil.isUnresolvedClassType(functionalInterfaceType))) {
String description =
JavaErrorBundle.message("not.a.functional.interface", functionalInterfaceType.getPresentableText());
add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(description));
}
}
if (!hasErrorResults()) {
add(LambdaHighlightingUtil.checkFunctionalInterfaceTypeAccessible(myFile.getProject(), expression, functionalInterfaceType));
}
if (!hasErrorResults()) {
String errorMessage = PsiMethodReferenceHighlightingUtil.checkMethodReferenceContext(expression);
if (errorMessage != null) {
HighlightInfo.Builder info =
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(errorMessage);
if (method instanceof PsiMethod psiMethod &&
!psiMethod.isConstructor() &&
!psiMethod.hasModifierProperty(PsiModifier.ABSTRACT)) {
boolean shouldHave = !psiMethod.hasModifierProperty(PsiModifier.STATIC);
QuickFixAction.registerQuickFixActions(info, null, JvmElementActionFactories.createModifierActions(
(JvmModifiersOwner)method, MemberRequestsKt.modifierRequest(JvmModifier.STATIC, shouldHave)));
}
add(info);
}
}
}
if (!hasErrorResults()) {
PsiElement qualifier = expression.getQualifier();
if (qualifier instanceof PsiTypeElement typeElement) {
PsiType psiType = typeElement.getType();
String wildcardMessage = checkTypeArguments(typeElement, psiType);
if (wildcardMessage != null) {
add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(qualifier).descriptionAndTooltip(wildcardMessage));
}
}
}
if (!hasErrorResults()) {
add(PsiMethodReferenceHighlightingUtil.checkRawConstructorReference(expression));
}
if (!hasErrorResults()) {
boolean resolvedButNonApplicable = results.length == 1 && results[0] instanceof MethodCandidateInfo methodInfo &&
!methodInfo.isApplicable() &&
functionalInterfaceType != null;
if (results.length != 1 || resolvedButNonApplicable) {
String description = null;
if (results.length == 1) {
description = ((MethodCandidateInfo)results[0]).getInferenceErrorMessage();
}
if (expression.isConstructor()) {
PsiClass containingClass = PsiMethodReferenceUtil.getQualifierResolveResult(expression).getContainingClass();
if (containingClass != null &&
containingClass.isPhysical() &&
description == null) {
description = JavaErrorBundle.message("cannot.resolve.constructor", containingClass.getName());
}
}
else if (description == null) {
if (results.length > 1) {
if (IncompleteModelUtil.isIncompleteModel(expression) &&
IncompleteModelUtil.isUnresolvedClassType(functionalInterfaceType)) {
return;
}
String t1 = HighlightUtil.format(requireNonNull(results[0].getElement()));
String t2 = HighlightUtil.format(requireNonNull(results[1].getElement()));
description = JavaErrorBundle.message("ambiguous.reference", expression.getReferenceName(), t1, t2);
}
else {
if (IncompleteModelUtil.isIncompleteModel(expression) && IncompleteModelUtil.canBePendingReference(expression)) {
PsiElement referenceNameElement = expression.getReferenceNameElement();
if (referenceNameElement != null) {
add(HighlightUtil.getPendingReferenceHighlightInfo(referenceNameElement));
}
return;
}
if (!(resolvedButNonApplicable && HighlightMethodUtil.hasSurroundingInferenceError(expression))) {
description = JavaErrorBundle.message("cannot.resolve.method", expression.getReferenceName());
}
}
}
if (description != null) {
PsiElement referenceNameElement = ObjectUtils.notNull(expression.getReferenceNameElement(), expression);
HighlightInfoType type = results.length == 0 ? HighlightInfoType.WRONG_REF : HighlightInfoType.ERROR;
HighlightInfo.Builder highlightInfo =
HighlightInfo.newHighlightInfo(type).descriptionAndTooltip(description).range(referenceNameElement);
TextRange fixRange = HighlightMethodUtil.getFixRange(referenceNameElement);
IntentionAction action = QuickFixFactory.getInstance().createCreateMethodFromUsageFix(expression);
highlightInfo.registerFix(action, null, null, fixRange, null);
add(highlightInfo);
}
}
}
if (!hasErrorResults()) {
String badReturnTypeMessage = PsiMethodReferenceUtil.checkReturnType(expression, result, functionalInterfaceType);
if (badReturnTypeMessage != null) {
HighlightInfo.Builder info =
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(badReturnTypeMessage);
IntentionAction action = AdjustFunctionContextFix.createFix(expression);
if (action != null) {
info.registerFix(action, null, null, null, null);
}
add(info);
}
}
if (!hasErrorResults() && method instanceof PsiModifierListOwner) {
PreviewFeatureUtil.checkPreviewFeature(expression, myPreviewFeatureVisitor);
}
@@ -895,19 +738,4 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
private @Nullable HighlightInfo.Builder checkFeature(@NotNull PsiElement element, @NotNull JavaFeature feature) {
return HighlightUtil.checkFeature(element, feature, myLanguageLevel, myFile);
}
private static @NlsContexts.DetailedDescription String checkTypeArguments(PsiTypeElement qualifier, PsiType psiType) {
if (psiType instanceof PsiClassType) {
final PsiJavaCodeReferenceElement referenceElement = qualifier.getInnermostComponentReferenceElement();
if (referenceElement != null) {
PsiType[] typeParameters = referenceElement.getTypeParameters();
for (PsiType typeParameter : typeParameters) {
if (typeParameter instanceof PsiWildcardType) {
return JavaPsiBundle.message("error.message.wildcard.not.expected");
}
}
}
}
return null;
}
}

View File

@@ -104,7 +104,7 @@ final class JavaErrorFixProvider {
NEW_EXPRESSION_ANONYMOUS_IMPLEMENTS_INTERFACE_WITH_TYPE_ARGUMENTS,
CALL_DIRECT_ABSTRACT_METHOD_ACCESS, RECORD_SPECIAL_METHOD_TYPE_PARAMETERS,
RECORD_SPECIAL_METHOD_THROWS, ARRAY_TYPE_ARGUMENTS, ARRAY_EMPTY_DIAMOND,
IMPORT_LIST_EXTRA_SEMICOLON, ENUM_CONSTANT_MODIFIER)) {
IMPORT_LIST_EXTRA_SEMICOLON, ENUM_CONSTANT_MODIFIER, METHOD_REFERENCE_PARAMETERIZED_QUALIFIER)) {
fix(kind, genericRemover);
}
@@ -536,6 +536,13 @@ final class JavaErrorFixProvider {
}
return null;
});
fix(METHOD_REFERENCE_RETURN_TYPE_ERROR, error -> AdjustFunctionContextFix.createFix(error.psi()));
fix(METHOD_REFERENCE_UNRESOLVED_METHOD, error -> myFactory.createCreateMethodFromUsageFix(error.psi()));
fix(METHOD_REFERENCE_UNRESOLVED_CONSTRUCTOR, error -> myFactory.createCreateMethodFromUsageFix(error.psi()));
fix(METHOD_REFERENCE_INFERENCE_ERROR, error -> myFactory.createCreateMethodFromUsageFix(error.psi()));
fix(METHOD_REFERENCE_STATIC_METHOD_NON_STATIC_QUALIFIER, error -> removeModifierFix(error.context(), PsiModifier.STATIC));
fix(METHOD_REFERENCE_STATIC_METHOD_RECEIVER, error -> removeModifierFix(error.context(), PsiModifier.STATIC));
fix(METHOD_REFERENCE_NON_STATIC_METHOD_IN_STATIC_CONTEXT, error -> addModifierFix(error.context(), PsiModifier.STATIC));
}
private void createAccessFixes() {

View File

@@ -1,103 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.daemon.impl.analysis;
import com.intellij.codeInsight.daemon.JavaErrorBundle;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
import com.intellij.java.analysis.JavaAnalysisBundle;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.psi.*;
import com.intellij.psi.util.*;
import org.jetbrains.annotations.NotNull;
public final class PsiMethodReferenceHighlightingUtil {
static HighlightInfo.Builder checkRawConstructorReference(@NotNull PsiMethodReferenceExpression expression) {
if (expression.isConstructor()) {
PsiType[] typeParameters = expression.getTypeParameters();
if (typeParameters.length > 0) {
PsiElement qualifier = expression.getQualifier();
if (qualifier instanceof PsiReferenceExpression) {
PsiElement resolve = ((PsiReferenceExpression)qualifier).resolve();
if (resolve instanceof PsiClass && ((PsiClass)resolve).hasTypeParameters()) {
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression)
.descriptionAndTooltip(JavaAnalysisBundle.message("text.raw.ctor.reference.with.type.parameters"));
}
}
}
}
return null;
}
static @NlsContexts.DetailedDescription String checkMethodReferenceContext(@NotNull PsiMethodReferenceExpression methodRef) {
PsiElement resolve = methodRef.resolve();
if (resolve == null) return null;
return checkMethodReferenceContext(methodRef, resolve, methodRef.getFunctionalInterfaceType());
}
public static @NlsContexts.DetailedDescription String checkMethodReferenceContext(@NotNull PsiMethodReferenceExpression methodRef,
@NotNull PsiElement resolve,
PsiType functionalInterfaceType) {
PsiClass containingClass = resolve instanceof PsiMethod method ? method.getContainingClass() : (PsiClass)resolve;
boolean isStaticSelector = PsiMethodReferenceUtil.isStaticallyReferenced(methodRef);
PsiElement qualifier = methodRef.getQualifier();
boolean isMethodStatic = false;
boolean receiverReferenced = false;
boolean isConstructor = true;
if (resolve instanceof PsiMethod method) {
isMethodStatic = method.hasModifierProperty(PsiModifier.STATIC);
isConstructor = method.isConstructor();
PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
receiverReferenced = PsiMethodReferenceUtil.isResolvedBySecondSearch(methodRef,
interfaceMethod != null ? interfaceMethod.getSignature(LambdaUtil.getSubstitutor(interfaceMethod, resolveResult)) : null,
method.isVarArgs(),
isMethodStatic,
method.getParameterList().getParametersCount());
if (method.hasModifierProperty(PsiModifier.ABSTRACT) && qualifier instanceof PsiSuperExpression) {
return JavaErrorBundle.message("abstract.method.0.cannot.be.accessed.directly.method.reference.context", method.getName());
}
}
if (!receiverReferenced) {
if (isStaticSelector && !isMethodStatic && !isConstructor) {
if (functionalInterfaceType instanceof PsiClassType classType && classType.hasParameters()) {
// Prefer surrounding error, as it could be more descriptive
if (HighlightMethodUtil.hasSurroundingInferenceError(methodRef)) return null;
}
return JavaErrorBundle.message("non.static.method.cannot.be.referenced.from.a.static.context.method.reference.context");
}
if (!isStaticSelector && isMethodStatic) {
return JavaErrorBundle.message("static.method.referenced.through.non.static.qualifier.method.reference.context");
}
}
else if (isStaticSelector && isMethodStatic) {
return JavaErrorBundle.message("static.method.referenced.through.receiver.method.reference.context");
}
if (isMethodStatic && isStaticSelector && qualifier instanceof PsiTypeElement) {
PsiJavaCodeReferenceElement referenceElement = PsiTreeUtil.getChildOfType(qualifier, PsiJavaCodeReferenceElement.class);
if (referenceElement != null) {
PsiReferenceParameterList parameterList = referenceElement.getParameterList();
if (parameterList != null && parameterList.getTypeArguments().length > 0) {
return JavaErrorBundle.message("parameterized.qualifier.on.static.method.reference.context");
}
}
}
if (isConstructor) {
if (containingClass != null && PsiUtil.isInnerClass(containingClass) && containingClass.isPhysical()) {
PsiClass outerClass = containingClass.getContainingClass();
if (outerClass != null && !InheritanceUtil.hasEnclosingInstanceInScope(outerClass, methodRef, true, false)) {
return JavaErrorBundle.message("an.enclosing.instance.of.type.not.in.scope.method.reference.context",
PsiFormatUtil.formatClass(outerClass, PsiFormatUtilBase.SHOW_NAME));
}
}
}
return null;
}
}

View File

@@ -1,9 +1,9 @@
// 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.completion.scope;
import com.intellij.codeInsight.daemon.impl.analysis.PsiMethodReferenceHighlightingUtil;
import com.intellij.codeInspection.SuppressManager;
import com.intellij.codeInspection.accessStaticViaInstance.AccessStaticViaInstanceBase;
import com.intellij.java.codeserver.highlighting.JavaErrorCollector;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Key;
@@ -235,8 +235,8 @@ public final class JavaCompletionProcessor implements PsiScopeProcessor, Element
return LambdaUtil.performWithTargetType(referenceExpression, expectedType, () -> {
JavaResolveResult result = referenceExpression.advancedResolve(false);
return method.getManager().areElementsEquivalent(method, result.getElement()) &&
PsiMethodReferenceUtil.isReturnTypeCompatible(referenceExpression, result, expectedType) &&
PsiMethodReferenceHighlightingUtil.checkMethodReferenceContext(referenceExpression, method, expectedType) == null;
PsiMethodReferenceUtil.isReturnTypeCompatible(referenceExpression, result, expectedType) &&
JavaErrorCollector.findSingleError(referenceExpression) == null;
});
}

View File

@@ -81,7 +81,7 @@ public final class PsiMethodReferenceUtil {
public static boolean isReturnTypeCompatible(PsiMethodReferenceExpression expression,
JavaResolveResult result,
PsiType functionalInterfaceType) {
@Nullable PsiType functionalInterfaceType) {
return isReturnTypeCompatible(expression, result, functionalInterfaceType, null);
}
@@ -146,7 +146,7 @@ public final class PsiMethodReferenceUtil {
private static boolean isReturnTypeCompatible(PsiMethodReferenceExpression expression,
JavaResolveResult result,
PsiType functionalInterfaceType,
@Nullable PsiType functionalInterfaceType,
Ref<@Nls String> errorMessage) {
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
@@ -302,7 +302,8 @@ public final class PsiMethodReferenceUtil {
return type;
}
public static @NlsContexts.DetailedDescription String checkReturnType(PsiMethodReferenceExpression expression, JavaResolveResult result, PsiType functionalInterfaceType) {
public static @NlsContexts.DetailedDescription String checkReturnType(PsiMethodReferenceExpression expression, JavaResolveResult result,
@Nullable PsiType functionalInterfaceType) {
final Ref<@Nls String> errorMessage = Ref.create();
if (!isReturnTypeCompatible(expression, result, functionalInterfaceType, errorMessage)) {
return errorMessage.get();

View File

@@ -28,7 +28,7 @@ class AlienTest {
IInt i3 = MyTest::<error descr="Cannot resolve method 'bar'">bar</error>;
IIntInt i4 = MyTest::<error descr="Reference to 'bar' is ambiguous, both 'bar(Integer, Number)' and 'bar(Number, Integer)' match">bar</error>;
IInt i5 = <error descr="Non-static method cannot be referenced from a static context">MyTest::baz</error>;
IInt i6 = <error descr="'foo(int)' is not public in 'MyTest.Foo'. Cannot be accessed from outside package">MyTest.foo::foo</error>;
IInt i6 = MyTest.foo::<error descr="'foo(int)' is not public in 'MyTest.Foo'. Cannot be accessed from outside package">foo</error>;
IInt i7 = MyTest.<error descr="'MyTest.Foo' has private access in 'MyTest'">Foo</error>::foo;
}
}

View File

@@ -38,5 +38,5 @@ class Test2 {
void foo(Integer i) {}
Object o = <error descr="Object is not a functional interface">Test2::foo</error>;
Object o = <error descr="java.lang.Object is not a functional interface">Test2::foo</error>;
}

View File

@@ -4,7 +4,7 @@ class Test<X> {
}
void test() {
I i1 = <error descr="Parameterized qualifier on static method reference">Test<String>::foo</error>;
I i1 = Test<error descr="Parameterized qualifier on static method reference"><String></error>::foo;
I i2 = Test::foo;
}

View File

@@ -4,7 +4,7 @@ import java.util.function.Function;
class Main {
{
List<Optional<Function<String, String>>> list = asList(of(<error descr="Object is not a functional interface">Main::identity</error>));
List<Optional<Function<String, String>>> list = asList(of(<error descr="java.lang.Object is not a functional interface">Main::identity</error>));
}
static <T> List<T> asList(T a) { return null;}

View File

@@ -1,7 +1,7 @@
class Test {
{
asList(<error descr="Target type of a lambda conversion must be an interface">o -> {}</error>, 1, 2, 3);
asList(<error descr="Integer is not a functional interface">Test::foo</error>, 1, 2, 3);
asList(<error descr="java.lang.Integer is not a functional interface">Test::foo</error>, 1, 2, 3);
}
void foo() {}