mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-20657 Loose return types of generators annotated to return not typing.Generator
Sometimes people use types other than typing.Generator to annotate result types of generator functions, e.g. Iterable[T]. Because these other generic types don't allow to specify type of expressions in return statements, consider it to be Any, so as not to warn about mismatched actual and expected return types in type checker inspection.
This commit is contained in:
@@ -72,6 +72,11 @@ public interface PyFunction extends PsiNamedElement, StubBasedPsiElement<PyFunct
|
||||
@Nullable
|
||||
Modifier getModifier();
|
||||
|
||||
/**
|
||||
* Checks whether the function contains a yield expression in its body.
|
||||
*/
|
||||
boolean isGenerator();
|
||||
|
||||
boolean isAsync();
|
||||
|
||||
boolean isAsyncAllowed();
|
||||
|
||||
@@ -115,10 +115,16 @@ public class PyTypeCheckerInspection extends PyInspection {
|
||||
if (PyNames.FAKE_COROUTINE.equals(genericType.getName())) {
|
||||
return genericType.getIteratedItemType();
|
||||
}
|
||||
else if (PyNames.FAKE_GENERATOR.equals(genericType.getName()) ||
|
||||
genericType instanceof PyClassType && "typing.Generator".equals(((PyClassType)genericType).getClassQName())) {
|
||||
// Generator's type is parametrized as [YieldType, SendType, ReturnType]
|
||||
return ContainerUtil.getOrElse(genericType.getElementTypes(myTypeEvalContext), 2, null);
|
||||
else if (function.isGenerator()) {
|
||||
if (PyNames.FAKE_GENERATOR.equals(genericType.getName()) ||
|
||||
genericType instanceof PyClassType && "typing.Generator".equals(((PyClassType)genericType).getClassQName())) {
|
||||
// Generator's type is parametrized as [YieldType, SendType, ReturnType]
|
||||
return ContainerUtil.getOrElse(genericType.getElementTypes(myTypeEvalContext), 2, null);
|
||||
}
|
||||
else {
|
||||
// Assume that any other return type annotation for a generator cannot contain its return type
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -693,6 +693,30 @@ public class PyFunctionImpl extends PyBaseElementImpl<PyFunctionStub> implements
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGenerator() {
|
||||
final Ref<Boolean> result = new Ref<>(false);
|
||||
getStatementList().accept(new PyRecursiveElementVisitor() {
|
||||
@Override
|
||||
public void visitPyYieldExpression(PyYieldExpression node) {
|
||||
result.set(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyFunction(PyFunction node) {
|
||||
// Ignore nested functions
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitElement(PsiElement element) {
|
||||
if (!result.get()) {
|
||||
super.visitElement(element);
|
||||
}
|
||||
}
|
||||
});
|
||||
return result.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAsync() {
|
||||
final PyFunctionStub stub = getStub();
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
from typing import Iterable
|
||||
|
||||
|
||||
def f() -> Iterable[int]:
|
||||
for i in range(10):
|
||||
yield i
|
||||
@@ -179,6 +179,11 @@ public class Py3TypeCheckerInspectionTest extends PyTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-20657
|
||||
public void testGeneratorAnnotatedToReturnIterable() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-21083
|
||||
public void testFloatFromhex() {
|
||||
doTest();
|
||||
|
||||
Reference in New Issue
Block a user