summaryrefslogtreecommitdiff
path: root/java/java-psi-impl/src/com/intellij/psi
diff options
context:
space:
mode:
Diffstat (limited to 'java/java-psi-impl/src/com/intellij/psi')
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/PsiDiamondTypeImpl.java31
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/controlFlow/ControlFlowAnalyzer.java4
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/JavaPsiFacadeImpl.java10
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/InferenceGraphNode.java143
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/ProcessCandidateParameterTypeInferencePolicy.java5
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/PsiOldInferenceHelper.java1233
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/PsiResolveHelperImpl.java1192
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceBound.java25
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceIncorporationPhase.java187
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java333
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariable.java72
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariablesOrder.java184
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/PsiGraphInferenceHelper.java78
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/PsiPolyExpressionUtil.java38
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/ConstraintFormula.java27
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/ExpressionCompatibilityConstraint.java107
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/LambdaExpressionCompatibilityConstraint.java75
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/SubtypingConstraint.java175
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/TypeCompatibilityConstraint.java81
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/TypeEqualityConstraint.java124
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaTreeGenerator.java244
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java2
-rw-r--r--java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java3
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;
}