verify argument list even if unable to resolve callee (PY-10351)

This commit is contained in:
Dmitry Jemerov
2013-10-31 21:00:19 +01:00
parent eb53fab850
commit 8e3e414a66
4 changed files with 64 additions and 30 deletions
@@ -65,29 +65,9 @@ public class CallArgumentsMappingImpl implements CallArgumentsMapping {
public void mapArguments(PyCallExpression.PyMarkedCallee resolved_callee, @NotNull TypeEvalContext context) {
PyExpression[] arguments = myArgumentList.getArguments();
myMarkedCallee = resolved_callee;
List<PyExpression> unmatched_args = new LinkedList<PyExpression>();
Collections.addAll(unmatched_args, arguments);
final List<PyExpression> unmatched_subargs = new LinkedList<PyExpression>(); // unmatched nested arguments will go here
// detect starred args
for (PyExpression arg : arguments) {
if (arg instanceof PyStarArgument) {
PyStarArgument star_arg = (PyStarArgument)arg;
if (star_arg.isKeyword()) {
if (myKwdArg == null) myKwdArg = star_arg;
else {
markArgument(arg, ArgFlag.IS_DUP_KWD);
unmatched_args.remove(arg);
}
}
else {
if (myTupleArg == null) myTupleArg = star_arg;
else {
markArgument(arg, ArgFlag.IS_DUP_TUPLE);
unmatched_args.remove(arg);
}
}
}
}
List<PyExpression> unmatched_args = verifyArguments();
final List<PyParameter> parameters = PyUtil.getParameters(myMarkedCallee.getCallable(), context);
// prepare parameter slots
Map<PyNamedParameter, PyExpression> slots = new LinkedHashMap<PyNamedParameter, PyExpression>();
@@ -181,7 +161,6 @@ public class CallArgumentsMappingImpl implements CallArgumentsMapping {
markArgument(arg, ArgFlag.IS_UNMAPPED);
}
markPastBoundPositionalArguments(arguments, positional_bound);
boolean seen_named_args = false;
// map named args to named params if possible
@@ -346,15 +325,53 @@ public class CallArgumentsMappingImpl implements CallArgumentsMapping {
}
}
private void markPastBoundPositionalArguments(PyExpression[] arguments, int positionalBound) {
public List<PyExpression> verifyArguments() {
List<PyExpression> unmatched_args = new LinkedList<PyExpression>();
Collections.addAll(unmatched_args, myArgumentList.getArguments());
// detect starred args
for (PyExpression arg : myArgumentList.getArguments()) {
if (arg instanceof PyStarArgument) {
PyStarArgument star_arg = (PyStarArgument)arg;
if (star_arg.isKeyword()) {
if (myKwdArg == null) myKwdArg = star_arg;
else {
markArgument(arg, ArgFlag.IS_DUP_KWD);
unmatched_args.remove(arg);
}
}
else {
if (myTupleArg == null) myTupleArg = star_arg;
else {
markArgument(arg, ArgFlag.IS_DUP_TUPLE);
unmatched_args.remove(arg);
}
}
}
}
markPastBoundPositionalArguments(myArgumentList.getArguments());
return unmatched_args;
}
private void markPastBoundPositionalArguments(PyExpression[] arguments) {
boolean seenKwArg = false;
for(int i=positionalBound; i<arguments.length; i++) {
PyExpression arg = arguments[i];
boolean seenKeyword = false;
boolean seenStar = false;
for (PyExpression arg : arguments) {
if (arg == myKwdArg) {
seenKwArg = true;
}
if (!(arg instanceof PyStarArgument) && (seenKwArg || !(arg instanceof PyKeywordArgument))) {
markArgument(arg, ArgFlag.IS_POS_PAST_KWD);
else if (arg instanceof PyKeywordArgument) {
seenKeyword = true;
}
else if (arg instanceof PyStarArgument) {
seenStar = true;
}
if (seenKeyword || seenKwArg || seenStar) {
if (!(arg instanceof PyStarArgument) && (seenKwArg || !(arg instanceof PyKeywordArgument))) {
markArgument(arg, ArgFlag.IS_POS_PAST_KWD);
}
}
}
}
@@ -269,8 +269,13 @@ public class PyArgumentListImpl extends PyElementImpl implements PyArgumentList
// following the spec: http://docs.python.org/ref/calls.html
PyCallExpression call = getCallExpression();
if (call != null) {
PyCallExpression.PyMarkedCallee resolved_callee = call.resolveCallee(resolveContext, offset);
if (resolved_callee != null) ret.mapArguments(resolved_callee, resolveContext.getTypeEvalContext());
PyCallExpression.PyMarkedCallee resolvedCallee = call.resolveCallee(resolveContext, offset);
if (resolvedCallee != null) {
ret.mapArguments(resolvedCallee, resolveContext.getTypeEvalContext());
}
else {
ret.verifyArguments();
}
}
return ret;
}
@@ -0,0 +1,8 @@
kwargs = {'foo': 'bar'}
class Foo(object):
@classmethod
def test(cls):
cls(**kwargs, <error descr="Cannot appear past keyword arguments or *arg or **kwarg">foo=1</error>)
@@ -172,4 +172,8 @@ public class PyArgumentListInspectionTest extends PyTestCase {
public void testParameterWithDefaultAfterKeywordContainer() { // PY-9934
doTest();
}
public void testParameterWithDefaultAfterKeywordContainer2() { // PY-10351
doTest();
}
}