resolve to instance varaibles not only in constructor (PY-281)

This commit is contained in:
Dmitry Jemerov
2010-03-02 20:01:42 +03:00
parent d726d88695
commit dd36bc8017
5 changed files with 78 additions and 37 deletions
@@ -24,7 +24,7 @@ public class PyFileElementType extends IStubFileElementType {
@Override
public int getStubVersion() {
return 10;
return 11;
}
@Override
@@ -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<PyClassStub> 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<PyClassStub> 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<String, PyTargetExpression> result = new HashMap<String, PyTargetExpression>();
// __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<PyTargetExpression> result = new ArrayList<PyTargetExpression>();
// 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<PyTargetExpression> expressions = result.values();
return expressions.toArray(new PyTargetExpression[expressions.size()]);
}
private static void collectInstanceAttributes(PyFunctionImpl method, final Map<String, PyTargetExpression> 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<PyClassStub> implement
if (myScopeRef != null){
myScopeRef.clear();
}
if (myInstanceAttributes != null) {
myInstanceAttributes = null;
}
}
private SoftReference<ControlFlow> myControlFlowRef;
@@ -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<PyTargetExp
final ASTNode functionNode = TreeUtil.findParent(node, PyElementTypes.FUNCTION_DECLARATION);
final ASTNode qualifierNode = node.findChildByType(PyElementTypes.TARGET_EXPRESSION);
if (functionNode != null && qualifierNode != null) {
final ASTNode nameNode = functionNode.findChildByType(PyTokenTypes.IDENTIFIER);
if (nameNode != null && PyNames.INIT.equals(nameNode.getText())) {
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;
}
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;
@@ -0,0 +1,7 @@
class Foo(object):
def initialize(self):
self.request = 'a'
def bar(self):
print self.r<ref>equest
@@ -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);
}
}