From dd36bc80173987306ba66e614beb5ba2369a2204 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 2 Mar 2010 19:16:41 +0300 Subject: [PATCH] resolve to instance varaibles not only in constructor (PY-281) --- .../python/psi/PyFileElementType.java | 2 +- .../python/psi/impl/PyClassImpl.java | 82 +++++++++++++------ .../stubs/PyTargetExpressionElementType.java | 19 ++--- python/testData/resolve/FieldNotInInit.py | 7 ++ .../com/jetbrains/python/PyResolveTest.java | 5 ++ 5 files changed, 78 insertions(+), 37 deletions(-) create mode 100644 python/testData/resolve/FieldNotInInit.py diff --git a/python/src/com/jetbrains/python/psi/PyFileElementType.java b/python/src/com/jetbrains/python/psi/PyFileElementType.java index 859e350d4842..07246eff3e72 100644 --- a/python/src/com/jetbrains/python/psi/PyFileElementType.java +++ b/python/src/com/jetbrains/python/psi/PyFileElementType.java @@ -24,7 +24,7 @@ public class PyFileElementType extends IStubFileElementType { @Override public int getStubVersion() { - return 10; + return 11; } @Override diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java index 795b8db05809..2ecfd44342d5 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -13,10 +13,10 @@ import com.jetbrains.python.PyElementTypes; import com.jetbrains.python.PyNames; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.PythonDocStringFinder; +import com.jetbrains.python.codeInsight.controlflow.PyControlFlowBuilder; import com.jetbrains.python.codeInsight.dataflow.scope.Scope; import com.jetbrains.python.codeInsight.dataflow.scope.impl.ScopeImpl; import com.jetbrains.python.psi.*; -import com.jetbrains.python.codeInsight.controlflow.PyControlFlowBuilder; import com.jetbrains.python.psi.resolve.PyResolveUtil; import com.jetbrains.python.psi.resolve.VariantsProcessor; import com.jetbrains.python.psi.stubs.PyClassStub; @@ -32,9 +32,10 @@ import java.util.*; * @author yole */ public class PyClassImpl extends PyPresentableElementImpl implements PyClass { - public static final PyClass[] EMPTY_ARRAY = new PyClassImpl[0]; + private PyTargetExpression[] myInstanceAttributes; + public PyClassImpl(ASTNode astNode) { super(astNode); } @@ -220,33 +221,65 @@ public class PyClassImpl extends PyPresentableElementImpl implement } public PyTargetExpression[] getInstanceAttributes() { - PyFunctionImpl initMethod = (PyFunctionImpl) findMethodByName(PyNames.INIT, false); - if (initMethod == null) return PyTargetExpression.EMPTY_ARRAY; - final PyParameter[] params = initMethod.getParameterList().getParameters(); - if (params.length == 0) return PyTargetExpression.EMPTY_ARRAY; + if (myInstanceAttributes == null) { + myInstanceAttributes = collectInstanceAttributes(); + } + return myInstanceAttributes; + } - final PyFunctionStub methodStub = initMethod.getStub(); - if (methodStub != null) { - return methodStub.getChildrenByType(PyElementTypes.TARGET_EXPRESSION, PyTargetExpression.EMPTY_ARRAY); + private PyTargetExpression[] collectInstanceAttributes() { + Map result = new HashMap(); + + // __init__ takes priority over all other methods + PyFunctionImpl initMethod = (PyFunctionImpl)findMethodByName(PyNames.INIT, false); + if (initMethod != null) { + collectInstanceAttributes(initMethod, result); + } + final PyFunction[] methods = getMethods(); + for (PyFunction method : methods) { + if (!PyNames.INIT.equals(method.getName())) { + collectInstanceAttributes((PyFunctionImpl)method, result); + } } - final List result = new ArrayList(); - // NOTE: maybe treeCrawlUp would be more precise, but currently it works well enough; don't care. - initMethod.getStatementList().accept(new PyRecursiveElementVisitor() { - public void visitPyAssignmentStatement(final PyAssignmentStatement node) { - super.visitPyAssignmentStatement(node); - final PyExpression[] targets = node.getTargets(); - for(PyExpression target: targets) { - if (target instanceof PyTargetExpression) { - PyExpression qualifier = ((PyTargetExpression) target).getQualifier(); - if (qualifier != null && qualifier.getText().equals(params [0].getName())) { - result.add((PyTargetExpression)target); + final Collection expressions = result.values(); + return expressions.toArray(new PyTargetExpression[expressions.size()]); + } + + private static void collectInstanceAttributes(PyFunctionImpl method, final Map result) { + final PyParameter[] params = method.getParameterList().getParameters(); + if (params.length == 0) { + return; + } + final String selfName = params [0].getName(); + + final PyFunctionStub methodStub = method.getStub(); + if (methodStub != null) { + final PyTargetExpression[] targets = methodStub.getChildrenByType(PyElementTypes.TARGET_EXPRESSION, PyTargetExpression.EMPTY_ARRAY); + for (PyTargetExpression target : targets) { + if (!result.containsKey(target.getName())) { + result.put(target.getName(), target); + } + } + } + else { + // NOTE: maybe treeCrawlUp would be more precise, but currently it works well enough; don't care. + method.getStatementList().accept(new PyRecursiveElementVisitor() { + public void visitPyAssignmentStatement(final PyAssignmentStatement node) { + super.visitPyAssignmentStatement(node); + final PyExpression[] targets = node.getTargets(); + for (PyExpression target : targets) { + if (target instanceof PyTargetExpression) { + final PyTargetExpression targetExpr = (PyTargetExpression)target; + PyExpression qualifier = targetExpr.getQualifier(); + if (qualifier != null && qualifier.getText().equals(selfName) && !result.containsKey(targetExpr.getName())) { + result.put(targetExpr.getName(), targetExpr); + } } } } - } - }); - return result.toArray(new PyTargetExpression[result.size()]); + }); + } } public boolean isNewStyleClass() { @@ -313,6 +346,9 @@ public class PyClassImpl extends PyPresentableElementImpl implement if (myScopeRef != null){ myScopeRef.clear(); } + if (myInstanceAttributes != null) { + myInstanceAttributes = null; + } } private SoftReference myControlFlowRef; 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 6230396f9dc6..c49afeb28278 100644 --- a/python/src/com/jetbrains/python/psi/impl/stubs/PyTargetExpressionElementType.java +++ b/python/src/com/jetbrains/python/psi/impl/stubs/PyTargetExpressionElementType.java @@ -4,14 +4,10 @@ import com.intellij.lang.ASTNode; import com.intellij.psi.PsiElement; import com.intellij.psi.impl.source.tree.TreeUtil; import com.intellij.psi.stubs.StubElement; -import com.intellij.psi.stubs.StubOutputStream; import com.intellij.psi.stubs.StubInputStream; -import com.intellij.util.io.DataInputOutputUtil; -import com.intellij.util.io.PersistentStringEnumerator; +import com.intellij.psi.stubs.StubOutputStream; import com.intellij.util.io.StringRef; import com.jetbrains.python.PyElementTypes; -import com.jetbrains.python.PyNames; -import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.psi.PyStubElementType; import com.jetbrains.python.psi.PyTargetExpression; import com.jetbrains.python.psi.impl.PyTargetExpressionImpl; @@ -54,14 +50,11 @@ public class PyTargetExpressionElementType extends PyStubElementType 0 && children [0].getText().equals(qualifierNode.getText())) { - return true; - } + final ASTNode parameterList = functionNode.findChildByType(PyElementTypes.PARAMETER_LIST); + assert parameterList != null; + final ASTNode[] children = parameterList.getChildren(PyElementTypes.FORMAL_PARAMETER_SET); + if (children.length > 0 && children [0].getText().equals(qualifierNode.getText())) { + return true; } } return qualifierNode == null; diff --git a/python/testData/resolve/FieldNotInInit.py b/python/testData/resolve/FieldNotInInit.py new file mode 100644 index 000000000000..6651821d642c --- /dev/null +++ b/python/testData/resolve/FieldNotInInit.py @@ -0,0 +1,7 @@ +class Foo(object): + def initialize(self): + self.request = 'a' + + def bar(self): + print self.request + \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyResolveTest.java b/python/testSrc/com/jetbrains/python/PyResolveTest.java index 7a91764a32ec..d3556ba60ebc 100644 --- a/python/testSrc/com/jetbrains/python/PyResolveTest.java +++ b/python/testSrc/com/jetbrains/python/PyResolveTest.java @@ -211,4 +211,9 @@ public class PyResolveTest extends PyResolveTestCase { PsiElement targetElement = resolve(); assertNull(targetElement); } + + public void testFieldNotInInit() throws Exception { + PsiElement targetElement = resolve(); + assertTrue(targetElement instanceof PyTargetExpression); + } } \ No newline at end of file