From bb91645b3650de9238bd6d768022d3c0e473ffda Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Tue, 22 Jan 2013 18:00:02 +0400 Subject: [PATCH] Completion for classes with non-class expressions in their base class list (PY-4345) --- .../python/psi/types/PyClassTypeImpl.java | 34 ++++++++++++++----- .../completion/namedTupleBaseClass.after.py | 10 ++++++ .../completion/namedTupleBaseClass.py | 10 ++++++ .../jetbrains/python/Py3CompletionTest.java | 4 +++ 4 files changed, 50 insertions(+), 8 deletions(-) create mode 100644 python/testData/completion/namedTupleBaseClass.after.py create mode 100644 python/testData/completion/namedTupleBaseClass.py diff --git a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java index 6c4867540f90..7dfd2861482f 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java @@ -11,6 +11,7 @@ import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.UserDataHolderBase; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiInvalidElementAccessException; +import com.intellij.psi.PsiReference; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.ProcessingContext; @@ -369,16 +370,33 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { Set namesAlready, ProcessingContext context, List ret) { - for (PyClass ancestor : myClass.getSuperClasses()) { - Object[] ancestry = (new PyClassTypeImpl(ancestor, myIsDefinition)).getCompletionVariants(name, expressionHook, context); - for (Object ob : ancestry) { - String inheritedName = ob.toString(); - if (!namesAlready.contains(inheritedName) && !isClassPrivate(inheritedName)) { - ret.add(ob); - namesAlready.add(inheritedName); + for (PyExpression expression : myClass.getSuperClassExpressions()) { + final PsiReference reference = expression.getReference(); + PsiElement element = null; + if (reference != null) { + element = reference.resolve(); + } + PyType type; + if (element instanceof PyClass) { + type = new PyClassTypeImpl((PyClass)element, myIsDefinition); + } + else { + type = expression.getType(TypeEvalContext.fastStubOnly(myClass.getContainingFile())); + if (type instanceof PyClassType && !myIsDefinition) { + type = ((PyClassType)type).toInstance(); } } - ContainerUtil.addAll(ret, ancestry); + if (type != null) { + Object[] ancestry = type.getCompletionVariants(name, expressionHook, context); + for (Object ob : ancestry) { + String inheritedName = ob.toString(); + if (!namesAlready.contains(inheritedName) && !isClassPrivate(inheritedName)) { + ret.add(ob); + namesAlready.add(inheritedName); + } + } + ContainerUtil.addAll(ret, ancestry); + } } } diff --git a/python/testData/completion/namedTupleBaseClass.after.py b/python/testData/completion/namedTupleBaseClass.after.py new file mode 100644 index 000000000000..01aaf1402b40 --- /dev/null +++ b/python/testData/completion/namedTupleBaseClass.after.py @@ -0,0 +1,10 @@ +from collections import namedtuple + + +class C(namedtuple('Coord', 'latitude longitude')): + def foo(self): + return -1 + + +c = C() +c.latitude diff --git a/python/testData/completion/namedTupleBaseClass.py b/python/testData/completion/namedTupleBaseClass.py new file mode 100644 index 000000000000..eae904970e05 --- /dev/null +++ b/python/testData/completion/namedTupleBaseClass.py @@ -0,0 +1,10 @@ +from collections import namedtuple + + +class C(namedtuple('Coord', 'latitude longitude')): + def foo(self): + return -1 + + +c = C() +c.lat diff --git a/python/testSrc/com/jetbrains/python/Py3CompletionTest.java b/python/testSrc/com/jetbrains/python/Py3CompletionTest.java index 88306fd3658b..1b635b9f7ea2 100644 --- a/python/testSrc/com/jetbrains/python/Py3CompletionTest.java +++ b/python/testSrc/com/jetbrains/python/Py3CompletionTest.java @@ -33,6 +33,10 @@ public class Py3CompletionTest extends PyTestCase { assertTrue(strings.contains("long")); } + public void testNamedTupleBaseClass() { + doTest(); + } + private void doTest() { CamelHumpMatcher.forceStartMatching(getTestRootDisposable()); final String testName = "completion/" + getTestName(true);