@Override @Nullable public PsiType fun(final PsiMethodCallExpression call) { PsiReferenceExpression methodExpression = call.getMethodExpression(); PsiType theOnly = null; final JavaResolveResult[] results = methodExpression.multiResolve(false); LanguageLevel languageLevel = PsiUtil.getLanguageLevel(call); final PsiElement callParent = PsiUtil.skipParenthesizedExprUp(call.getParent()); final PsiExpressionList parentArgList; if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8)) { parentArgList = callParent instanceof PsiConditionalExpression && !PsiPolyExpressionUtil.isPolyExpression((PsiExpression) callParent) ? null : PsiTreeUtil.getParentOfType(call, PsiExpressionList.class); } else { parentArgList = null; } final MethodCandidateInfo.CurrentCandidateProperties properties = MethodCandidateInfo.getCurrentMethod(parentArgList); final boolean genericMethodCall = properties != null && properties.getInfo().isToInferApplicability(); for (int i = 0; i < results.length; i++) { final JavaResolveResult candidateInfo = results[i]; if (genericMethodCall && PsiPolyExpressionUtil.isMethodCallPolyExpression( call, (PsiMethod) candidateInfo.getElement())) { if (callParent instanceof PsiAssignmentExpression) { return null; } LOG.error("poly expression evaluation during overload resolution"); } final PsiType type = getResultType(call, methodExpression, candidateInfo, languageLevel); if (type == null) { return null; } if (i == 0) { theOnly = type; } else if (!theOnly.equals(type)) { return null; } } return PsiClassImplUtil.correctType(theOnly, call.getResolveScope()); }
@Override public boolean reduce(InferenceSession session, List<ConstraintFormula> constraints) { if (!LambdaUtil.isFunctionalType(myT)) { return false; } final PsiType groundTargetType = FunctionalInterfaceParameterizationUtil.getGroundTargetType(myT); final PsiClassType.ClassResolveResult classResolveResult = PsiUtil.resolveGenericsClassInType(groundTargetType); final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(classResolveResult); if (interfaceMethod == null) { return false; } final PsiSubstitutor substitutor = LambdaUtil.getSubstitutor(interfaceMethod, classResolveResult); final PsiParameter[] targetParameters = interfaceMethod.getParameterList().getParameters(); final PsiType interfaceMethodReturnType = interfaceMethod.getReturnType(); final PsiType returnType = substitutor.substitute(interfaceMethodReturnType); final PsiType[] typeParameters = myExpression.getTypeParameters(); final PsiMethodReferenceUtil.QualifierResolveResult qualifierResolveResult = PsiMethodReferenceUtil.getQualifierResolveResult(myExpression); if (!myExpression.isExact()) { for (PsiParameter parameter : targetParameters) { if (!session.isProperType(substitutor.substitute(parameter.getType()))) { return false; } } } else { final PsiMember applicableMember = myExpression.getPotentiallyApplicableMember(); LOG.assertTrue(applicableMember != null); final PsiClass applicableMemberContainingClass = applicableMember.getContainingClass(); final PsiClass containingClass = qualifierResolveResult.getContainingClass(); PsiSubstitutor psiSubstitutor = qualifierResolveResult.getSubstitutor(); psiSubstitutor = applicableMemberContainingClass == null || containingClass == null || myExpression.isConstructor() ? psiSubstitutor : TypeConversionUtil.getSuperClassSubstitutor( applicableMemberContainingClass, containingClass, psiSubstitutor); PsiType applicableMethodReturnType = applicableMember instanceof PsiMethod ? ((PsiMethod) applicableMember).getReturnType() : null; int idx = 0; for (PsiTypeParameter param : ((PsiTypeParameterListOwner) applicableMember).getTypeParameters()) { if (idx < typeParameters.length) { psiSubstitutor = psiSubstitutor.put(param, typeParameters[idx++]); } } final PsiParameter[] parameters = applicableMember instanceof PsiMethod ? ((PsiMethod) applicableMember).getParameterList().getParameters() : PsiParameter.EMPTY_ARRAY; if (targetParameters.length == parameters.length + 1) { specialCase(session, constraints, substitutor, targetParameters, true); for (int i = 1; i < targetParameters.length; i++) { constraints.add( new TypeCompatibilityConstraint( session.substituteWithInferenceVariables( psiSubstitutor.substitute(parameters[i - 1].getType())), substitutor.substitute(targetParameters[i].getType()))); } } else if (targetParameters.length == parameters.length) { for (int i = 0; i < targetParameters.length; i++) { constraints.add( new TypeCompatibilityConstraint( session.substituteWithInferenceVariables( psiSubstitutor.substitute(parameters[i].getType())), substitutor.substitute(targetParameters[i].getType()))); } } else { return false; } if (returnType != PsiType.VOID && returnType != null) { if (applicableMethodReturnType == PsiType.VOID) { return false; } if (applicableMethodReturnType != null) { constraints.add( new TypeCompatibilityConstraint( returnType, session.substituteWithInferenceVariables( psiSubstitutor.substitute(applicableMethodReturnType)))); } else if (applicableMember instanceof PsiClass || applicableMember instanceof PsiMethod && ((PsiMethod) applicableMember).isConstructor()) { final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(applicableMember.getProject()); if (containingClass != null) { final PsiType classType = session.substituteWithInferenceVariables( elementFactory.createType(containingClass, psiSubstitutor)); constraints.add(new TypeCompatibilityConstraint(returnType, classType)); } } } return true; } final Map<PsiElement, PsiType> map = LambdaUtil.getFunctionalTypeMap(); final PsiType added = map.put(myExpression, session.startWithFreshVars(groundTargetType)); final JavaResolveResult resolve; try { resolve = myExpression.advancedResolve(true); } finally { if (added == null) { map.remove(myExpression); } } final PsiElement element = resolve.getElement(); if (element == null) { return false; } if (PsiType.VOID.equals(returnType) || returnType == null) { return true; } if (element instanceof PsiMethod) { final PsiMethod method = (PsiMethod) element; final PsiType referencedMethodReturnType; final PsiClass containingClass = method.getContainingClass(); LOG.assertTrue(containingClass != null, method); PsiClass qContainingClass = qualifierResolveResult.getContainingClass(); PsiSubstitutor psiSubstitutor = qualifierResolveResult.getSubstitutor(); if (qContainingClass != null) { if (PsiUtil.isRawSubstitutor(qContainingClass, psiSubstitutor)) { psiSubstitutor = PsiSubstitutor.EMPTY; } if (qContainingClass.isInheritor(containingClass, true)) { psiSubstitutor = TypeConversionUtil.getClassSubstitutor( containingClass, qContainingClass, PsiSubstitutor.EMPTY); LOG.assertTrue(psiSubstitutor != null); } } if (method.isConstructor()) { referencedMethodReturnType = JavaPsiFacade.getElementFactory(method.getProject()) .createType(containingClass, PsiSubstitutor.EMPTY); } else { referencedMethodReturnType = method.getReturnType(); } LOG.assertTrue(referencedMethodReturnType != null, method); if (!PsiTreeUtil.isContextAncestor(containingClass, myExpression, false) || PsiUtil.getEnclosingStaticElement(myExpression, containingClass) != null) { session.initBounds(myExpression, containingClass.getTypeParameters()); } session.initBounds(myExpression, method.getTypeParameters()); // if i) the method reference elides NonWildTypeArguments, // ii) the compile-time declaration is a generic method, and // iii) the return type of the compile-time declaration mentions at least one of the method's // type parameters; if (typeParameters.length == 0 && method.getTypeParameters().length > 0) { final PsiClass interfaceClass = classResolveResult.getElement(); LOG.assertTrue(interfaceClass != null); if (PsiPolyExpressionUtil.mentionsTypeParameters( referencedMethodReturnType, ContainerUtil.newHashSet(method.getTypeParameters()))) { // the constraint reduces to the bound set B3 which would be used to determine the method // reference's invocation type // when targeting the return type of the function type, as defined in 18.5.2. session.collectApplicabilityConstraints( myExpression, ((MethodCandidateInfo) resolve), groundTargetType); session.registerReturnTypeConstraints(referencedMethodReturnType, returnType); return true; } } if (PsiType.VOID.equals(referencedMethodReturnType)) { return false; } int idx = 0; for (PsiTypeParameter param : method.getTypeParameters()) { if (idx < typeParameters.length) { psiSubstitutor = psiSubstitutor.put(param, typeParameters[idx++]); } } final PsiParameter[] parameters = method.getParameterList().getParameters(); if (targetParameters.length == parameters.length + 1 && !method.isVarArgs() && PsiPolyExpressionUtil.mentionsTypeParameters( referencedMethodReturnType, ContainerUtil.newHashSet( containingClass.getTypeParameters()))) { // todo specification bug? specialCase(session, constraints, substitutor, targetParameters, false); } constraints.add( new TypeCompatibilityConstraint( returnType, session.substituteWithInferenceVariables( psiSubstitutor.substitute(referencedMethodReturnType)))); } return true; }
protected boolean findNewParamsPlace( PsiExpression[] expressions, PsiMethod targetMethod, PsiSubstitutor substitutor, StringBuilder buf, HashSet<ParameterInfoImpl> newParams, PsiParameter[] parameters, List<ParameterInfoImpl> result) { // find which parameters to introduce and where Set<String> existingNames = new HashSet<String>(); for (PsiParameter parameter : parameters) { existingNames.add(parameter.getName()); } int ei = 0; int pi = 0; PsiParameter varargParam = targetMethod.isVarArgs() ? parameters[parameters.length - 1] : null; while (ei < expressions.length || pi < parameters.length) { if (buf.length() > 0) buf.append(", "); PsiExpression expression = ei < expressions.length ? expressions[ei] : null; PsiParameter parameter = pi < parameters.length ? parameters[pi] : null; PsiType paramType = parameter == null ? null : substitutor.substitute(parameter.getType()); boolean parameterAssignable = paramType != null && (expression == null || TypeConversionUtil.areTypesAssignmentCompatible(paramType, expression)); if (parameterAssignable) { final PsiType type = parameter.getType(); result.add(new ParameterInfoImpl(pi, parameter.getName(), type)); buf.append(escapePresentableType(type)); pi++; ei++; } else if (isArgumentInVarargPosition(expressions, ei, varargParam, substitutor)) { if (pi == parameters.length - 1) { assert varargParam != null; final PsiType type = varargParam.getType(); result.add(new ParameterInfoImpl(pi, varargParam.getName(), type)); buf.append(escapePresentableType(type)); } pi++; ei++; } else if (expression != null) { if (varargParam != null && pi >= parameters.length) return false; if (PsiPolyExpressionUtil.isPolyExpression(expression)) return false; PsiType exprType = RefactoringUtil.getTypeByExpression(expression); if (exprType == null) return false; if (exprType instanceof PsiDisjunctionType) { exprType = ((PsiDisjunctionType) exprType).getLeastUpperBound(); } JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(expression.getProject()); String name = suggestUniqueParameterName(codeStyleManager, expression, exprType, existingNames); final ParameterInfoImpl newParameterInfo = new ParameterInfoImpl(-1, name, exprType, expression.getText().replace('\n', ' ')); result.add(newParameterInfo); newParams.add(newParameterInfo); buf.append("<b>").append(escapePresentableType(exprType)).append("</b>"); ei++; } } if (result.size() != expressions.length && varargParam == null) return false; return true; }
private ParameterInfoImpl[] getNewParametersInfo( PsiExpression[] expressions, PsiMethod targetMethod, PsiSubstitutor substitutor, final StringBuilder buf, final HashSet<ParameterInfoImpl> newParams, final HashSet<ParameterInfoImpl> removedParams, final HashSet<ParameterInfoImpl> changedParams) { PsiParameter[] parameters = targetMethod.getParameterList().getParameters(); List<ParameterInfoImpl> result = new ArrayList<ParameterInfoImpl>(); if (expressions.length < parameters.length) { // find which parameters to remove int ei = 0; int pi = 0; while (ei < expressions.length && pi < parameters.length) { PsiExpression expression = expressions[ei]; PsiParameter parameter = parameters[pi]; PsiType paramType = substitutor.substitute(parameter.getType()); if (buf.length() > 0) buf.append(", "); final PsiType parameterType = PsiUtil.convertAnonymousToBaseType(paramType); final String presentableText = escapePresentableType(parameterType); final ParameterInfoImpl parameterInfo = new ParameterInfoImpl(pi, parameter.getName(), parameter.getType()); if (TypeConversionUtil.areTypesAssignmentCompatible(paramType, expression)) { buf.append(presentableText); result.add(parameterInfo); pi++; ei++; } else { buf.append("<s>").append(presentableText).append("</s>"); removedParams.add(parameterInfo); pi++; } } if (result.size() != expressions.length) return null; for (int i = pi; i < parameters.length; i++) { if (buf.length() > 0) buf.append(", "); buf.append("<s>").append(escapePresentableType(parameters[i].getType())).append("</s>"); final ParameterInfoImpl parameterInfo = new ParameterInfoImpl(pi, parameters[i].getName(), parameters[i].getType()); removedParams.add(parameterInfo); } } else if (expressions.length > parameters.length) { if (!findNewParamsPlace( expressions, targetMethod, substitutor, buf, newParams, parameters, result)) return null; } else { // parameter type changed for (int i = 0; i < parameters.length; i++) { if (buf.length() > 0) buf.append(", "); PsiParameter parameter = parameters[i]; PsiExpression expression = expressions[i]; PsiType paramType = substitutor.substitute(parameter.getType()); final String presentableText = escapePresentableType(paramType); if (TypeConversionUtil.areTypesAssignmentCompatible(paramType, expression)) { result.add(new ParameterInfoImpl(i, parameter.getName(), paramType)); buf.append(presentableText); } else { if (PsiPolyExpressionUtil.isPolyExpression(expression)) return null; PsiType exprType = RefactoringUtil.getTypeByExpression(expression); if (exprType == null) return null; if (exprType instanceof PsiDisjunctionType) { exprType = ((PsiDisjunctionType) exprType).getLeastUpperBound(); } final ParameterInfoImpl changedParameterInfo = new ParameterInfoImpl(i, parameter.getName(), exprType); result.add(changedParameterInfo); changedParams.add(changedParameterInfo); buf.append("<s>") .append(presentableText) .append("</s> <b>") .append(escapePresentableType(exprType)) .append("</b>"); } } // do not perform silly refactorings boolean isSilly = true; for (int i = 0; i < result.size(); i++) { PsiParameter parameter = parameters[i]; PsiType paramType = substitutor.substitute(parameter.getType()); ParameterInfoImpl parameterInfo = result.get(i); String typeText = parameterInfo.getTypeText(); if (!paramType.equalsToText(typeText) && !paramType.getPresentableText().equals(typeText)) { isSilly = false; break; } } if (isSilly) return null; } return result.toArray(new ParameterInfoImpl[result.size()]); }