From 309dd061f07fb423fd952599089d4ffa781cbd4e Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Wed, 9 Nov 2016 19:28:02 +0300 Subject: [PATCH] Support type checking for positional arguments --- .../inspections/PyTypeCheckerInspection.java | 27 +++++++++++++++---- .../PositionalArguments.py | 8 ++++++ .../PyTypeCheckerInspectionTest.java | 4 +++ 3 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 python/testData/inspections/PyTypeCheckerInspection/PositionalArguments.py diff --git a/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java b/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java index 0ff3c667f448..bef8c9c0f5ba 100644 --- a/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java @@ -31,6 +31,7 @@ import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; import com.jetbrains.python.documentation.PythonDocumentationProvider; import com.jetbrains.python.inspections.quickfix.PyMakeFunctionReturnTypeQuickFix; import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.types.*; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; @@ -203,19 +204,19 @@ public class PyTypeCheckerInspection extends PyInspection { for (Map.Entry entry : mapping.entrySet()) { final PyNamedParameter param = entry.getValue(); final PyExpression arg = entry.getKey(); - if (param.isPositionalContainer() || param.isKeywordContainer()) { + if (param.isKeywordContainer()) { continue; } - final PyType paramType = myTypeEvalContext.getType(param); - if (paramType == null) { + final PyType expectedArgType = getExpectedArgumentType(param); + if (expectedArgType == null) { continue; } - final PyType argType = myTypeEvalContext.getType(arg); + final PyType actualArgType = myTypeEvalContext.getType(arg); if (!genericsCollected) { substitutions.putAll(PyTypeChecker.unifyReceiver(receiver, myTypeEvalContext)); genericsCollected = true; } - final Pair problem = checkTypes(paramType, argType, myTypeEvalContext, substitutions); + final Pair problem = checkTypes(expectedArgType, actualArgType, myTypeEvalContext, substitutions); if (problem != null) { problems.put(arg, problem); } @@ -223,6 +224,22 @@ public class PyTypeCheckerInspection extends PyInspection { return problems; } + @Nullable + private PyType getExpectedArgumentType(@NotNull PyNamedParameter param) { + final PyType paramType = myTypeEvalContext.getType(param); + + if (param.isPositionalContainer()) { + if (paramType == PyBuiltinCache.getInstance(param).getTupleType()) { + return null; + } + else if (paramType instanceof PyCollectionType) { + return ((PyCollectionType)paramType).getIteratedItemType(); + } + } + + return paramType; + } + @Nullable private static Pair checkTypes(@Nullable PyType expected, @Nullable PyType actual, diff --git a/python/testData/inspections/PyTypeCheckerInspection/PositionalArguments.py b/python/testData/inspections/PyTypeCheckerInspection/PositionalArguments.py new file mode 100644 index 000000000000..1d2eb2d5daf7 --- /dev/null +++ b/python/testData/inspections/PyTypeCheckerInspection/PositionalArguments.py @@ -0,0 +1,8 @@ +def foo(*args): + """ + :type args: tuple[str] + """ + pass + + +foo(1, '1') diff --git a/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java index b73f19a9c8ed..32f4f7ff39a1 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java @@ -346,4 +346,8 @@ public class PyTypeCheckerInspectionTest extends PyTestCase { public void testMapArgumentsInOppositeOrder() { doTest(); } + + public void testPositionalArguments() { + doTest(); + } }