parameters = ArrayUtil.remove(parameters, 0);
}
- PsiType[] parameterTypes = skipOptionalParametersAndSubstitute(argumentTypes.length, parameters, substitutor);
- if (parameterTypes.length - 1 > argumentTypes.length) return false; //one Map type might represent named arguments
- if (parameterTypes.length == 0 && argumentTypes.length > 0) return false;
-
- final GlobalSearchScope scope = method.getResolveScope();
-
- if (parameterTypes.length - 1 == argumentTypes.length) {
- final PsiType firstType = parameterTypes[0];
- final PsiClassType mapType = createMapType(method.getManager(), scope);
- if (mapType.isAssignableFrom(firstType)) {
- final PsiType[] trimmed = new PsiType[parameterTypes.length - 1];
- System.arraycopy(parameterTypes, 1, trimmed, 0, trimmed.length);
- parameterTypes = trimmed;
- } else if (!method.isVarArgs()) return false;
- }
-
- for (int i = 0; i < argumentTypes.length; i++) {
- PsiType argType = argumentTypes[i];
- PsiType parameterTypeToCheck;
- if (i < parameterTypes.length - 1) {
- parameterTypeToCheck = parameterTypes[i];
- } else {
- PsiType lastParameterType = parameterTypes[parameterTypes.length - 1];
- if (lastParameterType instanceof PsiArrayType && !(argType instanceof PsiArrayType)) {
- parameterTypeToCheck = ((PsiArrayType)lastParameterType).getComponentType();
- } else if (parameterTypes.length == argumentTypes.length) {
- parameterTypeToCheck = lastParameterType;
- } else {
- return false;
- }
+ if (parameters.length == 0) return argumentTypes.length == 0;
+
+ final PsiParameter lastParameter = parameters[parameters.length - 1];
+ boolean hasVarArgs = lastParameter.isVarArgs() || lastParameter.getType() instanceof PsiArrayType;
+ if (!hasVarArgs && argumentTypes.length > parameters.length) return false;
+
+ int allOptionalParameterCount = 0;
+ for (PsiParameter parameter : parameters) {
+ if (isOptionalParameter(parameter)) {
+ allOptionalParameterCount++;
}
+ }
- if (!TypesUtil.isAssignableByMethodCallConversion(parameterTypeToCheck, argType, method.getManager(), scope)) return false;
+ int optionalParameterCount;
+ if (hasVarArgs) {
+ optionalParameterCount = allOptionalParameterCount - (parameters.length - 1) + argumentTypes.length;
+ if (optionalParameterCount < 0) return false;
+ for (int i = optionalParameterCount; i >= 0; i--) {
+ if (checkMethodApplicability(parameters, argumentTypes, i, hasVarArgs, substitutor, method.getResolveScope(), method.getManager())) {
+ return true;
+ }
+ }
+ return false;
}
+ else {
+ optionalParameterCount = allOptionalParameterCount - parameters.length + argumentTypes.length;
+ if (optionalParameterCount < 0) return false;
+ return checkMethodApplicability(parameters, argumentTypes, optionalParameterCount, hasVarArgs, substitutor, method.getResolveScope(),
+ method.getManager());
- return true;
+ }
}
- public static PsiType[] skipOptionalParametersAndSubstitute(int argNum, PsiParameter[] parameters, PsiSubstitutor substitutor) {
- int diff = parameters.length - argNum;
- List<PsiType> result = new ArrayList<PsiType>(argNum);
- for (PsiParameter parameter : parameters) {
- if (diff > 0 && parameter instanceof GrParameter && ((GrParameter)parameter).isOptional()) {
- diff--;
- continue;
+ private static boolean checkMethodApplicability(PsiParameter[] parameters,
+ PsiType[] argumentTypes,
+ int optionalParametersCount,
+ boolean hasVarArg,
+ PsiSubstitutor substitutor,
+ GlobalSearchScope scope,
+ PsiManager manager) {
+
+ int argIndex = 0;
+ for (int i = 0; i < parameters.length - 1; i++) {
+ if (isOptionalParameter(parameters[i])) {
+ if (optionalParametersCount == 0) continue;
+ optionalParametersCount--;
}
- result.add(substitutor.substitute(parameter.getType()));
+ final PsiType parameterType = substitutor.substitute(parameters[i].getType());
+ if (argIndex >= argumentTypes.length) return false;
+ final PsiType argType = argumentTypes[argIndex++];
+ if (!TypesUtil.isAssignableByMethodCallConversion(parameterType, argType, manager, scope)) {
+ return false;
+ }
}
- return result.toArray(new PsiType[result.size()]);
+ final PsiParameter lastParameter = parameters[parameters.length - 1];
+ final PsiType lastParameterType = substitutor.substitute(lastParameter.getType());
+
+ if (hasVarArg) {
+ if (argIndex == argumentTypes.length - 1 &&
+ TypesUtil.isAssignableByMethodCallConversion(lastParameterType, argumentTypes[argIndex], manager, scope)) {
+ return true;
+ }
+ final PsiType arrayType = ((PsiArrayType)lastParameterType).getComponentType();
+ for (; argIndex < argumentTypes.length; argIndex++) {
+ if (!TypesUtil.isAssignableByMethodCallConversion(arrayType, argumentTypes[argIndex], manager, scope)) return false;
+ }
+ }
+ else {
+ if (!isOptionalParameter(lastParameter) || optionalParametersCount > 0) {
+ if (argIndex >= argumentTypes.length) return false;
+ final PsiType argType = argumentTypes[argIndex++];
+ if (!TypesUtil.isAssignableByMethodCallConversion(lastParameterType, argType, manager, scope)) return false;
+ }
+ }
+ return argIndex == argumentTypes.length;
+ }
+
+ private static boolean isOptionalParameter(PsiParameter parameter) {
+ return parameter instanceof GrParameter && ((GrParameter)parameter).isOptional();
}
public static PsiClassType createMapType(PsiManager manager, GlobalSearchScope scope) {
@Nullable
public static PsiElement skipWhitespaces(@Nullable PsiElement elem, boolean forward) {
+ //noinspection ConstantConditions
while (elem != null &&
elem.getNode() != null &&
GroovyElementTypes.WHITE_SPACES_OR_COMMENTS.contains(elem.getNode().getElementType())) {
public void testGstringAssignableToString() throws Exception{doTest();}
public void testGstringAssignableToStringInClosureParameter() throws Exception{doTest();}
public void testEverythingAssignableToString() throws Exception {doTest(new GroovyAssignabilityCheckInspection());}
+
+ public void testMethodCallWithDefaultParameters() throws Exception {doTest();}
}
\ No newline at end of file
public void testConstructor2() throws Exception {
PsiReference ref = configureByFile("constructor2/A.groovy");
PsiMethod method = ((GrNewExpression) ref.getElement().getParent()).resolveConstructor();
- assertNotNull(method);
- assertTrue(method.isConstructor());
- final PsiParameter[] parameters = method.getParameterList().getParameters();
- assertEquals(1, parameters.length);
- assertTrue(parameters[0].getType().equalsToText("java.util.Map"));
+ assertNull(method);
}
//grvy-101
assertInstanceOf(resolved, PsiMethod.class);
assertEquals("A", ((PsiMethod)resolved).getContainingClass().getName());
}
+
+ public void testOptionalParameters1() throws Exception {
+ PsiReference ref = configureByFile("optionalParameters1/A.groovy");
+ final PsiElement resolved = ref.resolve();
+ assertInstanceOf(resolved, PsiMethod.class);
+ }
+
+ public void testOptionalParameters2() throws Exception {
+ PsiReference ref = configureByFile("optionalParameters2/A.groovy");
+ final PsiElement resolved = ref.resolve();
+ assertInstanceOf(resolved, PsiMethod.class);
+ }
+
+ public void testOptionalParameters3() throws Exception {
+ PsiReference ref = configureByFile("optionalParameters3/A.groovy");
+ final PsiElement resolved = ref.resolve();
+ assertInstanceOf(resolved, PsiMethod.class);
+ }
+
+ public void testOptionalParameters4() throws Exception {
+ PsiReference ref = configureByFile("optionalParameters4/A.groovy");
+ final PsiElement resolved = ref.resolve();
+ assertInstanceOf(resolved, PsiMethod.class);
+ }
}
--- /dev/null
+public def createRegisteredPerson(String username,
+ String password,
+ String email,
+ String ipAddress,
+ String roleName = null,
+ String firstName = null,
+ String lastName = null,
+ Date birthday = null,
+ String bio = null,
+ String homepage = null,
+ Date timeZone = null,
+ String country = null,
+ String city = null,
+ String jabber = null,
+ String site = null,
+ String sex = null) {
+}
+
+createRegisteredPerson('name', 'pswd', 'email', 'ip', 'role', 'firstName', 'lastName', null, 'bio', 'page')
+
+def foo(String a, Date b = null, int i = -1, String c, String d = 'd', String e, String f) {}
+
+
+foo('aa', 'cc', 'dd', 'ee')
+foo('a', null, 'c', 'e')
+foo('a', null, 'd', 'c', 'e')
+
+ foo<warning descr="'foo' in 'MethodCallWithDefaultParameters' cannot be applied to '(java.lang.String, null, java.lang.Integer, java.lang.String, java.lang.String)'">('a', null, 1, 'c', 'e')</warning>
+ foo<warning descr="'foo' in 'MethodCallWithDefaultParameters' cannot be applied to '(java.lang.String, java.lang.String, java.lang.String)'">("aa", 'cc', 'ee')</warning>
+
+foo('aa', null, 'cc', 'dd', 'ee')
+ foo<warning descr="'foo' in 'MethodCallWithDefaultParameters' cannot be applied to '(java.lang.String, null, java.lang.Integer, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)'">('aa', null, 1, 'cc', 'dd', 'ee', 'f', 'g')</warning>
+
+foo('a', 'cc', 'dd', 'ee')
+ foo<warning descr="'foo' in 'MethodCallWithDefaultParameters' cannot be applied to '(java.lang.String, java.lang.Integer, java.lang.String, java.lang.String, java.lang.String)'">('a', -1, 'cc', 'dd', 'ee')</warning>
+
+
+def bar(String a, Date b = null, int i = -1, String c, String d = 'd', String e, String... f) {}
+
+
+bar('aa', 'cc', 'dd', 'ee')
+bar('a', null, 'c', 'e')
+bar('a', null, 'd', 'c', 'e')
+
+bar('a', null, 1, 'c', 'e')
+bar("aa", 'cc', 'ee')
+
+bar('aa', null, 'cc', 'dd', 'ee')
+bar('aa', null, 1, 'cc', 'dd', 'ee', 'f', 'g')
+
+bar('a', 'cc', 'dd', 'ee')
+ bar<warning descr="'bar' in 'MethodCallWithDefaultParameters' cannot be applied to '(java.lang.String, java.lang.Integer, java.lang.String, java.lang.String, java.lang.String)'">('a', -1, 'cc', 'dd', 'ee')</warning>
+
+def go(String a, String b = 'b', String c, int ... i) {}
+
+go('a', 'c', 1, 2, 3);
\ No newline at end of file
--- /dev/null
+class RegisteredPerson{}
+public RegisteredPerson createRegisteredPerson(String username,
+ String password,
+ String email,
+ String ipAddress,
+ String roleName = null,
+ String firstName = null,
+ String lastName = null,
+ Date birthday = null,
+ String bio = null,
+ String homepage = null,
+ TimeZone timeZone = null,
+ String country = null,
+ String city = null,
+ String jabber = null,
+ String site = null,
+ String sex = null)
+{
+ return null;
+}
+
+createRe<ref>gisteredPerson('username', 'pswd', 'email', 'ip', 'role', 'name', 'family', null, 'bio', 'page')
\ No newline at end of file
--- /dev/null
+def foo(String a, String b = 'b', String c = 'c', int i = 1, String d) {}
+
+f<ref>oo('a', 'b', 2, 'd')
\ No newline at end of file
--- /dev/null
+def foo(String a, String b = 'b', String c = 'c', int i = 1, String d) {}
+
+f<ref>oo('a', 'b', 'd')
\ No newline at end of file
--- /dev/null
+def foo(String a, String b = 'b', int i = 1, String d, String c = 'c') {}
+
+f<ref>oo('a', 'bb', 'd', 'cc')
\ No newline at end of file