[syntax-errors] await outside async functions (#17363)

Summary
--

This PR implements detecting the use of `await` expressions outside of
async functions. This is a reimplementation of
[await-outside-async
(PLE1142)](https://docs.astral.sh/ruff/rules/await-outside-async/) as a
semantic syntax error.

Despite the rule name, PLE1142 also applies to `async for` and `async
with`, so these are covered here too.

Test Plan
--

Existing PLE1142 tests.

I also deleted more code from the `SemanticSyntaxCheckerVisitor` to
avoid changes in other parser tests.
This commit is contained in:
Brent Westbrook 2025-04-14 13:01:48 -04:00 committed by GitHub
parent e2a38e4c00
commit 014bb526f4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 186 additions and 89 deletions

View file

@ -462,7 +462,7 @@ impl<'ast> SourceOrderVisitor<'ast> for ValidateAstVisitor<'ast> {
enum Scope {
Module,
Function { is_async: bool },
Function,
Comprehension { is_async: bool },
Class,
}
@ -529,12 +529,7 @@ impl SemanticSyntaxContext for SemanticSyntaxCheckerVisitor<'_> {
}
fn in_async_context(&self) -> bool {
for scope in &self.scopes {
if let Scope::Function { is_async } = scope {
return *is_async;
}
}
false
true
}
fn in_sync_comprehension(&self) -> bool {
@ -561,6 +556,10 @@ impl SemanticSyntaxContext for SemanticSyntaxCheckerVisitor<'_> {
fn in_await_allowed_context(&self) -> bool {
true
}
fn in_generator_scope(&self) -> bool {
true
}
}
impl Visitor<'_> for SemanticSyntaxCheckerVisitor<'_> {
@ -587,10 +586,8 @@ impl Visitor<'_> for SemanticSyntaxCheckerVisitor<'_> {
self.visit_body(body);
self.scopes.pop().unwrap();
}
ast::Stmt::FunctionDef(ast::StmtFunctionDef { is_async, .. }) => {
self.scopes.push(Scope::Function {
is_async: *is_async,
});
ast::Stmt::FunctionDef(ast::StmtFunctionDef { .. }) => {
self.scopes.push(Scope::Function);
ast::visitor::walk_stmt(self, stmt);
self.scopes.pop().unwrap();
}
@ -604,7 +601,7 @@ impl Visitor<'_> for SemanticSyntaxCheckerVisitor<'_> {
self.with_semantic_checker(|semantic, context| semantic.visit_expr(expr, context));
match expr {
ast::Expr::Lambda(_) => {
self.scopes.push(Scope::Function { is_async: false });
self.scopes.push(Scope::Function);
ast::visitor::walk_expr(self, expr);
self.scopes.pop().unwrap();
}