diff --git a/java/java-impl/src/com/intellij/codeInsight/ExpectedTypeUtil.java b/java/java-impl/src/com/intellij/codeInsight/ExpectedTypeUtil.java index 3a8eeb35f779..3cbbb36bdee3 100644 --- a/java/java-impl/src/com/intellij/codeInsight/ExpectedTypeUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/ExpectedTypeUtil.java @@ -17,6 +17,8 @@ package com.intellij.codeInsight; import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.*; +import com.intellij.psi.impl.source.resolve.CompletionParameterTypeInferencePolicy; +import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy; import com.intellij.psi.util.PsiUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.Nullable; @@ -159,13 +161,14 @@ public class ExpectedTypeUtil { } @Nullable - public static PsiSubstitutor inferSubstitutor(final PsiMethod method, final PsiMethodCallExpression callExpr, final boolean forCompletion) { + public static PsiSubstitutor inferSubstitutor(final PsiMethod method, final PsiMethodCallExpression callExpr, boolean forCompletion) { final PsiResolveHelper helper = JavaPsiFacade.getInstance(method.getProject()).getResolveHelper(); final PsiParameter[] parameters = method.getParameterList().getParameters(); PsiExpression[] args = callExpr.getArgumentList().getExpressions(); PsiSubstitutor result = PsiSubstitutor.EMPTY; for (PsiTypeParameter typeParameter : PsiUtil.typeParametersIterable(method.getContainingClass())) { - PsiType type = helper.inferTypeForMethodTypeParameter(typeParameter, parameters, args, PsiSubstitutor.EMPTY, callExpr.getParent(), forCompletion); + PsiType type = helper.inferTypeForMethodTypeParameter(typeParameter, parameters, args, PsiSubstitutor.EMPTY, callExpr.getParent(), + forCompletion ? CompletionParameterTypeInferencePolicy.INSTANCE : DefaultParameterTypeInferencePolicy.INSTANCE); if (PsiType.NULL.equals(type)) return null; result = result.put(typeParameter, type); } diff --git a/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java b/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java index 635948faa2d0..d81e9aef0602 100644 --- a/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java @@ -22,6 +22,9 @@ import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.codeStyle.VariableKind; import com.intellij.psi.impl.source.jsp.jspJava.JspMethodCall; +import com.intellij.psi.impl.source.resolve.CompletionParameterTypeInferencePolicy; +import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy; +import com.intellij.psi.impl.source.resolve.ParameterTypeInferencePolicy; import com.intellij.psi.infos.CandidateInfo; import com.intellij.psi.infos.MethodCandidateInfo; import com.intellij.psi.search.GlobalSearchScope; @@ -915,13 +918,15 @@ public class ExpectedTypesProvider { leftArgs = null; } + ParameterTypeInferencePolicy policy = forCompletion ? CompletionParameterTypeInferencePolicy.INSTANCE : DefaultParameterTypeInferencePolicy.INSTANCE; + Set array = new LinkedHashSet(); for (CandidateInfo candidateInfo : methodCandidates) { PsiMethod method = (PsiMethod)candidateInfo.getElement(); PsiSubstitutor substitutor; if (candidateInfo instanceof MethodCandidateInfo) { final MethodCandidateInfo info = (MethodCandidateInfo)candidateInfo; - substitutor = info.inferTypeArguments(forCompletion); + substitutor = info.inferTypeArguments(policy); if (!info.isStaticsScopeCorrect() && method != null && !method.hasModifierProperty(PsiModifier.STATIC)) continue; } else { @@ -930,7 +935,7 @@ public class ExpectedTypesProvider { inferMethodCallArgumentTypes(argument, forCompletion, args, index, method, substitutor, array); if (leftArgs != null && candidateInfo instanceof MethodCandidateInfo) { - substitutor = ((MethodCandidateInfo)candidateInfo).inferTypeArguments(forCompletion, leftArgs); + substitutor = ((MethodCandidateInfo)candidateInfo).inferTypeArguments(policy, leftArgs); inferMethodCallArgumentTypes(argument, forCompletion, leftArgs, index, method, substitutor, array); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/VariableTypeFromCallFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/VariableTypeFromCallFix.java index 0f08dbba9a82..ba7ffcfd61b7 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/VariableTypeFromCallFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/VariableTypeFromCallFix.java @@ -26,6 +26,7 @@ import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.psi.*; +import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy; import com.intellij.psi.search.PsiSearchHelper; import com.intellij.psi.util.PsiUtil; import com.intellij.refactoring.typeMigration.TypeMigrationLabeler; @@ -104,7 +105,8 @@ public class VariableTypeFromCallFix implements IntentionAction { if (varClass != null) { final PsiSubstitutor psiSubstitutor = resolveHelper.inferTypeArguments(varClass.getTypeParameters(), parameters, - expressions, PsiSubstitutor.EMPTY, resolved, false); + expressions, PsiSubstitutor.EMPTY, resolved, + DefaultParameterTypeInferencePolicy.INSTANCE); final PsiClassType appropriateVarType = JavaPsiFacade.getElementFactory(expression.getProject()).createType(varClass, psiSubstitutor); if (!varType.equals(appropriateVarType)) { QuickFixAction.registerQuickFixAction(highlightInfo, new VariableTypeFromCallFix(appropriateVarType, (PsiVariable) resolved)); diff --git a/java/java-impl/src/com/intellij/codeInsight/hint/api/impls/MethodParameterInfoHandler.java b/java/java-impl/src/com/intellij/codeInsight/hint/api/impls/MethodParameterInfoHandler.java index e69cc03ccbbd..40409edd890a 100644 --- a/java/java-impl/src/com/intellij/codeInsight/hint/api/impls/MethodParameterInfoHandler.java +++ b/java/java-impl/src/com/intellij/codeInsight/hint/api/impls/MethodParameterInfoHandler.java @@ -23,6 +23,7 @@ import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.lang.parameterInfo.*; import com.intellij.openapi.project.DumbAware; import com.intellij.psi.*; +import com.intellij.psi.impl.source.resolve.CompletionParameterTypeInferencePolicy; import com.intellij.psi.infos.CandidateInfo; import com.intellij.psi.infos.MethodCandidateInfo; import com.intellij.psi.tree.IElementType; @@ -125,7 +126,7 @@ public class MethodParameterInfoHandler implements ParameterInfoHandlerWithTabAc for (int i = 0; i < candidates.length; i++) { CandidateInfo candidate = (CandidateInfo)candidates[i]; PsiMethod method = (PsiMethod)candidate.getElement(); - PsiSubstitutor substitutor = candidate instanceof MethodCandidateInfo && ((MethodCandidateInfo)candidate).isInferencePossible() ? ((MethodCandidateInfo)candidate).inferTypeArguments(true) : candidate.getSubstitutor(); + PsiSubstitutor substitutor = getCandidateInfoSubstitutor(candidate); assert substitutor != null; if (!method.isValid() || !substitutor.isValid()) { @@ -193,6 +194,12 @@ public class MethodParameterInfoHandler implements ParameterInfoHandlerWithTabAc } } + private static PsiSubstitutor getCandidateInfoSubstitutor(CandidateInfo candidate) { + return candidate instanceof MethodCandidateInfo && ((MethodCandidateInfo)candidate).isInferencePossible() + ? ((MethodCandidateInfo)candidate).inferTypeArguments(CompletionParameterTypeInferencePolicy.INSTANCE) + : candidate.getSubstitutor(); + } + private static boolean isAssignableParametersBeforeGivenIndex(final PsiParameter[] parms, final PsiExpression[] args, int length, @@ -408,7 +415,7 @@ public class MethodParameterInfoHandler implements ParameterInfoHandlerWithTabAc public void updateUI(final Object p, final ParameterInfoUIContext context) { if (p instanceof CandidateInfo) { CandidateInfo info = (CandidateInfo)p; - updateMethodPresentation((PsiMethod)info.getElement(), info instanceof MethodCandidateInfo && ((MethodCandidateInfo)info).isInferencePossible() ? ((MethodCandidateInfo)info).inferTypeArguments(true) : info.getSubstitutor(), context); + updateMethodPresentation((PsiMethod)info.getElement(), getCandidateInfoSubstitutor(info), context); } else { updateMethodPresentation((PsiMethod)p, null, context); diff --git a/java/java-impl/src/com/intellij/codeInspection/miscGenerics/RedundantTypeArgsInspection.java b/java/java-impl/src/com/intellij/codeInspection/miscGenerics/RedundantTypeArgsInspection.java index 6b15b9bb482c..9240ecd7d47c 100644 --- a/java/java-impl/src/com/intellij/codeInspection/miscGenerics/RedundantTypeArgsInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/miscGenerics/RedundantTypeArgsInspection.java @@ -20,6 +20,7 @@ import com.intellij.codeInspection.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.psi.*; +import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy; import com.intellij.psi.util.PsiUtil; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; @@ -102,11 +103,12 @@ public class RedundantTypeArgsInspection extends GenericsInspectionToolBase { PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(expression.getProject()).getResolveHelper(); for (int i = 0; i < typeParameters.length; i++) { PsiTypeParameter typeParameter = typeParameters[i]; - final PsiType inferedType = resolveHelper.inferTypeForMethodTypeParameter(typeParameter, parameters, + final PsiType inferredType = resolveHelper.inferTypeForMethodTypeParameter(typeParameter, parameters, argumentList.getExpressions(), - resolveResult.getSubstitutor(), expression, false); - if (!typeArguments[i].equals(inferedType)) return; - if (PsiUtil.resolveClassInType(method.getReturnType()) == typeParameter && PsiPrimitiveType.getUnboxedType(inferedType) != null) return; + resolveResult.getSubstitutor(), expression, + DefaultParameterTypeInferencePolicy.INSTANCE); + if (!typeArguments[i].equals(inferredType)) return; + if (PsiUtil.resolveClassInType(method.getReturnType()) == typeParameter && PsiPrimitiveType.getUnboxedType(inferredType) != null) return; } final PsiCallExpression copy = (PsiCallExpression)expression.copy(); //see IDEADEV-8174 diff --git a/java/java-impl/src/com/intellij/psi/impl/source/resolve/CompletionParameterTypeInferencePolicy.java b/java/java-impl/src/com/intellij/psi/impl/source/resolve/CompletionParameterTypeInferencePolicy.java new file mode 100644 index 000000000000..e44a5ebd94b5 --- /dev/null +++ b/java/java-impl/src/com/intellij/psi/impl/source/resolve/CompletionParameterTypeInferencePolicy.java @@ -0,0 +1,129 @@ +/* + * Copyright 2000-2011 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.ExpectedTypeInfo; +import com.intellij.codeInsight.ExpectedTypesProvider; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Pair; +import com.intellij.psi.*; +import com.intellij.psi.infos.MethodCandidateInfo; +import com.intellij.psi.scope.MethodProcessorSetupFailedException; +import com.intellij.psi.scope.processor.MethodCandidatesProcessor; +import com.intellij.psi.scope.util.PsiScopesUtil; +import com.intellij.psi.util.PsiUtil; +import com.intellij.util.ArrayUtil; + +import java.util.Arrays; +import java.util.List; + +/** + * @author yole + */ +public class CompletionParameterTypeInferencePolicy extends ParameterTypeInferencePolicy { + public static final CompletionParameterTypeInferencePolicy INSTANCE = new CompletionParameterTypeInferencePolicy(); + + private CompletionParameterTypeInferencePolicy() { + } + + @Override + public Pair inferTypeConstraintFromCallContext(PsiCallExpression innerMethodCall, + PsiExpressionList expressionList, + PsiCallExpression contextCall, + PsiTypeParameter typeParameter) { + final MethodCandidatesProcessor processor = new MethodCandidatesProcessor(contextCall); + try { + //can't call resolve() since it obtains full substitution, that may result in infinite recursion + PsiScopesUtil.setupAndRunProcessor(processor, contextCall, false); + PsiExpression[] expressions = expressionList.getExpressions(); + int i = ArrayUtil.find(expressions, innerMethodCall); + assert i >= 0; + final JavaResolveResult[] results = processor.getResult(); + PsiMethod owner = (PsiMethod)typeParameter.getOwner(); + if (owner == null) return null; + + final PsiType innerReturnType = owner.getReturnType(); + for (final JavaResolveResult result : results) { + final PsiSubstitutor substitutor; + if (result instanceof MethodCandidateInfo) { + List leftArgs = Arrays.asList(expressions).subList(0, i); + substitutor = ((MethodCandidateInfo)result).inferTypeArguments(this, leftArgs.toArray(new PsiExpression[leftArgs.size()])); + } else { + substitutor = result.getSubstitutor(); + } + + final PsiElement element = result.getElement(); + if (element instanceof PsiMethod) { + final PsiMethod method = (PsiMethod)element; + final PsiParameter[] parameters = method.getParameterList().getParameters(); + PsiParameter parameter = null; + if (parameters.length > i) { + parameter = parameters[i]; + } + else if (method.isVarArgs()) { + parameter = parameters[parameters.length - 1]; + } + if (parameter != null) { + final PsiParameter finalParameter = parameter; + PsiType type = PsiResolveHelperImpl.ourGuard.doPreventingRecursion(innerMethodCall, true, new Computable() { + @Override + public PsiType compute() { + return substitutor.substitute(finalParameter.getType()); + } + }); + final Pair constraint = + PsiResolveHelperImpl.getSubstitutionForTypeParameterConstraint(typeParameter, innerReturnType, type, false, + PsiUtil.getLanguageLevel(innerMethodCall)); + if (constraint != null) return constraint; + } + } + } + } + catch (MethodProcessorSetupFailedException ev) { + return null; + } + + return null; + } + + @Override + public PsiType getDefaultExpectedType(PsiCallExpression methodCall) { + ExpectedTypeInfo[] expectedTypes = ExpectedTypesProvider.getExpectedTypes(methodCall, true); + if (expectedTypes.length > 0) { + return expectedTypes[0].getType(); + } + return PsiType.NULL; + } + + @Override + public Pair getInferredTypeWithNoConstraint(PsiManager psiManager, PsiType superType) { + if (!(superType instanceof PsiWildcardType)) { + return new Pair(PsiWildcardType.createExtends(psiManager, superType), ConstraintType.EQUALS); + } + else { + return new Pair(superType, ConstraintType.SUBTYPE); + } + } + + @Override + public PsiType adjustInferredType(PsiManager manager, PsiType guess, ConstraintType constraintType) { + if (guess != null && !(guess instanceof PsiWildcardType)) { + if (constraintType == ConstraintType.SUPERTYPE) return PsiWildcardType.createExtends(manager, guess); + else if (constraintType == ConstraintType.SUBTYPE) return PsiWildcardType.createSuper(manager, guess); + } + return guess; + } +} diff --git a/java/java-impl/src/com/intellij/psi/impl/source/resolve/PsiResolveHelperImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/resolve/PsiResolveHelperImpl.java index dc09609e6062..f28f55c818b1 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/resolve/PsiResolveHelperImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/resolve/PsiResolveHelperImpl.java @@ -15,16 +15,12 @@ */ package com.intellij.psi.impl.source.resolve; -import com.intellij.codeInsight.ExpectedTypeInfo; -import com.intellij.codeInsight.ExpectedTypesProvider; -import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.RecursionGuard; import com.intellij.openapi.util.RecursionManager; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; 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; @@ -38,11 +34,8 @@ import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Arrays; -import java.util.List; - public class PsiResolveHelperImpl implements PsiResolveHelper { - private static final RecursionGuard ourGuard = RecursionManager.createGuard("typeArgInference"); + static final RecursionGuard ourGuard = RecursionManager.createGuard("typeArgInference"); private final PsiManager myManager; public PsiResolveHelperImpl(PsiManager manager) { @@ -146,7 +139,7 @@ public class PsiResolveHelperImpl implements PsiResolveHelper { PsiExpression[] arguments, PsiSubstitutor partialSubstitutor, PsiElement parent, - final boolean forCompletion) { + final ParameterTypeInferencePolicy policy) { PsiWildcardType wildcardToCapture = null; PsiType lowerBound = PsiType.NULL; PsiType upperBound = PsiType.NULL; @@ -218,7 +211,7 @@ public class PsiResolveHelperImpl implements PsiResolveHelper { if (parent != null) { final Pair constraint = - inferMethodTypeParameterFromParent(typeParameter, partialSubstitutor, parent, forCompletion); + inferMethodTypeParameterFromParent(typeParameter, partialSubstitutor, parent, policy); if (constraint != null) { if (constraint.getSecond() != ConstraintType.SUBTYPE) { return constraint; @@ -245,10 +238,10 @@ public class PsiResolveHelperImpl implements PsiResolveHelper { @NotNull PsiExpression[] arguments, @NotNull PsiSubstitutor partialSubstitutor, PsiElement parent, - final boolean forCompletion) { + final ParameterTypeInferencePolicy policy) { final Pair constraint = - inferTypeForMethodTypeParameterInner(typeParameter, parameters, arguments, partialSubstitutor, parent, forCompletion); + inferTypeForMethodTypeParameterInner(typeParameter, parameters, arguments, partialSubstitutor, parent, policy); if (constraint == null) return PsiType.NULL; return constraint.getFirst(); } @@ -259,13 +252,13 @@ public class PsiResolveHelperImpl implements PsiResolveHelper { @NotNull PsiExpression[] arguments, @NotNull PsiSubstitutor partialSubstitutor, @NotNull PsiElement parent, - boolean forCompletion) { + ParameterTypeInferencePolicy policy) { PsiType[] substitutions = new PsiType[typeParameters.length]; //noinspection unchecked Pair[] constraints = new Pair[typeParameters.length]; for (int i = 0; i < typeParameters.length; i++) { final Pair constraint = - inferTypeForMethodTypeParameterInner(typeParameters[i], parameters, arguments, partialSubstitutor, null, forCompletion); + inferTypeForMethodTypeParameterInner(typeParameters[i], parameters, arguments, partialSubstitutor, null, policy); constraints[i] = constraint; if (constraint != null && constraint.getSecond() != ConstraintType.SUBTYPE) { substitutions[i] = constraint.getFirst(); @@ -319,10 +312,10 @@ public class PsiResolveHelperImpl implements PsiResolveHelper { Pair constraint = constraints[i]; if (substitution == null) { if (constraint == null) { - constraint = inferMethodTypeParameterFromParent(typeParameter, partialSubstitutor, parent, forCompletion); + constraint = inferMethodTypeParameterFromParent(typeParameter, partialSubstitutor, parent, policy); } else if (constraint.getSecond() == ConstraintType.SUBTYPE) { Pair otherConstraint = - inferMethodTypeParameterFromParent(typeParameter, partialSubstitutor, parent, forCompletion); + inferMethodTypeParameterFromParent(typeParameter, partialSubstitutor, parent, policy); if (otherConstraint != null) { if (otherConstraint.getSecond() == ConstraintType.EQUALS || otherConstraint.getSecond() == ConstraintType.SUPERTYPE) constraint = otherConstraint; } @@ -405,14 +398,14 @@ public class PsiResolveHelperImpl implements PsiResolveHelper { } private static Pair inferMethodTypeParameterFromParent(final PsiTypeParameter typeParameter, - PsiSubstitutor substitutor, - PsiElement parent, - final boolean forCompletion) { + PsiSubstitutor substitutor, + PsiElement parent, + final ParameterTypeInferencePolicy policy) { PsiTypeParameterListOwner owner = typeParameter.getOwner(); Pair substitution = null; if (owner instanceof PsiMethod && parent instanceof PsiCallExpression) { PsiCallExpression methodCall = (PsiCallExpression)parent; - substitution = inferMethodTypeParameterFromParent(skipParenthesizedExprUp(methodCall.getParent()), methodCall, typeParameter, substitutor, forCompletion); + substitution = inferMethodTypeParameterFromParent(skipParenthesizedExprUp(methodCall.getParent()), methodCall, typeParameter, substitutor, policy); } return substitution; } @@ -428,7 +421,7 @@ public class PsiResolveHelperImpl implements PsiResolveHelper { } @Nullable - private static Pair getSubstitutionForTypeParameterConstraint(PsiTypeParameter typeParam, + public static Pair getSubstitutionForTypeParameterConstraint(PsiTypeParameter typeParam, PsiType param, PsiType arg, boolean isContraVariantPosition, @@ -493,12 +486,6 @@ public class PsiResolveHelperImpl implements PsiResolveHelper { return PsiIntersectionType.createIntersection(types); } - private enum ConstraintType { - EQUALS, - SUBTYPE, - SUPERTYPE - } - //represents the result of failed type inference: in case we failed inferring from parameters, do not perform inference from context private static final Pair FAILED_INFERENCE = new Pair(PsiType.NULL, ConstraintType.EQUALS); @@ -614,7 +601,7 @@ public class PsiResolveHelperImpl implements PsiResolveHelper { PsiCallExpression methodCall, final PsiTypeParameter typeParameter, PsiSubstitutor substitutor, - final boolean forCompletion) { + ParameterTypeInferencePolicy policy) { Pair constraint = null; PsiType expectedType = null; @@ -634,10 +621,11 @@ public class PsiResolveHelperImpl implements PsiResolveHelper { expectedType = method.getReturnType(); } } - else if (parent instanceof PsiExpressionList && forCompletion) { + else if (parent instanceof PsiExpressionList) { final PsiElement pParent = parent.getParent(); if (pParent instanceof PsiCallExpression && parent.equals(((PsiCallExpression)pParent).getArgumentList())) { - constraint = inferTypeForCompletionFromCallContext(methodCall, (PsiExpressionList)parent, (PsiCallExpression)pParent, typeParameter); + constraint = policy.inferTypeConstraintFromCallContext(methodCall, (PsiExpressionList)parent, (PsiCallExpression)pParent, + typeParameter); } } @@ -645,17 +633,8 @@ public class PsiResolveHelperImpl implements PsiResolveHelper { final GlobalSearchScope scope = parent.getResolveScope(); PsiType returnType = null; if (constraint == null) { - if (forCompletion && expectedType == null) { - ExpectedTypeInfo[] expectedTypes = ExpectedTypesProvider.getExpectedTypes(methodCall, true); - if (expectedTypes.length > 0) { - expectedType = expectedTypes[0].getType(); - } - } - if (expectedType == null) { - expectedType = forCompletion ? - PsiType.NULL : - PsiType.getJavaLangObject(manager, scope); + expectedType = policy.getDefaultExpectedType(methodCall); } returnType = ((PsiMethod)typeParameter.getOwner()).getReturnType(); @@ -686,19 +665,11 @@ public class PsiResolveHelperImpl implements PsiResolveHelper { PsiType superType = finalSubstitutor.substitute(superTypes[0]); if (superType == null) superType = PsiType.getJavaLangObject(manager, scope); if (superType == null) return null; - if (forCompletion && !(superType instanceof PsiWildcardType)) { - result = new Pair(PsiWildcardType.createExtends(manager, superType), ConstraintType.EQUALS); - } - else { - result = new Pair(superType, ConstraintType.SUBTYPE); - } + return policy.getInferredTypeWithNoConstraint(manager, superType); } else { PsiType guess = constraint.getFirst(); - if (forCompletion && guess != null && !(guess instanceof PsiWildcardType)) { - if (constraint.getSecond() == ConstraintType.SUPERTYPE) guess = PsiWildcardType.createExtends(manager, guess); - else if (constraint.getSecond() == ConstraintType.SUBTYPE) guess = PsiWildcardType.createSuper(manager, guess); - } + guess = policy.adjustInferredType(manager, 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())) { @@ -737,64 +708,4 @@ public class PsiResolveHelperImpl implements PsiResolveHelper { } return parent; } - - @Nullable - private static Pair inferTypeForCompletionFromCallContext(@NotNull final PsiCallExpression innerMethodCall, - final PsiExpressionList expressionList, - final PsiCallExpression contextCall, - final PsiTypeParameter typeParameter) { - final MethodCandidatesProcessor processor = new MethodCandidatesProcessor(contextCall); - try { - //can't call resolve() since it obtains full substitution, that may result in infinite recursion - PsiScopesUtil.setupAndRunProcessor(processor, contextCall, false); - PsiExpression[] expressions = expressionList.getExpressions(); - int i = ArrayUtil.find(expressions, innerMethodCall); - assert i >= 0; - final JavaResolveResult[] results = processor.getResult(); - PsiMethod owner = (PsiMethod)typeParameter.getOwner(); - if (owner == null) return null; - - final PsiType innerReturnType = owner.getReturnType(); - for (final JavaResolveResult result : results) { - final PsiSubstitutor substitutor; - if (result instanceof MethodCandidateInfo) { - List leftArgs = Arrays.asList(expressions).subList(0, i); - substitutor = ((MethodCandidateInfo)result).inferTypeArguments(true, leftArgs.toArray(new PsiExpression[leftArgs.size()])); - } else { - substitutor = result.getSubstitutor(); - } - - final PsiElement element = result.getElement(); - if (element instanceof PsiMethod) { - final PsiMethod method = (PsiMethod)element; - final PsiParameter[] parameters = method.getParameterList().getParameters(); - PsiParameter parameter = null; - if (parameters.length > i) { - parameter = parameters[i]; - } - else if (method.isVarArgs()) { - parameter = parameters[parameters.length - 1]; - } - if (parameter != null) { - final PsiParameter finalParameter = parameter; - PsiType type = ourGuard.doPreventingRecursion(innerMethodCall, true, new Computable() { - @Override - public PsiType compute() { - return substitutor.substitute(finalParameter.getType()); - } - }); - final Pair constraint = - getSubstitutionForTypeParameterConstraint(typeParameter, innerReturnType, type, false, - PsiUtil.getLanguageLevel(innerMethodCall)); - if (constraint != null) return constraint; - } - } - } - } - catch (MethodProcessorSetupFailedException ev) { - return null; - } - - return null; - } } diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectProcessor.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectProcessor.java index 95a3b65a36dc..34ecea450599 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectProcessor.java @@ -35,6 +35,7 @@ import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.codeStyle.VariableKind; import com.intellij.psi.controlFlow.ControlFlowUtil; import com.intellij.psi.impl.source.PsiImmediateClassType; +import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.util.PropertyUtil; @@ -451,7 +452,8 @@ public class ExtractMethodObjectProcessor extends BaseRefactoringProcessor { for (final PsiTypeParameter typeParameter : methodTypeParameters) { final PsiType type = resolveHelper.inferTypeForMethodTypeParameter(typeParameter, getMethod().getParameterList().getParameters(), methodCallExpression.getArgumentList().getExpressions(), - PsiSubstitutor.EMPTY, methodCallExpression, false); + PsiSubstitutor.EMPTY, methodCallExpression, + DefaultParameterTypeInferencePolicy.INSTANCE); if (type == null || PsiType.NULL.equals(type)) { return ""; } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceParameter/JavaIntroduceParameterMethodUsagesProcessor.java b/java/java-impl/src/com/intellij/refactoring/introduceParameter/JavaIntroduceParameterMethodUsagesProcessor.java index 1b652c516811..827540c785a4 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceParameter/JavaIntroduceParameterMethodUsagesProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceParameter/JavaIntroduceParameterMethodUsagesProcessor.java @@ -25,6 +25,7 @@ import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.impl.PsiDiamondTypeUtil; +import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy; import com.intellij.psi.javadoc.PsiDocTag; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; @@ -120,7 +121,8 @@ public class JavaIntroduceParameterMethodUsagesProcessor implements IntroducePar final Project project = method.getProject(); final PsiSubstitutor psiSubstitutor = JavaPsiFacade.getInstance(project).getResolveHelper() .inferTypeArguments(method.getTypeParameters(), method.getParameterList().getParameters(), - argList.getExpressions(), PsiSubstitutor.EMPTY, callExpression, false); + argList.getExpressions(), PsiSubstitutor.EMPTY, callExpression, + DefaultParameterTypeInferencePolicy.INSTANCE); RefactoringUtil.replaceMovedMemberTypeParameters(initializer, PsiUtil.typeParametersIterable(method), psiSubstitutor, JavaPsiFacade.getElementFactory(project)); } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java index b865966aa0b1..ca1365e05f9a 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java @@ -45,6 +45,7 @@ import com.intellij.openapi.wm.WindowManager; import com.intellij.psi.*; import com.intellij.psi.codeStyle.*; import com.intellij.psi.impl.PsiDiamondTypeUtil; +import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy; import com.intellij.psi.impl.source.tree.java.ReplaceExpressionUtil; import com.intellij.psi.util.*; import com.intellij.refactoring.*; @@ -390,7 +391,8 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase impleme final PsiExpression[] args = parent.getArgumentList().getExpressions(); final PsiSubstitutor psiSubstitutor = JavaPsiFacade.getInstance(parent.getProject()).getResolveHelper().inferTypeArguments(psiMethod.getTypeParameters(), parameters, - args, PsiSubstitutor.EMPTY, parent, false); + args, PsiSubstitutor.EMPTY, parent, + DefaultParameterTypeInferencePolicy.INSTANCE); if (startOffset < args[parameters.length - 1].getTextOffset()) return null; diff --git a/java/java-impl/src/com/intellij/slicer/SliceUtil.java b/java/java-impl/src/com/intellij/slicer/SliceUtil.java index c2e88bbdd0b6..281d54e289f5 100644 --- a/java/java-impl/src/com/intellij/slicer/SliceUtil.java +++ b/java/java-impl/src/com/intellij/slicer/SliceUtil.java @@ -21,6 +21,7 @@ import com.intellij.openapi.util.Comparing; import com.intellij.psi.*; import com.intellij.psi.impl.PsiSubstitutorImpl; import com.intellij.psi.impl.source.DummyHolder; +import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy; import com.intellij.psi.search.SearchScope; import com.intellij.psi.search.searches.MethodReferencesSearch; import com.intellij.psi.search.searches.OverridingMethodsSearch; @@ -309,7 +310,8 @@ public class SliceUtil { PsiTypeParameter[] typeParameters = substitutor.getSubstitutionMap().keySet().toArray(new PsiTypeParameter[0]); PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(project).getResolveHelper(); - substitutor = resolveHelper.inferTypeArguments(typeParameters, actualParameters, expressions, parentSubstitutor, argumentList, false); + substitutor = resolveHelper.inferTypeArguments(typeParameters, actualParameters, expressions, parentSubstitutor, argumentList, + DefaultParameterTypeInferencePolicy.INSTANCE); } substitutor = removeRawMappingsLeftFromResolve(substitutor); diff --git a/java/java-psi-api/src/com/intellij/psi/ConstraintType.java b/java/java-psi-api/src/com/intellij/psi/ConstraintType.java new file mode 100644 index 000000000000..ac8a56408bd7 --- /dev/null +++ b/java/java-psi-api/src/com/intellij/psi/ConstraintType.java @@ -0,0 +1,22 @@ +/* + * Copyright 2000-2011 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; + +public enum ConstraintType { + EQUALS, + SUBTYPE, + SUPERTYPE +} diff --git a/java/java-psi-api/src/com/intellij/psi/PsiResolveHelper.java b/java/java-psi-api/src/com/intellij/psi/PsiResolveHelper.java index e7f9f74477fb..851137c0b88f 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiResolveHelper.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiResolveHelper.java @@ -18,6 +18,7 @@ package com.intellij.psi; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.project.Project; import com.intellij.pom.java.LanguageLevel; +import com.intellij.psi.impl.source.resolve.ParameterTypeInferencePolicy; import com.intellij.psi.infos.CandidateInfo; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -122,7 +123,7 @@ public interface PsiResolveHelper { @NotNull PsiExpression[] arguments, @NotNull PsiSubstitutor partialSubstitutor, @Nullable PsiElement parent, - final boolean forCompletion); + final ParameterTypeInferencePolicy policy); @NotNull PsiSubstitutor inferTypeArguments(@NotNull PsiTypeParameter[] typeParameters, @@ -130,7 +131,7 @@ public interface PsiResolveHelper { @NotNull PsiExpression[] arguments, @NotNull PsiSubstitutor partialSubstitutor, @NotNull PsiElement parent, - final boolean forCompletion); + final ParameterTypeInferencePolicy policy); @NotNull PsiSubstitutor inferTypeArguments(@NotNull PsiTypeParameter[] typeParameters, diff --git a/java/java-psi-api/src/com/intellij/psi/impl/source/resolve/DefaultParameterTypeInferencePolicy.java b/java/java-psi-api/src/com/intellij/psi/impl/source/resolve/DefaultParameterTypeInferencePolicy.java new file mode 100644 index 000000000000..9302a2dc1e49 --- /dev/null +++ b/java/java-psi-api/src/com/intellij/psi/impl/source/resolve/DefaultParameterTypeInferencePolicy.java @@ -0,0 +1,54 @@ +/* + * Copyright 2000-2011 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.impl.source.resolve; + +import com.intellij.openapi.util.Pair; +import com.intellij.psi.*; +import org.jetbrains.annotations.Nullable; + +/** + * @author yole + */ +public class DefaultParameterTypeInferencePolicy extends ParameterTypeInferencePolicy { + public static final DefaultParameterTypeInferencePolicy INSTANCE = new DefaultParameterTypeInferencePolicy(); + + private DefaultParameterTypeInferencePolicy() { + } + + @Nullable + @Override + public Pair inferTypeConstraintFromCallContext(PsiCallExpression innerMethodCall, + PsiExpressionList parent, + PsiCallExpression contextCall, + PsiTypeParameter typeParameter) { + return null; + } + + @Override + public PsiType getDefaultExpectedType(PsiCallExpression methodCall) { + return PsiType.getJavaLangObject(methodCall.getManager(), methodCall.getResolveScope()); + } + + @Override + public Pair getInferredTypeWithNoConstraint(PsiManager manager, PsiType superType) { + return new Pair(superType, ConstraintType.SUBTYPE); + } + + @Override + public PsiType adjustInferredType(PsiManager manager, PsiType guess, ConstraintType constraintType) { + return guess; + } +} diff --git a/java/java-psi-api/src/com/intellij/psi/impl/source/resolve/ParameterTypeInferencePolicy.java b/java/java-psi-api/src/com/intellij/psi/impl/source/resolve/ParameterTypeInferencePolicy.java new file mode 100644 index 000000000000..e50133b99fb8 --- /dev/null +++ b/java/java-psi-api/src/com/intellij/psi/impl/source/resolve/ParameterTypeInferencePolicy.java @@ -0,0 +1,37 @@ +/* + * Copyright 2000-2011 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.impl.source.resolve; + +import com.intellij.openapi.util.Pair; +import com.intellij.psi.*; +import org.jetbrains.annotations.Nullable; + +/** + * @author yole + */ +public abstract class ParameterTypeInferencePolicy { + @Nullable + public abstract Pair inferTypeConstraintFromCallContext(PsiCallExpression innerMethodCall, + PsiExpressionList parent, + PsiCallExpression contextCall, + PsiTypeParameter typeParameter); + + public abstract PsiType getDefaultExpectedType(PsiCallExpression methodCall); + + public abstract Pair getInferredTypeWithNoConstraint(PsiManager manager, PsiType superType); + + public abstract PsiType adjustInferredType(PsiManager manager, PsiType guess, ConstraintType second); +} diff --git a/java/java-psi-api/src/com/intellij/psi/infos/MethodCandidateInfo.java b/java/java-psi-api/src/com/intellij/psi/infos/MethodCandidateInfo.java index 1b99fe323bad..aacbc93a047b 100644 --- a/java/java-psi-api/src/com/intellij/psi/infos/MethodCandidateInfo.java +++ b/java/java-psi-api/src/com/intellij/psi/infos/MethodCandidateInfo.java @@ -17,6 +17,8 @@ package com.intellij.psi.infos; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; +import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy; +import com.intellij.psi.impl.source.resolve.ParameterTypeInferencePolicy; import com.intellij.psi.util.PsiUtil; import org.jetbrains.annotations.Nullable; @@ -84,7 +86,7 @@ public class MethodCandidateInfo extends CandidateInfo{ PsiSubstitutor incompleteSubstitutor = super.getSubstitutor(); PsiMethod method = getElement(); if (myTypeArguments == null) { - myCalcedSubstitutor = inferTypeArguments(false); + myCalcedSubstitutor = inferTypeArguments(DefaultParameterTypeInferencePolicy.INSTANCE); } else { PsiTypeParameter[] typeParams = method.getTypeParameters(); @@ -114,13 +116,13 @@ public class MethodCandidateInfo extends CandidateInfo{ return (PsiMethod)super.getElement(); } - public PsiSubstitutor inferTypeArguments(final boolean forCompletion) { - return inferTypeArguments(forCompletion, myArgumentList instanceof PsiExpressionList + public PsiSubstitutor inferTypeArguments(final ParameterTypeInferencePolicy policy) { + return inferTypeArguments(policy, myArgumentList instanceof PsiExpressionList ? ((PsiExpressionList)myArgumentList).getExpressions() : PsiExpression.EMPTY_ARRAY); } - public PsiSubstitutor inferTypeArguments(final boolean forCompletion, final PsiExpression[] arguments) { + public PsiSubstitutor inferTypeArguments(final ParameterTypeInferencePolicy policy, final PsiExpression[] arguments) { PsiMethod method = getElement(); PsiTypeParameter[] typeParameters = method.getTypeParameters(); @@ -133,7 +135,7 @@ public class MethodCandidateInfo extends CandidateInfo{ } return javaPsiFacade.getResolveHelper().inferTypeArguments(typeParameters, method.getParameterList().getParameters(), arguments, mySubstitutor, - myArgumentList.getParent(), forCompletion); + myArgumentList.getParent(), policy); } public boolean isInferencePossible() { 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 6b3d12ed1e0e..fad4772ca4b1 100644 --- a/java/java-psi-impl/src/com/intellij/psi/PsiDiamondTypeImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/PsiDiamondTypeImpl.java @@ -20,6 +20,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; 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.search.GlobalSearchScope; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; @@ -243,7 +244,7 @@ public class PsiDiamondTypeImpl extends PsiDiamondType { final PsiExpressionList argumentList = expression.getArgumentList(); final PsiExpression[] expressions = argumentList.getExpressions(); return resolveHelper - .inferTypeArguments(staticFactoryMethod.getTypeParameters(), parameters, expressions, PsiSubstitutor.EMPTY, parent, false); + .inferTypeArguments(staticFactoryMethod.getTypeParameters(), parameters, expressions, PsiSubstitutor.EMPTY, parent, DefaultParameterTypeInferencePolicy.INSTANCE); } public static boolean hasDefaultConstructor(@NotNull final PsiClass psiClass) {