| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.intellij.psi.impl.source.resolve; |
| |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.projectRoots.JavaSdkVersion; |
| import com.intellij.openapi.projectRoots.JavaVersionService; |
| import com.intellij.openapi.util.Key; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.openapi.util.RecursionGuard; |
| import com.intellij.pom.java.LanguageLevel; |
| import com.intellij.psi.*; |
| import com.intellij.psi.search.GlobalSearchScope; |
| import com.intellij.psi.util.PsiTypesUtil; |
| import com.intellij.psi.util.PsiUtil; |
| import com.intellij.psi.util.TypeConversionUtil; |
| import com.intellij.util.ArrayUtil; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| /** |
| * User: anna |
| */ |
| public class PsiOldInferenceHelper implements PsiInferenceHelper { |
| private static final Logger LOG = Logger.getInstance("#" + PsiOldInferenceHelper.class.getName()); |
| public static final Pair<PsiType,ConstraintType> RAW_INFERENCE = new Pair<PsiType, ConstraintType>(null, ConstraintType.EQUALS); |
| private final PsiManager myManager; |
| |
| public PsiOldInferenceHelper(PsiManager manager) { |
| myManager = manager; |
| } |
| |
| private Pair<PsiType, ConstraintType> inferTypeForMethodTypeParameterInner(@NotNull PsiTypeParameter typeParameter, |
| @NotNull PsiParameter[] parameters, |
| @NotNull PsiExpression[] arguments, |
| @NotNull PsiSubstitutor partialSubstitutor, |
| final PsiElement parent, |
| @NotNull ParameterTypeInferencePolicy policy) { |
| PsiType[] paramTypes = PsiType.createArray(arguments.length); |
| PsiType[] argTypes = PsiType.createArray(arguments.length); |
| if (parameters.length > 0) { |
| for (int j = 0; j < argTypes.length; j++) { |
| final PsiExpression argument = arguments[j]; |
| if (argument == null) continue; |
| if (argument instanceof PsiMethodCallExpression && PsiResolveHelper.ourGuard.currentStack().contains(argument)) continue; |
| |
| final RecursionGuard.StackStamp stackStamp = PsiDiamondType.ourDiamondGuard.markStack(); |
| argTypes[j] = argument.getType(); |
| if (!stackStamp.mayCacheNow()) { |
| argTypes[j] = null; |
| continue; |
| } |
| |
| final PsiParameter parameter = parameters[Math.min(j, parameters.length - 1)]; |
| if (j >= parameters.length && !parameter.isVarArgs()) break; |
| paramTypes[j] = parameter.getType(); |
| if (paramTypes[j] instanceof PsiEllipsisType) { |
| paramTypes[j] = ((PsiEllipsisType)paramTypes[j]).getComponentType(); |
| if (arguments.length == parameters.length && |
| argTypes[j] instanceof PsiArrayType && |
| !(((PsiArrayType)argTypes[j]).getComponentType() instanceof PsiPrimitiveType)) { |
| argTypes[j] = ((PsiArrayType)argTypes[j]).getComponentType(); |
| } |
| } |
| } |
| } |
| return inferTypeForMethodTypeParameterInner(typeParameter, paramTypes, argTypes, partialSubstitutor, parent, policy); |
| } |
| |
| private Pair<PsiType, ConstraintType> inferTypeForMethodTypeParameterInner(@NotNull PsiTypeParameter typeParameter, |
| @NotNull PsiType[] paramTypes, |
| @NotNull PsiType[] argTypes, |
| @NotNull PsiSubstitutor partialSubstitutor, |
| @Nullable PsiElement parent, |
| @NotNull ParameterTypeInferencePolicy policy) { |
| PsiWildcardType wildcardToCapture = null; |
| Pair<PsiType, ConstraintType> rawInference = null; |
| PsiType lowerBound = PsiType.NULL; |
| PsiType upperBound = PsiType.NULL; |
| if (paramTypes.length > 0) { |
| for (int j = 0; j < argTypes.length; j++) { |
| PsiType argumentType = argTypes[j]; |
| if (argumentType == null) continue; |
| if (j >= paramTypes.length) break; |
| |
| PsiType parameterType = paramTypes[j]; |
| if (parameterType == null) break; |
| |
| if (parameterType instanceof PsiEllipsisType) { |
| parameterType = ((PsiEllipsisType)parameterType).getComponentType(); |
| if (argTypes.length == paramTypes.length && argumentType instanceof PsiArrayType && !(((PsiArrayType)argumentType).getComponentType() instanceof PsiPrimitiveType)) { |
| argumentType = ((PsiArrayType)argumentType).getComponentType(); |
| } |
| } |
| final Pair<PsiType,ConstraintType> currentSubstitution; |
| currentSubstitution = getSubstitutionForTypeParameterConstraint(typeParameter, parameterType, |
| argumentType, true, PsiUtil.getLanguageLevel(typeParameter)); |
| if (currentSubstitution == null) continue; |
| if (currentSubstitution == FAILED_INFERENCE) { |
| return getFailedInferenceConstraint(typeParameter); |
| } |
| |
| final ConstraintType constraintType = currentSubstitution.getSecond(); |
| final PsiType type = currentSubstitution.getFirst(); |
| if (type == null) { |
| rawInference = RAW_INFERENCE; |
| continue; |
| } |
| switch(constraintType) { |
| case EQUALS: |
| if (!(type instanceof PsiWildcardType)) return currentSubstitution; |
| if (wildcardToCapture != null) return getFailedInferenceConstraint(typeParameter); |
| wildcardToCapture = (PsiWildcardType) type; |
| break; |
| case SUPERTYPE: |
| if (PsiType.NULL.equals(lowerBound)) { |
| lowerBound = type; |
| } |
| else if (!lowerBound.equals(type)) { |
| lowerBound = GenericsUtil.getLeastUpperBound(lowerBound, type, myManager); |
| if (lowerBound == null) return getFailedInferenceConstraint(typeParameter); |
| } |
| break; |
| case SUBTYPE: |
| if (PsiType.NULL.equals(upperBound) || TypeConversionUtil.isAssignable(upperBound, type)) { |
| upperBound = type; |
| } |
| } |
| } |
| } |
| |
| if (wildcardToCapture != null) { |
| if (lowerBound != PsiType.NULL) { |
| if (!wildcardToCapture.isAssignableFrom(lowerBound)) return getFailedInferenceConstraint(typeParameter); |
| if (wildcardToCapture.isSuper()) { |
| return new Pair<PsiType, ConstraintType>(wildcardToCapture, ConstraintType.SUPERTYPE); |
| } |
| lowerBound = GenericsUtil.getLeastUpperBound(lowerBound, wildcardToCapture, myManager); |
| } |
| else { |
| if (upperBound != PsiType.NULL && !upperBound.isAssignableFrom(wildcardToCapture)) return getFailedInferenceConstraint(typeParameter); |
| return new Pair<PsiType, ConstraintType>(wildcardToCapture, ConstraintType.EQUALS); |
| } |
| } |
| |
| if (rawInference != null) return rawInference; |
| if (lowerBound != PsiType.NULL) return Pair.create(lowerBound, ConstraintType.EQUALS); |
| |
| if (parent != null) { |
| final Pair<PsiType, ConstraintType> constraint = |
| inferMethodTypeParameterFromParent(typeParameter, partialSubstitutor, parent, policy); |
| if (constraint != null) { |
| if (constraint.getSecond() != ConstraintType.SUBTYPE) { |
| return constraint; |
| } |
| |
| if (upperBound != PsiType.NULL) { |
| return Pair.create(upperBound, ConstraintType.SUBTYPE); |
| } |
| |
| return constraint; |
| } |
| } |
| |
| if (upperBound != PsiType.NULL) return Pair.create(upperBound, ConstraintType.SUBTYPE); |
| return null; |
| } |
| |
| private static Pair<PsiType, ConstraintType> getFailedInferenceConstraint(@NotNull PsiTypeParameter typeParameter) { |
| return new Pair<PsiType, ConstraintType>(JavaPsiFacade.getInstance(typeParameter.getProject()).getElementFactory().createType(typeParameter), ConstraintType.EQUALS); |
| } |
| |
| @Override |
| public PsiType inferTypeForMethodTypeParameter(@NotNull final PsiTypeParameter typeParameter, |
| @NotNull final PsiParameter[] parameters, |
| @NotNull PsiExpression[] arguments, |
| @NotNull PsiSubstitutor partialSubstitutor, |
| PsiElement parent, |
| @NotNull final ParameterTypeInferencePolicy policy) { |
| |
| final Pair<PsiType, ConstraintType> constraint = |
| inferTypeForMethodTypeParameterInner(typeParameter, parameters, arguments, partialSubstitutor, parent, policy); |
| if (constraint == null) return PsiType.NULL; |
| return constraint.getFirst(); |
| } |
| |
| @NotNull |
| @Override |
| public PsiSubstitutor inferTypeArguments(@NotNull PsiTypeParameter[] typeParameters, |
| @NotNull PsiParameter[] parameters, |
| @NotNull PsiExpression[] arguments, |
| @NotNull PsiSubstitutor partialSubstitutor, |
| @NotNull PsiElement parent, |
| @NotNull ParameterTypeInferencePolicy policy, |
| @NotNull LanguageLevel languageLevel) { |
| PsiType[] substitutions = PsiType.createArray(typeParameters.length); |
| @SuppressWarnings("unchecked") |
| Pair<PsiType, ConstraintType>[] constraints = new Pair[typeParameters.length]; |
| for (int i = 0; i < typeParameters.length; i++) { |
| if (substitutions[i] != null) continue; |
| final Pair<PsiType, ConstraintType> constraint = |
| inferTypeForMethodTypeParameterInner(typeParameters[i], parameters, arguments, partialSubstitutor, null, policy); |
| constraints[i] = constraint; |
| if (constraint != null && constraint.getSecond() != ConstraintType.SUBTYPE) { |
| substitutions[i] = constraint.getFirst(); |
| |
| if (substitutions[i] != null && languageLevel.isAtLeast(LanguageLevel.JDK_1_8)) { //try once more |
| partialSubstitutor = partialSubstitutor.put(typeParameters[i], substitutions[i]); |
| i = -1; |
| } |
| } |
| } |
| |
| for (int i = 0; i < typeParameters.length; i++) { |
| PsiTypeParameter typeParameter = typeParameters[i]; |
| if (substitutions[i] == null) { |
| PsiType substitutionFromBounds = PsiType.NULL; |
| OtherParameters: |
| for (int j = 0; j < typeParameters.length; j++) { |
| if (i != j) { |
| PsiTypeParameter other = typeParameters[j]; |
| final PsiType otherSubstitution = substitutions[j]; |
| if (otherSubstitution == null) continue; |
| final PsiClassType[] bounds = other.getExtendsListTypes(); |
| for (PsiClassType bound : bounds) { |
| final PsiType substitutedBound = partialSubstitutor.substitute(bound); |
| final Pair<PsiType, ConstraintType> currentConstraint = |
| getSubstitutionForTypeParameterConstraint(typeParameter, substitutedBound, otherSubstitution, true, languageLevel); |
| if (currentConstraint == null) continue; |
| final PsiType currentSubstitution = currentConstraint.getFirst(); |
| final ConstraintType currentConstraintType = currentConstraint.getSecond(); |
| if (currentConstraintType == ConstraintType.EQUALS) { |
| substitutionFromBounds = currentSubstitution; |
| if (currentSubstitution == null) { |
| constraints[i] = FAILED_INFERENCE; |
| } |
| break OtherParameters; |
| } |
| else if (currentConstraintType == ConstraintType.SUPERTYPE && !JavaVersionService.getInstance().isAtLeast(parent, JavaSdkVersion.JDK_1_7)) { |
| if (PsiType.NULL.equals(substitutionFromBounds)) { |
| substitutionFromBounds = currentSubstitution; |
| } |
| else { |
| substitutionFromBounds = GenericsUtil.getLeastUpperBound(substitutionFromBounds, currentSubstitution, myManager); |
| } |
| } |
| } |
| } |
| } |
| |
| if (substitutionFromBounds != PsiType.NULL) substitutions[i] = substitutionFromBounds; |
| } |
| } |
| |
| for (int i = 0; i < typeParameters.length; i++) { |
| PsiTypeParameter typeParameter = typeParameters[i]; |
| PsiType substitution = substitutions[i]; |
| if (substitution != PsiType.NULL) { |
| partialSubstitutor = partialSubstitutor.put(typeParameter, substitution); |
| } |
| } |
| |
| for (int i = 0; i < typeParameters.length; i++) { |
| PsiTypeParameter typeParameter = typeParameters[i]; |
| PsiType substitution = substitutions[i]; |
| if (substitution != null) continue; |
| |
| Pair<PsiType, ConstraintType> constraint = constraints[i]; |
| if (constraint == null) { |
| constraint = inferMethodTypeParameterFromParent(typeParameter, partialSubstitutor, parent, policy); |
| } |
| else if (constraint.getSecond() == ConstraintType.SUBTYPE) { |
| Pair<PsiType, ConstraintType> otherConstraint = |
| inferMethodTypeParameterFromParent(typeParameter, partialSubstitutor, parent, policy); |
| if (otherConstraint != null) { |
| if (otherConstraint.getSecond() == ConstraintType.EQUALS || otherConstraint.getSecond() == ConstraintType.SUPERTYPE || |
| compareSubtypes(constraint.getFirst(), otherConstraint.getFirst())) { |
| constraint = otherConstraint; |
| } |
| } |
| } |
| |
| if (constraint != null) { |
| substitution = constraint.getFirst(); |
| } |
| |
| if (substitution == null) { |
| PsiElementFactory factory = JavaPsiFacade.getInstance(myManager.getProject()).getElementFactory(); |
| return factory.createRawSubstitutor(partialSubstitutor, typeParameters); |
| } |
| if (substitution != PsiType.NULL) { |
| partialSubstitutor = partialSubstitutor.put(typeParameter, substitution); |
| } |
| } |
| return partialSubstitutor; |
| } |
| |
| private static boolean compareSubtypes(final PsiType type, final PsiType parentType) { |
| return type != null && parentType != null && TypeConversionUtil.isAssignable(type, parentType); |
| } |
| |
| @Override |
| @NotNull |
| public PsiSubstitutor inferTypeArguments(@NotNull PsiTypeParameter[] typeParameters, |
| @NotNull PsiType[] leftTypes, |
| @NotNull PsiType[] rightTypes, |
| @NotNull LanguageLevel languageLevel) { |
| if (leftTypes.length != rightTypes.length) throw new IllegalArgumentException("Types must be of the same length"); |
| PsiSubstitutor substitutor = PsiSubstitutor.EMPTY; |
| for (PsiTypeParameter typeParameter : typeParameters) { |
| PsiType substitution = PsiType.NULL; |
| PsiType lowerBound = PsiType.NULL; |
| for (int i1 = 0; i1 < leftTypes.length; i1++) { |
| PsiType leftType = leftTypes[i1]; |
| PsiType rightType = rightTypes[i1]; |
| final Pair<PsiType, ConstraintType> constraint = |
| getSubstitutionForTypeParameterConstraint(typeParameter, leftType, rightType, true, languageLevel); |
| if (constraint != null) { |
| final ConstraintType constraintType = constraint.getSecond(); |
| final PsiType current = constraint.getFirst(); |
| if (constraintType == ConstraintType.EQUALS) { |
| substitution = current; |
| break; |
| } |
| else if (constraintType == ConstraintType.SUBTYPE) { |
| if (PsiType.NULL.equals(substitution)) { |
| substitution = current; |
| } |
| else { |
| substitution = GenericsUtil.getLeastUpperBound(substitution, current, myManager); |
| } |
| } |
| else { |
| if (PsiType.NULL.equals(lowerBound)) { |
| lowerBound = current; |
| } |
| else { |
| lowerBound = GenericsUtil.getLeastUpperBound(lowerBound, current, myManager); |
| } |
| } |
| } |
| } |
| |
| if (PsiType.NULL.equals(substitution)) { |
| substitution = lowerBound; |
| } |
| |
| if (substitution != PsiType.NULL) { |
| substitutor = substitutor.put(typeParameter, substitution); |
| } |
| } |
| for (int i = 0; i < typeParameters.length; i++) { |
| PsiTypeParameter typeParameter = typeParameters[i]; |
| if (!substitutor.getSubstitutionMap().containsKey(typeParameter)) { |
| PsiType substitutionFromBounds = PsiType.NULL; |
| OtherParameters: |
| for (int j = 0; j < typeParameters.length; j++) { |
| if (i != j) { |
| PsiTypeParameter other = typeParameters[j]; |
| final PsiType otherSubstitution = substitutor.substitute(other); |
| if (otherSubstitution == null) continue; |
| final PsiClassType[] bounds = other.getExtendsListTypes(); |
| for (PsiClassType bound : bounds) { |
| final PsiType substitutedBound = substitutor.substitute(bound); |
| final Pair<PsiType, ConstraintType> currentConstraint = |
| getSubstitutionForTypeParameterConstraint(typeParameter, substitutedBound, otherSubstitution, true, languageLevel); |
| if (currentConstraint == null) continue; |
| final PsiType currentSubstitution = currentConstraint.getFirst(); |
| final ConstraintType currentConstraintType = currentConstraint.getSecond(); |
| if (currentConstraintType == ConstraintType.EQUALS) { |
| substitutionFromBounds = currentSubstitution; |
| break OtherParameters; |
| } |
| else if (currentConstraintType == ConstraintType.SUPERTYPE) { |
| if (PsiType.NULL.equals(substitutionFromBounds)) { |
| substitutionFromBounds = currentSubstitution; |
| } |
| else { |
| substitutionFromBounds = GenericsUtil.getLeastUpperBound(substitutionFromBounds, currentSubstitution, myManager); |
| } |
| } |
| } |
| } |
| } |
| if (substitutionFromBounds != PsiType.NULL) { |
| substitutor = substitutor.put(typeParameter, substitutionFromBounds); |
| } |
| } |
| } |
| return substitutor; |
| } |
| |
| @Nullable |
| private static Pair<PsiType, ConstraintType> processArgType(PsiType arg, final ConstraintType constraintType, |
| final boolean captureWildcard) { |
| if (arg instanceof PsiWildcardType && !captureWildcard) return FAILED_INFERENCE; |
| if (arg != PsiType.NULL) { |
| return Pair.create(arg, constraintType); |
| } |
| return null; |
| } |
| |
| private Pair<PsiType, ConstraintType> inferMethodTypeParameterFromParent(@NotNull PsiTypeParameter typeParameter, |
| @NotNull PsiSubstitutor substitutor, |
| @NotNull PsiElement parent, |
| @NotNull ParameterTypeInferencePolicy policy) { |
| PsiTypeParameterListOwner owner = typeParameter.getOwner(); |
| Pair<PsiType, ConstraintType> substitution = null; |
| if (owner instanceof PsiMethod && parent instanceof PsiCallExpression) { |
| PsiCallExpression methodCall = (PsiCallExpression)parent; |
| substitution = inferMethodTypeParameterFromParent(PsiUtil.skipParenthesizedExprUp(methodCall.getParent()), methodCall, typeParameter, substitutor, policy); |
| } |
| return substitution; |
| } |
| |
| @Override |
| public PsiType getSubstitutionForTypeParameter(PsiTypeParameter typeParam, |
| PsiType param, |
| PsiType arg, |
| boolean isContraVariantPosition, |
| final LanguageLevel languageLevel) { |
| final Pair<PsiType, ConstraintType> constraint = getSubstitutionForTypeParameterConstraint(typeParam, param, arg, isContraVariantPosition, |
| languageLevel); |
| return constraint == null ? PsiType.NULL : constraint.getFirst(); |
| } |
| |
| @Nullable |
| public Pair<PsiType, ConstraintType> getSubstitutionForTypeParameterConstraint(PsiTypeParameter typeParam, |
| PsiType param, |
| PsiType arg, |
| boolean isContraVariantPosition, |
| final LanguageLevel languageLevel) { |
| if (param instanceof PsiArrayType && arg instanceof PsiArrayType) { |
| return getSubstitutionForTypeParameterConstraint(typeParam, ((PsiArrayType)param).getComponentType(), ((PsiArrayType)arg).getComponentType(), |
| isContraVariantPosition, languageLevel); |
| } |
| |
| if (!(param instanceof PsiClassType)) return null; |
| PsiManager manager = myManager; |
| if (arg instanceof PsiPrimitiveType) { |
| if (!JavaVersionService.getInstance().isAtLeast(typeParam, JavaSdkVersion.JDK_1_7) && !isContraVariantPosition) return null; |
| arg = ((PsiPrimitiveType)arg).getBoxedType(typeParam); |
| if (arg == null) return null; |
| } |
| |
| JavaResolveResult paramResult = ((PsiClassType)param).resolveGenerics(); |
| PsiClass paramClass = (PsiClass)paramResult.getElement(); |
| if (typeParam == paramClass) { |
| final PsiClass psiClass = PsiUtil.resolveClassInType(arg); |
| if (arg == null || |
| arg.getDeepComponentType() instanceof PsiPrimitiveType || |
| arg instanceof PsiIntersectionType || |
| (psiClass != null && (isContraVariantPosition || !CommonClassNames.JAVA_LANG_OBJECT.equals(psiClass.getQualifiedName()) || (arg instanceof PsiArrayType)))) { |
| PsiType bound = intersectAllExtends(typeParam, arg); |
| return Pair.create(bound, ConstraintType.SUPERTYPE); |
| } |
| if (psiClass == null && arg instanceof PsiClassType) { |
| return Pair.create(arg, ConstraintType.EQUALS); |
| } |
| return null; |
| } |
| if (paramClass == null) return null; |
| |
| if (!(arg instanceof PsiClassType)) return null; |
| |
| JavaResolveResult argResult = ((PsiClassType)arg).resolveGenerics(); |
| PsiClass argClass = (PsiClass)argResult.getElement(); |
| if (argClass == null) return null; |
| |
| PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory(); |
| PsiType patternType = factory.createType(typeParam); |
| if (isContraVariantPosition) { |
| PsiSubstitutor substitutor = TypeConversionUtil.getClassSubstitutor(paramClass, argClass, argResult.getSubstitutor()); |
| if (substitutor == null) return null; |
| arg = factory.createType(paramClass, substitutor, languageLevel); |
| } |
| else { |
| PsiSubstitutor substitutor = TypeConversionUtil.getClassSubstitutor(argClass, paramClass, paramResult.getSubstitutor()); |
| if (substitutor == null) return null; |
| param = factory.createType(argClass, substitutor, languageLevel); |
| } |
| |
| return getSubstitutionForTypeParameterInner(param, arg, patternType, ConstraintType.SUPERTYPE, 0); |
| } |
| |
| private static PsiType intersectAllExtends(PsiTypeParameter typeParam, PsiType arg) { |
| if (arg == null) return null; |
| PsiClassType[] superTypes = typeParam.getSuperTypes(); |
| PsiType[] erasureTypes = PsiType.createArray(superTypes.length); |
| for (int i = 0; i < superTypes.length; i++) { |
| erasureTypes[i] = TypeConversionUtil.erasure(superTypes[i]); |
| } |
| PsiType[] types = ArrayUtil.append(erasureTypes, arg, PsiType.class); |
| assert types.length != 0; |
| return PsiIntersectionType.createIntersection(types); |
| } |
| |
| //represents the result of failed type inference: in case we failed inferring from parameters, do not perform inference from context |
| private static final Pair<PsiType, ConstraintType> FAILED_INFERENCE = new Pair<PsiType, ConstraintType>(PsiType.NULL, ConstraintType.EQUALS); |
| |
| @Nullable |
| private Pair<PsiType, ConstraintType> getSubstitutionForTypeParameterInner(PsiType param, |
| PsiType arg, |
| PsiType patternType, |
| final ConstraintType constraintType, |
| final int depth) { |
| if (patternType.equals(param)) { |
| return processArgType(arg, constraintType, depth < 2); |
| } |
| |
| if (arg instanceof PsiCapturedWildcardType && (depth < 2 || |
| constraintType != ConstraintType.EQUALS || |
| param instanceof PsiWildcardType)) { |
| arg = ((PsiCapturedWildcardType)arg).getWildcard(); //reopen |
| } |
| |
| if (param instanceof PsiWildcardType) { |
| final PsiWildcardType wildcardParam = (PsiWildcardType)param; |
| final PsiType paramBound = wildcardParam.getBound(); |
| if (paramBound == null) return null; |
| ConstraintType constrType = wildcardParam.isExtends() ? ConstraintType.SUPERTYPE : ConstraintType.SUBTYPE; |
| if (arg instanceof PsiWildcardType) { |
| if (((PsiWildcardType)arg).isExtends() == wildcardParam.isExtends() && ((PsiWildcardType)arg).isBounded() == wildcardParam.isBounded()) { |
| Pair<PsiType, ConstraintType> res = getSubstitutionForTypeParameterInner(paramBound, ((PsiWildcardType)arg).getBound(), |
| patternType, constrType, depth); |
| if (res != null) return res; |
| } |
| } |
| else if (patternType.equals(paramBound)) { |
| Pair<PsiType, ConstraintType> res = getSubstitutionForTypeParameterInner(paramBound, arg, |
| patternType, constrType, depth); |
| if (res != null) return res; |
| } |
| else if (paramBound instanceof PsiArrayType && arg instanceof PsiArrayType) { |
| Pair<PsiType, ConstraintType> res = getSubstitutionForTypeParameterInner(((PsiArrayType) paramBound).getComponentType(), |
| ((PsiArrayType) arg).getComponentType(), |
| patternType, constrType, depth); |
| if (res != null) return res; |
| } |
| else if (paramBound instanceof PsiClassType && arg instanceof PsiClassType) { |
| final PsiClassType.ClassResolveResult boundResult = ((PsiClassType)paramBound).resolveGenerics(); |
| final PsiClass boundClass = boundResult.getElement(); |
| if (boundClass != null) { |
| final PsiClassType.ClassResolveResult argResult = ((PsiClassType)arg).resolveGenerics(); |
| final PsiClass argClass = argResult.getElement(); |
| if (argClass != null) { |
| if (wildcardParam.isExtends()) { |
| PsiSubstitutor superSubstitutor = TypeConversionUtil.getClassSubstitutor(boundClass, argClass, argResult.getSubstitutor()); |
| if (superSubstitutor != null) { |
| for (PsiTypeParameter typeParameter : PsiUtil.typeParametersIterable(boundClass)) { |
| PsiType substituted = superSubstitutor.substitute(typeParameter); |
| if (substituted != null) { |
| Pair<PsiType, ConstraintType> res = getSubstitutionForTypeParameterInner( |
| boundResult.getSubstitutor().substitute(typeParameter), substituted, patternType, ConstraintType.EQUALS, depth + 1); |
| if (res != null) return res; |
| } |
| } |
| } |
| } |
| else { |
| PsiSubstitutor superSubstitutor = TypeConversionUtil.getClassSubstitutor(argClass, boundClass, boundResult.getSubstitutor()); |
| if (superSubstitutor != null) { |
| for (PsiTypeParameter typeParameter : PsiUtil.typeParametersIterable(argClass)) { |
| PsiType substituted = argResult.getSubstitutor().substitute(typeParameter); |
| if (substituted != null) { |
| Pair<PsiType, ConstraintType> res = getSubstitutionForTypeParameterInner( |
| superSubstitutor.substitute(typeParameter), substituted, patternType, ConstraintType.EQUALS, depth + 1); |
| if (res != null) { |
| if (res == FAILED_INFERENCE) continue; |
| return res; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| if (param instanceof PsiArrayType && arg instanceof PsiArrayType) { |
| return getSubstitutionForTypeParameterInner(((PsiArrayType)param).getComponentType(), ((PsiArrayType)arg).getComponentType(), |
| patternType, constraintType, depth); |
| } |
| |
| if (param instanceof PsiClassType && arg instanceof PsiClassType) { |
| PsiClassType.ClassResolveResult paramResult = ((PsiClassType)param).resolveGenerics(); |
| PsiClass paramClass = paramResult.getElement(); |
| if (paramClass == null) return null; |
| |
| PsiClassType.ClassResolveResult argResult = ((PsiClassType)arg).resolveGenerics(); |
| PsiClass argClass = argResult.getElement(); |
| if (argClass != paramClass) { |
| return inferBySubtypingConstraint(patternType, constraintType, depth, paramClass, argClass); |
| } |
| |
| PsiType lowerBound = PsiType.NULL; |
| PsiType upperBound = PsiType.NULL; |
| Pair<PsiType,ConstraintType> wildcardCaptured = null; |
| for (PsiTypeParameter typeParameter : PsiUtil.typeParametersIterable(paramClass)) { |
| PsiType paramType = paramResult.getSubstitutor().substitute(typeParameter); |
| PsiType argType = argResult.getSubstitutor().substituteWithBoundsPromotion(typeParameter); |
| |
| if (wildcardCaptured != null) { |
| boolean alreadyFound = false; |
| for (PsiTypeParameter typeParam : PsiUtil.typeParametersIterable(paramClass)) { |
| if (typeParam != typeParameter && |
| paramType != null && |
| argResult.getSubstitutor().substituteWithBoundsPromotion(typeParam) == argType && |
| paramType.equals(paramResult.getSubstitutor().substitute(typeParam))) { |
| alreadyFound = true; |
| } |
| } |
| if (alreadyFound) continue; |
| } |
| |
| Pair<PsiType,ConstraintType> res = getSubstitutionForTypeParameterInner(paramType, argType, patternType, ConstraintType.EQUALS, depth + 1); |
| |
| if (res != null) { |
| final PsiType type = res.getFirst(); |
| switch (res.getSecond()) { |
| case EQUALS: |
| if (!(type instanceof PsiWildcardType)) return res; |
| if (wildcardCaptured != null) return FAILED_INFERENCE; |
| wildcardCaptured = res; |
| break; |
| case SUPERTYPE: |
| wildcardCaptured = res; |
| if (PsiType.NULL.equals(lowerBound)) { |
| lowerBound = type; |
| } |
| else if (!lowerBound.equals(type)) { |
| lowerBound = GenericsUtil.getLeastUpperBound(lowerBound, type, myManager); |
| if (lowerBound == null) return FAILED_INFERENCE; |
| } |
| break; |
| case SUBTYPE: |
| wildcardCaptured = res; |
| if (PsiType.NULL.equals(upperBound) || TypeConversionUtil.isAssignable(upperBound, type)) { |
| upperBound = type; |
| } |
| } |
| } |
| } |
| |
| if (lowerBound != PsiType.NULL) return Pair.create(lowerBound, ConstraintType.SUPERTYPE); |
| if (upperBound != PsiType.NULL) return Pair.create(upperBound, ConstraintType.SUBTYPE); |
| |
| return wildcardCaptured; |
| } |
| |
| return null; |
| } |
| |
| private static final Key<Boolean> inferSubtyping = Key.create("infer.subtyping.marker"); |
| private Pair<PsiType, ConstraintType> inferBySubtypingConstraint(PsiType patternType, |
| ConstraintType constraintType, |
| int depth, |
| PsiClass paramClass, |
| PsiClass argClass) { |
| if (argClass instanceof PsiTypeParameter && paramClass instanceof PsiTypeParameter && PsiUtil.isLanguageLevel8OrHigher(argClass)) { |
| final Boolean alreadyInferBySubtyping = paramClass.getCopyableUserData(inferSubtyping); |
| if (alreadyInferBySubtyping != null) return null; |
| final PsiClassType[] argExtendsListTypes = argClass.getExtendsListTypes(); |
| final PsiClassType[] paramExtendsListTypes = paramClass.getExtendsListTypes(); |
| if (argExtendsListTypes.length == paramExtendsListTypes.length) { |
| try { |
| paramClass.putCopyableUserData(inferSubtyping, true); |
| for (int i = 0; i < argExtendsListTypes.length; i++) { |
| PsiClassType argBoundType = argExtendsListTypes[i]; |
| PsiClassType paramBoundType = paramExtendsListTypes[i]; |
| final PsiClassType.ClassResolveResult argResolveResult = argBoundType.resolveGenerics(); |
| final PsiClassType.ClassResolveResult paramResolveResult = paramBoundType.resolveGenerics(); |
| final PsiClass paramBoundClass = paramResolveResult.getElement(); |
| final PsiClass argBoundClass = argResolveResult.getElement(); |
| if (argBoundClass != null && paramBoundClass != null && paramBoundClass != argBoundClass) { |
| if (argBoundClass.isInheritor(paramBoundClass, true)) { |
| final PsiSubstitutor superClassSubstitutor = |
| TypeConversionUtil.getSuperClassSubstitutor(paramBoundClass, argBoundClass, argResolveResult.getSubstitutor()); |
| argBoundType = JavaPsiFacade.getElementFactory(argClass.getProject()).createType(paramBoundClass, superClassSubstitutor); |
| } else { |
| return null; |
| } |
| } |
| final Pair<PsiType, ConstraintType> constraint = |
| getSubstitutionForTypeParameterInner(paramBoundType, argBoundType, patternType, constraintType, depth); |
| if (constraint != null) { |
| return constraint; |
| } |
| } |
| } |
| finally { |
| paramClass.putCopyableUserData(inferSubtyping, null); |
| } |
| } |
| } |
| return null; |
| } |
| |
| private Pair<PsiType, ConstraintType> inferMethodTypeParameterFromParent(@NotNull final PsiElement parent, |
| @NotNull PsiExpression methodCall, |
| @NotNull PsiTypeParameter typeParameter, |
| @NotNull PsiSubstitutor substitutor, |
| @NotNull ParameterTypeInferencePolicy policy) { |
| Pair<PsiType, ConstraintType> constraint = null; |
| PsiType expectedType = PsiTypesUtil.getExpectedTypeByParent(methodCall); |
| |
| if (expectedType == null) { |
| if (parent instanceof PsiExpressionList) { |
| final PsiElement pParent = parent.getParent(); |
| if (pParent instanceof PsiCallExpression && parent.equals(((PsiCallExpression)pParent).getArgumentList())) { |
| constraint = policy.inferTypeConstraintFromCallContext(methodCall, (PsiExpressionList)parent, (PsiCallExpression)pParent, typeParameter); |
| } |
| } |
| } |
| |
| final GlobalSearchScope scope = parent.getResolveScope(); |
| PsiType returnType = null; |
| if (constraint == null) { |
| if (expectedType == null) { |
| expectedType = methodCall instanceof PsiCallExpression ? policy.getDefaultExpectedType((PsiCallExpression)methodCall) : null; |
| } |
| |
| returnType = ((PsiMethod)typeParameter.getOwner()).getReturnType(); |
| |
| constraint = |
| getSubstitutionForTypeParameterConstraint(typeParameter, returnType, expectedType, false, PsiUtil.getLanguageLevel(parent)); |
| |
| if (constraint != null) { |
| PsiType guess = constraint.getFirst(); |
| if (guess != null && |
| !guess.equals(PsiType.NULL) && |
| constraint.getSecond() == ConstraintType.SUPERTYPE && |
| guess instanceof PsiIntersectionType && |
| !JavaVersionService.getInstance().isAtLeast(parent, JavaSdkVersion.JDK_1_7)) { |
| for (PsiType conjuct : ((PsiIntersectionType)guess).getConjuncts()) { |
| if (!conjuct.isAssignableFrom(expectedType)) { |
| return FAILED_INFERENCE; |
| } |
| } |
| } |
| } |
| } |
| |
| if (constraint == null) { |
| if (methodCall instanceof PsiCallExpression) { |
| PsiType[] superTypes = typeParameter.getSuperTypes(); |
| if (superTypes.length == 0) return null; |
| final PsiType[] types = PsiType.createArray(superTypes.length); |
| for (int i = 0; i < superTypes.length; i++) { |
| PsiType superType = substitutor.substitute(superTypes[i]); |
| if (superType instanceof PsiClassType && ((PsiClassType)superType).isRaw()) { |
| superType = TypeConversionUtil.erasure(superType); |
| } |
| if (superType == null) superType = PsiType.getJavaLangObject(myManager, scope); |
| types[i] = superType; |
| } |
| return policy.getInferredTypeWithNoConstraint(myManager, PsiIntersectionType.createIntersection(types)); |
| } |
| return null; |
| } |
| PsiType guess = constraint.getFirst(); |
| guess = policy.adjustInferredType(myManager, guess, constraint.getSecond()); |
| |
| //The following code is the result of deep thought, do not shit it out before discussing with [ven] |
| if (returnType instanceof PsiClassType && typeParameter.equals(((PsiClassType)returnType).resolve())) { |
| PsiClassType[] extendsTypes = typeParameter.getExtendsListTypes(); |
| PsiSubstitutor newSubstitutor = substitutor.put(typeParameter, guess); |
| for (PsiClassType extendsType1 : extendsTypes) { |
| PsiType extendsType = newSubstitutor.substitute(extendsType1); |
| if (guess != null && !extendsType.isAssignableFrom(guess)) { |
| if (guess.isAssignableFrom(extendsType)) { |
| guess = extendsType; |
| newSubstitutor = substitutor.put(typeParameter, guess); |
| } |
| else { |
| break; |
| } |
| } |
| } |
| } |
| |
| return Pair.create(guess, constraint.getSecond()); |
| } |
| } |