diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/signatures/GrClosureSignatureUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/signatures/GrClosureSignatureUtil.java index 3e22c2ebb6be..b3c3b8ca762a 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/signatures/GrClosureSignatureUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/signatures/GrClosureSignatureUtil.java @@ -368,19 +368,22 @@ public class GrClosureSignatureUtil { boolean partial) { if (checkForOnlyMapParam(signature, args.length)) return ArgInfo.empty_array(); GrClosureParameter[] params = signature.getParameters(); - if (args.length > params.length && !signature.isVarargs()) return null; + 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[] map = mapSimple(params, args, typeComputer, context); + final ArgInfo[] map = mapSimple(params, args, typeComputer, context, false); if (map != null) return map; if (signature.isVarargs()) { return new ParameterMapperForVararg(context, params, args, typeComputer).isApplicable(); } - return null; + + if (!partial) return null; + + return mapSimple(params, args, typeComputer, context, true); } private static boolean checkForOnlyMapParam(@NotNull GrClosureSignature signature, final int argCount) { @@ -395,19 +398,25 @@ public class GrClosureSignatureUtil { private static ArgInfo[] mapSimple(@NotNull GrClosureParameter[] params, @NotNull Arg[] args, @NotNull Function typeComputer, - @NotNull GroovyPsiElement context) { + @NotNull GroovyPsiElement context, + boolean partial) { + if (args.length > params.length && !partial) return null; + ArgInfo[] 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 null; + if (cur == params.length) return partial ? map : null; if (params[cur].isOptional()) optionalArgs--; - if (!isAssignableByConversion(params[cur].getType(), typeComputer.fun(args[i]), context)) return null; + if (!isAssignableByConversion(params[cur].getType(), typeComputer.fun(args[i]), context)) return partial ? map : null; map[cur] = new ArgInfo(args[i]); } for (int i = 0; i < map.length; i++) { @@ -668,7 +677,9 @@ public class GrClosureSignatureUtil { if (hasNamedArgs) { if (params.length == 0) return null; PsiType type = params[0].getType(); - if (InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_UTIL_MAP)) { + if (InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_UTIL_MAP) || + type == null || + type.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) { innerArgs.add(new InnerArg(new GrMapType(context.getResolveScope()), namedArgs)); } else { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/inline/GroovyInlineMethodUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/inline/GroovyInlineMethodUtil.java index d8542cdda067..391e2b60174b 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/inline/GroovyInlineMethodUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/inline/GroovyInlineMethodUtil.java @@ -32,18 +32,18 @@ import com.intellij.refactoring.HelpID; import com.intellij.refactoring.inline.InlineOptionsDialog; import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.util.IncorrectOperationException; -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.lang.psi.GroovyFile; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; +import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap; +import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrClosureSignature; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrBlockStatement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrIfStatement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; 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.GrClosableBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrReturnStatement; @@ -53,6 +53,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember; 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.signatures.GrClosureSignatureUtil; import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle; import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringUtil; @@ -431,65 +432,76 @@ public class GroovyInlineMethodUtil { * @param method given method */ public static void replaceParametersWithArguments(GrCallExpression call, GrMethod method) throws IncorrectOperationException { + GrParameter[] parameters = method.getParameters(); + if (parameters.length == 0) return; + GrArgumentList argumentList = call.getArgumentList(); if (argumentList == null) { setDefaultValuesToParameters(method, null, call); return; } - // first parameter may have map type - final GrNamedArgument[] namedArguments = argumentList.getNamedArguments(); - boolean firstParamIsMap = namedArguments.length > 0; - ArrayList exprs = new ArrayList(); - if (firstParamIsMap) { - StringBuilder mapArg = new StringBuilder(); - mapArg.append('['); - for (GrNamedArgument namedArgument : namedArguments) { - mapArg.append(namedArgument.getText()).append(", "); - } + Project project = call.getProject(); - mapArg.delete(mapArg.length() - 2, mapArg.length()); - mapArg.append(']'); - exprs.add(GroovyPsiElementFactory.getInstance(call.getProject()).createExpressionFromText(mapArg.toString())); + GrClosureSignature signature = GrClosureSignatureUtil.createSignature(call); + if (signature == null) { + return; } + GrClosureSignatureUtil.ArgInfo[] infos = GrClosureSignatureUtil.mapParametersToArguments( + signature, + call.getNamedArguments(), + call.getExpressionArguments(), + call.getClosureArguments(), + call, true, false + ); + if (infos == null) return; - ContainerUtil.addAll(exprs, argumentList.getExpressionArguments()); - ContainerUtil.addAll(exprs, call.getClosureArguments()); + for (int i = 0; i < infos.length; i++) { + GrClosureSignatureUtil.ArgInfo argInfo = infos[i]; + GrParameter parameter = parameters[i]; - GrParameter[] parameters = method.getParameters(); - if (parameters.length == 0) return; - GrParameter firstParam = parameters[0]; - while (exprs.size() > parameters.length) { - exprs.remove(exprs.size() - 1); - } - - int nonDefault = 0; - for (GrParameter parameter : parameters) { - if (!(firstParam == parameter && firstParamIsMap)) { - if (parameter.getDefaultInitializer() == null) { - nonDefault++; - } + final GrExpression arg = inferArg(signature, parameters, parameter, argInfo, project); + if (arg != null) { + replaceAllOccurrencesWithExpression(method, call, arg, parameter); } } - nonDefault = exprs.size() - nonDefault - (firstParamIsMap ? 1 : 0); - // Parameters that will be replaced by its default values - Set nameFilter = new HashSet(); - for (GrParameter parameter : parameters) { - if (!(firstParam == parameter && firstParamIsMap)) { - GrExpression initializer = parameter.getDefaultInitializer(); - if (initializer != null) { - if (nonDefault > 0) { - nonDefault--; - } - else { - nameFilter.add(parameter.getName()); + } + + @Nullable + private static GrExpression inferArg(GrClosureSignature signature, + GrParameter[] parameters, + GrParameter parameter, + GrClosureSignatureUtil.ArgInfo argInfo, + Project project) { + if (argInfo == null) return null; + List arguments = argInfo.args; + + if (argInfo.isMultiArg) { //arguments for Map and varArg + final PsiType type = parameter.getDeclaredType(); + return GroovyRefactoringUtil.generateArgFromMultiArg(signature.getSubstitutor(), arguments, type, project); + } + else { //arguments for simple parameters + if (arguments.size() == 1) { //arg exists + PsiElement arg = arguments.iterator().next(); + if (isVararg(parameter, parameters)) { + if (arg instanceof GrSafeCastExpression) { + PsiElement expr = ((GrSafeCastExpression)arg).getOperand(); + if (expr instanceof GrListOrMap && !((GrListOrMap)expr).isMap()) { + return ((GrListOrMap)expr); + } } } + + return (GrExpression)arg; + } + else { //arg is skipped. Parameter is optional + return parameter.getDefaultInitializer(); } } + } - setDefaultValuesToParameters(method, nameFilter, call); - setValuesToParameters(method, call, exprs, nameFilter); + private static boolean isVararg(GrParameter p, GrParameter[] parameters) { + return parameters[parameters.length - 1] == p && p.getType() instanceof PsiArrayType; } /** @@ -515,32 +527,6 @@ public class GroovyInlineMethodUtil { } } - /** - * Replace first m parameters by given values, where m is length of given values vector - * - * @param method - * @param call - * @param values values vector - * @param nameFilter - */ - private static void setValuesToParameters(GrMethod method, GrCallExpression call, List values, Set nameFilter) - throws IncorrectOperationException { - GrParameter[] parameters = method.getParameters(); - if (parameters.length == 0) return; - - if (nameFilter == null) nameFilter = new HashSet(); - - int i = 0; - for (GrExpression value : values) { - while (i < parameters.length && nameFilter.contains(parameters[i].getName())) i++; - if (i < parameters.length) { - GrParameter parameter = parameters[i]; - replaceAllOccurrencesWithExpression(method, call, value, parameter); - } - i++; - } - } - private static void replaceAllOccurrencesWithExpression(GrMethod method, GrCallExpression call, GrExpression oldExpression, diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/inline/InlineMethodTest.java b/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/inline/InlineMethodTest.java index b5cbecf259d5..dd062fd96550 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/inline/InlineMethodTest.java +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/inline/InlineMethodTest.java @@ -103,6 +103,8 @@ public class InlineMethodTest extends LightCodeInsightFixtureTestCase { public void testSideEffectInitializer() {doTest();} + public void testVarargs() {doTest();} + public void testInlineAll() throws Throwable { doTest(new GroovyInlineHandler() { @Override diff --git a/plugins/groovy/testdata/groovy/refactoring/inlineMethod/varargs.test b/plugins/groovy/testdata/groovy/refactoring/inlineMethod/varargs.test new file mode 100644 index 000000000000..778f7a69c048 --- /dev/null +++ b/plugins/groovy/testdata/groovy/refactoring/inlineMethod/varargs.test @@ -0,0 +1,11 @@ +def foo(String s, int... var) { + bar(var) +} + +foo('a') +----- +def foo(String s, int... var) { + bar(var) +} + +bar([] as int[]) \ No newline at end of file