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_identif][ier()
\ 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
-N][onExistingModule.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
]