diff --git a/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java index 8a6b92d87cdf..e567f497ca00 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java @@ -67,15 +67,13 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression { private ExportedNameCache(long modificationStamp) { myModificationStamp = modificationStamp; - processDeclarations(PyPsiUtils.collectAllStubChildren(PyFileImpl.this, getStub()), element -> { - if (element instanceof PsiNamedElement && !(element instanceof PyKeywordArgument)) { + final StubElement stub = getStub(); + processDeclarations(PyPsiUtils.collectAllStubChildren(PyFileImpl.this, stub), element -> { + if (element instanceof PsiNamedElement && + !(element instanceof PyKeywordArgument) && + !(stub == null && element.getParent() instanceof PyImportElement)) { final PsiNamedElement namedElement = (PsiNamedElement)element; - final String name = namedElement.getName(); - if (!myNamedElements.containsKey(name)) { - myNamedElements.put(name, Lists.newArrayList()); - } - final List elements = myNamedElements.get(name); - elements.add(namedElement); + myNamedElements.computeIfAbsent(namedElement.getName(), __ -> new ArrayList<>()).add(namedElement); } if (element instanceof PyImportedNameDefiner) { myImportedNameDefiners.add((PyImportedNameDefiner)element); @@ -758,13 +756,6 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression { return newElement; } - private static class ArrayListThreadLocal extends ThreadLocal> { - @Override - protected List initialValue() { - return new ArrayList<>(); - } - } - @Override public ItemPresentation getPresentation() { return new ItemPresentation() { diff --git a/python/testData/codeInsight/classMRO/ClassImportedFromUnstubbedFileAndSuperImportedWithAs/A.py b/python/testData/codeInsight/classMRO/ClassImportedFromUnstubbedFileAndSuperImportedWithAs/A.py new file mode 100644 index 000000000000..25fc6b72ad0b --- /dev/null +++ b/python/testData/codeInsight/classMRO/ClassImportedFromUnstubbedFileAndSuperImportedWithAs/A.py @@ -0,0 +1,3 @@ +class A(object): + def __init__(self, x): # <- Has argument x + self.x = x diff --git a/python/testData/codeInsight/classMRO/ClassImportedFromUnstubbedFileAndSuperImportedWithAs/BC.py b/python/testData/codeInsight/classMRO/ClassImportedFromUnstubbedFileAndSuperImportedWithAs/BC.py new file mode 100644 index 000000000000..9675f4d71dfa --- /dev/null +++ b/python/testData/codeInsight/classMRO/ClassImportedFromUnstubbedFileAndSuperImportedWithAs/BC.py @@ -0,0 +1,9 @@ +from A import A as myA # No problem without the 'as' + + +class B(object): + pass + + +class C(myA, B): # No problem when only inheriting from myA + pass diff --git a/python/testData/codeInsight/classMRO/ClassImportedFromUnstubbedFileAndSuperImportedWithAs/D.py b/python/testData/codeInsight/classMRO/ClassImportedFromUnstubbedFileAndSuperImportedWithAs/D.py new file mode 100644 index 000000000000..413b0860b3e7 --- /dev/null +++ b/python/testData/codeInsight/classMRO/ClassImportedFromUnstubbedFileAndSuperImportedWithAs/D.py @@ -0,0 +1,10 @@ +from BC import C # No problem if D is also in BC.py + + +class D(C): + def __init__(self, x): + C.__init__(self, x) # <- "Unexpected argument" warning for x + + +d = D(4) +assert d.x == 4 # runs fine diff --git a/python/testSrc/com/jetbrains/python/codeInsight/PyClassMROTest.java b/python/testSrc/com/jetbrains/python/codeInsight/PyClassMROTest.java index 45dcd0f709b3..e93969dffc4b 100644 --- a/python/testSrc/com/jetbrains/python/codeInsight/PyClassMROTest.java +++ b/python/testSrc/com/jetbrains/python/codeInsight/PyClassMROTest.java @@ -15,10 +15,15 @@ */ package com.jetbrains.python.codeInsight; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiFile; import com.intellij.util.ArrayUtil; +import com.intellij.util.containers.ContainerUtil; +import com.jetbrains.python.PyNames; import com.jetbrains.python.fixtures.PyTestCase; import com.jetbrains.python.psi.LanguageLevel; import com.jetbrains.python.psi.PyClass; +import com.jetbrains.python.psi.PyFile; import com.jetbrains.python.psi.types.PyClassLikeType; import com.jetbrains.python.psi.types.PyClassType; import com.jetbrains.python.psi.types.PyType; @@ -162,8 +167,28 @@ public class PyClassMROTest extends PyTestCase { }); } + // PY-21837 + public void testClassImportedFromUnstubbedFileAndSuperImportedWithAs() { + myFixture.copyDirectoryToProject("codeInsight/classMRO/" + getTestName(false), ""); + + final VirtualFile d = myFixture.findFileInTempDir("D.py"); + final VirtualFile bc = myFixture.findFileInTempDir("BC.py"); + + final PyFile dPsi = (PyFile)myFixture.getPsiManager().findFile(d); + final PsiFile bPsi = myFixture.getPsiManager().findFile(bc); + + //noinspection ResultOfMethodCallIgnored + bPsi.getNode(); // unstubbing is necessary + + final PyClass dClass = dPsi.findTopLevelClass("D"); + final TypeEvalContext context = TypeEvalContext.codeAnalysis(myFixture.getProject(), dPsi); // such context is necessary + final List ancestors = dClass.getAncestorClasses(context); + + assertOrderedEquals(ContainerUtil.map(ancestors, PyClass::getName), Arrays.asList("C", "A", "B", PyNames.OBJECT)); + } + @NotNull - public PyClass getClass(@NotNull String name) { + private PyClass getClass(@NotNull String name) { myFixture.configureByFile(getPath(getTestName(false))); final PyClass cls = myFixture.findElementByText(name, PyClass.class); assertNotNull(cls);