@NotNull
  private static PsiSubstitutor replaceVariables(Collection<InferenceVariable> inferenceVariables) {
    final List<InferenceVariable> targetVars = new ArrayList<InferenceVariable>();
    PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
    final InferenceVariable[] oldVars =
        inferenceVariables.toArray(new InferenceVariable[inferenceVariables.size()]);
    for (InferenceVariable variable : oldVars) {
      final InferenceVariable newVariable =
          new InferenceVariable(
              variable.getCallContext(), variable.getParameter(), variable.getName());
      substitutor =
          substitutor.put(
              variable,
              JavaPsiFacade.getElementFactory(variable.getProject()).createType(newVariable));
      targetVars.add(newVariable);
      if (variable.isThrownBound()) {
        newVariable.setThrownBound();
      }
    }

    for (int i = 0; i < targetVars.size(); i++) {
      InferenceVariable var = targetVars.get(i);
      for (InferenceBound boundType : InferenceBound.values()) {
        for (PsiType bound : oldVars[i].getBounds(boundType)) {
          var.addBound(substitutor.substitute(bound), boundType, null);
        }
      }
    }
    return substitutor;
  }
  /* Guesswork
   */
  @Nullable
  private static PsiSubstitutor getInheritorSubstitutorForNewExpression(
      final PsiClass baseClass,
      final PsiClass inheritor,
      final PsiSubstitutor baseSubstitutor,
      final PsiElement context) {
    final Project project = baseClass.getProject();
    JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
    final PsiResolveHelper resolveHelper = facade.getResolveHelper();
    PsiSubstitutor superSubstitutor =
        TypeConversionUtil.getClassSubstitutor(baseClass, inheritor, PsiSubstitutor.EMPTY);
    if (superSubstitutor == null) return null;
    PsiSubstitutor inheritorSubstitutor = PsiSubstitutor.EMPTY;
    for (PsiTypeParameter inheritorParameter : PsiUtil.typeParametersIterable(inheritor)) {
      for (PsiTypeParameter baseParameter : PsiUtil.typeParametersIterable(baseClass)) {
        final PsiType substituted = superSubstitutor.substitute(baseParameter);
        PsiType arg = baseSubstitutor.substitute(baseParameter);
        if (arg instanceof PsiWildcardType) arg = ((PsiWildcardType) arg).getBound();
        PsiType substitution =
            resolveHelper.getSubstitutionForTypeParameter(
                inheritorParameter, substituted, arg, true, PsiUtil.getLanguageLevel(context));
        if (PsiType.NULL.equals(substitution)) continue;
        if (substitution == null) {
          return facade.getElementFactory().createRawSubstitutor(inheritor);
        }
        inheritorSubstitutor = inheritorSubstitutor.put(inheritorParameter, substitution);
        break;
      }
    }

    return inheritorSubstitutor;
  }
  public static PsiType getType(@NotNull PsiClassObjectAccessExpression classAccessExpression) {
    GlobalSearchScope resolveScope = classAccessExpression.getResolveScope();
    PsiManager manager = classAccessExpression.getManager();
    final PsiClass classClass =
        JavaPsiFacade.getInstance(manager.getProject()).findClass("java.lang.Class", resolveScope);
    if (classClass == null) {
      return new PsiClassReferenceType(
          new LightClassReference(manager, "Class", "java.lang.Class", resolveScope), null);
    }
    if (!PsiUtil.isLanguageLevel5OrHigher(classAccessExpression)) {
      // Raw java.lang.Class
      return JavaPsiFacade.getInstance(manager.getProject())
          .getElementFactory()
          .createType(classClass);
    }

    PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
    PsiType operandType = classAccessExpression.getOperand().getType();
    if (operandType instanceof PsiPrimitiveType && !PsiType.NULL.equals(operandType)) {
      if (PsiType.VOID.equals(operandType)) {
        operandType =
            JavaPsiFacade.getInstance(manager.getProject())
                .getElementFactory()
                .createTypeByFQClassName("java.lang.Void", classAccessExpression.getResolveScope());
      } else {
        operandType = ((PsiPrimitiveType) operandType).getBoxedType(classAccessExpression);
      }
    }
    final PsiTypeParameter[] typeParameters = classClass.getTypeParameters();
    if (typeParameters.length == 1) {
      substitutor = substitutor.put(typeParameters[0], operandType);
    }

    return new PsiImmediateClassType(classClass, substitutor);
  }
  @NotNull
  private PsiSubstitutor mapSubstitutor(
      PsiClass originalClass, PsiClass mappedClass, PsiSubstitutor substitutor) {
    PsiTypeParameter[] typeParameters = mappedClass.getTypeParameters();
    PsiTypeParameter[] originalTypeParameters = originalClass.getTypeParameters();
    if (typeParameters.length != originalTypeParameters.length) {
      if (originalTypeParameters.length == 0) {
        return JavaPsiFacade.getElementFactory(mappedClass.getProject())
            .createRawSubstitutor(mappedClass);
      }
      return substitutor;
    }

    Map<PsiTypeParameter, PsiType> substitutionMap = substitutor.getSubstitutionMap();

    PsiSubstitutor mappedSubstitutor = PsiSubstitutor.EMPTY;
    for (int i = 0; i < originalTypeParameters.length; i++) {
      if (!substitutionMap.containsKey(originalTypeParameters[i])) continue;

      PsiType originalSubstitute = substitutor.substitute(originalTypeParameters[i]);
      if (originalSubstitute != null) {
        PsiType substitute = mapType(originalSubstitute);
        if (substitute == null) return substitutor;

        mappedSubstitutor = mappedSubstitutor.put(typeParameters[i], substitute);
      } else {
        mappedSubstitutor = mappedSubstitutor.put(typeParameters[i], null);
      }
    }

    if (mappedClass.hasModifierProperty(PsiModifier.STATIC)) {
      return mappedSubstitutor;
    }
    PsiClass mappedContaining = mappedClass.getContainingClass();
    PsiClass originalContaining = originalClass.getContainingClass();
    //noinspection DoubleNegation
    if ((mappedContaining != null) != (originalContaining != null)) {
      return substitutor;
    }

    if (mappedContaining != null) {
      return mappedSubstitutor.putAll(
          mapSubstitutor(originalContaining, mappedContaining, substitutor));
    }

    return mappedSubstitutor;
  }
  @Nullable
  public static PsiType getSubstitutedType(@Nullable PsiParameter parameter) {
    if (parameter == null) return null;

    final PsiType type = getType(parameter);

    if (type instanceof PsiArrayType) {
      return type;
    }

    final PsiClassType.ClassResolveResult result = PsiUtil.resolveGenericsClassInType(type);
    final PsiClass psiClass = result.getElement();
    if (psiClass == null) return type;

    final Set<PsiTypeParameter> usedTypeParameters = new HashSet<PsiTypeParameter>();
    RefactoringUtil.collectTypeParameters(usedTypeParameters, parameter);
    for (Iterator<PsiTypeParameter> iterator = usedTypeParameters.iterator();
        iterator.hasNext(); ) {
      PsiTypeParameter usedTypeParameter = iterator.next();
      if (parameter.getDeclarationScope() != usedTypeParameter.getOwner()) {
        iterator.remove();
      }
    }

    PsiSubstitutor subst = PsiSubstitutor.EMPTY;
    for (PsiTypeParameter usedTypeParameter : usedTypeParameters) {
      subst =
          subst.put(usedTypeParameter, TypeConversionUtil.typeParameterErasure(usedTypeParameter));
    }

    PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
    final Map<PsiTypeParameter, PsiType> typeMap = result.getSubstitutor().getSubstitutionMap();
    for (PsiTypeParameter typeParameter : typeMap.keySet()) {
      final PsiType psiType = typeMap.get(typeParameter);
      substitutor =
          substitutor.put(typeParameter, psiType != null ? subst.substitute(psiType) : null);
    }

    if (psiClass instanceof PsiTypeParameter) {
      return subst.substitute((PsiTypeParameter) psiClass);
    } else {
      return JavaPsiFacade.getElementFactory(parameter.getProject())
          .createType(psiClass, substitutor);
    }
  }
  private static CompoundInitialState createState(InferenceSession topLevelSession) {
    final PsiSubstitutor topInferenceSubstitutor =
        replaceVariables(topLevelSession.getInferenceVariables());
    final Map<PsiElement, InitialInferenceState> nestedStates =
        new LinkedHashMap<PsiElement, InitialInferenceState>();

    final InferenceSessionContainer copy =
        new InferenceSessionContainer() {
          @Override
          public PsiSubstitutor findNestedSubstitutor(
              PsiElement arg, @Nullable PsiSubstitutor defaultSession) {
            // for the case foo(bar(a -> m())): top level inference won't touch lambda "a -> m()"
            // for the case foo(a -> bar(b -> m())): top level inference would go till nested lambda
            // "b -> m()" and the state from top level could be found here by "bar(b -> m())"
            // but proceeding with additional constraints from saved point would produce new
            // expression constraints with different inference variables (could be found in
            // myNestedSessions)
            // which won't be found in the system if we won't reject stored sessions in such cases
            final PsiSubstitutor substitutor = super.findNestedSubstitutor(arg, null);
            if (substitutor != null) {
              return substitutor;
            }

            final InitialInferenceState state =
                nestedStates.get(PsiTreeUtil.getParentOfType(arg, PsiCall.class));
            if (state != null) {
              return state.getInferenceSubstitutor();
            }
            return super.findNestedSubstitutor(arg, defaultSession);
          }
        };
    final Map<PsiElement, InferenceSession> nestedSessions =
        topLevelSession.getInferenceSessionContainer().myNestedSessions;
    for (Map.Entry<PsiElement, InferenceSession> entry : nestedSessions.entrySet()) {
      nestedStates.put(
          entry.getKey(),
          entry
              .getValue()
              .createInitialState(
                  copy, topLevelSession.getInferenceVariables(), topInferenceSubstitutor));
    }

    PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
    for (InferenceVariable variable : topLevelSession.getInferenceVariables()) {
      final PsiType instantiation = variable.getInstantiation();
      if (instantiation != PsiType.NULL) {
        final PsiClass psiClass =
            PsiUtil.resolveClassInClassTypeOnly(topInferenceSubstitutor.substitute(variable));
        if (psiClass instanceof InferenceVariable) {
          substitutor = substitutor.put((PsiTypeParameter) psiClass, instantiation);
        }
      }
    }

    return new CompoundInitialState(substitutor, nestedStates);
  }
 @NotNull
 private static PsiSubstitutor calculateMethodSubstitutor(
     @NotNull PsiTypeParameter[] typeParameters,
     @NotNull PsiMethod method,
     @NotNull PsiSubstitutor siteSubstitutor,
     @NotNull PsiType[] types1,
     @NotNull PsiType[] types2,
     @NotNull LanguageLevel languageLevel) {
   PsiSubstitutor substitutor =
       PsiResolveHelper.SERVICE
           .getInstance(method.getProject())
           .inferTypeArguments(typeParameters, types1, types2, languageLevel);
   for (PsiTypeParameter typeParameter : PsiUtil.typeParametersIterable(method)) {
     ProgressManager.checkCanceled();
     LOG.assertTrue(typeParameter != null);
     if (!substitutor.getSubstitutionMap().containsKey(typeParameter)) {
       PsiType type = siteSubstitutor.substitute(typeParameter);
       if (type instanceof PsiClassType && typeParameter.getOwner() == method) {
         final PsiClass aClass = ((PsiClassType) type).resolve();
         if (aClass instanceof PsiTypeParameter
             && ((PsiTypeParameter) aClass).getOwner() == method) {
           type = TypeConversionUtil.erasure(type, siteSubstitutor);
         }
       }
       substitutor = substitutor.put(typeParameter, type);
     } else {
       final PsiType type = substitutor.substitute(typeParameter);
       if (type instanceof PsiClassType) {
         final PsiClass aClass = ((PsiClassType) type).resolve();
         if (aClass instanceof PsiTypeParameter) {
           substitutor =
               substitutor.put(
                   typeParameter,
                   JavaPsiFacade.getElementFactory(aClass.getProject())
                       .createType(aClass, siteSubstitutor));
         }
       }
     }
   }
   return substitutor;
 }
 private PsiSubstitutor upDownSuperClassSubstitutor() {
   PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
   for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable(mySourceClass)) {
     substitutor = substitutor.put(parameter, null);
   }
   final Map<PsiTypeParameter, PsiType> substitutionMap =
       TypeConversionUtil.getSuperClassSubstitutor(
               myTargetSuperClass, mySourceClass, PsiSubstitutor.EMPTY)
           .getSubstitutionMap();
   for (PsiTypeParameter parameter : substitutionMap.keySet()) {
     final PsiType type = substitutionMap.get(parameter);
     final PsiClass resolvedClass = PsiUtil.resolveClassInType(type);
     if (resolvedClass instanceof PsiTypeParameter) {
       substitutor =
           substitutor.put(
               (PsiTypeParameter) resolvedClass,
               JavaPsiFacade.getElementFactory(myProject).createType(parameter));
     }
   }
   return substitutor;
 }
Beispiel #9
0
 private void getVariantsFromQualifier(@NotNull GrExpression qualifier) {
   Project project = qualifier.getProject();
   PsiType qualifierType = qualifier.getType();
   final ResolveState state = ResolveState.initial();
   if (qualifierType == null || qualifierType == PsiType.VOID) {
     if (qualifier instanceof GrReferenceExpression) {
       PsiElement resolved = ((GrReferenceExpression) qualifier).resolve();
       if (resolved instanceof PsiPackage || resolved instanceof PsiVariable) {
         resolved.processDeclarations(myProcessor, state, null, myRefExpr);
         return;
       }
     }
     getVariantsFromQualifierType(TypesUtil.getJavaLangObject(qualifier), project);
   } else if (qualifierType instanceof PsiIntersectionType) {
     for (PsiType conjunct : ((PsiIntersectionType) qualifierType).getConjuncts()) {
       getVariantsFromQualifierType(conjunct, project);
     }
   } else if (qualifierType instanceof GrTraitType) {
     GrTypeDefinition definition = ((GrTraitType) qualifierType).getMockTypeDefinition();
     if (definition != null) {
       PsiClassType classType = JavaPsiFacade.getElementFactory(project).createType(definition);
       getVariantsFromQualifierType(classType, project);
     } else {
       getVariantsFromQualifierType(((GrTraitType) qualifierType).getExprType(), project);
       for (PsiClassType traitType : ((GrTraitType) qualifierType).getTraitTypes()) {
         getVariantsFromQualifierType(traitType, project);
       }
     }
   } else {
     getVariantsFromQualifierType(qualifierType, project);
     if (qualifier instanceof GrReferenceExpression
         && !PsiUtil.isSuperReference(qualifier)
         && !PsiUtil.isInstanceThisRef(qualifier)) {
       PsiElement resolved = ((GrReferenceExpression) qualifier).resolve();
       if (resolved instanceof PsiClass) { // //omitted .class
         GlobalSearchScope scope = myRefExpr.getResolveScope();
         PsiClass javaLangClass = PsiUtil.getJavaLangClass(resolved, scope);
         if (javaLangClass != null) {
           PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
           PsiTypeParameter[] typeParameters = javaLangClass.getTypeParameters();
           if (typeParameters.length == 1) {
             substitutor = substitutor.put(typeParameters[0], qualifierType);
           }
           PsiType javaLangClassType =
               JavaPsiFacade.getElementFactory(myRefExpr.getProject())
                   .createType(javaLangClass, substitutor);
           ResolveUtil.processAllDeclarations(javaLangClassType, myProcessor, state, myRefExpr);
         }
       }
     }
   }
 }
    public PsiType substitute(final PsiType t) {
      if (t instanceof PsiWildcardType) {
        final PsiWildcardType wcType = (PsiWildcardType) t;
        final PsiType bound = wcType.getBound();

        if (bound == null) {
          return t;
        }

        final PsiManager manager = PsiManager.getInstance(myProject);
        final PsiType subst = substitute(bound);
        if (subst == null) return null;
        return subst instanceof PsiWildcardType
            ? subst
            : wcType.isExtends()
                ? PsiWildcardType.createExtends(manager, subst)
                : PsiWildcardType.createSuper(manager, subst);
      } else if (t instanceof PsiTypeVariable) {
        final PsiType b = apply(t);

        if (b instanceof Bottom || b instanceof PsiTypeVariable) {
          return null;
        }

        return substitute(b);
      } else if (t instanceof Bottom) {
        return null;
      } else if (t instanceof PsiArrayType) {
        return substitute(((PsiArrayType) t).getComponentType()).createArrayType();
      } else if (t instanceof PsiClassType) {
        final PsiClassType.ClassResolveResult result = ((PsiClassType) t).resolveGenerics();

        final PsiClass aClass = result.getElement();
        final PsiSubstitutor aSubst = result.getSubstitutor();

        if (aClass != null) {
          PsiSubstitutor theSubst = PsiSubstitutor.EMPTY;

          for (final PsiTypeParameter parm : aSubst.getSubstitutionMap().keySet()) {
            final PsiType type = aSubst.substitute(parm);

            theSubst = theSubst.put(parm, substitute(type));
          }

          return JavaPsiFacade.getInstance(aClass.getProject())
              .getElementFactory()
              .createType(aClass, theSubst);
        }
      }
      return t;
    }
  @NotNull
  @Override
  public PsiClassType getArrayClassType(
      @NotNull final PsiType componentType, @NotNull final LanguageLevel languageLevel) {
    final PsiClass arrayClass = getArrayClass(languageLevel);
    final PsiTypeParameter[] typeParameters = arrayClass.getTypeParameters();

    PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
    if (typeParameters.length == 1) {
      substitutor = substitutor.put(typeParameters[0], componentType);
    }

    return createType(arrayClass, substitutor);
  }
  private PsiClassType createEnumType() {
    JavaPsiFacade facade = JavaPsiFacade.getInstance(getProject());
    PsiClass enumClass = facade.findClass(JAVA_LANG_ENUM, getResolveScope());
    PsiElementFactory factory = facade.getElementFactory();
    if (enumClass != null) {
      PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
      PsiTypeParameter[] typeParameters = enumClass.getTypeParameters();
      if (typeParameters.length == 1) {
        substitutor = substitutor.put(typeParameters[0], factory.createType(this));
      }

      return factory.createType(enumClass, substitutor);
    }
    return TypesUtil.createTypeByFQClassName(JAVA_LANG_ENUM, this);
  }
 @Nullable
 public static PsiType createJavaLangClassType(
     @Nullable PsiType type, Project project, GlobalSearchScope resolveScope) {
   final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
   PsiType result = null;
   PsiClass javaLangClass = facade.findClass(JAVA_LANG_CLASS, resolveScope);
   if (javaLangClass != null) {
     PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
     final PsiTypeParameter[] typeParameters = javaLangClass.getTypeParameters();
     if (typeParameters.length == 1) {
       substitutor = substitutor.put(typeParameters[0], type);
     }
     result = facade.getElementFactory().createType(javaLangClass, substitutor);
   }
   return result;
 }
    public PsiType apply(final PsiType type) {
      if (type instanceof PsiTypeVariable) {
        final PsiType t = myBindings.get(((PsiTypeVariable) type).getIndex());
        return t == null ? type : t;
      } else if (type instanceof PsiArrayType) {
        return apply(((PsiArrayType) type).getComponentType()).createArrayType();
      } else if (type instanceof PsiClassType) {
        final PsiClassType.ClassResolveResult result = Util.resolveType(type);
        final PsiClass theClass = result.getElement();
        final PsiSubstitutor aSubst = result.getSubstitutor();

        PsiSubstitutor theSubst = PsiSubstitutor.EMPTY;

        if (theClass != null) {
          for (final PsiTypeParameter aParm : aSubst.getSubstitutionMap().keySet()) {
            final PsiType aType = aSubst.substitute(aParm);

            theSubst = theSubst.put(aParm, apply(aType));
          }

          return JavaPsiFacade.getInstance(theClass.getProject())
              .getElementFactory()
              .createType(theClass, theSubst);
        } else {
          return type;
        }
      } else if (type instanceof PsiWildcardType) {
        final PsiWildcardType wcType = (PsiWildcardType) type;
        final PsiType bound = wcType.getBound();

        if (bound != null) {
          final PsiType abound = apply(bound);

          if (abound instanceof PsiWildcardType) {
            return null;
          }

          return wcType.isExtends()
              ? PsiWildcardType.createExtends(PsiManager.getInstance(myProject), abound)
              : PsiWildcardType.createSuper(PsiManager.getInstance(myProject), abound);
        }

        return type;
      } else {
        return type;
      }
    }
 private static PsiSubstitutor getSuperClassSubstitutor(
     final PsiType superClassType,
     final PsiClassType targetClassType,
     final PsiResolveHelper resolveHelper,
     PsiClass targetClass) {
   PsiSubstitutor subst = PsiSubstitutor.EMPTY;
   for (PsiTypeParameter typeParameter : PsiUtil.typeParametersIterable(targetClass)) {
     subst =
         subst.put(
             typeParameter,
             resolveHelper.getSubstitutionForTypeParameter(
                 typeParameter,
                 targetClassType,
                 superClassType,
                 false,
                 PsiUtil.getLanguageLevel(targetClass)));
   }
   return subst;
 }
  @Nullable
  private static PsiSubstitutor getSuperMethodSignatureSubstitutorImpl(
      @NotNull MethodSignature methodSignature, @NotNull MethodSignature superSignature) {
    // normalize generic method declarations: correlate type parameters
    // todo: correlate type params by name?
    PsiTypeParameter[] methodTypeParameters = methodSignature.getTypeParameters();
    PsiTypeParameter[] superTypeParameters = superSignature.getTypeParameters();

    // both methods are parameterized and number of parameters mismatch
    if (methodTypeParameters.length != superTypeParameters.length) return null;

    PsiSubstitutor result = superSignature.getSubstitutor();
    for (int i = 0; i < methodTypeParameters.length; i++) {
      PsiTypeParameter methodTypeParameter = methodTypeParameters[i];
      PsiElementFactory factory =
          JavaPsiFacade.getInstance(methodTypeParameter.getProject()).getElementFactory();
      result = result.put(superTypeParameters[i], factory.createType(methodTypeParameter));
    }

    return result;
  }
 private static PsiClassType getEnumSuperType(
     @NotNull PsiClass psiClass, @NotNull PsiElementFactory factory) {
   PsiClassType superType;
   final PsiManager manager = psiClass.getManager();
   final PsiClass enumClass =
       JavaPsiFacade.getInstance(manager.getProject())
           .findClass("java.lang.Enum", psiClass.getResolveScope());
   if (enumClass == null) {
     try {
       superType = (PsiClassType) factory.createTypeFromText("java.lang.Enum", null);
     } catch (IncorrectOperationException e) {
       superType = null;
     }
   } else {
     final PsiTypeParameter[] typeParameters = enumClass.getTypeParameters();
     PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
     if (typeParameters.length == 1) {
       substitutor = substitutor.put(typeParameters[0], factory.createType(psiClass));
     }
     superType = new PsiImmediateClassType(enumClass, substitutor);
   }
   return superType;
 }
  public static PsiSubstitutor combineSubstitutors(
      PsiSubstitutor substitutor1, PsiSubstitutor substitutor2) {
    if (substitutor1 == PsiSubstitutor.EMPTY) return substitutor2;
    Set<PsiTypeParameter> parameters1 = substitutor1.getSubstitutionMap().keySet();
    final PsiTypeParameter[] typeParameters =
        parameters1.toArray(new PsiTypeParameter[parameters1.size()]);
    for (PsiTypeParameter typeParameter : typeParameters) {
      final PsiType type = substitutor1.substitute(typeParameter);
      PsiType otherSubstituted;
      if (type instanceof PsiClassType) {
        final PsiClass resolved = ((PsiClassType) type).resolve();
        otherSubstituted =
            resolved instanceof PsiTypeParameter
                ? substitutor2.substitute((PsiTypeParameter) resolved)
                : substitutor2.substitute(type);
      } else {
        otherSubstituted = substitutor2.substitute(type);
      }

      substitutor1 = substitutor1.put(typeParameter, otherSubstituted);
    }
    return substitutor1;
  }
Beispiel #19
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;
  }