IDEA-84657 No need to report non-inferrable argument types when parameter types are not specified

This commit is contained in:
Maxim.Medvedev
2012-04-27 16:25:36 +04:00
parent 7ff635a209
commit 04d2198017
5 changed files with 185 additions and 88 deletions
@@ -55,6 +55,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction;
import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType;
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.psi.util.GroovyCommonClassNames;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyConstantExpressionEvaluator;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils;
@@ -264,8 +265,7 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
final PsiElement constructor = constructorResolveResult.getElement();
if (constructor != null) {
if (!checkConstructorApplicability(constructorResolveResult, refElement)) return;
if (!checkUnknownParams(refElement)) return;
if (!checkConstructorApplicability(constructorResolveResult, refElement, true)) return;
}
else {
final GroovyResolveResult[] results = constructorCall.multiResolve(false);
@@ -273,10 +273,9 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
for (GroovyResolveResult result : results) {
PsiElement resolved = result.getElement();
if (resolved instanceof PsiMethod) {
if (!checkConstructorApplicability(result, refElement)) return;
if (!checkConstructorApplicability(result, refElement, false)) return;
}
}
if (!checkUnknownParams(refElement)) return;
registerError(getElementToHighlight(refElement, argList), GroovyBundle.message("constructor.call.is.ambiguous"));
}
else {
@@ -336,21 +335,21 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
}
}
else if (element instanceof PsiMethod && ((PsiMethod)element).isConstructor()){
checkLiteralConstructorApplicability(result, listOrMap);
checkLiteralConstructorApplicability(result, listOrMap, true);
}
}
else {
for (GroovyResolveResult result : results) {
PsiElement resolved = result.getElement();
if (resolved instanceof PsiMethod) {
if (!checkLiteralConstructorApplicability(result, listOrMap)) return;
if (!checkLiteralConstructorApplicability(result, listOrMap, false)) return;
}
registerError(listOrMap, GroovyBundle.message("constructor.call.is.ambiguous"));
}
}
}
private boolean checkLiteralConstructorApplicability(GroovyResolveResult result, GrListOrMap listOrMap) {
private boolean checkLiteralConstructorApplicability(GroovyResolveResult result, GrListOrMap listOrMap, boolean checkUnknownArgs) {
final PsiElement element = result.getElement();
LOG.assertTrue(element instanceof PsiMethod && ((PsiMethod)element).isConstructor());
final PsiMethod constructor = (PsiMethod)element;
@@ -373,15 +372,23 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
argumentTypes = new PsiType[]{listOrMap.getType()};
}
if (PsiUtil.isApplicable(argumentTypes, constructor, result.getSubstitutor(), listOrMap, false)) {
return true;
GrClosureSignatureUtil.ApplicabilityResult applicable =
PsiUtil.isApplicableConcrete(argumentTypes, constructor, result.getSubstitutor(), listOrMap, false);
switch (applicable) {
case inapplicable:
highlightInapplicableMethodUsage(result, listOrMap, constructor, argumentTypes);
return false;
case canBeApplicable:
if (checkUnknownArgs) {
highlightUnknownArgs(listOrMap);
}
return !checkUnknownArgs;
default:
return true;
}
highlightInapplicableMethodUsage(result, listOrMap, constructor, argumentTypes);
return false;
}
private boolean checkConstructorApplicability(GroovyResolveResult constructorResolveResult, GroovyPsiElement place) {
private boolean checkConstructorApplicability(GroovyResolveResult constructorResolveResult, GroovyPsiElement place, boolean checkUnknownArgs) {
final PsiElement element = constructorResolveResult.getElement();
LOG.assertTrue(element instanceof PsiMethod && ((PsiMethod)element).isConstructor());
final PsiMethod constructor = (PsiMethod)element;
@@ -392,7 +399,7 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
if (exprArgs.length == 0 && !PsiUtil.isConstructorHasRequiredParameters(constructor)) return true;
}
return checkMethodApplicability(constructorResolveResult, place);
return checkMethodApplicability(constructorResolveResult, place, checkUnknownArgs);
}
@Override
@@ -422,25 +429,22 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
final PsiType type = referenceExpression.getType();
if (resolved != null) {
if (resolved instanceof PsiMethod && !resolveResult.isInvokedOnProperty()) {
if (!checkMethodApplicability(resolveResult, referenceExpression)) return;
if (!checkMethodApplicability(resolveResult, referenceExpression, true)) return;
}
else {
if (!checkCallApplicability(type, referenceExpression)) return;
if (!checkCallApplicability(type, referenceExpression, true)) return;
}
if (!checkUnknownParams(referenceExpression)) return;
}
else if (results.length > 0) {
for (GroovyResolveResult result : results) {
resolved = result.getElement();
if (resolved instanceof PsiMethod && !resolveResult.isInvokedOnProperty()) {
if (!checkMethodApplicability(result, referenceExpression)) return;
if (!checkMethodApplicability(result, referenceExpression, false)) return;
}
else {
if (!checkCallApplicability(type, referenceExpression)) return;
if (!checkCallApplicability(type, referenceExpression, false)) return;
}
}
if (!checkUnknownParams(referenceExpression)) return;
registerError(getElementToHighlight(referenceExpression, PsiUtil.getArgumentsList(referenceExpression)),
GroovyBundle.message("method.call.is.ambiguous"));
@@ -448,14 +452,13 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
}
}
private boolean checkUnknownParams(PsiElement place) {
private boolean checkUnknownParams(PsiElement place, PsiElement resolved) {
PsiType[] args = PsiUtil.getArgumentTypes(place, true);
if (args == null) return true;
for (PsiType arg : args) {
if (arg == null) {
registerError(getElementToHighlight(place, PsiUtil.getArgumentsList(place)), GroovyBundle.message("cannot.infer.argument.types"),
LocalQuickFix.EMPTY_ARRAY, ProblemHighlightType.WEAK_WARNING);
return false;
}
}
@@ -523,7 +526,7 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
final GrExpression expression = call.getInvokedExpression();
if (!(expression instanceof GrReferenceExpression)) { //it checks in visitRefExpr(...)
final PsiType type = expression.getType();
checkCallApplicability(type, expression);
checkCallApplicability(type, expression, true);
}
checkNamedArgumentsType(call);
@@ -555,29 +558,36 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
}
private boolean checkCallApplicability(PsiType type, GroovyPsiElement invokedExpr) {
private boolean checkCallApplicability(PsiType type, GroovyPsiElement invokedExpr, boolean checkUnknownArgs) {
PsiType[] argumentTypes = PsiUtil.getArgumentTypes(invokedExpr, true);
if (type instanceof GrClosureType) {
if (argumentTypes == null) return true;
if (PsiUtil.isApplicable(argumentTypes, (GrClosureType)type, invokedExpr)) return true;
registerCannotApplyError(invokedExpr, argumentTypes, invokedExpr.getText());
return false;
GrClosureSignatureUtil.ApplicabilityResult result = PsiUtil.isApplicableConcrete(argumentTypes, (GrClosureType)type, invokedExpr);
switch (result) {
case inapplicable:
registerCannotApplyError(invokedExpr, argumentTypes, invokedExpr.getText());
return false;
case canBeApplicable:
if (checkUnknownArgs) {
highlightUnknownArgs(invokedExpr);
}
return !checkUnknownArgs;
default:
return true;
}
}
else if (type != null) {
final GroovyResolveResult[] calls = ResolveUtil.getMethodCandidates(type, "call", invokedExpr, argumentTypes);
for (GroovyResolveResult result : calls) {
PsiElement resolved = result.getElement();
if (resolved instanceof PsiMethod && !result.isInvokedOnProperty()) {
if (!checkMethodApplicability(result, invokedExpr)) return false;
if (!checkUnknownParams(invokedExpr)) return false;
if (!checkMethodApplicability(result, invokedExpr, checkUnknownArgs && calls.length == 1)) return false;
}
else if (resolved instanceof PsiField) {
if (!checkCallApplicability(((PsiField)resolved).getType(), invokedExpr)) return false;
if (!checkCallApplicability(((PsiField)resolved).getType(), invokedExpr, checkUnknownArgs && calls.length == 1)) return false;
}
if (!checkUnknownParams(invokedExpr)) return false;
}
if (calls.length == 0 && !(invokedExpr instanceof GrString)) {
registerCannotApplyError(invokedExpr, argumentTypes, invokedExpr.getText());
@@ -609,7 +619,7 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
return builder.toString();
}
private boolean checkMethodApplicability(GroovyResolveResult methodResolveResult, GroovyPsiElement place) {
private boolean checkMethodApplicability(GroovyResolveResult methodResolveResult, GroovyPsiElement place, boolean checkUnknownArgs) {
final PsiElement element = methodResolveResult.getElement();
if (!(element instanceof PsiMethod)) return true;
if (element instanceof GrBuilderMethod) return true;
@@ -621,39 +631,61 @@ public class GroovyAssignabilityCheckInspection extends BaseInspection {
if (qualifierExpression != null) {
final PsiType type = qualifierExpression.getType();
if (type instanceof GrClosureType) {
if (!PsiUtil.isApplicable(argumentTypes, (GrClosureType)type, place)) {
highlightInapplicableMethodUsage(methodResolveResult, place, method, argumentTypes);
return false;
GrClosureSignatureUtil.ApplicabilityResult result = PsiUtil.isApplicableConcrete(argumentTypes, (GrClosureType)type, place);
switch (result) {
case inapplicable:
highlightInapplicableMethodUsage(methodResolveResult, place, method, argumentTypes);
return false;
case canBeApplicable:
if (checkUnknownArgs) {
highlightUnknownArgs(place);
}
return !checkUnknownArgs;
default:
return true;
}
}
}
}
if (argumentTypes != null &&
!PsiUtil.isApplicable(argumentTypes, method, methodResolveResult.getSubstitutor(), place, false)) {
if (argumentTypes == null) return true;
//check for implicit use of property getter which returns closure
if (GroovyPropertyUtils.isSimplePropertyGetter(method)) {
if (method instanceof GrMethod || method instanceof GrAccessorMethod) {
final PsiType returnType = PsiUtil.getSmartReturnType(method);
if (returnType instanceof GrClosureType) {
if (PsiUtil.isApplicable(argumentTypes, ((GrClosureType)returnType), place)) {
GrClosureSignatureUtil.ApplicabilityResult applicable = PsiUtil.isApplicableConcrete(argumentTypes, method, methodResolveResult.getSubstitutor(), place, false);
switch (applicable) {
case inapplicable:
//check for implicit use of property getter which returns closure
if (GroovyPropertyUtils.isSimplePropertyGetter(method)) {
if (method instanceof GrMethod || method instanceof GrAccessorMethod) {
final PsiType returnType = PsiUtil.getSmartReturnType(method);
if (returnType instanceof GrClosureType) {
if (PsiUtil.isApplicable(argumentTypes, ((GrClosureType)returnType), place)) {
return true;
}
}
}
PsiType returnType = method.getReturnType();
if (returnType != null) {
if (TypesUtil.isAssignable(TypesUtil.createType(GroovyCommonClassNames.GROOVY_LANG_CLOSURE, element), returnType, place)) {
return true;
}
}
}
PsiType returnType = method.getReturnType();
if (returnType != null) {
if (TypesUtil.isAssignable(TypesUtil.createType(GroovyCommonClassNames.GROOVY_LANG_CLOSURE, element), returnType, place)) {
return true;
}
highlightInapplicableMethodUsage(methodResolveResult, place, method, argumentTypes);
return false;
case canBeApplicable:
if (checkUnknownArgs) {
highlightUnknownArgs(place);
}
}
highlightInapplicableMethodUsage(methodResolveResult, place, method, argumentTypes);
return false;
return !checkUnknownArgs;
default:
return true;
}
return true;
}
private void highlightUnknownArgs(GroovyPsiElement place) {
registerError(getElementToHighlight(place, PsiUtil.getArgumentsList(place)), GroovyBundle.message("cannot.infer.argument.types"),
LocalQuickFix.EMPTY_ARRAY, ProblemHighlightType.WEAK_WARNING);
}
}
@@ -167,26 +167,56 @@ public class GrClosureSignatureUtil {
return new GrClosureSignatureImpl(parameters, returnType);
}
public static boolean isSignatureApplicable(GrClosureSignature signature, PsiType[] args, GroovyPsiElement context) {
if (mapArgTypesToParameters(signature, args, context, false) != null) return true;
public static boolean isSignatureApplicable(@NotNull GrClosureSignature signature, @NotNull PsiType[] args, @NotNull GroovyPsiElement context) {
return isSignatureApplicableConcrete(signature, args, context) != ApplicabilityResult.inapplicable;
}
public static ApplicabilityResult isSignatureApplicableConcrete(@NotNull GrClosureSignature signature, @NotNull PsiType[] args, @NotNull GroovyPsiElement context) {
ArgInfo<PsiType>[] map = mapArgTypesToParameters(signature, args, context, false);
if (map != null) return isSignatureApplicableInner(map, signature);
// 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 false;
if (parameters.length == 1 && parameters[0].getType() instanceof PsiArrayType) return ApplicabilityResult.inapplicable;
PsiType arg = args[0];
if (arg instanceof GrTupleType) {
args = ((GrTupleType)arg).getComponentTypes();
if (mapArgTypesToParameters(signature, args, context, false) != null) return true;
map = mapArgTypesToParameters(signature, args, context, false);
if (map != null) return isSignatureApplicableInner(map, signature);
}
}
return false;
return ApplicabilityResult.inapplicable;
}
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;
}
public enum ApplicabilityResult {
applicable, inapplicable, canBeApplicable;
public static boolean isApplicable(ApplicabilityResult r) {
return r != inapplicable;
}
}
@Nullable
public static ArgInfo<PsiType>[] mapArgTypesToParameters(@NotNull GrClosureSignature signature,
PsiType[] args,
GroovyPsiElement context,
@NotNull PsiType[] args,
@NotNull GroovyPsiElement context,
boolean partial) {
return mapParametersToArguments(signature, args, FunctionUtil.<PsiType>id(), context, partial);
}
@@ -211,9 +241,9 @@ public class GrClosureSignatureUtil {
}
@Nullable
private static <Arg> ArgWrapper<Arg>[] getActualArgs(GrCurriedClosureSignature signature,
Arg[] args,
Function<Arg, PsiType> typeComputer) {
private static <Arg> ArgWrapper<Arg>[] getActualArgs(@NotNull GrCurriedClosureSignature signature,
@NotNull Arg[] args,
@NotNull Function<Arg, PsiType> typeComputer) {
List<ArgWrapper<Arg>> actual = new ArrayList<ArgWrapper<Arg>>(signature.getParameterCount());
for (Arg arg : args) {
actual.add(new ArgWrapper<Arg>(typeComputer.fun(arg), arg));
@@ -243,8 +273,8 @@ public class GrClosureSignatureUtil {
@Nullable
private static <Arg> ArgInfo<Arg>[] mapParametersToArguments(@NotNull GrClosureSignature signature,
Arg[] args,
Function<Arg, PsiType> typeComputer,
@NotNull Arg[] args,
@NotNull Function<Arg, PsiType> typeComputer,
@NotNull GroovyPsiElement context,
boolean partial) {
if (signature instanceof GrCurriedClosureSignature) {
@@ -291,10 +321,10 @@ public class GrClosureSignatureUtil {
}
@Nullable
private static <Arg> ArgInfo<Arg>[] mapSimple(GrClosureParameter[] params,
Arg[] args,
Function<Arg, PsiType> typeComputer,
GroovyPsiElement context) {
private static <Arg> ArgInfo<Arg>[] mapSimple(@NotNull GrClosureParameter[] params,
@NotNull Arg[] args,
@NotNull Function<Arg, PsiType> typeComputer,
@NotNull GroovyPsiElement context) {
ArgInfo<Arg>[] map = new ArgInfo[params.length];
int optional = getOptionalParamCount(params, false);
int notOptional = params.length - optional;
@@ -315,7 +345,7 @@ public class GrClosureSignatureUtil {
return map;
}
private static boolean isAssignableByConversion(PsiType paramType, PsiType argType, GroovyPsiElement context) {
private static boolean isAssignableByConversion(@Nullable PsiType paramType, @Nullable PsiType argType, @NotNull GroovyPsiElement context) {
if (argType == null) {
return true;
}
@@ -135,11 +135,20 @@ public class PsiUtil {
}
public static boolean isApplicable(@Nullable PsiType[] argumentTypes,
PsiMethod method,
PsiSubstitutor substitutor,
GroovyPsiElement place,
final boolean eraseParameterTypes) {
if (argumentTypes == null) return true;
PsiMethod method,
PsiSubstitutor substitutor,
GroovyPsiElement place,
final boolean eraseParameterTypes) {
return isApplicableConcrete(argumentTypes, method, substitutor, place, eraseParameterTypes) !=
GrClosureSignatureUtil.ApplicabilityResult.inapplicable;
}
public static GrClosureSignatureUtil.ApplicabilityResult isApplicableConcrete(@Nullable PsiType[] argumentTypes,
PsiMethod method,
PsiSubstitutor substitutor,
GroovyPsiElement place,
final boolean eraseParameterTypes) {
if (argumentTypes == null) return GrClosureSignatureUtil.ApplicabilityResult.canBeApplicable;
GrClosureSignature signature = eraseParameterTypes
? GrClosureSignatureUtil.createSignatureWithErasedParameterTypes(method)
@@ -149,34 +158,45 @@ public class PsiUtil {
if (method.isConstructor()) {
final PsiParameter[] parameters = method.getParameterList().getParameters();
if (parameters.length == 0 && argumentTypes.length == 1) {
return InheritanceUtil.isInheritor(argumentTypes[0], CommonClassNames.JAVA_UTIL_MAP);
return InheritanceUtil.isInheritor(argumentTypes[0], CommonClassNames.JAVA_UTIL_MAP)
? GrClosureSignatureUtil.ApplicabilityResult.applicable
: GrClosureSignatureUtil.ApplicabilityResult.inapplicable;
}
if (parameters.length == 1 &&
argumentTypes.length == 0 &&
InheritanceUtil.isInheritor(parameters[0].getType(), CommonClassNames.JAVA_UTIL_MAP)) {
return false;
return GrClosureSignatureUtil.ApplicabilityResult.inapplicable;
}
}
LOG.assertTrue(signature != null);
if (GrClosureSignatureUtil.isSignatureApplicable(signature, argumentTypes, place)) {
return true;
GrClosureSignatureUtil.ApplicabilityResult result = GrClosureSignatureUtil.isSignatureApplicableConcrete(signature, argumentTypes, place);
if (result != GrClosureSignatureUtil.ApplicabilityResult.inapplicable) {
return result;
}
if (method instanceof GrBuilderMethod &&!((GrBuilderMethod)method).hasObligatoryNamedArguments()) {
final PsiParameter[] parameters = method.getParameterList().getParameters();
if (parameters.length > 0 && parameters[0].getType() instanceof GrMapType &&
(argumentTypes.length == 0 || !(argumentTypes[0] instanceof GrMapType))) {
return GrClosureSignatureUtil.isSignatureApplicable(GrClosureSignatureUtil.removeParam(signature, 0), argumentTypes, place);
return GrClosureSignatureUtil.isSignatureApplicableConcrete(GrClosureSignatureUtil.removeParam(signature, 0), argumentTypes, place);
}
}
return false;
return GrClosureSignatureUtil.ApplicabilityResult.inapplicable;
}
public static boolean isApplicable(@Nullable PsiType[] argumentTypes, GrClosureType type, GroovyPsiElement context) {
if (argumentTypes == null) return true;
public static boolean isApplicable(@Nullable PsiType[] argumentTypes,
GrClosureType type,
GroovyPsiElement context) {
return isApplicableConcrete(argumentTypes, type, context) != GrClosureSignatureUtil.ApplicabilityResult.inapplicable;
}
public static GrClosureSignatureUtil.ApplicabilityResult isApplicableConcrete(@Nullable PsiType[] argumentTypes,
GrClosureType type,
GroovyPsiElement context) {
if (argumentTypes == null) return GrClosureSignatureUtil.ApplicabilityResult.canBeApplicable;
GrClosureSignature signature = type.getSignature();
return GrClosureSignatureUtil.isSignatureApplicable(signature, argumentTypes, context);
return GrClosureSignatureUtil.isSignatureApplicableConcrete(signature, argumentTypes, context);
}
@Nullable
@@ -788,4 +788,19 @@ class C {
myFixture.testHighlighting(true, false, false)
}
public void testNonInferrableArgsOfDefParams() {
myFixture.configureByText('_.groovy', '''\
def foo0(def a) { }
def bar0(def b) { foo0(b) }
def foo1(Object a) { }
def bar1(def b) { foo1(b) }
def foo2(String a) { }
def bar2(def b) { foo2<weak_warning descr="Cannot infer argument types">(b)</weak_warning> }
''')
myFixture.enableInspections(new GroovyAssignabilityCheckInspection())
myFixture.testHighlighting(true, false, true)
}
}
@@ -10,4 +10,4 @@ def abc(Date d){}
def abc(int i) {}
def x = unknown2
abc<weak_warning descr="Cannot infer argument types">(x)</weak_warning>
abc<warning descr="Method call is ambiguous">(x)</warning>