blob: 0bf1c1cd7218192dd2df147a267da6f53ffc8c0a [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.NullableComputable;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.VariableKind;
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;
import com.intellij.psi.search.PsiShortNamesCache;
import com.intellij.psi.search.searches.DeepestSuperMethodsSearch;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.NullableFunction;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.Stack;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author ven
*/
public class ExpectedTypesProvider {
private static final ExpectedTypeInfo VOID_EXPECTED = createInfoImpl(PsiType.VOID, ExpectedTypeInfo.TYPE_OR_SUBTYPE, PsiType.VOID, TailType.SEMICOLON);
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.ExpectedTypesProvider");
private ExpectedTypesProvider() {
}
public static ExpectedTypesProvider getInstance(@NotNull Project project) {
return ServiceManager.getService(project, ExpectedTypesProvider.class);
}
private static final int MAX_COUNT = 50;
private static final ExpectedClassProvider ourGlobalScopeClassProvider = new ExpectedClassProvider() {
@Override
@NotNull
public PsiField[] findDeclaredFields(@NotNull final PsiManager manager, @NotNull String name) {
final PsiShortNamesCache cache = PsiShortNamesCache.getInstance(manager.getProject());
GlobalSearchScope scope = GlobalSearchScope.allScope(manager.getProject());
return cache.getFieldsByName(name, scope);
}
@Override
@NotNull
public PsiMethod[] findDeclaredMethods(@NotNull final PsiManager manager, @NotNull String name) {
final PsiShortNamesCache cache = PsiShortNamesCache.getInstance(manager.getProject());
GlobalSearchScope scope = GlobalSearchScope.allScope(manager.getProject());
return cache.getMethodsByNameIfNotMoreThan(name, scope, MAX_COUNT);
}
};
private static final PsiType[] PRIMITIVE_TYPES = {PsiType.BYTE, PsiType.CHAR, PsiType.SHORT, PsiType.INT, PsiType.LONG, PsiType.FLOAT, PsiType.DOUBLE};
@NotNull
public static ExpectedTypeInfo createInfo(@NotNull PsiType type, @ExpectedTypeInfo.Type int kind, PsiType defaultType, @NotNull TailType tailType) {
return createInfoImpl(type, kind, defaultType, tailType);
}
@NotNull
private static ExpectedTypeInfoImpl createInfoImpl(@NotNull PsiType type, PsiType defaultType) {
return createInfoImpl(type, ExpectedTypeInfo.TYPE_OR_SUBTYPE, defaultType, TailType.NONE);
}
@NotNull
private static ExpectedTypeInfoImpl createInfoImpl(@NotNull PsiType type, @ExpectedTypeInfo.Type int kind, PsiType defaultType, @NotNull TailType tailType) {
return new ExpectedTypeInfoImpl(type, kind, defaultType, tailType, null, ExpectedTypeInfoImpl.NULL);
}
@NotNull
private static ExpectedTypeInfoImpl createInfoImpl(@NotNull PsiType type,
int kind,
PsiType defaultType,
@NotNull TailType tailType,
PsiMethod calledMethod,
NullableComputable<String> expectedName) {
return new ExpectedTypeInfoImpl(type, kind, defaultType, tailType, calledMethod, expectedName);
}
@NotNull
public static ExpectedTypeInfo[] getExpectedTypes(@Nullable PsiExpression expr, boolean forCompletion) {
return getExpectedTypes(expr, forCompletion, false, false);
}
@NotNull
public static ExpectedTypeInfo[] getExpectedTypes(@Nullable PsiExpression expr, boolean forCompletion, final boolean voidable, boolean usedAfter) {
return getExpectedTypes(expr, forCompletion, ourGlobalScopeClassProvider, voidable, usedAfter);
}
@NotNull
public static ExpectedTypeInfo[] getExpectedTypes(@Nullable PsiExpression expr,
boolean forCompletion,
ExpectedClassProvider classProvider, boolean usedAfter) {
return getExpectedTypes(expr, forCompletion, classProvider, false, usedAfter);
}
@NotNull
public static ExpectedTypeInfo[] getExpectedTypes(@Nullable PsiExpression expr, boolean forCompletion, ExpectedClassProvider classProvider,
final boolean voidable, boolean usedAfter) {
if (expr == null) return ExpectedTypeInfo.EMPTY_ARRAY;
PsiElement parent = expr.getParent();
MyParentVisitor visitor = new MyParentVisitor(expr, forCompletion, classProvider, voidable, usedAfter);
if (parent != null) {
parent.accept(visitor);
}
return visitor.getResult();
}
public static PsiType[] processExpectedTypes(@NotNull ExpectedTypeInfo[] infos,
@NotNull PsiTypeVisitor<PsiType> visitor, @NotNull Project project) {
LinkedHashSet<PsiType> set = new LinkedHashSet<PsiType>();
for (ExpectedTypeInfo info : infos) {
ExpectedTypeInfoImpl infoImpl = (ExpectedTypeInfoImpl)info;
if (infoImpl.getDefaultType() instanceof PsiClassType) {
JavaResolveResult result = ((PsiClassType)infoImpl.getDefaultType()).resolveGenerics();
PsiClass aClass = (PsiClass)result.getElement();
if (aClass instanceof PsiAnonymousClass) {
processType(((PsiAnonymousClass)aClass).getBaseClassType(), visitor, set);
((PsiAnonymousClass)aClass).getBaseClassType().accept(visitor);
}
else {
processType(infoImpl.getDefaultType(), visitor, set);
}
}
else {
processType(infoImpl.getDefaultType(), visitor, set);
}
if (infoImpl.getKind() == ExpectedTypeInfo.TYPE_OR_SUPERTYPE) {
processAllSuperTypes(infoImpl.getType(), visitor, project, set);
}
else if (infoImpl.getKind() == ExpectedTypeInfo.TYPE_OR_SUBTYPE) {
if (infoImpl.getType() instanceof PsiPrimitiveType) {
processPrimitiveTypeAndSubtypes((PsiPrimitiveType)infoImpl.getType(), visitor, set);
}
//else too expensive to search
}
}
return set.toArray(PsiType.createArray(set.size()));
}
private static void processType(@NotNull PsiType type, @NotNull PsiTypeVisitor<PsiType> visitor, @NotNull Set<PsiType> typeSet) {
PsiType accepted = type.accept(visitor);
if (accepted != null) typeSet.add(accepted);
}
public static void processPrimitiveTypeAndSubtypes(@NotNull PsiPrimitiveType type, @NotNull PsiTypeVisitor<PsiType> visitor, @NotNull Set<PsiType> set) {
if (type.equals(PsiType.BOOLEAN) || type.equals(PsiType.VOID) || type.equals(PsiType.NULL)) return;
for (int i = 0; ; i++) {
final PsiType primitive = PRIMITIVE_TYPES[i];
processType(primitive, visitor, set);
if (primitive.equals(type)) return;
}
}
public static void processAllSuperTypes(@NotNull PsiType type, @NotNull PsiTypeVisitor<PsiType> visitor, @NotNull Project project, @NotNull Set<PsiType> set) {
if (type instanceof PsiPrimitiveType) {
if (type.equals(PsiType.BOOLEAN) || type.equals(PsiType.VOID) || type.equals(PsiType.NULL)) return;
Stack<PsiType> stack = new Stack<PsiType>();
for (int i = PRIMITIVE_TYPES.length - 1; !PRIMITIVE_TYPES[i].equals(type); i--) {
stack.push(PRIMITIVE_TYPES[i]);
}
while(!stack.empty()) {
processType(stack.pop(), visitor, set);
}
}
else{
PsiManager manager = PsiManager.getInstance(project);
GlobalSearchScope resolveScope = type.getResolveScope();
if (resolveScope == null) resolveScope = GlobalSearchScope.allScope(project);
PsiClassType objectType = PsiType.getJavaLangObject(manager, resolveScope);
processType(objectType, visitor, set);
if (type instanceof PsiClassType) {
PsiType[] superTypes = type.getSuperTypes();
for (PsiType superType : superTypes) {
processType(superType, visitor, set);
processAllSuperTypes(superType, visitor, project, set);
}
}
}
}
private static class MyParentVisitor extends JavaElementVisitor {
private PsiExpression myExpr;
private final boolean myForCompletion;
private final boolean myUsedAfter;
private final ExpectedClassProvider myClassProvider;
private final boolean myVoidable;
final List<ExpectedTypeInfo> myResult = ContainerUtil.newArrayList();
@NonNls private static final String LENGTH_SYNTHETIC_ARRAY_FIELD = "length";
private MyParentVisitor(PsiExpression expr,
boolean forCompletion,
ExpectedClassProvider classProvider,
boolean voidable,
boolean usedAfter) {
myExpr = expr;
myForCompletion = forCompletion;
myClassProvider = classProvider;
myVoidable = voidable;
myUsedAfter = usedAfter;
}
@NotNull
public ExpectedTypeInfo[] getResult() {
return myResult.toArray(new ExpectedTypeInfo[myResult.size()]);
}
@Override
public void visitParenthesizedExpression(PsiParenthesizedExpression expression) {
PsiElement parent = expression.getParent();
if (parent != null) {
final MyParentVisitor visitor = new MyParentVisitor(expression, myForCompletion, myClassProvider, myVoidable, myUsedAfter);
parent.accept(visitor);
for (final ExpectedTypeInfo info : visitor.myResult) {
myResult.add(createInfoImpl(info.getType(), info.getKind(), info.getDefaultType(), TailTypes.RPARENTH, info.getCalledMethod(),
new NullableComputable<String>() {
@Nullable
@Override
public String compute() {
return ((ExpectedTypeInfoImpl)info).getExpectedName();
}
}));
}
}
}
@Override
public void visitAnnotationMethod(@NotNull final PsiAnnotationMethod method) {
if (myExpr == method.getDefaultValue()) {
final PsiType type = method.getReturnType();
if (type != null) {
myResult.add(createInfoImpl(type, ExpectedTypeInfo.TYPE_OR_SUBTYPE, type, TailType.SEMICOLON));
}
}
}
@Override
public void visitReferenceExpression(@NotNull PsiReferenceExpression expression) {
if (myForCompletion) {
final MyParentVisitor visitor = new MyParentVisitor(expression, myForCompletion, myClassProvider, myVoidable, myUsedAfter);
expression.getParent().accept(visitor);
myResult.addAll(visitor.myResult);
return;
}
String referenceName = expression.getReferenceName();
if (referenceName != null) {
final PsiElement parent = expression.getParent();
if (parent instanceof PsiMethodCallExpression) {
Collections.addAll(myResult, findClassesWithDeclaredMethod((PsiMethodCallExpression)parent, false));
}
else if (parent instanceof PsiReferenceExpression || parent instanceof PsiVariable ||
parent instanceof PsiExpression) {
if (LENGTH_SYNTHETIC_ARRAY_FIELD.equals(referenceName)) {
myResult.addAll(anyArrayType());
}
else {
Collections.addAll(myResult, findClassesWithDeclaredField(expression));
}
}
}
}
@Override
public void visitExpressionStatement(PsiExpressionStatement statement) {
if (myVoidable) {
myResult.add(VOID_EXPECTED);
}
}
@Override public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) {
myExpr = (PsiExpression)myExpr.getParent();
expression.getParent().accept(this);
}
@Override public void visitAnnotationArrayInitializer(@NotNull PsiArrayInitializerMemberValue initializer) {
PsiElement parent = initializer.getParent();
while (parent instanceof PsiArrayInitializerMemberValue) {
parent = parent.getParent();
}
final PsiType type;
if (parent instanceof PsiNameValuePair) {
type = getAnnotationMethodType((PsiNameValuePair)parent);
}
else {
type = ((PsiAnnotationMethod)parent).getReturnType();
}
if (type instanceof PsiArrayType) {
myResult.add(createInfoImpl(((PsiArrayType)type).getComponentType(), type));
}
}
@Override public void visitNameValuePair(@NotNull PsiNameValuePair pair) {
final PsiType type = getAnnotationMethodType(pair);
if (type == null) return;
myResult.add(createInfoImpl(type, type));
if (type instanceof PsiArrayType) {
PsiType componentType = ((PsiArrayType)type).getComponentType();
myResult.add(createInfoImpl(componentType, componentType));
}
}
@Nullable
private static PsiType getAnnotationMethodType(@NotNull final PsiNameValuePair pair) {
final PsiReference reference = pair.getReference();
if (reference != null) {
final PsiElement method = reference.resolve();
if (method instanceof PsiMethod) {
return ((PsiMethod)method).getReturnType();
}
}
return null;
}
@Override
public void visitLambdaExpression(PsiLambdaExpression lambdaExpression) {
super.visitLambdaExpression(lambdaExpression);
final PsiType functionalInterfaceType = lambdaExpression.getFunctionalInterfaceType();
final PsiMethod scopeMethod = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType);
if (scopeMethod != null) {
visitMethodReturnType(scopeMethod, LambdaUtil.getFunctionalInterfaceReturnType(functionalInterfaceType), LambdaHighlightingUtil
.insertSemicolonAfter(lambdaExpression));
}
}
@Override public void visitReturnStatement(PsiReturnStatement statement) {
final PsiMethod method;
final PsiType type;
final boolean tailTypeSemicolon;
final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(statement, PsiLambdaExpression.class);
if (lambdaExpression != null) {
final PsiType functionalInterfaceType = lambdaExpression.getFunctionalInterfaceType();
method = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType);
type = LambdaUtil.getFunctionalInterfaceReturnType(functionalInterfaceType);
tailTypeSemicolon = LambdaHighlightingUtil.insertSemicolonAfter(lambdaExpression);
}
else {
method = PsiTreeUtil.getParentOfType(statement, PsiMethod.class);
type = method != null ? method.getReturnType() : null;
tailTypeSemicolon = true;
}
if (method != null) {
visitMethodReturnType(method, type, tailTypeSemicolon);
}
}
private void visitMethodReturnType(final PsiMethod scopeMethod, PsiType type, boolean tailTypeSemicolon) {
if (type != null) {
NullableComputable<String> expectedName;
if (PropertyUtil.isSimplePropertyAccessor(scopeMethod)) {
expectedName = new NullableComputable<String>() {
@Override
public String compute() {
return PropertyUtil.getPropertyName(scopeMethod);
}
};
}
else {
expectedName = ExpectedTypeInfoImpl.NULL;
}
myResult.add(createInfoImpl(type, ExpectedTypeInfo.TYPE_OR_SUBTYPE, type,
tailTypeSemicolon ? TailType.SEMICOLON : TailType.NONE, null, expectedName));
}
}
@Override public void visitIfStatement(PsiIfStatement statement) {
myResult.add(createInfoImpl(PsiType.BOOLEAN, ExpectedTypeInfo.TYPE_STRICTLY, PsiType.BOOLEAN, TailTypes.IF_RPARENTH));
}
@Override public void visitWhileStatement(PsiWhileStatement statement) {
myResult.add(createInfoImpl(PsiType.BOOLEAN, ExpectedTypeInfo.TYPE_STRICTLY, PsiType.BOOLEAN, TailTypes.WHILE_RPARENTH));
}
@Override public void visitDoWhileStatement(PsiDoWhileStatement statement) {
myResult.add(createInfoImpl(PsiType.BOOLEAN, ExpectedTypeInfo.TYPE_STRICTLY, PsiType.BOOLEAN, TailTypes.WHILE_RPARENTH));
}
@Override public void visitForStatement(@NotNull PsiForStatement statement) {
if (myExpr.equals(statement.getCondition())) {
myResult.add(createInfoImpl(PsiType.BOOLEAN, ExpectedTypeInfo.TYPE_STRICTLY, PsiType.BOOLEAN, TailType.SEMICOLON));
}
}
@Override
public void visitAssertStatement(@NotNull PsiAssertStatement statement) {
if (statement.getAssertDescription() == myExpr) {
final PsiClassType stringType = PsiType.getJavaLangString(myExpr.getManager(), myExpr.getResolveScope());
myResult.add(createInfoImpl(stringType, ExpectedTypeInfo.TYPE_STRICTLY, stringType, TailType.SEMICOLON));
}
else {
myResult.add(createInfoImpl(PsiType.BOOLEAN, ExpectedTypeInfo.TYPE_STRICTLY, PsiType.BOOLEAN, TailType.SEMICOLON));
}
}
@Override public void visitForeachStatement(@NotNull PsiForeachStatement statement) {
if (myExpr.equals(statement.getIteratedValue())) {
PsiType type = statement.getIterationParameter().getType();
PsiType arrayType = type.createArrayType();
myResult.add(createInfoImpl(arrayType, arrayType));
PsiManager manager = statement.getManager();
PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory();
PsiClass iterableClass =
JavaPsiFacade.getInstance(manager.getProject()).findClass("java.lang.Iterable", statement.getResolveScope());
if (iterableClass != null && iterableClass.getTypeParameters().length == 1) {
Map<PsiTypeParameter, PsiType> map = new HashMap<PsiTypeParameter, PsiType>();
map.put(iterableClass.getTypeParameters()[0], PsiWildcardType.createExtends(manager, type));
PsiType iterableType = factory.createType(iterableClass, factory.createSubstitutor(map));
myResult.add(createInfoImpl(iterableType, iterableType));
}
}
}
@Override public void visitSwitchStatement(@NotNull PsiSwitchStatement statement) {
myResult.add(createInfoImpl(PsiType.LONG, PsiType.INT));
if (!PsiUtil.isLanguageLevel5OrHigher(statement)) {
return;
}
PsiManager manager = statement.getManager();
PsiClassType enumType = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createTypeByFQClassName("java.lang.Enum", statement.getResolveScope());
myResult.add(createInfoImpl(enumType, enumType));
}
@Override
public void visitSwitchLabelStatement(@NotNull final PsiSwitchLabelStatement statement) {
final PsiSwitchStatement switchStatement = statement.getEnclosingSwitchStatement();
if (switchStatement != null) {
final PsiExpression expression = switchStatement.getExpression();
if (expression != null) {
final PsiType type = expression.getType();
if (type != null) {
myResult.add(createInfoImpl(type, ExpectedTypeInfo.TYPE_OR_SUBTYPE, type, TailType.CASE_COLON));
}
}
}
}
@Override public void visitSynchronizedStatement(@NotNull PsiSynchronizedStatement statement) {
PsiElementFactory factory = JavaPsiFacade.getInstance(statement.getProject()).getElementFactory();
PsiType objectType = factory.createTypeByFQClassName(CommonClassNames.JAVA_LANG_OBJECT, myExpr.getResolveScope());
myResult.add(createInfoImpl(objectType, objectType));
}
@Override public void visitVariable(@NotNull PsiVariable variable) {
PsiType type = variable.getType();
myResult.add(createInfoImpl(type, ExpectedTypeInfo.TYPE_OR_SUBTYPE, type,
variable instanceof PsiResourceVariable ? TailType.NONE : TailType.SEMICOLON, null, getPropertyName(variable)));
}
@Override public void visitAssignmentExpression(@NotNull PsiAssignmentExpression assignment) {
if (myExpr.equals(assignment.getRExpression())) {
PsiExpression lExpr = assignment.getLExpression();
PsiType type = lExpr.getType();
if (type != null) {
TailType tailType = getAssignmentRValueTailType(assignment);
NullableComputable<String> expectedName = ExpectedTypeInfoImpl.NULL;
if (lExpr instanceof PsiReferenceExpression) {
PsiElement refElement = ((PsiReferenceExpression)lExpr).resolve();
if (refElement instanceof PsiVariable) {
expectedName = getPropertyName((PsiVariable)refElement);
}
}
myResult.add(createInfoImpl(type, ExpectedTypeInfo.TYPE_OR_SUBTYPE, type, tailType, null, expectedName));
}
}
else {
if (myForCompletion) {
myExpr = (PsiExpression)myExpr.getParent();
assignment.getParent().accept(this);
return;
}
PsiExpression rExpr = assignment.getRExpression();
if (rExpr != null) {
PsiType type = rExpr.getType();
if (type != null && type != PsiType.NULL) {
if (type instanceof PsiClassType) {
final PsiClass resolved = ((PsiClassType)type).resolve();
if (resolved instanceof PsiAnonymousClass) {
type = ((PsiAnonymousClass)resolved).getBaseClassType();
}
}
final int kind = assignment.getOperationTokenType() != JavaTokenType.EQ
? ExpectedTypeInfo.TYPE_STRICTLY
: ExpectedTypeInfo.TYPE_OR_SUPERTYPE;
myResult.add(createInfoImpl(type, kind, type, TailType.NONE));
}
}
}
}
private static TailType getAssignmentRValueTailType(@NotNull PsiAssignmentExpression assignment) {
if (assignment.getParent() instanceof PsiExpressionStatement) {
if (!(assignment.getParent().getParent() instanceof PsiForStatement)) {
return TailType.SEMICOLON;
}
PsiForStatement forStatement = (PsiForStatement)assignment.getParent().getParent();
if (!assignment.getParent().equals(forStatement.getUpdate())) {
return TailType.SEMICOLON;
}
}
return TailType.NONE;
}
@Override public void visitExpressionList(@NotNull PsiExpressionList list) {
PsiResolveHelper helper = JavaPsiFacade.getInstance(list.getProject()).getResolveHelper();
if (list.getParent() instanceof PsiMethodCallExpression) {
PsiMethodCallExpression methodCall = (PsiMethodCallExpression)list.getParent();
CandidateInfo[] candidates = helper.getReferencedMethodCandidates(methodCall, false, true);
Collections.addAll(myResult, getExpectedArgumentTypesForMethodCall(candidates, list, myExpr, myForCompletion));
}
else if (list.getParent() instanceof PsiEnumConstant) {
getExpectedArgumentsTypesForEnumConstant((PsiEnumConstant)list.getParent(), list);
}
else if (list.getParent() instanceof PsiNewExpression) {
getExpectedArgumentsTypesForNewExpression((PsiNewExpression)list.getParent(), list);
}
else if (list.getParent() instanceof PsiAnonymousClass) {
getExpectedArgumentsTypesForNewExpression((PsiNewExpression)list.getParent().getParent(), list);
}
}
private void getExpectedArgumentsTypesForEnumConstant(@NotNull final PsiEnumConstant enumConstant,
@NotNull final PsiExpressionList list) {
final PsiClass aClass = enumConstant.getContainingClass();
if (aClass != null) {
LOG.assertTrue(aClass.isEnum());
getExpectedTypesForConstructorCall(aClass, list, PsiSubstitutor.EMPTY);
}
}
private void getExpectedArgumentsTypesForNewExpression(@NotNull final PsiNewExpression newExpr,
@NotNull final PsiExpressionList list) {
PsiType newType = newExpr.getType();
if (newType instanceof PsiClassType) {
JavaResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(newType);
PsiClass newClass = (PsiClass)resolveResult.getElement();
final PsiSubstitutor substitutor;
if (newClass instanceof PsiAnonymousClass) {
final PsiAnonymousClass anonymous = (PsiAnonymousClass)newClass;
newClass = anonymous.getBaseClassType().resolve();
if (newClass == null) return;
substitutor = TypeConversionUtil.getSuperClassSubstitutor(newClass, anonymous, PsiSubstitutor.EMPTY);
} else if (newClass != null) {
substitutor = resolveResult.getSubstitutor();
}
else {
return;
}
getExpectedTypesForConstructorCall(newClass, list, substitutor);
}
}
private void getExpectedTypesForConstructorCall(@NotNull final PsiClass referencedClass,
@NotNull final PsiExpressionList argumentList,
final PsiSubstitutor substitutor) {
List<CandidateInfo> array = new ArrayList<CandidateInfo>();
for (PsiMethod constructor : referencedClass.getConstructors()) {
array.add(new MethodCandidateInfo(constructor, substitutor, false, false, argumentList, null, argumentList.getExpressionTypes(), null));
}
CandidateInfo[] candidates = array.toArray(new CandidateInfo[array.size()]);
Collections.addAll(myResult, getExpectedArgumentTypesForMethodCall(candidates, argumentList, myExpr, myForCompletion));
}
@Override
public void visitPolyadicExpression(@NotNull PsiPolyadicExpression expr) {
PsiExpression[] operands = expr.getOperands();
final int index = Arrays.asList(operands).indexOf(myExpr);
assert index >= 0;
if (myForCompletion && index == 0) {
final MyParentVisitor visitor = new MyParentVisitor(expr, myForCompletion, myClassProvider, myVoidable, myUsedAfter);
myExpr = (PsiExpression)myExpr.getParent();
expr.getParent().accept(visitor);
myResult.addAll(visitor.myResult);
if (!(expr.getParent() instanceof PsiExpressionList)) {
for (int i = 0; i < myResult.size(); i++) {
final ExpectedTypeInfo info = myResult.get(i);
myResult.set(i, createInfoImpl(info.getType(), info.getKind(), info.getDefaultType(), TailType.NONE, info.getCalledMethod(),
new NullableComputable<String>() {
@Nullable
@Override
public String compute() {
return ((ExpectedTypeInfoImpl)info).getExpectedName();
}
}
));
}
}
return;
}
PsiExpression anotherExpr = index > 0 ? operands[0] : 1 < operands.length ? operands[1] : null;
PsiType anotherType = anotherExpr != null ? anotherExpr.getType() : null;
IElementType i = expr.getOperationTokenType();
if (i == JavaTokenType.MINUS ||
i == JavaTokenType.ASTERISK ||
i == JavaTokenType.DIV ||
i == JavaTokenType.PERC ||
i == JavaTokenType.LT ||
i == JavaTokenType.GT ||
i == JavaTokenType.LE ||
i == JavaTokenType.GE) {
if (anotherType != null) {
myResult.add(createInfoImpl(PsiType.DOUBLE, anotherType));
}
}
else if (i == JavaTokenType.PLUS) {
if (anotherType == null || anotherType.equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
PsiClassType objectType = PsiType.getJavaLangObject(expr.getManager(), expr.getResolveScope());
myResult.add(createInfoImpl(objectType, anotherType != null ? anotherType : objectType));
}
else if (PsiType.DOUBLE.isAssignableFrom(anotherType)) {
myResult.add(createInfoImpl(PsiType.DOUBLE, anotherType));
}
}
else if (i == JavaTokenType.EQEQ || i == JavaTokenType.NE) {
ContainerUtil.addIfNotNull(myResult, getEqualsType(anotherExpr));
}
else if (i == JavaTokenType.LTLT || i == JavaTokenType.GTGT || i == JavaTokenType.GTGTGT) {
if (anotherType != null) {
myResult.add(createInfoImpl(PsiType.LONG, ExpectedTypeInfo.TYPE_BETWEEN, PsiType.SHORT, TailType.NONE));
}
}
else if (i == JavaTokenType.OROR || i == JavaTokenType.ANDAND) {
myResult.add(createInfoImpl(PsiType.BOOLEAN, ExpectedTypeInfo.TYPE_STRICTLY, PsiType.BOOLEAN, TailType.NONE));
}
else if (i == JavaTokenType.OR || i == JavaTokenType.XOR || i == JavaTokenType.AND) {
if (anotherType != null) {
ExpectedTypeInfoImpl info;
if (PsiType.BOOLEAN.equals(anotherType)) {
info = createInfoImpl(anotherType, ExpectedTypeInfo.TYPE_STRICTLY, anotherType, TailType.NONE);
}
else {
info = createInfoImpl(PsiType.LONG, anotherType);
}
myResult.add(info);
}
}
}
@Nullable
private static ExpectedTypeInfo getEqualsType(@Nullable PsiExpression anotherExpr) {
PsiType anotherType = anotherExpr != null ? anotherExpr.getType() : null;
if (anotherType == null) {
return null;
}
NullableComputable<String> expectedName = ExpectedTypeInfoImpl.NULL;
if (anotherExpr instanceof PsiReferenceExpression) {
PsiElement refElement = ((PsiReferenceExpression)anotherExpr).resolve();
if (refElement instanceof PsiVariable) {
expectedName = getPropertyName((PsiVariable)refElement);
}
}
ExpectedTypeInfoImpl info;
if (anotherType instanceof PsiPrimitiveType) {
if (PsiType.BOOLEAN.equals(anotherType)) {
info = createInfoImpl(anotherType, ExpectedTypeInfo.TYPE_STRICTLY, anotherType, TailType.NONE, null, expectedName);
}
else if (PsiType.NULL.equals(anotherType)) {
PsiType objectType = PsiType.getJavaLangObject(anotherExpr.getManager(), anotherExpr.getResolveScope());
info = createInfoImpl(objectType, ExpectedTypeInfo.TYPE_OR_SUBTYPE, objectType, TailType.NONE, null, expectedName);
}
else {
info = createInfoImpl(PsiType.DOUBLE, ExpectedTypeInfo.TYPE_OR_SUBTYPE, anotherType, TailType.NONE, null, expectedName);
}
}
else {
info = createInfoImpl(anotherType, ExpectedTypeInfo.TYPE_STRICTLY, anotherType, TailType.NONE, null, expectedName);
}
return info;
}
@Override
public void visitPrefixExpression(@NotNull PsiPrefixExpression expr) {
IElementType i = expr.getOperationTokenType();
final PsiType type = expr.getType();
final TailType tailType = expr.getParent() instanceof PsiAssignmentExpression && ((PsiAssignmentExpression) expr.getParent()).getRExpression() == expr ?
getAssignmentRValueTailType((PsiAssignmentExpression) expr.getParent()) :
TailType.NONE;
if (i == JavaTokenType.PLUSPLUS || i == JavaTokenType.MINUSMINUS || i == JavaTokenType.TILDE) {
ExpectedTypeInfoImpl info;
if (myUsedAfter && type != null) {
info = createInfoImpl(type, ExpectedTypeInfo.TYPE_STRICTLY, type, tailType);
}
else {
if (type != null) {
info = createInfoImpl(type, ExpectedTypeInfo.TYPE_OR_SUPERTYPE, PsiType.INT, tailType);
}
else {
info = createInfoImpl(PsiType.LONG, ExpectedTypeInfo.TYPE_OR_SUBTYPE, PsiType.INT, tailType);
}
}
myResult.add(info);
}
else if (i == JavaTokenType.PLUS || i == JavaTokenType.MINUS) {
myResult.add(createInfoImpl(PsiType.DOUBLE, ExpectedTypeInfo.TYPE_OR_SUBTYPE, PsiType.INT, tailType));
}
else if (i == JavaTokenType.EXCL) {
myResult.add(createInfoImpl(PsiType.BOOLEAN, ExpectedTypeInfo.TYPE_STRICTLY, PsiType.BOOLEAN, tailType));
}
}
@Override
public void visitPostfixExpression(@NotNull PsiPostfixExpression expr) {
if (myForCompletion) return;
PsiType type = expr.getType();
ExpectedTypeInfoImpl info;
if (myUsedAfter && type != null) {
info = createInfoImpl(type, ExpectedTypeInfo.TYPE_STRICTLY, type, TailType.NONE);
}
else {
if (type != null) {
info = createInfoImpl(type, ExpectedTypeInfo.TYPE_OR_SUPERTYPE, PsiType.INT, TailType.NONE);
}
else {
info = createInfoImpl(PsiType.LONG, PsiType.INT);
}
}
myResult.add(info);
}
@Override
public void visitArrayInitializerExpression(@NotNull PsiArrayInitializerExpression expr) {
PsiElement pParent = expr.getParent();
PsiType arrayType = null;
if (pParent instanceof PsiVariable) {
arrayType = ((PsiVariable)pParent).getType();
}
else if (pParent instanceof PsiNewExpression) {
arrayType = ((PsiNewExpression)pParent).getType();
}
else if (pParent instanceof PsiArrayInitializerExpression) {
PsiType type = ((PsiArrayInitializerExpression)pParent).getType();
if (type instanceof PsiArrayType) {
arrayType = ((PsiArrayType)type).getComponentType();
}
}
if (arrayType instanceof PsiArrayType) {
PsiType componentType = ((PsiArrayType)arrayType).getComponentType();
myResult.add(createInfoImpl(componentType, componentType));
}
}
@Override public void visitNewExpression(@NotNull PsiNewExpression expression) {
PsiExpression[] arrayDimensions = expression.getArrayDimensions();
for (PsiExpression dimension : arrayDimensions) {
if (myExpr.equals(dimension)) {
myResult.add(createInfoImpl(PsiType.INT, PsiType.INT));
return;
}
}
}
@Override public void visitArrayAccessExpression(@NotNull PsiArrayAccessExpression expr) {
if (myExpr.equals(expr.getIndexExpression())) {
myResult.add(createInfoImpl(PsiType.INT, PsiType.INT));
}
else if (myExpr.equals(expr.getArrayExpression())) {
if (myForCompletion) {
myExpr = (PsiExpression)myExpr.getParent();
expr.getParent().accept(this);
return;
}
PsiElement parent = expr.getParent();
MyParentVisitor visitor = new MyParentVisitor(expr, myForCompletion, myClassProvider, myVoidable, myUsedAfter);
myExpr = (PsiExpression)myExpr.getParent();
parent.accept(visitor);
ExpectedTypeInfo[] componentTypeInfo = visitor.getResult();
if (componentTypeInfo.length == 0) {
myResult.addAll(anyArrayType());
}
else {
for (int i = 0; i < componentTypeInfo.length; i++) {
ExpectedTypeInfo compInfo = componentTypeInfo[i];
PsiType expectedArrayType = compInfo.getType().createArrayType();
myResult.add(createInfoImpl(expectedArrayType, expectedArrayType));
}
}
}
}
@Override public void visitConditionalExpression(@NotNull PsiConditionalExpression expr) {
if (myExpr.equals(expr.getCondition())) {
if (myForCompletion) {
myExpr = expr;
myExpr.getParent().accept(this);
return;
}
myResult.add(createInfoImpl(PsiType.BOOLEAN, ExpectedTypeInfo.TYPE_STRICTLY, PsiType.BOOLEAN, TailType.NONE));
}
else if (myExpr.equals(expr.getThenExpression())) {
ExpectedTypeInfo[] types = getExpectedTypes(expr, myForCompletion);
for (int i = 0; i < types.length; i++) {
final ExpectedTypeInfo info = types[i];
types[i] = createInfoImpl(info.getType(), info.getKind(), info.getDefaultType(), TailType.COND_EXPR_COLON, info.getCalledMethod(),
new NullableComputable<String>() {
@Nullable
@Override
public String compute() {
return ((ExpectedTypeInfoImpl)info).getExpectedName();
}
});
}
Collections.addAll(myResult, types);
}
else {
if (!myExpr.equals(expr.getElseExpression())) {
LOG.error(Arrays.asList(expr.getChildren()) + "; " + myExpr);
}
Collections.addAll(myResult, getExpectedTypes(expr, myForCompletion));
}
}
@Override public void visitThrowStatement(@NotNull PsiThrowStatement statement) {
if (statement.getException() == myExpr) {
PsiManager manager = statement.getManager();
PsiType throwableType = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createTypeByFQClassName("java.lang.Throwable", myExpr.getResolveScope());
PsiMember container = PsiTreeUtil.getParentOfType(statement, PsiMethod.class, PsiClass.class);
PsiType[] throwsTypes = PsiType.EMPTY_ARRAY;
if (container instanceof PsiMethod) {
throwsTypes = ((PsiMethod)container).getThrowsList().getReferencedTypes();
}
if (throwsTypes.length == 0) {
final PsiClassType exceptionType = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createTypeByFQClassName("java.lang.Exception", myExpr.getResolveScope());
throwsTypes = new PsiClassType[]{exceptionType};
}
for (int i = 0; i < throwsTypes.length; i++) {
myResult.add(createInfoImpl(
myExpr instanceof PsiTypeCastExpression && myForCompletion ?
throwsTypes[i] :
throwableType,
ExpectedTypeInfo.TYPE_OR_SUBTYPE,
throwsTypes[i],
TailType.SEMICOLON
));
}
}
}
@Override public void visitCodeFragment(@NotNull JavaCodeFragment codeFragment) {
if (codeFragment instanceof PsiExpressionCodeFragment) {
final PsiType type = ((PsiExpressionCodeFragment)codeFragment).getExpectedType();
if (type != null) {
myResult.add(createInfoImpl(type, type));
}
}
}
@NotNull
private ExpectedTypeInfo[] getExpectedArgumentTypesForMethodCall(@NotNull CandidateInfo[] allCandidates,
@NotNull PsiExpressionList argumentList,
@NotNull PsiExpression argument,
boolean forCompletion) {
if (allCandidates.length == 0) {
return ExpectedTypeInfo.EMPTY_ARRAY;
}
PsiResolveHelper helper = JavaPsiFacade.getInstance(myExpr.getProject()).getResolveHelper();
List<CandidateInfo> methodCandidates = new ArrayList<CandidateInfo>();
for (CandidateInfo candidate : allCandidates) {
PsiElement element = candidate.getElement();
if (element instanceof PsiMethod && helper.isAccessible((PsiMember)element, argumentList, null)) {
methodCandidates.add(candidate);
}
}
final PsiExpression[] args = argumentList.getExpressions().clone();
final int index = ArrayUtil.indexOf(args, argument);
LOG.assertTrue(index >= 0);
final PsiExpression[] leftArgs;
if (index <= args.length - 1) {
leftArgs = new PsiExpression[index];
System.arraycopy(args, 0, leftArgs, 0, index);
if (forCompletion) {
args[index] = null;
}
}
else {
leftArgs = null;
}
ParameterTypeInferencePolicy policy = forCompletion ? CompletionParameterTypeInferencePolicy.INSTANCE : DefaultParameterTypeInferencePolicy.INSTANCE;
Set<ExpectedTypeInfo> array = new LinkedHashSet<ExpectedTypeInfo>();
for (CandidateInfo candidateInfo : methodCandidates) {
PsiMethod method = (PsiMethod)candidateInfo.getElement();
PsiSubstitutor substitutor;
if (candidateInfo instanceof MethodCandidateInfo) {
final MethodCandidateInfo info = (MethodCandidateInfo)candidateInfo;
substitutor = info.inferTypeArguments(policy, args, true);
if (!info.isStaticsScopeCorrect() && method != null && !method.hasModifierProperty(PsiModifier.STATIC)) continue;
}
else {
substitutor = candidateInfo.getSubstitutor();
}
inferMethodCallArgumentTypes(argument, forCompletion, args, index, method, substitutor, array);
if (leftArgs != null && candidateInfo instanceof MethodCandidateInfo) {
substitutor = ((MethodCandidateInfo)candidateInfo).inferTypeArguments(policy, leftArgs, true);
inferMethodCallArgumentTypes(argument, forCompletion, leftArgs, index, method, substitutor, array);
}
}
// try to find some variants without considering previous argument PRIMITIVE_TYPES
if (forCompletion && array.isEmpty()) {
for (CandidateInfo candidate : methodCandidates) {
PsiMethod method = (PsiMethod)candidate.getElement();
PsiSubstitutor substitutor = candidate.getSubstitutor();
PsiParameter[] params = method.getParameterList().getParameters();
if (params.length <= index) continue;
PsiParameter param = params[index];
PsiType paramType = getParameterType(param, substitutor);
TailType tailType = getMethodArgumentTailType(argument, index, method, substitutor, params);
ExpectedTypeInfoImpl info = createInfoImpl(paramType, ExpectedTypeInfo.TYPE_OR_SUBTYPE, paramType,
tailType, method, getPropertyName(param));
array.add(info);
}
}
return array.toArray(new ExpectedTypeInfo[array.size()]);
}
@NotNull
private static TailType getMethodArgumentTailType(@NotNull final PsiExpression argument,
final int index,
@NotNull final PsiMethod method,
@NotNull final PsiSubstitutor substitutor,
@NotNull final PsiParameter[] params) {
if (index >= params.length || index == params.length - 2 && params[index + 1].isVarArgs()) {
return TailType.NONE;
}
if (index == params.length - 1) {
final PsiElement call = argument.getParent().getParent();
// ignore JspMethodCall
if (call instanceof SyntheticElement) return TailType.NONE;
PsiType returnType = method.getReturnType();
if (returnType != null) returnType = substitutor.substitute(returnType);
return getFinalCallParameterTailType(call, returnType, method);
}
return TailType.COMMA;
}
private static void inferMethodCallArgumentTypes(@NotNull final PsiExpression argument,
final boolean forCompletion,
@NotNull final PsiExpression[] args,
final int index,
@NotNull final PsiMethod method,
@NotNull final PsiSubstitutor substitutor,
@NotNull final Set<ExpectedTypeInfo> array) {
LOG.assertTrue(substitutor.isValid());
PsiParameter[] parameters = method.getParameterList().getParameters();
if (!forCompletion && parameters.length != args.length) return;
if (parameters.length <= index && !method.isVarArgs()) return;
for (int j = 0; j < index; j++) {
PsiType paramType = getParameterType(parameters[Math.min(parameters.length - 1, j)],
substitutor);
PsiType argType = args[j].getType();
if (argType != null && !paramType.isAssignableFrom(argType)) return;
}
PsiParameter parameter = parameters[Math.min(parameters.length - 1, index)];
PsiType parameterType = getParameterType(parameter, substitutor);
TailType tailType = getMethodArgumentTailType(argument, index, method, substitutor, parameters);
PsiType defaultType = getDefaultType(method, substitutor, parameterType, argument, args, index);
NullableComputable<String> propertyName = getPropertyName(parameter);
ExpectedTypeInfoImpl info = createInfoImpl(parameterType, ExpectedTypeInfo.TYPE_OR_SUBTYPE, defaultType, tailType, method,
propertyName);
array.add(info);
if (index == parameters.length - 1 && parameter.isVarArgs()) {
//Then we may still want to call with array argument
final PsiArrayType arrayType = parameterType.createArrayType();
ExpectedTypeInfoImpl info1 = createInfoImpl(arrayType, ExpectedTypeInfo.TYPE_OR_SUBTYPE, arrayType, tailType, method, propertyName);
array.add(info1);
}
}
@Nullable
private static PsiType getTypeParameterValue(@NotNull PsiClass rootClass, @NotNull PsiClass derivedClass, PsiSubstitutor substitutor, int index) {
final PsiTypeParameter[] typeParameters = rootClass.getTypeParameters();
if (typeParameters.length > index) {
final PsiSubstitutor psiSubstitutor = TypeConversionUtil.getClassSubstitutor(rootClass, derivedClass, substitutor);
if (psiSubstitutor != null) {
PsiType type = psiSubstitutor.substitute(typeParameters[index]);
if (type != null) return type;
}
}
return null;
}
@Nullable
protected static PsiType checkMethod(@NotNull PsiMethod method, @NotNull @NonNls final String className, @NotNull final NullableFunction<PsiClass,PsiType> function) {
final PsiClass containingClass = method.getContainingClass();
if (containingClass == null) return null;
if (className.equals(containingClass.getQualifiedName())) {
return function.fun(containingClass);
}
final PsiType[] type = {null};
DeepestSuperMethodsSearch.search(method).forEach(new Processor<PsiMethod>() {
@Override
public boolean process(@NotNull PsiMethod psiMethod) {
final PsiClass rootClass = psiMethod.getContainingClass();
assert rootClass != null;
if (className.equals(rootClass.getQualifiedName())) {
type[0] = function.fun(rootClass);
return false;
}
return true;
}
});
return type[0];
}
@Nullable
private static PsiType getDefaultType(@NotNull final PsiMethod method, final PsiSubstitutor substitutor, @NotNull final PsiType parameterType,
@NotNull final PsiExpression argument, @NotNull PsiExpression[] args, int index) {
final PsiClass containingClass = method.getContainingClass();
if (containingClass == null) return parameterType;
@NonNls final String name = method.getName();
if ("contains".equals(name) || "remove".equals(name)) {
final PsiType type = checkMethod(method, CommonClassNames.JAVA_UTIL_COLLECTION, new NullableFunction<PsiClass, PsiType>() {
@Override
public PsiType fun(@NotNull final PsiClass psiClass) {
return getTypeParameterValue(psiClass, containingClass, substitutor, 0);
}
});
if (type != null) return type;
}
if ("containsKey".equals(name) || "remove".equals(name) || "get".equals(name) || "containsValue".equals(name)) {
final PsiType type = checkMethod(method, CommonClassNames.JAVA_UTIL_MAP, new NullableFunction<PsiClass, PsiType>() {
@Override
public PsiType fun(@NotNull final PsiClass psiClass) {
return getTypeParameterValue(psiClass, containingClass, substitutor, name.equals("containsValue") ? 1 : 0);
}
});
if (type != null) return type;
}
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(containingClass.getProject());
if ("equals".equals(name)) {
final PsiType type = checkMethod(method, CommonClassNames.JAVA_LANG_OBJECT, new NullableFunction<PsiClass, PsiType>() {
@Override
public PsiType fun(final PsiClass psiClass) {
final PsiElement parent = argument.getParent().getParent();
if (parent instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression expression = (PsiMethodCallExpression)parent;
final PsiExpression qualifierExpression = expression.getMethodExpression().getQualifierExpression();
if (qualifierExpression != null) {
return qualifierExpression.getType();
}
final PsiClass aClass = PsiTreeUtil.getContextOfType(parent, PsiClass.class, true);
if (aClass != null) {
return factory.createType(aClass);
}
}
return null;
}
});
if (type != null) return type;
}
int argCount = Math.max(index + 1, args.length);
if ("assertEquals".equals(name) || "assertSame".equals(name) && method.getParameterList().getParametersCount() == argCount) {
if (argCount == 2 ||
argCount == 3 && method.getParameterList().getParameters()[0].getType().equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
int other = index == argCount - 1 ? index - 1 : index + 1;
if (args.length > other) {
ExpectedTypeInfo info = getEqualsType(args[other]);
if (info != null && parameterType.isAssignableFrom(info.getDefaultType())) {
return info.getDefaultType();
}
}
}
}
if ("Logger".equals(containingClass.getName()) || "Log".equals(containingClass.getName())) {
if (parameterType instanceof PsiClassType) {
PsiType typeArg = PsiUtil.substituteTypeParameter(parameterType, CommonClassNames.JAVA_LANG_CLASS, 0, true);
if (typeArg != null && TypeConversionUtil.erasure(typeArg).equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) {
PsiClass placeClass = PsiTreeUtil.getContextOfType(argument, PsiClass.class);
PsiClass classClass = ((PsiClassType)parameterType).resolve();
if (placeClass != null && classClass != null) {
return factory.createType(classClass, factory.createType(placeClass));
}
}
}
}
return parameterType;
}
private static PsiType getParameterType(@NotNull PsiParameter parameter, @NotNull PsiSubstitutor substitutor) {
PsiType type = parameter.getType();
LOG.assertTrue(type.isValid());
if (parameter.isVarArgs()) {
if (type instanceof PsiArrayType) {
type = ((PsiArrayType)type).getComponentType();
}
else {
LOG.error("Vararg parameter with non-array type. Class=" + parameter.getClass() + "; type=" + parameter.getType());
}
}
PsiType parameterType = substitutor.substitute(type);
if (parameterType instanceof PsiCapturedWildcardType) {
parameterType = ((PsiCapturedWildcardType)parameterType).getWildcard();
}
if (parameterType instanceof PsiWildcardType) {
final PsiType bound = ((PsiWildcardType)parameterType).getBound();
return bound != null ? bound : PsiType.getJavaLangObject(parameter.getManager(), GlobalSearchScope.allScope(parameter.getProject()));
}
return parameterType;
}
@Nullable
private static NullableComputable<String> getPropertyName(@NotNull final PsiVariable variable) {
return new NullableComputable<String>() {
@Override
public String compute() {
final String name = variable.getName();
if (name == null) return null;
JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(variable.getProject());
VariableKind variableKind = codeStyleManager.getVariableKind(variable);
return codeStyleManager.variableNameToPropertyName(name, variableKind);
}
};
}
@NotNull
private List<ExpectedTypeInfo> anyArrayType() {
PsiType objType = PsiType.getJavaLangObject(myExpr.getManager(), myExpr.getResolveScope()).createArrayType();
ExpectedTypeInfo info = createInfoImpl(objType, objType);
ExpectedTypeInfo info1 = createInfoImpl(PsiType.DOUBLE.createArrayType(), PsiType.INT.createArrayType());
PsiType booleanType = PsiType.BOOLEAN.createArrayType();
ExpectedTypeInfo info2 = createInfoImpl(booleanType, ExpectedTypeInfo.TYPE_STRICTLY, booleanType, TailType.NONE);
return Arrays.asList(info, info1, info2);
}
@NotNull
private ExpectedTypeInfo[] findClassesWithDeclaredMethod(@NotNull final PsiMethodCallExpression methodCallExpr, final boolean forCompletion) {
final PsiReferenceExpression reference = methodCallExpr.getMethodExpression();
if (reference.getQualifierExpression() instanceof PsiClassObjectAccessExpression) {
return ExpectedTypeInfo.EMPTY_ARRAY;
}
final PsiManager manager = methodCallExpr.getManager();
final JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject());
final PsiMethod[] methods = myClassProvider.findDeclaredMethods(reference.getManager(), reference.getReferenceName());
Set<ExpectedTypeInfo> types = new THashSet<ExpectedTypeInfo>();
for (PsiMethod method : methods) {
final PsiClass aClass = method.getContainingClass();
if (aClass == null || !facade.getResolveHelper().isAccessible(method, reference, aClass)) continue;
final PsiSubstitutor substitutor = ExpectedTypeUtil.inferSubstitutor(method, methodCallExpr, forCompletion);
final PsiClassType type =
substitutor == null ? facade.getElementFactory().createType(aClass) : facade.getElementFactory().createType(aClass, substitutor);
if (method.hasModifierProperty(PsiModifier.STATIC) ||
method.hasModifierProperty(PsiModifier.FINAL) ||
method.hasModifierProperty(PsiModifier.PRIVATE)) {
types.add(createInfoImpl(type, ExpectedTypeInfo.TYPE_STRICTLY, type, TailType.DOT));
} else if (method.findSuperMethods().length == 0) {
types.add(createInfoImpl(type, ExpectedTypeInfo.TYPE_OR_SUBTYPE, type, TailType.DOT));
}
}
return types.toArray(new ExpectedTypeInfo[types.size()]);
}
@NotNull
private ExpectedTypeInfo[] findClassesWithDeclaredField(@NotNull PsiReferenceExpression expression) {
final JavaPsiFacade facade = JavaPsiFacade.getInstance(expression.getProject());
PsiField[] fields = myClassProvider.findDeclaredFields(expression.getManager(), expression.getReferenceName());
List<ExpectedTypeInfo> types = new ArrayList<ExpectedTypeInfo>();
for (PsiField field : fields) {
final PsiClass aClass = field.getContainingClass();
if (aClass == null || !facade.getResolveHelper().isAccessible(field, expression, aClass)) continue;
final PsiType type = facade.getElementFactory().createType(aClass);
int kind = field.hasModifierProperty(PsiModifier.STATIC) ||
field.hasModifierProperty(PsiModifier.FINAL) ||
field.hasModifierProperty(PsiModifier.PRIVATE)
? ExpectedTypeInfo.TYPE_STRICTLY
: ExpectedTypeInfo.TYPE_OR_SUBTYPE;
ExpectedTypeInfo info = createInfoImpl(type, kind, type, TailType.DOT);
//Do not filter inheritors!
types.add(info);
}
return types.toArray(new ExpectedTypeInfo[types.size()]);
}
}
/**
* Finds fields and methods of specified name whenever corresponding reference has been encountered.
* By default searches in the global scope (see ourGlobalScopeClassProvider), but caller can provide its own algorithm e.g. to narrow search scope
*/
public interface ExpectedClassProvider {
PsiField[] findDeclaredFields(final PsiManager manager, String name);
PsiMethod[] findDeclaredMethods(final PsiManager manager, String name);
}
@NotNull
public static TailType getFinalCallParameterTailType(@NotNull PsiElement call, @Nullable PsiType returnType, @NotNull PsiMethod method) {
if (method.isConstructor() &&
call instanceof PsiMethodCallExpression && ((PsiMethodCallExpression)call).getMethodExpression() instanceof PsiSuperExpression) {
return TailTypes.CALL_RPARENTH_SEMICOLON;
}
final boolean chainable = !PsiType.VOID.equals(returnType) && returnType != null || method.isConstructor() && call instanceof PsiNewExpression;
final PsiElement parent = call.getParent();
final boolean statementContext = parent instanceof PsiExpressionStatement || parent instanceof PsiVariable ||
parent instanceof PsiCodeBlock;
if (parent instanceof PsiThrowStatement || statementContext && !chainable) {
return TailTypes.CALL_RPARENTH_SEMICOLON;
}
return TailTypes.CALL_RPARENTH;
}
}