mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-26 11:59:10 +00:00
[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:
parent
e2a38e4c00
commit
014bb526f4
9 changed files with 186 additions and 89 deletions
|
@ -72,3 +72,15 @@ def await_generator_target():
|
||||||
# See: https://github.com/astral-sh/ruff/issues/14167
|
# See: https://github.com/astral-sh/ruff/issues/14167
|
||||||
def async_for_list_comprehension_target():
|
def async_for_list_comprehension_target():
|
||||||
[x for x in await foo()]
|
[x for x in await foo()]
|
||||||
|
|
||||||
|
|
||||||
|
def async_for_dictionary_comprehension_key():
|
||||||
|
{await x: y for x, y in foo()}
|
||||||
|
|
||||||
|
|
||||||
|
def async_for_dictionary_comprehension_value():
|
||||||
|
{y: await x for x, y in foo()}
|
||||||
|
|
||||||
|
|
||||||
|
def async_for_dict_comprehension():
|
||||||
|
{x: y async for x, y in foo()}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use ruff_python_ast::Comprehension;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::codes::Rule;
|
use crate::codes::Rule;
|
||||||
use crate::rules::{flake8_simplify, pylint, refurb};
|
use crate::rules::{flake8_simplify, refurb};
|
||||||
|
|
||||||
/// Run lint rules over a [`Comprehension`] syntax nodes.
|
/// Run lint rules over a [`Comprehension`] syntax nodes.
|
||||||
pub(crate) fn comprehension(comprehension: &Comprehension, checker: &Checker) {
|
pub(crate) fn comprehension(comprehension: &Comprehension, checker: &Checker) {
|
||||||
|
@ -12,9 +12,4 @@ pub(crate) fn comprehension(comprehension: &Comprehension, checker: &Checker) {
|
||||||
if checker.enabled(Rule::ReadlinesInFor) {
|
if checker.enabled(Rule::ReadlinesInFor) {
|
||||||
refurb::rules::readlines_in_comprehension(checker, comprehension);
|
refurb::rules::readlines_in_comprehension(checker, comprehension);
|
||||||
}
|
}
|
||||||
if comprehension.is_async {
|
|
||||||
if checker.enabled(Rule::AwaitOutsideAsync) {
|
|
||||||
pylint::rules::await_outside_async(checker, comprehension);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1215,11 +1215,6 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
|
||||||
pylint::rules::yield_from_in_async_function(checker, yield_from);
|
pylint::rules::yield_from_in_async_function(checker, yield_from);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Await(_) => {
|
|
||||||
if checker.enabled(Rule::AwaitOutsideAsync) {
|
|
||||||
pylint::rules::await_outside_async(checker, expr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expr::FString(f_string_expr @ ast::ExprFString { value, .. }) => {
|
Expr::FString(f_string_expr @ ast::ExprFString { value, .. }) => {
|
||||||
if checker.enabled(Rule::FStringMissingPlaceholders) {
|
if checker.enabled(Rule::FStringMissingPlaceholders) {
|
||||||
pyflakes::rules::f_string_missing_placeholders(checker, f_string_expr);
|
pyflakes::rules::f_string_missing_placeholders(checker, f_string_expr);
|
||||||
|
|
|
@ -1242,14 +1242,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||||
ruff::rules::invalid_assert_message_literal_argument(checker, assert_stmt);
|
ruff::rules::invalid_assert_message_literal_argument(checker, assert_stmt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stmt::With(
|
Stmt::With(with_stmt @ ast::StmtWith { items, body, .. }) => {
|
||||||
with_stmt @ ast::StmtWith {
|
|
||||||
items,
|
|
||||||
body,
|
|
||||||
is_async,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
if checker.enabled(Rule::TooManyNestedBlocks) {
|
if checker.enabled(Rule::TooManyNestedBlocks) {
|
||||||
pylint::rules::too_many_nested_blocks(checker, stmt);
|
pylint::rules::too_many_nested_blocks(checker, stmt);
|
||||||
}
|
}
|
||||||
|
@ -1284,11 +1277,6 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||||
if checker.enabled(Rule::CancelScopeNoCheckpoint) {
|
if checker.enabled(Rule::CancelScopeNoCheckpoint) {
|
||||||
flake8_async::rules::cancel_scope_no_checkpoint(checker, with_stmt, items);
|
flake8_async::rules::cancel_scope_no_checkpoint(checker, with_stmt, items);
|
||||||
}
|
}
|
||||||
if *is_async {
|
|
||||||
if checker.enabled(Rule::AwaitOutsideAsync) {
|
|
||||||
pylint::rules::await_outside_async(checker, stmt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Stmt::While(while_stmt @ ast::StmtWhile { body, orelse, .. }) => {
|
Stmt::While(while_stmt @ ast::StmtWhile { body, orelse, .. }) => {
|
||||||
if checker.enabled(Rule::TooManyNestedBlocks) {
|
if checker.enabled(Rule::TooManyNestedBlocks) {
|
||||||
|
@ -1377,11 +1365,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||||
if checker.enabled(Rule::ReadlinesInFor) {
|
if checker.enabled(Rule::ReadlinesInFor) {
|
||||||
refurb::rules::readlines_in_for(checker, for_stmt);
|
refurb::rules::readlines_in_for(checker, for_stmt);
|
||||||
}
|
}
|
||||||
if *is_async {
|
if !*is_async {
|
||||||
if checker.enabled(Rule::AwaitOutsideAsync) {
|
|
||||||
pylint::rules::await_outside_async(checker, stmt);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if checker.enabled(Rule::ReimplementedBuiltin) {
|
if checker.enabled(Rule::ReimplementedBuiltin) {
|
||||||
flake8_simplify::rules::convert_for_loop_to_any_all(checker, stmt);
|
flake8_simplify::rules::convert_for_loop_to_any_all(checker, stmt);
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@ use crate::registry::Rule;
|
||||||
use crate::rules::pyflakes::rules::{
|
use crate::rules::pyflakes::rules::{
|
||||||
LateFutureImport, ReturnOutsideFunction, YieldOutsideFunction,
|
LateFutureImport, ReturnOutsideFunction, YieldOutsideFunction,
|
||||||
};
|
};
|
||||||
use crate::rules::pylint::rules::LoadBeforeGlobalDeclaration;
|
use crate::rules::pylint::rules::{AwaitOutsideAsync, LoadBeforeGlobalDeclaration};
|
||||||
use crate::rules::{flake8_pyi, flake8_type_checking, pyflakes, pyupgrade};
|
use crate::rules::{flake8_pyi, flake8_type_checking, pyflakes, pyupgrade};
|
||||||
use crate::settings::{flags, LinterSettings};
|
use crate::settings::{flags, LinterSettings};
|
||||||
use crate::{docstrings, noqa, Locator};
|
use crate::{docstrings, noqa, Locator};
|
||||||
|
@ -604,6 +604,11 @@ impl SemanticSyntaxContext for Checker<'_> {
|
||||||
self.report_diagnostic(Diagnostic::new(ReturnOutsideFunction, error.range));
|
self.report_diagnostic(Diagnostic::new(ReturnOutsideFunction, error.range));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SemanticSyntaxErrorKind::AwaitOutsideAsyncFunction(_) => {
|
||||||
|
if self.settings.rules.enabled(Rule::AwaitOutsideAsync) {
|
||||||
|
self.report_diagnostic(Diagnostic::new(AwaitOutsideAsync, error.range));
|
||||||
|
}
|
||||||
|
}
|
||||||
SemanticSyntaxErrorKind::ReboundComprehensionVariable
|
SemanticSyntaxErrorKind::ReboundComprehensionVariable
|
||||||
| SemanticSyntaxErrorKind::DuplicateTypeParameter
|
| SemanticSyntaxErrorKind::DuplicateTypeParameter
|
||||||
| SemanticSyntaxErrorKind::MultipleCaseAssignment(_)
|
| SemanticSyntaxErrorKind::MultipleCaseAssignment(_)
|
||||||
|
@ -680,6 +685,16 @@ impl SemanticSyntaxContext for Checker<'_> {
|
||||||
fn in_notebook(&self) -> bool {
|
fn in_notebook(&self) -> bool {
|
||||||
self.source_type.is_ipynb()
|
self.source_type.is_ipynb()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn in_generator_scope(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
&self.semantic.current_scope().kind,
|
||||||
|
ScopeKind::Generator {
|
||||||
|
kind: GeneratorKind::Generator,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Visitor<'a> for Checker<'a> {
|
impl<'a> Visitor<'a> for Checker<'a> {
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::Violation;
|
||||||
use ruff_macros::{derive_message_formats, ViolationMetadata};
|
use ruff_macros::{derive_message_formats, ViolationMetadata};
|
||||||
use ruff_python_semantic::{GeneratorKind, ScopeKind};
|
|
||||||
use ruff_text_size::Ranged;
|
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
|
||||||
|
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks for uses of `await` outside `async` functions.
|
/// Checks for uses of `await` outside `async` functions.
|
||||||
|
@ -47,39 +43,3 @@ impl Violation for AwaitOutsideAsync {
|
||||||
"`await` should be used within an async function".to_string()
|
"`await` should be used within an async function".to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PLE1142
|
|
||||||
pub(crate) fn await_outside_async<T: Ranged>(checker: &Checker, node: T) {
|
|
||||||
// If we're in an `async` function, we're good.
|
|
||||||
if checker.semantic().in_async_context() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// `await` is allowed at the top level of a Jupyter notebook.
|
|
||||||
// See: https://ipython.readthedocs.io/en/stable/interactive/autoawait.html.
|
|
||||||
if checker.semantic().current_scope().kind.is_module() && checker.source_type.is_ipynb() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generators are evaluated lazily, so you can use `await` in them. For example:
|
|
||||||
// ```python
|
|
||||||
// # This is valid
|
|
||||||
// (await x for x in y)
|
|
||||||
// (x async for x in y)
|
|
||||||
//
|
|
||||||
// # This is invalid
|
|
||||||
// (x for x in async y)
|
|
||||||
// [await x for x in y]
|
|
||||||
// ```
|
|
||||||
if matches!(
|
|
||||||
checker.semantic().current_scope().kind,
|
|
||||||
ScopeKind::Generator {
|
|
||||||
kind: GeneratorKind::Generator,
|
|
||||||
..
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
checker.report_diagnostic(Diagnostic::new(AwaitOutsideAsync, node.range()));
|
|
||||||
}
|
|
||||||
|
|
|
@ -63,3 +63,24 @@ await_outside_async.py:74:17: PLE1142 `await` should be used within an async fun
|
||||||
74 | [x for x in await foo()]
|
74 | [x for x in await foo()]
|
||||||
| ^^^^^^^^^^^ PLE1142
|
| ^^^^^^^^^^^ PLE1142
|
||||||
|
|
|
|
||||||
|
|
||||||
|
await_outside_async.py:78:6: PLE1142 `await` should be used within an async function
|
||||||
|
|
|
||||||
|
77 | def async_for_dictionary_comprehension_key():
|
||||||
|
78 | {await x: y for x, y in foo()}
|
||||||
|
| ^^^^^^^ PLE1142
|
||||||
|
|
|
||||||
|
|
||||||
|
await_outside_async.py:82:9: PLE1142 `await` should be used within an async function
|
||||||
|
|
|
||||||
|
81 | def async_for_dictionary_comprehension_value():
|
||||||
|
82 | {y: await x for x, y in foo()}
|
||||||
|
| ^^^^^^^ PLE1142
|
||||||
|
|
|
||||||
|
|
||||||
|
await_outside_async.py:86:11: PLE1142 `await` should be used within an async function
|
||||||
|
|
|
||||||
|
85 | def async_for_dict_comprehension():
|
||||||
|
86 | {x: y async for x, y in foo()}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^ PLE1142
|
||||||
|
|
|
||||||
|
|
|
@ -103,12 +103,31 @@ impl SemanticSyntaxChecker {
|
||||||
Self::add_error(ctx, SemanticSyntaxErrorKind::ReturnOutsideFunction, *range);
|
Self::add_error(ctx, SemanticSyntaxErrorKind::ReturnOutsideFunction, *range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stmt::For(ast::StmtFor { target, iter, .. }) => {
|
Stmt::For(ast::StmtFor {
|
||||||
|
target,
|
||||||
|
iter,
|
||||||
|
is_async,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
// test_err single_star_for
|
// test_err single_star_for
|
||||||
// for _ in *x: ...
|
// for _ in *x: ...
|
||||||
// for *x in xs: ...
|
// for *x in xs: ...
|
||||||
Self::invalid_star_expression(target, ctx);
|
Self::invalid_star_expression(target, ctx);
|
||||||
Self::invalid_star_expression(iter, ctx);
|
Self::invalid_star_expression(iter, ctx);
|
||||||
|
if *is_async {
|
||||||
|
Self::await_outside_async_function(
|
||||||
|
ctx,
|
||||||
|
stmt,
|
||||||
|
AwaitOutsideAsyncFunctionKind::AsyncFor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Stmt::With(ast::StmtWith { is_async: true, .. }) => {
|
||||||
|
Self::await_outside_async_function(
|
||||||
|
ctx,
|
||||||
|
stmt,
|
||||||
|
AwaitOutsideAsyncFunctionKind::AsyncWith,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -514,11 +533,13 @@ impl SemanticSyntaxChecker {
|
||||||
}) => {
|
}) => {
|
||||||
Self::check_generator_expr(elt, generators, ctx);
|
Self::check_generator_expr(elt, generators, ctx);
|
||||||
Self::async_comprehension_outside_async_function(ctx, generators);
|
Self::async_comprehension_outside_async_function(ctx, generators);
|
||||||
}
|
for generator in generators.iter().filter(|g| g.is_async) {
|
||||||
Expr::Generator(ast::ExprGenerator {
|
Self::await_outside_async_function(
|
||||||
elt, generators, ..
|
ctx,
|
||||||
}) => {
|
generator,
|
||||||
Self::check_generator_expr(elt, generators, ctx);
|
AwaitOutsideAsyncFunctionKind::AsyncComprehension,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Expr::DictComp(ast::ExprDictComp {
|
Expr::DictComp(ast::ExprDictComp {
|
||||||
key,
|
key,
|
||||||
|
@ -529,6 +550,20 @@ impl SemanticSyntaxChecker {
|
||||||
Self::check_generator_expr(key, generators, ctx);
|
Self::check_generator_expr(key, generators, ctx);
|
||||||
Self::check_generator_expr(value, generators, ctx);
|
Self::check_generator_expr(value, generators, ctx);
|
||||||
Self::async_comprehension_outside_async_function(ctx, generators);
|
Self::async_comprehension_outside_async_function(ctx, generators);
|
||||||
|
for generator in generators.iter().filter(|g| g.is_async) {
|
||||||
|
Self::await_outside_async_function(
|
||||||
|
ctx,
|
||||||
|
generator,
|
||||||
|
AwaitOutsideAsyncFunctionKind::AsyncComprehension,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::Generator(ast::ExprGenerator {
|
||||||
|
elt, generators, ..
|
||||||
|
}) => {
|
||||||
|
Self::check_generator_expr(elt, generators, ctx);
|
||||||
|
// Note that `await_outside_async_function` is not called here because generators
|
||||||
|
// are evaluated lazily. See the note in the function for more details.
|
||||||
}
|
}
|
||||||
Expr::Name(ast::ExprName {
|
Expr::Name(ast::ExprName {
|
||||||
range,
|
range,
|
||||||
|
@ -603,11 +638,53 @@ impl SemanticSyntaxChecker {
|
||||||
}
|
}
|
||||||
Expr::Await(_) => {
|
Expr::Await(_) => {
|
||||||
Self::yield_outside_function(ctx, expr, YieldOutsideFunctionKind::Await);
|
Self::yield_outside_function(ctx, expr, YieldOutsideFunctionKind::Await);
|
||||||
|
Self::await_outside_async_function(ctx, expr, AwaitOutsideAsyncFunctionKind::Await);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// PLE1142
|
||||||
|
fn await_outside_async_function<Ctx: SemanticSyntaxContext, Node: Ranged>(
|
||||||
|
ctx: &Ctx,
|
||||||
|
node: Node,
|
||||||
|
kind: AwaitOutsideAsyncFunctionKind,
|
||||||
|
) {
|
||||||
|
if ctx.in_async_context() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// `await` is allowed at the top level of a Jupyter notebook.
|
||||||
|
// See: https://ipython.readthedocs.io/en/stable/interactive/autoawait.html.
|
||||||
|
if ctx.in_module_scope() && ctx.in_notebook() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Generators are evaluated lazily, so you can use `await` in them. For example:
|
||||||
|
//
|
||||||
|
// ```python
|
||||||
|
// # This is valid
|
||||||
|
// def f():
|
||||||
|
// (await x for x in y)
|
||||||
|
// (x async for x in y)
|
||||||
|
//
|
||||||
|
// # This is invalid
|
||||||
|
// def f():
|
||||||
|
// (x for x in await y)
|
||||||
|
// [await x for x in y]
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// This check is required in addition to avoiding calling this function in `visit_expr`
|
||||||
|
// because the generator scope applies to nested parts of the `Expr::Generator` that are
|
||||||
|
// visited separately.
|
||||||
|
if ctx.in_generator_scope() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Self::add_error(
|
||||||
|
ctx,
|
||||||
|
SemanticSyntaxErrorKind::AwaitOutsideAsyncFunction(kind),
|
||||||
|
node.range(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// F704
|
/// F704
|
||||||
fn yield_outside_function<Ctx: SemanticSyntaxContext>(
|
fn yield_outside_function<Ctx: SemanticSyntaxContext>(
|
||||||
ctx: &Ctx,
|
ctx: &Ctx,
|
||||||
|
@ -803,6 +880,9 @@ impl Display for SemanticSyntaxError {
|
||||||
SemanticSyntaxErrorKind::ReturnOutsideFunction => {
|
SemanticSyntaxErrorKind::ReturnOutsideFunction => {
|
||||||
f.write_str("`return` statement outside of a function")
|
f.write_str("`return` statement outside of a function")
|
||||||
}
|
}
|
||||||
|
SemanticSyntaxErrorKind::AwaitOutsideAsyncFunction(kind) => {
|
||||||
|
write!(f, "`{kind}` outside of an asynchronous function")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1101,6 +1181,38 @@ pub enum SemanticSyntaxErrorKind {
|
||||||
|
|
||||||
/// Represents the use of `return` outside of a function scope.
|
/// Represents the use of `return` outside of a function scope.
|
||||||
ReturnOutsideFunction,
|
ReturnOutsideFunction,
|
||||||
|
|
||||||
|
/// Represents the use of `await`, `async for`, or `async with` outside of an asynchronous
|
||||||
|
/// function.
|
||||||
|
///
|
||||||
|
/// ## Examples
|
||||||
|
///
|
||||||
|
/// ```python
|
||||||
|
/// def f():
|
||||||
|
/// await 1 # error
|
||||||
|
/// async for x in y: ... # error
|
||||||
|
/// async with x: ... # error
|
||||||
|
/// ```
|
||||||
|
AwaitOutsideAsyncFunction(AwaitOutsideAsyncFunctionKind),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub enum AwaitOutsideAsyncFunctionKind {
|
||||||
|
Await,
|
||||||
|
AsyncFor,
|
||||||
|
AsyncWith,
|
||||||
|
AsyncComprehension,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for AwaitOutsideAsyncFunctionKind {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str(match self {
|
||||||
|
AwaitOutsideAsyncFunctionKind::Await => "await",
|
||||||
|
AwaitOutsideAsyncFunctionKind::AsyncFor => "async for",
|
||||||
|
AwaitOutsideAsyncFunctionKind::AsyncWith => "async with",
|
||||||
|
AwaitOutsideAsyncFunctionKind::AsyncComprehension => "asynchronous comprehension",
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
@ -1527,6 +1639,12 @@ pub trait SemanticSyntaxContext {
|
||||||
/// Returns `true` if the visitor is in a function scope.
|
/// Returns `true` if the visitor is in a function scope.
|
||||||
fn in_function_scope(&self) -> bool;
|
fn in_function_scope(&self) -> bool;
|
||||||
|
|
||||||
|
/// Returns `true` if the visitor is in a generator scope.
|
||||||
|
///
|
||||||
|
/// Note that this refers to an `Expr::Generator` precisely, not to comprehensions more
|
||||||
|
/// generally.
|
||||||
|
fn in_generator_scope(&self) -> bool;
|
||||||
|
|
||||||
/// Returns `true` if the source file is a Jupyter notebook.
|
/// Returns `true` if the source file is a Jupyter notebook.
|
||||||
fn in_notebook(&self) -> bool;
|
fn in_notebook(&self) -> bool;
|
||||||
|
|
||||||
|
|
|
@ -462,7 +462,7 @@ impl<'ast> SourceOrderVisitor<'ast> for ValidateAstVisitor<'ast> {
|
||||||
|
|
||||||
enum Scope {
|
enum Scope {
|
||||||
Module,
|
Module,
|
||||||
Function { is_async: bool },
|
Function,
|
||||||
Comprehension { is_async: bool },
|
Comprehension { is_async: bool },
|
||||||
Class,
|
Class,
|
||||||
}
|
}
|
||||||
|
@ -529,12 +529,7 @@ impl SemanticSyntaxContext for SemanticSyntaxCheckerVisitor<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn in_async_context(&self) -> bool {
|
fn in_async_context(&self) -> bool {
|
||||||
for scope in &self.scopes {
|
true
|
||||||
if let Scope::Function { is_async } = scope {
|
|
||||||
return *is_async;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn in_sync_comprehension(&self) -> bool {
|
fn in_sync_comprehension(&self) -> bool {
|
||||||
|
@ -561,6 +556,10 @@ impl SemanticSyntaxContext for SemanticSyntaxCheckerVisitor<'_> {
|
||||||
fn in_await_allowed_context(&self) -> bool {
|
fn in_await_allowed_context(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn in_generator_scope(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Visitor<'_> for SemanticSyntaxCheckerVisitor<'_> {
|
impl Visitor<'_> for SemanticSyntaxCheckerVisitor<'_> {
|
||||||
|
@ -587,10 +586,8 @@ impl Visitor<'_> for SemanticSyntaxCheckerVisitor<'_> {
|
||||||
self.visit_body(body);
|
self.visit_body(body);
|
||||||
self.scopes.pop().unwrap();
|
self.scopes.pop().unwrap();
|
||||||
}
|
}
|
||||||
ast::Stmt::FunctionDef(ast::StmtFunctionDef { is_async, .. }) => {
|
ast::Stmt::FunctionDef(ast::StmtFunctionDef { .. }) => {
|
||||||
self.scopes.push(Scope::Function {
|
self.scopes.push(Scope::Function);
|
||||||
is_async: *is_async,
|
|
||||||
});
|
|
||||||
ast::visitor::walk_stmt(self, stmt);
|
ast::visitor::walk_stmt(self, stmt);
|
||||||
self.scopes.pop().unwrap();
|
self.scopes.pop().unwrap();
|
||||||
}
|
}
|
||||||
|
@ -604,7 +601,7 @@ impl Visitor<'_> for SemanticSyntaxCheckerVisitor<'_> {
|
||||||
self.with_semantic_checker(|semantic, context| semantic.visit_expr(expr, context));
|
self.with_semantic_checker(|semantic, context| semantic.visit_expr(expr, context));
|
||||||
match expr {
|
match expr {
|
||||||
ast::Expr::Lambda(_) => {
|
ast::Expr::Lambda(_) => {
|
||||||
self.scopes.push(Scope::Function { is_async: false });
|
self.scopes.push(Scope::Function);
|
||||||
ast::visitor::walk_expr(self, expr);
|
ast::visitor::walk_expr(self, expr);
|
||||||
self.scopes.pop().unwrap();
|
self.scopes.pop().unwrap();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue