Auto generate ast expression nodes (#16285)

## Summary

Part of https://github.com/astral-sh/ruff/issues/15655

- Auto generate AST nodes using definitions in `ast.toml`. I added
attributes similar to
[`Field`](https://github.com/python/cpython/blob/main/Parser/asdl.py#L67)
in ASDL to hold field information

## Test Plan

Nothing outside the `ruff_python_ast` package should change.

---------

Co-authored-by: Douglas Creager <dcreager@dcreager.net>
This commit is contained in:
Shaygan Hooshyari 2025-03-05 14:25:55 +01:00 committed by GitHub
parent cc324abcc2
commit 23fd4927ae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 719 additions and 360 deletions

View file

@ -1,5 +1,9 @@
#![allow(clippy::derive_partial_eq_without_eq)]
use crate::generated::{
ExprBytesLiteral, ExprDict, ExprFString, ExprList, ExprName, ExprSet, ExprStringLiteral,
ExprTuple,
};
use std::borrow::Cow;
use std::fmt;
use std::fmt::Debug;
@ -120,6 +124,7 @@ pub struct StmtClassDef {
pub decorator_list: Vec<Decorator>,
pub name: Identifier,
pub type_params: Option<Box<TypeParams>>,
// TODO: can remove?
pub arguments: Option<Box<Arguments>>,
pub body: Vec<Stmt>,
}
@ -379,74 +384,6 @@ impl ExprRef<'_> {
}
}
/// An AST node used to represent a IPython escape command at the expression level.
///
/// For example,
/// ```python
/// dir = !pwd
/// ```
///
/// Here, the escape kind can only be `!` or `%` otherwise it is a syntax error.
///
/// For more information related to terminology and syntax of escape commands,
/// see [`StmtIpyEscapeCommand`].
#[derive(Clone, Debug, PartialEq)]
pub struct ExprIpyEscapeCommand {
pub range: TextRange,
pub kind: IpyEscapeKind,
pub value: Box<str>,
}
/// See also [BoolOp](https://docs.python.org/3/library/ast.html#ast.BoolOp)
#[derive(Clone, Debug, PartialEq)]
pub struct ExprBoolOp {
pub range: TextRange,
pub op: BoolOp,
pub values: Vec<Expr>,
}
/// See also [NamedExpr](https://docs.python.org/3/library/ast.html#ast.NamedExpr)
#[derive(Clone, Debug, PartialEq)]
pub struct ExprNamed {
pub range: TextRange,
pub target: Box<Expr>,
pub value: Box<Expr>,
}
/// See also [BinOp](https://docs.python.org/3/library/ast.html#ast.BinOp)
#[derive(Clone, Debug, PartialEq)]
pub struct ExprBinOp {
pub range: TextRange,
pub left: Box<Expr>,
pub op: Operator,
pub right: Box<Expr>,
}
/// See also [UnaryOp](https://docs.python.org/3/library/ast.html#ast.UnaryOp)
#[derive(Clone, Debug, PartialEq)]
pub struct ExprUnaryOp {
pub range: TextRange,
pub op: UnaryOp,
pub operand: Box<Expr>,
}
/// See also [Lambda](https://docs.python.org/3/library/ast.html#ast.Lambda)
#[derive(Clone, Debug, PartialEq)]
pub struct ExprLambda {
pub range: TextRange,
pub parameters: Option<Box<Parameters>>,
pub body: Box<Expr>,
}
/// See also [IfExp](https://docs.python.org/3/library/ast.html#ast.IfExp)
#[derive(Clone, Debug, PartialEq)]
pub struct ExprIf {
pub range: TextRange,
pub test: Box<Expr>,
pub body: Box<Expr>,
pub orelse: Box<Expr>,
}
/// Represents an item in a [dictionary literal display][1].
///
/// Consider the following Python dictionary literal:
@ -495,13 +432,6 @@ impl Ranged for DictItem {
}
}
/// See also [Dict](https://docs.python.org/3/library/ast.html#ast.Dict)
#[derive(Clone, Debug, PartialEq)]
pub struct ExprDict {
pub range: TextRange,
pub items: Vec<DictItem>,
}
impl ExprDict {
/// Returns an `Iterator` over the AST nodes representing the
/// dictionary's keys.
@ -637,13 +567,6 @@ impl DoubleEndedIterator for DictValueIterator<'_> {
impl FusedIterator for DictValueIterator<'_> {}
impl ExactSizeIterator for DictValueIterator<'_> {}
/// See also [Set](https://docs.python.org/3/library/ast.html#ast.Set)
#[derive(Clone, Debug, PartialEq)]
pub struct ExprSet {
pub range: TextRange,
pub elts: Vec<Expr>,
}
impl ExprSet {
pub fn iter(&self) -> std::slice::Iter<'_, Expr> {
self.elts.iter()
@ -667,78 +590,6 @@ impl<'a> IntoIterator for &'a ExprSet {
}
}
/// See also [ListComp](https://docs.python.org/3/library/ast.html#ast.ListComp)
#[derive(Clone, Debug, PartialEq)]
pub struct ExprListComp {
pub range: TextRange,
pub elt: Box<Expr>,
pub generators: Vec<Comprehension>,
}
/// See also [SetComp](https://docs.python.org/3/library/ast.html#ast.SetComp)
#[derive(Clone, Debug, PartialEq)]
pub struct ExprSetComp {
pub range: TextRange,
pub elt: Box<Expr>,
pub generators: Vec<Comprehension>,
}
/// See also [DictComp](https://docs.python.org/3/library/ast.html#ast.DictComp)
#[derive(Clone, Debug, PartialEq)]
pub struct ExprDictComp {
pub range: TextRange,
pub key: Box<Expr>,
pub value: Box<Expr>,
pub generators: Vec<Comprehension>,
}
/// See also [GeneratorExp](https://docs.python.org/3/library/ast.html#ast.GeneratorExp)
#[derive(Clone, Debug, PartialEq)]
pub struct ExprGenerator {
pub range: TextRange,
pub elt: Box<Expr>,
pub generators: Vec<Comprehension>,
pub parenthesized: bool,
}
/// See also [Await](https://docs.python.org/3/library/ast.html#ast.Await)
#[derive(Clone, Debug, PartialEq)]
pub struct ExprAwait {
pub range: TextRange,
pub value: Box<Expr>,
}
/// See also [Yield](https://docs.python.org/3/library/ast.html#ast.Yield)
#[derive(Clone, Debug, PartialEq)]
pub struct ExprYield {
pub range: TextRange,
pub value: Option<Box<Expr>>,
}
/// See also [YieldFrom](https://docs.python.org/3/library/ast.html#ast.YieldFrom)
#[derive(Clone, Debug, PartialEq)]
pub struct ExprYieldFrom {
pub range: TextRange,
pub value: Box<Expr>,
}
/// See also [Compare](https://docs.python.org/3/library/ast.html#ast.Compare)
#[derive(Clone, Debug, PartialEq)]
pub struct ExprCompare {
pub range: TextRange,
pub left: Box<Expr>,
pub ops: Box<[CmpOp]>,
pub comparators: Box<[Expr]>,
}
/// See also [Call](https://docs.python.org/3/library/ast.html#ast.Call)
#[derive(Clone, Debug, PartialEq)]
pub struct ExprCall {
pub range: TextRange,
pub func: Box<Expr>,
pub arguments: Arguments,
}
#[derive(Clone, Debug, PartialEq)]
pub struct FStringFormatSpec {
pub range: TextRange,
@ -811,20 +662,6 @@ pub struct DebugText {
pub trailing: String,
}
/// An AST node that represents either a single-part f-string literal
/// or an implicitly concatenated f-string literal.
///
/// This type differs from the original Python AST ([JoinedStr]) in that it
/// doesn't join the implicitly concatenated parts into a single string. Instead,
/// it keeps them separate and provide various methods to access the parts.
///
/// [JoinedStr]: https://docs.python.org/3/library/ast.html#ast.JoinedStr
#[derive(Clone, Debug, PartialEq)]
pub struct ExprFString {
pub range: TextRange,
pub value: FStringValue,
}
impl ExprFString {
/// Returns the single [`FString`] if the f-string isn't implicitly concatenated, [`None`]
/// otherwise.
@ -1286,14 +1123,6 @@ impl fmt::Debug for FStringElements {
}
}
/// An AST node that represents either a single-part string literal
/// or an implicitly concatenated string literal.
#[derive(Clone, Debug, PartialEq)]
pub struct ExprStringLiteral {
pub range: TextRange,
pub value: StringLiteralValue,
}
impl ExprStringLiteral {
/// Return `Some(literal)` if the string only consists of a single `StringLiteral` part
/// (indicating that it is not implicitly concatenated). Otherwise, return `None`.
@ -1738,14 +1567,6 @@ impl Debug for ConcatenatedStringLiteral {
}
}
/// An AST node that represents either a single-part bytestring literal
/// or an implicitly concatenated bytestring literal.
#[derive(Clone, Debug, PartialEq)]
pub struct ExprBytesLiteral {
pub range: TextRange,
pub value: BytesLiteralValue,
}
impl ExprBytesLiteral {
/// Return `Some(literal)` if the bytestring only consists of a single `BytesLiteral` part
/// (indicating that it is not implicitly concatenated). Otherwise, return `None`.
@ -2347,12 +2168,6 @@ impl From<FStringFlags> for AnyStringFlags {
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct ExprNumberLiteral {
pub range: TextRange,
pub value: Number,
}
#[derive(Clone, Debug, PartialEq, is_macro::Is)]
pub enum Number {
Int(int::Int),
@ -2360,70 +2175,12 @@ pub enum Number {
Complex { real: f64, imag: f64 },
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct ExprBooleanLiteral {
pub range: TextRange,
pub value: bool,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct ExprNoneLiteral {
pub range: TextRange,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct ExprEllipsisLiteral {
pub range: TextRange,
}
/// See also [Attribute](https://docs.python.org/3/library/ast.html#ast.Attribute)
#[derive(Clone, Debug, PartialEq)]
pub struct ExprAttribute {
pub range: TextRange,
pub value: Box<Expr>,
pub attr: Identifier,
pub ctx: ExprContext,
}
/// See also [Subscript](https://docs.python.org/3/library/ast.html#ast.Subscript)
#[derive(Clone, Debug, PartialEq)]
pub struct ExprSubscript {
pub range: TextRange,
pub value: Box<Expr>,
pub slice: Box<Expr>,
pub ctx: ExprContext,
}
/// See also [Starred](https://docs.python.org/3/library/ast.html#ast.Starred)
#[derive(Clone, Debug, PartialEq)]
pub struct ExprStarred {
pub range: TextRange,
pub value: Box<Expr>,
pub ctx: ExprContext,
}
/// See also [Name](https://docs.python.org/3/library/ast.html#ast.Name)
#[derive(Clone, Debug, PartialEq)]
pub struct ExprName {
pub range: TextRange,
pub id: Name,
pub ctx: ExprContext,
}
impl ExprName {
pub fn id(&self) -> &Name {
&self.id
}
}
/// See also [List](https://docs.python.org/3/library/ast.html#ast.List)
#[derive(Clone, Debug, PartialEq)]
pub struct ExprList {
pub range: TextRange,
pub elts: Vec<Expr>,
pub ctx: ExprContext,
}
impl ExprList {
pub fn iter(&self) -> std::slice::Iter<'_, Expr> {
self.elts.iter()
@ -2447,17 +2204,6 @@ impl<'a> IntoIterator for &'a ExprList {
}
}
/// See also [Tuple](https://docs.python.org/3/library/ast.html#ast.Tuple)
#[derive(Clone, Debug, PartialEq)]
pub struct ExprTuple {
pub range: TextRange,
pub elts: Vec<Expr>,
pub ctx: ExprContext,
/// Whether the tuple is parenthesized in the source code.
pub parenthesized: bool,
}
impl ExprTuple {
pub fn iter(&self) -> std::slice::Iter<'_, Expr> {
self.elts.iter()
@ -2481,15 +2227,6 @@ impl<'a> IntoIterator for &'a ExprTuple {
}
}
/// See also [Slice](https://docs.python.org/3/library/ast.html#ast.Slice)
#[derive(Clone, Debug, PartialEq)]
pub struct ExprSlice {
pub range: TextRange,
pub lower: Option<Box<Expr>>,
pub upper: Option<Box<Expr>>,
pub step: Option<Box<Expr>>,
}
/// See also [expr_context](https://docs.python.org/3/library/ast.html#ast.expr_context)
#[derive(Clone, Debug, PartialEq, is_macro::Is, Copy, Hash, Eq)]
pub enum ExprContext {
@ -3625,6 +3362,7 @@ mod tests {
#[allow(clippy::wildcard_imports)]
use super::*;
use crate::generated::*;
use crate::Mod;
#[test]