From 7fb9772003bd1d2a5df62b52471b218cb682656c Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Mon, 13 Jan 2014 16:24:55 +0400 Subject: [PATCH] fixed PY-11800 Parameter unfilled false positive for decorators with '*' arguments. --- .../inspections/PyArgumentListInspection.java | 49 +++++++++---------- .../python/psi/impl/PyDecoratorImpl.java | 4 +- .../decoratorsPy3K.py | 7 +++ .../PyArgumentListInspectionTest.java | 10 ++++ 4 files changed, 42 insertions(+), 28 deletions(-) create mode 100644 python/testData/inspections/PyArgumentListInspection/decoratorsPy3K.py diff --git a/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java b/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java index 97a5a365f2ea..6faea31eabcb 100644 --- a/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java @@ -68,32 +68,29 @@ public class PyArgumentListInspection extends PyInspection { @Override public void visitPyDecoratorList(final PyDecoratorList node) { - PyDecorator[] decos = node.getDecorators(); - for (PyDecorator deco : decos) { - if (! deco.hasArgumentList()) { - // empty arglist; deco function must have a non-kwarg first arg - PyCallExpression.PyMarkedCallee mkfunc = deco.resolveCallee(resolveWithoutImplicits()); - if (mkfunc != null && !mkfunc.isImplicitlyResolved()) { - Callable callable = mkfunc.getCallable(); - int first_param_offset = mkfunc.getImplicitOffset(); - final List params = PyUtil.getParameters(callable, myTypeEvalContext); - final PyNamedParameter alleged_first_param = params.size() < first_param_offset ? - null : params.get(first_param_offset-1).getAsNamed(); - if (alleged_first_param == null || alleged_first_param.isKeywordContainer()) { - // no parameters left to pass function implicitly, or wrong param type - registerProblem(deco, PyBundle.message("INSP.func.$0.lacks.first.arg", callable.getName())); // TODO: better names for anon lambdas - } - else { - // possible unfilled params - for (int i=first_param_offset; i < params.size(); i += 1) { - PyNamedParameter par = params.get(i).getAsNamed(); - // param tuples, non-starred or non-default won't do - if (par == null || (! par.isKeywordContainer() && ! par.isPositionalContainer() && !par.hasDefaultValue())) { - String par_name; - if (par != null) par_name = par.getName(); - else par_name = "(...)"; // can't be bothered to find the first non-tuple inside it - registerProblem(deco, PyBundle.message("INSP.parameter.$0.unfilled", par_name)); - } + PyDecorator[] decorators = node.getDecorators(); + for (PyDecorator deco : decorators) { + if (deco.hasArgumentList()) continue; + final PyCallExpression.PyMarkedCallee markedCallee = deco.resolveCallee(resolveWithoutImplicits()); + if (markedCallee != null && !markedCallee.isImplicitlyResolved()) { + final Callable callable = markedCallee.getCallable(); + int firstParamOffset = markedCallee.getImplicitOffset(); + final List params = PyUtil.getParameters(callable, myTypeEvalContext); + final PyNamedParameter allegedFirstParam = params.size() < firstParamOffset ? + null : params.get(firstParamOffset-1).getAsNamed(); + if (allegedFirstParam == null || allegedFirstParam.isKeywordContainer()) { + // no parameters left to pass function implicitly, or wrong param type + registerProblem(deco, PyBundle.message("INSP.func.$0.lacks.first.arg", callable.getName())); // TODO: better names for anon lambdas + } + else { // possible unfilled params + for (int i = firstParamOffset; i < params.size(); i += 1) { + final PyParameter parameter = params.get(i); + if (parameter instanceof PySingleStarParameter) continue; + final PyNamedParameter par = parameter.getAsNamed(); + // param tuples, non-starred or non-default won't do + if (par == null || (!par.isKeywordContainer() && !par.isPositionalContainer() &&!par.hasDefaultValue())) { + String parameterName = par != null ? par.getName() : "(...)"; + registerProblem(deco, PyBundle.message("INSP.parameter.$0.unfilled", parameterName)); } } } diff --git a/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java b/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java index f15a664fcbba..f64b533f3303 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java @@ -74,8 +74,8 @@ public class PyDecoratorImpl extends StubBasedPsiElementBase im } public boolean hasArgumentList() { - ASTNode arglist_node = getNode().findChildByType(PyElementTypes.ARGUMENT_LIST); - return (arglist_node != null) && (arglist_node.findChildByType(PyTokenTypes.LPAR) != null); + final ASTNode arglistNode = getNode().findChildByType(PyElementTypes.ARGUMENT_LIST); + return (arglistNode != null) && (arglistNode.findChildByType(PyTokenTypes.LPAR) != null); } public QualifiedName getQualifiedName() { diff --git a/python/testData/inspections/PyArgumentListInspection/decoratorsPy3K.py b/python/testData/inspections/PyArgumentListInspection/decoratorsPy3K.py new file mode 100644 index 000000000000..07e9942700b1 --- /dev/null +++ b/python/testData/inspections/PyArgumentListInspection/decoratorsPy3K.py @@ -0,0 +1,7 @@ + +def deco(func, *args): + return func + +@deco # <= Here is a false positive. +def myfunc(a, b): + print(a, b) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java index fabba0c2458f..c0b20d80a542 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java @@ -34,6 +34,16 @@ public class PyArgumentListInspectionTest extends PyTestCase { public void testDecorators() { doTest(); } + + public void testDecoratorsPy3K() { + PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), LanguageLevel.PYTHON30); + try { + doTest(); + } + finally { + PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), null); + } + } public void testTupleVsLiteralList() { doTest();