highlight keyword argument after **kwarg as error (PY-9934)

This commit is contained in:
Dmitry Jemerov
2013-10-31 21:00:18 +01:00
parent c61eb4b007
commit eb53fab850
7 changed files with 32 additions and 15 deletions
@@ -16,6 +16,7 @@
package com.jetbrains.python.inspections;
import com.intellij.codeInspection.LocalInspectionToolSession;
import com.intellij.codeInspection.ProblemHighlightType;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElementVisitor;
@@ -138,7 +139,7 @@ public class PyArgumentListInspection extends PyInspection {
holder.registerProblem(arg, PyBundle.message("INSP.duplicate.star.arg"));
}
if (flags.contains(CallArgumentsMapping.ArgFlag.IS_POS_PAST_KWD)) {
holder.registerProblem(arg, PyBundle.message("INSP.cannot.appear.past.keyword.arg"));
holder.registerProblem(arg, PyBundle.message("INSP.cannot.appear.past.keyword.arg"), ProblemHighlightType.ERROR);
}
if (flags.contains(CallArgumentsMapping.ArgFlag.IS_UNMAPPED)) {
holder.registerProblem(arg, PyBundle.message("INSP.unexpected.arg"));
@@ -180,15 +180,9 @@ public class CallArgumentsMappingImpl implements CallArgumentsMapping {
for (PyExpression arg : unmatched_subargs) {
markArgument(arg, ArgFlag.IS_UNMAPPED);
}
// mark past-bound positional args
i = positional_bound;
while (i<arguments.length) {
PyExpression arg = arguments[i];
if (!(arg instanceof PyStarArgument) && !(arg instanceof PyKeywordArgument)) {
markArgument(arg, ArgFlag.IS_POS_PAST_KWD);
}
i += 1;
}
markPastBoundPositionalArguments(arguments, positional_bound);
boolean seen_named_args = false;
// map named args to named params if possible
Map<String, PyNamedParameter> parameter_by_name = new LinkedHashMap<String, PyNamedParameter>();
@@ -352,6 +346,19 @@ public class CallArgumentsMappingImpl implements CallArgumentsMapping {
}
}
private void markPastBoundPositionalArguments(PyExpression[] arguments, int positionalBound) {
boolean seenKwArg = false;
for(int i=positionalBound; i<arguments.length; i++) {
PyExpression arg = arguments[i];
if (arg == myKwdArg) {
seenKwArg = true;
}
if (!(arg instanceof PyStarArgument) && (seenKwArg || !(arg instanceof PyKeywordArgument))) {
markArgument(arg, ArgFlag.IS_POS_PAST_KWD);
}
}
}
private static boolean isPositionalArg(PyExpression arg) {
return !(arg instanceof PyKeywordArgument) && !(arg instanceof PyStarArgument);
}
@@ -42,7 +42,7 @@ f3(1, 2, <warning descr="Unexpected argument">3</warning>)
f3(b=2, a=1)
f3(b=1, <warning descr="Duplicate argument">b=2</warning>, a=1)
f3(1, b=2)
f3(a=1, <warning descr="Cannot appear past keyword arguments or *arg or **kwarg">2</warning><warning descr="Parameter 'b' unfilled">)</warning>
f3(a=1, <error descr="Cannot appear past keyword arguments or *arg or **kwarg">2</error><warning descr="Parameter 'b' unfilled">)</warning>
def f4(a, *b):
pass
@@ -52,4 +52,4 @@ f4(1, 2)
f4(1, 2, 3)
f4(1, *(2, 3))
f4(*(1,2,3))
f4(a=1, <warning descr="Cannot appear past keyword arguments or *arg or **kwarg">2</warning>, <warning descr="Cannot appear past keyword arguments or *arg or **kwarg">3</warning>)
f4(a=1, <error descr="Cannot appear past keyword arguments or *arg or **kwarg">2</error>, <error descr="Cannot appear past keyword arguments or *arg or **kwarg">3</error>)
@@ -0,0 +1,5 @@
def foo(**kwargs): pass
two = 0
kw = {}
foo(**kw, <error descr="Cannot appear past keyword arguments or *arg or **kwarg">two=1</error>)
@@ -19,7 +19,7 @@ def f2(a, b, c=1, *d):
f2(c=3, *(1,2))
f2(1,2,3, *(1,2))
f2(*(1,2), c=20)
f2(*(1,2), <warning descr="Cannot appear past keyword arguments or *arg or **kwarg">20</warning>) # fail: positional past *
f2(*(1,2), <error descr="Cannot appear past keyword arguments or *arg or **kwarg">20</error>) # fail: positional past *
def f3(a=1, b=2, c=3, *d):
return a,b,c,d
@@ -13,7 +13,7 @@ namedpast(1,2,3, foo='a') # pass
namedpast(*args, foo='b') # pass
namedpast(foo='c') # pass
namedpast() # pass
namedpast(foo='1', <warning descr="Cannot appear past keyword arguments or *arg or **kwarg">2</warning>) # fail
namedpast(foo='1', <error descr="Cannot appear past keyword arguments or *arg or **kwarg">2</error>) # fail
def a23(a, *b, c=1):
pass
@@ -21,7 +21,7 @@ def a23(a, *b, c=1):
a23(1,2,3, c=10) # pass
a23(1,2,3, c=10, <warning descr="Duplicate argument">a=1</warning>) # fail
a23(c=10, a=1) # pass
a23(c=10, <warning descr="Cannot appear past keyword arguments or *arg or **kwarg">1</warning><warning descr="Parameter 'a' unfilled">)</warning> # fail
a23(c=10, <error descr="Cannot appear past keyword arguments or *arg or **kwarg">1</error><warning descr="Parameter 'a' unfilled">)</warning> # fail
a23(<warning descr="Multiple values resolve to positional parameter 'a'">*args</warning>, a=1) # fail
a23(*args, c=1) # pass
@@ -168,4 +168,8 @@ public class PyArgumentListInspectionTest extends PyTestCase {
public void testDictFromKeys() {
doTest();
}
public void testParameterWithDefaultAfterKeywordContainer() { // PY-9934
doTest();
}
}