Remove async AST node variants for with, for, and def (#6369)
## Summary Per the suggestion in https://github.com/astral-sh/ruff/discussions/6183, this PR removes `AsyncWith`, `AsyncFor`, and `AsyncFunctionDef`, replacing them with an `is_async` field on the non-async variants of those structs. Unlike an interpreter, we _generally_ have identical handling for these nodes, so separating them into distinct variants adds complexity from which we don't really benefit. This can be seen below, where we get to remove a _ton_ of code related to adding generic `Any*` wrappers, and a ton of duplicate branches for these cases. ## Test Plan `cargo test` is unchanged, apart from parser snapshots.
This commit is contained in:
@@ -178,8 +178,7 @@ impl ModuleSource<'_> {
|
||||
|
||||
pub(crate) fn function_visibility(stmt: &Stmt) -> Visibility {
|
||||
match stmt {
|
||||
Stmt::FunctionDef(ast::StmtFunctionDef { name, .. })
|
||||
| Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef { name, .. }) => {
|
||||
Stmt::FunctionDef(ast::StmtFunctionDef { name, .. }) => {
|
||||
if name.starts_with('_') {
|
||||
Visibility::Private
|
||||
} else {
|
||||
@@ -191,52 +190,45 @@ pub(crate) fn function_visibility(stmt: &Stmt) -> Visibility {
|
||||
}
|
||||
|
||||
pub(crate) fn method_visibility(stmt: &Stmt) -> Visibility {
|
||||
match stmt {
|
||||
Stmt::FunctionDef(ast::StmtFunctionDef {
|
||||
name,
|
||||
decorator_list,
|
||||
..
|
||||
let Stmt::FunctionDef(ast::StmtFunctionDef {
|
||||
name,
|
||||
decorator_list,
|
||||
..
|
||||
}) = stmt
|
||||
else {
|
||||
panic!("Found non-FunctionDef in method_visibility")
|
||||
};
|
||||
|
||||
// Is this a setter or deleter?
|
||||
if decorator_list.iter().any(|decorator| {
|
||||
collect_call_path(&decorator.expression).is_some_and(|call_path| {
|
||||
call_path.as_slice() == [name, "setter"] || call_path.as_slice() == [name, "deleter"]
|
||||
})
|
||||
| Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef {
|
||||
name,
|
||||
decorator_list,
|
||||
..
|
||||
}) => {
|
||||
// Is this a setter or deleter?
|
||||
if decorator_list.iter().any(|decorator| {
|
||||
collect_call_path(&decorator.expression).is_some_and(|call_path| {
|
||||
call_path.as_slice() == [name, "setter"]
|
||||
|| call_path.as_slice() == [name, "deleter"]
|
||||
})
|
||||
}) {
|
||||
return Visibility::Private;
|
||||
}
|
||||
|
||||
// Is the method non-private?
|
||||
if !name.starts_with('_') {
|
||||
return Visibility::Public;
|
||||
}
|
||||
|
||||
// Is this a magic method?
|
||||
if name.starts_with("__") && name.ends_with("__") {
|
||||
return Visibility::Public;
|
||||
}
|
||||
|
||||
Visibility::Private
|
||||
}
|
||||
_ => panic!("Found non-FunctionDef in method_visibility"),
|
||||
}) {
|
||||
return Visibility::Private;
|
||||
}
|
||||
|
||||
// Is the method non-private?
|
||||
if !name.starts_with('_') {
|
||||
return Visibility::Public;
|
||||
}
|
||||
|
||||
// Is this a magic method?
|
||||
if name.starts_with("__") && name.ends_with("__") {
|
||||
return Visibility::Public;
|
||||
}
|
||||
|
||||
Visibility::Private
|
||||
}
|
||||
|
||||
pub(crate) fn class_visibility(stmt: &Stmt) -> Visibility {
|
||||
match stmt {
|
||||
Stmt::ClassDef(ast::StmtClassDef { name, .. }) => {
|
||||
if name.starts_with('_') {
|
||||
Visibility::Private
|
||||
} else {
|
||||
Visibility::Public
|
||||
}
|
||||
}
|
||||
_ => panic!("Found non-ClassDef in function_visibility"),
|
||||
let Stmt::ClassDef(ast::StmtClassDef { name, .. }) = stmt else {
|
||||
panic!("Found non-ClassDef in class_visibility")
|
||||
};
|
||||
|
||||
if name.starts_with('_') {
|
||||
Visibility::Private
|
||||
} else {
|
||||
Visibility::Public
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,6 @@ impl<'a> Member<'a> {
|
||||
pub fn name(&self) -> Option<&'a str> {
|
||||
match &self.stmt {
|
||||
Stmt::FunctionDef(ast::StmtFunctionDef { name, .. })
|
||||
| Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef { name, .. })
|
||||
| Stmt::ClassDef(ast::StmtClassDef { name, .. }) => Some(name),
|
||||
_ => None,
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ impl<'a> StatementVisitor<'a> for GlobalsVisitor<'a> {
|
||||
self.0.insert(name.as_str(), *range);
|
||||
}
|
||||
}
|
||||
Stmt::FunctionDef(_) | Stmt::AsyncFunctionDef(_) | Stmt::ClassDef(_) => {
|
||||
Stmt::FunctionDef(_) | Stmt::ClassDef(_) => {
|
||||
// Don't recurse.
|
||||
}
|
||||
_ => walk_stmt(self, stmt),
|
||||
|
||||
@@ -478,7 +478,7 @@ impl<'a> SemanticModel<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
seen_function |= scope.kind.is_any_function();
|
||||
seen_function |= scope.kind.is_function();
|
||||
import_starred = import_starred || scope.uses_star_imports();
|
||||
}
|
||||
|
||||
@@ -540,7 +540,7 @@ impl<'a> SemanticModel<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
seen_function |= scope.kind.is_any_function();
|
||||
seen_function |= scope.kind.is_function();
|
||||
}
|
||||
|
||||
None
|
||||
@@ -1015,11 +1015,8 @@ impl<'a> SemanticModel<'a> {
|
||||
/// Return `true` if the model is in an async context.
|
||||
pub fn in_async_context(&self) -> bool {
|
||||
for scope in self.current_scopes() {
|
||||
if scope.kind.is_async_function() {
|
||||
return true;
|
||||
}
|
||||
if scope.kind.is_function() {
|
||||
return false;
|
||||
if let ScopeKind::Function(ast::StmtFunctionDef { is_async, .. }) = scope.kind {
|
||||
return *is_async;
|
||||
}
|
||||
}
|
||||
false
|
||||
|
||||
@@ -178,19 +178,12 @@ bitflags! {
|
||||
pub enum ScopeKind<'a> {
|
||||
Class(&'a ast::StmtClassDef),
|
||||
Function(&'a ast::StmtFunctionDef),
|
||||
AsyncFunction(&'a ast::StmtAsyncFunctionDef),
|
||||
Generator,
|
||||
Module,
|
||||
Type,
|
||||
Lambda(&'a ast::ExprLambda),
|
||||
}
|
||||
|
||||
impl ScopeKind<'_> {
|
||||
pub const fn is_any_function(&self) -> bool {
|
||||
matches!(self, ScopeKind::Function(_) | ScopeKind::AsyncFunction(_))
|
||||
}
|
||||
}
|
||||
|
||||
/// Id uniquely identifying a scope in a program.
|
||||
///
|
||||
/// Using a `u32` is sufficient because Ruff only supports parsing documents with a size of max `u32::max`
|
||||
|
||||
Reference in New Issue
Block a user