public static PsiType captureReturnType(
      PsiMethodCallExpression call,
      PsiMethod method,
      PsiType ret,
      JavaResolveResult result,
      LanguageLevel languageLevel) {
    PsiSubstitutor substitutor = result.getSubstitutor();
    PsiType substitutedReturnType = substitutor.substitute(ret);
    if (substitutedReturnType == null) {
      return TypeConversionUtil.erasure(ret);
    }

    if (InferenceSession.wasUncheckedConversionPerformed(call)) {
      // 18.5.2
      // if unchecked conversion was necessary, then this substitution provides the parameter types
      // of the invocation type,
      // while the return type and thrown types are given by the erasure of m's type (without
      // applying θ').
      // due to https://bugs.openjdk.java.net/browse/JDK-8135087 erasure is called on
      // substitutedReturnType and not on ret type itself as by spec
      return TypeConversionUtil.erasure(substitutedReturnType);
    }

    // 15.12.2.6. Method Invocation Type
    // If unchecked conversion was necessary for the method to be applicable,
    // the parameter types of the invocation type are the parameter types of the method's type,
    // and the return type and thrown types are given by the erasures of the return type and thrown
    // types of the method's type.
    if (!languageLevel.isAtLeast(LanguageLevel.JDK_1_8)
        && (method.hasTypeParameters()
            || JavaVersionService.getInstance().isAtLeast(call, JavaSdkVersion.JDK_1_8))
        && result instanceof MethodCandidateInfo
        && ((MethodCandidateInfo) result).isApplicable()) {
      final PsiType[] args = call.getArgumentList().getExpressionTypes();
      final boolean allowUncheckedConversion = false;
      final int applicabilityLevel =
          PsiUtil.getApplicabilityLevel(
              method, substitutor, args, languageLevel, allowUncheckedConversion, true);
      if (applicabilityLevel == MethodCandidateInfo.ApplicabilityLevel.NOT_APPLICABLE) {
        return TypeConversionUtil.erasure(substitutedReturnType);
      }
    }

    if (PsiUtil.isRawSubstitutor(method, substitutor)) {
      final PsiType returnTypeErasure = TypeConversionUtil.erasure(ret);
      if (Comparing.equal(TypeConversionUtil.erasure(substitutedReturnType), returnTypeErasure)) {
        return returnTypeErasure;
      }
    }
    return PsiUtil.captureToplevelWildcards(substitutedReturnType, call);
  }
 public static PsiSubstitutor obtainFinalSubstitutor(
     @NotNull PsiClass candidateClass,
     @NotNull PsiSubstitutor candidateSubstitutor,
     @NotNull PsiClass aClass,
     @NotNull PsiSubstitutor substitutor,
     @NotNull PsiElementFactory elementFactory,
     @NotNull LanguageLevel languageLevel) {
   if (PsiUtil.isRawSubstitutor(aClass, substitutor)) {
     return elementFactory.createRawSubstitutor(candidateClass);
   }
   final PsiType containingType =
       elementFactory.createType(candidateClass, candidateSubstitutor, languageLevel);
   PsiType type = substitutor.substitute(containingType);
   if (!(type instanceof PsiClassType)) return candidateSubstitutor;
   return ((PsiClassType) type).resolveGenerics().getSubstitutor();
 }
Exemplo n.º 3
0
  private void specialCase(
      InferenceSession session,
      List<ConstraintFormula> constraints,
      PsiSubstitutor substitutor,
      PsiParameter[] targetParameters,
      boolean ignoreRaw) {
    final PsiElement qualifier = myExpression.getQualifier();
    PsiType qualifierType = null;
    if (qualifier instanceof PsiTypeElement) {
      qualifierType = ((PsiTypeElement) qualifier).getType();
      final PsiClass qualifierClass = PsiUtil.resolveClassInType(qualifierType);
      if (qualifierClass != null) {
        qualifierType =
            JavaPsiFacade.getElementFactory(myExpression.getProject())
                .createType(qualifierClass, PsiSubstitutor.EMPTY);
      }
    } else if (qualifier instanceof PsiExpression) {
      qualifierType = ((PsiExpression) qualifier).getType();
      if (qualifierType == null && qualifier instanceof PsiReferenceExpression) {
        final JavaResolveResult resolveResult =
            ((PsiReferenceExpression) qualifier).advancedResolve(false);
        final PsiElement res = resolveResult.getElement();
        if (res instanceof PsiClass) {
          PsiClass containingClass = (PsiClass) res;
          final boolean isRawSubst =
              !ignoreRaw
                  && !myExpression.isConstructor()
                  && PsiUtil.isRawSubstitutor(containingClass, resolveResult.getSubstitutor());
          qualifierType =
              JavaPsiFacade.getElementFactory(res.getProject())
                  .createType(
                      containingClass,
                      isRawSubst ? PsiSubstitutor.EMPTY : resolveResult.getSubstitutor());
        }
      }
    }

    final PsiClass qualifierClass = PsiUtil.resolveClassInType(qualifierType);
    if (qualifierClass != null) {
      session.initBounds(myExpression, qualifierClass.getTypeParameters());
      constraints.add(
          new StrictSubtypingConstraint(
              session.substituteWithInferenceVariables(qualifierType),
              session.substituteWithInferenceVariables(
                  substitutor.substitute(targetParameters[0].getType()))));
    }
  }
  public static boolean processDeclarationsInClass(
      @NotNull PsiClass aClass,
      @NotNull final PsiScopeProcessor processor,
      @NotNull ResolveState state,
      @Nullable Set<PsiClass> visited,
      PsiElement last,
      @NotNull PsiElement place,
      boolean isRaw) {
    if (last instanceof PsiTypeParameterList || last instanceof PsiModifierList) {
      return true; // TypeParameterList and ModifierList do not see our declarations
    }
    if (visited != null && visited.contains(aClass)) return true;

    PsiSubstitutor substitutor = state.get(PsiSubstitutor.KEY);
    isRaw = isRaw || PsiUtil.isRawSubstitutor(aClass, substitutor);

    ParameterizedCachedValue<MembersMap, PsiClass> cache =
        getValues(aClass); // aClass.getUserData(MAP_IN_CLASS_KEY);
    boolean upToDate = cache.hasUpToDateValue();
    LanguageLevel languageLevel = PsiUtil.getLanguageLevel(place);
    if (
    /*true || */ upToDate) {
      final NameHint nameHint = processor.getHint(NameHint.KEY);
      if (nameHint != null) {
        String name = nameHint.getName(state);
        return processCachedMembersByName(
            aClass,
            processor,
            state,
            visited,
            last,
            place,
            isRaw,
            substitutor,
            cache.getValue(aClass),
            name,
            languageLevel);
      }
    }
    return processDeclarationsInClassNotCached(
        aClass, processor, state, visited, last, place, isRaw, languageLevel);
  }
 private static boolean processSuperTypes(
     @NotNull PsiClass aClass,
     @NotNull PsiScopeProcessor processor,
     @Nullable Set<PsiClass> visited,
     PsiElement last,
     @NotNull PsiElement place,
     @NotNull ResolveState state,
     boolean isRaw,
     @NotNull PsiElementFactory factory,
     @NotNull LanguageLevel languageLevel) {
   boolean resolved = false;
   for (final PsiClassType superType : aClass.getSuperTypes()) {
     final PsiClassType.ClassResolveResult superTypeResolveResult = superType.resolveGenerics();
     PsiClass superClass = superTypeResolveResult.getElement();
     if (superClass == null) continue;
     PsiSubstitutor finalSubstitutor =
         obtainFinalSubstitutor(
             superClass,
             superTypeResolveResult.getSubstitutor(),
             aClass,
             state.get(PsiSubstitutor.KEY),
             factory,
             languageLevel);
     if (aClass instanceof PsiTypeParameter
         && PsiUtil.isRawSubstitutor(superClass, finalSubstitutor)) {
       finalSubstitutor = PsiSubstitutor.EMPTY;
     }
     if (!processDeclarationsInClass(
         superClass,
         processor,
         state.put(PsiSubstitutor.KEY, finalSubstitutor),
         visited,
         last,
         place,
         isRaw)) {
       resolved = true;
     }
   }
   return !resolved;
 }
Exemplo n.º 6
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;
  }
  private static Map<MethodSignature, HierarchicalMethodSignature> buildMethodHierarchy(
      PsiClass aClass,
      PsiSubstitutor substitutor,
      final boolean includePrivates,
      final Set<PsiClass> visited,
      boolean isInRawContext) {
    Map<MethodSignature, HierarchicalMethodSignature> result =
        new LinkedHashMap<MethodSignature, HierarchicalMethodSignature>();
    final Map<MethodSignature, List<PsiMethod>> sameParameterErasureMethods =
        new THashMap<MethodSignature, List<PsiMethod>>(
            MethodSignatureUtil.METHOD_PARAMETERS_ERASURE_EQUALITY);

    Map<MethodSignature, HierarchicalMethodSignatureImpl> map =
        new THashMap<MethodSignature, HierarchicalMethodSignatureImpl>(
            new TObjectHashingStrategy<MethodSignature>() {
              @Override
              public int computeHashCode(MethodSignature signature) {
                return MethodSignatureUtil.METHOD_PARAMETERS_ERASURE_EQUALITY.computeHashCode(
                    signature);
              }

              @Override
              public boolean equals(MethodSignature o1, MethodSignature o2) {
                if (!MethodSignatureUtil.METHOD_PARAMETERS_ERASURE_EQUALITY.equals(o1, o2))
                  return false;
                List<PsiMethod> list = sameParameterErasureMethods.get(o1);
                boolean toCheckReturnType = list != null && list.size() > 1;
                if (!toCheckReturnType) return true;
                PsiType returnType1 =
                    ((MethodSignatureBackedByPsiMethod) o1).getMethod().getReturnType();
                PsiType returnType2 =
                    ((MethodSignatureBackedByPsiMethod) o2).getMethod().getReturnType();
                if (returnType1 == null && returnType2 == null) return true;
                if (returnType1 == null || returnType2 == null) return false;

                PsiType erasure1 =
                    TypeConversionUtil.erasure(o1.getSubstitutor().substitute(returnType1));
                PsiType erasure2 =
                    TypeConversionUtil.erasure(o2.getSubstitutor().substitute(returnType2));
                return erasure1.equals(erasure2);
              }
            });

    for (PsiMethod method : aClass.getMethods()) {
      if (!method.isValid()) {
        throw new PsiInvalidElementAccessException(
            method, "class.valid=" + aClass.isValid() + "; name=" + method.getName());
      }
      if (!includePrivates && method.hasModifierProperty(PsiModifier.PRIVATE)) continue;
      final MethodSignatureBackedByPsiMethod signature =
          MethodSignatureBackedByPsiMethod.create(method, substitutor, isInRawContext);
      HierarchicalMethodSignatureImpl newH = new HierarchicalMethodSignatureImpl(signature);

      List<PsiMethod> list = sameParameterErasureMethods.get(signature);
      if (list == null) {
        list = new SmartList<PsiMethod>();
        sameParameterErasureMethods.put(signature, list);
      }
      list.add(method);

      LOG.assertTrue(newH.getMethod().isValid());
      result.put(signature, newH);
      map.put(signature, newH);
    }

    for (PsiClassType superType : aClass.getSuperTypes()) {
      PsiClassType.ClassResolveResult superTypeResolveResult = superType.resolveGenerics();
      PsiClass superClass = superTypeResolveResult.getElement();
      if (superClass == null) continue;
      if (!visited.add(superClass)) continue; // cyclic inheritance
      final PsiSubstitutor superSubstitutor = superTypeResolveResult.getSubstitutor();
      PsiSubstitutor finalSubstitutor =
          obtainFinalSubstitutor(superClass, superSubstitutor, substitutor, isInRawContext);

      final boolean isInRawContextSuper =
          (isInRawContext || PsiUtil.isRawSubstitutor(superClass, superSubstitutor))
              && superClass.getTypeParameters().length != 0;
      Map<MethodSignature, HierarchicalMethodSignature> superResult =
          buildMethodHierarchy(superClass, finalSubstitutor, false, visited, isInRawContextSuper);
      visited.remove(superClass);

      List<Pair<MethodSignature, HierarchicalMethodSignature>> flattened =
          new ArrayList<Pair<MethodSignature, HierarchicalMethodSignature>>();
      for (Map.Entry<MethodSignature, HierarchicalMethodSignature> entry : superResult.entrySet()) {
        HierarchicalMethodSignature hms = entry.getValue();
        MethodSignature signature = entry.getKey();
        PsiClass containingClass = hms.getMethod().getContainingClass();
        List<HierarchicalMethodSignature> supers =
            new ArrayList<HierarchicalMethodSignature>(hms.getSuperSignatures());
        for (HierarchicalMethodSignature aSuper : supers) {
          PsiClass superContainingClass = aSuper.getMethod().getContainingClass();
          if (containingClass != null
              && superContainingClass != null
              && !containingClass.isInheritor(superContainingClass, true)) {
            // methods must be inherited from unrelated classes, so flatten hierarchy here
            // class C implements SAM1, SAM2 { void methodimpl() {} }
            // hms.getSuperSignatures().remove(aSuper);
            flattened.add(
                new Pair<MethodSignature, HierarchicalMethodSignature>(signature, aSuper));
          }
        }
        putInMap(aClass, result, map, hms, signature);
      }
      for (Pair<MethodSignature, HierarchicalMethodSignature> pair : flattened) {
        putInMap(aClass, result, map, pair.second, pair.first);
      }
    }

    for (Map.Entry<MethodSignature, HierarchicalMethodSignatureImpl> entry : map.entrySet()) {
      HierarchicalMethodSignatureImpl hierarchicalMethodSignature = entry.getValue();
      MethodSignature methodSignature = entry.getKey();
      if (result.get(methodSignature) == null
          && PsiUtil.isAccessible(
              aClass.getProject(), hierarchicalMethodSignature.getMethod(), aClass, aClass)) {
        LOG.assertTrue(hierarchicalMethodSignature.getMethod().isValid());
        result.put(methodSignature, hierarchicalMethodSignature);
      }
    }

    return result;
  }
  private Specifics isMoreSpecific(
      @NotNull MethodCandidateInfo info1,
      @NotNull MethodCandidateInfo info2,
      @MethodCandidateInfo.ApplicabilityLevelConstant int applicabilityLevel,
      @NotNull LanguageLevel languageLevel) {
    PsiMethod method1 = info1.getElement();
    PsiMethod method2 = info2.getElement();
    final PsiClass class1 = method1.getContainingClass();
    final PsiClass class2 = method2.getContainingClass();

    final PsiParameter[] params1 = method1.getParameterList().getParameters();
    final PsiParameter[] params2 = method2.getParameterList().getParameters();

    final PsiTypeParameter[] typeParameters1 = method1.getTypeParameters();
    final PsiTypeParameter[] typeParameters2 = method2.getTypeParameters();
    final PsiSubstitutor classSubstitutor1 =
        info1.getSubstitutor(false); // substitutions for method type parameters will be ignored
    final PsiSubstitutor classSubstitutor2 = info2.getSubstitutor(false);

    final int max = Math.max(params1.length, params2.length);
    PsiType[] types1 = PsiType.createArray(max);
    PsiType[] types2 = PsiType.createArray(max);
    final boolean varargsPosition =
        applicabilityLevel == MethodCandidateInfo.ApplicabilityLevel.VARARGS;
    for (int i = 0; i < max; i++) {
      ProgressManager.checkCanceled();
      PsiType type1 =
          params1.length > 0 ? params1[Math.min(i, params1.length - 1)].getType() : null;
      PsiType type2 =
          params2.length > 0 ? params2[Math.min(i, params2.length - 1)].getType() : null;
      if (varargsPosition) {
        if (type1 instanceof PsiEllipsisType
            && type2 instanceof PsiEllipsisType
            && params1.length == params2.length
            && class1 != null
            && (!JavaVersionService.getInstance().isAtLeast(class1, JavaSdkVersion.JDK_1_7)
                || ((PsiArrayType) type1)
                    .getComponentType()
                    .equalsToText(CommonClassNames.JAVA_LANG_OBJECT)
                || ((PsiArrayType) type2)
                    .getComponentType()
                    .equalsToText(CommonClassNames.JAVA_LANG_OBJECT))) {
          type1 = ((PsiEllipsisType) type1).toArrayType();
          type2 = ((PsiEllipsisType) type2).toArrayType();
        } else {
          type1 =
              type1 instanceof PsiEllipsisType ? ((PsiArrayType) type1).getComponentType() : type1;
          type2 =
              type2 instanceof PsiEllipsisType ? ((PsiArrayType) type2).getComponentType() : type2;
        }
      }

      types1[i] = type1;
      types2[i] = type2;
    }

    boolean sameBoxing = true;
    int[] boxingHappened = new int[2];
    for (int i = 0; i < types1.length; i++) {
      ProgressManager.checkCanceled();
      PsiType type1 = classSubstitutor1.substitute(types1[i]);
      PsiType type2 = classSubstitutor2.substitute(types2[i]);
      PsiType argType = i < getActualParameterTypes().length ? getActualParameterTypes()[i] : null;

      boolean boxingInFirst = false;
      if (isBoxingHappened(argType, type1, languageLevel)) {
        boxingHappened[0] += 1;
        boxingInFirst = true;
      }

      boolean boxingInSecond = false;
      if (isBoxingHappened(argType, type2, languageLevel)) {
        boxingHappened[1] += 1;
        boxingInSecond = true;
      }
      sameBoxing &= boxingInFirst == boxingInSecond;
    }
    if (boxingHappened[0] == 0 && boxingHappened[1] > 0) return Specifics.FIRST;
    if (boxingHappened[0] > 0 && boxingHappened[1] == 0) return Specifics.SECOND;

    if (sameBoxing) {
      final PsiSubstitutor siteSubstitutor1 = info1.getSiteSubstitutor();
      final PsiSubstitutor siteSubstitutor2 = info2.getSiteSubstitutor();

      final PsiType[] types2AtSite = typesAtSite(types2, siteSubstitutor2);
      final PsiType[] types1AtSite = typesAtSite(types1, siteSubstitutor1);

      final PsiSubstitutor methodSubstitutor1 =
          calculateMethodSubstitutor(
              typeParameters1, method1, siteSubstitutor1, types1, types2AtSite, languageLevel);
      boolean applicable12 =
          isApplicableTo(
              types2AtSite, method1, languageLevel, varargsPosition, methodSubstitutor1, method2);

      final PsiSubstitutor methodSubstitutor2 =
          calculateMethodSubstitutor(
              typeParameters2, method2, siteSubstitutor2, types2, types1AtSite, languageLevel);
      boolean applicable21 =
          isApplicableTo(
              types1AtSite, method2, languageLevel, varargsPosition, methodSubstitutor2, method1);

      if (!myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_8)) {
        final boolean typeArgsApplicable12 =
            GenericsUtil.isTypeArgumentsApplicable(
                typeParameters1, methodSubstitutor1, myArgumentsList, !applicable21);
        final boolean typeArgsApplicable21 =
            GenericsUtil.isTypeArgumentsApplicable(
                typeParameters2, methodSubstitutor2, myArgumentsList, !applicable12);

        if (!typeArgsApplicable12) {
          applicable12 = false;
        }

        if (!typeArgsApplicable21) {
          applicable21 = false;
        }
      }

      if (applicable12 || applicable21) {

        if (applicable12 && !applicable21) return Specifics.SECOND;
        if (applicable21 && !applicable12) return Specifics.FIRST;

        final boolean abstract1 = method1.hasModifierProperty(PsiModifier.ABSTRACT);
        final boolean abstract2 = method2.hasModifierProperty(PsiModifier.ABSTRACT);
        if (abstract1 && !abstract2) {
          return Specifics.SECOND;
        }
        if (abstract2 && !abstract1) {
          return Specifics.FIRST;
        }
      }

      if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8)
          && myArgumentsList instanceof PsiExpressionList
          && (typeParameters1.length == 0 || typeParameters2.length == 0)) {
        boolean toCompareFunctional = false;
        if (types1.length > 0 && types2.length > 0) {
          for (int i = 0; i < getActualParametersLength(); i++) {
            final PsiType type1 = types1[Math.min(i, types1.length - 1)];
            final PsiType type2 = types2[Math.min(i, types2.length - 1)];
            // from 15.12.2.5 Choosing the Most Specific Method
            // In addition, a functional interface type S is more specific than a functional
            // interface type T for an expression exp
            // if T is not a subtype of S and one of the following conditions apply.
            if (LambdaUtil.isFunctionalType(type1)
                && !TypeConversionUtil.erasure(type1).isAssignableFrom(type2)
                && LambdaUtil.isFunctionalType(type2)
                && !TypeConversionUtil.erasure(type2).isAssignableFrom(type1)) {
              types1AtSite[Math.min(i, types1.length - 1)] = PsiType.NULL;
              types2AtSite[Math.min(i, types2.length - 1)] = PsiType.NULL;
              toCompareFunctional = true;
            }
          }
        }

        if (toCompareFunctional) {
          final boolean applicable12ignoreFunctionalType =
              isApplicableTo(
                  types2AtSite,
                  method1,
                  languageLevel,
                  varargsPosition,
                  calculateMethodSubstitutor(
                      typeParameters1,
                      method1,
                      siteSubstitutor1,
                      types1,
                      types2AtSite,
                      languageLevel),
                  null);
          final boolean applicable21ignoreFunctionalType =
              isApplicableTo(
                  types1AtSite,
                  method2,
                  languageLevel,
                  varargsPosition,
                  calculateMethodSubstitutor(
                      typeParameters2,
                      method2,
                      siteSubstitutor2,
                      types2,
                      types1AtSite,
                      languageLevel),
                  null);

          if (applicable12ignoreFunctionalType || applicable21ignoreFunctionalType) {
            Specifics specifics = null;
            for (int i = 0; i < getActualParametersLength(); i++) {
              if (types1AtSite[Math.min(i, types1.length - 1)] == PsiType.NULL
                  && types2AtSite[Math.min(i, types2.length - 1)] == PsiType.NULL) {
                Specifics specific =
                    isFunctionalTypeMoreSpecific(
                        info1, info2, ((PsiExpressionList) myArgumentsList).getExpressions()[i], i);
                if (specific == Specifics.NEITHER) {
                  specifics = Specifics.NEITHER;
                  break;
                }

                if (specifics == null) {
                  specifics = specific;
                } else if (specifics != specific) {
                  specifics = Specifics.NEITHER;
                  break;
                }
              }
            }

            if (!applicable12ignoreFunctionalType && applicable21ignoreFunctionalType) {
              return specifics == Specifics.FIRST ? Specifics.FIRST : Specifics.NEITHER;
            }

            if (!applicable21ignoreFunctionalType && applicable12ignoreFunctionalType) {
              return specifics == Specifics.SECOND ? Specifics.SECOND : Specifics.NEITHER;
            }

            return specifics;
          }
        }
      }
    } else if (varargsPosition) {
      final PsiType lastParamType1 = classSubstitutor1.substitute(types1[types1.length - 1]);
      final PsiType lastParamType2 = classSubstitutor2.substitute(types2[types1.length - 1]);
      final boolean assignable1 = TypeConversionUtil.isAssignable(lastParamType2, lastParamType1);
      final boolean assignable2 = TypeConversionUtil.isAssignable(lastParamType1, lastParamType2);
      if (assignable1 && !assignable2) {
        return Specifics.FIRST;
      }
      if (assignable2 && !assignable1) {
        return Specifics.SECOND;
      }
    }

    if (class1 != class2) {
      if (class2.isInheritor(class1, true) || class1.isInterface() && !class2.isInterface()) {
        if (MethodSignatureUtil.isSubsignature(
            method1.getSignature(info1.getSubstitutor(false)),
            method2.getSignature(info2.getSubstitutor(false)))) {
          return Specifics.SECOND;
        } else if (method1.hasModifierProperty(PsiModifier.STATIC)
            && method2.hasModifierProperty(PsiModifier.STATIC)
            && boxingHappened[0] == 0) {
          return Specifics.SECOND;
        }
      } else if (MethodSignatureUtil.areErasedParametersEqual(
              method1.getSignature(PsiSubstitutor.EMPTY),
              method2.getSignature(PsiSubstitutor.EMPTY))
          && MethodSignatureUtil.isSubsignature(
              method2.getSignature(info2.getSubstitutor(false)),
              method1.getSignature(info1.getSubstitutor(false)))) {
        return Specifics.FIRST;
      } else if (class1.isInheritor(class2, true) || class2.isInterface()) {
        if (method1.hasModifierProperty(PsiModifier.STATIC)
            && method2.hasModifierProperty(PsiModifier.STATIC)
            && boxingHappened[0] == 0) {
          return Specifics.FIRST;
        }
      }
    }

    final boolean raw1 = PsiUtil.isRawSubstitutor(method1, classSubstitutor1);
    final boolean raw2 = PsiUtil.isRawSubstitutor(method2, classSubstitutor2);
    if (raw1 ^ raw2) {
      return raw1 ? Specifics.SECOND : Specifics.FIRST;
    }

    final boolean varargs1 = info1.isVarargs();
    final boolean varargs2 = info2.isVarargs();
    if (varargs1 ^ varargs2) {
      return varargs1 ? Specifics.SECOND : Specifics.FIRST;
    }

    return Specifics.NEITHER;
  }
 private static boolean emptyOrRaw(PsiClass containingClass, PsiSubstitutor psiSubstitutor) {
   return PsiUtil.isRawSubstitutor(containingClass, psiSubstitutor)
       || psiSubstitutor.getSubstitutionMap().isEmpty();
 }
  private static PsiMethod generateDelegateMethod(
      PsiMethod method,
      PsiClass superClass,
      PsiSubstitutor substitutor,
      boolean keepParameterAnnotations) {
    final LightMethodBuilder builder =
        new LightMethodBuilder(
            superClass.getManager(), GroovyFileType.GROOVY_LANGUAGE, method.getName());
    builder.setContainingClass(superClass);
    builder.setMethodReturnType(substitutor.substitute(method.getReturnType()));
    builder.setNavigationElement(method);
    builder.addModifier(PsiModifier.PUBLIC);

    final PsiTypeParameter[] typeParameters = method.getTypeParameters();

    final PsiClass containingClass = method.getContainingClass();
    boolean isRaw =
        containingClass != null && PsiUtil.isRawSubstitutor(containingClass, substitutor);
    if (isRaw) {
      substitutor =
          JavaPsiFacade.getInstance(method.getProject())
              .getElementFactory()
              .createRawSubstitutor(substitutor, typeParameters);
    }

    if (!isRaw) {
      for (PsiTypeParameter typeParameter : typeParameters) {
        builder.addTypeParameter(typeParameter);
      }
    }

    final PsiParameter[] originalParameters = method.getParameterList().getParameters();

    for (int i = 0; i < originalParameters.length; i++) {
      PsiParameter originalParameter = originalParameters[i];
      PsiType type;
      if (isRaw) {
        type = TypeConversionUtil.erasure(substitutor.substitute(originalParameter.getType()));
      } else {
        type = substitutor.substitute(originalParameter.getType());
      }
      if (type == null) {
        type = PsiType.getJavaLangObject(superClass.getManager(), superClass.getResolveScope());
      }
      final LightParameter lightParameter =
          new LightParameter(
              StringUtil.notNullize(originalParameter.getName(), "p" + i),
              type,
              builder,
              JavaLanguage.INSTANCE);
      if (keepParameterAnnotations) {
        final PsiCompositeModifierList delegatingModifierList =
            new PsiCompositeModifierList(
                method.getManager(),
                Collections.singletonList(originalParameter.getModifierList()));
        lightParameter.setModifierList(delegatingModifierList);
      }
      builder.addParameter(lightParameter);
    }
    builder.setBaseIcon(JetgroovyIcons.Groovy.Method);

    return new DelegatedMethod(builder, method);
  }