mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
fixed PY-11800 Parameter unfilled false positive for decorators with '*' arguments.
This commit is contained in:
@@ -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<PyParameter> 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<PyParameter> 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,8 +74,8 @@ public class PyDecoratorImpl extends StubBasedPsiElementBase<PyDecoratorStub> 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() {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
def deco(func, *args):
|
||||
return func
|
||||
|
||||
@deco # <= Here is a false positive.
|
||||
def myfunc(a, b):
|
||||
print(a, b)
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user