| /* |
| * 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.lang.psi.impl.signatures; |
| |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.openapi.util.Trinity; |
| import com.intellij.pom.java.LanguageLevel; |
| 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; |
| import com.intellij.psi.util.TypeConversionUtil; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.util.Function; |
| import com.intellij.util.FunctionUtil; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.util.containers.MultiMap; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; |
| import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; |
| import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrClosureSignature; |
| import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrMultiSignature; |
| import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrRecursiveSignatureVisitor; |
| import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrSignature; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCall; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrNewExpression; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrReflectedMethod; |
| import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureParameter; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.GrMapType; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.LazyFqnClassType; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; |
| import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; |
| |
| import java.util.*; |
| |
| /** |
| * @author Maxim.Medvedev |
| */ |
| public class GrClosureSignatureUtil { |
| private static final Logger LOG = Logger.getInstance(GrClosureSignatureUtil.class); |
| |
| private GrClosureSignatureUtil() { |
| } |
| |
| public static GrMultiSignature createMultiSignature(GrClosureSignature[] signatures) { |
| return new GrMultiSignatureImpl(signatures); |
| } |
| |
| @Nullable |
| public static GrClosureSignature createSignature(GrCall call) { |
| if (call instanceof GrMethodCall) { |
| final GrExpression invokedExpression = ((GrMethodCall)call).getInvokedExpression(); |
| final PsiType type = invokedExpression.getType(); |
| if (type instanceof GrClosureType) { |
| final GrSignature signature = ((GrClosureType)type).getSignature(); |
| final Trinity<GrClosureSignature, ArgInfo<PsiType>[], ApplicabilityResult> trinity = |
| getApplicableSignature(signature, PsiUtil.getArgumentTypes(invokedExpression, true), call); |
| if (trinity != null) { |
| return trinity.first; |
| } |
| return null; |
| } |
| } |
| |
| final GroovyResolveResult resolveResult = call.advancedResolve(); |
| final PsiElement element = resolveResult.getElement(); |
| if (element instanceof PsiMethod) { |
| return createSignature((PsiMethod)element, resolveResult.getSubstitutor()); |
| } |
| |
| return null; |
| } |
| |
| public static GrClosureSignature createSignature(MethodSignature signature) { |
| final PsiType[] types = signature.getParameterTypes(); |
| GrClosureParameter[] parameters = new GrClosureParameter[types.length]; |
| ContainerUtil.map(types, new Function<PsiType, GrClosureParameter>() { |
| @Override |
| public GrClosureParameter fun(PsiType type) { |
| return new GrImmediateClosureParameterImpl(type, null, false, null); |
| } |
| }, parameters); |
| return new GrImmediateClosureSignatureImpl(parameters, null, false, false); |
| } |
| |
| |
| @NotNull |
| public static GrClosureSignature createSignature(final GrClosableBlock block) { |
| return new GrClosableSignatureImpl(block); |
| } |
| |
| public static GrClosureSignature createSignature(final PsiMethod method, PsiSubstitutor substitutor) { |
| return new GrMethodSignatureImpl(method, substitutor); |
| } |
| |
| public static GrClosureSignature removeParam(final GrClosureSignature signature, int i) { |
| final GrClosureParameter[] newParams = ArrayUtil.remove(signature.getParameters(), i); |
| return new GrClosureSignatureWithNewParameters(signature, newParams); |
| } |
| |
| public static GrClosureSignature createSignatureWithErasedParameterTypes(final PsiMethod method) { |
| return new GrMethodSignatureWithErasedTypes(method); |
| } |
| |
| @NotNull |
| public static GrClosureSignature createSignatureWithErasedParameterTypes(final GrClosableBlock closure) { |
| return new GrClosableSignatureWithErasedParameters(closure); |
| } |
| |
| @NotNull |
| public static GrClosureSignature rawSignature(@NotNull final GrClosureSignature signature) { |
| final GrClosureParameter[] params = signature.getParameters(); |
| |
| final GrClosureParameter[] closureParams = ContainerUtil.map(params, new Function<GrClosureParameter, GrClosureParameter>() { |
| @Override |
| public GrClosureParameter fun(GrClosureParameter parameter) { |
| PsiType type = TypeConversionUtil.erasure(parameter.getType()); |
| return new GrImmediateClosureParameterImpl(type, parameter.getName(), parameter.isOptional(), parameter.getDefaultInitializer()); |
| } |
| }, new GrClosureParameter[params.length]); |
| |
| return new GrClosureSignatureWithNewParameters(signature, closureParams); |
| } |
| |
| |
| public static GrClosureSignature createSignature(PsiParameter[] parameters, @Nullable PsiType returnType) { |
| return new GrImmediateClosureSignatureImpl(parameters, returnType); |
| } |
| |
| |
| @Nullable |
| public static PsiType getReturnType(@NotNull final GrSignature signature, @NotNull GrMethodCall expr) { |
| return getReturnType(signature, PsiUtil.getArgumentTypes(expr.getInvokedExpression(), true), expr); |
| } |
| |
| @Nullable |
| public static PsiType getReturnType(@NotNull final GrSignature signature, @Nullable PsiType[] args, @NotNull GroovyPsiElement context) { |
| if (signature instanceof GrClosureSignature) return ((GrClosureSignature)signature).getReturnType(); |
| |
| if (args == null) { |
| return TypesUtil.getLeastUpperBoundNullable(new Iterable<PsiType>() { |
| |
| @Override |
| public Iterator<PsiType> iterator() { |
| return new Iterator<PsiType>() { |
| private final Iterator<GrClosureSignature> it = Arrays.asList(((GrMultiSignature)signature).getAllSignatures()).iterator(); |
| |
| @Override |
| public boolean hasNext() { |
| return it.hasNext(); |
| } |
| |
| @Override |
| public PsiType next() { |
| return it.next().getReturnType(); |
| } |
| |
| @Override |
| public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| }; |
| } |
| }, context.getManager()); |
| } |
| |
| final List<Trinity<GrClosureSignature, ArgInfo<PsiType>[], ApplicabilityResult>> results = |
| getSignatureApplicabilities(signature, args, context); |
| |
| if (results.size() == 1) return results.get(0).first.getReturnType(); |
| |
| return TypesUtil.getLeastUpperBoundNullable(new Iterable<PsiType>() { |
| @Override |
| public Iterator<PsiType> iterator() { |
| return new Iterator<PsiType>() { |
| private final Iterator<Trinity<GrClosureSignature, ArgInfo<PsiType>[], ApplicabilityResult>> myIterator = results.iterator(); |
| |
| @Override |
| public boolean hasNext() { |
| return myIterator.hasNext(); |
| } |
| |
| @Nullable |
| @Override |
| public PsiType next() { |
| return myIterator.next().first.getReturnType(); |
| } |
| |
| @Override |
| public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| }; |
| } |
| }, context.getManager()); |
| } |
| |
| public static boolean isSignatureApplicable(@NotNull GrSignature signature, @NotNull PsiType[] args, @NotNull PsiElement context) { |
| return isSignatureApplicableConcrete(signature, args, context) != ApplicabilityResult.inapplicable; |
| } |
| |
| public static ApplicabilityResult isSignatureApplicableConcrete(@NotNull GrSignature signature, @NotNull final PsiType[] args, @NotNull final PsiElement context) { |
| final List<Trinity<GrClosureSignature, ArgInfo<PsiType>[], ApplicabilityResult>> results = |
| getSignatureApplicabilities(signature, args, context); |
| |
| if (results.isEmpty()) return ApplicabilityResult.inapplicable; |
| else if (results.size()==1) return results.get(0).third; |
| else return ApplicabilityResult.ambiguous; |
| } |
| |
| @Nullable |
| public static Trinity<GrClosureSignature, ArgInfo<PsiType>[], ApplicabilityResult> getApplicableSignature(@NotNull GrSignature signature, |
| @Nullable final PsiType[] args, |
| @NotNull final GroovyPsiElement context) { |
| if (args == null) return null; |
| final List<Trinity<GrClosureSignature, ArgInfo<PsiType>[], ApplicabilityResult>> results = getSignatureApplicabilities(signature, args, context); |
| |
| if (results.size() == 1) return results.get(0); |
| else return null; |
| } |
| |
| private static List<Trinity<GrClosureSignature, ArgInfo<PsiType>[], ApplicabilityResult>> getSignatureApplicabilities(@NotNull GrSignature signature, |
| @NotNull final PsiType[] args, |
| @NotNull final PsiElement context) { |
| final List<Trinity<GrClosureSignature, ArgInfo<PsiType>[], ApplicabilityResult>> results = |
| new ArrayList<Trinity<GrClosureSignature, GrClosureSignatureUtil.ArgInfo<PsiType>[], ApplicabilityResult>>(); |
| signature.accept(new GrRecursiveSignatureVisitor() { |
| @Override |
| public void visitClosureSignature(GrClosureSignature signature) { |
| ArgInfo<PsiType>[] map = mapArgTypesToParameters(signature, args, context, false); |
| if (map != null) { |
| results.add(new Trinity<GrClosureSignature, ArgInfo<PsiType>[], ApplicabilityResult>(signature, map, isSignatureApplicableInner(map, signature))); |
| return; |
| } |
| |
| // check for the case foo([1, 2, 3]) if foo(int, int, int) |
| if (args.length == 1 && PsiUtil.isInMethodCallContext(context)) { |
| final GrClosureParameter[] parameters = signature.getParameters(); |
| if (parameters.length == 1 && parameters[0].getType() instanceof PsiArrayType) { |
| return; |
| } |
| PsiType arg = args[0]; |
| if (arg instanceof GrTupleType) { |
| PsiType[] _args = ((GrTupleType)arg).getComponentTypes(); |
| map = mapArgTypesToParameters(signature, _args, context, false); |
| if (map != null) { |
| results.add(new Trinity<GrClosureSignature, ArgInfo<PsiType>[], ApplicabilityResult>(signature, map, |
| isSignatureApplicableInner(map, |
| signature))); |
| } |
| } |
| } |
| } |
| }); |
| return results; |
| } |
| |
| private static ApplicabilityResult isSignatureApplicableInner(@NotNull ArgInfo<PsiType>[] infos, @NotNull GrClosureSignature signature) { |
| GrClosureParameter[] parameters = signature.getParameters(); |
| for (int i = 0; i < infos.length; i++) { |
| ArgInfo<PsiType> info = infos[i]; |
| if (info.args.size() != 1 || info.isMultiArg) continue; |
| PsiType type = info.args.get(0); |
| if (type != null) continue; |
| |
| PsiType pType = parameters[i].getType(); |
| if (pType != null && !pType.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) { |
| return ApplicabilityResult.canBeApplicable; |
| } |
| } |
| return ApplicabilityResult.applicable; |
| } |
| |
| static GrSignature curryImpl(GrClosureSignature original, PsiType[] args, int position, PsiElement context) { |
| GrClosureParameter[] params = original.getParameters(); |
| |
| List<GrClosureParameter> newParams = new ArrayList<GrClosureParameter>(params.length); |
| List<GrClosureParameter> opts = new ArrayList<GrClosureParameter>(params.length); |
| List<Integer> optInds = new ArrayList<Integer>(params.length); |
| |
| if (position == -1) { |
| position = params.length - args.length; |
| } |
| |
| if (position < 0 || position >= params.length) return GrMultiSignature.EMPTY_SIGNATURE; |
| |
| for (int i = 0; i < params.length; i++) { |
| if (params[i].isOptional()) { |
| opts.add(params[i]); |
| optInds.add(i); |
| } |
| else { |
| newParams.add(params[i]); |
| } |
| } |
| |
| final PsiType rtype = original.getReturnType(); |
| final ArrayList<GrClosureSignature> result = new ArrayList<GrClosureSignature>(); |
| checkAndAddSignature(result, args, position, newParams, rtype, context); |
| |
| for (int i = 0; i < opts.size(); i++) { |
| newParams.add(optInds.get(i), opts.get(i)); |
| checkAndAddSignature(result, args, position, newParams, rtype, context); |
| } |
| |
| if (result.size() == 1) { |
| return result.get(0); |
| } |
| else { |
| return new GrMultiSignatureImpl(result.toArray(new GrClosureSignature[result.size()])); |
| } |
| } |
| |
| public static boolean isVarArgsImpl(@NotNull GrClosureParameter[] parameters) { |
| return parameters.length > 0 && parameters[parameters.length - 1].getType() instanceof PsiArrayType; |
| } |
| |
| public enum ApplicabilityResult { |
| applicable, inapplicable, canBeApplicable, ambiguous; |
| |
| public static boolean isApplicable(ApplicabilityResult r) { |
| return r != inapplicable && r!= ambiguous; |
| } |
| } |
| |
| @Nullable |
| public static ArgInfo<PsiType>[] mapArgTypesToParameters(@NotNull GrClosureSignature signature, |
| @NotNull PsiType[] args, |
| @NotNull PsiElement context, |
| boolean partial) { |
| return mapParametersToArguments(signature, args, FunctionUtil.<PsiType>id(), context, partial); |
| } |
| |
| private static class ArgWrapper<Arg> { |
| PsiType type; |
| @Nullable Arg arg; |
| |
| private ArgWrapper(PsiType type, @Nullable Arg arg) { |
| this.type = type; |
| this.arg = arg; |
| } |
| } |
| |
| private static <Arg> Function<ArgWrapper<Arg>, PsiType> ARG_WRAPPER_COMPUTER() { |
| return new Function<ArgWrapper<Arg>, PsiType>() { |
| @Override |
| public PsiType fun(ArgWrapper<Arg> argWrapper) { |
| return argWrapper.type; |
| } |
| }; |
| } |
| |
| @Nullable |
| private static <Arg> ArgInfo<Arg>[] mapParametersToArguments(@NotNull GrClosureSignature signature, |
| @NotNull Arg[] args, |
| @NotNull Function<Arg, PsiType> typeComputer, |
| @NotNull PsiElement context, |
| boolean partial) { |
| LOG.assertTrue(signature.isValid(), signature.getClass()); |
| |
| if (checkForOnlyMapParam(signature, args.length)) return ArgInfo.empty_array(); |
| GrClosureParameter[] params = signature.getParameters(); |
| if (args.length > params.length && !signature.isVarargs() && !partial) return null; |
| int optional = getOptionalParamCount(signature, false); |
| int notOptional = params.length - optional; |
| if (signature.isVarargs()) notOptional--; |
| if (notOptional > args.length && !partial) return null; |
| |
| final ArgInfo<Arg>[] map = mapSimple(params, args, typeComputer, context, false); |
| if (map != null) return map; |
| |
| if (signature.isVarargs()) { |
| return new ParameterMapperForVararg<Arg>(context, params, args, typeComputer).isApplicable(); |
| } |
| |
| if (!partial) return null; |
| |
| return mapSimple(params, args, typeComputer, context, true); |
| } |
| |
| private static boolean checkForOnlyMapParam(@NotNull GrClosureSignature signature, final int argCount) { |
| if (argCount > 0 || signature.isCurried()) return false; |
| final GrClosureParameter[] parameters = signature.getParameters(); |
| if (parameters.length != 1) return false; |
| final PsiType type = parameters[0].getType(); |
| return InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_UTIL_MAP); |
| } |
| |
| @Nullable |
| private static <Arg> ArgInfo<Arg>[] mapSimple(@NotNull GrClosureParameter[] params, |
| @NotNull Arg[] args, |
| @NotNull Function<Arg, PsiType> typeComputer, |
| @NotNull PsiElement context, |
| boolean partial) { |
| if (args.length > params.length && !partial) return null; |
| |
| ArgInfo<Arg>[] map = new ArgInfo[params.length]; |
| int optional = getOptionalParamCount(params, false); |
| int notOptional = params.length - optional; |
| int optionalArgs = args.length - notOptional; |
| |
| if (notOptional > args.length && !partial) return null; |
| |
| int cur = 0; |
| for (int i = 0; i < args.length; i++, cur++) { |
| while (optionalArgs == 0 && cur < params.length && params[cur].isOptional()) { |
| cur++; |
| } |
| if (cur == params.length) return partial ? map : null; |
| if (params[cur].isOptional()) optionalArgs--; |
| final PsiType type = typeComputer.fun(args[i]); |
| if (!isAssignableByConversion(params[cur].getType(), type, context)) return partial ? map : null; |
| map[cur] = new ArgInfo<Arg>(args[i], type); |
| } |
| for (int i = 0; i < map.length; i++) { |
| if (map[i] == null) map[i] = new ArgInfo<Arg>(Collections.<Arg>emptyList(), false, null); |
| } |
| return map; |
| } |
| |
| private static boolean isAssignableByConversion(@Nullable PsiType paramType, @Nullable PsiType argType, @NotNull PsiElement context) { |
| if (argType == null || paramType == null) { |
| return true; |
| } |
| if (TypesUtil.isAssignableByMethodCallConversion(paramType, argType, context)) { |
| return true; |
| } |
| |
| final PsiType lType = TypesUtil.rawSecondGeneric(paramType, context.getProject()); |
| final PsiType rType = TypesUtil.rawSecondGeneric(argType, context.getProject()); |
| if (lType == null && rType == null) return false; |
| |
| return TypesUtil.isAssignableByMethodCallConversion(lType != null ? lType : paramType, rType != null ? rType : argType, context); |
| } |
| |
| public static void checkAndAddSignature(List<GrClosureSignature> list, |
| PsiType[] args, |
| int position, |
| List<GrClosureParameter> params, |
| PsiType returnType, |
| @NotNull PsiElement context) { |
| final int last = position + args.length; |
| if (last > params.size()) return; |
| |
| for (int i = position; i < last; i++) { |
| final GrClosureParameter p = params.get(i); |
| final PsiType type = p.getType(); |
| if (!isAssignableByConversion(type, args[i - position], context)) return; |
| } |
| GrClosureParameter[] _p = new GrClosureParameter[params.size() - args.length]; |
| int j = 0; |
| for (int i = 0; i < position; i++) { |
| _p[j++] = params.get(i); |
| } |
| for (int i = position + args.length; i < params.size(); i++) { |
| _p[j++] = params.get(i); |
| } |
| |
| list.add(new GrImmediateClosureSignatureImpl(_p, returnType, _p.length > 0 && _p[_p.length - 1].getType() instanceof PsiArrayType, true)); |
| } |
| |
| |
| @Nullable |
| public static GrClosureSignature createSignature(GroovyResolveResult resolveResult) { |
| final PsiElement resolved = resolveResult.getElement(); |
| if (!(resolved instanceof PsiMethod)) return null; |
| final PsiSubstitutor substitutor = resolveResult.getSubstitutor(); |
| return createSignature((PsiMethod)resolved, substitutor); |
| } |
| |
| private static class ParameterMapperForVararg<Arg> { |
| private final PsiElement context; |
| private final GrClosureParameter[] params; |
| private final Arg[] args; |
| private final PsiType[] types; |
| private final PsiType vararg; |
| private final int paramLength; |
| private final ArgInfo<Arg>[] map; |
| |
| private ParameterMapperForVararg(PsiElement context, |
| GrClosureParameter[] params, |
| Arg[] args, |
| Function<Arg, PsiType> typeComputer) { |
| this.context = context; |
| this.params = params; |
| this.args = args; |
| this.types = PsiType.createArray(args.length); |
| for (int i = 0; i < args.length; i++) { |
| types[i] = typeComputer.fun(args[i]); |
| } |
| paramLength = params.length - 1; |
| vararg = ((PsiArrayType)params[paramLength].getType()).getComponentType(); |
| map = new ArgInfo[params.length]; |
| } |
| |
| @Nullable |
| private ArgInfo<Arg>[] isApplicable() { |
| int notOptionals = 0; |
| for (int i = 0; i < paramLength; i++) { |
| if (!params[i].isOptional()) notOptionals++; |
| } |
| if (isApplicableInternal(0, 0, false, notOptionals)) { |
| for (int i = 0; i < map.length; i++) { |
| if (map[i] == null) map[i] = new ArgInfo<Arg>(false, null); |
| } |
| return map; |
| } |
| else { |
| return null; |
| } |
| } |
| |
| private boolean isApplicableInternal(int curParam, int curArg, boolean skipOptionals, int notOptional) { |
| int startParam = curParam; |
| if (notOptional > args.length - curArg) return false; |
| if (notOptional == args.length - curArg) skipOptionals = true; |
| |
| while (curArg < args.length) { |
| if (skipOptionals) { |
| while (curParam < paramLength && params[curParam].isOptional()) curParam++; |
| } |
| |
| if (curParam == paramLength) break; |
| |
| if (params[curParam].isOptional()) { |
| if (isAssignableByConversion(params[curParam].getType(), types[curArg], context) && |
| isApplicableInternal(curParam + 1, curArg + 1, false, notOptional)) { |
| map[curParam] = new ArgInfo<Arg>(args[curArg], types[curArg]); |
| return true; |
| } |
| skipOptionals = true; |
| } |
| else { |
| if (!isAssignableByConversion(params[curParam].getType(), types[curArg], context)) { |
| for (int i = startParam; i < curParam; i++) map[i] = null; |
| return false; |
| } |
| map[curParam] = new ArgInfo<Arg>(args[curArg], types[curArg]); |
| notOptional--; |
| curArg++; |
| curParam++; |
| } |
| } |
| |
| List<Arg> varargs = new ArrayList<Arg>(); |
| for (; curArg < args.length; curArg++) { |
| if (!isAssignableByConversion(vararg, types[curArg], context)) { |
| for (int i = startParam; i < curParam; i++) map[i] = null; |
| return false; |
| } |
| varargs.add(args[curArg]); |
| } |
| map[paramLength] = new ArgInfo<Arg>(varargs, true, new PsiEllipsisType(vararg)); |
| return true; |
| } |
| } |
| |
| public static int getOptionalParamCount(GrClosureSignature signature, boolean hasNamedArgs) { |
| return getOptionalParamCount(signature.getParameters(), hasNamedArgs); |
| } |
| |
| public static int getOptionalParamCount(GrClosureParameter[] parameters, boolean hasNamedArgs) { |
| int count = 0; |
| int i = 0; |
| if (hasNamedArgs) i++; |
| for (; i < parameters.length; i++) { |
| GrClosureParameter parameter = parameters[i]; |
| if (parameter.isOptional()) count++; |
| } |
| return count; |
| } |
| |
| public static class ArgInfo<ArgType> { |
| public static final ArgInfo[] EMPTY_ARRAY = new ArgInfo[0]; |
| |
| public List<ArgType> args; |
| public final boolean isMultiArg; |
| public final PsiType type; |
| |
| public ArgInfo(List<ArgType> args, boolean multiArg, PsiType type) { |
| this.args = args; |
| isMultiArg = multiArg; |
| this.type = type; |
| } |
| |
| public ArgInfo(ArgType arg, PsiType type) { |
| this(Collections.singletonList(arg), false, type); |
| } |
| |
| public ArgInfo(boolean isMultiArg, PsiType type) { |
| this(Collections.<ArgType>emptyList(), isMultiArg, type); |
| } |
| |
| public static <ArgType> ArgInfo<ArgType>[] empty_array() { |
| return EMPTY_ARRAY; |
| } |
| } |
| |
| private static class InnerArg { |
| List<PsiElement> list; |
| PsiType type; |
| |
| InnerArg(PsiType type, PsiElement... elements) { |
| this.list = new ArrayList<PsiElement>(Arrays.asList(elements)); |
| this.type = type; |
| } |
| } |
| |
| @Nullable |
| public static Map<GrExpression, Pair<PsiParameter, PsiType>> mapArgumentsToParameters(@NotNull GroovyResolveResult resolveResult, |
| @NotNull PsiElement context, |
| final boolean partial, |
| final boolean eraseArgs, |
| @NotNull final GrNamedArgument[] namedArgs, |
| @NotNull final GrExpression[] expressionArgs, |
| @NotNull GrClosableBlock[] closureArguments) { |
| final GrClosureSignature signature; |
| final PsiParameter[] parameters; |
| final PsiElement element = resolveResult.getElement(); |
| final PsiSubstitutor substitutor = resolveResult.getSubstitutor(); |
| if (element instanceof PsiMethod) { |
| signature = |
| eraseArgs ? createSignatureWithErasedParameterTypes((PsiMethod)element) : createSignature((PsiMethod)element, substitutor); |
| parameters = ((PsiMethod)element).getParameterList().getParameters(); |
| } |
| else if (element instanceof GrClosableBlock) { |
| signature = |
| eraseArgs ? createSignatureWithErasedParameterTypes((GrClosableBlock)element) : createSignature(((GrClosableBlock)element)); |
| parameters = ((GrClosableBlock)element).getAllParameters(); |
| } |
| else { |
| return null; |
| } |
| |
| final ArgInfo<PsiElement>[] argInfos = mapParametersToArguments(signature, namedArgs, expressionArgs, closureArguments, context, partial, eraseArgs); |
| if (argInfos == null) { |
| 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]; |
| if (info == null) continue; |
| for (PsiElement arg : info.args) { |
| if (arg instanceof GrNamedArgument) { |
| arg = ((GrNamedArgument)arg).getExpression(); |
| } |
| 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))); |
| } |
| } |
| |
| return result; |
| } |
| |
| |
| @Nullable |
| public static ArgInfo<PsiElement>[] mapParametersToArguments(@NotNull GrClosureSignature signature, @NotNull GrCall call) { |
| return mapParametersToArguments(signature, call.getNamedArguments(), call.getExpressionArguments(), call.getClosureArguments(), call, |
| false, false); |
| } |
| |
| @Nullable |
| public static ArgInfo<PsiElement>[] mapParametersToArguments(@NotNull GrClosureSignature signature, |
| @NotNull GrNamedArgument[] namedArgs, |
| @NotNull GrExpression[] expressionArgs, |
| @NotNull GrClosableBlock[] closureArguments, |
| @NotNull PsiElement context, |
| boolean partial, boolean eraseArgs) { |
| List<InnerArg> innerArgs = new ArrayList<InnerArg>(); |
| |
| boolean 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) || |
| type == null || |
| type.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) { |
| innerArgs.add(new InnerArg(GrMapType.create(context.getResolveScope()), namedArgs)); |
| } |
| else { |
| return null; |
| } |
| } |
| |
| for (GrExpression expression : expressionArgs) { |
| PsiType type = expression.getType(); |
| if (partial && expression instanceof GrNewExpression && com.intellij.psi.util.PsiUtil.resolveClassInType(type) == null) { |
| type = null; |
| } |
| if (eraseArgs) { |
| type = TypeConversionUtil.erasure(type); |
| } |
| innerArgs.add(new InnerArg(type, expression)); |
| } |
| |
| for (GrClosableBlock closureArgument : closureArguments) { |
| innerArgs.add(new InnerArg(TypeConversionUtil.erasure(closureArgument.getType()), closureArgument)); |
| } |
| |
| return mapParametersToArguments(signature, innerArgs, hasNamedArgs, partial, context); |
| } |
| |
| private static ArgInfo<PsiElement>[] mapParametersToArguments(@NotNull GrClosureSignature signature, |
| @NotNull List<InnerArg> innerArgs, |
| boolean hasNamedArgs, |
| boolean partial, |
| @NotNull PsiElement context) { |
| final ArgInfo<InnerArg>[] innerMap = mapParametersToArguments(signature, innerArgs.toArray(new InnerArg[innerArgs.size()]), new Function<InnerArg, PsiType>() { |
| @Override |
| public PsiType fun(InnerArg o) { |
| return o.type; |
| } |
| }, context, partial); |
| if (innerMap == null) return null; |
| |
| ArgInfo<PsiElement>[] map = new ArgInfo[innerMap.length]; |
| int i = 0; |
| if (hasNamedArgs) { |
| map[i] = new ArgInfo<PsiElement>(innerMap[i].args.iterator().next().list, true, innerArgs.get(i).type); |
| i++; |
| } |
| |
| for (; i < innerMap.length; i++) { |
| final ArgInfo<InnerArg> innerArg = innerMap[i]; |
| if (innerArg == null) { |
| map[i] = null; |
| } |
| else { |
| List<PsiElement> argList = new ArrayList<PsiElement>(); |
| for (InnerArg arg : innerArg.args) { |
| argList.addAll(arg.list); |
| } |
| boolean multiArg = innerArg.isMultiArg || argList.size() > 1; |
| map[i] = new ArgInfo<PsiElement>(argList, multiArg, innerArg.type); |
| } |
| } |
| |
| return map; |
| } |
| |
| public static List<MethodSignature> generateAllSignaturesForMethod(GrMethod method, PsiSubstitutor substitutor) { |
| GrClosureSignature signature = createSignature(method, substitutor); |
| String name = method.getName(); |
| PsiTypeParameter[] typeParameters = method.getTypeParameters(); |
| |
| final ArrayList<MethodSignature> result = new ArrayList<MethodSignature>(); |
| generateAllMethodSignaturesByClosureSignature(name, signature, typeParameters, substitutor, result); |
| return result; |
| } |
| |
| @NotNull |
| public static MultiMap<MethodSignature, PsiMethod> findRawMethodSignatures(@NotNull PsiMethod[] methods, @NotNull PsiClass clazz) { |
| Map<PsiTypeParameter, PsiType> initialMap = ContainerUtil.newHashMap(); |
| |
| for (PsiTypeParameter parameter : clazz.getTypeParameters()) { |
| initialMap.put(parameter, null); |
| } |
| |
| final PsiSubstitutor initialSubstitutor = PsiSubstitutorImpl.createSubstitutor(initialMap); |
| |
| MultiMap<MethodSignature, PsiMethod> result = new MultiMap<MethodSignature, PsiMethod>(); |
| for (PsiMethod method : methods) { |
| final PsiMethod actual = method instanceof GrReflectedMethod ? ((GrReflectedMethod)method).getBaseMethod() : method; |
| |
| PsiSubstitutor substitutor = calcRawSubstitutor(initialMap, initialSubstitutor, actual); |
| result.putValue(method.getSignature(substitutor), actual); |
| } |
| |
| return result; |
| } |
| |
| @NotNull |
| private static PsiSubstitutor calcRawSubstitutor(@NotNull Map<PsiTypeParameter, PsiType> initialMap, |
| @NotNull PsiSubstitutor initialSubstitutor, |
| @NotNull PsiMethod actual) { |
| if (actual.hasTypeParameters()) { |
| final HashMap<PsiTypeParameter, PsiType> map1 = ContainerUtil.newHashMap(initialMap); |
| for (PsiTypeParameter parameter : actual.getTypeParameters()) { |
| map1.put(parameter, null); |
| } |
| return PsiSubstitutorImpl.createSubstitutor(map1); |
| } |
| else { |
| return initialSubstitutor; |
| } |
| } |
| |
| private static MethodSignature generateSignature(String name, |
| List<PsiType> paramTypes, |
| PsiTypeParameter[] typeParameters, |
| PsiSubstitutor substitutor) { |
| return MethodSignatureUtil.createMethodSignature(name, paramTypes.toArray(PsiType.createArray(paramTypes.size())), typeParameters, substitutor); |
| } |
| |
| public static void generateAllMethodSignaturesByClosureSignature(@NotNull String name, |
| @NotNull GrClosureSignature signature, |
| @NotNull PsiTypeParameter[] typeParameters, |
| @NotNull PsiSubstitutor substitutor, |
| List<MethodSignature> result) { |
| GrClosureParameter[] params = signature.getParameters(); |
| |
| ArrayList<PsiType> newParams = new ArrayList<PsiType>(params.length); |
| ArrayList<GrClosureParameter> opts = new ArrayList<GrClosureParameter>(params.length); |
| ArrayList<Integer> optInds = new ArrayList<Integer>(params.length); |
| |
| for (int i = 0; i < params.length; i++) { |
| if (params[i].isOptional()) { |
| opts.add(params[i]); |
| optInds.add(i); |
| } |
| else { |
| newParams.add(params[i].getType()); |
| } |
| } |
| |
| result.add(generateSignature(name, newParams, typeParameters, substitutor)); |
| for (int i = 0; i < opts.size(); i++) { |
| newParams.add(optInds.get(i), opts.get(i).getType()); |
| result.add(generateSignature(name, newParams, typeParameters, substitutor)); |
| } |
| } |
| |
| public static List<MethodSignature> generateAllMethodSignaturesBySignature(@NotNull final String name, |
| @NotNull final GrSignature signature) { |
| |
| final ArrayList<MethodSignature> result = new ArrayList<MethodSignature>(); |
| signature.accept(new GrRecursiveSignatureVisitor() { |
| @Override |
| public void visitClosureSignature(GrClosureSignature signature) { |
| generateAllMethodSignaturesByClosureSignature(name, signature, PsiTypeParameter.EMPTY_ARRAY, PsiSubstitutor.EMPTY, result); |
| } |
| }); |
| return result; |
| } |
| |
| @Nullable |
| public static PsiType getTypeByArg(ArgInfo<PsiElement> arg, PsiManager manager, GlobalSearchScope resolveScope) { |
| if (arg.isMultiArg) { |
| if (arg.args.isEmpty()) return LazyFqnClassType.getLazyType(CommonClassNames.JAVA_LANG_OBJECT, LanguageLevel.JDK_1_5, resolveScope, |
| JavaPsiFacade.getInstance(manager.getProject())).createArrayType(); |
| PsiType leastUpperBound = null; |
| PsiElement first = arg.args.get(0); |
| if (first instanceof GrNamedArgument) { |
| GrNamedArgument[] args=new GrNamedArgument[arg.args.size()]; |
| for (int i = 0, size = arg.args.size(); i < size; i++) { |
| args[i] = (GrNamedArgument)arg.args.get(i); |
| } |
| return GrMapType.createFromNamedArgs(first, args); |
| } |
| else { |
| for (PsiElement elem : arg.args) { |
| if (elem instanceof GrExpression) { |
| leastUpperBound = TypesUtil.getLeastUpperBoundNullable(leastUpperBound, ((GrExpression)elem).getType(), manager); |
| } |
| } |
| if (leastUpperBound == null) return null; |
| return leastUpperBound.createArrayType(); |
| } |
| } |
| else { |
| if (arg.args.isEmpty()) return null; |
| PsiElement elem = arg.args.get(0); |
| if (elem instanceof GrExpression) { |
| return ((GrExpression)elem).getType(); |
| } |
| return null; |
| } |
| } |
| |
| |
| /** |
| * |
| * @param signature |
| * @return return type or null if there is some different return types |
| */ |
| @Nullable |
| public static PsiType getReturnType(GrSignature signature) { |
| if (signature instanceof GrClosureSignature) { |
| return ((GrClosureSignature)signature).getReturnType(); |
| } |
| else if (signature instanceof GrMultiSignature) { |
| final GrClosureSignature[] signatures = ((GrMultiSignature)signature).getAllSignatures(); |
| if (signatures.length == 0) return null; |
| final PsiType type = signatures[0].getReturnType(); |
| if (type == null) return null; |
| String firstType = type.getCanonicalText(); |
| for (int i = 1; i < signatures.length; i++) { |
| final PsiType _type = signatures[i].getReturnType(); |
| if (_type == null) return null; |
| if (!firstType.equals(_type.getCanonicalText())) return null; |
| } |
| return type; |
| } |
| |
| return null; |
| } |
| |
| |
| public static class MapResultWithError<Arg> { |
| private final ArgInfo<Arg>[] mapping; |
| private final List<Pair<Integer, PsiType>> errorsAndExpectedType; |
| |
| public MapResultWithError(ArgInfo<Arg>[] mapping, List<Pair<Integer, PsiType>> errorsAndExpectedType) { |
| this.mapping = mapping; |
| this.errorsAndExpectedType = errorsAndExpectedType; |
| } |
| |
| public ArgInfo<Arg>[] getMapping() { |
| return mapping; |
| } |
| |
| public List<Pair<Integer, PsiType>> getErrors() { |
| return errorsAndExpectedType; |
| } |
| } |
| |
| @Nullable |
| public static <Arg> MapResultWithError<Arg> mapSimpleSignatureWithErrors(@NotNull GrClosureSignature signature, |
| @NotNull Arg[] args, |
| @NotNull Function<Arg, PsiType> typeComputer, |
| @NotNull GroovyPsiElement context, |
| int maxErrorCount) { |
| final GrClosureParameter[] params = signature.getParameters(); |
| if (args.length < params.length) return null; |
| |
| if (args.length > params.length && !signature.isVarargs()) return null; |
| |
| int optional = getOptionalParamCount(params, false); |
| assert optional == 0; |
| |
| int errorCount = 0; |
| ArgInfo<Arg>[] map = new ArgInfo[params.length]; |
| |
| List<Pair<Integer, PsiType>> errors = new ArrayList<Pair<Integer, PsiType>>(maxErrorCount); |
| |
| for (int i = 0; i < params.length; i++) { |
| final PsiType type = typeComputer.fun(args[i]); |
| if (isAssignableByConversion(params[i].getType(), type, context)) { |
| map[i] = new ArgInfo<Arg>(args[i], type); |
| } |
| else if (params[i].getType() instanceof PsiArrayType && i == params.length - 1) { |
| if (i + 1 == args.length) { |
| errors.add(new Pair<Integer, PsiType>(i, params[i].getType())); |
| } |
| final PsiType ellipsis = ((PsiArrayType)params[i].getType()).getComponentType(); |
| for (int j = i; j < args.length; j++) { |
| if (!isAssignableByConversion(ellipsis, typeComputer.fun(args[j]), context)) { |
| errorCount++; |
| if (errorCount > maxErrorCount) return null; |
| errors.add(new Pair<Integer, PsiType>(i, ellipsis)); |
| } |
| map[i] = new ArgInfo<Arg>(args[i], type); |
| } |
| } |
| else { |
| errorCount++; |
| if (errorCount > maxErrorCount) return null; |
| errors.add(new Pair<Integer, PsiType>(i, params[i].getType())); |
| } |
| } |
| return new MapResultWithError<Arg>(map, errors); |
| } |
| |
| public static List<GrClosureSignature> generateSimpleSignatures(@NotNull GrSignature signature) { |
| final List<GrClosureSignature> result = new ArrayList<GrClosureSignature>(); |
| signature.accept(new GrRecursiveSignatureVisitor() { |
| @Override |
| public void visitClosureSignature(GrClosureSignature signature) { |
| final GrClosureParameter[] original = signature.getParameters(); |
| final ArrayList<GrClosureParameter> parameters = new ArrayList<GrClosureParameter>(original.length); |
| |
| for (GrClosureParameter parameter : original) { |
| parameters.add(new GrDelegatingClosureParameter(parameter) { |
| @Override |
| public boolean isOptional() { |
| return false; |
| } |
| |
| @Nullable |
| @Override |
| public GrExpression getDefaultInitializer() { |
| return null; |
| } |
| }); |
| } |
| |
| final int pcount = signature.isVarargs() ? signature.getParameterCount() - 2 : signature.getParameterCount() - 1; |
| for (int i = pcount; i >= 0; i--) { |
| if (original[i].isOptional()) { |
| result.add(new GrImmediateClosureSignatureImpl(parameters.toArray(new GrClosureParameter[parameters.size()]), signature.getReturnType(), signature.isVarargs(), false)); |
| parameters.remove(i); |
| } |
| } |
| result.add(new GrImmediateClosureSignatureImpl(parameters.toArray(new GrClosureParameter[parameters.size()]), signature.getReturnType(), signature.isVarargs(), false)); |
| } |
| }); |
| return result; |
| } |
| } |