mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
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:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user