mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 13:51:37 +00:00
[red-knot] Infer Unknown
for the loop var in async for
loops (#13243)
This commit is contained in:
parent
0512428a6f
commit
e965f9cc0e
3 changed files with 83 additions and 9 deletions
|
@ -672,6 +672,7 @@ where
|
||||||
ForStmtDefinitionNodeRef {
|
ForStmtDefinitionNodeRef {
|
||||||
iterable: &node.iter,
|
iterable: &node.iter,
|
||||||
target: name_node,
|
target: name_node,
|
||||||
|
is_async: node.is_async,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,6 +152,7 @@ pub(crate) struct WithItemDefinitionNodeRef<'a> {
|
||||||
pub(crate) struct ForStmtDefinitionNodeRef<'a> {
|
pub(crate) struct ForStmtDefinitionNodeRef<'a> {
|
||||||
pub(crate) iterable: &'a ast::Expr,
|
pub(crate) iterable: &'a ast::Expr,
|
||||||
pub(crate) target: &'a ast::ExprName,
|
pub(crate) target: &'a ast::ExprName,
|
||||||
|
pub(crate) is_async: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
@ -206,12 +207,15 @@ impl DefinitionNodeRef<'_> {
|
||||||
DefinitionNodeRef::AugmentedAssignment(augmented_assignment) => {
|
DefinitionNodeRef::AugmentedAssignment(augmented_assignment) => {
|
||||||
DefinitionKind::AugmentedAssignment(AstNodeRef::new(parsed, augmented_assignment))
|
DefinitionKind::AugmentedAssignment(AstNodeRef::new(parsed, augmented_assignment))
|
||||||
}
|
}
|
||||||
DefinitionNodeRef::For(ForStmtDefinitionNodeRef { iterable, target }) => {
|
DefinitionNodeRef::For(ForStmtDefinitionNodeRef {
|
||||||
DefinitionKind::For(ForStmtDefinitionKind {
|
iterable,
|
||||||
|
target,
|
||||||
|
is_async,
|
||||||
|
}) => DefinitionKind::For(ForStmtDefinitionKind {
|
||||||
iterable: AstNodeRef::new(parsed.clone(), iterable),
|
iterable: AstNodeRef::new(parsed.clone(), iterable),
|
||||||
target: AstNodeRef::new(parsed, target),
|
target: AstNodeRef::new(parsed, target),
|
||||||
})
|
is_async,
|
||||||
}
|
}),
|
||||||
DefinitionNodeRef::Comprehension(ComprehensionDefinitionNodeRef {
|
DefinitionNodeRef::Comprehension(ComprehensionDefinitionNodeRef {
|
||||||
iterable,
|
iterable,
|
||||||
target,
|
target,
|
||||||
|
@ -265,6 +269,7 @@ impl DefinitionNodeRef<'_> {
|
||||||
Self::For(ForStmtDefinitionNodeRef {
|
Self::For(ForStmtDefinitionNodeRef {
|
||||||
iterable: _,
|
iterable: _,
|
||||||
target,
|
target,
|
||||||
|
is_async: _,
|
||||||
}) => target.into(),
|
}) => target.into(),
|
||||||
Self::Comprehension(ComprehensionDefinitionNodeRef { target, .. }) => target.into(),
|
Self::Comprehension(ComprehensionDefinitionNodeRef { target, .. }) => target.into(),
|
||||||
Self::Parameter(node) => match node {
|
Self::Parameter(node) => match node {
|
||||||
|
@ -388,6 +393,7 @@ impl WithItemDefinitionKind {
|
||||||
pub struct ForStmtDefinitionKind {
|
pub struct ForStmtDefinitionKind {
|
||||||
iterable: AstNodeRef<ast::Expr>,
|
iterable: AstNodeRef<ast::Expr>,
|
||||||
target: AstNodeRef<ast::ExprName>,
|
target: AstNodeRef<ast::ExprName>,
|
||||||
|
is_async: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ForStmtDefinitionKind {
|
impl ForStmtDefinitionKind {
|
||||||
|
@ -398,6 +404,10 @@ impl ForStmtDefinitionKind {
|
||||||
pub(crate) fn target(&self) -> &ast::ExprName {
|
pub(crate) fn target(&self) -> &ast::ExprName {
|
||||||
self.target.node()
|
self.target.node()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_async(&self) -> bool {
|
||||||
|
self.is_async
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||||
|
|
|
@ -394,6 +394,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
self.infer_for_statement_definition(
|
self.infer_for_statement_definition(
|
||||||
for_statement_definition.target(),
|
for_statement_definition.target(),
|
||||||
for_statement_definition.iterable(),
|
for_statement_definition.iterable(),
|
||||||
|
for_statement_definition.is_async(),
|
||||||
definition,
|
definition,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1045,6 +1046,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
&mut self,
|
&mut self,
|
||||||
target: &ast::ExprName,
|
target: &ast::ExprName,
|
||||||
iterable: &ast::Expr,
|
iterable: &ast::Expr,
|
||||||
|
is_async: bool,
|
||||||
definition: Definition<'db>,
|
definition: Definition<'db>,
|
||||||
) {
|
) {
|
||||||
let expression = self.index.expression(iterable);
|
let expression = self.index.expression(iterable);
|
||||||
|
@ -1054,9 +1056,14 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
.types
|
.types
|
||||||
.expression_ty(iterable.scoped_ast_id(self.db, self.scope));
|
.expression_ty(iterable.scoped_ast_id(self.db, self.scope));
|
||||||
|
|
||||||
let loop_var_value_ty = iterable_ty
|
let loop_var_value_ty = if is_async {
|
||||||
|
// TODO(Alex): async iterables/iterators!
|
||||||
|
Type::Unknown
|
||||||
|
} else {
|
||||||
|
iterable_ty
|
||||||
.iterate(self.db)
|
.iterate(self.db)
|
||||||
.unwrap_with_diagnostic(iterable.into(), self);
|
.unwrap_with_diagnostic(iterable.into(), self)
|
||||||
|
};
|
||||||
|
|
||||||
self.types
|
self.types
|
||||||
.expressions
|
.expressions
|
||||||
|
@ -3026,6 +3033,62 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This tests that we understand that `async` for loops
|
||||||
|
/// do not work according to the synchronous iteration protocol
|
||||||
|
#[test]
|
||||||
|
fn invalid_async_for_loop() -> anyhow::Result<()> {
|
||||||
|
let mut db = setup_db();
|
||||||
|
|
||||||
|
db.write_dedented(
|
||||||
|
"src/a.py",
|
||||||
|
"
|
||||||
|
async def foo():
|
||||||
|
class Iterator:
|
||||||
|
def __next__(self) -> int:
|
||||||
|
return 42
|
||||||
|
|
||||||
|
class Iterable:
|
||||||
|
def __iter__(self) -> Iterator:
|
||||||
|
return Iterator()
|
||||||
|
|
||||||
|
async for x in Iterator():
|
||||||
|
pass
|
||||||
|
",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// TODO(Alex) async iterables/iterators!
|
||||||
|
assert_scope_ty(&db, "src/a.py", &["foo"], "x", "Unknown");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_async_for_loop() -> anyhow::Result<()> {
|
||||||
|
let mut db = setup_db();
|
||||||
|
|
||||||
|
db.write_dedented(
|
||||||
|
"src/a.py",
|
||||||
|
"
|
||||||
|
async def foo():
|
||||||
|
class IntAsyncIterator:
|
||||||
|
async def __anext__(self) -> int:
|
||||||
|
return 42
|
||||||
|
|
||||||
|
class IntAsyncIterable:
|
||||||
|
def __aiter__(self) -> IntAsyncIterator:
|
||||||
|
return IntAsyncIterator()
|
||||||
|
|
||||||
|
async for x in IntAsyncIterable():
|
||||||
|
pass
|
||||||
|
",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// TODO(Alex) async iterables/iterators!
|
||||||
|
assert_scope_ty(&db, "src/a.py", &["foo"], "x", "Unknown");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn class_constructor_call_expression() -> anyhow::Result<()> {
|
fn class_constructor_call_expression() -> anyhow::Result<()> {
|
||||||
let mut db = setup_db();
|
let mut db = setup_db();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue