diff options
Diffstat (limited to 'java/java-psi-impl/src/com/intellij/psi')
23 files changed, 3031 insertions, 1342 deletions
diff --git a/java/java-psi-impl/src/com/intellij/psi/PsiDiamondTypeImpl.java b/java/java-psi-impl/src/com/intellij/psi/PsiDiamondTypeImpl.java index 88440552b4ca..ca2539195ee2 100644 --- a/java/java-psi-impl/src/com/intellij/psi/PsiDiamondTypeImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/PsiDiamondTypeImpl.java @@ -23,7 +23,7 @@ import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.codeStyle.JavaCodeStyleManager; -import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy; +import com.intellij.psi.infos.MethodCandidateInfo; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; @@ -130,7 +130,7 @@ public class PsiDiamondTypeImpl extends PsiDiamondType { final PsiExpressionList argumentList = newExpression.getArgumentList(); if (argumentList == null) return DiamondInferenceResult.NULL_RESULT; final Ref<PsiMethod> staticFactoryRef = new Ref<PsiMethod>(); - final PsiSubstitutor inferredSubstitutor = ourDiamondGuard.doPreventingRecursion(newExpression, false, new Computable<PsiSubstitutor>() { + final PsiSubstitutor inferredSubstitutor = ourDiamondGuard.doPreventingRecursion(context, false, new Computable<PsiSubstitutor>() { @Override public PsiSubstitutor compute() { final PsiMethod constructor = findConstructor(psiClass, newExpression); @@ -268,14 +268,27 @@ public class PsiDiamondTypeImpl extends PsiDiamondType { private static PsiSubstitutor inferTypeParametersForStaticFactory(@NotNull PsiMethod staticFactoryMethod, PsiNewExpression expression, - PsiElement parent) { - final JavaPsiFacade facade = JavaPsiFacade.getInstance(staticFactoryMethod.getProject()); - final PsiResolveHelper resolveHelper = facade.getResolveHelper(); - final PsiParameter[] parameters = staticFactoryMethod.getParameterList().getParameters(); + final PsiElement parent) { final PsiExpressionList argumentList = expression.getArgumentList(); - final PsiExpression[] expressions = argumentList.getExpressions(); - return resolveHelper - .inferTypeArguments(staticFactoryMethod.getTypeParameters(), parameters, expressions, PsiSubstitutor.EMPTY, parent, DefaultParameterTypeInferencePolicy.INSTANCE); + if (argumentList != null) { + final MethodCandidateInfo staticFactoryCandidateInfo = + new MethodCandidateInfo(staticFactoryMethod, PsiSubstitutor.EMPTY, false, false, argumentList, parent, + argumentList.getExpressionTypes(), null) { + @Override + protected PsiElement getParent() { + return parent; + } + + @Override + protected PsiElement getMarkerList() { + return parent instanceof PsiNewExpression ? ((PsiNewExpression)parent).getArgumentList() : super.getMarkerList(); + } + }; + return staticFactoryCandidateInfo.getSubstitutor(); + } + else { + return PsiSubstitutor.EMPTY; + } } public static boolean hasDefaultConstructor(@NotNull final PsiClass psiClass) { diff --git a/java/java-psi-impl/src/com/intellij/psi/controlFlow/ControlFlowAnalyzer.java b/java/java-psi-impl/src/com/intellij/psi/controlFlow/ControlFlowAnalyzer.java index c2675894986d..da3411af8ed6 100644 --- a/java/java-psi-impl/src/com/intellij/psi/controlFlow/ControlFlowAnalyzer.java +++ b/java/java-psi-impl/src/com/intellij/psi/controlFlow/ControlFlowAnalyzer.java @@ -1504,7 +1504,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor { startElement(expression); IElementType op = expression.getOperationTokenType(); - PsiExpression operand = expression.getOperand(); + PsiExpression operand = PsiUtil.skipParenthesizedExprDown(expression.getOperand()); operand.accept(this); if (op == JavaTokenType.PLUSPLUS || op == JavaTokenType.MINUSMINUS) { if (operand instanceof PsiReferenceExpression) { @@ -1521,7 +1521,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor { @Override public void visitPrefixExpression(PsiPrefixExpression expression) { startElement(expression); - PsiExpression operand = expression.getOperand(); + PsiExpression operand = PsiUtil.skipParenthesizedExprDown(expression.getOperand()); if (operand != null) { IElementType operationSign = expression.getOperationTokenType(); if (operationSign == JavaTokenType.EXCL) { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/JavaPsiFacadeImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/JavaPsiFacadeImpl.java index b805ef4cf8f0..07f275817a7c 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/JavaPsiFacadeImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/JavaPsiFacadeImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2013 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. @@ -413,21 +413,15 @@ public class JavaPsiFacadeImpl extends JavaPsiFacadeEx { } } - @Override public boolean isPartOfPackagePrefix(@NotNull String packageName) { final Collection<String> packagePrefixes = myFileManager.getNonTrivialPackagePrefixes(); for (final String subpackageName : packagePrefixes) { - if (isSubpackageOf(subpackageName, packageName)) return true; + if (PsiNameHelper.isSubpackageOf(subpackageName, packageName)) return true; } return false; } - private static boolean isSubpackageOf(@NotNull String subpackageName, @NotNull String packageName) { - return subpackageName.equals(packageName) || - subpackageName.startsWith(packageName) && subpackageName.charAt(packageName.length()) == '.'; - } - @Override public boolean isInPackage(@NotNull PsiElement element, @NotNull PsiPackage aPackage) { final PsiFile file = FileContextUtil.getContextFile(element); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/InferenceGraphNode.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/InferenceGraphNode.java deleted file mode 100644 index d127d4037a28..000000000000 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/InferenceGraphNode.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2000-2013 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 java.util.*; - -/** - * User: anna - * Date: 7/4/13 - */ -public class InferenceGraphNode<T> { - private final List<T> myValue = new ArrayList<T>(); - private Set<InferenceGraphNode<T>> myDependencies = new HashSet<InferenceGraphNode<T>>(); - - private int index = -1; - private int lowlink; - - public InferenceGraphNode(T value) { - myValue.add(value); - } - - public List<T> getValue() { - return myValue; - } - - public Set<InferenceGraphNode<T>> getDependencies() { - return myDependencies; - } - - public void addDependency(InferenceGraphNode<T> node) { - myDependencies.add(node); - } - - public static <T> List<List<InferenceGraphNode<T>>> tarjan(Collection<InferenceGraphNode<T>> nodes) { - final ArrayList<List<InferenceGraphNode<T>>> result = new ArrayList<List<InferenceGraphNode<T>>>(); - final Stack<InferenceGraphNode<T>> currentStack = new Stack<InferenceGraphNode<T>>(); - int index = 0; - for (InferenceGraphNode<T> node : nodes) { - if (node.index == -1) { - index += strongConnect(node, index, currentStack, result); - } - } - return result; - } - - public static <T> ArrayList<InferenceGraphNode<T>> initNodes(Collection<InferenceGraphNode<T>> allNodes) { - final List<List<InferenceGraphNode<T>>> nodes = tarjan(allNodes); - final ArrayList<InferenceGraphNode<T>> acyclicNodes = new ArrayList<InferenceGraphNode<T>>(); - for (List<InferenceGraphNode<T>> cycle : nodes) { - acyclicNodes.add(merge(cycle, allNodes)); - } - return acyclicNodes; - } - - private static <T> InferenceGraphNode<T> merge(final List<InferenceGraphNode<T>> cycle, - final Collection<InferenceGraphNode<T>> allNodes) { - assert !cycle.isEmpty(); - final InferenceGraphNode<T> root = cycle.get(0); - if (cycle.size() > 1) { - for (int i = 1; i < cycle.size(); i++) { - final InferenceGraphNode<T> cycleNode = cycle.get(i); - - root.copyFrom(cycleNode); - root.filterInterCycleDependencies(); - - for (InferenceGraphNode<T> node : allNodes) { - if (node.myDependencies.remove(cycleNode)) { - node.myDependencies.add(root); - } - } - } - } - return root; - } - - private void filterInterCycleDependencies() { - boolean includeSelfDependency = false; - for (Iterator<InferenceGraphNode<T>> iterator = myDependencies.iterator(); iterator.hasNext(); ) { - InferenceGraphNode<T> d = iterator.next(); - assert d.myValue.size() >= 1; - final T initialNodeValue = d.myValue.get(0); - if (myValue.contains(initialNodeValue)) { - includeSelfDependency = true; - iterator.remove(); - } - } - - if (includeSelfDependency) { - myDependencies.add(this); - } - } - - private void copyFrom(final InferenceGraphNode<T> cycleNode) { - myValue.addAll(cycleNode.myValue); - myDependencies.addAll(cycleNode.myDependencies); - } - - private static <T> int strongConnect(InferenceGraphNode<T> currentNode, - int index, - Stack<InferenceGraphNode<T>> currentStack, - ArrayList<List<InferenceGraphNode<T>>> result) { - currentNode.index = index; - currentNode.lowlink = index; - index++; - - currentStack.push(currentNode); - - for (InferenceGraphNode<T> dependantNode : currentNode.getDependencies()) { - if (dependantNode.index == -1) { - strongConnect(dependantNode, index, currentStack, result); - currentNode.lowlink = Math.min(currentNode.lowlink, dependantNode.lowlink); - } - else if (currentStack.contains(dependantNode)) { - currentNode.lowlink = Math.min(currentNode.lowlink, dependantNode.index); - } - } - - if (currentNode.lowlink == currentNode.index) { - final ArrayList<InferenceGraphNode<T>> arrayList = new ArrayList<InferenceGraphNode<T>>(); - InferenceGraphNode<T> cyclicNode; - do { - cyclicNode = currentStack.pop(); - arrayList.add(cyclicNode); - } - while (cyclicNode != currentNode); - result.add(arrayList); - } - return index; - } -} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/ProcessCandidateParameterTypeInferencePolicy.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/ProcessCandidateParameterTypeInferencePolicy.java index 1b33cfb196b6..e88fca2a3025 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/ProcessCandidateParameterTypeInferencePolicy.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/ProcessCandidateParameterTypeInferencePolicy.java @@ -17,6 +17,7 @@ package com.intellij.psi.impl.source.resolve; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Pair; +import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.infos.MethodCandidateInfo; import com.intellij.psi.scope.MethodProcessorSetupFailedException; @@ -109,9 +110,9 @@ public class ProcessCandidateParameterTypeInferencePolicy extends DefaultParamet } }); PsiResolveHelperImpl resolveHelper = (PsiResolveHelperImpl)JavaPsiFacade.getInstance(method.getProject()).getResolveHelper(); + final LanguageLevel languageLevel = PsiUtil.getLanguageLevel(finalParameter); final Pair<PsiType, ConstraintType> constraint = - resolveHelper.getSubstitutionForTypeParameterConstraint(typeParameter, innerReturnType, type, false, - PsiUtil.getLanguageLevel(finalParameter)); + ((PsiOldInferenceHelper)resolveHelper.getInferenceHelper(languageLevel)).getSubstitutionForTypeParameterConstraint(typeParameter, innerReturnType, type, false, languageLevel); if (constraint != null) return constraint; } } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/PsiOldInferenceHelper.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/PsiOldInferenceHelper.java new file mode 100644 index 000000000000..8615d912354c --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/PsiOldInferenceHelper.java @@ -0,0 +1,1233 @@ +/* + * Copyright 2000-2013 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.codeInsight.ExceptionUtil; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.JavaSdkVersion; +import com.intellij.openapi.projectRoots.JavaVersionService; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.RecursionGuard; +import com.intellij.openapi.util.registry.Registry; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.psi.*; +import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession; +import com.intellij.psi.infos.MethodCandidateInfo; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.*; +import com.intellij.util.ArrayUtil; +import com.intellij.util.containers.HashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; + +/** + * 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 = new PsiType[arguments.length]; + PsiType[] argTypes = new PsiType[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) { + sortLambdaExpressionsLast(paramTypes, argTypes); + boolean rawType = false; + boolean nullPassed = false; + boolean lambdaRaw = false; + 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; + rawType |= parameterType instanceof PsiClassType && ((PsiClassType)parameterType).isRaw(); + nullPassed |= argumentType == PsiType.NULL; + + 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; + if (argumentType instanceof PsiLambdaExpressionType) { + currentSubstitution = inferSubstitutionFromLambda(typeParameter, (PsiLambdaExpressionType)argumentType, lowerBound, partialSubstitutor); + if (rawType) { + if (currentSubstitution == FAILED_INFERENCE || currentSubstitution == null && lowerBound == PsiType.NULL) return RAW_INFERENCE; + } + if (nullPassed && currentSubstitution == null) return RAW_INFERENCE; + if (currentSubstitution != null && currentSubstitution.first == null) { + lambdaRaw = true; + } + if (currentSubstitution == null && lambdaRaw) { + return new Pair<PsiType, ConstraintType>(PsiType.getJavaLangObject(myManager, typeParameter.getResolveScope()), ConstraintType.EQUALS); + } + } else if (argumentType instanceof PsiMethodReferenceType) { + final PsiMethodReferenceExpression referenceExpression = ((PsiMethodReferenceType)argumentType).getExpression(); + currentSubstitution = inferConstraintFromFunctionalInterfaceMethod(typeParameter, referenceExpression, partialSubstitutor.substitute(parameterType), partialSubstitutor, policy); + } + else { + 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 new Pair<PsiType, ConstraintType>(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 new Pair<PsiType, ConstraintType>(upperBound, ConstraintType.SUBTYPE); + } + + return constraint; + } + } + + if (upperBound != PsiType.NULL) return new Pair<PsiType, ConstraintType>(upperBound, ConstraintType.SUBTYPE); + return null; + } + + private static void sortLambdaExpressionsLast(@NotNull PsiType[] paramTypes, @NotNull PsiType[] argTypes) { + for (int i = 0; i < argTypes.length; i++) { + PsiType argType = argTypes[i]; + if ((argType instanceof PsiLambdaExpressionType || argType instanceof PsiMethodReferenceType) && i < argTypes.length - 1) { + int k = i + 1; + while((argTypes[k] instanceof PsiLambdaExpressionType || argTypes[k] instanceof PsiMethodReferenceType) && k < argTypes.length - 1) { + k++; + } + if (!(argTypes[k] instanceof PsiLambdaExpressionType || argTypes[k] instanceof PsiMethodReferenceType)) { + ArrayUtil.swap(paramTypes, i, k); + ArrayUtil.swap(argTypes, i, k); + i = k; + } + } + } + } + + 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 = new PsiType[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) { + 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); + } + } + + try { + 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) { + 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); + } + } + } + finally { + GraphInferencePolicy.forget(parent); + } + return partialSubstitutor; + } + + @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 new Pair<PsiType, ConstraintType>(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 new Pair<PsiType, ConstraintType>(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); + } + + @Nullable + private Pair<PsiType, ConstraintType> inferSubstitutionFromLambda(PsiTypeParameter typeParam, + PsiLambdaExpressionType arg, + PsiType lowerBound, + PsiSubstitutor partialSubstitutor) { + final PsiLambdaExpression lambdaExpression = arg.getExpression(); + if (PsiUtil.getLanguageLevel(lambdaExpression).isAtLeast(LanguageLevel.JDK_1_8)) { + final PsiElement parent = PsiUtil.skipParenthesizedExprUp(lambdaExpression.getParent()); + if (parent instanceof PsiExpressionList) { + final PsiExpressionList expressionList = (PsiExpressionList)parent; + final Map<PsiElement, Pair<PsiMethod, PsiSubstitutor>> methodMap = MethodCandidateInfo.CURRENT_CANDIDATE.get(); + final Pair<PsiMethod, PsiSubstitutor> pair = methodMap != null ? methodMap.get(expressionList) : null; + if (pair != null) { + final int i = LambdaUtil.getLambdaIdx(expressionList, lambdaExpression); + if (i < 0) return null; + final PsiParameter[] parameters = pair.first.getParameterList().getParameters(); + if (parameters.length <= i) return null; + final PsiSubstitutor combinedSubst = pair.second.putAll(partialSubstitutor); + methodMap.put(expressionList, Pair.create(pair.first, combinedSubst)); + return inferConstraintFromFunctionalInterfaceMethod(typeParam, lambdaExpression, combinedSubst.substitute(parameters[i].getType()), lowerBound); + } + } + else { + return inferConstraintFromFunctionalInterfaceMethod(typeParam, lambdaExpression, + partialSubstitutor.substitute(lambdaExpression.getFunctionalInterfaceType()), lowerBound); + } + } + return null; + } + + @Nullable + private Pair<PsiType, ConstraintType> inferConstraintFromFunctionalInterfaceMethod(final PsiTypeParameter typeParam, + final PsiMethodReferenceExpression methodReferenceExpression, + final PsiType functionalInterfaceType, + final PsiSubstitutor partialSubstitutor, + final ParameterTypeInferencePolicy policy) { + final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType); + final PsiMethod functionalInterfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult); + if (functionalInterfaceMethod != null) { + final PsiSubstitutor subst = LambdaUtil.getSubstitutor(functionalInterfaceMethod, resolveResult); + final PsiParameter[] methodParameters = functionalInterfaceMethod.getParameterList().getParameters(); + PsiType[] methodParamTypes = new PsiType[methodParameters.length]; + for (int i = 0; i < methodParameters.length; i++) { + methodParamTypes[i] = GenericsUtil.eliminateWildcards(subst.substitute(methodParameters[i].getType())); + } + + if (methodParamsDependOn(typeParam, methodReferenceExpression, functionalInterfaceType, methodParameters, subst)) { + return null; + } + + final PsiType[] args = new PsiType[methodParameters.length]; + Map<PsiMethodReferenceExpression,PsiType> map = PsiMethodReferenceUtil.ourRefs.get(); + if (map == null) { + map = new HashMap<PsiMethodReferenceExpression, PsiType>(); + PsiMethodReferenceUtil.ourRefs.set(map); + } + final PsiType added = map.put(methodReferenceExpression, functionalInterfaceType); + final JavaResolveResult methReferenceResolveResult; + try { + methReferenceResolveResult = methodReferenceExpression.advancedResolve(false); + } + finally { + if (added == null) { + map.remove(methodReferenceExpression); + } + } + final PsiElement resolved = methReferenceResolveResult.getElement(); + if (resolved instanceof PsiMethod) { + final PsiMethod method = (PsiMethod)resolved; + final PsiParameter[] parameters = method.getParameterList().getParameters(); + boolean hasReceiver = false; + if (methodParamTypes.length == parameters.length + 1) { + if (!PsiMethodReferenceUtil + .isReceiverType(methodParamTypes[0], method.getContainingClass(), methReferenceResolveResult.getSubstitutor())) return null; + hasReceiver = true; + } else if (parameters.length != methodParameters.length) { + return null; + } + for (int i = 0; i < parameters.length; i++) { + args[i] = methReferenceResolveResult.getSubstitutor().substitute(subst.substitute(parameters[i].getType())); + } + + final PsiType[] typesToInfer = hasReceiver ? ArrayUtil.remove(methodParamTypes, 0) : methodParamTypes; + final Pair<PsiType, ConstraintType> constraint = inferTypeForMethodTypeParameterInner(typeParam, typesToInfer, args, subst, null, DefaultParameterTypeInferencePolicy.INSTANCE); + if (constraint != null){ + return constraint; + } + PsiType functionalInterfaceReturnType = functionalInterfaceMethod.getReturnType(); + if (functionalInterfaceReturnType != null && functionalInterfaceReturnType != PsiType.VOID) { + functionalInterfaceReturnType = GenericsUtil.eliminateWildcards(subst.substitute(functionalInterfaceReturnType)); + final PsiType argType; + if (method.isConstructor()) { + argType = JavaPsiFacade.getElementFactory(functionalInterfaceMethod.getProject()).createType(method.getContainingClass(), methReferenceResolveResult.getSubstitutor()); + } else { + argType = methReferenceResolveResult.getSubstitutor().substitute(subst.substitute(method.getReturnType())); + } + final Pair<PsiType, ConstraintType> typeParameterConstraint = + getSubstitutionForTypeParameterConstraint(typeParam, functionalInterfaceReturnType, argType, true, PsiUtil.getLanguageLevel(functionalInterfaceMethod)); + if (typeParameterConstraint != null && typeParameterConstraint.getSecond() != ConstraintType.EQUALS && method.isConstructor()) { + final Pair<PsiType, ConstraintType> constraintFromParent = + inferMethodTypeParameterFromParent(typeParam, partialSubstitutor, methodReferenceExpression.getParent().getParent(), policy); + if (constraintFromParent != null && constraintFromParent.getSecond() == ConstraintType.EQUALS) return constraintFromParent; + } + return typeParameterConstraint; + } + } + } + return null; + } + + @Nullable + private Pair<PsiType, ConstraintType> inferConstraintFromFunctionalInterfaceMethod(PsiTypeParameter typeParam, + final PsiLambdaExpression lambdaExpression, + final PsiType functionalInterfaceType, + PsiType lowerBound) { + final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType); + final PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(resolveResult); + if (method != null) { + final PsiSubstitutor subst = LambdaUtil.getSubstitutor(method, resolveResult); + final Pair<PsiType, ConstraintType> constraintFromFormalParams = inferConstraintFromLambdaFormalParams(typeParam, subst, method, lambdaExpression); + if (constraintFromFormalParams != null) return constraintFromFormalParams; + + final PsiParameter[] methodParameters = method.getParameterList().getParameters(); + if (methodParamsDependOn(typeParam, lambdaExpression, functionalInterfaceType, methodParameters, subst)) { + return null; + } + + final PsiType returnType = subst.substitute(method.getReturnType()); + if (returnType != null && returnType != PsiType.VOID) { + Pair<PsiType, ConstraintType> constraint = null; + final List<PsiExpression> expressions = LambdaUtil.getReturnExpressions(lambdaExpression); + for (final PsiExpression expression : expressions) { + final boolean independent = lambdaExpression.hasFormalParameterTypes() || LambdaUtil.isFreeFromTypeInferenceArgs(methodParameters, lambdaExpression, expression, subst, functionalInterfaceType, typeParam); + if (!independent) { + if (lowerBound != PsiType.NULL) { + return null; + } + continue; + } + if (expression instanceof PsiReferenceExpression && ((PsiReferenceExpression)expression).resolve() == null) continue; + PsiType exprType = PsiResolveHelper.ourGraphGuard.doPreventingRecursion(expression, true, new Computable<PsiType>() { + @Override + public PsiType compute() { + return expression.getType(); + } + }); + if (exprType instanceof PsiLambdaParameterType) { + final PsiParameter parameter = ((PsiLambdaParameterType)exprType).getParameter(); + final int parameterIndex = lambdaExpression.getParameterList().getParameterIndex(parameter); + if (parameterIndex > -1) { + exprType = subst.substitute(methodParameters[parameterIndex].getType()); + } + } else if (exprType instanceof PsiLambdaExpressionType) { + return inferConstraintFromFunctionalInterfaceMethod(typeParam, ((PsiLambdaExpressionType)exprType).getExpression(), returnType, + lowerBound); + } else if (exprType == null && independent) { + return null; + } + + if (exprType == null){ + return FAILED_INFERENCE; + } + + final Pair<PsiType, ConstraintType> returnExprConstraint = + getSubstitutionForTypeParameterConstraint(typeParam, GenericsUtil.eliminateWildcards(returnType), exprType, true, PsiUtil.getLanguageLevel(method)); + if (returnExprConstraint != null) { + if (returnExprConstraint == FAILED_INFERENCE) return returnExprConstraint; + if (constraint != null) { + final PsiType leastUpperBound = GenericsUtil.getLeastUpperBound(constraint.getFirst(), returnExprConstraint.getFirst(), myManager); + constraint = new Pair<PsiType, ConstraintType>(leastUpperBound, ConstraintType.SUPERTYPE); + } else { + constraint = returnExprConstraint; + } + } + } + if (constraint != null) return constraint; + } + } + return null; + } + + private static boolean methodParamsDependOn(PsiTypeParameter typeParam, PsiElement psiElement, + PsiType functionalInterfaceType, + PsiParameter[] methodParameters, + PsiSubstitutor subst) { + for (PsiParameter parameter : methodParameters) { + if (LambdaUtil.dependsOnTypeParams(subst.substitute(parameter.getType()), functionalInterfaceType, psiElement, typeParam)) { + return true; + } + } + return false; + } + + @Nullable + private Pair<PsiType, ConstraintType> inferConstraintFromLambdaFormalParams(PsiTypeParameter typeParam, + PsiSubstitutor subst, + PsiMethod method, PsiLambdaExpression lambdaExpression) { + final PsiParameter[] parameters = lambdaExpression.getParameterList().getParameters(); + if (parameters.length == 0) return null; + final PsiType[] lambdaArgs = new PsiType[parameters.length]; + for (int i = 0; i < parameters.length; i++) { + PsiParameter parameter = parameters[i]; + if (parameter.getTypeElement() == null) { + return null; + } + lambdaArgs[i] = parameter.getType(); + } + + final PsiParameter[] methodParameters = method.getParameterList().getParameters(); + PsiType[] methodParamTypes = new PsiType[methodParameters.length]; + for (int i = 0; i < methodParameters.length; i++) { + methodParamTypes[i] = GenericsUtil.eliminateWildcards(subst.substitute(methodParameters[i].getType())); + } + return inferTypeForMethodTypeParameterInner(typeParam, methodParamTypes, lambdaArgs, subst, null, DefaultParameterTypeInferencePolicy.INSTANCE); + } + + private static PsiType intersectAllExtends(PsiTypeParameter typeParam, PsiType arg) { + if (arg == null) return null; + PsiClassType[] superTypes = typeParam.getSuperTypes(); + PsiType[] erasureTypes = new PsiType[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 (arg instanceof PsiCapturedWildcardType && (depth < 2 || constraintType != ConstraintType.EQUALS)) arg = ((PsiCapturedWildcardType)arg).getWildcard(); //reopen + + if (patternType.equals(param)) { + return processArgType(arg, constraintType, depth < 2); + } + + 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 new Pair<PsiType, ConstraintType>(lowerBound, ConstraintType.SUPERTYPE); + if (upperBound != PsiType.NULL) return new Pair<PsiType, ConstraintType>(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 PsiReturnStatement) { + final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(parent, PsiLambdaExpression.class); + if (lambdaExpression != null) { + return getFailedInferenceConstraint(typeParameter); + } + } + else 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); + if (constraint == null && PsiUtil.isLanguageLevel8OrHigher(methodCall)) { + constraint = graphInferenceFromCallContext(methodCall, typeParameter, (PsiCallExpression)pParent); + if (constraint != null) { + final PsiType constraintFirst = constraint.getFirst(); + if (constraintFirst == null || constraintFirst.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) { + constraint = null; + } + } + } + } + } else if (parent instanceof PsiLambdaExpression) { + expectedType = PsiResolveHelper.ourGraphGuard.doPreventingRecursion(methodCall, true, new Computable<PsiType>() { + @Override + public PsiType compute() { + return LambdaUtil.getFunctionalInterfaceReturnType(((PsiLambdaExpression)parent).getFunctionalInterfaceType()); + } + }); + if (expectedType == null) { + return null; + } + expectedType = GenericsUtil.eliminateWildcards(expectedType); + } else if (parent instanceof PsiConditionalExpression) { + if (PsiUtil.isLanguageLevel8OrHigher(parent)) { + try { + final Pair<PsiType, ConstraintType> pair = inferFromConditionalExpression(parent, methodCall, typeParameter, substitutor, policy); + if (pair != null) { + return pair; + } + } + finally { + GraphInferencePolicy.forget(parent); + } + } + } + } + + 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) { + for (PsiType conjuct : ((PsiIntersectionType)guess).getConjuncts()) { + if (!conjuct.isAssignableFrom(expectedType)) { + return FAILED_INFERENCE; + } + } + } + } + } + + if (constraint == null) { + if (methodCall instanceof PsiCallExpression) { + final PsiExpressionList argumentList = ((PsiCallExpression)methodCall).getArgumentList(); + if (argumentList != null && PsiUtil.getLanguageLevel(argumentList).isAtLeast(LanguageLevel.JDK_1_8)) { + for (PsiExpression expression : argumentList.getExpressions()) { + if (expression instanceof PsiLambdaExpression || expression instanceof PsiMethodReferenceExpression) { + final PsiType functionalInterfaceType = LambdaUtil.getFunctionalInterfaceType(expression, false); + if (functionalInterfaceType == null || PsiUtil.resolveClassInType(functionalInterfaceType) == typeParameter){ + return getFailedInferenceConstraint(typeParameter); + } + final PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType); + + final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType); + if (method == null || methodParamsDependOn(typeParameter, expression, + functionalInterfaceType, method.getParameterList().getParameters(), + LambdaUtil.getSubstitutor(method, resolveResult))) { + if (expression instanceof PsiMethodReferenceExpression) { + return getFailedInferenceConstraint(typeParameter); + } + return null; + } + final Pair<PsiType, ConstraintType> inferredExceptionTypeConstraint = inferExceptionConstrains(typeParameter, expression, method, resolveResult.getSubstitutor()); + if (inferredExceptionTypeConstraint != null) { + return inferredExceptionTypeConstraint; + } + } + } + } + + PsiType[] superTypes = typeParameter.getSuperTypes(); + if (superTypes.length == 0) return null; + final PsiType[] types = new PsiType[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); + if (superType == null) return null; + 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 new Pair<PsiType, ConstraintType>(guess, constraint.getSecond()); + } + + private static boolean checkSameExpression(PsiExpression templateExpr, final PsiExpression expression) { + return templateExpr.equals(PsiUtil.skipParenthesizedExprDown(expression)); + } + + private static Pair<PsiType, ConstraintType> inferExceptionConstrains(PsiTypeParameter typeParameter, + PsiExpression expression, + PsiMethod method, + PsiSubstitutor substitutor) { + final PsiClassType[] declaredExceptions = method.getThrowsList().getReferencedTypes(); + for (PsiClassType exception : declaredExceptions) { + final PsiType substitute = substitutor.substitute(exception); + if (PsiUtil.resolveClassInType(substitute) == typeParameter) { + if (expression instanceof PsiLambdaExpression) { + final PsiElement body = ((PsiLambdaExpression)expression).getBody(); + if (body != null) { + final List<PsiClassType> unhandledExceptions = ExceptionUtil.getUnhandledExceptions(body); + if (unhandledExceptions.isEmpty()) { + return inferUncheckedException(typeParameter, exception, method); + } + } + } + else if (expression instanceof PsiMethodReferenceExpression) { + final PsiElement resolve = ((PsiMethodReferenceExpression)expression).resolve(); + if (resolve instanceof PsiMethod) { + final PsiClassType[] declaredThrowsList = ((PsiMethod)resolve).getThrowsList().getReferencedTypes(); + for (PsiClassType psiClassType : declaredThrowsList) { + if (!ExceptionUtil.isUncheckedException(psiClassType)) return null; + } + return inferUncheckedException(typeParameter, exception, method); + } + } + break; + } + } + return null; + } + + private static Pair<PsiType, ConstraintType> inferUncheckedException(PsiTypeParameter typeParameter, + PsiClassType exception, + PsiMethod method) { + final Project project = typeParameter.getProject(); + final PsiClass runtimeException = JavaPsiFacade.getInstance(project).findClass(CommonClassNames.JAVA_LANG_RUNTIME_EXCEPTION, method.getResolveScope()); + if (runtimeException != null) { + for (PsiType superType : exception.getSuperTypes()) { + if (!InheritanceUtil.isInheritorOrSelf(runtimeException, PsiUtil.resolveClassInType(superType), true)) { + return getFailedInferenceConstraint(typeParameter); + } + } + return Pair.<PsiType, ConstraintType>create(JavaPsiFacade.getElementFactory(project).createType(runtimeException, PsiSubstitutor.EMPTY), ConstraintType.EQUALS); + } + return null; + } + + private Pair<PsiType, ConstraintType> inferFromConditionalExpression(@NotNull PsiElement parent, + @NotNull PsiExpression methodCall, + @NotNull PsiTypeParameter typeParameter, + @NotNull PsiSubstitutor substitutor, + @NotNull ParameterTypeInferencePolicy policy) { + Pair<PsiType, ConstraintType> pair = + inferMethodTypeParameterFromParent(PsiUtil.skipParenthesizedExprUp(parent.getParent()), (PsiExpression)parent, typeParameter, substitutor, policy); + if (pair == null) { + final PsiExpression thenExpression = ((PsiConditionalExpression)parent).getThenExpression(); + final PsiExpression elseExpression = ((PsiConditionalExpression)parent).getElseExpression(); + final PsiType[] paramTypes = {((PsiMethod)typeParameter.getOwner()).getReturnType()}; + if (methodCall.equals(PsiUtil.skipParenthesizedExprDown(elseExpression)) && thenExpression != null) { + final PsiType thenType = PsiResolveHelper.ourGraphGuard.doPreventingRecursion(parent, true, new Computable<PsiType>() { + @Override + public PsiType compute() { + return thenExpression.getType(); + } + }); + if (thenType != null) { + pair = inferTypeForMethodTypeParameterInner(typeParameter, paramTypes, new PsiType[] {thenType}, substitutor, null, policy); + } + } else if (methodCall.equals(PsiUtil.skipParenthesizedExprDown(thenExpression)) && elseExpression != null) { + final PsiType elseType = PsiResolveHelper.ourGraphGuard.doPreventingRecursion(parent, true, new Computable<PsiType>() { + @Override + public PsiType compute() { + return elseExpression.getType(); + } + }); + if (elseType != null) { + pair = inferTypeForMethodTypeParameterInner(typeParameter, paramTypes, new PsiType[] {elseType}, substitutor, null, policy); + } + } + } + return pair; + } + + private static final ProcessCandidateParameterTypeInferencePolicy GRAPH_INFERENCE_POLICY = new GraphInferencePolicy(); + + private static Pair<PsiType, ConstraintType> graphInferenceFromCallContext(@NotNull final PsiExpression methodCall, + @NotNull final PsiTypeParameter typeParameter, + @NotNull final PsiCallExpression parentCall) { + if (Registry.is("disable.graph.inference", false)) return null; + final PsiExpressionList argumentList = parentCall.getArgumentList(); + if (PsiDiamondType.ourDiamondGuard.currentStack().contains(parentCall)) { + PsiDiamondType.ourDiamondGuard.prohibitResultCaching(parentCall); + return FAILED_INFERENCE; + } + return PsiResolveHelper.ourGraphGuard.doPreventingRecursion(methodCall, true, new Computable<Pair<PsiType, ConstraintType>>() { + @Override + public Pair<PsiType, ConstraintType> compute() { + return GRAPH_INFERENCE_POLICY.inferTypeConstraintFromCallContext(methodCall, argumentList, parentCall, typeParameter); + } + }); + } + +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/PsiResolveHelperImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/PsiResolveHelperImpl.java index 6410991c63c1..68bb8b865c92 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/PsiResolveHelperImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/PsiResolveHelperImpl.java @@ -15,38 +15,23 @@ */ package com.intellij.psi.impl.source.resolve; -import com.intellij.codeInsight.ExceptionUtil; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.projectRoots.JavaSdkVersion; -import com.intellij.openapi.projectRoots.JavaVersionService; -import com.intellij.openapi.util.Computable; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.RecursionGuard; import com.intellij.openapi.util.registry.Registry; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; +import com.intellij.psi.impl.source.resolve.graphInference.PsiGraphInferenceHelper; import com.intellij.psi.infos.CandidateInfo; -import com.intellij.psi.infos.MethodCandidateInfo; import com.intellij.psi.scope.MethodProcessorSetupFailedException; import com.intellij.psi.scope.processor.MethodCandidatesProcessor; import com.intellij.psi.scope.processor.MethodResolverProcessor; import com.intellij.psi.scope.util.PsiScopesUtil; -import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.*; -import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; -import com.intellij.util.containers.HashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.List; -import java.util.Map; - public class PsiResolveHelperImpl implements PsiResolveHelper { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.resolve.PsiResolveHelperImpl"); - public static final Pair<PsiType,ConstraintType> RAW_INFERENCE = new Pair<PsiType, ConstraintType>(null, ConstraintType.EQUALS); private final PsiManager myManager; public PsiResolveHelperImpl(PsiManager manager) { @@ -155,314 +140,27 @@ public class PsiResolveHelperImpl implements PsiResolveHelper { return processor.getCandidates(); } - 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 = new PsiType[arguments.length]; - PsiType[] argTypes = new PsiType[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 && 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) { - sortLambdaExpressionsLast(paramTypes, argTypes); - boolean rawType = false; - boolean nullPassed = false; - boolean lambdaRaw = false; - 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; - rawType |= parameterType instanceof PsiClassType && ((PsiClassType)parameterType).isRaw(); - nullPassed |= argumentType == PsiType.NULL; - - 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; - if (argumentType instanceof PsiLambdaExpressionType) { - currentSubstitution = inferSubstitutionFromLambda(typeParameter, (PsiLambdaExpressionType)argumentType, lowerBound, partialSubstitutor); - if (rawType) { - if (currentSubstitution == FAILED_INFERENCE || currentSubstitution == null && lowerBound == PsiType.NULL) return RAW_INFERENCE; - } - if (nullPassed && currentSubstitution == null) return RAW_INFERENCE; - if (currentSubstitution != null && currentSubstitution.first == null) { - lambdaRaw = true; - } - if (currentSubstitution == null && lambdaRaw) { - return new Pair<PsiType, ConstraintType>(PsiType.getJavaLangObject(myManager, typeParameter.getResolveScope()), ConstraintType.EQUALS); - } - } else if (argumentType instanceof PsiMethodReferenceType) { - final PsiMethodReferenceExpression referenceExpression = ((PsiMethodReferenceType)argumentType).getExpression(); - currentSubstitution = inferConstraintFromFunctionalInterfaceMethod(typeParameter, referenceExpression, partialSubstitutor.substitute(parameterType), partialSubstitutor, policy); - } - else { - 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 new Pair<PsiType, ConstraintType>(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 new Pair<PsiType, ConstraintType>(upperBound, ConstraintType.SUBTYPE); - } - - return constraint; - } - } - - if (upperBound != PsiType.NULL) return new Pair<PsiType, ConstraintType>(upperBound, ConstraintType.SUBTYPE); - return null; - } - - private static void sortLambdaExpressionsLast(@NotNull PsiType[] paramTypes, @NotNull PsiType[] argTypes) { - for (int i = 0; i < argTypes.length; i++) { - PsiType argType = argTypes[i]; - if ((argType instanceof PsiLambdaExpressionType || argType instanceof PsiMethodReferenceType) && i < argTypes.length - 1) { - int k = i + 1; - while((argTypes[k] instanceof PsiLambdaExpressionType || argTypes[k] instanceof PsiMethodReferenceType) && k < argTypes.length - 1) { - k++; - } - if (!(argTypes[k] instanceof PsiLambdaExpressionType || argTypes[k] instanceof PsiMethodReferenceType)) { - ArrayUtil.swap(paramTypes, i, k); - ArrayUtil.swap(argTypes, i, k); - i = k; - } - } - } - } - - 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, + public PsiType inferTypeForMethodTypeParameter(@NotNull PsiTypeParameter typeParameter, + @NotNull 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(); + @Nullable PsiElement parent, + @NotNull ParameterTypeInferencePolicy policy) { + return getInferenceHelper(PsiUtil.getLanguageLevel(parent != null ? parent : typeParameter)) + .inferTypeForMethodTypeParameter(typeParameter, parameters, arguments, partialSubstitutor, parent, policy); } - @NotNull @Override + @NotNull 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 = new PsiType[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) { - 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); - } - } - - try { - 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) { - 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); - } - } - } - finally { - GraphInferencePolicy.forget(parent); - } - return partialSubstitutor; + @NotNull ParameterTypeInferencePolicy policy) { + return getInferenceHelper(PsiUtil.getLanguageLevel(parent)) + .inferTypeArguments(typeParameters, parameters, arguments, partialSubstitutor, parent, policy, PsiUtil.getLanguageLevel(parent)); } @Override @@ -472,8 +170,10 @@ public class PsiResolveHelperImpl implements PsiResolveHelper { @NotNull PsiExpression[] arguments, @NotNull PsiSubstitutor partialSubstitutor, @NotNull PsiElement parent, - @NotNull ParameterTypeInferencePolicy policy) { - return inferTypeArguments(typeParameters, parameters, arguments, partialSubstitutor, parent, policy, PsiUtil.getLanguageLevel(parent)); + @NotNull ParameterTypeInferencePolicy policy, + @NotNull LanguageLevel languageLevel) { + return getInferenceHelper(languageLevel) + .inferTypeArguments(typeParameters, parameters, arguments, partialSubstitutor, parent, policy, languageLevel); } @Override @@ -482,866 +182,24 @@ public class PsiResolveHelperImpl implements PsiResolveHelper { @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 new Pair<PsiType, ConstraintType>(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; + return getInferenceHelper(languageLevel) + .inferTypeArguments(typeParameters, leftTypes, rightTypes, languageLevel); } @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 new Pair<PsiType, ConstraintType>(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); - } - - @Nullable - private Pair<PsiType, ConstraintType> inferSubstitutionFromLambda(PsiTypeParameter typeParam, - PsiLambdaExpressionType arg, - PsiType lowerBound, - PsiSubstitutor partialSubstitutor) { - final PsiLambdaExpression lambdaExpression = arg.getExpression(); - if (PsiUtil.getLanguageLevel(lambdaExpression).isAtLeast(LanguageLevel.JDK_1_8)) { - final PsiElement parent = PsiUtil.skipParenthesizedExprUp(lambdaExpression.getParent()); - if (parent instanceof PsiExpressionList) { - final PsiExpressionList expressionList = (PsiExpressionList)parent; - final Map<PsiElement, Pair<PsiMethod, PsiSubstitutor>> methodMap = MethodCandidateInfo.CURRENT_CANDIDATE.get(); - final Pair<PsiMethod, PsiSubstitutor> pair = methodMap != null ? methodMap.get(expressionList) : null; - if (pair != null) { - final int i = LambdaUtil.getLambdaIdx(expressionList, lambdaExpression); - if (i < 0) return null; - final PsiParameter[] parameters = pair.first.getParameterList().getParameters(); - if (parameters.length <= i) return null; - final PsiSubstitutor combinedSubst = pair.second.putAll(partialSubstitutor); - methodMap.put(expressionList, Pair.create(pair.first, combinedSubst)); - return inferConstraintFromFunctionalInterfaceMethod(typeParam, lambdaExpression, combinedSubst.substitute(parameters[i].getType()), lowerBound); - } - } - else { - return inferConstraintFromFunctionalInterfaceMethod(typeParam, lambdaExpression, - partialSubstitutor.substitute(lambdaExpression.getFunctionalInterfaceType()), lowerBound); - } - } - return null; + boolean isContraVariantPosition, + LanguageLevel languageLevel) { + return getInferenceHelper(languageLevel) + .getSubstitutionForTypeParameter(typeParam, param, arg, isContraVariantPosition, languageLevel); } - @Nullable - private Pair<PsiType, ConstraintType> inferConstraintFromFunctionalInterfaceMethod(final PsiTypeParameter typeParam, - final PsiMethodReferenceExpression methodReferenceExpression, - final PsiType functionalInterfaceType, - final PsiSubstitutor partialSubstitutor, - final ParameterTypeInferencePolicy policy) { - final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType); - final PsiMethod functionalInterfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult); - if (functionalInterfaceMethod != null) { - final PsiSubstitutor subst = LambdaUtil.getSubstitutor(functionalInterfaceMethod, resolveResult); - final PsiParameter[] methodParameters = functionalInterfaceMethod.getParameterList().getParameters(); - PsiType[] methodParamTypes = new PsiType[methodParameters.length]; - for (int i = 0; i < methodParameters.length; i++) { - methodParamTypes[i] = GenericsUtil.eliminateWildcards(subst.substitute(methodParameters[i].getType())); - } - - if (methodParamsDependOn(typeParam, methodReferenceExpression, functionalInterfaceType, methodParameters, subst)) { - return null; - } - - final PsiType[] args = new PsiType[methodParameters.length]; - Map<PsiMethodReferenceExpression,PsiType> map = PsiMethodReferenceUtil.ourRefs.get(); - if (map == null) { - map = new HashMap<PsiMethodReferenceExpression, PsiType>(); - PsiMethodReferenceUtil.ourRefs.set(map); - } - final PsiType added = map.put(methodReferenceExpression, functionalInterfaceType); - final JavaResolveResult methReferenceResolveResult; - try { - methReferenceResolveResult = methodReferenceExpression.advancedResolve(false); - } - finally { - if (added == null) { - map.remove(methodReferenceExpression); - } - } - final PsiElement resolved = methReferenceResolveResult.getElement(); - if (resolved instanceof PsiMethod) { - final PsiMethod method = (PsiMethod)resolved; - final PsiParameter[] parameters = method.getParameterList().getParameters(); - boolean hasReceiver = false; - if (methodParamTypes.length == parameters.length + 1) { - if (!PsiMethodReferenceUtil - .isReceiverType(methodParamTypes[0], method.getContainingClass(), methReferenceResolveResult.getSubstitutor())) return null; - hasReceiver = true; - } else if (parameters.length != methodParameters.length) { - return null; - } - for (int i = 0; i < parameters.length; i++) { - args[i] = methReferenceResolveResult.getSubstitutor().substitute(subst.substitute(parameters[i].getType())); - } - - final PsiType[] typesToInfer = hasReceiver ? ArrayUtil.remove(methodParamTypes, 0) : methodParamTypes; - final Pair<PsiType, ConstraintType> constraint = inferTypeForMethodTypeParameterInner(typeParam, typesToInfer, args, subst, null, DefaultParameterTypeInferencePolicy.INSTANCE); - if (constraint != null){ - return constraint; - } - PsiType functionalInterfaceReturnType = functionalInterfaceMethod.getReturnType(); - if (functionalInterfaceReturnType != null && functionalInterfaceReturnType != PsiType.VOID) { - functionalInterfaceReturnType = GenericsUtil.eliminateWildcards(subst.substitute(functionalInterfaceReturnType)); - final PsiType argType; - if (method.isConstructor()) { - argType = JavaPsiFacade.getElementFactory(functionalInterfaceMethod.getProject()).createType(method.getContainingClass(), methReferenceResolveResult.getSubstitutor()); - } else { - argType = methReferenceResolveResult.getSubstitutor().substitute(subst.substitute(method.getReturnType())); - } - final Pair<PsiType, ConstraintType> typeParameterConstraint = - getSubstitutionForTypeParameterConstraint(typeParam, functionalInterfaceReturnType, argType, true, PsiUtil.getLanguageLevel(functionalInterfaceMethod)); - if (typeParameterConstraint != null && typeParameterConstraint.getSecond() != ConstraintType.EQUALS && method.isConstructor()) { - final Pair<PsiType, ConstraintType> constraintFromParent = - inferMethodTypeParameterFromParent(typeParam, partialSubstitutor, methodReferenceExpression.getParent().getParent(), policy); - if (constraintFromParent != null && constraintFromParent.getSecond() == ConstraintType.EQUALS) return constraintFromParent; - } - return typeParameterConstraint; - } - } - } - return null; - } - - @Nullable - private Pair<PsiType, ConstraintType> inferConstraintFromFunctionalInterfaceMethod(PsiTypeParameter typeParam, - final PsiLambdaExpression lambdaExpression, - final PsiType functionalInterfaceType, - PsiType lowerBound) { - final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType); - final PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(resolveResult); - if (method != null) { - final PsiSubstitutor subst = LambdaUtil.getSubstitutor(method, resolveResult); - final Pair<PsiType, ConstraintType> constraintFromFormalParams = inferConstraintFromLambdaFormalParams(typeParam, subst, method, lambdaExpression); - if (constraintFromFormalParams != null) return constraintFromFormalParams; - - final PsiParameter[] methodParameters = method.getParameterList().getParameters(); - if (methodParamsDependOn(typeParam, lambdaExpression, functionalInterfaceType, methodParameters, subst)) { - return null; - } - - final PsiType returnType = subst.substitute(method.getReturnType()); - if (returnType != null && returnType != PsiType.VOID) { - Pair<PsiType, ConstraintType> constraint = null; - final List<PsiExpression> expressions = LambdaUtil.getReturnExpressions(lambdaExpression); - for (final PsiExpression expression : expressions) { - final boolean independent = lambdaExpression.hasFormalParameterTypes() || LambdaUtil.isFreeFromTypeInferenceArgs(methodParameters, lambdaExpression, expression, subst, functionalInterfaceType, typeParam); - if (!independent) { - if (lowerBound != PsiType.NULL) { - return null; - } - continue; - } - if (expression instanceof PsiReferenceExpression && ((PsiReferenceExpression)expression).resolve() == null) continue; - PsiType exprType = ourGraphGuard.doPreventingRecursion(expression, true, new Computable<PsiType>() { - @Override - public PsiType compute() { - return expression.getType(); - } - }); - if (exprType instanceof PsiLambdaParameterType) { - final PsiParameter parameter = ((PsiLambdaParameterType)exprType).getParameter(); - final int parameterIndex = lambdaExpression.getParameterList().getParameterIndex(parameter); - if (parameterIndex > -1) { - exprType = subst.substitute(methodParameters[parameterIndex].getType()); - } - } else if (exprType instanceof PsiLambdaExpressionType) { - return inferConstraintFromFunctionalInterfaceMethod(typeParam, ((PsiLambdaExpressionType)exprType).getExpression(), returnType, - lowerBound); - } else if (exprType == null && independent) { - return null; - } - - if (exprType == null){ - return FAILED_INFERENCE; - } - - final Pair<PsiType, ConstraintType> returnExprConstraint = - getSubstitutionForTypeParameterConstraint(typeParam, GenericsUtil.eliminateWildcards(returnType), exprType, true, PsiUtil.getLanguageLevel(method)); - if (returnExprConstraint != null) { - if (returnExprConstraint == FAILED_INFERENCE) return returnExprConstraint; - if (constraint != null) { - final PsiType leastUpperBound = GenericsUtil.getLeastUpperBound(constraint.getFirst(), returnExprConstraint.getFirst(), myManager); - constraint = new Pair<PsiType, ConstraintType>(leastUpperBound, ConstraintType.SUPERTYPE); - } else { - constraint = returnExprConstraint; - } - } - } - if (constraint != null) return constraint; - } - } - return null; - } - - private static boolean methodParamsDependOn(PsiTypeParameter typeParam, PsiElement psiElement, - PsiType functionalInterfaceType, - PsiParameter[] methodParameters, - PsiSubstitutor subst) { - for (PsiParameter parameter : methodParameters) { - if (LambdaUtil.dependsOnTypeParams(subst.substitute(parameter.getType()), functionalInterfaceType, psiElement, typeParam)) { - return true; - } - } - return false; - } - - @Nullable - private Pair<PsiType, ConstraintType> inferConstraintFromLambdaFormalParams(PsiTypeParameter typeParam, - PsiSubstitutor subst, - PsiMethod method, PsiLambdaExpression lambdaExpression) { - final PsiParameter[] parameters = lambdaExpression.getParameterList().getParameters(); - if (parameters.length == 0) return null; - final PsiType[] lambdaArgs = new PsiType[parameters.length]; - for (int i = 0; i < parameters.length; i++) { - PsiParameter parameter = parameters[i]; - if (parameter.getTypeElement() == null) { - return null; - } - lambdaArgs[i] = parameter.getType(); - } - - final PsiParameter[] methodParameters = method.getParameterList().getParameters(); - PsiType[] methodParamTypes = new PsiType[methodParameters.length]; - for (int i = 0; i < methodParameters.length; i++) { - methodParamTypes[i] = GenericsUtil.eliminateWildcards(subst.substitute(methodParameters[i].getType())); - } - return inferTypeForMethodTypeParameterInner(typeParam, methodParamTypes, lambdaArgs, subst, null, DefaultParameterTypeInferencePolicy.INSTANCE); - } - - private static PsiType intersectAllExtends(PsiTypeParameter typeParam, PsiType arg) { - if (arg == null) return null; - PsiClassType[] superTypes = typeParam.getSuperTypes(); - PsiType[] erasureTypes = new PsiType[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 (arg instanceof PsiCapturedWildcardType && (depth < 2 || constraintType != ConstraintType.EQUALS)) arg = ((PsiCapturedWildcardType)arg).getWildcard(); //reopen - - if (patternType.equals(param)) { - return processArgType(arg, constraintType, depth < 2); - } - - 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 new Pair<PsiType, ConstraintType>(lowerBound, ConstraintType.SUPERTYPE); - if (upperBound != PsiType.NULL) return new Pair<PsiType, ConstraintType>(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 PsiReturnStatement) { - final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(parent, PsiLambdaExpression.class); - if (lambdaExpression != null) { - return getFailedInferenceConstraint(typeParameter); - } - } - else 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); - if (constraint == null && PsiUtil.isLanguageLevel8OrHigher(methodCall)) { - constraint = graphInferenceFromCallContext(methodCall, typeParameter, (PsiCallExpression)pParent); - if (constraint != null) { - final PsiType constraintFirst = constraint.getFirst(); - if (constraintFirst == null || constraintFirst.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) { - constraint = null; - } - } - } - } - } else if (parent instanceof PsiLambdaExpression) { - expectedType = ourGraphGuard.doPreventingRecursion(methodCall, true, new Computable<PsiType>() { - @Override - public PsiType compute() { - return LambdaUtil.getFunctionalInterfaceReturnType(((PsiLambdaExpression)parent).getFunctionalInterfaceType()); - } - }); - if (expectedType == null) { - return null; - } - expectedType = GenericsUtil.eliminateWildcards(expectedType); - } else if (parent instanceof PsiConditionalExpression) { - if (PsiUtil.isLanguageLevel8OrHigher(parent)) { - try { - final Pair<PsiType, ConstraintType> pair = inferFromConditionalExpression(parent, methodCall, typeParameter, substitutor, policy); - if (pair != null) { - return pair; - } - } - finally { - GraphInferencePolicy.forget(parent); - } - } - } - } - - 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) { - for (PsiType conjuct : ((PsiIntersectionType)guess).getConjuncts()) { - if (!conjuct.isAssignableFrom(expectedType)) { - return FAILED_INFERENCE; - } - } - } - } - } - - if (constraint == null) { - if (methodCall instanceof PsiCallExpression) { - final PsiExpressionList argumentList = ((PsiCallExpression)methodCall).getArgumentList(); - if (argumentList != null && PsiUtil.getLanguageLevel(argumentList).isAtLeast(LanguageLevel.JDK_1_8)) { - for (PsiExpression expression : argumentList.getExpressions()) { - if (expression instanceof PsiLambdaExpression || expression instanceof PsiMethodReferenceExpression) { - final PsiType functionalInterfaceType = LambdaUtil.getFunctionalInterfaceType(expression, false); - if (functionalInterfaceType == null || PsiUtil.resolveClassInType(functionalInterfaceType) == typeParameter){ - return getFailedInferenceConstraint(typeParameter); - } - final PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType); - - final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType); - if (method == null || methodParamsDependOn(typeParameter, expression, - functionalInterfaceType, method.getParameterList().getParameters(), - LambdaUtil.getSubstitutor(method, resolveResult))) { - if (expression instanceof PsiMethodReferenceExpression) { - return getFailedInferenceConstraint(typeParameter); - } - return null; - } - final Pair<PsiType, ConstraintType> inferredExceptionTypeConstraint = inferExceptionConstrains(typeParameter, expression, method, resolveResult.getSubstitutor()); - if (inferredExceptionTypeConstraint != null) { - return inferredExceptionTypeConstraint; - } - } - } - } - - PsiType[] superTypes = typeParameter.getSuperTypes(); - if (superTypes.length == 0) return null; - final PsiType[] types = new PsiType[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); - if (superType == null) return null; - 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 new Pair<PsiType, ConstraintType>(guess, constraint.getSecond()); - } - - private static boolean checkSameExpression(PsiExpression templateExpr, final PsiExpression expression) { - return templateExpr.equals(PsiUtil.skipParenthesizedExprDown(expression)); - } - - private static Pair<PsiType, ConstraintType> inferExceptionConstrains(PsiTypeParameter typeParameter, - PsiExpression expression, - PsiMethod method, - PsiSubstitutor substitutor) { - final PsiClassType[] declaredExceptions = method.getThrowsList().getReferencedTypes(); - for (PsiClassType exception : declaredExceptions) { - final PsiType substitute = substitutor.substitute(exception); - if (PsiUtil.resolveClassInType(substitute) == typeParameter) { - if (expression instanceof PsiLambdaExpression) { - final PsiElement body = ((PsiLambdaExpression)expression).getBody(); - if (body != null) { - final List<PsiClassType> unhandledExceptions = ExceptionUtil.getUnhandledExceptions(body); - if (unhandledExceptions.isEmpty()) { - return inferUncheckedException(typeParameter, exception, method); - } - } - } - else if (expression instanceof PsiMethodReferenceExpression) { - final PsiElement resolve = ((PsiMethodReferenceExpression)expression).resolve(); - if (resolve instanceof PsiMethod) { - final PsiClassType[] declaredThrowsList = ((PsiMethod)resolve).getThrowsList().getReferencedTypes(); - for (PsiClassType psiClassType : declaredThrowsList) { - if (!ExceptionUtil.isUncheckedException(psiClassType)) return null; - } - return inferUncheckedException(typeParameter, exception, method); - } - } - break; - } - } - return null; - } - - private static Pair<PsiType, ConstraintType> inferUncheckedException(PsiTypeParameter typeParameter, - PsiClassType exception, - PsiMethod method) { - final Project project = typeParameter.getProject(); - final PsiClass runtimeException = JavaPsiFacade.getInstance(project).findClass(CommonClassNames.JAVA_LANG_RUNTIME_EXCEPTION, method.getResolveScope()); - if (runtimeException != null) { - for (PsiType superType : exception.getSuperTypes()) { - if (!InheritanceUtil.isInheritorOrSelf(runtimeException, PsiUtil.resolveClassInType(superType), true)) { - return getFailedInferenceConstraint(typeParameter); - } - } - return Pair.<PsiType, ConstraintType>create(JavaPsiFacade.getElementFactory(project).createType(runtimeException, PsiSubstitutor.EMPTY), ConstraintType.EQUALS); - } - return null; - } - - private Pair<PsiType, ConstraintType> inferFromConditionalExpression(@NotNull PsiElement parent, - @NotNull PsiExpression methodCall, - @NotNull PsiTypeParameter typeParameter, - @NotNull PsiSubstitutor substitutor, - @NotNull ParameterTypeInferencePolicy policy) { - Pair<PsiType, ConstraintType> pair = - inferMethodTypeParameterFromParent(PsiUtil.skipParenthesizedExprUp(parent.getParent()), (PsiExpression)parent, typeParameter, substitutor, policy); - if (pair == null) { - final PsiExpression thenExpression = ((PsiConditionalExpression)parent).getThenExpression(); - final PsiExpression elseExpression = ((PsiConditionalExpression)parent).getElseExpression(); - final PsiType[] paramTypes = {((PsiMethod)typeParameter.getOwner()).getReturnType()}; - if (methodCall.equals(PsiUtil.skipParenthesizedExprDown(elseExpression)) && thenExpression != null) { - final PsiType thenType = ourGraphGuard.doPreventingRecursion(parent, true, new Computable<PsiType>() { - @Override - public PsiType compute() { - return thenExpression.getType(); - } - }); - if (thenType != null) { - pair = inferTypeForMethodTypeParameterInner(typeParameter, paramTypes, new PsiType[] {thenType}, substitutor, null, policy); - } - } else if (methodCall.equals(PsiUtil.skipParenthesizedExprDown(thenExpression)) && elseExpression != null) { - final PsiType elseType = ourGraphGuard.doPreventingRecursion(parent, true, new Computable<PsiType>() { - @Override - public PsiType compute() { - return elseExpression.getType(); - } - }); - if (elseType != null) { - pair = inferTypeForMethodTypeParameterInner(typeParameter, paramTypes, new PsiType[] {elseType}, substitutor, null, policy); - } - } - } - return pair; - } - - private static final ProcessCandidateParameterTypeInferencePolicy GRAPH_INFERENCE_POLICY = new GraphInferencePolicy(); - - private static Pair<PsiType, ConstraintType> graphInferenceFromCallContext(@NotNull final PsiExpression methodCall, - @NotNull final PsiTypeParameter typeParameter, - @NotNull final PsiCallExpression parentCall) { - if (Registry.is("disable.graph.inference", false)) return null; - final PsiExpressionList argumentList = parentCall.getArgumentList(); - if (PsiDiamondType.ourDiamondGuard.currentStack().contains(parentCall)) { - PsiDiamondType.ourDiamondGuard.prohibitResultCaching(parentCall); - return FAILED_INFERENCE; + public PsiInferenceHelper getInferenceHelper(LanguageLevel languageLevel) { + if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8) && Registry.is("enable.graph.inference", false)) { + return new PsiGraphInferenceHelper(myManager); } - return ourGraphGuard.doPreventingRecursion(methodCall, true, new Computable<Pair<PsiType, ConstraintType>>() { - @Override - public Pair<PsiType, ConstraintType> compute() { - return GRAPH_INFERENCE_POLICY.inferTypeConstraintFromCallContext(methodCall, argumentList, parentCall, typeParameter); - } - }); + return new PsiOldInferenceHelper(myManager); } } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceBound.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceBound.java new file mode 100644 index 000000000000..df63a7cfec88 --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceBound.java @@ -0,0 +1,25 @@ +/* + * Copyright 2000-2013 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.graphInference; + +/** +* User: anna +*/ +public enum InferenceBound { + UPPER, + LOWER, + EQ +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceIncorporationPhase.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceIncorporationPhase.java new file mode 100644 index 000000000000..a63e43824986 --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceIncorporationPhase.java @@ -0,0 +1,187 @@ +/* + * Copyright 2000-2013 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.graphInference; + +import com.intellij.psi.PsiType; +import com.intellij.psi.impl.source.resolve.graphInference.constraints.ConstraintFormula; +import com.intellij.psi.impl.source.resolve.graphInference.constraints.SubtypingConstraint; +import com.intellij.psi.impl.source.resolve.graphInference.constraints.TypeEqualityConstraint; + +import java.util.List; + +/** + * User: anna + */ +public class InferenceIncorporationPhase { + private InferenceSession mySession; + + public InferenceIncorporationPhase(InferenceSession session) { + mySession = session; + } + + public void incorporate() { + for (InferenceVariable inferenceVariable : mySession.getInferenceVariables()) { + if (inferenceVariable.getInstantiation() != PsiType.NULL) continue; + final List<PsiType> eqBounds = inferenceVariable.getBounds(InferenceBound.EQ); + final List<PsiType> upperBounds = inferenceVariable.getBounds(InferenceBound.UPPER); + final List<PsiType> lowerBounds = inferenceVariable.getBounds(InferenceBound.LOWER); + /* + todo inference errors + Infer infer = inferenceContext.infer(); + for (Type e : uv.getBounds(InferenceBound.EQ)) { + if (e.containsAny(inferenceContext.inferenceVars())) continue; + for (Type u : uv.getBounds(InferenceBound.UPPER)) { + if (!isSubtype(e, inferenceContext.asFree(u), warn, infer)) { + infer.reportBoundError(uv, BoundErrorKind.BAD_EQ_UPPER); + } + } + for (Type l : uv.getBounds(InferenceBound.LOWER)) { + if (!isSubtype(inferenceContext.asFree(l), e, warn, infer)) { + infer.reportBoundError(uv, BoundErrorKind.BAD_EQ_LOWER); + } + } + } + */ + + eqEq(eqBounds); + upperLower(upperBounds, lowerBounds); + + upDown(eqBounds, upperBounds); + upDown(lowerBounds, eqBounds); + } + } + + boolean isFullyIncorporated() { + boolean needFurtherIncorporation = false; + for (InferenceVariable inferenceVariable : mySession.getInferenceVariables()) { + if (inferenceVariable.getInstantiation() != PsiType.NULL) continue; + final List<PsiType> eqBounds = inferenceVariable.getBounds(InferenceBound.EQ); + final List<PsiType> upperBounds = inferenceVariable.getBounds(InferenceBound.UPPER); + final List<PsiType> lowerBounds = inferenceVariable.getBounds(InferenceBound.LOWER); + needFurtherIncorporation |= crossVariables(inferenceVariable, upperBounds, lowerBounds, InferenceBound.LOWER); + needFurtherIncorporation |= crossVariables(inferenceVariable, lowerBounds, upperBounds, InferenceBound.UPPER); + + needFurtherIncorporation |= eqCrossVariables(inferenceVariable, eqBounds); + } + return !needFurtherIncorporation; + } + + /** + * a = b imply every bound of a matches a bound of b and vice versa + */ + private boolean eqCrossVariables(InferenceVariable inferenceVariable, List<PsiType> eqBounds) { + boolean needFurtherIncorporation = false; + for (PsiType eqBound : eqBounds) { + final InferenceVariable inferenceVar = mySession.getInferenceVariable(eqBound); + if (inferenceVar != null) { + if (inferenceVar.isCaptured()) continue; + //inferenceVar.addBound(inferenceVariable.qType, InferenceVariable.InferenceBound.EQ); + for (InferenceBound inferenceBound : InferenceBound.values()) { + for (PsiType bound : inferenceVariable.getBounds(inferenceBound)) { + if (mySession.getInferenceVariable(bound) != inferenceVar) { + needFurtherIncorporation |= inferenceVar.addBound(bound, inferenceBound); + } + } + for (PsiType bound : inferenceVar.getBounds(inferenceBound)) { + if (mySession.getInferenceVariable(bound) != inferenceVariable) { + needFurtherIncorporation |= inferenceVariable.addBound(bound, inferenceBound); + } + } + } + } + } + return needFurtherIncorporation; + } + + /** + * a < b & S <: a & b <: T imply S <: b & a <: T + */ + private boolean crossVariables(InferenceVariable inferenceVariable, + List<PsiType> upperBounds, + List<PsiType> lowerBounds, + InferenceBound inferenceBound) { + + final InferenceBound oppositeBound = inferenceBound == InferenceBound.LOWER + ? InferenceBound.UPPER + : InferenceBound.LOWER; + boolean result = false; + for (PsiType upperBound : upperBounds) { + final InferenceVariable inferenceVar = mySession.getInferenceVariable(upperBound); + if (inferenceVar != null) { + if (inferenceVar.isCaptured()) continue; + //todo inferenceVar.addBound(inferenceVariable.qType, inferenceBound); + for (PsiType lowerBound : lowerBounds) { + result |= inferenceVar.addBound(lowerBound, inferenceBound); + } + + + for (PsiType varUpperBound : inferenceVar.getBounds(oppositeBound)) { + result |= inferenceVariable.addBound(varUpperBound, oppositeBound); + } + } + } + return result; + } + + /** + * a = S & a <: T imply S <: T + * or + * a = S & T <: a imply T <: S + */ + private void upDown(List<PsiType> eqBounds, List<PsiType> upperBounds) { + for (PsiType upperBound : upperBounds) { + final boolean properType = mySession.isProperType(upperBound); + for (PsiType eqBound : eqBounds) { + if (properType && mySession.isProperType(eqBound)) continue; + if (!upperBound.equals(eqBound)) { + addConstraint(new SubtypingConstraint(upperBound, eqBound, true)); + } + } + } + } + + /** + * S <: a & a <: T imply S <: T + */ + private void upperLower(List<PsiType> upperBounds, List<PsiType> lowerBounds) { + for (PsiType upperBound : upperBounds) { + final boolean properType = mySession.isProperType(upperBound); + for (PsiType lowerBound : lowerBounds) { + if (properType && mySession.isProperType(lowerBound)) continue; + if (!upperBound.equals(lowerBound)) { + addConstraint(new SubtypingConstraint(upperBound, lowerBound, true)); + } + } + } + } + + /** + * a = S & a = T imply S = T + */ + private void eqEq(List<PsiType> eqBounds) { + for (int i = 0; i < eqBounds.size(); i++) { + PsiType sBound= eqBounds.get(i); + for (int j = i + 1; j < eqBounds.size(); j++) { + final PsiType tBound = eqBounds.get(j); + addConstraint(new TypeEqualityConstraint(tBound, sBound)); + } + } + } + + private void addConstraint(ConstraintFormula constraint) { + mySession.addConstraint(constraint); + } +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java new file mode 100644 index 000000000000..dba4a7289a5e --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java @@ -0,0 +1,333 @@ +/* + * Copyright 2000-2013 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.graphInference; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Pair; +import com.intellij.psi.*; +import com.intellij.psi.impl.source.resolve.graphInference.constraints.ConstraintFormula; +import com.intellij.psi.impl.source.resolve.graphInference.constraints.ExpressionCompatibilityConstraint; +import com.intellij.psi.impl.source.resolve.graphInference.constraints.TypeCompatibilityConstraint; +import com.intellij.psi.infos.MethodCandidateInfo; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.TypeConversionUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * User: anna + */ +public class InferenceSession { + private static final Logger LOG = Logger.getInstance("#" + InferenceSession.class.getName()); + + private Map<PsiTypeParameter, InferenceVariable> myInferenceVariables = new LinkedHashMap<PsiTypeParameter, InferenceVariable>(); + private final List<ConstraintFormula> myConstraints = new ArrayList<ConstraintFormula>(); + private final List<ConstraintFormula> myDelayedConstraints = new ArrayList<ConstraintFormula>(); + + private PsiSubstitutor mySiteSubstitutor; + private PsiManager myManager; + private int myConstraintIdx = 0; + + private final InferenceIncorporationPhase myIncorporationPhase = new InferenceIncorporationPhase(this); + + public InferenceSession(PsiTypeParameter[] typeParams, + PsiType[] leftTypes, + PsiType[] rightTypes, + PsiSubstitutor siteSubstitutor, + PsiManager manager) { + myManager = manager; + mySiteSubstitutor = siteSubstitutor; + + initBounds(typeParams); + + LOG.assertTrue(leftTypes.length == rightTypes.length); + for (int i = 0; i < leftTypes.length; i++) { + myConstraints.add(new TypeCompatibilityConstraint(leftTypes[i], mySiteSubstitutor.substitute(rightTypes[i]))); + } + } + + public InferenceSession(PsiTypeParameter[] typeParams, + PsiParameter[] parameters, + PsiExpression[] args, + PsiSubstitutor siteSubstitutor, + PsiElement parent, + PsiManager manager) { + myManager = manager; + mySiteSubstitutor = siteSubstitutor; + + initBounds(typeParams); + + if (parameters.length > 0) { + for (int i = 0; i < args.length; i++) { + PsiType parameterType = mySiteSubstitutor.substitute(parameters[i < parameters.length ? i : parameters.length - 1].getType()); + if (parameterType instanceof PsiEllipsisType) { + if (args.length != parameters.length || args[i] != null && !(args[i].getType() instanceof PsiArrayType)) { + parameterType = ((PsiEllipsisType)parameterType).getComponentType(); + } + } + if (args[i] != null) { + myConstraints.add(new ExpressionCompatibilityConstraint(args[i], parameterType)); + } + } + } + + if (parent instanceof PsiCallExpression) { + final Map<PsiElement, Pair<PsiMethod, PsiSubstitutor>> map = MethodCandidateInfo.CURRENT_CANDIDATE.get(); + if (map != null) { + final Pair<PsiMethod, PsiSubstitutor> pair = map.get(((PsiCallExpression)parent).getArgumentList()); + if (pair != null) { + initReturnTypeConstraint(pair.first, (PsiCallExpression)parent); + } + } + } + } + + @NotNull + public PsiSubstitutor infer() { + repeatInferencePhases(); + + for (InferenceVariable inferenceVariable : myInferenceVariables.values()) { + final PsiTypeParameter typeParameter = inferenceVariable.getParameter(); + PsiType instantiation = inferenceVariable.getInstantiation(); + if (instantiation == PsiType.NULL) { + //failed inference + mySiteSubstitutor = mySiteSubstitutor + .put(typeParameter, JavaPsiFacade.getInstance(typeParameter.getProject()).getElementFactory().createType(typeParameter)); + } + } + return mySiteSubstitutor; + } + + private void initBounds(PsiTypeParameter[] typeParameters) { + for (PsiTypeParameter parameter : typeParameters) { + myInferenceVariables.put(parameter, new InferenceVariable(parameter)); + } + + for (InferenceVariable variable : myInferenceVariables.values()) { + final PsiTypeParameter parameter = variable.getParameter(); + boolean added = false; + final PsiClassType[] extendsListTypes = parameter.getExtendsListTypes(); + for (PsiType classType : extendsListTypes) { + classType = mySiteSubstitutor.substitute(classType); + if (isProperType(classType)) { + added = true; + } + variable.addBound(classType, InferenceBound.UPPER); + } + if (!added) { + variable.addBound(PsiType.getJavaLangObject(parameter.getManager(), parameter.getResolveScope()), + InferenceBound.UPPER); + } + } + } + + private void initReturnTypeConstraint(PsiMethod method, PsiCallExpression context) { + if (PsiPolyExpressionUtil.isPolyExpression(context) || + context instanceof PsiNewExpression && PsiDiamondType.ourDiamondGuard.currentStack().contains(context)) { + final PsiType returnType = method.getReturnType(); + if (!PsiType.VOID.equals(returnType) && returnType != null) { + final PsiType targetType = PsiPolyExpressionUtil.getTargetType(context);//todo primitive type + if (targetType != null) { + myConstraints.add(new TypeCompatibilityConstraint(targetType, returnType)); + } + } + } + } + + public InferenceVariable getInferenceVariable(PsiType psiType) { + final PsiClass psiClass = PsiUtil.resolveClassInClassTypeOnly(psiType); + if (psiClass instanceof PsiTypeParameter) { + final InferenceVariable inferenceVariable = myInferenceVariables.get(psiClass); + if (inferenceVariable != null) { + return inferenceVariable; + } + } + return null; + } + + public boolean isProperType(@Nullable PsiType type) { + return collectDependencies(type, null); + } + + public boolean collectDependencies(@Nullable PsiType type, @Nullable final Set<InferenceVariable> dependencies) { + if (type == null) return true; + final Boolean isProper = type.accept(new PsiTypeVisitor<Boolean>() { + @Nullable + @Override + public Boolean visitType(PsiType type) { + return true; + } + + @Nullable + @Override + public Boolean visitArrayType(PsiArrayType arrayType) { + return arrayType.getComponentType().accept(this); + } + + @Nullable + @Override + public Boolean visitWildcardType(PsiWildcardType wildcardType) { + final PsiType bound = wildcardType.getBound(); + if (bound == null) return true; + return bound.accept(this); + } + + @Nullable + @Override + public Boolean visitClassType(PsiClassType classType) { + final InferenceVariable inferenceVariable = getInferenceVariable(classType); + if (inferenceVariable != null) { + if (dependencies != null) { + dependencies.add(inferenceVariable); + return true; + } + return false; + } + for (PsiType psiType : classType.getParameters()) { + if (!psiType.accept(this)) return false; + } + return true; + } + }); + return dependencies != null ? !dependencies.isEmpty() : isProper; + } + + private void repeatInferencePhases() { + do { + if (!reduceConstraints()) { + //inference error occurred + return; + } + myIncorporationPhase.incorporate(); + + } while (!myIncorporationPhase.isFullyIncorporated() || myConstraintIdx < myConstraints.size()); + + resolveBounds(); + } + + private boolean reduceConstraints() { + List<ConstraintFormula> newConstraints = new ArrayList<ConstraintFormula>(); + for (int i = myConstraintIdx; i < myConstraints.size(); i++) { + ConstraintFormula constraint = myConstraints.get(i); + if (!constraint.reduce(this, newConstraints, myDelayedConstraints)) { + return false; + } + } + myConstraintIdx = myConstraints.size(); + for (ConstraintFormula constraint : newConstraints) { + addConstraint(constraint); + } + return true; + } + + private void resolveBounds() { + final List<List<InferenceVariable>> independentVars = InferenceVariablesOrder.resolveOrder(myInferenceVariables.values(), this); + for (List<InferenceVariable> variables : independentVars) { + for (InferenceVariable inferenceVariable : variables) { + + if (inferenceVariable.getInstantiation() != PsiType.NULL) continue; + final PsiTypeParameter typeParameter = inferenceVariable.getParameter(); + try { + final List<PsiType> eqBounds = inferenceVariable.getBounds(InferenceBound.EQ); + final List<PsiType> lowerBounds = inferenceVariable.getBounds(InferenceBound.LOWER); + final List<PsiType> upperBounds = inferenceVariable.getBounds(InferenceBound.UPPER); + if (/*eqBounds.contains(null) || lowerBounds.contains(null) || */upperBounds.contains(null)) { + inferenceVariable.setInstantiation(null); + continue; + } + PsiType bound = null; + for (PsiType eqBound : eqBounds) { + eqBound = acceptBoundsWithRecursiveDependencies(typeParameter, eqBound); + if (isProperType(eqBound)) { + bound = eqBound; + break; + } + } + if (bound != null) { + inferenceVariable.setInstantiation(bound); + } else { + PsiType lub = null; + for (PsiType lowerBound : lowerBounds) { + lowerBound = acceptBoundsWithRecursiveDependencies(typeParameter, lowerBound); + if (isProperType(lowerBound)) { + if (lub == null) { + lub = lowerBound; + } + else { + lub = GenericsUtil.getLeastUpperBound(lub, lowerBound, myManager); + } + } + } + if (lub != null) { + inferenceVariable.setInstantiation(lub instanceof PsiCapturedWildcardType ? ((PsiCapturedWildcardType)lub).getWildcard() : lub); + } + else { + PsiType glb = null; + for (PsiType upperBound : upperBounds) { + upperBound = acceptBoundsWithRecursiveDependencies(typeParameter, upperBound); + if (isProperType(upperBound)) { + if (glb == null) { + glb = upperBound; + } + else { + glb = GenericsUtil.getGreatestLowerBound(glb, upperBound); + } + } + } + if (glb != null) { + inferenceVariable.setInstantiation(glb); + } + } + } + } + finally { + final PsiType instantiation = inferenceVariable.getInstantiation(); + if (instantiation != PsiType.NULL) { + mySiteSubstitutor = mySiteSubstitutor.put(typeParameter, instantiation); + } + } + } + } + } + + private PsiType acceptBoundsWithRecursiveDependencies(PsiTypeParameter typeParameter, PsiType bound) { + if (PsiPolyExpressionUtil.mentionsTypeParameters(bound, Collections.singleton(typeParameter))) { + return mySiteSubstitutor.put(typeParameter, null).substitute(bound); + } + return bound; + } + + public PsiManager getManager() { + return myManager; + } + + public GlobalSearchScope getScope() { + return GlobalSearchScope.allScope(myManager.getProject()); + } + + public Collection<InferenceVariable> getInferenceVariables() { + return myInferenceVariables.values(); + } + + public void addConstraint(ConstraintFormula constraint) { + if (!myConstraints.contains(constraint)) { + myConstraints.add(constraint); + } + } +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariable.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariable.java new file mode 100644 index 000000000000..e69f229fe91f --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariable.java @@ -0,0 +1,72 @@ +/* + * Copyright 2000-2013 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.graphInference; + +import com.intellij.psi.*; + +import java.util.*; + +/** + * User: anna + */ +public class InferenceVariable { + private boolean myCaptured; + + public PsiTypeParameter getParameter() { + return myParameter; + } + + private Map<InferenceBound, List<PsiType>> myBounds = new HashMap<InferenceBound, List<PsiType>>(); + private PsiTypeParameter myParameter; + + private PsiType myInstantiation = PsiType.NULL; + public InferenceVariable(PsiTypeParameter parameter) { + myParameter = parameter; + } + public PsiType getInstantiation() { + return myInstantiation; + } + + public void setInstantiation(PsiType instantiation) { + myInstantiation = instantiation; + } + + public boolean isCaptured() { + return myCaptured; + } + + public void setCaptured(boolean captured) { + myCaptured = captured; + } + + public boolean addBound(PsiType classType, InferenceBound inferenceBound) { + List<PsiType> list = myBounds.get(inferenceBound); + if (list == null) { + list = new ArrayList<PsiType>(); + myBounds.put(inferenceBound, list); + } + if (!list.contains(classType)) { + list.add(classType); + return true; + } + return false; + } + + public List<PsiType> getBounds(InferenceBound inferenceBound) { + final List<PsiType> bounds = myBounds.get(inferenceBound); + return bounds != null ? new ArrayList<PsiType>(bounds) : Collections.<PsiType>emptyList(); + } +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariablesOrder.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariablesOrder.java new file mode 100644 index 000000000000..4a2c4126611e --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariablesOrder.java @@ -0,0 +1,184 @@ +/* + * Copyright 2000-2013 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.graphInference; + +import com.intellij.psi.PsiType; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; + +import java.util.*; + +/** + * User: anna + * Date: 7/4/13 + */ + +public class InferenceVariablesOrder { + + public static List<List<InferenceVariable>> resolveOrder(Collection<InferenceVariable> vars, InferenceSession session) { + Map<InferenceVariable, InferenceGraphNode<InferenceVariable>> nodes = + new HashMap<InferenceVariable, InferenceGraphNode<InferenceVariable>>(); + for (InferenceVariable var : vars) { + nodes.put(var, new InferenceGraphNode<InferenceVariable>(var)); + } + + for (InferenceVariable var : vars) { + if (var.getInstantiation() != PsiType.NULL) continue; + final InferenceGraphNode<InferenceVariable> node = nodes.get(var); + for (InferenceBound inferenceBound : InferenceBound.values()) { + for (PsiType bound : var.getBounds(inferenceBound)) { + final HashSet<InferenceVariable> dependencies = new HashSet<InferenceVariable>(); + session.collectDependencies(bound, dependencies); + for (InferenceVariable dependentVariable : dependencies) { + final InferenceGraphNode<InferenceVariable> dependency = nodes.get(dependentVariable); + if (dependency != null) { + node.addDependency(dependency); + } + } + } + } + } + final ArrayList<InferenceGraphNode<InferenceVariable>> acyclicNodes = initNodes(nodes.values()); + return ContainerUtil.map(acyclicNodes, new Function<InferenceGraphNode<InferenceVariable>, List<InferenceVariable>>() { + @Override + public List<InferenceVariable> fun(InferenceGraphNode<InferenceVariable> node) { + return node.getValue(); + } + }); + } + + public static <T> List<List<InferenceGraphNode<T>>> tarjan(Collection<InferenceGraphNode<T>> nodes) { + final ArrayList<List<InferenceGraphNode<T>>> result = new ArrayList<List<InferenceGraphNode<T>>>(); + final Stack<InferenceGraphNode<T>> currentStack = new Stack<InferenceGraphNode<T>>(); + int index = 0; + for (InferenceGraphNode<T> node : nodes) { + if (node.index == -1) { + index += InferenceGraphNode.strongConnect(node, index, currentStack, result); + } + } + return result; + } + + public static <T> ArrayList<InferenceGraphNode<T>> initNodes(Collection<InferenceGraphNode<T>> allNodes) { + final List<List<InferenceGraphNode<T>>> nodes = tarjan(allNodes); + final ArrayList<InferenceGraphNode<T>> acyclicNodes = new ArrayList<InferenceGraphNode<T>>(); + for (List<InferenceGraphNode<T>> cycle : nodes) { + acyclicNodes.add(InferenceGraphNode.merge(cycle, allNodes)); + } + return acyclicNodes; + } + + public static class InferenceGraphNode<T> { + private final List<T> myValue = new ArrayList<T>(); + private Set<InferenceGraphNode<T>> myDependencies = new HashSet<InferenceGraphNode<T>>(); + + private int index = -1; + private int lowlink; + + public InferenceGraphNode(T value) { + myValue.add(value); + } + + public List<T> getValue() { + return myValue; + } + + public Set<InferenceGraphNode<T>> getDependencies() { + return myDependencies; + } + + public void addDependency(InferenceGraphNode<T> node) { + myDependencies.add(node); + } + + + private static <T> InferenceGraphNode<T> merge(final List<InferenceGraphNode<T>> cycle, + final Collection<InferenceGraphNode<T>> allNodes) { + assert !cycle.isEmpty(); + final InferenceGraphNode<T> root = cycle.get(0); + if (cycle.size() > 1) { + for (int i = 1; i < cycle.size(); i++) { + final InferenceGraphNode<T> cycleNode = cycle.get(i); + + root.copyFrom(cycleNode); + root.filterInterCycleDependencies(); + + for (InferenceGraphNode<T> node : allNodes) { + if (node.myDependencies.remove(cycleNode)) { + node.myDependencies.add(root); + } + } + } + } + return root; + } + + private void filterInterCycleDependencies() { + boolean includeSelfDependency = false; + for (Iterator<InferenceGraphNode<T>> iterator = myDependencies.iterator(); iterator.hasNext(); ) { + InferenceGraphNode<T> d = iterator.next(); + assert d.myValue.size() >= 1; + final T initialNodeValue = d.myValue.get(0); + if (myValue.contains(initialNodeValue)) { + includeSelfDependency = true; + iterator.remove(); + } + } + + if (includeSelfDependency) { + myDependencies.add(this); + } + } + + private void copyFrom(final InferenceGraphNode<T> cycleNode) { + myValue.addAll(cycleNode.myValue); + myDependencies.addAll(cycleNode.myDependencies); + } + + private static <T> int strongConnect(InferenceGraphNode<T> currentNode, + int index, + Stack<InferenceGraphNode<T>> currentStack, + ArrayList<List<InferenceGraphNode<T>>> result) { + currentNode.index = index; + currentNode.lowlink = index; + index++; + + currentStack.push(currentNode); + + for (InferenceGraphNode<T> dependantNode : currentNode.getDependencies()) { + if (dependantNode.index == -1) { + strongConnect(dependantNode, index, currentStack, result); + currentNode.lowlink = Math.min(currentNode.lowlink, dependantNode.lowlink); + } + else if (currentStack.contains(dependantNode)) { + currentNode.lowlink = Math.min(currentNode.lowlink, dependantNode.index); + } + } + + if (currentNode.lowlink == currentNode.index) { + final ArrayList<InferenceGraphNode<T>> arrayList = new ArrayList<InferenceGraphNode<T>>(); + InferenceGraphNode<T> cyclicNode; + do { + cyclicNode = currentStack.pop(); + arrayList.add(cyclicNode); + } + while (cyclicNode != currentNode); + result.add(arrayList); + } + return index; + } + } +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/PsiGraphInferenceHelper.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/PsiGraphInferenceHelper.java new file mode 100644 index 000000000000..372f67b7e8e3 --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/PsiGraphInferenceHelper.java @@ -0,0 +1,78 @@ +/* + * Copyright 2000-2013 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.graphInference; + +import com.intellij.pom.java.LanguageLevel; +import com.intellij.psi.*; +import com.intellij.psi.impl.source.resolve.ParameterTypeInferencePolicy; +import com.intellij.psi.impl.source.resolve.PsiOldInferenceHelper; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * User: anna + */ +public class PsiGraphInferenceHelper implements PsiInferenceHelper { + private final PsiManager myManager; + + public PsiGraphInferenceHelper(PsiManager manager) { + myManager = manager; + } + + @Override + public PsiType inferTypeForMethodTypeParameter(@NotNull PsiTypeParameter typeParameter, + @NotNull PsiParameter[] parameters, + @NotNull PsiExpression[] arguments, + @NotNull PsiSubstitutor partialSubstitutor, + @Nullable PsiElement parent, + @NotNull ParameterTypeInferencePolicy policy) { + final InferenceSession inferenceSession = + new InferenceSession(new PsiTypeParameter[]{typeParameter}, parameters, arguments, partialSubstitutor, parent, myManager); + return inferenceSession.infer().substitute(typeParameter); + } + + @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) { + if (typeParameters.length == 0) return partialSubstitutor; + return new InferenceSession(typeParameters, parameters, arguments, partialSubstitutor, parent, myManager).infer(); + } + + @NotNull + @Override + public PsiSubstitutor inferTypeArguments(@NotNull PsiTypeParameter[] typeParameters, + @NotNull PsiType[] leftTypes, + @NotNull PsiType[] rightTypes, + @NotNull LanguageLevel languageLevel) { + if (typeParameters.length == 0) return PsiSubstitutor.EMPTY; + return new InferenceSession(typeParameters, leftTypes, rightTypes, PsiSubstitutor.EMPTY, myManager).infer(); + } + + @Override + public PsiType getSubstitutionForTypeParameter(PsiTypeParameter typeParam, + PsiType param, + PsiType arg, + boolean isContraVariantPosition, + LanguageLevel languageLevel) { + return new PsiOldInferenceHelper(myManager).getSubstitutionForTypeParameter(typeParam, param, arg, isContraVariantPosition, languageLevel); + } +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/PsiPolyExpressionUtil.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/PsiPolyExpressionUtil.java index f3611e939bc4..2e425e0fb436 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/PsiPolyExpressionUtil.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/PsiPolyExpressionUtil.java @@ -16,7 +16,10 @@ package com.intellij.psi.impl.source.resolve.graphInference; import com.intellij.psi.*; +import com.intellij.psi.util.PsiTypesUtil; +import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Arrays; @@ -52,19 +55,19 @@ public class PsiPolyExpressionUtil { if (parameterList != null) { final PsiTypeElement[] typeElements = parameterList.getTypeParameterElements(); if (typeElements.length == 1 && typeElements[0].getType() instanceof PsiDiamondType) { - return isAssignmentOrInvocationContext(expression.getParent()); + return isInAssignmentOrInvocationContext(expression); } } } } else if (expression instanceof PsiMethodCallExpression) { - if (isAssignmentOrInvocationContext(expression.getParent()) && ((PsiMethodCallExpression)expression).getTypeArguments().length == 0) { + if (isInAssignmentOrInvocationContext(expression) && ((PsiMethodCallExpression)expression).getTypeArguments().length == 0) { final PsiMethod method = ((PsiMethodCallExpression)expression).resolveMethod(); if (method != null) { final Set<PsiTypeParameter> typeParameters = new HashSet<PsiTypeParameter>(Arrays.asList(method.getTypeParameters())); if (typeParameters.size() > 0) { final PsiType returnType = method.getReturnType(); if (returnType != null) { - return returnTypeMentionsTypeParameters(typeParameters, returnType); + return mentionsTypeParameters(returnType, typeParameters); } } } @@ -73,13 +76,18 @@ public class PsiPolyExpressionUtil { else if (expression instanceof PsiConditionalExpression) { final ConditionalKind conditionalKind = isBooleanOrNumeric(expression); if (conditionalKind == null) { - return isAssignmentOrInvocationContext(expression.getParent()); + return isInAssignmentOrInvocationContext(expression); } } return false; } - private static Boolean returnTypeMentionsTypeParameters(final Set<PsiTypeParameter> typeParameters, PsiType returnType) { + public static PsiType getTargetType(@NotNull PsiCallExpression expression) { + return PsiTypesUtil.getExpectedTypeByParent(expression); + } + + public static Boolean mentionsTypeParameters(@Nullable PsiType returnType, final Set<PsiTypeParameter> typeParameters) { + if (returnType == null) return false; return returnType.accept(new PsiTypeVisitor<Boolean>() { @Nullable @Override @@ -89,6 +97,16 @@ public class PsiPolyExpressionUtil { @Nullable @Override + public Boolean visitWildcardType(PsiWildcardType wildcardType) { + final PsiType bound = wildcardType.getBound(); + if (bound != null) { + return bound.accept(this); + } + return false; + } + + @Nullable + @Override public Boolean visitClassType(PsiClassType classType) { for (PsiType type : classType.getParameters()) { if (type.accept(this)) return true; @@ -105,12 +123,14 @@ public class PsiPolyExpressionUtil { }); } - private static boolean isAssignmentOrInvocationContext(PsiElement context) { - return context instanceof PsiExpressionList || isAssignmentContext(context); + private static boolean isInAssignmentOrInvocationContext(PsiExpression expr) { + final PsiElement context = expr.getParent(); + return context instanceof PsiExpressionList || isAssignmentContext(expr, context); } - private static boolean isAssignmentContext(PsiElement context) { - return context instanceof PsiReturnStatement || + private static boolean isAssignmentContext(PsiExpression expr, PsiElement context) { + return PsiUtil.isCondition(expr, context) || + context instanceof PsiReturnStatement || context instanceof PsiAssignmentExpression || context instanceof PsiVariable; } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/ConstraintFormula.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/ConstraintFormula.java new file mode 100644 index 000000000000..992d875bb535 --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/ConstraintFormula.java @@ -0,0 +1,27 @@ +/* + * Copyright 2000-2013 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.graphInference.constraints; + +import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession; + +import java.util.List; + +/** + * User: anna + */ +public interface ConstraintFormula { + boolean reduce(InferenceSession session, List<ConstraintFormula> constraints, List<ConstraintFormula> delayedConstraints); +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/ExpressionCompatibilityConstraint.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/ExpressionCompatibilityConstraint.java new file mode 100644 index 000000000000..a0e6a767d8f2 --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/ExpressionCompatibilityConstraint.java @@ -0,0 +1,107 @@ +/* + * Copyright 2000-2013 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.graphInference.constraints; + +import com.intellij.psi.*; +import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession; +import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil; +import com.intellij.psi.util.TypeConversionUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * User: anna + */ +public class ExpressionCompatibilityConstraint implements ConstraintFormula { + private PsiExpression myExpression; + private PsiType myT; + + public ExpressionCompatibilityConstraint(@NotNull PsiExpression expression, @NotNull PsiType type) { + myExpression = expression; + myT = type; + } + + @Override + public boolean reduce(InferenceSession session, List<ConstraintFormula> constraints, List<ConstraintFormula> delayedConstraints) { + if (session.isProperType(myT)) { + return TypeConversionUtil.areTypesAssignmentCompatible(myT, myExpression); + } + if (!PsiPolyExpressionUtil.isPolyExpression(myExpression)) { + final PsiType exprType = myExpression.getType(); + if (exprType != null && !exprType.equals(PsiType.NULL)) { + constraints.add(new TypeCompatibilityConstraint(myT, exprType)); + } + return true; + } + if (myExpression instanceof PsiParenthesizedExpression) { + final PsiExpression expression = ((PsiParenthesizedExpression)myExpression).getExpression(); + if (expression != null) { + constraints.add(new ExpressionCompatibilityConstraint(expression, myT)); + return true; + } + } + + if (myExpression instanceof PsiConditionalExpression) { + final PsiExpression thenExpression = ((PsiConditionalExpression)myExpression).getThenExpression(); + if (thenExpression != null) { + constraints.add(new ExpressionCompatibilityConstraint(thenExpression, myT)); + } + + final PsiExpression elseExpression = ((PsiConditionalExpression)myExpression).getElseExpression(); + if (elseExpression != null) { + constraints.add(new ExpressionCompatibilityConstraint(elseExpression, myT)); + } + return true; + } + + if (myExpression instanceof PsiCallExpression) { + //todo + } + + if (myExpression instanceof PsiMethodReferenceExpression) { + //todo + } + + if (myExpression instanceof PsiLambdaExpression) { + constraints.add(new LambdaExpressionCompatibilityConstraint((PsiLambdaExpression)myExpression, myT)); + return true; + } + + + return true; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ExpressionCompatibilityConstraint that = (ExpressionCompatibilityConstraint)o; + + if (!myExpression.equals(that.myExpression)) return false; + if (!myT.equals(that.myT)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = myExpression.hashCode(); + result = 31 * result + myT.hashCode(); + return result; + } +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/LambdaExpressionCompatibilityConstraint.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/LambdaExpressionCompatibilityConstraint.java new file mode 100644 index 000000000000..9d9dad46ffc8 --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/LambdaExpressionCompatibilityConstraint.java @@ -0,0 +1,75 @@ +package com.intellij.psi.impl.source.resolve.graphInference.constraints; + +import com.intellij.psi.*; +import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession; +import com.intellij.psi.impl.source.resolve.graphInference.InferenceVariable; +import com.intellij.psi.util.PsiUtil; + +import java.util.List; + +/** + * User: anna + */ +public class LambdaExpressionCompatibilityConstraint implements ConstraintFormula { + private final PsiLambdaExpression myExpression; + private final PsiType myT; + + public LambdaExpressionCompatibilityConstraint(PsiLambdaExpression expression, PsiType t) { + myExpression = expression; + myT = t; + } + + @Override + public boolean reduce(InferenceSession session, List<ConstraintFormula> constraints, List<ConstraintFormula> delayedConstraints) { + final InferenceVariable inferenceVariable = session.getInferenceVariable(myT); + if (inferenceVariable != null) { + delayedConstraints.add(this); + return true; + } + if (LambdaHighlightingUtil.checkInterfaceFunctional(myT) != null) { + return false; + } + + if (myExpression.hasFormalParameterTypes()) { + } + final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(myT); + if (interfaceMethod == null) { + return false; + } + final PsiSubstitutor substitutor = LambdaUtil.getSubstitutor(interfaceMethod, PsiUtil.resolveGenericsClassInType(myT)); + final PsiParameter[] parameters = interfaceMethod.getParameterList().getParameters(); + for (PsiParameter parameter : parameters) { + if (!session.isProperType(parameter.getType())) { + delayedConstraints.add(this); + return true; + } + } + + final PsiParameter[] lambdaParameters = myExpression.getParameterList().getParameters(); + if (lambdaParameters.length != parameters.length) { + return false; + } + if (myExpression.hasFormalParameterTypes()) { + for (int i = 0; i < lambdaParameters.length; i++) { + constraints.add(new TypeEqualityConstraint(lambdaParameters[i].getType(), substitutor.substitute(parameters[i].getType()))); + } + } + + final PsiType returnType = interfaceMethod.getReturnType(); + if (returnType != null) { + if (returnType.equals(PsiType.VOID)) { + if (!myExpression.isVoidCompatible() && !(myExpression.getBody() instanceof PsiExpression)) { + return false; + } + } else { + if (myExpression.isVoidCompatible()) { //not value-compatible + return false; + } + for (PsiExpression returnExpressions : LambdaUtil.getReturnExpressions(myExpression)) { + constraints.add(new ExpressionCompatibilityConstraint(returnExpressions, returnType)); + } + } + } + return true; + } +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/SubtypingConstraint.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/SubtypingConstraint.java new file mode 100644 index 000000000000..64c28d1b462f --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/SubtypingConstraint.java @@ -0,0 +1,175 @@ +/* + * Copyright 2000-2013 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.graphInference.constraints; + +import com.intellij.psi.*; +import com.intellij.psi.impl.source.resolve.graphInference.InferenceBound; +import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession; +import com.intellij.psi.impl.source.resolve.graphInference.InferenceVariable; +import com.intellij.psi.util.TypeConversionUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * User: anna + */ +public class SubtypingConstraint implements ConstraintFormula { + private PsiType myS; + private PsiType myT; + private boolean myIsRefTypes; + + public SubtypingConstraint(PsiType t, PsiType s, boolean isRefTypes) { + myT = t; + myS = s; + myIsRefTypes = isRefTypes; + } + + @Override + public boolean reduce(InferenceSession session, List<ConstraintFormula> constraints, List<ConstraintFormula> delayedConstraints) { + if (myIsRefTypes) { + if (session.isProperType(myS) && session.isProperType(myT)) { + if (myT == null || myS == null) return myS == myT; + return TypeConversionUtil.isAssignable(myT, myS); + } + InferenceVariable inferenceVariable = session.getInferenceVariable(myS); + if (inferenceVariable != null) { + inferenceVariable.addBound(myT, InferenceBound.UPPER); + return true; + } + if (PsiType.NULL.equals(myS)) return true; + inferenceVariable = session.getInferenceVariable(myT); + if (inferenceVariable != null) { + inferenceVariable.addBound(myS, InferenceBound.LOWER); + return true; + } + if (myT instanceof PsiArrayType) { + if (!(myS instanceof PsiArrayType)) return false; //todo most specific array supertype + final PsiType tComponentType = ((PsiArrayType)myT).getComponentType(); + final PsiType sComponentType = ((PsiArrayType)myS).getComponentType(); + if (!(tComponentType instanceof PsiPrimitiveType) && !(sComponentType instanceof PsiPrimitiveType)) { + constraints.add(new SubtypingConstraint(tComponentType, sComponentType, true)); + return true; + } + return sComponentType instanceof PsiPrimitiveType && sComponentType.equals(tComponentType); + } + if (myT instanceof PsiClassType) { + final PsiClassType.ClassResolveResult TResult = ((PsiClassType)myT).resolveGenerics(); + final PsiClass CClass = TResult.getElement(); + if (CClass != null) { + if (CClass instanceof PsiTypeParameter) { + if (myS instanceof PsiIntersectionType) { + for (PsiType conjunct : ((PsiIntersectionType)myS).getConjuncts()) { + if (myT.equals(conjunct)) return true; + } + } + //todo ((PsiTypeParameter)C).getLowerBound() + return false; + } + + if (!(myS instanceof PsiClassType)) return false; + PsiClassType.ClassResolveResult SResult = ((PsiClassType)myS).resolveGenerics(); + PsiClass SClass = SResult.getElement(); + final PsiSubstitutor tSubstitutor = TResult.getSubstitutor(); + final PsiSubstitutor sSubstitutor = SClass != null ? TypeConversionUtil.getClassSubstitutor(CClass, SClass, SResult.getSubstitutor()) : null; + if (sSubstitutor != null) { + for (PsiTypeParameter parameter : CClass.getTypeParameters()) { + final PsiType tSubstituted = tSubstitutor.substitute(parameter); + final PsiType sSubstituted = sSubstitutor.substituteWithBoundsPromotion(parameter); + constraints.add(new SubtypingConstraint(tSubstituted, sSubstituted, false)); + } + return true; + } + } + return false; + } + + if (myT instanceof PsiIntersectionType) { + for (PsiType conjunct : ((PsiIntersectionType)myT).getConjuncts()) { + constraints.add(new SubtypingConstraint(conjunct, myS, true)); + } + return true; + } + + if (PsiType.NULL.equals(myT)) return false; + } else { + if (myT instanceof PsiWildcardType) { + final PsiType tBound = ((PsiWildcardType)myT).getBound(); + if (tBound == null) { + return true; + } + if (((PsiWildcardType)myT).isExtends()) { + if (tBound.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) { + return true; + } + if (myS instanceof PsiWildcardType) { + final PsiType sBound = ((PsiWildcardType)myS).getBound(); + if (sBound != null && ((PsiWildcardType)myS).isExtends()) { + constraints.add(new SubtypingConstraint(tBound, sBound, true)); + return true; + } + } else { + constraints.add(new SubtypingConstraint(tBound, myS, true)); + return true; + } + return false; + } else { + if (myS instanceof PsiWildcardType) { + final PsiType sBound = ((PsiWildcardType)myS).getBound(); + if (sBound != null && ((PsiWildcardType)myS).isSuper()) { + constraints.add(new SubtypingConstraint(sBound, tBound, true)); + return true; + } + } else { + constraints.add(new SubtypingConstraint(myS, tBound, true)); + return true; + } + } + return false; + } else { + if (myS instanceof PsiWildcardType) { + return false; + } else { + constraints.add(new SubtypingConstraint(myT, myS, true)); + return true; + } + } + } + return true; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + SubtypingConstraint that = (SubtypingConstraint)o; + + if (myIsRefTypes != that.myIsRefTypes) return false; + if (myS != null ? !myS.equals(that.myS) : that.myS != null) return false; + if (myT != null ? !myT.equals(that.myT) : that.myT != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = myS != null ? myS.hashCode() : 0; + result = 31 * result + (myT != null ? myT.hashCode() : 0); + result = 31 * result + (myIsRefTypes ? 1 : 0); + return result; + } +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/TypeCompatibilityConstraint.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/TypeCompatibilityConstraint.java new file mode 100644 index 000000000000..353f03b66a80 --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/TypeCompatibilityConstraint.java @@ -0,0 +1,81 @@ +/* + * Copyright 2000-2013 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.graphInference.constraints; + +import com.intellij.psi.PsiClassType; +import com.intellij.psi.PsiPrimitiveType; +import com.intellij.psi.PsiType; +import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession; +import com.intellij.psi.util.TypeConversionUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * User: anna + */ +public class TypeCompatibilityConstraint implements ConstraintFormula { + private PsiType myT; + private PsiType myS; + + public TypeCompatibilityConstraint(@NotNull PsiType t, @NotNull PsiType s) { + myT = t; + myS = s; + } + + @Override + public boolean reduce(InferenceSession session, List<ConstraintFormula> constraints, List<ConstraintFormula> delayedConstraints) { + if (session.isProperType(myT) && session.isProperType(myS)) { + return TypeConversionUtil.isAssignable(myS, myT); + } + if (myS instanceof PsiPrimitiveType) { + final PsiClassType boxedType = ((PsiPrimitiveType)myS).getBoxedType(session.getManager(), session.getScope()); + if (boxedType != null) { + constraints.add(new TypeCompatibilityConstraint(myT, boxedType)); + return true; + } + } + if (myT instanceof PsiPrimitiveType) { + final PsiClassType boxedType = ((PsiPrimitiveType)myT).getBoxedType(session.getManager(), session.getScope()); + if (boxedType != null) { + constraints.add(new TypeCompatibilityConstraint(boxedType, myS)); + return true; + } + } + constraints.add(new SubtypingConstraint(myT, myS, true)); + return true; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + TypeCompatibilityConstraint that = (TypeCompatibilityConstraint)o; + + if (!myS.equals(that.myS)) return false; + if (!myT.equals(that.myT)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = myT.hashCode(); + result = 31 * result + myS.hashCode(); + return result; + } +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/TypeEqualityConstraint.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/TypeEqualityConstraint.java new file mode 100644 index 000000000000..a71465653a6f --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/TypeEqualityConstraint.java @@ -0,0 +1,124 @@ +/* + * Copyright 2000-2013 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.graphInference.constraints; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.psi.*; +import com.intellij.psi.impl.source.resolve.graphInference.InferenceBound; +import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession; +import com.intellij.psi.impl.source.resolve.graphInference.InferenceVariable; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * User: anna + */ +public class TypeEqualityConstraint implements ConstraintFormula { + private static final Logger LOG = Logger.getInstance("#" + TypeEqualityConstraint.class.getName()); + private PsiType myT; + private PsiType myS; + + public TypeEqualityConstraint(@NotNull PsiType t, @NotNull PsiType s) { + myT = t; + myS = s; + } + + @Override + public boolean reduce(InferenceSession session, List<ConstraintFormula> constraints, List<ConstraintFormula> delayedConstraints) { + if (session.isProperType(myT) && session.isProperType(myS)) { + return myT.equals(myS); + } + InferenceVariable inferenceVariable = session.getInferenceVariable(myS); + if (inferenceVariable != null) { + inferenceVariable.addBound(myT, InferenceBound.EQ); + return true; + } + inferenceVariable = session.getInferenceVariable(myT); + if (inferenceVariable != null) { + inferenceVariable.addBound(myS, InferenceBound.EQ); + return true; + } + if (myT instanceof PsiClassType && myS instanceof PsiClassType) { + final PsiClassType.ClassResolveResult tResult = ((PsiClassType)myT).resolveGenerics(); + final PsiClassType.ClassResolveResult sResult = ((PsiClassType)myS).resolveGenerics(); + final PsiClass C = tResult.getElement(); + if (C == sResult.getElement() && C != null) { + final PsiSubstitutor tSubstitutor = tResult.getSubstitutor(); + final PsiSubstitutor sSubstitutor = sResult.getSubstitutor(); + for (PsiTypeParameter typeParameter : C.getTypeParameters()) { + final PsiType tSubstituted = tSubstitutor.substitute(typeParameter); + final PsiType sSubstituted = sSubstitutor.substitute(typeParameter); + if (tSubstituted != null && sSubstituted != null) { + constraints.add(new TypeEqualityConstraint(tSubstituted, sSubstituted)); + } + } + return true; + } + } + if (myT instanceof PsiArrayType && myS instanceof PsiArrayType) { + constraints.add(new TypeEqualityConstraint(((PsiArrayType)myT).getComponentType(), ((PsiArrayType)myS).getComponentType())); + return true; + } + if (myT instanceof PsiIntersectionType && myS instanceof PsiIntersectionType) { + final PsiType[] tConjuncts = ((PsiIntersectionType)myT).getConjuncts(); + final PsiType[] sConjuncts = ((PsiIntersectionType)myS).getConjuncts(); + if (sConjuncts.length == tConjuncts.length) { + for (int i = 0; i < sConjuncts.length; i++) { + constraints.add(new TypeEqualityConstraint(tConjuncts[i], sConjuncts[i])); + } + return true; + } + } + + if (myT instanceof PsiWildcardType && myS instanceof PsiWildcardType) { + final PsiType tBound = ((PsiWildcardType)myT).getBound(); + final PsiType sBound = ((PsiWildcardType)myS).getBound(); + + if (tBound == null && sBound == null) return true; + + if (((PsiWildcardType)myT).isExtends() && ((PsiWildcardType)myS).isExtends() || + ((PsiWildcardType)myT).isSuper() && ((PsiWildcardType)myS).isSuper()) { + + LOG.assertTrue(tBound != null); + LOG.assertTrue(sBound != null); + constraints.add(new TypeEqualityConstraint(tBound, sBound)); + return true; + } + } + return false; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + TypeEqualityConstraint that = (TypeEqualityConstraint)o; + + if (!myS.equals(that.myS)) return false; + if (!myT.equals(that.myT)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = myT.hashCode(); + result = 31 * result + myS.hashCode(); + return result; + } +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaTreeGenerator.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaTreeGenerator.java new file mode 100644 index 000000000000..f6ba9fdf5af4 --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaTreeGenerator.java @@ -0,0 +1,244 @@ +/* + * Copyright 2000-2013 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.tree; + +import com.intellij.lang.ASTNode; +import com.intellij.lang.PsiBuilder; +import com.intellij.lang.java.parser.JavaParser; +import com.intellij.lang.java.parser.JavaParserUtil; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Key; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.psi.*; +import com.intellij.psi.impl.GeneratedMarkerVisitor; +import com.intellij.psi.impl.source.*; +import com.intellij.psi.impl.source.codeStyle.CodeEditUtil; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiUtil; +import com.intellij.util.CharTable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author max + */ +public class JavaTreeGenerator implements TreeGenerator { + private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.JavaTreeGenerator"); + + private static final JavaParserUtil.ParserWrapper MOD_LIST = new JavaParserUtil.ParserWrapper() { + @Override + public void parse(final PsiBuilder builder) { + JavaParser.INSTANCE.getDeclarationParser().parseModifierList(builder); + } + }; + + @Override + @Nullable + public TreeElement generateTreeFor(PsiElement original, final CharTable table, final PsiManager manager) { + if (original instanceof PsiKeyword || original instanceof PsiIdentifier) { + final String text = original.getText(); + return createLeafFromText(text, table, manager, original, ((PsiJavaToken)original).getTokenType()); + } + + if (original instanceof PsiModifierList) { + final String text = original.getText(); + assert text != null : "Text is null for " + original + "; " + original.getClass(); + final LanguageLevel level = PsiUtil.getLanguageLevel(original); + final DummyHolder holder = DummyHolderFactory.createHolder(original.getManager(), new JavaDummyElement(text, MOD_LIST, level), null); + final TreeElement modifierListElement = holder.getTreeElement().getFirstChildNode(); + return markGeneratedIfNeeded(original, modifierListElement); + } + + if (original instanceof PsiReferenceExpression) { + TreeElement element = createReferenceExpression(original.getProject(), original.getText(), original); + PsiElement refElement = ((PsiJavaCodeReferenceElement)original).resolve(); + if (refElement instanceof PsiClass) { + element.putCopyableUserData(REFERENCED_CLASS_KEY, (PsiClass)refElement); + } + return element; + } + + if (original instanceof PsiJavaCodeReferenceElement) { + PsiElement refElement = ((PsiJavaCodeReferenceElement)original).resolve(); + final boolean generated = refElement != null && CodeEditUtil.isNodeGenerated(refElement.getNode()); + if (refElement instanceof PsiClass) { + if (refElement instanceof PsiAnonymousClass) { + PsiJavaCodeReferenceElement ref = ((PsiAnonymousClass)refElement).getBaseClassReference(); + original = ref; + refElement = ref.resolve(); + } + + boolean isFQ = false; + if (original instanceof PsiJavaCodeReferenceElementImpl) { + int kind = ((PsiJavaCodeReferenceElementImpl)original).getKind(); + switch (kind) { + case PsiJavaCodeReferenceElementImpl.CLASS_OR_PACKAGE_NAME_KIND: + case PsiJavaCodeReferenceElementImpl.CLASS_NAME_KIND: + case PsiJavaCodeReferenceElementImpl.CLASS_IN_QUALIFIED_NEW_KIND: + isFQ = false; + break; + + case PsiJavaCodeReferenceElementImpl.CLASS_FQ_NAME_KIND: + case PsiJavaCodeReferenceElementImpl.CLASS_FQ_OR_PACKAGE_NAME_KIND: + isFQ = true; + break; + + default: + LOG.assertTrue(false); + } + } + + final String text = isFQ ? ((PsiClass)refElement).getQualifiedName() : original.getText(); + final TreeElement element = createReference(original.getProject(), text, generated); + element.putCopyableUserData(REFERENCED_CLASS_KEY, (PsiClass)refElement); + return element; + } + return createReference(original.getProject(), original.getText(), generated); + } + + if (original instanceof PsiCompiledElement) { + PsiElement sourceVersion = original.getNavigationElement(); + if (sourceVersion != original) { + return ChangeUtil.generateTreeElement(sourceVersion, table, manager); + } + PsiElement mirror = ((PsiCompiledElement)original).getMirror(); + return ChangeUtil.generateTreeElement(mirror, table, manager); + } + + if (original instanceof PsiTypeElement) { + PsiTypeElement typeElement = (PsiTypeElement)original; + PsiType type = typeElement.getType(); + + if (type instanceof PsiIntersectionType) { + type = ((PsiIntersectionType)type).getRepresentative(); + } + else if (type instanceof PsiMethodReferenceType || type instanceof PsiLambdaExpressionType) { + type = PsiType.getJavaLangObject(manager, GlobalSearchScope.projectScope(manager.getProject())); + } + + String text = type.getPresentableText(); + PsiJavaParserFacade parserFacade = JavaPsiFacade.getInstance(original.getProject()).getParserFacade(); + PsiTypeElement element = parserFacade.createTypeElementFromText(text, original); + + TreeElement result = (TreeElement)element.getNode(); + markGeneratedIfNeeded(original, result); + encodeInfoInTypeElement(result, type); + return result; + } + + return null; + } + + private static LeafElement createLeafFromText(String text, CharTable table, PsiManager manager, PsiElement original, IElementType type) { + return Factory.createSingleLeafElement(type, text, 0, text.length(), table, manager, CodeEditUtil.isNodeGenerated(original.getNode())); + } + + private static TreeElement markGeneratedIfNeeded(PsiElement original, TreeElement copy) { + if (CodeEditUtil.isNodeGenerated(original.getNode())) { + copy.acceptTree(new GeneratedMarkerVisitor()); + } + return copy; + } + + private static TreeElement createReference(final Project project, final String text, boolean mark) { + final PsiJavaParserFacade parserFacade = JavaPsiFacade.getInstance(project).getParserFacade(); + final TreeElement element = (TreeElement)parserFacade.createReferenceFromText(text, null).getNode(); + if (mark) element.acceptTree(new GeneratedMarkerVisitor()); + return element; + } + + private static TreeElement createReferenceExpression(final Project project, final String text, final PsiElement context) { + final PsiJavaParserFacade parserFacade = JavaPsiFacade.getInstance(project).getParserFacade(); + final PsiExpression expression = parserFacade.createExpressionFromText(text, context); + return (TreeElement)expression.getNode(); + } + + private static void encodeInfoInTypeElement(ASTNode typeElement, PsiType type) { + if (type instanceof PsiPrimitiveType) return; + LOG.assertTrue(typeElement.getElementType() == JavaElementType.TYPE); + if (type instanceof PsiArrayType) { + final ASTNode firstChild = typeElement.getFirstChildNode(); + LOG.assertTrue(firstChild.getElementType() == JavaElementType.TYPE); + encodeInfoInTypeElement(firstChild, ((PsiArrayType)type).getComponentType()); + } + else if (type instanceof PsiWildcardType) { + final PsiType bound = ((PsiWildcardType)type).getBound(); + if (bound == null) return; + final ASTNode lastChild = typeElement.getLastChildNode(); + if (lastChild.getElementType() != JavaElementType.TYPE) return; + encodeInfoInTypeElement(lastChild, bound); + } + else if (type instanceof PsiCapturedWildcardType) { + final PsiType bound = ((PsiCapturedWildcardType)type).getWildcard().getBound(); + if (bound == null) return; + final ASTNode lastChild = typeElement.getLastChildNode(); + if (lastChild.getElementType() != JavaElementType.TYPE) return; + encodeInfoInTypeElement(lastChild, bound); + } + else if (type instanceof PsiIntersectionType) { + encodeInfoInTypeElement(typeElement, ((PsiIntersectionType)type).getRepresentative()); + } + else if (type instanceof PsiClassType) { + final PsiClassType classType = (PsiClassType)type; + final PsiClassType.ClassResolveResult resolveResult = classType.resolveGenerics(); + PsiClass referencedClass = resolveResult.getElement(); + if (referencedClass == null) return; + if (referencedClass instanceof PsiAnonymousClass) { + encodeInfoInTypeElement(typeElement, ((PsiAnonymousClass)referencedClass).getBaseClassType()); + } + else { + final ASTNode reference = typeElement.findChildByType(JavaElementType.JAVA_CODE_REFERENCE); + // can be not the case for "? name" + if (reference instanceof CompositeElement) { + encodeClassTypeInfoInReference((CompositeElement)reference, resolveResult.getElement(), resolveResult.getSubstitutor()); + } + } + } + } + + private static void encodeClassTypeInfoInReference(@NotNull CompositeElement reference, PsiClass referencedClass, PsiSubstitutor substitutor) { + reference.putCopyableUserData(REFERENCED_CLASS_KEY, referencedClass); + + final PsiTypeParameter[] typeParameters = referencedClass.getTypeParameters(); + if (typeParameters.length == 0) return; + + final ASTNode referenceParameterList = reference.findChildByRole(ChildRole.REFERENCE_PARAMETER_LIST); + int index = 0; + for (ASTNode child = referenceParameterList.getFirstChildNode(); child != null && index < typeParameters.length; child = child.getTreeNext()) { + if (child.getElementType() == JavaElementType.TYPE) { + final PsiType substitutedType = substitutor.substitute(typeParameters[index]); + if (substitutedType != null) { + encodeInfoInTypeElement(child, substitutedType); + } + index++; + } + } + + final ASTNode qualifier = reference.findChildByRole(ChildRole.QUALIFIER); + if (qualifier != null) { + if (referencedClass.hasModifierProperty(PsiModifier.STATIC)) return; + final PsiClass outerClass = referencedClass.getContainingClass(); + if (outerClass != null) { + encodeClassTypeInfoInReference((CompositeElement)qualifier, outerClass, substitutor); + } + } + } + + static final Key<PsiClass> REFERENCED_CLASS_KEY = Key.create("REFERENCED_CLASS_KEY"); + static final Key<PsiMember> REFERENCED_MEMBER_KEY = Key.create("REFERENCED_MEMBER_KEY"); +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java index 1b857c421770..46788578dee6 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java @@ -323,7 +323,7 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase argumentList != null ? argumentList.getExpressionTypes() : null, getTypeArguments(), getLanguageLevel()) { @Override - public PsiSubstitutor inferTypeArguments(@NotNull ParameterTypeInferencePolicy policy) { + public PsiSubstitutor inferTypeArguments(@NotNull ParameterTypeInferencePolicy policy, boolean includeReturnConstraint) { return inferTypeArgumentsFromInterfaceMethod(signature, interfaceMethodReturnType, method, substitutor, languageLevel); } }; diff --git a/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java b/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java index 2ac053285b78..27e4227b5afb 100644 --- a/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java +++ b/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java @@ -240,7 +240,8 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{ PsiMethod existingMethod = (PsiMethod)existing.getElement(); assert existingMethod != null; PsiClass existingClass = existingMethod.getContainingClass(); - if (class1.isInterface() && CommonClassNames.JAVA_LANG_OBJECT.equals(existingClass.getQualifiedName())) { //prefer interface methods to methods from Object + if (class1 != null && existingClass != null && + class1.isInterface() && CommonClassNames.JAVA_LANG_OBJECT.equals(existingClass.getQualifiedName())) { //prefer interface methods to methods from Object signatures.put(signature, info); continue; } |