Example #1
0
  @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;
  }
 @Nullable
 @Override
 public PsiType getFunctionalInterfaceType() {
   return FunctionalInterfaceParameterizationUtil.getGroundTargetType(
       LambdaUtil.getFunctionalInterfaceType(this, true), this);
 }