From a6f9e823e744ca7ee9052fd5798051291d6e76e4 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Fri, 12 Aug 2011 16:26:25 +0200 Subject: [PATCH] implicit resolve works also for instance attributes (PY-4292) --- python/src/META-INF/python-plugin-common.xml | 1 + .../psi/impl/PyQualifiedReferenceImpl.java | 25 +++++++++++++++--- .../stubs/PyTargetExpressionElementType.java | 18 ++++++++----- .../psi/stubs/PyInstanceAttributeIndex.java | 26 +++++++++++++++++++ .../ImplicitResolveInstanceAttribute.py | 8 ++++++ python/testData/resolve/TextBasedResolve.py | 3 ++- .../resolve/ToConstructorInherited.py | 3 ++- python/testData/resolve/UnresolvedImport.py | 3 ++- .../com/jetbrains/python/PyResolveTest.java | 17 +++++++++--- 9 files changed, 88 insertions(+), 16 deletions(-) create mode 100644 python/src/com/jetbrains/python/psi/stubs/PyInstanceAttributeIndex.java create mode 100644 python/testData/resolve/ImplicitResolveInstanceAttribute.py diff --git a/python/src/META-INF/python-plugin-common.xml b/python/src/META-INF/python-plugin-common.xml index a1037056466f..bb5864375fbf 100644 --- a/python/src/META-INF/python-plugin-common.xml +++ b/python/src/META-INF/python-plugin-common.xml @@ -66,6 +66,7 @@ + diff --git a/python/src/com/jetbrains/python/psi/impl/PyQualifiedReferenceImpl.java b/python/src/com/jetbrains/python/psi/impl/PyQualifiedReferenceImpl.java index 8f8479172624..d2f3ea9f1f44 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyQualifiedReferenceImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyQualifiedReferenceImpl.java @@ -21,6 +21,7 @@ import com.jetbrains.python.psi.patterns.SyntaxMatchers; import com.jetbrains.python.psi.resolve.*; import com.jetbrains.python.psi.stubs.PyClassNameIndexInsensitive; import com.jetbrains.python.psi.stubs.PyFunctionNameIndex; +import com.jetbrains.python.psi.stubs.PyInstanceAttributeIndex; import com.jetbrains.python.psi.types.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -80,9 +81,19 @@ public class PyQualifiedReferenceImpl extends PyReferenceImpl { } PyFunction pyFunction = (PyFunction) function; if (pyFunction.getContainingClass() != null) { - ret.add(new ImplicitResolveResult(pyFunction, getFunctionRate(pyFunction))); + ret.add(new ImplicitResolveResult(pyFunction, getImplicitResultRate(pyFunction))); } } + + final Collection attributes = PyInstanceAttributeIndex.find(referencedName, myElement.getProject()); + for (Object attribute : attributes) { + if (!(attribute instanceof PyTargetExpression)) { + FileBasedIndex.getInstance().scheduleRebuild(StubUpdatingIndex.INDEX_ID, + new Throwable("found non-target expression object " + attribute + " in target expression list")); + break; + } + ret.add(new ImplicitResolveResult((PyTargetExpression) attribute, getImplicitResultRate((PyTargetExpression)attribute))); + } } // special case of __doc__ if ("__doc__".equals(referencedName)) { @@ -91,17 +102,23 @@ public class PyQualifiedReferenceImpl extends PyReferenceImpl { return ret; } - private int getFunctionRate(PyFunction pyFunction) { + private int getImplicitResultRate(PyElement target) { int rate = RatedResolveResult.RATE_LOW; - if (pyFunction.getContainingFile() == myElement.getContainingFile()) { + if (target.getContainingFile() == myElement.getContainingFile()) { rate += 200; } else { - final VirtualFile vFile = pyFunction.getContainingFile().getVirtualFile(); + final VirtualFile vFile = target.getContainingFile().getVirtualFile(); if (vFile != null && ProjectScope.getProjectScope(myElement.getProject()).contains(vFile)) { rate += 80; } } + if (myElement.getParent() instanceof PyCallExpression) { + if (target instanceof PyFunction) rate += 50; + } + else { + if (!(target instanceof PyFunction)) rate += 50; + } return rate; } diff --git a/python/src/com/jetbrains/python/psi/impl/stubs/PyTargetExpressionElementType.java b/python/src/com/jetbrains/python/psi/impl/stubs/PyTargetExpressionElementType.java index 1d5f949942a2..0c544c727e87 100644 --- a/python/src/com/jetbrains/python/psi/impl/stubs/PyTargetExpressionElementType.java +++ b/python/src/com/jetbrains/python/psi/impl/stubs/PyTargetExpressionElementType.java @@ -14,9 +14,7 @@ import com.jetbrains.python.PyElementTypes; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyQualifiedName; import com.jetbrains.python.psi.impl.PyTargetExpressionImpl; -import com.jetbrains.python.psi.stubs.PyFileStub; -import com.jetbrains.python.psi.stubs.PyTargetExpressionStub; -import com.jetbrains.python.psi.stubs.PyVariableNameIndex; +import com.jetbrains.python.psi.stubs.*; import java.io.IOException; @@ -121,14 +119,22 @@ public class PyTargetExpressionElementType extends PyStubElementType { + public static final StubIndexKey KEY = StubIndexKey.createIndexKey("Py.instanceAttribute.name"); + + @Override + public StubIndexKey getKey() { + return KEY; + } + + public static Collection find(String name, Project project) { + return StubIndex.getInstance().get(KEY, name, project, ProjectScope.getAllScope(project)); + } +} diff --git a/python/testData/resolve/ImplicitResolveInstanceAttribute.py b/python/testData/resolve/ImplicitResolveInstanceAttribute.py new file mode 100644 index 000000000000..2b53442d56e6 --- /dev/null +++ b/python/testData/resolve/ImplicitResolveInstanceAttribute.py @@ -0,0 +1,8 @@ +class MyObject: + def __init__(self): + self.xyzzy = None + + +def foo(p): + print p.xyzzy +# diff --git a/python/testData/resolve/TextBasedResolve.py b/python/testData/resolve/TextBasedResolve.py index 0bae8e0092aa..0a332f60dcde 100644 --- a/python/testData/resolve/TextBasedResolve.py +++ b/python/testData/resolve/TextBasedResolve.py @@ -2,4 +2,5 @@ class A: def long_unique_identifier(self): pass def foo(x): - x.long_unique_identifier() \ No newline at end of file + x.long_unique_identifier() +# \ No newline at end of file diff --git a/python/testData/resolve/ToConstructorInherited.py b/python/testData/resolve/ToConstructorInherited.py index 47d290e14c56..95ade209d1b4 100644 --- a/python/testData/resolve/ToConstructorInherited.py +++ b/python/testData/resolve/ToConstructorInherited.py @@ -6,4 +6,5 @@ class Foo: class Bar(Foo): pass -Bar() +Bar() +# \ No newline at end of file diff --git a/python/testData/resolve/UnresolvedImport.py b/python/testData/resolve/UnresolvedImport.py index 2e6cbfc163fd..d44d2ffa3f48 100644 --- a/python/testData/resolve/UnresolvedImport.py +++ b/python/testData/resolve/UnresolvedImport.py @@ -1,2 +1,3 @@ import NonExistingModule -NonExistingModule.foo() +NonExistingModule.foo() +# diff --git a/python/testSrc/com/jetbrains/python/PyResolveTest.java b/python/testSrc/com/jetbrains/python/PyResolveTest.java index 5d5d0df7b060..05045408bb78 100644 --- a/python/testSrc/com/jetbrains/python/PyResolveTest.java +++ b/python/testSrc/com/jetbrains/python/PyResolveTest.java @@ -13,10 +13,14 @@ import com.jetbrains.python.psi.resolve.ImportedResolveResult; public class PyResolveTest extends PyResolveTestCase { @Override protected PsiElement doResolve() { + final PsiReference ref = findReferenceByMarker(); + return ref.resolve(); + } + + private PsiReference findReferenceByMarker() { myFixture.configureByFile("resolve/" + getTestName(false) + ".py"); int offset = findMarkerOffset(myFixture.getFile()); - final PsiReference ref = myFixture.getFile().findReferenceAt(offset); - return ref.resolve(); + return myFixture.getFile().findReferenceAt(offset); } protected PsiElement resolve() { @@ -26,7 +30,7 @@ public class PyResolveTest extends PyResolveTestCase { } private ResolveResult[] multiResolve() { - PsiReference ref = configureByFile("resolve/" + getTestName(false) + ".py"); + PsiReference ref = findReferenceByMarker(); assertTrue(ref instanceof PsiPolyVariantReference); return ((PsiPolyVariantReference)ref).multiResolve(false); } @@ -421,4 +425,11 @@ public class PyResolveTest extends PyResolveTestCase { public void testKeywordArgument() { assertResolvesTo(PyNamedParameter.class, "bar"); } + + public void testImplicitResolveInstanceAttribute() { + ResolveResult[] resolveResults = multiResolve(); + assertEquals(1, resolveResults.length); + final PsiElement psiElement = resolveResults[0].getElement(); + assertTrue(psiElement instanceof PyTargetExpression && "xyzzy".equals(((PyTargetExpression)psiElement).getName())); + } } \ No newline at end of file