Multiple targets support fixed in followAssignmentsChain(); some tests added.

This commit is contained in:
Dmitry Cheryasov
2009-11-11 06:55:40 +02:00
parent 540e705834
commit ea9b154d07
7 changed files with 60 additions and 27 deletions

View File

@@ -621,10 +621,9 @@ public class PyUtil {
else if (ret == null && elt instanceof PyElement) { // remember this result, but a reference may be the next resolve result
ret = (PyElement)elt;
}
else { // not a reassignment, not anything from Python; no point to continue
break SEARCH;
}
}
// all resolve results checked, reassignment not detected, nothing more to do
break;
}
return ret;
}

View File

@@ -24,6 +24,7 @@ import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.ResolveState;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.SmartList;
import com.intellij.util.containers.HashMap;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.psi.*;
@@ -81,31 +82,16 @@ public class PyAssignmentStatementImpl extends PyElementImpl implements PyAssign
@NotNull
public List<Pair<PyExpression, PyExpression>> getTargetsToValuesMapping() {
List<Pair<PyExpression, PyExpression>> ret = new ArrayList<Pair<PyExpression, PyExpression>>();
List<PyExpression> lhses = new ArrayList<PyExpression>(1);
PyExpression rhs = null;
// extract all LHSes and RHS
boolean seen_eq = false;
for (PsiElement child = this.getFirstChild(); child != null; child = child.getNextSibling()) {
if (child instanceof PsiWhiteSpace) continue;
if ("=".equals(child.getText())) seen_eq = true;
if (child instanceof PyExpression) {
PyExpression expr = (PyExpression)child;
if (seen_eq) {
if (rhs != null) { // more than one RHS is clearly a parsing error, return nothing.
ret.clear();
return ret;
}
rhs = expr;
}
else lhses.add(expr);
List<Pair<PyExpression, PyExpression>> ret = new SmartList<Pair<PyExpression, PyExpression>>();
if (!PsiTreeUtil.hasErrorElements(this)) { // no parse errors
PyExpression[] constituents = PsiTreeUtil.getChildrenOfType(this, PyExpression.class); // "a = b = c" -> [a, b, c]
if (constituents != null && constituents.length > 1) {
PyExpression rhs = constituents[constituents.length - 1]; // last
List<PyExpression> lhses = new ArrayList<PyExpression>(constituents.length - 1);
for (int i = 0; i < constituents.length - 1; i += 1) lhses.add(constituents[i]); // copy all but last; most often it's one element.
for (PyExpression lhs : lhses) mapToValues(lhs, rhs, ret);
}
}
if (lhses.size() == 0) { // no LHS, must be incorrectly parsed
ret.clear();
return ret;
}
for (PyExpression lhs : lhses) mapToValues(lhs, rhs, ret);
return ret;
}

View File

@@ -45,6 +45,7 @@ public class PyCallExpressionHelper {
if (callee instanceof PyReferenceExpression) {
PyReferenceExpression ref = (PyReferenceExpression)callee;
PsiElement resolved = PyUtil.followAssignmentsChain(ref);
if (resolved instanceof PyClass) resolved = ((PyClass)resolved).findMethodByName(PyNames.INIT); // class to constructor call
if (resolved instanceof PyFunction) {
EnumSet<PyCallExpression.Flag> flags = EnumSet.noneOf(PyCallExpression.Flag.class);
int implicit_offset = 0;

View File

@@ -0,0 +1,7 @@
class Bar:
def __init__(self, a, b):
pass
Boar = Bambr = Bar
Bambr(<arg1>1, <arg2>2)

View File

@@ -0,0 +1,7 @@
def foo(a, b):
pass
zoo = foo
moo, n = zoo, 1
moo(<arg1>1, <arg2>n)

View File

@@ -0,0 +1,8 @@
class Foo:
def moo(self, a, b, c):
pass
foo = Foo()
qoo = foo
qoo.moo(<arg1>1, <arg2>2, <arg3>"bamboo")

View File

@@ -213,7 +213,32 @@ public class PyParameterInfoTest extends MarkedTestCase {
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("self,a", new String[]{"a"});
}
// TODO: add method tests with decorators when a mock SDK is available
public void testReassignedFunction() throws Exception {
Map<String, PsiElement> marks = loadTest();
assertEquals("Test data sanity", marks.size(), 2);
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("a,b", new String[]{"a,"});
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("a,b", new String[]{"b"});
}
public void testReassignedInstanceMethod() throws Exception {
Map<String, PsiElement> marks = loadTest();
assertEquals("Test data sanity", marks.size(), 3);
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("self,a,b,c", new String[]{"a,"}, new String[]{"self,"});
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("self,a,b,c", new String[]{"b,"}, new String[]{"self,"});
feignCtrlP(marks.get("<arg3>").getTextOffset()).check("self,a,b,c", new String[]{"c"}, new String[]{"self,"});
}
public void testReassignedClassInit() throws Exception {
Map<String, PsiElement> marks = loadTest();
assertEquals("Test data sanity", marks.size(), 2);
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("self,a,b", new String[]{"a,"}, new String[]{"self,"});
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("self,a,b", new String[]{"b"}, new String[]{"self,"});
}
// TODO: add method tests with decorators when a mock SDK is available
/**
* Imitates pressing of Ctrl+P; fails if results are not as expected.