mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-19826 Fixed: Generic type of list not inferred when created from tuple
Inherit PyTupleType from PyCollectionType so that getElementTypes().get(i) returns type of 'i'-th element in the tuple
This commit is contained in:
@@ -18,6 +18,7 @@ package com.jetbrains.python.documentation;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.codeInsight.PyTypingTypeProvider;
|
||||
import com.jetbrains.python.psi.types.*;
|
||||
@@ -197,7 +198,17 @@ public class PyTypeModelBuilder {
|
||||
myVisited.put(type, null); //mark as evaluating
|
||||
|
||||
TypeModel result = null;
|
||||
if (type instanceof PyCollectionType) {
|
||||
if (type instanceof PyTupleType) {
|
||||
final PyTupleType tupleType = (PyTupleType)type;
|
||||
|
||||
final List<PyType> elementTypes = tupleType.isHomogeneous()
|
||||
? Collections.singletonList(tupleType.getElementType(0))
|
||||
: tupleType.getElementTypes(myContext);
|
||||
|
||||
final List<TypeModel> elementModels = ContainerUtil.map(elementTypes, elementType -> build(elementType, true));
|
||||
result = new TupleType(elementModels, tupleType.isHomogeneous());
|
||||
}
|
||||
else if (type instanceof PyCollectionType) {
|
||||
final String name = type.getName();
|
||||
final List<PyType> elementTypes = ((PyCollectionType)type).getElementTypes(myContext);
|
||||
boolean nullOnlyTypes = true;
|
||||
@@ -232,15 +243,6 @@ public class PyTypeModelBuilder {
|
||||
else if (type instanceof PyCallableType && !(type instanceof PyClassLikeType)) {
|
||||
result = build((PyCallableType)type);
|
||||
}
|
||||
else if (type instanceof PyTupleType) {
|
||||
final List<TypeModel> elementModels = new ArrayList<>();
|
||||
final PyTupleType tupleType = (PyTupleType)type;
|
||||
for (int i = 0; i < (tupleType.isHomogeneous() ? 1 : tupleType.getElementCount()); i++) {
|
||||
final PyType elementType = tupleType.getElementType(i);
|
||||
elementModels.add(build(elementType, true));
|
||||
}
|
||||
result = new TupleType(elementModels, tupleType.isHomogeneous());
|
||||
}
|
||||
if (result == null) {
|
||||
result = type != null ? _(type.getName()) : _(PyNames.UNKNOWN_TYPE);
|
||||
}
|
||||
@@ -364,7 +366,7 @@ public class PyTypeModelBuilder {
|
||||
@Override
|
||||
public void oneOf(OneOf oneOf) {
|
||||
myDepth++;
|
||||
if (myDepth>MAX_DEPTH) {
|
||||
if (myDepth > MAX_DEPTH) {
|
||||
add("...");
|
||||
return;
|
||||
}
|
||||
@@ -393,7 +395,7 @@ public class PyTypeModelBuilder {
|
||||
@Override
|
||||
public void collectionOf(CollectionOf collectionOf) {
|
||||
myDepth++;
|
||||
if (myDepth>MAX_DEPTH) {
|
||||
if (myDepth > MAX_DEPTH) {
|
||||
add("...");
|
||||
return;
|
||||
}
|
||||
@@ -416,7 +418,7 @@ public class PyTypeModelBuilder {
|
||||
@Override
|
||||
public void function(FunctionType function) {
|
||||
myDepth++;
|
||||
if (myDepth>MAX_DEPTH) {
|
||||
if (myDepth > MAX_DEPTH) {
|
||||
add("...");
|
||||
return;
|
||||
}
|
||||
@@ -436,7 +438,7 @@ public class PyTypeModelBuilder {
|
||||
@Override
|
||||
public void param(ParamType param) {
|
||||
myDepth++;
|
||||
if (myDepth>MAX_DEPTH) {
|
||||
if (myDepth > MAX_DEPTH) {
|
||||
add("...");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ public class PyCallExpressionHelper {
|
||||
}
|
||||
}
|
||||
final List<PyExpression> resolvedQualifiers = resolveResult != null ? resolveResult.getQualifiers() : null;
|
||||
final List<PyExpression> qualifiers = resolvedQualifiers != null ? resolvedQualifiers : Collections.<PyExpression>emptyList();
|
||||
final List<PyExpression> qualifiers = resolvedQualifiers != null ? resolvedQualifiers : Collections.emptyList();
|
||||
final TypeEvalContext context = resolveContext.getTypeEvalContext();
|
||||
if (resolved instanceof PyFunction) {
|
||||
final PyFunction function = (PyFunction)resolved;
|
||||
@@ -267,7 +267,7 @@ public class PyCallExpressionHelper {
|
||||
QualifiedResolveResult followed = callReference.followAssignmentsChain(resolveContext);
|
||||
final List<PyExpression> qualifiers = followed.getQualifiers();
|
||||
final PyExpression firstQualifier = qualifiers != null && !qualifiers.isEmpty() ? qualifiers.get(0) : null;
|
||||
boolean isByInstance = isQualifiedByInstance(function, qualifiers != null ? qualifiers : Collections.<PyExpression>emptyList(),
|
||||
boolean isByInstance = isQualifiedByInstance(function, qualifiers != null ? qualifiers : Collections.emptyList(),
|
||||
resolveContext.getTypeEvalContext());
|
||||
final boolean isConstructorCall = isConstructorName(function.getName()) &&
|
||||
(!callReference.isQualified() || !isConstructorName(callReference.getName()));
|
||||
@@ -539,14 +539,21 @@ public class PyCallExpressionHelper {
|
||||
}
|
||||
if (init != null) {
|
||||
final PyType t = init.getCallType(context, call);
|
||||
if (cls != null) {
|
||||
if (init.getContainingClass() != cls) {
|
||||
if (t instanceof PyCollectionType) {
|
||||
final List<PyType> elementTypes = ((PyCollectionType)t).getElementTypes(context);
|
||||
return Ref.create(new PyCollectionTypeImpl(cls, false, elementTypes));
|
||||
}
|
||||
return Ref.create(new PyClassTypeImpl(cls, false));
|
||||
if (cls != null && cls != init.getContainingClass()) {
|
||||
if (t instanceof PyTupleType) {
|
||||
final PyTupleType tupleType = (PyTupleType)t;
|
||||
final List<PyType> elementTypes = tupleType.getElementTypes(context);
|
||||
final PyTupleType newTupleType = new PyTupleType(cls, elementTypes.toArray(new PyType[0]), tupleType.isHomogeneous());
|
||||
|
||||
return Ref.create(newTupleType);
|
||||
}
|
||||
|
||||
if (t instanceof PyCollectionType) {
|
||||
final List<PyType> elementTypes = ((PyCollectionType)t).getElementTypes(context);
|
||||
return Ref.create(new PyCollectionTypeImpl(cls, false, elementTypes));
|
||||
}
|
||||
|
||||
return Ref.create(new PyClassTypeImpl(cls, false));
|
||||
}
|
||||
if (t != null && !(t instanceof PyNoneType)) {
|
||||
return Ref.create(t);
|
||||
@@ -680,10 +687,10 @@ public class PyCallExpressionHelper {
|
||||
final PyCallExpression.PyMarkedCallee markedCallee = callExpression.resolveCallee(resolveContext, implicitOffset);
|
||||
|
||||
if (markedCallee == null || argumentList == null) {
|
||||
return new PyCallExpression.PyArgumentsMapping(callExpression, null, Collections.<PyExpression, PyNamedParameter>emptyMap(),
|
||||
Collections.<PyParameter>emptyList(), Collections.<PyExpression>emptyList(),
|
||||
Collections.<PyNamedParameter>emptyList(), Collections.<PyNamedParameter>emptyList(),
|
||||
Collections.<PyExpression, PyTupleParameter>emptyMap());
|
||||
return new PyCallExpression.PyArgumentsMapping(callExpression, null, Collections.emptyMap(),
|
||||
Collections.emptyList(), Collections.emptyList(),
|
||||
Collections.emptyList(), Collections.emptyList(),
|
||||
Collections.emptyMap());
|
||||
}
|
||||
final TypeEvalContext context = resolveContext.getTypeEvalContext();
|
||||
final List<PyParameter> parameters = PyUtil.getParameters(markedCallee.getCallable(), context);
|
||||
|
||||
@@ -124,21 +124,21 @@ public class PyFunctionImpl extends PyBaseElementImpl<PyFunctionStub> implements
|
||||
public Icon getIcon(int flags) {
|
||||
PyPsiUtils.assertValid(this);
|
||||
final Property property = getProperty();
|
||||
if (property != null) {
|
||||
if (property.getGetter().valueOrNull() == this) {
|
||||
return PythonIcons.Python.PropertyGetter;
|
||||
}
|
||||
if (property.getSetter().valueOrNull() == this) {
|
||||
return PythonIcons.Python.PropertySetter;
|
||||
}
|
||||
if (property.getDeleter().valueOrNull() == this) {
|
||||
return PythonIcons.Python.PropertyDeleter;
|
||||
}
|
||||
return PlatformIcons.PROPERTY_ICON;
|
||||
if (property != null) {
|
||||
if (property.getGetter().valueOrNull() == this) {
|
||||
return PythonIcons.Python.PropertyGetter;
|
||||
}
|
||||
if (getContainingClass() != null) {
|
||||
return PlatformIcons.METHOD_ICON;
|
||||
if (property.getSetter().valueOrNull() == this) {
|
||||
return PythonIcons.Python.PropertySetter;
|
||||
}
|
||||
if (property.getDeleter().valueOrNull() == this) {
|
||||
return PythonIcons.Python.PropertyDeleter;
|
||||
}
|
||||
return PlatformIcons.PROPERTY_ICON;
|
||||
}
|
||||
if (getContainingClass() != null) {
|
||||
return PlatformIcons.METHOD_ICON;
|
||||
}
|
||||
return PythonIcons.Python.Function;
|
||||
}
|
||||
|
||||
@@ -324,14 +324,18 @@ public class PyFunctionImpl extends PyBaseElementImpl<PyFunctionStub> implements
|
||||
public void visitPyYieldExpression(PyYieldExpression node) {
|
||||
final PyExpression expr = node.getExpression();
|
||||
final PyType type = expr != null ? context.getType(expr) : null;
|
||||
|
||||
if (node.isDelegating()) {
|
||||
if (type instanceof PyCollectionType) {
|
||||
if (type instanceof PyTupleType) {
|
||||
types.addAll(((PyTupleType)type).getElementTypes(context));
|
||||
}
|
||||
else if (type instanceof PyCollectionType) {
|
||||
final PyCollectionType collectionType = (PyCollectionType)type;
|
||||
// TODO: Select the parameter types that matches T in Iterable[T]
|
||||
final List<PyType> elementTypes = collectionType.getElementTypes(context);
|
||||
types.add(elementTypes.isEmpty() ? null : elementTypes.get(0));
|
||||
}
|
||||
else if (ArrayUtil.contains(type, cache.getListType(), cache.getDictType(), cache.getSetType())) {
|
||||
else if (ArrayUtil.contains(type, cache.getListType(), cache.getDictType(), cache.getSetType(), cache.getTupleType())) {
|
||||
types.add(null);
|
||||
}
|
||||
else {
|
||||
@@ -605,7 +609,7 @@ public class PyFunctionImpl extends PyBaseElementImpl<PyFunctionStub> implements
|
||||
if (inlineComment != null && PyTypingTypeProvider.getTypeCommentValue(inlineComment.getText()) != null) {
|
||||
return inlineComment;
|
||||
}
|
||||
|
||||
|
||||
final PyStatementList statements = getStatementList();
|
||||
if (statements.getStatements().length != 0) {
|
||||
final PsiComment comment = as(statements.getFirstChild(), PsiComment.class);
|
||||
@@ -621,7 +625,7 @@ public class PyFunctionImpl extends PyBaseElementImpl<PyFunctionStub> implements
|
||||
public String getTypeCommentAnnotation() {
|
||||
final PyFunctionStub stub = getStub();
|
||||
if (stub != null) {
|
||||
return stub.getTypeComment();
|
||||
return stub.getTypeComment();
|
||||
}
|
||||
final PsiComment comment = getTypeComment();
|
||||
if (comment != null) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -77,20 +77,13 @@ public class PyCollectionTypeImpl extends PyClassTypeImpl implements PyCollectio
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof PyCollectionType)) return false;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
if (!super.equals(o)) return false;
|
||||
|
||||
PyCollectionType type = (PyCollectionType)o;
|
||||
final PyCollectionTypeImpl that = (PyCollectionTypeImpl)o;
|
||||
|
||||
if (!myElementTypes.equals(that.myElementTypes)) return false;
|
||||
|
||||
final TypeEvalContext context = TypeEvalContext.codeInsightFallback(myClass.getProject());
|
||||
final List<PyType> otherElementTypes = type.getElementTypes(context);
|
||||
if (myElementTypes.size() != otherElementTypes.size()) return false;
|
||||
for (int i = 0; i < myElementTypes.size(); i++) {
|
||||
final PyType elementType = myElementTypes.get(i);
|
||||
final PyType otherElementType = otherElementTypes.get(i);
|
||||
if (elementType == null && otherElementType != null) return false;
|
||||
if (elementType != null && !elementType.equals(otherElementType)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,11 +26,12 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class PyTupleType extends PyClassTypeImpl implements PySubscriptableType {
|
||||
public class PyTupleType extends PyClassTypeImpl implements PySubscriptableType, PyCollectionType {
|
||||
private final PyType[] myElementTypes;
|
||||
private final boolean myHomogeneous;
|
||||
|
||||
@@ -47,12 +48,12 @@ public class PyTupleType extends PyClassTypeImpl implements PySubscriptableType
|
||||
public static PyTupleType createHomogeneous(@NotNull PsiElement anchor, @Nullable PyType elementType) {
|
||||
PyClass tuple = PyBuiltinCache.getInstance(anchor).getClass(PyNames.TUPLE);
|
||||
if (tuple != null) {
|
||||
return new PyTupleType(tuple, new PyType[] {elementType}, true);
|
||||
return new PyTupleType(tuple, new PyType[]{elementType}, true);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
PyTupleType(@NotNull PyClass tupleClass, @NotNull PyType[] elementTypes, boolean homogeneous) {
|
||||
public PyTupleType(@NotNull PyClass tupleClass, @NotNull PyType[] elementTypes, boolean homogeneous) {
|
||||
super(tupleClass, false);
|
||||
myElementTypes = elementTypes;
|
||||
myHomogeneous = homogeneous;
|
||||
@@ -124,4 +125,10 @@ public class PyTupleType extends PyClassTypeImpl implements PySubscriptableType
|
||||
result = 31 * result + (myElementTypes != null ? Arrays.hashCode(myElementTypes) : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<PyType> getElementTypes(@NotNull TypeEvalContext context) {
|
||||
return Arrays.asList(myElementTypes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.jetbrains.python.psi.types;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiNamedElement;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
@@ -116,22 +117,7 @@ public class PyTypeChecker {
|
||||
if (expected instanceof PyClassType && actual instanceof PyClassType) {
|
||||
final PyClass superClass = ((PyClassType)expected).getPyClass();
|
||||
final PyClass subClass = ((PyClassType)actual).getPyClass();
|
||||
if (expected instanceof PyCollectionType && actual instanceof PyCollectionType) {
|
||||
if (!matchClasses(superClass, subClass, context)) {
|
||||
return false;
|
||||
}
|
||||
// TODO: Match generic parameters based on the correspondence between the generic parameters of subClass and its base classes
|
||||
final List<PyType> superElementTypes = ((PyCollectionType)expected).getElementTypes(context);
|
||||
final List<PyType> subElementTypes = ((PyCollectionType)actual).getElementTypes(context);
|
||||
for (int i = 0; i < subElementTypes.size(); i++) {
|
||||
final PyType superElementType = i < superElementTypes.size() ? superElementTypes.get(i) : null;
|
||||
if (!match(superElementType, subElementTypes.get(i), context, substitutions, recursive)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (expected instanceof PyTupleType && actual instanceof PyTupleType) {
|
||||
if (expected instanceof PyTupleType && actual instanceof PyTupleType) {
|
||||
final PyTupleType superTupleType = (PyTupleType)expected;
|
||||
final PyTupleType subTupleType = (PyTupleType)actual;
|
||||
if (!superTupleType.isHomogeneous() && !subTupleType.isHomogeneous()) {
|
||||
@@ -163,6 +149,39 @@ public class PyTypeChecker {
|
||||
return match(superTupleType.getElementType(0), subTupleType.getElementType(0), context);
|
||||
}
|
||||
}
|
||||
else if (expected instanceof PyCollectionType && actual instanceof PyTupleType) {
|
||||
if (!matchClasses(superClass, subClass, context)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final PyTupleType actualTupleType = (PyTupleType)actual;
|
||||
final PyType superElementType = ContainerUtil.getFirstItem(((PyCollectionType)expected).getElementTypes(context));
|
||||
final PyType subElementType = actualTupleType.isHomogeneous()
|
||||
? actualTupleType.getElementType(0)
|
||||
: PyUnionType.union(actualTupleType.getElementTypes(context));
|
||||
|
||||
if (!match(superElementType, subElementType, context, substitutions, recursive)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (expected instanceof PyCollectionType && actual instanceof PyCollectionType) {
|
||||
if (!matchClasses(superClass, subClass, context)) {
|
||||
return false;
|
||||
}
|
||||
// TODO: Match generic parameters based on the correspondence between the generic parameters of subClass and its base classes
|
||||
final List<PyType> superElementTypes = ((PyCollectionType)expected).getElementTypes(context);
|
||||
final List<PyType> subElementTypes = ((PyCollectionType)actual).getElementTypes(context);
|
||||
for (int i = 0; i < subElementTypes.size(); i++) {
|
||||
final PyType superElementType = i < superElementTypes.size() ? superElementTypes.get(i) : null;
|
||||
if (!match(superElementType, subElementTypes.get(i), context, substitutions, recursive)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
else if (matchClasses(superClass, subClass, context)) {
|
||||
return true;
|
||||
}
|
||||
@@ -295,12 +314,6 @@ public class PyTypeChecker {
|
||||
collectGenerics(t, context, collected, visited);
|
||||
}
|
||||
}
|
||||
else if (type instanceof PyCollectionType) {
|
||||
final PyCollectionType collection = (PyCollectionType)type;
|
||||
for (PyType elementType : collection.getElementTypes(context)) {
|
||||
collectGenerics(elementType, context, collected, visited);
|
||||
}
|
||||
}
|
||||
else if (type instanceof PyTupleType) {
|
||||
final PyTupleType tuple = (PyTupleType)type;
|
||||
final int n = tuple.isHomogeneous() ? 1 : tuple.getElementCount();
|
||||
@@ -308,6 +321,12 @@ public class PyTypeChecker {
|
||||
collectGenerics(tuple.getElementType(i), context, collected, visited);
|
||||
}
|
||||
}
|
||||
else if (type instanceof PyCollectionType) {
|
||||
final PyCollectionType collection = (PyCollectionType)type;
|
||||
for (PyType elementType : collection.getElementTypes(context)) {
|
||||
collectGenerics(elementType, context, collected, visited);
|
||||
}
|
||||
}
|
||||
else if (type instanceof PyCallableType) {
|
||||
final PyCallableType callable = (PyCallableType)type;
|
||||
final List<PyCallableParameter> parameters = callable.getParameters(context);
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<html><body><code>def <b>get_tuple</b>()<br>Inferred type: () -> Tuple[<a href="psi_element://#typename#int">int</a>, <a href="psi_element://#typename#int">int</a>, <a href="psi_element://#typename#str">str</a>]<br></code></body></html>
|
||||
@@ -0,0 +1,4 @@
|
||||
def get_tuple() -> tuple[int, int, str]:
|
||||
pass
|
||||
|
||||
<the_ref>get_tuple()
|
||||
@@ -0,0 +1 @@
|
||||
<html><body><code>def <b>get_tuple</b>()<br>Inferred type: () -> Tuple[<a href="psi_element://#typename#str">str</a>, ...]<br></code></body></html>
|
||||
@@ -0,0 +1,4 @@
|
||||
def get_tuple() -> tuple[str, ...]:
|
||||
pass
|
||||
|
||||
<the_ref>get_tuple()
|
||||
@@ -0,0 +1 @@
|
||||
<html><body><code>def <b>get_tuple</b>()<br>Inferred type: () -> <a href="psi_element://#typename#tuple">tuple</a><br></code></body></html>
|
||||
@@ -0,0 +1,4 @@
|
||||
def get_tuple() -> tuple:
|
||||
pass
|
||||
|
||||
<the_ref>get_tuple()
|
||||
@@ -84,6 +84,40 @@ public class Py3TypeTest extends PyTestCase {
|
||||
" pass"));
|
||||
}
|
||||
|
||||
public void testYieldFromHomogeneousTuple() {
|
||||
myFixture.copyDirectoryToProject("typing", "");
|
||||
doTest("str",
|
||||
"import typing\n"+
|
||||
"def get_tuple() -> typing.Tuple[str, ...]:\n" +
|
||||
" pass\n" +
|
||||
"def gen()\n" +
|
||||
" yield from get_tuple()\n" +
|
||||
"for expr in gen():" +
|
||||
" pass");
|
||||
}
|
||||
|
||||
public void testYieldFromHeterogeneousTuple() {
|
||||
myFixture.copyDirectoryToProject("typing", "");
|
||||
doTest("Union[int, str]",
|
||||
"import typing\n" +
|
||||
"def get_tuple() -> typing.Tuple[int, int, str]:\n" +
|
||||
" pass\n" +
|
||||
"def gen()\n" +
|
||||
" yield from get_tuple()\n" +
|
||||
"for expr in gen():" +
|
||||
" pass");
|
||||
}
|
||||
|
||||
public void testYieldFromUnknownTuple() {
|
||||
doTest("Any",
|
||||
"def get_tuple() -> tuple:\n" +
|
||||
" pass\n" +
|
||||
"def gen()\n" +
|
||||
" yield from get_tuple()\n" +
|
||||
"for expr in gen():" +
|
||||
" pass");
|
||||
}
|
||||
|
||||
public void testYieldFromUnknownList() {
|
||||
doTest("Any",
|
||||
"def get_list() -> list:\n" +
|
||||
|
||||
@@ -265,4 +265,16 @@ public class PyQuickDocTest extends LightMarkedTestCase {
|
||||
public void testOptionalParameterType() {
|
||||
checkHTMLOnly();
|
||||
}
|
||||
|
||||
public void testHomogeneousTuple() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON35, this::checkHTMLOnly);
|
||||
}
|
||||
|
||||
public void testHeterogeneousTuple() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON35, this::checkHTMLOnly);
|
||||
}
|
||||
|
||||
public void testUnknownTuple() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON35, this::checkHTMLOnly);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -886,6 +886,22 @@ public class PyTypeTest extends PyTestCase {
|
||||
" expr, foo = xs\n");
|
||||
}
|
||||
|
||||
// PY-19826
|
||||
public void testListFromTuple() {
|
||||
doTest("List[Union[str, int]]",
|
||||
"expr = list(('1', 2, 3))");
|
||||
}
|
||||
|
||||
public void testDictFromTuple() {
|
||||
doTest("Dict[Union[str, int], Union[str, int]]",
|
||||
"expr = dict((('1', 1), (2, 2), (3, '3')))");
|
||||
}
|
||||
|
||||
public void testSetFromTuple() {
|
||||
doTest("Set[Union[str, int]]",
|
||||
"expr = set(('1', 2, 3))");
|
||||
}
|
||||
|
||||
public void testHomogeneousTupleSubstitution() {
|
||||
runWithLanguageLevel(
|
||||
LanguageLevel.PYTHON35,
|
||||
|
||||
Reference in New Issue
Block a user