From 02fc8fea51738bd2c6c5a26d9e7b9b41ebd597aa Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Wed, 2 Apr 2014 20:34:12 +0200 Subject: [PATCH] method refs refactoring --- .../intellij/psi/PsiMethodReferenceUtil.java | 21 + .../tree/java/MethodReferenceResolver.java | 404 ++++++++++++++++++ .../PsiMethodReferenceExpressionImpl.java | 401 +---------------- 3 files changed, 427 insertions(+), 399 deletions(-) create mode 100644 java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/MethodReferenceResolver.java diff --git a/java/java-psi-api/src/com/intellij/psi/PsiMethodReferenceUtil.java b/java/java-psi-api/src/com/intellij/psi/PsiMethodReferenceUtil.java index 133d170a2b36..911eb048f217 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiMethodReferenceUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiMethodReferenceUtil.java @@ -65,6 +65,27 @@ public class PsiMethodReferenceUtil { return null; } + public static boolean isCorrectAssignment(PsiType[] signatureParameterTypes2, + PsiType[] parameterTypes, + PsiSubstitutor substitutor, + boolean varargs, + int offset) { + final int min = Math.min(signatureParameterTypes2.length, parameterTypes.length - offset); + for (int i = 0; i < min; i++) { + final PsiType type1 = substitutor.substitute(parameterTypes[i + offset]); + final PsiType type2 = signatureParameterTypes2[i]; + if (varargs && i == signatureParameterTypes2.length - 1) { + if (!TypeConversionUtil.isAssignable(type2, type1) && !TypeConversionUtil.isAssignable(((PsiArrayType)type2).getComponentType(), type1)) { + return false; + } + } + else if (!TypeConversionUtil.isAssignable(type2, type1)) { + return false; + } + } + return true; + } + public static class QualifierResolveResult { private final PsiClass myContainingClass; private final PsiSubstitutor mySubstitutor; diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/MethodReferenceResolver.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/MethodReferenceResolver.java new file mode 100644 index 000000000000..cf25710008bc --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/MethodReferenceResolver.java @@ -0,0 +1,404 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.impl.source.tree.java; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.psi.*; +import com.intellij.psi.impl.source.resolve.ParameterTypeInferencePolicy; +import com.intellij.psi.impl.source.resolve.ResolveCache; +import com.intellij.psi.impl.source.resolve.graphInference.FunctionalInterfaceParameterizationUtil; +import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession; +import com.intellij.psi.impl.source.resolve.graphInference.constraints.TypeCompatibilityConstraint; +import com.intellij.psi.infos.CandidateInfo; +import com.intellij.psi.infos.ClassCandidateInfo; +import com.intellij.psi.infos.MethodCandidateInfo; +import com.intellij.psi.scope.JavaScopeProcessorEvent; +import com.intellij.psi.scope.PsiConflictResolver; +import com.intellij.psi.scope.conflictResolvers.JavaMethodsConflictResolver; +import com.intellij.psi.scope.processor.MethodCandidatesProcessor; +import com.intellij.psi.util.MethodSignature; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.TypeConversionUtil; +import com.intellij.util.SmartList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +class MethodReferenceResolver implements ResolveCache.PolyVariantResolver { + private static final Logger LOG = Logger.getInstance("#" + MethodReferenceResolver.class.getName()); + + @NotNull + @Override + public ResolveResult[] resolve(@NotNull final PsiMethodReferenceExpression reference, boolean incompleteCode) { + final PsiMethodReferenceUtil.QualifierResolveResult qualifierResolveResult = PsiMethodReferenceUtil.getQualifierResolveResult( + reference); + + final PsiClass containingClass = qualifierResolveResult.getContainingClass(); + PsiSubstitutor substitutor = qualifierResolveResult.getSubstitutor(); + + if (containingClass != null) { + final PsiElement element = reference.getReferenceNameElement(); + final boolean isConstructor = reference.isConstructor(); + if (element instanceof PsiIdentifier || isConstructor) { + if (isConstructor && (containingClass.isEnum() || containingClass.hasModifierProperty(PsiModifier.ABSTRACT))) { + return JavaResolveResult.EMPTY_ARRAY; + } + final PsiType functionalInterfaceType = getInterfaceType(reference); + final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType); + final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult); + final PsiSubstitutor functionalInterfaceSubstitutor = interfaceMethod != null ? LambdaUtil.getSubstitutor(interfaceMethod, resolveResult) : null; + final MethodSignature signature = interfaceMethod != null ? interfaceMethod.getSignature(functionalInterfaceSubstitutor) : null; + final PsiType interfaceMethodReturnType = LambdaUtil.getFunctionalInterfaceReturnType(functionalInterfaceType); + if (isConstructor && containingClass.getConstructors().length == 0) { + if (interfaceMethod != null) { + final PsiClassType returnType = composeReturnType(containingClass, substitutor); + final InferenceSession session = new InferenceSession(containingClass.getTypeParameters(), substitutor, reference.getManager(), null); + if (!(session.isProperType(returnType) && session.isProperType(interfaceMethodReturnType))) { + session.registerConstraints(returnType, interfaceMethodReturnType); + substitutor = session.infer(); + } + } + ClassCandidateInfo candidateInfo = null; + final boolean isArray = containingClass == JavaPsiFacade.getElementFactory(reference.getProject()).getArrayClass(PsiUtil.getLanguageLevel(containingClass)); + if (signature == null || + !isArray && (containingClass.getContainingClass() == null || !isLocatedInStaticContext(containingClass, reference)) && signature.getParameterTypes().length == 0 || + isArray && arrayCreationSignature(signature)) { + candidateInfo = new ClassCandidateInfo(containingClass, substitutor); + } + return candidateInfo == null ? JavaResolveResult.EMPTY_ARRAY : new JavaResolveResult[]{candidateInfo}; + } + + final PsiConflictResolver conflictResolver = createResolver(reference, qualifierResolveResult, interfaceMethod, signature); + final MethodCandidatesProcessor processor = + new MethodCandidatesProcessor(reference, reference.getContainingFile(), new PsiConflictResolver[] {conflictResolver}, new SmartList()) { + @Override + protected boolean acceptVarargs() { + return true; + } + + @Override + protected MethodCandidateInfo createCandidateInfo(@NotNull final PsiMethod method, + @NotNull final PsiSubstitutor substitutor, + final boolean staticProblem, + final boolean accessible, + final boolean varargs) { + final PsiExpressionList argumentList = getArgumentList(); + final PsiType[] typeParameters = reference.getTypeParameters(); + return new MethodCandidateInfo(method, substitutor, !accessible, staticProblem, argumentList, myCurrentFileContext, + argumentList != null ? argumentList.getExpressionTypes() : null, typeParameters.length > 0 ? typeParameters : null, + getLanguageLevel()) { + @Override + public boolean isVarargs() { + return varargs; + } + + @NotNull + @Override + public PsiSubstitutor inferTypeArguments(@NotNull ParameterTypeInferencePolicy policy, boolean includeReturnConstraint) { + return inferTypeArguments(varargs); + } + + public PsiSubstitutor inferTypeArguments(boolean varargs) { + if (interfaceMethod == null) return substitutor; + final PsiSubstitutor qualifierResultSubstitutor = qualifierResolveResult.getSubstitutor(); + final InferenceSession session = new InferenceSession(method.getTypeParameters(), substitutor, reference.getManager(), reference); + + //lift parameters from outer call + final CurrentCandidateProperties methodSubstitutorPair = MethodCandidateInfo.getCurrentMethod(reference.getParent()); + if (methodSubstitutorPair != null) { + session.initBounds(methodSubstitutorPair.getMethod().getTypeParameters()); + } + + final PsiParameter[] functionalMethodParameters = interfaceMethod.getParameterList().getParameters(); + final PsiParameter[] parameters = method.getParameterList().getParameters(); + final boolean isStatic = method.hasModifierProperty(PsiModifier.STATIC); + if (parameters.length == functionalMethodParameters.length && !varargs || isStatic && varargs) {//static methods + + if (method.isConstructor() && PsiUtil.isRawSubstitutor(containingClass, qualifierResultSubstitutor)) { + session.initBounds(containingClass.getTypeParameters()); + } + + for (int i = 0; i < functionalMethodParameters.length; i++) { + final PsiType pType = signature.getParameterTypes()[i]; + session.addConstraint(new TypeCompatibilityConstraint(getParameterType(parameters, i, varargs), pType)); + } + } + else if (parameters.length + 1 == functionalMethodParameters.length && !varargs || !isStatic && varargs && functionalMethodParameters.length > 0) { //instance methods + final PsiClass aClass = qualifierResolveResult.getContainingClass(); + session.initBounds(aClass.getTypeParameters()); + + final PsiType pType = signature.getParameterTypes()[0]; + + PsiSubstitutor psiSubstitutor = qualifierResultSubstitutor; + // 15.28.1 If the ReferenceType is a raw type, and there exists a parameterization of this type, T, that is a supertype of P1, + // the type to search is the result of capture conversion (5.1.10) applied to T; + // otherwise, the type to search is the same as the type of the first search. Again, the type arguments, if any, are given by the method reference. + if (PsiUtil.isRawSubstitutor(containingClass, qualifierResultSubstitutor)) { + final PsiClassType.ClassResolveResult pResult = PsiUtil.resolveGenericsClassInType(pType); + final PsiClass pClass = pResult.getElement(); + final PsiSubstitutor receiverSubstitutor = pClass != null ? TypeConversionUtil + .getClassSubstitutor(containingClass, pClass, pResult.getSubstitutor()) : null; + if (receiverSubstitutor != null) { + if (!method.hasTypeParameters() && signature.getParameterTypes().length == 1) return receiverSubstitutor; + psiSubstitutor = receiverSubstitutor; + } + } + + final PsiType qType = JavaPsiFacade.getElementFactory(reference.getProject()).createType(containingClass, psiSubstitutor); + + session.addConstraint(new TypeCompatibilityConstraint(qType, pType)); + + for (int i = 0; i < signature.getParameterTypes().length - 1; i++) { + final PsiType interfaceParamType = signature.getParameterTypes()[i + 1]; + session.addConstraint(new TypeCompatibilityConstraint(getParameterType(parameters, i, varargs), interfaceParamType)); + } + } + + if (!session.repeatInferencePhases(false)) { + return substitutor; + } + + if (interfaceMethodReturnType != PsiType.VOID) { + final PsiType returnType = method.isConstructor() ? composeReturnType(containingClass, substitutor) : method.getReturnType(); + if (returnType != null) { + session.registerConstraints(returnType, interfaceMethodReturnType); + } + } + return session.infer(parameters, null, null); + } + + private PsiType getParameterType(PsiParameter[] parameters, int i, boolean varargs) { + if (varargs && i >= parameters.length - 1) { + final PsiType type = parameters[parameters.length - 1].getType(); + LOG.assertTrue(type instanceof PsiEllipsisType); + return ((PsiEllipsisType)type).getComponentType(); + } + return parameters[i].getType(); + } + }; + } + }; + processor.setIsConstructor(isConstructor); + processor.setName(isConstructor ? containingClass.getName() : element.getText()); + final PsiExpression expression = reference.getQualifierExpression(); + if (expression == null || !(expression.getType() instanceof PsiArrayType)) { + processor.setAccessClass(containingClass); + } + + if (qualifierResolveResult.isReferenceTypeQualified() && isLocatedInStaticContext(containingClass, reference)) { + processor.handleEvent(JavaScopeProcessorEvent.START_STATIC, null); + } + ResolveState state = ResolveState.initial().put(PsiSubstitutor.KEY, substitutor); + containingClass.processDeclarations(processor, state, reference, reference); + return processor.getResult(); + } + } + return JavaResolveResult.EMPTY_ARRAY; + } + + private static boolean isLocatedInStaticContext(PsiClass containingClass, PsiMethodReferenceExpression reference) { + final PsiClass gContainingClass = containingClass.getContainingClass(); + if (gContainingClass == null || !containingClass.hasModifierProperty(PsiModifier.STATIC)) { + PsiClass aClass = null; + if (PsiTreeUtil.isAncestor(gContainingClass != null ? gContainingClass : containingClass, reference, false)) { + aClass = gContainingClass != null ? gContainingClass : containingClass; + } + if (PsiUtil.getEnclosingStaticElement(reference, aClass) != null) { + return true; + } + } + return false; + } + + protected PsiType getInterfaceType(PsiMethodReferenceExpression reference) { + PsiType functionalInterfaceType = null; + final Map map = PsiMethodReferenceUtil.ourRefs.get(); + if (map != null) { + functionalInterfaceType = FunctionalInterfaceParameterizationUtil.getGroundTargetType(map.get(reference)); + } + + if (functionalInterfaceType == null) { + functionalInterfaceType = reference.getFunctionalInterfaceType(); + } + + return functionalInterfaceType; + } + + protected PsiConflictResolver createResolver(PsiMethodReferenceExpression referenceExpression, + PsiMethodReferenceUtil.QualifierResolveResult qualifierResolveResult, + PsiMethod interfaceMethod, + MethodSignature signature) { + return new MethodReferenceConflictResolver(referenceExpression, qualifierResolveResult, signature, + interfaceMethod != null && interfaceMethod.isVarArgs()); + } + + private static PsiClassType composeReturnType(PsiClass containingClass, PsiSubstitutor substitutor) { + final boolean isRawSubst = PsiUtil.isRawSubstitutor(containingClass, substitutor); + return JavaPsiFacade.getElementFactory(containingClass.getProject()) + .createType(containingClass, isRawSubst ? PsiSubstitutor.EMPTY : substitutor); + } + + private static class MethodReferenceConflictResolver extends JavaMethodsConflictResolver { + private final MethodSignature mySignature; + private PsiMethodReferenceExpression myReferenceExpression; + private final PsiMethodReferenceUtil.QualifierResolveResult myQualifierResolveResult; + private final boolean myFunctionalMethodVarArgs; + + private MethodReferenceConflictResolver(PsiMethodReferenceExpression referenceExpression, + PsiMethodReferenceUtil.QualifierResolveResult qualifierResolveResult, + @Nullable MethodSignature signature, boolean varArgs) { + super(referenceExpression, signature != null ? signature.getParameterTypes() : PsiType.EMPTY_ARRAY, PsiUtil.getLanguageLevel(referenceExpression)); + myReferenceExpression = referenceExpression; + myQualifierResolveResult = qualifierResolveResult; + myFunctionalMethodVarArgs = varArgs; + mySignature = signature; + } + + @Override + protected int getPertinentApplicabilityLevel(MethodCandidateInfo conflict) { + return conflict.isVarargs() ? MethodCandidateInfo.ApplicabilityLevel.VARARGS : MethodCandidateInfo.ApplicabilityLevel.FIXED_ARITY; + } + + @Nullable + @Override + public CandidateInfo resolveConflict(@NotNull List conflicts) { + if (mySignature == null) return null; + + checkSameSignatures(conflicts); + checkAccessStaticLevels(conflicts, true); + + final PsiType[] parameterTypes = mySignature.getParameterTypes(); + boolean hasReceiver = PsiMethodReferenceUtil.hasReceiver(parameterTypes, myQualifierResolveResult, myReferenceExpression); + + final List firstCandidates = new ArrayList(); + final List secondCandidates = new ArrayList(); + + for (CandidateInfo conflict : conflicts) { + if (!(conflict instanceof MethodCandidateInfo)) continue; + final PsiMethod psiMethod = ((MethodCandidateInfo)conflict).getElement(); + + final PsiSubstitutor substitutor = conflict.getSubstitutor(); + final PsiType[] signatureParameterTypes2 = psiMethod.getSignature(substitutor).getParameterTypes(); + + final boolean varargs = ((MethodCandidateInfo)conflict).isVarargs(); + if (varargs && (!psiMethod.isVarArgs() || myFunctionalMethodVarArgs)) continue; + + if ((varargs || parameterTypes.length == signatureParameterTypes2.length) && + PsiMethodReferenceUtil.isCorrectAssignment(signatureParameterTypes2, parameterTypes, substitutor, varargs, 0)) { + firstCandidates.add(conflict); + } + + if (hasReceiver && + (varargs || parameterTypes.length == signatureParameterTypes2.length + 1) && + PsiMethodReferenceUtil.isCorrectAssignment(signatureParameterTypes2, parameterTypes, substitutor, varargs, 1)) { + secondCandidates.add(conflict); + } + } + + if (myQualifierResolveResult.isReferenceTypeQualified() && myReferenceExpression.getReferenceNameElement() instanceof PsiIdentifier) { + //If the first search produces a static method, and no non-static method is applicable for the second search, then the result of the first search is the compile-time declaration. + CandidateInfo candidateInfo = filterStaticCorrectCandidates(firstCandidates, secondCandidates, true); + if (candidateInfo != null) { + return candidateInfo; + } + + //If the second search produces a non-static method, and no static method is applicable for the first search, then the result of the second search is the compile-time declaration. + candidateInfo = filterStaticCorrectCandidates(secondCandidates, firstCandidates, false); + if (candidateInfo != null) { + return candidateInfo; + } + } + + if (resolveConflicts(firstCandidates, secondCandidates, MethodCandidateInfo.ApplicabilityLevel.FIXED_ARITY)) { + return !firstCandidates.isEmpty() ? firstCandidates.get(0) : secondCandidates.get(0); + } + + if (resolveConflicts(firstCandidates, secondCandidates, MethodCandidateInfo.ApplicabilityLevel.VARARGS)) { + return !firstCandidates.isEmpty() ? firstCandidates.get(0) : secondCandidates.get(0); + } + + conflicts.clear(); + firstCandidates.addAll(secondCandidates); + conflicts.addAll(firstCandidates); + return null; + } + + private boolean resolveConflicts(List firstCandidates, List secondCandidates, int applicabilityLevel) { + + checkApplicability(firstCandidates); + checkSpecifics(firstCandidates, applicabilityLevel, myLanguageLevel); + + checkApplicability(secondCandidates); + checkSpecifics(secondCandidates, applicabilityLevel, myLanguageLevel); + + return firstCandidates.size() + secondCandidates.size() == 1; + } + + @Override + protected boolean nonComparable(CandidateInfo method, CandidateInfo conflict) { + if (method == conflict) return true; + PsiElement psiElement = method.getElement(); + PsiElement conflictElement = conflict.getElement(); + if (psiElement instanceof PsiMethod && conflictElement instanceof PsiMethod) { + if (((PsiMethod)psiElement).getParameterList().getParametersCount() != + ((PsiMethod)conflictElement).getParameterList().getParametersCount()) { + return true; + } + } + return false; + } + + /** + * 15.13.1 + */ + private static CandidateInfo filterStaticCorrectCandidates(List firstCandidates, + List secondCandidates, + boolean shouldBeStatic) { + if (firstCandidates.size() == 1) { + final CandidateInfo candidateInfo = firstCandidates.get(0); + final PsiElement element = candidateInfo.getElement(); + if (element instanceof PsiMethod) { + final boolean isStatic = ((PsiMethod)element).hasModifierProperty(PsiModifier.STATIC); + if (shouldBeStatic && isStatic || !shouldBeStatic && !isStatic) { + for (CandidateInfo secondCandidate : secondCandidates) { + final PsiElement psiElement = secondCandidate.getElement(); + if (psiElement instanceof PsiMethod) { + final boolean oppositeStatic = ((PsiMethod)psiElement).hasModifierProperty(PsiModifier.STATIC); + if (shouldBeStatic && !oppositeStatic || !shouldBeStatic && oppositeStatic) { + return null; + } + } + } + return candidateInfo; + } + } + } + return null; + } + } + + private static boolean arrayCreationSignature(MethodSignature signature) { + final PsiType[] parameterTypes = signature.getParameterTypes(); + if (parameterTypes.length == 1 && parameterTypes[0] != null && TypeConversionUtil.isAssignable(PsiType.INT, parameterTypes[0])) { + return true; + } + return false; + } +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java index 22b0c520f47b..dd0552055e5c 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java @@ -21,38 +21,27 @@ import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; import com.intellij.psi.impl.PsiManagerEx; -import com.intellij.psi.impl.source.resolve.ParameterTypeInferencePolicy; import com.intellij.psi.impl.source.resolve.ResolveCache; import com.intellij.psi.impl.source.resolve.graphInference.FunctionalInterfaceParameterizationUtil; import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession; -import com.intellij.psi.impl.source.resolve.graphInference.constraints.TypeCompatibilityConstraint; import com.intellij.psi.impl.source.tree.ChildRole; import com.intellij.psi.impl.source.tree.FileElement; import com.intellij.psi.impl.source.tree.JavaElementType; import com.intellij.psi.impl.source.tree.SharedImplUtil; -import com.intellij.psi.infos.CandidateInfo; -import com.intellij.psi.infos.ClassCandidateInfo; import com.intellij.psi.infos.MethodCandidateInfo; import com.intellij.psi.scope.ElementClassFilter; -import com.intellij.psi.scope.JavaScopeProcessorEvent; import com.intellij.psi.scope.PsiConflictResolver; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.scope.conflictResolvers.DuplicateConflictResolver; -import com.intellij.psi.scope.conflictResolvers.JavaMethodsConflictResolver; import com.intellij.psi.scope.processor.FilterScopeProcessor; -import com.intellij.psi.scope.processor.MethodCandidatesProcessor; import com.intellij.psi.scope.util.PsiScopesUtil; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.*; -import com.intellij.util.Function; import com.intellij.util.IncorrectOperationException; -import com.intellij.util.SmartList; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase implements PsiMethodReferenceExpression { @@ -62,25 +51,6 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase super(JavaElementType.METHOD_REF_EXPRESSION); } - private static boolean arrayCreationSignature(MethodSignature signature) { - if (arrayCompatibleSignature(signature.getParameterTypes(), new Function() { - @Override - public PsiType fun(PsiType[] types) { - return types[0]; - } - })) { - return true; - } - return false; - } - - public static boolean arrayCompatibleSignature(T[] paramTypes, Function fun) { - if (paramTypes.length == 1) { - final PsiType paramType = fun.fun(paramTypes); - if (paramType != null && TypeConversionUtil.isAssignable(PsiType.INT, paramType)) return true; - } - return false; - } @Override public PsiTypeElement getQualifierType() { @@ -106,7 +76,8 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase final MethodReferenceResolver resolver = new MethodReferenceResolver() { @Override - protected PsiConflictResolver createResolver(PsiMethodReferenceUtil.QualifierResolveResult qualifierResolveResult, + protected PsiConflictResolver createResolver(PsiMethodReferenceExpression referenceExpression, + PsiMethodReferenceUtil.QualifierResolveResult qualifierResolveResult, PsiMethod interfaceMethod, MethodSignature signature) { return DuplicateConflictResolver.INSTANCE; @@ -390,374 +361,6 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase return "PsiMethodReferenceExpression:" + getText(); } - private boolean isLocatedInStaticContext(PsiClass containingClass) { - final PsiClass gContainingClass = containingClass.getContainingClass(); - if (gContainingClass == null || !containingClass.hasModifierProperty(PsiModifier.STATIC)) { - PsiClass aClass = null; - if (PsiTreeUtil.isAncestor(gContainingClass != null ? gContainingClass : containingClass, this, false)) { - aClass = gContainingClass != null ? gContainingClass : containingClass; - } - if (PsiUtil.getEnclosingStaticElement(this, aClass) != null) { - return true; - } - } - return false; - } - - private class MethodReferenceResolver implements ResolveCache.PolyVariantResolver { - @NotNull - @Override - public ResolveResult[] resolve(@NotNull final PsiMethodReferenceExpression reference, boolean incompleteCode) { - final PsiMethodReferenceUtil.QualifierResolveResult qualifierResolveResult = PsiMethodReferenceUtil.getQualifierResolveResult(reference); - - final PsiClass containingClass = qualifierResolveResult.getContainingClass(); - PsiSubstitutor substitutor = qualifierResolveResult.getSubstitutor(); - - if (containingClass != null) { - final PsiElement element = getReferenceNameElement(); - final boolean isConstructor = isConstructor(); - if (element instanceof PsiIdentifier || isConstructor) { - if (isConstructor && (containingClass.isEnum() || containingClass.hasModifierProperty(PsiModifier.ABSTRACT))) { - return JavaResolveResult.EMPTY_ARRAY; - } - final PsiType functionalInterfaceType = getInterfaceType(reference); - final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType); - final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult); - final PsiSubstitutor functionalInterfaceSubstitutor = interfaceMethod != null ? LambdaUtil.getSubstitutor(interfaceMethod, resolveResult) : null; - final MethodSignature signature = interfaceMethod != null ? interfaceMethod.getSignature(functionalInterfaceSubstitutor) : null; - final PsiType interfaceMethodReturnType = LambdaUtil.getFunctionalInterfaceReturnType(functionalInterfaceType); - if (isConstructor && containingClass.getConstructors().length == 0) { - if (interfaceMethod != null) { - final PsiClassType returnType = composeReturnType(containingClass, substitutor); - final InferenceSession session = new InferenceSession(containingClass.getTypeParameters(), substitutor, getManager(), null); - if (!(session.isProperType(returnType) && session.isProperType(interfaceMethodReturnType))) { - session.registerConstraints(returnType, interfaceMethodReturnType); - substitutor = session.infer(); - } - } - ClassCandidateInfo candidateInfo = null; - final boolean isArray = containingClass == JavaPsiFacade.getElementFactory(getProject()).getArrayClass(PsiUtil.getLanguageLevel(containingClass)); - if (signature == null || - !isArray && (containingClass.getContainingClass() == null || !isLocatedInStaticContext(containingClass)) && signature.getParameterTypes().length == 0 || - isArray && arrayCreationSignature(signature)) { - candidateInfo = new ClassCandidateInfo(containingClass, substitutor); - } - return candidateInfo == null ? JavaResolveResult.EMPTY_ARRAY : new JavaResolveResult[]{candidateInfo}; - } - - final PsiConflictResolver conflictResolver = createResolver(qualifierResolveResult, interfaceMethod, signature); - final MethodCandidatesProcessor processor = - new MethodCandidatesProcessor(reference, getContainingFile(), new PsiConflictResolver[] {conflictResolver}, new SmartList()) { - @Override - protected boolean acceptVarargs() { - return true; - } - - @Override - protected MethodCandidateInfo createCandidateInfo(@NotNull final PsiMethod method, - @NotNull final PsiSubstitutor substitutor, - final boolean staticProblem, - final boolean accessible, - final boolean varargs) { - final PsiExpressionList argumentList = getArgumentList(); - final PsiType[] typeParameters = reference.getTypeParameters(); - return new MethodCandidateInfo(method, substitutor, !accessible, staticProblem, argumentList, myCurrentFileContext, - argumentList != null ? argumentList.getExpressionTypes() : null, typeParameters.length > 0 ? typeParameters : null, - getLanguageLevel()) { - @Override - public boolean isVarargs() { - return varargs; - } - - @NotNull - @Override - public PsiSubstitutor inferTypeArguments(@NotNull ParameterTypeInferencePolicy policy, boolean includeReturnConstraint) { - return inferTypeArguments(varargs); - } - - public PsiSubstitutor inferTypeArguments(boolean varargs) { - if (interfaceMethod == null) return substitutor; - final PsiSubstitutor qualifierResultSubstitutor = qualifierResolveResult.getSubstitutor(); - final InferenceSession session = new InferenceSession(method.getTypeParameters(), substitutor, getManager(), reference); - - //lift parameters from outer call - final CurrentCandidateProperties methodSubstitutorPair = MethodCandidateInfo.getCurrentMethod(reference.getParent()); - if (methodSubstitutorPair != null) { - session.initBounds(methodSubstitutorPair.getMethod().getTypeParameters()); - } - - final PsiParameter[] functionalMethodParameters = interfaceMethod.getParameterList().getParameters(); - final PsiParameter[] parameters = method.getParameterList().getParameters(); - final boolean isStatic = method.hasModifierProperty(PsiModifier.STATIC); - if (parameters.length == functionalMethodParameters.length && !varargs || isStatic && varargs) {//static methods - - if (method.isConstructor() && PsiUtil.isRawSubstitutor(containingClass, qualifierResultSubstitutor)) { - session.initBounds(containingClass.getTypeParameters()); - } - - for (int i = 0; i < functionalMethodParameters.length; i++) { - final PsiType pType = signature.getParameterTypes()[i]; - session.addConstraint(new TypeCompatibilityConstraint(getParameterType(parameters, i, varargs), pType)); - } - } - else if (parameters.length + 1 == functionalMethodParameters.length && !varargs || !isStatic && varargs && functionalMethodParameters.length > 0) { //instance methods - final PsiClass aClass = qualifierResolveResult.getContainingClass(); - session.initBounds(aClass.getTypeParameters()); - - final PsiType pType = signature.getParameterTypes()[0]; - - PsiSubstitutor psiSubstitutor = qualifierResultSubstitutor; - // 15.28.1 If the ReferenceType is a raw type, and there exists a parameterization of this type, T, that is a supertype of P1, - // the type to search is the result of capture conversion (5.1.10) applied to T; - // otherwise, the type to search is the same as the type of the first search. Again, the type arguments, if any, are given by the method reference. - if (PsiUtil.isRawSubstitutor(containingClass, qualifierResultSubstitutor)) { - final PsiClassType.ClassResolveResult pResult = PsiUtil.resolveGenericsClassInType(pType); - final PsiClass pClass = pResult.getElement(); - final PsiSubstitutor receiverSubstitutor = pClass != null ? TypeConversionUtil.getClassSubstitutor(containingClass, pClass, pResult.getSubstitutor()) : null; - if (receiverSubstitutor != null) { - if (!method.hasTypeParameters() && signature.getParameterTypes().length == 1) return receiverSubstitutor; - psiSubstitutor = receiverSubstitutor; - } - } - - final PsiType qType = JavaPsiFacade.getElementFactory(getProject()).createType(containingClass, psiSubstitutor); - - session.addConstraint(new TypeCompatibilityConstraint(qType, pType)); - - for (int i = 0; i < signature.getParameterTypes().length - 1; i++) { - final PsiType interfaceParamType = signature.getParameterTypes()[i + 1]; - session.addConstraint(new TypeCompatibilityConstraint(getParameterType(parameters, i, varargs), interfaceParamType)); - } - } - - if (!session.repeatInferencePhases(false)) { - return substitutor; - } - - if (interfaceMethodReturnType != PsiType.VOID) { - final PsiType returnType = method.isConstructor() ? composeReturnType(containingClass, substitutor) : method.getReturnType(); - if (returnType != null) { - session.registerConstraints(returnType, interfaceMethodReturnType); - } - } - return session.infer(parameters, null, null); - } - - private PsiType getParameterType(PsiParameter[] parameters, int i, boolean varargs) { - if (varargs && i >= parameters.length - 1) { - final PsiType type = parameters[parameters.length - 1].getType(); - LOG.assertTrue(type instanceof PsiEllipsisType); - return ((PsiEllipsisType)type).getComponentType(); - } - return parameters[i].getType(); - } - }; - } - }; - processor.setIsConstructor(isConstructor); - processor.setName(isConstructor ? containingClass.getName() : element.getText()); - final PsiExpression expression = getQualifierExpression(); - if (expression == null || !(expression.getType() instanceof PsiArrayType)) { - processor.setAccessClass(containingClass); - } - - if (qualifierResolveResult.isReferenceTypeQualified() && isLocatedInStaticContext(containingClass)) { - processor.handleEvent(JavaScopeProcessorEvent.START_STATIC, null); - } - ResolveState state = ResolveState.initial().put(PsiSubstitutor.KEY, substitutor); - containingClass.processDeclarations(processor, state, reference, reference); - return processor.getResult(); - } - } - return JavaResolveResult.EMPTY_ARRAY; - } - - protected PsiType getInterfaceType(PsiMethodReferenceExpression reference) { - PsiType functionalInterfaceType = null; - final Map map = PsiMethodReferenceUtil.ourRefs.get(); - if (map != null) { - functionalInterfaceType = FunctionalInterfaceParameterizationUtil.getGroundTargetType(map.get(reference)); - } - - if (functionalInterfaceType == null) { - functionalInterfaceType = reference.getFunctionalInterfaceType(); - } - - return functionalInterfaceType; - } - - protected PsiConflictResolver createResolver(PsiMethodReferenceUtil.QualifierResolveResult qualifierResolveResult, - PsiMethod interfaceMethod, - MethodSignature signature) { - return new MethodReferenceConflictResolver(qualifierResolveResult, signature, interfaceMethod != null && interfaceMethod.isVarArgs()); - } - - private PsiClassType composeReturnType(PsiClass containingClass, PsiSubstitutor substitutor) { - final boolean isRawSubst = PsiUtil.isRawSubstitutor(containingClass, substitutor); - return JavaPsiFacade.getElementFactory(containingClass.getProject()) - .createType(containingClass, isRawSubst ? PsiSubstitutor.EMPTY : substitutor); - } - - private class MethodReferenceConflictResolver extends JavaMethodsConflictResolver { - private final MethodSignature mySignature; - private final PsiMethodReferenceUtil.QualifierResolveResult myQualifierResolveResult; - private final boolean myFunctionalMethodVarArgs; - - private MethodReferenceConflictResolver(PsiMethodReferenceUtil.QualifierResolveResult qualifierResolveResult, - @Nullable MethodSignature signature, boolean varArgs) { - super(PsiMethodReferenceExpressionImpl.this, signature != null ? signature.getParameterTypes() : PsiType.EMPTY_ARRAY, PsiUtil.getLanguageLevel(PsiMethodReferenceExpressionImpl.this)); - myQualifierResolveResult = qualifierResolveResult; - myFunctionalMethodVarArgs = varArgs; - mySignature = signature; - } - - @Override - protected int getPertinentApplicabilityLevel(MethodCandidateInfo conflict) { - return conflict.isVarargs() ? MethodCandidateInfo.ApplicabilityLevel.VARARGS : MethodCandidateInfo.ApplicabilityLevel.FIXED_ARITY; - } - - @Nullable - @Override - public CandidateInfo resolveConflict(@NotNull List conflicts) { - if (mySignature == null) return null; - - checkSameSignatures(conflicts); - checkAccessStaticLevels(conflicts, true); - - final PsiType[] parameterTypes = mySignature.getParameterTypes(); - boolean hasReceiver = PsiMethodReferenceUtil.hasReceiver(parameterTypes, myQualifierResolveResult, - PsiMethodReferenceExpressionImpl.this); - - final List firstCandidates = new ArrayList(); - final List secondCandidates = new ArrayList(); - - for (CandidateInfo conflict : conflicts) { - if (!(conflict instanceof MethodCandidateInfo)) continue; - final PsiMethod psiMethod = ((MethodCandidateInfo)conflict).getElement(); - if (psiMethod == null) continue; - - final PsiSubstitutor substitutor = conflict.getSubstitutor(); - final PsiType[] signatureParameterTypes2 = psiMethod.getSignature(substitutor).getParameterTypes(); - - final boolean varargs = ((MethodCandidateInfo)conflict).isVarargs(); - if (varargs && (!psiMethod.isVarArgs() || myFunctionalMethodVarArgs)) continue; - - if ((varargs || parameterTypes.length == signatureParameterTypes2.length) && - isCorrectAssignment(signatureParameterTypes2, parameterTypes, substitutor, varargs, 0)) { - firstCandidates.add(conflict); - } - - if (hasReceiver && - (varargs || parameterTypes.length == signatureParameterTypes2.length + 1) && - isCorrectAssignment(signatureParameterTypes2, parameterTypes, substitutor, varargs, 1)) { - secondCandidates.add(conflict); - } - } - - if (myQualifierResolveResult.isReferenceTypeQualified() && getReferenceNameElement() instanceof PsiIdentifier) { - //If the first search produces a static method, and no non-static method is applicable for the second search, then the result of the first search is the compile-time declaration. - CandidateInfo candidateInfo = filterStaticCorrectCandidates(firstCandidates, secondCandidates, true); - if (candidateInfo != null) { - return candidateInfo; - } - - //If the second search produces a non-static method, and no static method is applicable for the first search, then the result of the second search is the compile-time declaration. - candidateInfo = filterStaticCorrectCandidates(secondCandidates, firstCandidates, false); - if (candidateInfo != null) { - return candidateInfo; - } - } - - if (resolveConflicts(firstCandidates, secondCandidates, MethodCandidateInfo.ApplicabilityLevel.FIXED_ARITY)) { - return !firstCandidates.isEmpty() ? firstCandidates.get(0) : secondCandidates.get(0); - } - - if (resolveConflicts(firstCandidates, secondCandidates, MethodCandidateInfo.ApplicabilityLevel.VARARGS)) { - return !firstCandidates.isEmpty() ? firstCandidates.get(0) : secondCandidates.get(0); - } - - conflicts.clear(); - firstCandidates.addAll(secondCandidates); - conflicts.addAll(firstCandidates); - return null; - } - - private boolean resolveConflicts(List firstCandidates, List secondCandidates, int applicabilityLevel) { - - checkApplicability(firstCandidates); - checkSpecifics(firstCandidates, applicabilityLevel, myLanguageLevel); - - checkApplicability(secondCandidates); - checkSpecifics(secondCandidates, applicabilityLevel, myLanguageLevel); - - return firstCandidates.size() + secondCandidates.size() == 1; - } - - @Override - protected boolean nonComparable(CandidateInfo method, CandidateInfo conflict) { - if (method == conflict) return true; - PsiElement psiElement = method.getElement(); - PsiElement conflictElement = conflict.getElement(); - if (psiElement instanceof PsiMethod && conflictElement instanceof PsiMethod) { - if (((PsiMethod)psiElement).getParameterList().getParametersCount() != - ((PsiMethod)conflictElement).getParameterList().getParametersCount()) { - return true; - } - } - return false; - } - - /** - * 15.13.1 - */ - private CandidateInfo filterStaticCorrectCandidates(List firstCandidates, - List secondCandidates, - boolean shouldBeStatic) { - if (firstCandidates.size() == 1) { - final CandidateInfo candidateInfo = firstCandidates.get(0); - final PsiElement element = candidateInfo.getElement(); - if (element instanceof PsiMethod) { - final boolean isStatic = ((PsiMethod)element).hasModifierProperty(PsiModifier.STATIC); - if (shouldBeStatic && isStatic || !shouldBeStatic && !isStatic) { - for (CandidateInfo secondCandidate : secondCandidates) { - final PsiElement psiElement = secondCandidate.getElement(); - if (psiElement instanceof PsiMethod) { - final boolean oppositeStatic = ((PsiMethod)psiElement).hasModifierProperty(PsiModifier.STATIC); - if (shouldBeStatic && !oppositeStatic || !shouldBeStatic && oppositeStatic) { - return null; - } - } - } - return candidateInfo; - } - } - } - return null; - } - - private boolean isCorrectAssignment(PsiType[] signatureParameterTypes2, - PsiType[] parameterTypes, - PsiSubstitutor substitutor, - boolean varargs, - int offset) { - final int min = Math.min(signatureParameterTypes2.length, parameterTypes.length - offset); - for (int i = 0; i < min; i++) { - final PsiType type1 = substitutor.substitute(parameterTypes[i + offset]); - final PsiType type2 = signatureParameterTypes2[i]; - if (varargs && i == signatureParameterTypes2.length - 1) { - if (!TypeConversionUtil.isAssignable(type2, type1) && !TypeConversionUtil.isAssignable(((PsiArrayType)type2).getComponentType(), type1)) { - return false; - } - } - else if (!TypeConversionUtil.isAssignable(type2, type1)) { - return false; - } - } - return true; - } - } - } - @Override public boolean isAcceptable(PsiType left) { if (left instanceof PsiIntersectionType) {