From 60451cb44838b7b000e2f8f38cd4b36b5c4ceb6b Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 26 Oct 2016 20:05:31 +0300 Subject: [PATCH] PY-21088 Understand __all__.append() and __all__.extend() PyCharm used to fail to statically evaluate __all__.append() calls in typing.py and concluded that since __all__ was too dynamic to analyse it was better to assume that __all__ isn't present. It affected resolving to internal module attributes not mentioned in __all__ and even led to false positives in unused locals when these internal names were imported from another module right next to star-import from the module with __all__. --- .../jetbrains/python/psi/impl/PyFileImpl.java | 20 +++++++++++++++++-- ...tResolveToMissingNameInDynamicDunderAll.py | 5 +++++ .../m1.py | 13 ++++++++++++ .../python/PyMultiFileResolveTest.java | 5 +++++ 4 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 python/testData/resolve/multiFile/dontResolveToMissingNameInDynamicDunderAll/DontResolveToMissingNameInDynamicDunderAll.py create mode 100644 python/testData/resolve/multiFile/dontResolveToMissingNameInDynamicDunderAll/m1.py diff --git a/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java index 1fa8b886c6d6..dbb7ec3902e0 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java @@ -23,7 +23,6 @@ import com.intellij.lang.Language; import com.intellij.navigation.ItemPresentation; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.roots.ProjectFileIndex; -import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.RecursionManager; import com.intellij.openapi.util.io.FileUtil; @@ -575,7 +574,24 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression { if (callee instanceof PyQualifiedExpression) { final PyExpression qualifier = ((PyQualifiedExpression)callee).getQualifier(); if (qualifier != null && PyNames.ALL.equals(qualifier.getText())) { - // TODO handle append and extend with constant arguments here + final String calleeName = callee.getName(); + if ("append".equals(calleeName)) { + final PyStringLiteralExpression argument = node.getArgument(0, PyStringLiteralExpression.class); + if (argument != null) { + myResult.add(argument.getStringValue()); + return; + } + } + else if ("extend".equals(calleeName)) { + final PyExpression argument = node.getArgument(0, PyExpression.class); + if (argument != null) { + final List results = PyUtil.strListValue(argument); + if (results != null) { + myResult.addAll(results); + return; + } + } + } myDynamic = true; } } diff --git a/python/testData/resolve/multiFile/dontResolveToMissingNameInDynamicDunderAll/DontResolveToMissingNameInDynamicDunderAll.py b/python/testData/resolve/multiFile/dontResolveToMissingNameInDynamicDunderAll/DontResolveToMissingNameInDynamicDunderAll.py new file mode 100644 index 000000000000..e6ed3a8fc889 --- /dev/null +++ b/python/testData/resolve/multiFile/dontResolveToMissingNameInDynamicDunderAll/DontResolveToMissingNameInDynamicDunderAll.py @@ -0,0 +1,5 @@ +from m1 import * + + +print(internal_name) +# diff --git a/python/testData/resolve/multiFile/dontResolveToMissingNameInDynamicDunderAll/m1.py b/python/testData/resolve/multiFile/dontResolveToMissingNameInDynamicDunderAll/m1.py new file mode 100644 index 000000000000..7d79d318040b --- /dev/null +++ b/python/testData/resolve/multiFile/dontResolveToMissingNameInDynamicDunderAll/m1.py @@ -0,0 +1,13 @@ +__all__ = [ + 'foo', + 'bar' +] + +foo = 0 +bar = 1 + +__all__.append('quux') + +quux = 3 +internal_name = 4 + diff --git a/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java b/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java index 6d61dfaed139..13a1f32bf97f 100644 --- a/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java +++ b/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java @@ -473,6 +473,11 @@ public class PyMultiFileResolveTest extends PyMultiFileResolveTestCase { assertSameElements(doMultiResolveAndGetFileUrls("pkg/__init__.py"), "pkg/mod.py"); } + // PY-21088 + public void testDontResolveToMissingNameInDynamicDunderAll() { + assertNull(doResolve()); + } + @NotNull private List doMultiResolveAndGetFileUrls(@NotNull String currentFilePath) { myFixture.configureByFile(currentFilePath);