mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 05:45:24 +00:00
Reduce size of Expr
from 80 to 64 bytes (#9900)
## Summary This PR reduces the size of `Expr` from 80 to 64 bytes, by reducing the sizes of... - `ExprCall` from 72 to 56 bytes, by using boxed slices for `Arguments`. - `ExprCompare` from 64 to 48 bytes, by using boxed slices for its various vectors. In testing, the parser gets a bit faster, and the linter benchmarks improve quite a bit.
This commit is contained in:
parent
bd8123c0d8
commit
49fe1b85f2
78 changed files with 326 additions and 258 deletions
|
@ -60,16 +60,14 @@ where
|
|||
}
|
||||
}
|
||||
Expr::Call(ast::ExprCall {
|
||||
func,
|
||||
arguments: ast::Arguments { args, keywords, .. },
|
||||
..
|
||||
func, arguments, ..
|
||||
}) => {
|
||||
// Allow `tuple()`, `list()`, and their generic forms, like `list[int]()`.
|
||||
if keywords.is_empty() && args.len() <= 1 {
|
||||
if arguments.keywords.is_empty() && arguments.args.len() <= 1 {
|
||||
if let Expr::Name(ast::ExprName { id, .. }) = map_subscript(func) {
|
||||
let id = id.as_str();
|
||||
if matches!(id, "tuple" | "list") && is_builtin(id) {
|
||||
let [arg] = args.as_slice() else {
|
||||
let [arg] = arguments.args.as_ref() else {
|
||||
return (None, DunderAllFlags::empty());
|
||||
};
|
||||
match arg {
|
||||
|
|
|
@ -52,12 +52,12 @@ where
|
|||
// Accept empty initializers.
|
||||
if let Expr::Call(ast::ExprCall {
|
||||
func,
|
||||
arguments: Arguments { args, keywords, .. },
|
||||
arguments,
|
||||
range: _,
|
||||
}) = expr
|
||||
{
|
||||
// Ex) `list()`
|
||||
if args.is_empty() && keywords.is_empty() {
|
||||
if arguments.is_empty() {
|
||||
if let Expr::Name(ast::ExprName { id, .. }) = func.as_ref() {
|
||||
if !is_iterable_initializer(id.as_str(), |id| is_builtin(id)) {
|
||||
return true;
|
||||
|
@ -221,14 +221,14 @@ pub fn any_over_expr(expr: &Expr, func: &dyn Fn(&Expr) -> bool) -> bool {
|
|||
}) => any_over_expr(left, func) || comparators.iter().any(|expr| any_over_expr(expr, func)),
|
||||
Expr::Call(ast::ExprCall {
|
||||
func: call_func,
|
||||
arguments: Arguments { args, keywords, .. },
|
||||
arguments,
|
||||
range: _,
|
||||
}) => {
|
||||
any_over_expr(call_func, func)
|
||||
// Note that this is the evaluation order but not necessarily the declaration order
|
||||
// (e.g. for `f(*args, a=2, *args2, **kwargs)` it's not)
|
||||
|| args.iter().any(|expr| any_over_expr(expr, func))
|
||||
|| keywords
|
||||
|| arguments.args.iter().any(|expr| any_over_expr(expr, func))
|
||||
|| arguments.keywords
|
||||
.iter()
|
||||
.any(|keyword| any_over_expr(&keyword.value, func))
|
||||
}
|
||||
|
@ -1227,18 +1227,16 @@ impl Truthiness {
|
|||
}
|
||||
}
|
||||
Expr::Call(ast::ExprCall {
|
||||
func,
|
||||
arguments: Arguments { args, keywords, .. },
|
||||
..
|
||||
func, arguments, ..
|
||||
}) => {
|
||||
if let Expr::Name(ast::ExprName { id, .. }) = func.as_ref() {
|
||||
if is_iterable_initializer(id.as_str(), |id| is_builtin(id)) {
|
||||
if args.is_empty() && keywords.is_empty() {
|
||||
if arguments.is_empty() {
|
||||
// Ex) `list()`
|
||||
Self::Falsey
|
||||
} else if args.len() == 1 && keywords.is_empty() {
|
||||
} else if arguments.args.len() == 1 && arguments.keywords.is_empty() {
|
||||
// Ex) `list([1, 2, 3])`
|
||||
Self::from_expr(&args[0], is_builtin)
|
||||
Self::from_expr(&arguments.args[0], is_builtin)
|
||||
} else {
|
||||
Self::Unknown
|
||||
}
|
||||
|
|
|
@ -2588,7 +2588,7 @@ impl AstNode for ast::ExprCompare {
|
|||
|
||||
visitor.visit_expr(left);
|
||||
|
||||
for (op, comparator) in ops.iter().zip(comparators) {
|
||||
for (op, comparator) in ops.iter().zip(&**comparators) {
|
||||
visitor.visit_cmp_op(op);
|
||||
visitor.visit_expr(comparator);
|
||||
}
|
||||
|
|
|
@ -894,8 +894,8 @@ impl From<ExprYieldFrom> for Expr {
|
|||
pub struct ExprCompare {
|
||||
pub range: TextRange,
|
||||
pub left: Box<Expr>,
|
||||
pub ops: Vec<CmpOp>,
|
||||
pub comparators: Vec<Expr>,
|
||||
pub ops: Box<[CmpOp]>,
|
||||
pub comparators: Box<[Expr]>,
|
||||
}
|
||||
|
||||
impl From<ExprCompare> for Expr {
|
||||
|
@ -2987,8 +2987,8 @@ pub struct ParameterWithDefault {
|
|||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Arguments {
|
||||
pub range: TextRange,
|
||||
pub args: Vec<Expr>,
|
||||
pub keywords: Vec<Keyword>,
|
||||
pub args: Box<[Expr]>,
|
||||
pub keywords: Box<[Keyword]>,
|
||||
}
|
||||
|
||||
/// An entry in the argument list of a function call.
|
||||
|
@ -3894,10 +3894,42 @@ mod tests {
|
|||
assert!(std::mem::size_of::<StmtFunctionDef>() <= 144);
|
||||
assert!(std::mem::size_of::<StmtClassDef>() <= 104);
|
||||
assert!(std::mem::size_of::<StmtTry>() <= 112);
|
||||
// 80 for Rustc < 1.76
|
||||
assert!(matches!(std::mem::size_of::<Expr>(), 72 | 80));
|
||||
assert!(std::mem::size_of::<Mod>() <= 32);
|
||||
// 96 for Rustc < 1.76
|
||||
assert!(matches!(std::mem::size_of::<Pattern>(), 88 | 96));
|
||||
assert!(std::mem::size_of::<Mod>() <= 32);
|
||||
|
||||
assert_eq!(std::mem::size_of::<Expr>(), 64);
|
||||
assert_eq!(std::mem::size_of::<ExprAttribute>(), 56);
|
||||
assert_eq!(std::mem::size_of::<ExprAwait>(), 16);
|
||||
assert_eq!(std::mem::size_of::<ExprBinOp>(), 32);
|
||||
assert_eq!(std::mem::size_of::<ExprBoolOp>(), 40);
|
||||
assert_eq!(std::mem::size_of::<ExprBooleanLiteral>(), 12);
|
||||
assert_eq!(std::mem::size_of::<ExprBytesLiteral>(), 40);
|
||||
assert_eq!(std::mem::size_of::<ExprCall>(), 56);
|
||||
assert_eq!(std::mem::size_of::<ExprCompare>(), 48);
|
||||
assert_eq!(std::mem::size_of::<ExprDict>(), 56);
|
||||
assert_eq!(std::mem::size_of::<ExprDictComp>(), 48);
|
||||
assert_eq!(std::mem::size_of::<ExprEllipsisLiteral>(), 8);
|
||||
assert_eq!(std::mem::size_of::<ExprFString>(), 48);
|
||||
assert_eq!(std::mem::size_of::<ExprGeneratorExp>(), 40);
|
||||
assert_eq!(std::mem::size_of::<ExprIfExp>(), 32);
|
||||
assert_eq!(std::mem::size_of::<ExprIpyEscapeCommand>(), 32);
|
||||
assert_eq!(std::mem::size_of::<ExprLambda>(), 24);
|
||||
assert_eq!(std::mem::size_of::<ExprList>(), 40);
|
||||
assert_eq!(std::mem::size_of::<ExprListComp>(), 40);
|
||||
assert_eq!(std::mem::size_of::<ExprName>(), 40);
|
||||
assert_eq!(std::mem::size_of::<ExprNamedExpr>(), 24);
|
||||
assert_eq!(std::mem::size_of::<ExprNoneLiteral>(), 8);
|
||||
assert_eq!(std::mem::size_of::<ExprNumberLiteral>(), 32);
|
||||
assert_eq!(std::mem::size_of::<ExprSet>(), 32);
|
||||
assert_eq!(std::mem::size_of::<ExprSetComp>(), 40);
|
||||
assert_eq!(std::mem::size_of::<ExprSlice>(), 32);
|
||||
assert_eq!(std::mem::size_of::<ExprStarred>(), 24);
|
||||
assert_eq!(std::mem::size_of::<ExprStringLiteral>(), 48);
|
||||
assert_eq!(std::mem::size_of::<ExprSubscript>(), 32);
|
||||
assert_eq!(std::mem::size_of::<ExprTuple>(), 40);
|
||||
assert_eq!(std::mem::size_of::<ExprUnaryOp>(), 24);
|
||||
assert_eq!(std::mem::size_of::<ExprYield>(), 16);
|
||||
assert_eq!(std::mem::size_of::<ExprYieldFrom>(), 16);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -461,10 +461,10 @@ pub fn walk_expr<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, expr: &'a Expr) {
|
|||
range: _,
|
||||
}) => {
|
||||
visitor.visit_expr(left);
|
||||
for cmp_op in ops {
|
||||
for cmp_op in &**ops {
|
||||
visitor.visit_cmp_op(cmp_op);
|
||||
}
|
||||
for expr in comparators {
|
||||
for expr in &**comparators {
|
||||
visitor.visit_expr(expr);
|
||||
}
|
||||
}
|
||||
|
@ -594,10 +594,10 @@ pub fn walk_arguments<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, arguments: &
|
|||
// Note that the there might be keywords before the last arg, e.g. in
|
||||
// f(*args, a=2, *args2, **kwargs)`, but we follow Python in evaluating first `args` and then
|
||||
// `keywords`. See also [Arguments::arguments_source_order`].
|
||||
for arg in &arguments.args {
|
||||
for arg in arguments.args.iter() {
|
||||
visitor.visit_expr(arg);
|
||||
}
|
||||
for keyword in &arguments.keywords {
|
||||
for keyword in arguments.keywords.iter() {
|
||||
visitor.visit_keyword(keyword);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -448,10 +448,10 @@ pub fn walk_expr<V: Transformer + ?Sized>(visitor: &V, expr: &mut Expr) {
|
|||
range: _,
|
||||
}) => {
|
||||
visitor.visit_expr(left);
|
||||
for cmp_op in ops {
|
||||
for cmp_op in &mut **ops {
|
||||
visitor.visit_cmp_op(cmp_op);
|
||||
}
|
||||
for expr in comparators {
|
||||
for expr in &mut **comparators {
|
||||
visitor.visit_expr(expr);
|
||||
}
|
||||
}
|
||||
|
@ -580,10 +580,10 @@ pub fn walk_arguments<V: Transformer + ?Sized>(visitor: &V, arguments: &mut Argu
|
|||
// Note that the there might be keywords before the last arg, e.g. in
|
||||
// f(*args, a=2, *args2, **kwargs)`, but we follow Python in evaluating first `args` and then
|
||||
// `keywords`. See also [Arguments::arguments_source_order`].
|
||||
for arg in &mut arguments.args {
|
||||
for arg in arguments.args.iter_mut() {
|
||||
visitor.visit_expr(arg);
|
||||
}
|
||||
for keyword in &mut arguments.keywords {
|
||||
for keyword in arguments.keywords.iter_mut() {
|
||||
visitor.visit_keyword(keyword);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue