blob: e57242f55cc790bbadb14ee57127d6e4f432cad1 [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 org.jetbrains.plugins.groovy.findUsages;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.util.*;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.NullableFunction;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils;
import org.jetbrains.plugins.groovy.gpp.GppTypeConverter;
import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner;
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.GroovyExpectedTypesProvider;
import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyResolveResultImpl;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
/**
* @author peter
*/
public class LiteralConstructorReference extends PsiReferenceBase.Poly<GrListOrMap> {
public LiteralConstructorReference(@NotNull GrListOrMap element) {
super(element, TextRange.from(0, 0), false);
}
@Override
public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
return getElement();
}
@Override
public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
return getElement();
}
@Nullable
public PsiClassType getConstructedClassType() {
return CachedValuesManager.getCachedValue(getElement(), new CachedValueProvider<PsiClassType>() {
@Nullable
@Override
public Result<PsiClassType> compute() {
return Result.create(inferConversionType(), PsiModificationTracker.MODIFICATION_COUNT);
}
});
}
@Nullable
private PsiClassType inferConversionType() {
GrListOrMap map = getElement();
final PsiClassType conversionType = getTargetConversionType(map);
if (conversionType == null) return null;
PsiType type = map.getType();
PsiType ownType = type instanceof PsiClassType ? ((PsiClassType)type).rawType() : type;
if (ownType != null && TypesUtil.isAssignableWithoutConversions(conversionType.rawType(), ownType, map)) return null;
final PsiClass resolved = conversionType.resolve();
if (resolved != null) {
if (InheritanceUtil.isInheritor(resolved, CommonClassNames.JAVA_UTIL_SET)) return null;
if (InheritanceUtil.isInheritor(resolved, CommonClassNames.JAVA_UTIL_LIST)) return null;
}
return conversionType;
}
@Nullable
public static PsiClassType getTargetConversionType(@NotNull final GrExpression expression) {
//todo hack
final PsiElement parent = PsiUtil.skipParentheses(expression.getParent(), true);
PsiType type = null;
if (parent instanceof GrSafeCastExpression) {
type = ((GrSafeCastExpression)parent).getType();
}
else if (parent instanceof GrTypeCastExpression) {
type = ((GrTypeCastExpression)parent).getType();
}
else if (parent instanceof GrAssignmentExpression &&
PsiTreeUtil.isAncestor(((GrAssignmentExpression)parent).getRValue(), expression, false)) {
final PsiElement lValue = PsiUtil.skipParentheses(((GrAssignmentExpression)parent).getLValue(), false);
if (lValue instanceof GrReferenceExpression) {
type = ((GrReferenceExpression)lValue).getNominalType();
}
}
else if (parent instanceof GrVariable) {
type = ((GrVariable)parent).getDeclaredType();
}
else if (parent instanceof GrArgumentList && GppTypeConverter.hasTypedContext(parent)) {
for (PsiType expected : GroovyExpectedTypesProvider.getDefaultExpectedTypes(expression)) {
expected = filterOutTrashTypes(expected);
if (expected != null) return (PsiClassType)expected;
}
}
else if (parent instanceof GrNamedArgument) { //possible default constructor named arg
for (PsiType expected : GroovyExpectedTypesProvider.getDefaultExpectedTypes(expression)) {
expected = filterOutTrashTypes(expected);
if (expected != null) return (PsiClassType)expected;
}
}
else {
final GrControlFlowOwner controlFlowOwner = ControlFlowUtils.findControlFlowOwner(expression);
if (controlFlowOwner instanceof GrOpenBlock && controlFlowOwner.getParent() instanceof GrMethod) {
if (ControlFlowUtils.isReturnValue(expression, controlFlowOwner)) {
type = ((GrMethod)controlFlowOwner.getParent()).getReturnType();
}
}
}
return filterOutTrashTypes(type);
}
@Nullable
private static PsiClassType filterOutTrashTypes(PsiType type) {
if (!(type instanceof PsiClassType)) return null;
if (type.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) return null;
if (type.equalsToText(CommonClassNames.JAVA_LANG_STRING)) return null;
if (TypesUtil.resolvesTo(type, CommonClassNames.JAVA_UTIL_MAP)) return null;
if (TypesUtil.resolvesTo(type, CommonClassNames.JAVA_UTIL_HASH_MAP)) return null;
if (TypesUtil.resolvesTo(type, CommonClassNames.JAVA_UTIL_LIST)) return null;
final PsiType erased = TypeConversionUtil.erasure(type);
if (erased == null || erased.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) return null;
return (PsiClassType)type;
}
@NotNull
public GrExpression[] getCallArguments() {
final GrListOrMap literal = getElement();
if (literal.isMap()) {
final GrNamedArgument argument = literal.findNamedArgument("super");
if (argument != null) {
final GrExpression expression = argument.getExpression();
if (expression instanceof GrListOrMap && !((GrListOrMap)expression).isMap()) {
return ((GrListOrMap)expression).getInitializers();
}
if (expression != null) {
return new GrExpression[]{expression};
}
return GrExpression.EMPTY_ARRAY;
}
else {
return new GrExpression[]{literal};
}
}
return literal.getInitializers();
}
@NotNull
private PsiType[] getCallArgumentTypes() {
final GrExpression[] arguments = getCallArguments();
return ContainerUtil.map2Array(arguments, PsiType.class, new NullableFunction<GrExpression, PsiType>() {
@Override
public PsiType fun(GrExpression grExpression) {
return grExpression.getType();
}
});
}
@NotNull
@Override
public GroovyResolveResult[] multiResolve(boolean incompleteCode) {
PsiClassType type = getConstructedClassType();
if (type == null) return GroovyResolveResult.EMPTY_ARRAY;
final PsiClassType.ClassResolveResult classResolveResult = type.resolveGenerics();
final GroovyResolveResult[] constructorCandidates = PsiUtil.getConstructorCandidates(type, getCallArgumentTypes(), getElement());
if (constructorCandidates.length == 0 && classResolveResult.getElement() != null) {
return new GroovyResolveResult[]{new GroovyResolveResultImpl(classResolveResult)};
}
return constructorCandidates;
}
@NotNull
@Override
public Object[] getVariants() {
return EMPTY_ARRAY;
}
}