PY-5833 Fixed: No namedtuple autocompletion if field names passed in

Try to resolve fieldNamesExpression if it is a reference
This commit is contained in:
Semyon Proshev
2016-02-15 19:01:21 +03:00
parent e5ec091587
commit 2e2de9e48c
5 changed files with 46 additions and 8 deletions
@@ -25,7 +25,9 @@ import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyBuiltinCache;
import com.jetbrains.python.psi.impl.PyElementImpl;
import com.jetbrains.python.psi.impl.PyPsiUtils;
import com.jetbrains.python.psi.impl.PyReferenceExpressionImpl;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.resolve.QualifiedResolveResult;
import com.jetbrains.python.psi.resolve.RatedResolveResult;
import com.jetbrains.python.psi.types.*;
import org.jetbrains.annotations.NotNull;
@@ -113,10 +115,10 @@ public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType
}
@Nullable
public static PyType fromCall(PyCallExpression call, int level) {
public static PyType fromCall(@NotNull PyCallExpression call, @NotNull TypeEvalContext context, int level) {
final String name = PyPsiUtils.strValue(call.getArgument(0, PyExpression.class));
final PyExpression fieldNamesExpression = PyPsiUtils.flattenParens(call.getArgument(1, PyExpression.class));
if (name == null || fieldNamesExpression == null) {
final PyExpression fieldNamesExpression = resolveFieldNamesExpression(call, context);
if (name == null || fieldNamesExpression == null || !context.maySwitchToAST(fieldNamesExpression)) {
return null;
}
List<String> fieldNames = null;
@@ -138,7 +140,26 @@ public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType
return null;
}
private static List<String> parseFieldNamesString(String fieldNamesString) {
@Nullable
private static PyExpression resolveFieldNamesExpression(@NotNull PyCallExpression call, @NotNull TypeEvalContext context) {
final PyExpression fieldNamesExpression = PyPsiUtils.flattenParens(call.getArgument(1, PyExpression.class));
if (fieldNamesExpression instanceof PyReferenceExpression) {
final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(context);
final QualifiedResolveResult resolveResult = ((PyReferenceExpression)fieldNamesExpression).followAssignmentsChain(resolveContext);
final PsiElement resolvedFieldNamesExpression = resolveResult.getElement();
if (resolvedFieldNamesExpression instanceof PyExpression) {
return (PyExpression)resolvedFieldNamesExpression;
}
}
return fieldNamesExpression;
}
@NotNull
private static List<String> parseFieldNamesString(@NotNull String fieldNamesString) {
List<String> result = new ArrayList<String>();
for(String name: StringUtil.tokenize(fieldNamesString, ", ")) {
result.add(name);
@@ -63,7 +63,7 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase {
if (type != null) {
return type;
}
type = getNamedTupleType(referenceTarget, anchor);
type = getNamedTupleType(referenceTarget, context, anchor);
if (type != null) {
return type;
}
@@ -226,7 +226,9 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase {
}
@Nullable
private static PyType getNamedTupleType(@NotNull PsiElement referenceTarget, @Nullable PsiElement anchor) {
private static PyType getNamedTupleType(@NotNull PsiElement referenceTarget,
@NotNull TypeEvalContext context,
@Nullable PsiElement anchor) {
if (referenceTarget instanceof PyTargetExpression) {
final PyTargetExpression target = (PyTargetExpression)referenceTarget;
final QualifiedName calleeName = target.getCalleeName();
@@ -239,7 +241,7 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase {
if (callee != null) {
final PyCallable callable = callee.getCallable();
if (PyNames.COLLECTIONS_NAMEDTUPLE.equals(callable.getQualifiedName())) {
return PyNamedTupleType.fromCall(call, 1);
return PyNamedTupleType.fromCall(call, context, 1);
}
}
}
@@ -248,7 +250,7 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase {
else if (referenceTarget instanceof PyFunction && anchor instanceof PyCallExpression) {
final PyFunction function = (PyFunction)referenceTarget;
if (PyNames.NAMEDTUPLE.equals(function.getName()) && PyNames.COLLECTIONS_NAMEDTUPLE.equals(function.getQualifiedName())) {
return PyNamedTupleType.fromCall((PyCallExpression)anchor, 2);
return PyNamedTupleType.fromCall((PyCallExpression)anchor, context, 2);
}
}
return null;
@@ -0,0 +1,5 @@
from collections import namedtuple
foofields = 'bar', 'xyzzy'
nt = namedtuple('foo', foofields)
o = nt(1, 2)
print o.xyzzy
@@ -0,0 +1,5 @@
from collections import namedtuple
foofields = 'bar', 'xyzzy'
nt = namedtuple('foo', foofields)
o = nt(1, 2)
print o.xyz<caret>
@@ -823,6 +823,11 @@ public class PythonCompletionTest extends PyTestCase {
assertUnderscoredMethodSpecialAttributesSuggested();
}
// PY-5833
public void testPassedNamedTupleAttributes() {
doTest();
}
private void assertUnderscoredFunctionAttributesSuggested() {
final List<String> suggested = doTestByFile();
assertNotNull(suggested);