Introduce an Arguments AST node for function calls and class definitions (#6259)

## Summary

This PR adds a new `Arguments` AST node, which we can use for function
calls and class definitions.

The `Arguments` node spans from the left (open) to right (close)
parentheses inclusive.

In the case of classes, the `Arguments` is an option, to differentiate
between:

```python
# None
class C: ...

# Some, with empty vectors
class C(): ...
```

In this PR, we don't really leverage this change (except that a few
rules get much simpler, since we don't need to lex to find the start and
end ranges of the parentheses, e.g.,
`crates/ruff/src/rules/pyupgrade/rules/lru_cache_without_parameters.rs`,
`crates/ruff/src/rules/pyupgrade/rules/unnecessary_class_parentheses.rs`).

In future PRs, this will be especially helpful for the formatter, since
we can track comments enclosed on the node itself.

## Test Plan

`cargo test`
This commit is contained in:
Charlie Marsh 2023-08-02 10:01:13 -04:00 committed by GitHub
parent 0d62ad2480
commit 981e64f82b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
107 changed files with 24258 additions and 23835 deletions

View file

@ -64,8 +64,7 @@ where
}
Expr::Call(ast::ExprCall {
func,
args,
keywords,
arguments: ast::Arguments { args, keywords, .. },
..
}) => {
// Allow `tuple()`, `list()`, and their generic forms, like `list[int]()`.

View file

@ -339,6 +339,27 @@ impl<'a> From<&'a ast::Constant> for ComparableConstant<'a> {
}
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct ComparableArguments<'a> {
args: Vec<ComparableExpr<'a>>,
keywords: Vec<ComparableKeyword<'a>>,
}
impl<'a> From<&'a ast::Arguments> for ComparableArguments<'a> {
fn from(arguments: &'a ast::Arguments) -> Self {
Self {
args: arguments.args.iter().map(Into::into).collect(),
keywords: arguments.keywords.iter().map(Into::into).collect(),
}
}
}
impl<'a> From<&'a Box<ast::Arguments>> for ComparableArguments<'a> {
fn from(arguments: &'a Box<ast::Arguments>) -> Self {
(arguments.as_ref()).into()
}
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct ComparableParameters<'a> {
posonlyargs: Vec<ComparableParameterWithDefault<'a>>,
@ -583,8 +604,7 @@ pub struct ExprCompare<'a> {
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct ExprCall<'a> {
func: Box<ComparableExpr<'a>>,
args: Vec<ComparableExpr<'a>>,
keywords: Vec<ComparableKeyword<'a>>,
arguments: ComparableArguments<'a>,
}
#[derive(Debug, PartialEq, Eq, Hash)]
@ -837,13 +857,11 @@ impl<'a> From<&'a ast::Expr> for ComparableExpr<'a> {
}),
ast::Expr::Call(ast::ExprCall {
func,
args,
keywords,
arguments,
range: _range,
}) => Self::Call(ExprCall {
func: func.into(),
args: args.iter().map(Into::into).collect(),
keywords: keywords.iter().map(Into::into).collect(),
arguments: arguments.into(),
}),
ast::Expr::FormattedValue(ast::ExprFormattedValue {
value,
@ -968,8 +986,7 @@ pub struct StmtAsyncFunctionDef<'a> {
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct StmtClassDef<'a> {
name: &'a str,
bases: Vec<ComparableExpr<'a>>,
keywords: Vec<ComparableKeyword<'a>>,
arguments: Option<ComparableArguments<'a>>,
body: Vec<ComparableStmt<'a>>,
decorator_list: Vec<ComparableDecorator<'a>>,
type_params: Vec<ComparableTypeParam<'a>>,
@ -1240,16 +1257,14 @@ impl<'a> From<&'a ast::Stmt> for ComparableStmt<'a> {
}),
ast::Stmt::ClassDef(ast::StmtClassDef {
name,
bases,
keywords,
arguments,
body,
decorator_list,
type_params,
range: _range,
}) => Self::ClassDef(StmtClassDef {
name: name.as_str(),
bases: bases.iter().map(Into::into).collect(),
keywords: keywords.iter().map(Into::into).collect(),
arguments: arguments.as_ref().map(Into::into),
body: body.iter().map(Into::into).collect(),
decorator_list: decorator_list.iter().map(Into::into).collect(),
type_params: type_params.iter().map(Into::into).collect(),

View file

@ -2,8 +2,8 @@ use std::borrow::Cow;
use std::path::Path;
use crate::{
self as ast, Constant, ExceptHandler, Expr, Keyword, MatchCase, Parameters, Pattern, Ranged,
Stmt, TypeParam,
self as ast, Arguments, Constant, ExceptHandler, Expr, Keyword, MatchCase, Parameters, Pattern,
Ranged, Stmt, TypeParam,
};
use num_traits::Zero;
use ruff_text_size::TextRange;
@ -50,8 +50,7 @@ where
// Accept empty initializers.
if let Expr::Call(ast::ExprCall {
func,
args,
keywords,
arguments: Arguments { args, keywords, .. },
range: _range,
}) = expr
{
@ -237,8 +236,7 @@ where
}) => any_over_expr(left, func) || comparators.iter().any(|expr| any_over_expr(expr, func)),
Expr::Call(ast::ExprCall {
func: call_func,
args,
keywords,
arguments: Arguments { args, keywords, .. },
range: _range,
}) => {
any_over_expr(call_func, func)
@ -396,16 +394,19 @@ where
.is_some_and(|value| any_over_expr(value, func))
}
Stmt::ClassDef(ast::StmtClassDef {
bases,
keywords,
arguments,
body,
decorator_list,
..
}) => {
bases.iter().any(|expr| any_over_expr(expr, func))
|| keywords
.iter()
.any(|keyword| any_over_expr(&keyword.value, func))
arguments
.as_ref()
.is_some_and(|Arguments { args, keywords, .. }| {
args.iter().any(|expr| any_over_expr(expr, func))
|| keywords
.iter()
.any(|keyword| any_over_expr(&keyword.value, func))
})
|| body.iter().any(|stmt| any_over_stmt(stmt, func))
|| decorator_list
.iter()
@ -640,6 +641,8 @@ pub fn is_constant_non_singleton(expr: &Expr) -> bool {
/// Return the [`Keyword`] with the given name, if it's present in the list of
/// [`Keyword`] arguments.
///
/// TODO(charlie): Make this an associated function on [`Arguments`].
pub fn find_keyword<'a>(keywords: &'a [Keyword], keyword_name: &str) -> Option<&'a Keyword> {
keywords.iter().find(|keyword| {
let Keyword { arg, .. } = keyword;
@ -1229,30 +1232,14 @@ impl Truthiness {
None
}
}
Expr::List(ast::ExprList {
elts,
range: _range,
..
})
| Expr::Set(ast::ExprSet {
elts,
range: _range,
})
| Expr::Tuple(ast::ExprTuple {
elts,
range: _range,
..
}) => Some(!elts.is_empty()),
Expr::Dict(ast::ExprDict {
keys,
range: _range,
..
}) => Some(!keys.is_empty()),
Expr::List(ast::ExprList { elts, .. })
| Expr::Set(ast::ExprSet { elts, .. })
| Expr::Tuple(ast::ExprTuple { elts, .. }) => Some(!elts.is_empty()),
Expr::Dict(ast::ExprDict { keys, .. }) => Some(!keys.is_empty()),
Expr::Call(ast::ExprCall {
func,
args,
keywords,
range: _range,
arguments: Arguments { args, keywords, .. },
..
}) => {
if let Expr::Name(ast::ExprName { id, .. }) = func.as_ref() {
if is_iterable_initializer(id.as_str(), |id| is_builtin(id)) {

View file

@ -1,6 +1,6 @@
use crate::{
self as ast, Alias, Comprehension, Decorator, ExceptHandler, Expr, Keyword, MatchCase, Mod,
Parameter, ParameterWithDefault, Parameters, Pattern, Ranged, Stmt, TypeParam,
self as ast, Alias, Arguments, Comprehension, Decorator, ExceptHandler, Expr, Keyword,
MatchCase, Mod, Parameter, ParameterWithDefault, Parameters, Pattern, Ranged, Stmt, TypeParam,
TypeParamParamSpec, TypeParamTypeVar, TypeParamTypeVarTuple, WithItem,
};
use ruff_text_size::TextRange;
@ -90,6 +90,7 @@ pub enum AnyNode {
PatternMatchAs(ast::PatternMatchAs),
PatternMatchOr(ast::PatternMatchOr),
Comprehension(Comprehension),
Arguments(Arguments),
Parameters(Parameters),
Parameter(Parameter),
ParameterWithDefault(ParameterWithDefault),
@ -177,6 +178,7 @@ impl AnyNode {
| AnyNode::PatternMatchAs(_)
| AnyNode::PatternMatchOr(_)
| AnyNode::Comprehension(_)
| AnyNode::Arguments(_)
| AnyNode::Parameters(_)
| AnyNode::Parameter(_)
| AnyNode::ParameterWithDefault(_)
@ -264,6 +266,7 @@ impl AnyNode {
| AnyNode::PatternMatchAs(_)
| AnyNode::PatternMatchOr(_)
| AnyNode::Comprehension(_)
| AnyNode::Arguments(_)
| AnyNode::Parameters(_)
| AnyNode::Parameter(_)
| AnyNode::ParameterWithDefault(_)
@ -351,6 +354,7 @@ impl AnyNode {
| AnyNode::PatternMatchAs(_)
| AnyNode::PatternMatchOr(_)
| AnyNode::Comprehension(_)
| AnyNode::Arguments(_)
| AnyNode::Parameters(_)
| AnyNode::Parameter(_)
| AnyNode::ParameterWithDefault(_)
@ -438,6 +442,7 @@ impl AnyNode {
| AnyNode::ExprLineMagic(_)
| AnyNode::ExceptHandlerExceptHandler(_)
| AnyNode::Comprehension(_)
| AnyNode::Arguments(_)
| AnyNode::Parameters(_)
| AnyNode::Parameter(_)
| AnyNode::ParameterWithDefault(_)
@ -525,6 +530,7 @@ impl AnyNode {
| AnyNode::PatternMatchAs(_)
| AnyNode::PatternMatchOr(_)
| AnyNode::Comprehension(_)
| AnyNode::Arguments(_)
| AnyNode::Parameters(_)
| AnyNode::Parameter(_)
| AnyNode::ParameterWithDefault(_)
@ -631,6 +637,7 @@ impl AnyNode {
Self::PatternMatchAs(node) => AnyNodeRef::PatternMatchAs(node),
Self::PatternMatchOr(node) => AnyNodeRef::PatternMatchOr(node),
Self::Comprehension(node) => AnyNodeRef::Comprehension(node),
Self::Arguments(node) => AnyNodeRef::Arguments(node),
Self::Parameters(node) => AnyNodeRef::Parameters(node),
Self::Parameter(node) => AnyNodeRef::Parameter(node),
Self::ParameterWithDefault(node) => AnyNodeRef::ParameterWithDefault(node),
@ -3574,6 +3581,7 @@ impl Ranged for AnyNode {
AnyNode::PatternMatchAs(node) => node.range(),
AnyNode::PatternMatchOr(node) => node.range(),
AnyNode::Comprehension(node) => node.range(),
AnyNode::Arguments(node) => node.range(),
AnyNode::Parameters(node) => node.range(),
AnyNode::Parameter(node) => node.range(),
AnyNode::ParameterWithDefault(node) => node.range(),
@ -3661,6 +3669,7 @@ pub enum AnyNodeRef<'a> {
PatternMatchAs(&'a ast::PatternMatchAs),
PatternMatchOr(&'a ast::PatternMatchOr),
Comprehension(&'a Comprehension),
Arguments(&'a Arguments),
Parameters(&'a Parameters),
Parameter(&'a Parameter),
ParameterWithDefault(&'a ParameterWithDefault),
@ -3747,6 +3756,7 @@ impl AnyNodeRef<'_> {
AnyNodeRef::PatternMatchAs(node) => NonNull::from(*node).cast(),
AnyNodeRef::PatternMatchOr(node) => NonNull::from(*node).cast(),
AnyNodeRef::Comprehension(node) => NonNull::from(*node).cast(),
AnyNodeRef::Arguments(node) => NonNull::from(*node).cast(),
AnyNodeRef::Parameters(node) => NonNull::from(*node).cast(),
AnyNodeRef::Parameter(node) => NonNull::from(*node).cast(),
AnyNodeRef::ParameterWithDefault(node) => NonNull::from(*node).cast(),
@ -3839,6 +3849,7 @@ impl AnyNodeRef<'_> {
AnyNodeRef::PatternMatchAs(_) => NodeKind::PatternMatchAs,
AnyNodeRef::PatternMatchOr(_) => NodeKind::PatternMatchOr,
AnyNodeRef::Comprehension(_) => NodeKind::Comprehension,
AnyNodeRef::Arguments(_) => NodeKind::Arguments,
AnyNodeRef::Parameters(_) => NodeKind::Parameters,
AnyNodeRef::Parameter(_) => NodeKind::Parameter,
AnyNodeRef::ParameterWithDefault(_) => NodeKind::ParameterWithDefault,
@ -3926,6 +3937,7 @@ impl AnyNodeRef<'_> {
| AnyNodeRef::PatternMatchAs(_)
| AnyNodeRef::PatternMatchOr(_)
| AnyNodeRef::Comprehension(_)
| AnyNodeRef::Arguments(_)
| AnyNodeRef::Parameters(_)
| AnyNodeRef::Parameter(_)
| AnyNodeRef::ParameterWithDefault(_)
@ -4013,6 +4025,7 @@ impl AnyNodeRef<'_> {
| AnyNodeRef::PatternMatchAs(_)
| AnyNodeRef::PatternMatchOr(_)
| AnyNodeRef::Comprehension(_)
| AnyNodeRef::Arguments(_)
| AnyNodeRef::Parameters(_)
| AnyNodeRef::Parameter(_)
| AnyNodeRef::ParameterWithDefault(_)
@ -4099,6 +4112,7 @@ impl AnyNodeRef<'_> {
| AnyNodeRef::PatternMatchAs(_)
| AnyNodeRef::PatternMatchOr(_)
| AnyNodeRef::Comprehension(_)
| AnyNodeRef::Arguments(_)
| AnyNodeRef::Parameters(_)
| AnyNodeRef::Parameter(_)
| AnyNodeRef::ParameterWithDefault(_)
@ -4186,6 +4200,7 @@ impl AnyNodeRef<'_> {
| AnyNodeRef::ExprLineMagic(_)
| AnyNodeRef::ExceptHandlerExceptHandler(_)
| AnyNodeRef::Comprehension(_)
| AnyNodeRef::Arguments(_)
| AnyNodeRef::Parameters(_)
| AnyNodeRef::Parameter(_)
| AnyNodeRef::ParameterWithDefault(_)
@ -4273,6 +4288,7 @@ impl AnyNodeRef<'_> {
| AnyNodeRef::PatternMatchAs(_)
| AnyNodeRef::PatternMatchOr(_)
| AnyNodeRef::Comprehension(_)
| AnyNodeRef::Arguments(_)
| AnyNodeRef::Parameters(_)
| AnyNodeRef::Parameter(_)
| AnyNodeRef::ParameterWithDefault(_)
@ -4985,6 +5001,7 @@ impl Ranged for AnyNodeRef<'_> {
AnyNodeRef::PatternMatchAs(node) => node.range(),
AnyNodeRef::PatternMatchOr(node) => node.range(),
AnyNodeRef::Comprehension(node) => node.range(),
AnyNodeRef::Arguments(node) => node.range(),
AnyNodeRef::Parameters(node) => node.range(),
AnyNodeRef::Parameter(node) => node.range(),
AnyNodeRef::ParameterWithDefault(node) => node.range(),
@ -5075,6 +5092,7 @@ pub enum NodeKind {
PatternMatchOr,
TypeIgnoreTypeIgnore,
Comprehension,
Arguments,
Parameters,
Parameter,
ParameterWithDefault,

View file

@ -158,13 +158,32 @@ impl From<StmtAsyncFunctionDef> for Stmt {
pub struct StmtClassDef {
pub range: TextRange,
pub name: Identifier,
pub bases: Vec<Expr>,
pub keywords: Vec<Keyword>,
pub arguments: Option<Arguments>,
pub body: Vec<Stmt>,
pub type_params: Vec<TypeParam>,
pub decorator_list: Vec<Decorator>,
}
impl StmtClassDef {
/// Return an iterator over the bases of the class.
pub fn bases(&self) -> impl Iterator<Item = &Expr> {
self.arguments
.as_ref()
.map(|arguments| &arguments.args)
.into_iter()
.flatten()
}
/// Return an iterator over the metaclass keywords of the class.
pub fn keywords(&self) -> impl Iterator<Item = &Keyword> {
self.arguments
.as_ref()
.map(|arguments| &arguments.keywords)
.into_iter()
.flatten()
}
}
impl From<StmtClassDef> for Stmt {
fn from(payload: StmtClassDef) -> Self {
Stmt::ClassDef(payload)
@ -836,8 +855,7 @@ impl From<ExprCompare> for Expr {
pub struct ExprCall {
pub range: TextRange,
pub func: Box<Expr>,
pub args: Vec<Expr>,
pub keywords: Vec<Keyword>,
pub arguments: Arguments,
}
impl From<ExprCall> for Expr {
@ -2073,6 +2091,35 @@ pub struct ParameterWithDefault {
pub default: Option<Box<Expr>>,
}
/// An AST node used to represent the arguments passed to a function call or class definition.
///
/// For example, given:
/// ```python
/// foo(1, 2, 3, bar=4, baz=5)
/// ```
/// The `Arguments` node would span from the left to right parentheses (inclusive), and contain
/// the arguments and keyword arguments in the order they appear in the source code.
///
/// Similarly, given:
/// ```python
/// class Foo(Bar, baz=1, qux=2):
/// pass
/// ```
/// The `Arguments` node would again span from the left to right parentheses (inclusive), and
/// contain the `Bar` argument and the `baz` and `qux` keyword arguments in the order they
/// appear in the source code.
///
/// In the context of a class definition, the Python-style AST refers to the arguments as `bases`,
/// as they represent the "explicitly specified base classes", while the keyword arguments are
/// typically used for `metaclass`, with any additional arguments being passed to the `metaclass`.
#[derive(Clone, Debug, PartialEq)]
pub struct Arguments {
pub range: TextRange,
pub args: Vec<Expr>,
pub keywords: Vec<Keyword>,
}
pub type Suite = Vec<Stmt>;
impl CmpOp {
@ -2934,6 +2981,11 @@ impl Ranged for crate::nodes::Decorator {
self.range
}
}
impl Ranged for crate::nodes::Arguments {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::nodes::Parameters {
fn range(&self) -> TextRange {
self.range
@ -2951,9 +3003,9 @@ mod size_assertions {
use super::*;
use static_assertions::assert_eq_size;
assert_eq_size!(Stmt, [u8; 168]);
assert_eq_size!(Stmt, [u8; 176]);
assert_eq_size!(StmtFunctionDef, [u8; 128]);
assert_eq_size!(StmtClassDef, [u8; 160]);
assert_eq_size!(StmtClassDef, [u8; 168]);
assert_eq_size!(StmtTry, [u8; 104]);
assert_eq_size!(Expr, [u8; 80]);
assert_eq_size!(Constant, [u8; 32]);

View file

@ -1,4 +1,4 @@
use crate::{nodes, Expr, Keyword};
use crate::{nodes, Arguments, Expr, Keyword};
use ruff_text_size::TextRange;
fn relocate_keyword(keyword: &mut Keyword, location: TextRange) {
@ -116,8 +116,7 @@ pub fn relocate_expr(expr: &mut Expr, location: TextRange) {
}
Expr::Call(nodes::ExprCall {
func,
args,
keywords,
arguments: Arguments { args, keywords, .. },
range,
}) => {
*range = location;

View file

@ -3,9 +3,9 @@
pub mod preorder;
use crate::{
self as ast, Alias, BoolOp, CmpOp, Comprehension, Decorator, ElifElseClause, ExceptHandler,
Expr, ExprContext, Keyword, MatchCase, Operator, Parameter, Parameters, Pattern, Stmt,
TypeParam, TypeParamTypeVar, UnaryOp, WithItem,
self as ast, Alias, Arguments, BoolOp, CmpOp, Comprehension, Decorator, ElifElseClause,
ExceptHandler, Expr, ExprContext, Keyword, MatchCase, Operator, Parameter, Parameters, Pattern,
Stmt, TypeParam, TypeParamTypeVar, UnaryOp, WithItem,
};
/// A trait for AST visitors. Visits all nodes in the AST recursively in evaluation-order.
@ -52,6 +52,9 @@ pub trait Visitor<'a> {
fn visit_format_spec(&mut self, format_spec: &'a Expr) {
walk_format_spec(self, format_spec);
}
fn visit_arguments(&mut self, arguments: &'a Arguments) {
walk_arguments(self, arguments);
}
fn visit_parameters(&mut self, parameters: &'a Parameters) {
walk_parameters(self, parameters);
}
@ -143,8 +146,7 @@ pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) {
visitor.visit_body(body);
}
Stmt::ClassDef(ast::StmtClassDef {
bases,
keywords,
arguments,
body,
decorator_list,
type_params,
@ -156,11 +158,8 @@ pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) {
for type_param in type_params {
visitor.visit_type_param(type_param);
}
for expr in bases {
visitor.visit_expr(expr);
}
for keyword in keywords {
visitor.visit_keyword(keyword);
if let Some(arguments) = arguments {
visitor.visit_arguments(arguments);
}
visitor.visit_body(body);
}
@ -522,17 +521,11 @@ pub fn walk_expr<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, expr: &'a Expr) {
}
Expr::Call(ast::ExprCall {
func,
args,
keywords,
arguments,
range: _range,
}) => {
visitor.visit_expr(func);
for expr in args {
visitor.visit_expr(expr);
}
for keyword in keywords {
visitor.visit_keyword(keyword);
}
visitor.visit_arguments(arguments);
}
Expr::FormattedValue(ast::ExprFormattedValue {
value, format_spec, ..
@ -645,6 +638,15 @@ pub fn walk_format_spec<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, format_spe
visitor.visit_expr(format_spec);
}
pub fn walk_arguments<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, arguments: &'a Arguments) {
for arg in &arguments.args {
visitor.visit_expr(arg);
}
for keyword in &arguments.keywords {
visitor.visit_keyword(keyword);
}
}
pub fn walk_parameters<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, parameters: &'a Parameters) {
// Defaults are evaluated before annotations.
for arg in &parameters.posonlyargs {

View file

@ -1,7 +1,8 @@
use crate::{
self as ast, Alias, BoolOp, CmpOp, Comprehension, Constant, Decorator, ElifElseClause,
ExceptHandler, Expr, Keyword, MatchCase, Mod, Operator, Parameter, ParameterWithDefault,
Parameters, Pattern, Stmt, TypeParam, TypeParamTypeVar, UnaryOp, WithItem,
self as ast, Alias, Arguments, BoolOp, CmpOp, Comprehension, Constant, Decorator,
ElifElseClause, ExceptHandler, Expr, Keyword, MatchCase, Mod, Operator, Parameter,
ParameterWithDefault, Parameters, Pattern, Stmt, TypeParam, TypeParamTypeVar, UnaryOp,
WithItem,
};
/// Visitor that traverses all nodes recursively in pre-order.
@ -56,6 +57,10 @@ pub trait PreorderVisitor<'a> {
walk_format_spec(self, format_spec);
}
fn visit_arguments(&mut self, arguments: &'a Arguments) {
walk_arguments(self, arguments);
}
fn visit_parameters(&mut self, parameters: &'a Parameters) {
walk_parameters(self, parameters);
}
@ -166,8 +171,7 @@ where
}
Stmt::ClassDef(ast::StmtClassDef {
bases,
keywords,
arguments,
body,
decorator_list,
type_params,
@ -181,12 +185,8 @@ where
visitor.visit_type_param(type_param);
}
for expr in bases {
visitor.visit_expr(expr);
}
for keyword in keywords {
visitor.visit_keyword(keyword);
if let Some(arguments) = arguments {
visitor.visit_arguments(arguments);
}
visitor.visit_body(body);
@ -592,17 +592,11 @@ where
Expr::Call(ast::ExprCall {
func,
args,
keywords,
arguments,
range: _range,
}) => {
visitor.visit_expr(func);
for expr in args {
visitor.visit_expr(expr);
}
for keyword in keywords {
visitor.visit_keyword(keyword);
}
visitor.visit_arguments(arguments);
}
Expr::FormattedValue(ast::ExprFormattedValue {
@ -749,6 +743,19 @@ pub fn walk_format_spec<'a, V: PreorderVisitor<'a> + ?Sized>(
visitor.visit_expr(format_spec);
}
pub fn walk_arguments<'a, V>(visitor: &mut V, arguments: &'a Arguments)
where
V: PreorderVisitor<'a> + ?Sized,
{
for arg in &arguments.args {
visitor.visit_expr(arg);
}
for keyword in &arguments.keywords {
visitor.visit_keyword(keyword);
}
}
pub fn walk_parameters<'a, V>(visitor: &mut V, parameters: &'a Parameters)
where
V: PreorderVisitor<'a> + ?Sized,