From 360ba095ffc7dbbdb175fbaab011c25bcb4ae48a Mon Sep 17 00:00:00 2001 From: Shaygan Hooshyari Date: Thu, 13 Mar 2025 15:43:48 +0100 Subject: [PATCH] [red-knot] Auto generate statement nodes (#16645) ## Summary Part of #15655 Replaced statement nodes with autogenerated ones. Reused the stuff we introduced in #16285. Nothing except for copying the nodes to new format. ## Test Plan Tests run without any changes. Also moved the test that checks size of AST nodes to `generated.rs` since all of the structs that it tests are now there. --- crates/ruff_python_ast/ast.toml | 349 ++++++++++++++++++------ crates/ruff_python_ast/generate.py | 8 + crates/ruff_python_ast/src/generated.rs | 276 ++++++++++++++++++- crates/ruff_python_ast/src/nodes.rs | 294 +------------------- 4 files changed, 552 insertions(+), 375 deletions(-) diff --git a/crates/ruff_python_ast/ast.toml b/crates/ruff_python_ast/ast.toml index 95fcfb7bc2..612f8a8f14 100644 --- a/crates/ruff_python_ast/ast.toml +++ b/crates/ruff_python_ast/ast.toml @@ -66,32 +66,236 @@ add_suffix_to_is_methods = true anynode_is_label = "statement" doc = "See also [stmt](https://docs.python.org/3/library/ast.html#ast.stmt)" -[Stmt.nodes] -StmtFunctionDef = {} -StmtClassDef = {} -StmtReturn = {} -StmtDelete = {} -StmtTypeAlias = {} -StmtAssign = {} -StmtAugAssign = {} -StmtAnnAssign = {} -StmtFor = {} -StmtWhile = {} -StmtIf = {} -StmtWith = {} -StmtMatch = {} -StmtRaise = {} -StmtTry = {} -StmtAssert = {} -StmtImport = {} -StmtImportFrom = {} -StmtGlobal = {} -StmtNonlocal = {} -StmtExpr = {} -StmtPass = {} -StmtBreak = {} -StmtContinue = {} -StmtIpyEscapeCommand = {} +[Stmt.nodes.StmtFunctionDef] +doc = """See also [FunctionDef](https://docs.python.org/3/library/ast.html#ast.FunctionDef) +and [AsyncFunctionDef](https://docs.python.org/3/library/ast.html#ast.AsyncFunctionDef). + +This type differs from the original Python AST, as it collapses the synchronous and asynchronous variants into a single type.""" +fields = [ + { name = "is_async", type = "bool" }, + { name = "decorator_list", type = "Decorator*" }, + { name = "name", type = "Identifier" }, + { name = "type_params", type = "Box?" }, + { name = "parameters", type = "Box" }, + + { name = "returns", type = "Expr?" }, + { name = "body", type = "Stmt*" }, +] + +[Stmt.nodes.StmtClassDef] +doc = "See also [ClassDef](https://docs.python.org/3/library/ast.html#ast.ClassDef)" +fields = [ + { name = "decorator_list", type = "Decorator*" }, + { name = "name", type = "Identifier" }, + { name = "type_params", type = "Box?" }, + { name = "arguments", type = "Box?" }, + { name = "body", type = "Stmt*" }, +] + +[Stmt.nodes.StmtReturn] +doc = "See also [Return](https://docs.python.org/3/library/ast.html#ast.Return)" +fields = [{ name = "value", type = "Expr?" }] + +[Stmt.nodes.StmtDelete] +doc = "See also [Delete](https://docs.python.org/3/library/ast.html#ast.Delete)" +fields = [{ name = "targets", type = "Expr*" }] + +[Stmt.nodes.StmtTypeAlias] +doc = "See also [TypeAlias](https://docs.python.org/3/library/ast.html#ast.TypeAlias)" +fields = [ + { name = "name", type = "Expr" }, + { name = "type_params", type = "TypeParams?" }, + { name = "value", type = "Expr" }, +] + +[Stmt.nodes.StmtAssign] +doc = "See also [Assign](https://docs.python.org/3/library/ast.html#ast.Assign)" +fields = [ + { name = "targets", type = "Expr*" }, + { name = "value", type = "Expr" }, +] + +[Stmt.nodes.StmtAugAssign] +doc = "See also [AugAssign](https://docs.python.org/3/library/ast.html#ast.AugAssign)" +fields = [ + { name = "target", type = "Expr" }, + { name = "op", type = "Operator" }, + { name = "value", type = "Expr" }, +] + +[Stmt.nodes.StmtAnnAssign] +doc = "See also [AnnAssign](https://docs.python.org/3/library/ast.html#ast.AnnAssign)" +fields = [ + { name = "target", type = "Expr" }, + { name = "annotation", type = "Expr" }, + { name = "value", type = "Expr?" }, + { name = "simple", type = "bool" }, +] + +[Stmt.nodes.StmtFor] +doc = """See also [For](https://docs.python.org/3/library/ast.html#ast.For) +and [AsyncFor](https://docs.python.org/3/library/ast.html#ast.AsyncFor). + +This type differs from the original Python AST, as it collapses the synchronous and asynchronous variants into a single type.""" +fields = [ + { name = "is_async", type = "bool" }, + { name = "target", type = "Expr" }, + { name = "iter", type = "Expr" }, + { name = "body", type = "Stmt*" }, + { name = "orelse", type = "Stmt*" }, +] + +[Stmt.nodes.StmtWhile] +doc = """See also [While](https://docs.python.org/3/library/ast.html#ast.While) +and [AsyncWhile](https://docs.python.org/3/library/ast.html#ast.AsyncWhile).""" +fields = [ + { name = "test", type = "Expr" }, + { name = "body", type = "Stmt*" }, + { name = "orelse", type = "Stmt*" }, +] + +[Stmt.nodes.StmtIf] +doc = "See also [If](https://docs.python.org/3/library/ast.html#ast.If)" +fields = [ + + { name = "test", type = "Expr" }, + { name = "body", type = "Stmt*" }, + { name = "elif_else_clauses", type = "ElifElseClause*" }, +] + +[Stmt.nodes.StmtWith] +doc = """See also [With](https://docs.python.org/3/library/ast.html#ast.With) +and [AsyncWith](https://docs.python.org/3/library/ast.html#ast.AsyncWith). + +This type differs from the original Python AST, as it collapses the synchronous and asynchronous variants into a single type.""" +fields = [ + { name = "is_async", type = "bool" }, + { name = "items", type = "WithItem*" }, + { name = "body", type = "Stmt*" }, +] + +[Stmt.nodes.StmtMatch] +doc = "See also [Match](https://docs.python.org/3/library/ast.html#ast.Match)" +fields = [ + { name = "subject", type = "Expr" }, + { name = "cases", type = "MatchCase*" }, +] + +[Stmt.nodes.StmtRaise] +doc = "See also [Raise](https://docs.python.org/3/library/ast.html#ast.Raise)" +fields = [{ name = "exc", type = "Expr?" }, { name = "cause", type = "Expr?" }] + +[Stmt.nodes.StmtTry] +doc = """See also [Try](https://docs.python.org/3/library/ast.html#ast.Try) +and [TryStar](https://docs.python.org/3/library/ast.html#ast.TryStar)""" +fields = [ + { name = "body", type = "Stmt*" }, + { name = "handlers", type = "ExceptHandler*" }, + { name = "orelse", type = "Stmt*" }, + { name = "finalbody", type = "Stmt*" }, + { name = "is_star", type = "bool" }, +] + +[Stmt.nodes.StmtAssert] +doc = "See also [Assert](https://docs.python.org/3/library/ast.html#ast.Assert)" +fields = [{ name = "test", type = "Expr" }, { name = "msg", type = "Expr?" }] + +[Stmt.nodes.StmtImport] +doc = "See also [Import](https://docs.python.org/3/library/ast.html#ast.Import)" +fields = [{ name = "names", type = "Alias*" }] + +[Stmt.nodes.StmtImportFrom] +doc = "See also [ImportFrom](https://docs.python.org/3/library/ast.html#ast.ImportFrom)" +fields = [ + { name = "module", type = "Identifier?" }, + { name = "names", type = "Alias*" }, + { name = "level", type = "u32" }, +] + +[Stmt.nodes.StmtGlobal] +doc = "See also [Global](https://docs.python.org/3/library/ast.html#ast.Global)" +fields = [{ name = "names", type = "Identifier*" }] + +[Stmt.nodes.StmtNonlocal] +doc = "See also [Nonlocal](https://docs.python.org/3/library/ast.html#ast.Nonlocal)" +fields = [{ name = "names", type = "Identifier*" }] + +[Stmt.nodes.StmtExpr] +doc = "See also [Expr](https://docs.python.org/3/library/ast.html#ast.Expr)" +fields = [{ name = "value", type = "Expr" }] + +[Stmt.nodes.StmtPass] +doc = "See also [Pass](https://docs.python.org/3/library/ast.html#ast.Pass)" +fields = [] + +[Stmt.nodes.StmtBreak] +doc = "See also [Break](https://docs.python.org/3/library/ast.html#ast.Break)" +fields = [] + +[Stmt.nodes.StmtContinue] +doc = "See also [Continue](https://docs.python.org/3/library/ast.html#ast.Continue)" +fields = [] + +[Stmt.nodes.StmtIpyEscapeCommand] +# TODO: remove crate:: prefix from IpyEscapeKind after it's auto generated +doc = """An AST node used to represent a IPython escape command at the statement level. + +For example, +```python +%matplotlib inline +``` + +## Terminology + +Escape commands are special IPython syntax which starts with a token to identify +the escape kind followed by the command value itself. [Escape kind] are the kind +of escape commands that are recognized by the token: `%`, `%%`, `!`, `!!`, +`?`, `??`, `/`, `;`, and `,`. + +Help command (or Dynamic Object Introspection as it's called) are the escape commands +of the kind `?` and `??`. For example, `?str.replace`. Help end command are a subset +of Help command where the token can be at the end of the line i.e., after the value. +For example, `str.replace?`. + +Here's where things get tricky. I'll divide the help end command into two types for +better understanding: +1. Strict version: The token is _only_ at the end of the line. For example, + `str.replace?` or `str.replace??`. +2. Combined version: Along with the `?` or `??` token, which are at the end of the + line, there are other escape kind tokens that are present at the start as well. + For example, `%matplotlib?` or `%%timeit?`. + +Priority comes into picture for the "Combined version" mentioned above. How do +we determine the escape kind if there are tokens on both side of the value, i.e., which +token to choose? The Help end command always takes priority over any other token which +means that if there is `?`/`??` at the end then that is used to determine the kind. +For example, in `%matplotlib?` the escape kind is determined using the `?` token +instead of `%` token. + +## Syntax + +`` + +The simplest form is an escape kind token followed by the command value. For example, +`%matplotlib inline`, `/foo`, `!pwd`, etc. + +`` + +The help end escape command would be the reverse of the above syntax. Here, the +escape kind token can only be either `?` or `??` and it is at the end of the line. +For example, `str.replace?`, `math.pi??`, etc. + +`` + +The final syntax is the combined version of the above two. For example, `%matplotlib?`, +`%%timeit??`, etc. + +[Escape kind]: crate::IpyEscapeKind +""" +fields = [ + { name = "kind", type = "IpyEscapeKind" }, + { name = "value", type = "Box" }, +] [Expr] add_suffix_to_is_methods = true @@ -100,38 +304,32 @@ doc = "See also [expr](https://docs.python.org/3/library/ast.html#ast.expr)" [Expr.nodes.ExprBoolOp] doc = "See also [BoolOp](https://docs.python.org/3/library/ast.html#ast.BoolOp)" -fields = [ - { name = "op", type = "BoolOp" }, - { name = "values", type = "Expr*" } -] +fields = [{ name = "op", type = "BoolOp" }, { name = "values", type = "Expr*" }] [Expr.nodes.ExprNamed] doc = "See also [NamedExpr](https://docs.python.org/3/library/ast.html#ast.NamedExpr)" -fields = [ - { name = "target", type = "Expr" }, - { name = "value", type = "Expr" } -] +fields = [{ name = "target", type = "Expr" }, { name = "value", type = "Expr" }] [Expr.nodes.ExprBinOp] doc = "See also [BinOp](https://docs.python.org/3/library/ast.html#ast.BinOp)" fields = [ { name = "left", type = "Expr" }, { name = "op", type = "Operator" }, - { name = "right", type = "Expr" } + { name = "right", type = "Expr" }, ] [Expr.nodes.ExprUnaryOp] doc = "See also [UnaryOp](https://docs.python.org/3/library/ast.html#ast.UnaryOp)" fields = [ { name = "op", type = "UnaryOp" }, - { name = "operand", type = "Expr" } + { name = "operand", type = "Expr" }, ] [Expr.nodes.ExprLambda] doc = "See also [Lambda](https://docs.python.org/3/library/ast.html#ast.Lambda)" fields = [ { name = "parameters", type = "Box?" }, - { name = "body", type = "Expr" } + { name = "body", type = "Expr" }, ] [Expr.nodes.ExprIf] @@ -139,33 +337,29 @@ doc = "See also [IfExp](https://docs.python.org/3/library/ast.html#ast.IfExp)" fields = [ { name = "test", type = "Expr" }, { name = "body", type = "Expr" }, - { name = "orelse", type = "Expr" } + { name = "orelse", type = "Expr" }, ] [Expr.nodes.ExprDict] doc = "See also [Dict](https://docs.python.org/3/library/ast.html#ast.Dict)" -fields = [ - { name = "items", type = "DictItem*" } -] +fields = [{ name = "items", type = "DictItem*" }] [Expr.nodes.ExprSet] doc = "See also [Set](https://docs.python.org/3/library/ast.html#ast.Set)" -fields = [ - { name = "elts", type = "Expr*" } -] +fields = [{ name = "elts", type = "Expr*" }] [Expr.nodes.ExprListComp] doc = "See also [ListComp](https://docs.python.org/3/library/ast.html#ast.ListComp)" fields = [ { name = "elt", type = "Expr" }, - { name = "generators", type = "Comprehension*" } + { name = "generators", type = "Comprehension*" }, ] [Expr.nodes.ExprSetComp] doc = "See also [SetComp](https://docs.python.org/3/library/ast.html#ast.SetComp)" fields = [ { name = "elt", type = "Expr" }, - { name = "generators", type = "Comprehension*" } + { name = "generators", type = "Comprehension*" }, ] [Expr.nodes.ExprDictComp] @@ -173,7 +367,7 @@ doc = "See also [DictComp](https://docs.python.org/3/library/ast.html#ast.DictCo fields = [ { name = "key", type = "Expr" }, { name = "value", type = "Expr" }, - { name = "generators", type = "Comprehension*" } + { name = "generators", type = "Comprehension*" }, ] [Expr.nodes.ExprGenerator] @@ -181,40 +375,34 @@ doc = "See also [GeneratorExp](https://docs.python.org/3/library/ast.html#ast.Ge fields = [ { name = "elt", type = "Expr" }, { name = "generators", type = "Comprehension*" }, - { name = "parenthesized", type = "bool" } + { name = "parenthesized", type = "bool" }, ] [Expr.nodes.ExprAwait] doc = "See also [Await](https://docs.python.org/3/library/ast.html#ast.Await)" -fields = [ - { name = "value", type = "Expr" } -] +fields = [{ name = "value", type = "Expr" }] [Expr.nodes.ExprYield] doc = "See also [Yield](https://docs.python.org/3/library/ast.html#ast.Yield)" -fields = [ - { name = "value", type = "Expr?" } -] +fields = [{ name = "value", type = "Expr?" }] [Expr.nodes.ExprYieldFrom] doc = "See also [YieldFrom](https://docs.python.org/3/library/ast.html#ast.YieldFrom)" -fields = [ - { name = "value", type = "Expr" } -] +fields = [{ name = "value", type = "Expr" }] [Expr.nodes.ExprCompare] doc = "See also [Compare](https://docs.python.org/3/library/ast.html#ast.Compare)" fields = [ { name = "left", type = "Expr" }, { name = "ops", type = "&CmpOp*" }, - { name = "comparators", type = "&Expr*" } + { name = "comparators", type = "&Expr*" }, ] [Expr.nodes.ExprCall] doc = "See also [Call](https://docs.python.org/3/library/ast.html#ast.Call)" fields = [ { name = "func", type = "Expr" }, - { name = "arguments", type = "Arguments" } + { name = "arguments", type = "Arguments" }, ] [Expr.nodes.ExprFString] @@ -226,33 +414,23 @@ doesn't join the implicitly concatenated parts into a single string. Instead, it keeps them separate and provide various methods to access the parts. See also [JoinedStr](https://docs.python.org/3/library/ast.html#ast.JoinedStr)""" -fields = [ - { name = "value", type = "FStringValue" } -] +fields = [{ name = "value", type = "FStringValue" }] [Expr.nodes.ExprStringLiteral] doc = """An AST node that represents either a single-part string literal or an implicitly concatenated string literal.""" -fields = [ - { name = "value", type = "StringLiteralValue" } -] +fields = [{ name = "value", type = "StringLiteralValue" }] [Expr.nodes.ExprBytesLiteral] doc = """An AST node that represents either a single-part bytestring literal or an implicitly concatenated bytestring literal.""" -fields = [ - { name = "value", type = "BytesLiteralValue" } -] +fields = [{ name = "value", type = "BytesLiteralValue" }] [Expr.nodes.ExprNumberLiteral] -fields = [ - { name = "value", type = "Number" } -] +fields = [{ name = "value", type = "Number" }] [Expr.nodes.ExprBooleanLiteral] -fields = [ - { name = "value", type = "bool" } -] +fields = [{ name = "value", type = "bool" }] derives = ["Default"] [Expr.nodes.ExprNoneLiteral] @@ -268,7 +446,7 @@ doc = "See also [Attribute](https://docs.python.org/3/library/ast.html#ast.Attri fields = [ { name = "value", type = "Expr" }, { name = "attr", type = "Identifier" }, - { name = "ctx", type = "ExprContext" } + { name = "ctx", type = "ExprContext" }, ] [Expr.nodes.ExprSubscript] @@ -276,28 +454,28 @@ doc = "See also [Subscript](https://docs.python.org/3/library/ast.html#ast.Subsc fields = [ { name = "value", type = "Expr" }, { name = "slice", type = "Expr" }, - { name = "ctx", type = "ExprContext" } + { name = "ctx", type = "ExprContext" }, ] [Expr.nodes.ExprStarred] doc = "See also [Starred](https://docs.python.org/3/library/ast.html#ast.Starred)" fields = [ { name = "value", type = "Expr" }, - { name = "ctx", type = "ExprContext" } + { name = "ctx", type = "ExprContext" }, ] [Expr.nodes.ExprName] doc = "See also [Name](https://docs.python.org/3/library/ast.html#ast.Name)" fields = [ { name = "id", type = "Name" }, - { name = "ctx", type = "ExprContext" } + { name = "ctx", type = "ExprContext" }, ] [Expr.nodes.ExprList] doc = "See also [List](https://docs.python.org/3/library/ast.html#ast.List)" fields = [ { name = "elts", type = "Expr*" }, - { name = "ctx", type = "ExprContext" } + { name = "ctx", type = "ExprContext" }, ] [Expr.nodes.ExprTuple] @@ -305,7 +483,7 @@ doc = "See also [Tuple](https://docs.python.org/3/library/ast.html#ast.Tuple)" fields = [ { name = "elts", type = "Expr*" }, { name = "ctx", type = "ExprContext" }, - { name = "parenthesized", type = "bool" } + { name = "parenthesized", type = "bool" }, ] [Expr.nodes.ExprSlice] @@ -313,11 +491,10 @@ doc = "See also [Slice](https://docs.python.org/3/library/ast.html#ast.Slice)" fields = [ { name = "lower", type = "Expr?" }, { name = "upper", type = "Expr?" }, - { name = "step", type = "Expr?" } + { name = "step", type = "Expr?" }, ] [Expr.nodes.ExprIpyEscapeCommand] -# TODO: Remove the crate:: prefix once StmtIpyEscapeCommand is moved to generated.rs doc = """An AST node used to represent a IPython escape command at the expression level. For example, @@ -328,11 +505,11 @@ 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 [`crate::StmtIpyEscapeCommand`].""" +see [`StmtIpyEscapeCommand`].""" fields = [ { name = "kind", type = "IpyEscapeKind" }, - { name = "value", type = "Box" } + { name = "value", type = "Box" }, ] [ExceptHandler] @@ -342,8 +519,8 @@ doc = "See also [excepthandler](https://docs.python.org/3/library/ast.html#ast.e ExceptHandlerExceptHandler = {} [FStringElement.nodes] -FStringExpressionElement = {variant = "Expression"} -FStringLiteralElement = {variant = "Literal"} +FStringExpressionElement = { variant = "Expression" } +FStringLiteralElement = { variant = "Literal" } [Pattern] doc = "See also [pattern](https://docs.python.org/3/library/ast.html#ast.pattern)" diff --git a/crates/ruff_python_ast/generate.py b/crates/ruff_python_ast/generate.py index 59088034f9..31e1ca1a98 100644 --- a/crates/ruff_python_ast/generate.py +++ b/crates/ruff_python_ast/generate.py @@ -30,6 +30,14 @@ types_requiring_create_prefix = [ "UnaryOp", "BoolOp", "Operator", + "Decorator", + "TypeParams", + "Parameters", + "Arguments", + "ElifElseClause", + "WithItem", + "MatchCase", + "Alias", ] diff --git a/crates/ruff_python_ast/src/generated.rs b/crates/ruff_python_ast/src/generated.rs index 7238eb204f..e573315ab1 100644 --- a/crates/ruff_python_ast/src/generated.rs +++ b/crates/ruff_python_ast/src/generated.rs @@ -6448,6 +6448,280 @@ impl AnyNodeRef<'_> { } } +/// See also [FunctionDef](https://docs.python.org/3/library/ast.html#ast.FunctionDef) +/// and [AsyncFunctionDef](https://docs.python.org/3/library/ast.html#ast.AsyncFunctionDef). +/// +/// This type differs from the original Python AST, as it collapses the synchronous and asynchronous variants into a single type. +#[derive(Clone, Debug, PartialEq)] +pub struct StmtFunctionDef { + pub range: ruff_text_size::TextRange, + pub is_async: bool, + pub decorator_list: Vec, + pub name: crate::Identifier, + pub type_params: Option>, + pub parameters: Box, + pub returns: Option>, + pub body: Vec, +} + +/// See also [ClassDef](https://docs.python.org/3/library/ast.html#ast.ClassDef) +#[derive(Clone, Debug, PartialEq)] +pub struct StmtClassDef { + pub range: ruff_text_size::TextRange, + pub decorator_list: Vec, + pub name: crate::Identifier, + pub type_params: Option>, + pub arguments: Option>, + pub body: Vec, +} + +/// See also [Return](https://docs.python.org/3/library/ast.html#ast.Return) +#[derive(Clone, Debug, PartialEq)] +pub struct StmtReturn { + pub range: ruff_text_size::TextRange, + pub value: Option>, +} + +/// See also [Delete](https://docs.python.org/3/library/ast.html#ast.Delete) +#[derive(Clone, Debug, PartialEq)] +pub struct StmtDelete { + pub range: ruff_text_size::TextRange, + pub targets: Vec, +} + +/// See also [TypeAlias](https://docs.python.org/3/library/ast.html#ast.TypeAlias) +#[derive(Clone, Debug, PartialEq)] +pub struct StmtTypeAlias { + pub range: ruff_text_size::TextRange, + pub name: Box, + pub type_params: Option, + pub value: Box, +} + +/// See also [Assign](https://docs.python.org/3/library/ast.html#ast.Assign) +#[derive(Clone, Debug, PartialEq)] +pub struct StmtAssign { + pub range: ruff_text_size::TextRange, + pub targets: Vec, + pub value: Box, +} + +/// See also [AugAssign](https://docs.python.org/3/library/ast.html#ast.AugAssign) +#[derive(Clone, Debug, PartialEq)] +pub struct StmtAugAssign { + pub range: ruff_text_size::TextRange, + pub target: Box, + pub op: crate::Operator, + pub value: Box, +} + +/// See also [AnnAssign](https://docs.python.org/3/library/ast.html#ast.AnnAssign) +#[derive(Clone, Debug, PartialEq)] +pub struct StmtAnnAssign { + pub range: ruff_text_size::TextRange, + pub target: Box, + pub annotation: Box, + pub value: Option>, + pub simple: bool, +} + +/// See also [For](https://docs.python.org/3/library/ast.html#ast.For) +/// and [AsyncFor](https://docs.python.org/3/library/ast.html#ast.AsyncFor). +/// +/// This type differs from the original Python AST, as it collapses the synchronous and asynchronous variants into a single type. +#[derive(Clone, Debug, PartialEq)] +pub struct StmtFor { + pub range: ruff_text_size::TextRange, + pub is_async: bool, + pub target: Box, + pub iter: Box, + pub body: Vec, + pub orelse: Vec, +} + +/// See also [While](https://docs.python.org/3/library/ast.html#ast.While) +/// and [AsyncWhile](https://docs.python.org/3/library/ast.html#ast.AsyncWhile). +#[derive(Clone, Debug, PartialEq)] +pub struct StmtWhile { + pub range: ruff_text_size::TextRange, + pub test: Box, + pub body: Vec, + pub orelse: Vec, +} + +/// See also [If](https://docs.python.org/3/library/ast.html#ast.If) +#[derive(Clone, Debug, PartialEq)] +pub struct StmtIf { + pub range: ruff_text_size::TextRange, + pub test: Box, + pub body: Vec, + pub elif_else_clauses: Vec, +} + +/// See also [With](https://docs.python.org/3/library/ast.html#ast.With) +/// and [AsyncWith](https://docs.python.org/3/library/ast.html#ast.AsyncWith). +/// +/// This type differs from the original Python AST, as it collapses the synchronous and asynchronous variants into a single type. +#[derive(Clone, Debug, PartialEq)] +pub struct StmtWith { + pub range: ruff_text_size::TextRange, + pub is_async: bool, + pub items: Vec, + pub body: Vec, +} + +/// See also [Match](https://docs.python.org/3/library/ast.html#ast.Match) +#[derive(Clone, Debug, PartialEq)] +pub struct StmtMatch { + pub range: ruff_text_size::TextRange, + pub subject: Box, + pub cases: Vec, +} + +/// See also [Raise](https://docs.python.org/3/library/ast.html#ast.Raise) +#[derive(Clone, Debug, PartialEq)] +pub struct StmtRaise { + pub range: ruff_text_size::TextRange, + pub exc: Option>, + pub cause: Option>, +} + +/// See also [Try](https://docs.python.org/3/library/ast.html#ast.Try) +/// and [TryStar](https://docs.python.org/3/library/ast.html#ast.TryStar) +#[derive(Clone, Debug, PartialEq)] +pub struct StmtTry { + pub range: ruff_text_size::TextRange, + pub body: Vec, + pub handlers: Vec, + pub orelse: Vec, + pub finalbody: Vec, + pub is_star: bool, +} + +/// See also [Assert](https://docs.python.org/3/library/ast.html#ast.Assert) +#[derive(Clone, Debug, PartialEq)] +pub struct StmtAssert { + pub range: ruff_text_size::TextRange, + pub test: Box, + pub msg: Option>, +} + +/// See also [Import](https://docs.python.org/3/library/ast.html#ast.Import) +#[derive(Clone, Debug, PartialEq)] +pub struct StmtImport { + pub range: ruff_text_size::TextRange, + pub names: Vec, +} + +/// See also [ImportFrom](https://docs.python.org/3/library/ast.html#ast.ImportFrom) +#[derive(Clone, Debug, PartialEq)] +pub struct StmtImportFrom { + pub range: ruff_text_size::TextRange, + pub module: Option, + pub names: Vec, + pub level: u32, +} + +/// See also [Global](https://docs.python.org/3/library/ast.html#ast.Global) +#[derive(Clone, Debug, PartialEq)] +pub struct StmtGlobal { + pub range: ruff_text_size::TextRange, + pub names: Vec, +} + +/// See also [Nonlocal](https://docs.python.org/3/library/ast.html#ast.Nonlocal) +#[derive(Clone, Debug, PartialEq)] +pub struct StmtNonlocal { + pub range: ruff_text_size::TextRange, + pub names: Vec, +} + +/// See also [Expr](https://docs.python.org/3/library/ast.html#ast.Expr) +#[derive(Clone, Debug, PartialEq)] +pub struct StmtExpr { + pub range: ruff_text_size::TextRange, + pub value: Box, +} + +/// See also [Pass](https://docs.python.org/3/library/ast.html#ast.Pass) +#[derive(Clone, Debug, PartialEq)] +pub struct StmtPass { + pub range: ruff_text_size::TextRange, +} + +/// See also [Break](https://docs.python.org/3/library/ast.html#ast.Break) +#[derive(Clone, Debug, PartialEq)] +pub struct StmtBreak { + pub range: ruff_text_size::TextRange, +} + +/// See also [Continue](https://docs.python.org/3/library/ast.html#ast.Continue) +#[derive(Clone, Debug, PartialEq)] +pub struct StmtContinue { + pub range: ruff_text_size::TextRange, +} + +/// An AST node used to represent a IPython escape command at the statement level. +/// +/// For example, +/// ```python +/// %matplotlib inline +/// ``` +/// +/// ## Terminology +/// +/// Escape commands are special IPython syntax which starts with a token to identify +/// the escape kind followed by the command value itself. [Escape kind] are the kind +/// of escape commands that are recognized by the token: `%`, `%%`, `!`, `!!`, +/// `?`, `??`, `/`, `;`, and `,`. +/// +/// Help command (or Dynamic Object Introspection as it's called) are the escape commands +/// of the kind `?` and `??`. For example, `?str.replace`. Help end command are a subset +/// of Help command where the token can be at the end of the line i.e., after the value. +/// For example, `str.replace?`. +/// +/// Here's where things get tricky. I'll divide the help end command into two types for +/// better understanding: +/// 1. Strict version: The token is _only_ at the end of the line. For example, +/// `str.replace?` or `str.replace??`. +/// 2. Combined version: Along with the `?` or `??` token, which are at the end of the +/// line, there are other escape kind tokens that are present at the start as well. +/// For example, `%matplotlib?` or `%%timeit?`. +/// +/// Priority comes into picture for the "Combined version" mentioned above. How do +/// we determine the escape kind if there are tokens on both side of the value, i.e., which +/// token to choose? The Help end command always takes priority over any other token which +/// means that if there is `?`/`??` at the end then that is used to determine the kind. +/// For example, in `%matplotlib?` the escape kind is determined using the `?` token +/// instead of `%` token. +/// +/// ## Syntax +/// +/// `` +/// +/// The simplest form is an escape kind token followed by the command value. For example, +/// `%matplotlib inline`, `/foo`, `!pwd`, etc. +/// +/// `` +/// +/// The help end escape command would be the reverse of the above syntax. Here, the +/// escape kind token can only be either `?` or `??` and it is at the end of the line. +/// For example, `str.replace?`, `math.pi??`, etc. +/// +/// `` +/// +/// The final syntax is the combined version of the above two. For example, `%matplotlib?`, +/// `%%timeit??`, etc. +/// +/// [Escape kind]: crate::IpyEscapeKind +/// +#[derive(Clone, Debug, PartialEq)] +pub struct StmtIpyEscapeCommand { + pub range: ruff_text_size::TextRange, + pub kind: crate::IpyEscapeKind, + pub value: Box, +} + /// See also [BoolOp](https://docs.python.org/3/library/ast.html#ast.BoolOp) #[derive(Clone, Debug, PartialEq)] pub struct ExprBoolOp { @@ -6706,7 +6980,7 @@ pub struct ExprSlice { /// 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 [`crate::StmtIpyEscapeCommand`]. +/// see [`StmtIpyEscapeCommand`]. #[derive(Clone, Debug, PartialEq)] pub struct ExprIpyEscapeCommand { pub range: ruff_text_size::TextRange, diff --git a/crates/ruff_python_ast/src/nodes.rs b/crates/ruff_python_ast/src/nodes.rs index 2c776a18a5..ace57d0291 100644 --- a/crates/ruff_python_ast/src/nodes.rs +++ b/crates/ruff_python_ast/src/nodes.rs @@ -2,7 +2,7 @@ use crate::generated::{ ExprBytesLiteral, ExprDict, ExprFString, ExprList, ExprName, ExprSet, ExprStringLiteral, - ExprTuple, + ExprTuple, StmtClassDef, }; use std::borrow::Cow; use std::fmt; @@ -17,13 +17,13 @@ use itertools::Itertools; use ruff_text_size::{Ranged, TextLen, TextRange, TextSize}; +use crate::str_prefix::{AnyStringPrefix, ByteStringPrefix, FStringPrefix, StringLiteralPrefix}; use crate::{ int, name::Name, str::{Quote, TripleQuotes}, - str_prefix::{AnyStringPrefix, ByteStringPrefix, FStringPrefix, StringLiteralPrefix}, - ExceptHandler, Expr, ExprRef, FStringElement, LiteralExpressionRef, OperatorPrecedence, - Pattern, Stmt, TypeParam, + Expr, ExprRef, FStringElement, LiteralExpressionRef, OperatorPrecedence, Pattern, Stmt, + TypeParam, }; /// See also [Module](https://docs.python.org/3/library/ast.html#ast.Module) @@ -40,95 +40,6 @@ pub struct ModExpression { pub body: Box, } -/// An AST node used to represent a IPython escape command at the statement level. -/// -/// For example, -/// ```python -/// %matplotlib inline -/// ``` -/// -/// ## Terminology -/// -/// Escape commands are special IPython syntax which starts with a token to identify -/// the escape kind followed by the command value itself. [Escape kind] are the kind -/// of escape commands that are recognized by the token: `%`, `%%`, `!`, `!!`, -/// `?`, `??`, `/`, `;`, and `,`. -/// -/// Help command (or Dynamic Object Introspection as it's called) are the escape commands -/// of the kind `?` and `??`. For example, `?str.replace`. Help end command are a subset -/// of Help command where the token can be at the end of the line i.e., after the value. -/// For example, `str.replace?`. -/// -/// Here's where things get tricky. I'll divide the help end command into two types for -/// better understanding: -/// 1. Strict version: The token is _only_ at the end of the line. For example, -/// `str.replace?` or `str.replace??`. -/// 2. Combined version: Along with the `?` or `??` token, which are at the end of the -/// line, there are other escape kind tokens that are present at the start as well. -/// For example, `%matplotlib?` or `%%timeit?`. -/// -/// Priority comes into picture for the "Combined version" mentioned above. How do -/// we determine the escape kind if there are tokens on both side of the value, i.e., which -/// token to choose? The Help end command always takes priority over any other token which -/// means that if there is `?`/`??` at the end then that is used to determine the kind. -/// For example, in `%matplotlib?` the escape kind is determined using the `?` token -/// instead of `%` token. -/// -/// ## Syntax -/// -/// `` -/// -/// The simplest form is an escape kind token followed by the command value. For example, -/// `%matplotlib inline`, `/foo`, `!pwd`, etc. -/// -/// `` -/// -/// The help end escape command would be the reverse of the above syntax. Here, the -/// escape kind token can only be either `?` or `??` and it is at the end of the line. -/// For example, `str.replace?`, `math.pi??`, etc. -/// -/// `` -/// -/// The final syntax is the combined version of the above two. For example, `%matplotlib?`, -/// `%%timeit??`, etc. -/// -/// [Escape kind]: IpyEscapeKind -#[derive(Clone, Debug, PartialEq)] -pub struct StmtIpyEscapeCommand { - pub range: TextRange, - pub kind: IpyEscapeKind, - pub value: Box, -} - -/// See also [FunctionDef](https://docs.python.org/3/library/ast.html#ast.FunctionDef) and -/// [AsyncFunctionDef](https://docs.python.org/3/library/ast.html#ast.AsyncFunctionDef). -/// -/// This type differs from the original Python AST, as it collapses the -/// synchronous and asynchronous variants into a single type. -#[derive(Clone, Debug, PartialEq)] -pub struct StmtFunctionDef { - pub range: TextRange, - pub is_async: bool, - pub decorator_list: Vec, - pub name: Identifier, - pub type_params: Option>, - pub parameters: Box, - pub returns: Option>, - pub body: Vec, -} - -/// See also [ClassDef](https://docs.python.org/3/library/ast.html#ast.ClassDef) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtClassDef { - pub range: TextRange, - pub decorator_list: Vec, - pub name: Identifier, - pub type_params: Option>, - // TODO: can remove? - pub arguments: Option>, - pub body: Vec, -} - impl StmtClassDef { /// Return an iterator over the bases of the class. pub fn bases(&self) -> &[Expr] { @@ -147,90 +58,6 @@ impl StmtClassDef { } } -/// See also [Return](https://docs.python.org/3/library/ast.html#ast.Return) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtReturn { - pub range: TextRange, - pub value: Option>, -} - -/// See also [Delete](https://docs.python.org/3/library/ast.html#ast.Delete) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtDelete { - pub range: TextRange, - pub targets: Vec, -} - -/// See also [TypeAlias](https://docs.python.org/3/library/ast.html#ast.TypeAlias) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtTypeAlias { - pub range: TextRange, - pub name: Box, - pub type_params: Option, - pub value: Box, -} - -/// See also [Assign](https://docs.python.org/3/library/ast.html#ast.Assign) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtAssign { - pub range: TextRange, - pub targets: Vec, - pub value: Box, -} - -/// See also [AugAssign](https://docs.python.org/3/library/ast.html#ast.AugAssign) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtAugAssign { - pub range: TextRange, - pub target: Box, - pub op: Operator, - pub value: Box, -} - -/// See also [AnnAssign](https://docs.python.org/3/library/ast.html#ast.AnnAssign) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtAnnAssign { - pub range: TextRange, - pub target: Box, - pub annotation: Box, - pub value: Option>, - pub simple: bool, -} - -/// See also [For](https://docs.python.org/3/library/ast.html#ast.For) and -/// [AsyncFor](https://docs.python.org/3/library/ast.html#ast.AsyncFor). -/// -/// This type differs from the original Python AST, as it collapses the -/// synchronous and asynchronous variants into a single type. -#[derive(Clone, Debug, PartialEq)] -pub struct StmtFor { - pub range: TextRange, - pub is_async: bool, - pub target: Box, - pub iter: Box, - pub body: Vec, - pub orelse: Vec, -} - -/// See also [While](https://docs.python.org/3/library/ast.html#ast.While) and -/// [AsyncWhile](https://docs.python.org/3/library/ast.html#ast.AsyncWhile). -#[derive(Clone, Debug, PartialEq)] -pub struct StmtWhile { - pub range: TextRange, - pub test: Box, - pub body: Vec, - pub orelse: Vec, -} - -/// See also [If](https://docs.python.org/3/library/ast.html#ast.If) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtIf { - pub range: TextRange, - pub test: Box, - pub body: Vec, - pub elif_else_clauses: Vec, -} - #[derive(Clone, Debug, PartialEq)] pub struct ElifElseClause { pub range: TextRange, @@ -238,110 +65,6 @@ pub struct ElifElseClause { pub body: Vec, } -/// See also [With](https://docs.python.org/3/library/ast.html#ast.With) and -/// [AsyncWith](https://docs.python.org/3/library/ast.html#ast.AsyncWith). -/// -/// This type differs from the original Python AST, as it collapses the -/// synchronous and asynchronous variants into a single type. -#[derive(Clone, Debug, PartialEq)] -pub struct StmtWith { - pub range: TextRange, - pub is_async: bool, - pub items: Vec, - pub body: Vec, -} - -/// See also [Match](https://docs.python.org/3/library/ast.html#ast.Match) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtMatch { - pub range: TextRange, - pub subject: Box, - pub cases: Vec, -} - -/// See also [Raise](https://docs.python.org/3/library/ast.html#ast.Raise) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtRaise { - pub range: TextRange, - pub exc: Option>, - pub cause: Option>, -} - -/// See also [Try](https://docs.python.org/3/library/ast.html#ast.Try) and -/// [TryStar](https://docs.python.org/3/library/ast.html#ast.TryStar) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtTry { - pub range: TextRange, - pub body: Vec, - pub handlers: Vec, - pub orelse: Vec, - pub finalbody: Vec, - pub is_star: bool, -} - -/// See also [Assert](https://docs.python.org/3/library/ast.html#ast.Assert) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtAssert { - pub range: TextRange, - pub test: Box, - pub msg: Option>, -} - -/// See also [Import](https://docs.python.org/3/library/ast.html#ast.Import) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtImport { - pub range: TextRange, - pub names: Vec, -} - -/// See also [ImportFrom](https://docs.python.org/3/library/ast.html#ast.ImportFrom) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtImportFrom { - pub range: TextRange, - pub module: Option, - pub names: Vec, - pub level: u32, -} - -/// See also [Global](https://docs.python.org/3/library/ast.html#ast.Global) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtGlobal { - pub range: TextRange, - pub names: Vec, -} - -/// See also [Nonlocal](https://docs.python.org/3/library/ast.html#ast.Nonlocal) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtNonlocal { - pub range: TextRange, - pub names: Vec, -} - -/// See also [Expr](https://docs.python.org/3/library/ast.html#ast.Expr) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtExpr { - pub range: TextRange, - pub value: Box, -} - -/// See also [Pass](https://docs.python.org/3/library/ast.html#ast.Pass) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtPass { - pub range: TextRange, -} - -/// See also [Break](https://docs.python.org/3/library/ast.html#ast.Break) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtBreak { - pub range: TextRange, -} - -/// See also [Continue](https://docs.python.org/3/library/ast.html#ast.Continue) -#[derive(Clone, Debug, PartialEq)] -pub struct StmtContinue { - pub range: TextRange, -} - impl Expr { /// Returns `true` if the expression is a literal expression. /// @@ -3366,9 +3089,6 @@ impl From for Singleton { #[cfg(test)] mod tests { - #[allow(clippy::wildcard_imports)] - use super::*; - use crate::generated::*; use crate::Mod; @@ -3380,8 +3100,7 @@ mod tests { assert!(std::mem::size_of::() <= 104); assert!(std::mem::size_of::() <= 112); assert!(std::mem::size_of::() <= 32); - // 96 for Rustc < 1.76 - assert!(matches!(std::mem::size_of::(), 88 | 96)); + assert!(matches!(std::mem::size_of::(), 88)); assert_eq!(std::mem::size_of::(), 64); assert_eq!(std::mem::size_of::(), 56); @@ -3395,8 +3114,7 @@ mod tests { assert_eq!(std::mem::size_of::(), 32); assert_eq!(std::mem::size_of::(), 48); assert_eq!(std::mem::size_of::(), 8); - // 56 for Rustc < 1.76 - assert!(matches!(std::mem::size_of::(), 48 | 56)); + assert!(matches!(std::mem::size_of::(), 48)); assert_eq!(std::mem::size_of::(), 48); assert_eq!(std::mem::size_of::(), 32); assert_eq!(std::mem::size_of::(), 32);