search for literal constructors passed to vararg methods

use smart arg-parameter binding for calculating call arguments expected types
This commit is contained in:
peter
2010-07-30 13:00:06 +01:00
parent 8e63f416ca
commit cc40f9dfec
10 changed files with 160 additions and 79 deletions
@@ -796,10 +796,14 @@ public final class PsiUtil extends PsiUtilBase {
}
public static boolean hasDefaultConstructor(PsiClass clazz, boolean allowProtected) {
return hasDefaultConstructor(clazz, allowProtected, true);
}
public static boolean hasDefaultConstructor(PsiClass clazz, boolean allowProtected, boolean checkModifiers) {
final PsiMethod[] constructors = clazz.getConstructors();
if (constructors.length > 0) {
for (PsiMethod cls: constructors) {
if ((cls.hasModifierProperty(PsiModifier.PUBLIC) ||
if ((!checkModifiers || cls.hasModifierProperty(PsiModifier.PUBLIC) ||
allowProtected && cls.hasModifierProperty(PsiModifier.PROTECTED)) &&
cls.getParameterList().getParametersCount() == 0) {
return true;
@@ -807,7 +811,7 @@ public final class PsiUtil extends PsiUtilBase {
}
} else {
final PsiClass superClass = clazz.getSuperClass();
return superClass == null || hasDefaultConstructor(superClass, true);
return superClass == null || hasDefaultConstructor(superClass, true, true);
}
return false;
}
@@ -171,11 +171,18 @@ public class GroovyConstructorUsagesSearcher extends QueryExecutorBase<PsiRefere
final PsiParameter parameter = PsiTreeUtil.getParentOfType(element, PsiParameter.class);
if (parameter != null) {
final PsiMethod method = PsiTreeUtil.getParentOfType(parameter, PsiMethod.class);
if (method != null && Arrays.asList(method.getParameterList().getParameters()).contains(parameter)) {
final PsiType parameterType = parameter.getType();
if (parameterType instanceof PsiClassType) {
if (method.getManager().areElementsEquivalent(targetClass, ((PsiClassType)parameterType).resolve())) {
return method;
if (method != null) {
final PsiParameter[] parameters = method.getParameterList().getParameters();
final int idx = Arrays.asList(parameters).indexOf(parameter);
if (idx >= 0) {
PsiType parameterType = parameter.getType();
if (parameterType instanceof PsiArrayType && idx == parameters.length - 1) {
parameterType = ((PsiArrayType)parameterType).getComponentType();
}
if (parameterType instanceof PsiClassType) {
if (method.getManager().areElementsEquivalent(targetClass, ((PsiClassType)parameterType).resolve())) {
return method;
}
}
}
}
@@ -72,7 +72,7 @@ public class GppTypeConverter extends GrTypeConverter {
return true;
}
if (!isMethodCallConversion(context) && hasDefaultConstructor(lType)) {
if ((!isMethodCallConversion(context) || hasTypedContext(context)) && hasDefaultConstructor(lType)) {
return true;
}
}
@@ -89,7 +89,7 @@ public class GppTypeConverter extends GrTypeConverter {
private static boolean hasDefaultConstructor(PsiType type) {
final PsiClass psiClass = PsiUtil.resolveClassInType(type);
return psiClass != null && PsiUtil.hasDefaultConstructor(psiClass, true);
return psiClass != null && PsiUtil.hasDefaultConstructor(psiClass, true, false);
}
@@ -207,7 +207,7 @@ public class ConvertParameterToMapEntryIntention extends Intention {
call instanceof GrCallExpression ? ((GrCallExpression)call).getClosureArguments() : GrClosableBlock.EMPTY_ARRAY;
final GrClosureSignatureUtil.ArgInfo<PsiElement>[] argInfos =
GrClosureSignatureUtil.mapParametersToArguments(signature, argumentList, closureArguments, owner.getResolveScope());
GrClosureSignatureUtil.mapParametersToArguments(signature, argumentList, argumentList, closureArguments);
if (argInfos == null) continue;
final GrClosureSignatureUtil.ArgInfo<PsiElement> argInfo = argInfos[index];
@@ -15,7 +15,6 @@
*/
package org.jetbrains.plugins.groovy.lang.psi.api.types;
import com.intellij.psi.PsiParameter;
import com.intellij.psi.PsiType;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
@@ -24,7 +23,6 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpres
* @author Maxim.Medvedev
*/
public interface GrClosureParameter {
PsiParameter[] EMPTY_ARRAY = new PsiParameter[0];
@Nullable
PsiType getType();
@@ -16,11 +16,11 @@
package org.jetbrains.plugins.groovy.lang.psi.expectedTypes;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
@@ -38,6 +38,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrM
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrClosureSignatureUtil;
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
import java.util.*;
@@ -128,13 +129,9 @@ public class GroovyExpectedTypesProvider {
if (closureIndex >= 0) {
List<TypeConstraint> constraints = new ArrayList<TypeConstraint>();
for (GroovyResolveResult variant : ResolveUtil.getCallVariants(myExpression)) {
PsiParameter[] parameters = getCallParameters(variant);
if (parameters == null || parameters.length == 0) continue;
final int paramIndex = parameters.length - closureArgs.size() + closureIndex;
if (paramIndex >= 0) {
constraints.add(SubtypeConstraint.create(variant.getSubstitutor().substitute(parameters[paramIndex].getType())));
}
addConstraintsFromMap(constraints,
GrClosureSignatureUtil.mapArgumentsToParameters(variant, methodCall.getArgumentList(), methodCall, methodCall.getClosureArguments(),
true));
}
if (!constraints.isEmpty()) {
myResult = constraints.toArray(new TypeConstraint[constraints.size()]);
@@ -175,30 +172,24 @@ public class GroovyExpectedTypesProvider {
}
public void visitArgumentList(GrArgumentList list) {
int idx = list.getExpressionArgumentIndex(myExpression);
List<TypeConstraint> constraints = new ArrayList<TypeConstraint>();
for (GroovyResolveResult variant : ResolveUtil.getCallVariants(list)) {
PsiParameter[] parameters = getCallParameters(variant);
if (parameters == null || parameters.length <= idx) continue;
PsiType parameterType = variant.getSubstitutor().substitute(parameters[idx].getType());
constraints.add(SubtypeConstraint.create(parameterType));
addConstraintsFromMap(constraints,
GrClosureSignatureUtil.mapArgumentsToParameters(variant, list, list, GrClosableBlock.EMPTY_ARRAY, true));
}
if (!constraints.isEmpty()) {
myResult = constraints.toArray(new TypeConstraint[constraints.size()]);
}
}
@Nullable
private static PsiParameter[] getCallParameters(GroovyResolveResult variant) {
PsiElement element = variant.getElement();
if (element instanceof GrParametersOwner) {
return ((GrParametersOwner)element).getParameters();
private void addConstraintsFromMap(List<TypeConstraint> constraints,
Map<GrExpression, Pair<PsiParameter, PsiType>> map) {
if (map != null) {
final Pair<PsiParameter, PsiType> pair = map.get(myExpression);
if (pair != null) {
constraints.add(SubtypeConstraint.create(pair.second));
}
}
else if (element instanceof PsiMethod) {
return ((PsiMethod)element).getParameterList().getParameters();
}
return null;
}
public void visitAssignmentExpression(GrAssignmentExpression expression) {
@@ -142,7 +142,7 @@ public class GrClosureSignatureImpl implements GrClosureSignature {
private class DerivedClosureSignature extends GrClosureSignatureImpl {
DerivedClosureSignature() {
super(GrClosureParameter.EMPTY_ARRAY, null);
super(PsiParameter.EMPTY_ARRAY, null);
}
DerivedClosureSignature(@NotNull GrClosureParameter[] params, @Nullable PsiType returnType, boolean isVarArgs) {
@@ -17,7 +17,6 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.types;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.MethodSignature;
import com.intellij.psi.util.MethodSignatureUtil;
@@ -40,10 +39,7 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.*;
/**
* @author Maxim.Medvedev
@@ -139,7 +135,8 @@ public class GrClosureSignatureUtil {
return false;
}
public static ArgInfo<PsiType>[] mapArgTypesToParameters(GrClosureSignature signature,
@Nullable
public static ArgInfo<PsiType>[] mapArgTypesToParameters(@NotNull GrClosureSignature signature,
PsiType[] args,
GroovyPsiElement context,
boolean partial) {
@@ -147,10 +144,10 @@ public class GrClosureSignatureUtil {
}
@Nullable
private static <Arg> ArgInfo<Arg>[] mapParametersToArguments(GrClosureSignature signature,
private static <Arg> ArgInfo<Arg>[] mapParametersToArguments(@NotNull GrClosureSignature signature,
Arg[] args,
Function<Arg, PsiType> typeComputer,
GroovyPsiElement context, boolean partial) {
@NotNull GroovyPsiElement context, boolean partial) {
GrClosureParameter[] params = signature.getParameters();
if (args.length > params.length && !signature.isVarargs()) return null;
int optional = getOptionalParamCount(signature, false);
@@ -158,11 +155,11 @@ public class GrClosureSignatureUtil {
if (signature.isVarargs()) notOptional--;
if (notOptional > args.length && !partial) return null;
final ArgInfo<Arg>[] map = mapSimple(params, args, typeComputer, context);
final ArgInfo<Arg>[] map = mapSimple(params, args, typeComputer, context, partial);
if (map != null) return map;
if (signature.isVarargs()) {
return new ParameterMapperForVararg<Arg>(context, params, args, typeComputer).isApplicable();
return new ParameterMapperForVararg<Arg>(context, params, args, typeComputer).isApplicable(partial);
}
return null;
}
@@ -171,7 +168,7 @@ public class GrClosureSignatureUtil {
private static <Arg> ArgInfo<Arg>[] mapSimple(GrClosureParameter[] params,
Arg[] args,
Function<Arg, PsiType> typeComputer,
GroovyPsiElement context) {
GroovyPsiElement context, boolean incompleteCode) {
ArgInfo<Arg>[] map = new ArgInfo[params.length];
int optional = getOptionalParamCount(params, false);
int notOptional = params.length - optional;
@@ -183,7 +180,7 @@ public class GrClosureSignatureUtil {
}
if (cur == params.length) return null;
if (params[cur].isOptional()) optionalArgs--;
if (!TypesUtil.isAssignableByMethodCallConversion(params[cur].getType(), typeComputer.fun(args[i]), context)) return null;
if (!isAssibnableByConversion(params[cur].getType(), typeComputer.fun(args[i]), context, incompleteCode)) return null;
map[cur] = new ArgInfo<Arg>(args[i]);
}
for (int i = 0; i < map.length; i++) {
@@ -192,6 +189,16 @@ public class GrClosureSignatureUtil {
return map;
}
private static <Arg> boolean isAssibnableByConversion(PsiType paramType,
PsiType argType,
GroovyPsiElement context,
boolean incompleteCode) {
if (argType == null && incompleteCode) {
return true;
}
return TypesUtil.isAssignableByMethodCallConversion(paramType, argType, context);
}
private static class ParameterMapperForVararg<Arg> {
private GroovyPsiElement context;
GrClosureParameter[] params;
@@ -218,12 +225,12 @@ public class GrClosureSignatureUtil {
}
@Nullable
public ArgInfo<Arg>[] isApplicable() {
public ArgInfo<Arg>[] isApplicable(boolean incompleteCode) {
int notOptionals = 0;
for (int i = 0; i < paramLength; i++) {
if (!params[i].isOptional()) notOptionals++;
}
if (isApplicableInternal(0, 0, false, notOptionals)) {
if (isApplicableInternal(0, 0, false, notOptionals, incompleteCode)) {
for (int i = 0; i < map.length; i++) {
if (map[i] == null) map[i] = new ArgInfo<Arg>(false);
}
@@ -234,7 +241,7 @@ public class GrClosureSignatureUtil {
}
}
private boolean isApplicableInternal(int curParam, int curArg, boolean skipOptionals, int notOptional) {
private boolean isApplicableInternal(int curParam, int curArg, boolean skipOptionals, int notOptional, boolean incompleteCode) {
int startParam = curParam;
if (notOptional > args.length - curArg) return false;
if (notOptional == args.length - curArg) skipOptionals = true;
@@ -247,15 +254,15 @@ public class GrClosureSignatureUtil {
if (curParam == paramLength) break;
if (params[curParam].isOptional()) {
if (TypesUtil.isAssignableByMethodCallConversion(params[curParam].getType(), types[curArg], context) &&
isApplicableInternal(curParam + 1, curArg + 1, false, notOptional)) {
if (isAssibnableByConversion(params[curParam].getType(), types[curArg], context, incompleteCode) &&
isApplicableInternal(curParam + 1, curArg + 1, false, notOptional, incompleteCode)) {
map[curParam] = new ArgInfo<Arg>(args[curArg]);
return true;
}
skipOptionals = true;
}
else {
if (!TypesUtil.isAssignableByMethodCallConversion(params[curParam].getType(), types[curArg], context)) {
if (!isAssibnableByConversion(params[curParam].getType(), types[curArg], context, incompleteCode)) {
for (int i = startParam; i < curParam; i++) map[i] = null;
return false;
}
@@ -268,7 +275,7 @@ public class GrClosureSignatureUtil {
List<Arg> varargs = new ArrayList<Arg>();
for (; curArg < args.length; curArg++) {
if (!TypesUtil.isAssignableByMethodCallConversion(vararg, types[curArg], context)) {
if (!isAssibnableByConversion(vararg, types[curArg], context, incompleteCode)) {
for (int i = startParam; i < curParam; i++) map[i] = null;
return false;
}
@@ -326,9 +333,8 @@ public class GrClosureSignatureUtil {
*/
@Nullable
public static ArgInfo<PsiElement>[] mapParametersToArguments(@NotNull GrClosureSignature signature,
@NotNull GrArgumentList list,
GlobalSearchScope scope) {
return mapParametersToArguments(signature, list, GrClosableBlock.EMPTY_ARRAY, scope);
@NotNull GrArgumentList list) {
return mapParametersToArguments(signature, list, list, GrClosableBlock.EMPTY_ARRAY);
}
private static class InnerArg {
@@ -342,29 +348,83 @@ public class GrClosureSignatureUtil {
}
@Nullable
public static ArgInfo<PsiElement>[] mapParametersToArguments(@NotNull GrClosureSignature signature,
@NotNull GrArgumentList list,
@NotNull GrClosableBlock[] closureArguments,
GlobalSearchScope scope) {
final GrNamedArgument[] namedArgs = list.getNamedArguments();
boolean hasNamedArgs = namedArgs.length > 0;
GrClosureParameter[] params = signature.getParameters();
public static Map<GrExpression, Pair<PsiParameter, PsiType>> mapArgumentsToParameters(@NotNull GroovyResolveResult resolveResult,
@Nullable GrArgumentList list,
@NotNull GroovyPsiElement context,
@NotNull GrClosableBlock[] closureArguments,
final boolean partial) {
final GrClosureSignature signature;
final PsiParameter[] parameters;
final PsiElement element = resolveResult.getElement();
final PsiSubstitutor substitutor = resolveResult.getSubstitutor();
if (element instanceof PsiMethod) {
signature = createSignature((PsiMethod)element, substitutor);
parameters = ((PsiMethod)element).getParameterList().getParameters();
} else if (element instanceof GrClosableBlock) {
signature = createSignature((GrClosableBlock)element);
parameters = ((GrClosableBlock)element).getAllParameters();
} else {
return null;
}
List<InnerArg> innerArgs = new ArrayList<InnerArg>();
final ArgInfo<PsiElement>[] argInfos = mapParametersToArguments(signature, list, context, closureArguments, partial);
if (argInfos == null) {
return null;
}
if (hasNamedArgs) {
if (params.length == 0) return null;
PsiType type = params[0].getType();
if (InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_UTIL_MAP)) {
innerArgs.add(new InnerArg(PsiUtil.createMapType(scope), namedArgs));
}
else {
return null;
final HashMap<GrExpression, Pair<PsiParameter, PsiType>> result = new HashMap<GrExpression, Pair<PsiParameter, PsiType>>();
for (int i = 0; i < argInfos.length; i++) {
ArgInfo<PsiElement> info = argInfos[i];
for (PsiElement arg : info.args) {
final GrExpression expression = (GrExpression)arg;
PsiType type = parameters[i].getType();
if (info.isMultiArg && type instanceof PsiArrayType) {
type = ((PsiArrayType)type).getComponentType();
}
result.put(expression, Pair.create(parameters[i], substitutor.substitute(type)));
}
}
for (GrExpression expression : list.getExpressionArguments()) {
innerArgs.add(new InnerArg(expression.getType(), expression));
return result;
}
@Nullable
public static ArgInfo<PsiElement>[] mapParametersToArguments(@NotNull GrClosureSignature signature,
@Nullable GrArgumentList list,
@NotNull GroovyPsiElement context,
@NotNull GrClosableBlock[] closureArguments) {
return mapParametersToArguments(signature, list, context, closureArguments, false);
}
@Nullable
public static ArgInfo<PsiElement>[] mapParametersToArguments(@NotNull GrClosureSignature signature,
@Nullable GrArgumentList list,
@NotNull GroovyPsiElement context,
@NotNull GrClosableBlock[] closureArguments, final boolean partial) {
List<InnerArg> innerArgs = new ArrayList<InnerArg>();
boolean hasNamedArgs = false;
if (list != null) {
final GrNamedArgument[] namedArgs = list.getNamedArguments();
hasNamedArgs = namedArgs.length > 0;
GrClosureParameter[] params = signature.getParameters();
if (hasNamedArgs) {
if (params.length == 0) return null;
PsiType type = params[0].getType();
if (InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_UTIL_MAP)) {
innerArgs.add(new InnerArg(PsiUtil.createMapType(list.getResolveScope()), namedArgs));
}
else {
return null;
}
}
for (GrExpression expression : list.getExpressionArguments()) {
innerArgs.add(new InnerArg(expression.getType(), expression));
}
}
for (GrClosableBlock closureArgument : closureArguments) {
@@ -377,7 +437,7 @@ public class GrClosureSignatureUtil {
public PsiType fun(InnerArg o) {
return o.type;
}
}, list, false);
}, context, partial);
if (innerMap == null) return null;
ArgInfo<PsiElement>[] map = new ArgInfo[innerMap.length];
@@ -17,7 +17,6 @@ package org.jetbrains.plugins.groovy.refactoring.changeSignature;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiSubstitutorImpl;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.MethodSignature;
import com.intellij.psi.util.MethodSignatureUtil;
@@ -87,7 +86,7 @@ public class GrMethodCallUsageInfo extends UsageInfo implements PossiblyIncorrec
}
else {
myMapToArguments =
GrClosureSignatureUtil.mapParametersToArguments(signature, list, GlobalSearchScope.allScope(getProject()));
GrClosureSignatureUtil.mapParametersToArguments(signature, list);
}
}
@@ -137,5 +137,27 @@ class LiteralConstructorUsagesTest extends LightCodeInsightFixtureTestCase {
assertEquals(1, ReferencesSearch.search(foo.constructors[0]).findAll().size())
assertEquals(1, ReferencesSearch.search(foo.constructors[1]).findAll().size())
}
public void testGppCallVarargs() throws Exception {
def foo = myFixture.addClass("""
class Foo {
Foo() {}
Foo(int a) {}
}
""")
myFixture.addClass("""
class Bar {
static void foo(Foo f1, Foo f2) {}
static void doo(int a, Foo f1, Foo f2) {}
}
""")
myFixture.addFileToProject "a.gpp", """
Bar.foo([:], [super:2])
Bar.doo 3, [:], [super:2]
"""
assertEquals(2, ReferencesSearch.search(foo.constructors[0]).findAll().size())
assertEquals(2, ReferencesSearch.search(foo.constructors[1]).findAll().size())
}
}