#![allow(clippy::derive_partial_eq_without_eq)] use std::iter; use itertools::Itertools; use rustpython_parser::ast::{Constant, Location}; use rustpython_parser::Mode; use ruff_python_ast::source_code::Locator; use ruff_python_ast::types::Range; use crate::core::helpers::{expand_indented_block, find_tok, is_elif}; use crate::trivia::{Parenthesize, Trivia}; type Ident = String; #[derive(Clone, Debug, PartialEq)] pub struct Located { pub location: Location, pub end_location: Option, pub node: T, pub trivia: Vec, pub parentheses: Parenthesize, } impl Located { pub fn new(location: Location, end_location: Location, node: T) -> Self { Self { location, end_location: Some(end_location), node, trivia: Vec::new(), parentheses: Parenthesize::Never, } } pub fn add_trivia(&mut self, trivia: Trivia) { self.trivia.push(trivia); } pub fn id(&self) -> usize { self as *const _ as usize } } impl From<&Located> for Range { fn from(located: &Located) -> Self { Self::new(located.location, located.end_location.unwrap()) } } impl From<&Box>> for Range { fn from(located: &Box>) -> Self { Self::new(located.location, located.end_location.unwrap()) } } #[derive(Clone, Debug, PartialEq)] pub enum ExprContext { Load, Store, Del, } impl From for ExprContext { fn from(context: rustpython_parser::ast::ExprContext) -> Self { match context { rustpython_parser::ast::ExprContext::Load => Self::Load, rustpython_parser::ast::ExprContext::Store => Self::Store, rustpython_parser::ast::ExprContext::Del => Self::Del, } } } #[derive(Clone, Debug, PartialEq)] pub enum BoolOpKind { And, Or, } impl From<&rustpython_parser::ast::Boolop> for BoolOpKind { fn from(op: &rustpython_parser::ast::Boolop) -> Self { match op { rustpython_parser::ast::Boolop::And => Self::And, rustpython_parser::ast::Boolop::Or => Self::Or, } } } pub type BoolOp = Located; #[derive(Clone, Debug, PartialEq)] pub enum OperatorKind { Add, Sub, Mult, MatMult, Div, Mod, Pow, LShift, RShift, BitOr, BitXor, BitAnd, FloorDiv, } pub type Operator = Located; impl From<&rustpython_parser::ast::Operator> for OperatorKind { fn from(op: &rustpython_parser::ast::Operator) -> Self { match op { rustpython_parser::ast::Operator::Add => Self::Add, rustpython_parser::ast::Operator::Sub => Self::Sub, rustpython_parser::ast::Operator::Mult => Self::Mult, rustpython_parser::ast::Operator::MatMult => Self::MatMult, rustpython_parser::ast::Operator::Div => Self::Div, rustpython_parser::ast::Operator::Mod => Self::Mod, rustpython_parser::ast::Operator::Pow => Self::Pow, rustpython_parser::ast::Operator::LShift => Self::LShift, rustpython_parser::ast::Operator::RShift => Self::RShift, rustpython_parser::ast::Operator::BitOr => Self::BitOr, rustpython_parser::ast::Operator::BitXor => Self::BitXor, rustpython_parser::ast::Operator::BitAnd => Self::BitAnd, rustpython_parser::ast::Operator::FloorDiv => Self::FloorDiv, } } } #[derive(Clone, Debug, PartialEq)] pub enum UnaryOpKind { Invert, Not, UAdd, USub, } pub type UnaryOp = Located; impl From<&rustpython_parser::ast::Unaryop> for UnaryOpKind { fn from(op: &rustpython_parser::ast::Unaryop) -> Self { match op { rustpython_parser::ast::Unaryop::Invert => Self::Invert, rustpython_parser::ast::Unaryop::Not => Self::Not, rustpython_parser::ast::Unaryop::UAdd => Self::UAdd, rustpython_parser::ast::Unaryop::USub => Self::USub, } } } #[derive(Clone, Debug, PartialEq)] pub enum CmpOpKind { Eq, NotEq, Lt, LtE, Gt, GtE, Is, IsNot, In, NotIn, } pub type CmpOp = Located; impl From<&rustpython_parser::ast::Cmpop> for CmpOpKind { fn from(op: &rustpython_parser::ast::Cmpop) -> Self { match op { rustpython_parser::ast::Cmpop::Eq => Self::Eq, rustpython_parser::ast::Cmpop::NotEq => Self::NotEq, rustpython_parser::ast::Cmpop::Lt => Self::Lt, rustpython_parser::ast::Cmpop::LtE => Self::LtE, rustpython_parser::ast::Cmpop::Gt => Self::Gt, rustpython_parser::ast::Cmpop::GtE => Self::GtE, rustpython_parser::ast::Cmpop::Is => Self::Is, rustpython_parser::ast::Cmpop::IsNot => Self::IsNot, rustpython_parser::ast::Cmpop::In => Self::In, rustpython_parser::ast::Cmpop::NotIn => Self::NotIn, } } } pub type Body = Located>; impl From<(Vec, &Locator<'_>)> for Body { fn from((body, locator): (Vec, &Locator)) -> Self { Body { location: body.first().unwrap().location, end_location: body.last().unwrap().end_location, node: body .into_iter() .map(|node| (node, locator).into()) .collect(), trivia: vec![], parentheses: Parenthesize::Never, } } } #[derive(Clone, Debug, PartialEq)] pub enum StmtKind { FunctionDef { name: Ident, args: Box, body: Body, decorator_list: Vec, returns: Option>, type_comment: Option, }, AsyncFunctionDef { name: Ident, args: Box, body: Body, decorator_list: Vec, returns: Option>, type_comment: Option, }, ClassDef { name: Ident, bases: Vec, keywords: Vec, body: Body, decorator_list: Vec, }, Return { value: Option, }, Delete { targets: Vec, }, Assign { targets: Vec, value: Box, type_comment: Option, }, AugAssign { target: Box, op: Operator, value: Box, }, AnnAssign { target: Box, annotation: Box, value: Option>, simple: usize, }, For { target: Box, iter: Box, body: Body, orelse: Option, type_comment: Option, }, AsyncFor { target: Box, iter: Box, body: Body, orelse: Option, type_comment: Option, }, While { test: Box, body: Body, orelse: Option, }, If { test: Box, body: Body, orelse: Option, is_elif: bool, }, With { items: Vec, body: Body, type_comment: Option, }, AsyncWith { items: Vec, body: Body, type_comment: Option, }, Match { subject: Box, cases: Vec, }, Raise { exc: Option>, cause: Option>, }, Try { body: Body, handlers: Vec, orelse: Option, finalbody: Option, }, TryStar { body: Body, handlers: Vec, orelse: Option, finalbody: Option, }, Assert { test: Box, msg: Option>, }, Import { names: Vec, }, ImportFrom { module: Option, names: Vec, level: Option, }, Global { names: Vec, }, Nonlocal { names: Vec, }, Expr { value: Box, }, Pass, Break, Continue, } pub type Stmt = Located; #[derive(Clone, Debug, PartialEq)] pub enum ExprKind { BoolOp { ops: Vec, values: Vec, }, NamedExpr { target: Box, value: Box, }, BinOp { left: Box, op: Operator, right: Box, }, UnaryOp { op: UnaryOp, operand: Box, }, Lambda { args: Box, body: Box, }, IfExp { test: Box, body: Box, orelse: Box, }, Dict { keys: Vec>, values: Vec, }, Set { elts: Vec, }, ListComp { elt: Box, generators: Vec, }, SetComp { elt: Box, generators: Vec, }, DictComp { key: Box, value: Box, generators: Vec, }, GeneratorExp { elt: Box, generators: Vec, }, Await { value: Box, }, Yield { value: Option>, }, YieldFrom { value: Box, }, Compare { left: Box, ops: Vec, comparators: Vec, }, Call { func: Box, args: Vec, keywords: Vec, }, FormattedValue { value: Box, conversion: usize, format_spec: Option>, }, JoinedStr { values: Vec, }, Constant { value: Constant, kind: Option, }, Attribute { value: Box, attr: Ident, ctx: ExprContext, }, Subscript { value: Box, slice: Box, ctx: ExprContext, }, Starred { value: Box, ctx: ExprContext, }, Name { id: String, ctx: ExprContext, }, List { elts: Vec, ctx: ExprContext, }, Tuple { elts: Vec, ctx: ExprContext, }, Slice { lower: SliceIndex, upper: SliceIndex, step: Option, }, } pub type Expr = Located; #[derive(Clone, Debug, PartialEq)] pub struct Comprehension { pub target: Expr, pub iter: Expr, pub ifs: Vec, pub is_async: usize, } #[derive(Clone, Debug, PartialEq)] pub enum ExcepthandlerKind { ExceptHandler { type_: Option>, name: Option, body: Body, }, } pub type Excepthandler = Located; #[derive(Clone, Debug, PartialEq)] pub enum SliceIndexKind { /// The index slot exists, but is empty. Empty, /// The index slot contains an expression. Index { value: Box }, } pub type SliceIndex = Located; #[derive(Clone, Debug, PartialEq)] pub struct Arguments { pub posonlyargs: Vec, pub args: Vec, pub vararg: Option>, pub kwonlyargs: Vec, pub kw_defaults: Vec, pub kwarg: Option>, pub defaults: Vec, } #[derive(Clone, Debug, PartialEq)] pub struct ArgData { pub arg: Ident, pub annotation: Option>, pub type_comment: Option, } pub type Arg = Located; #[derive(Clone, Debug, PartialEq)] pub struct KeywordData { pub arg: Option, pub value: Expr, } pub type Keyword = Located; #[derive(Clone, Debug, PartialEq)] pub struct AliasData { pub name: Ident, pub asname: Option, } pub type Alias = Located; #[derive(Clone, Debug, PartialEq)] pub struct Withitem { pub context_expr: Expr, pub optional_vars: Option>, } #[derive(Clone, Debug, PartialEq)] pub struct MatchCase { pub pattern: Pattern, pub guard: Option>, pub body: Body, } #[allow(clippy::enum_variant_names)] #[derive(Clone, Debug, PartialEq)] pub enum PatternKind { MatchValue { value: Box, }, MatchSingleton { value: Constant, }, MatchSequence { patterns: Vec, }, MatchMapping { keys: Vec, patterns: Vec, rest: Option, }, MatchClass { cls: Box, patterns: Vec, kwd_attrs: Vec, kwd_patterns: Vec, }, MatchStar { name: Option, }, MatchAs { pattern: Option>, name: Option, }, MatchOr { patterns: Vec, }, } pub type Pattern = Located; impl From<(rustpython_parser::ast::Alias, &Locator<'_>)> for Alias { fn from((alias, _locator): (rustpython_parser::ast::Alias, &Locator)) -> Self { Alias { location: alias.location, end_location: alias.end_location, node: AliasData { name: alias.node.name, asname: alias.node.asname, }, trivia: vec![], parentheses: Parenthesize::Never, } } } impl From<(rustpython_parser::ast::Withitem, &Locator<'_>)> for Withitem { fn from((withitem, locator): (rustpython_parser::ast::Withitem, &Locator)) -> Self { Withitem { context_expr: (withitem.context_expr, locator).into(), optional_vars: withitem .optional_vars .map(|v| Box::new((*v, locator).into())), } } } impl From<(rustpython_parser::ast::Excepthandler, &Locator<'_>)> for Excepthandler { fn from((excepthandler, locator): (rustpython_parser::ast::Excepthandler, &Locator)) -> Self { let rustpython_parser::ast::ExcepthandlerKind::ExceptHandler { type_, name, body } = excepthandler.node; // Find the start and end of the `body`. let body = { let (body_location, body_end_location) = expand_indented_block( excepthandler.location, body.last().unwrap().end_location.unwrap(), locator, ); Body { location: body_location, end_location: Some(body_end_location), node: body .into_iter() .map(|node| (node, locator).into()) .collect(), trivia: vec![], parentheses: Parenthesize::Never, } }; Excepthandler { location: excepthandler.location, end_location: body.end_location, node: ExcepthandlerKind::ExceptHandler { type_: type_.map(|type_| Box::new((*type_, locator).into())), name, body, }, trivia: vec![], parentheses: Parenthesize::Never, } } } impl From<(rustpython_parser::ast::Pattern, &Locator<'_>)> for Pattern { fn from((pattern, locator): (rustpython_parser::ast::Pattern, &Locator)) -> Self { Pattern { location: pattern.location, end_location: pattern.end_location, node: match pattern.node { rustpython_parser::ast::PatternKind::MatchValue { value } => { PatternKind::MatchValue { value: Box::new((*value, locator).into()), } } rustpython_parser::ast::PatternKind::MatchSingleton { value } => { PatternKind::MatchSingleton { value } } rustpython_parser::ast::PatternKind::MatchSequence { patterns } => { PatternKind::MatchSequence { patterns: patterns .into_iter() .map(|pattern| (pattern, locator).into()) .collect(), } } rustpython_parser::ast::PatternKind::MatchMapping { keys, patterns, rest, } => PatternKind::MatchMapping { keys: keys.into_iter().map(|key| (key, locator).into()).collect(), patterns: patterns .into_iter() .map(|pattern| (pattern, locator).into()) .collect(), rest, }, rustpython_parser::ast::PatternKind::MatchClass { cls, patterns, kwd_attrs, kwd_patterns, } => PatternKind::MatchClass { cls: Box::new((*cls, locator).into()), patterns: patterns .into_iter() .map(|pattern| (pattern, locator).into()) .collect(), kwd_attrs, kwd_patterns: kwd_patterns .into_iter() .map(|pattern| (pattern, locator).into()) .collect(), }, rustpython_parser::ast::PatternKind::MatchStar { name } => { PatternKind::MatchStar { name } } rustpython_parser::ast::PatternKind::MatchAs { pattern, name } => { PatternKind::MatchAs { pattern: pattern.map(|pattern| Box::new((*pattern, locator).into())), name, } } rustpython_parser::ast::PatternKind::MatchOr { patterns } => PatternKind::MatchOr { patterns: patterns .into_iter() .map(|pattern| (pattern, locator).into()) .collect(), }, }, trivia: vec![], parentheses: Parenthesize::Never, } } } impl From<(rustpython_parser::ast::MatchCase, &Locator<'_>)> for MatchCase { fn from((match_case, locator): (rustpython_parser::ast::MatchCase, &Locator)) -> Self { // Find the start and end of the `body`. let body = { let (body_location, body_end_location) = expand_indented_block( match_case.pattern.location, match_case.body.last().unwrap().end_location.unwrap(), locator, ); Body { location: body_location, end_location: Some(body_end_location), node: match_case .body .into_iter() .map(|node| (node, locator).into()) .collect(), trivia: vec![], parentheses: Parenthesize::Never, } }; MatchCase { pattern: (match_case.pattern, locator).into(), guard: match_case .guard .map(|guard| Box::new((*guard, locator).into())), body, } } } impl From<(rustpython_parser::ast::Stmt, &Locator<'_>)> for Stmt { fn from((stmt, locator): (rustpython_parser::ast::Stmt, &Locator)) -> Self { match stmt.node { rustpython_parser::ast::StmtKind::Expr { value } => Stmt { location: stmt.location, end_location: stmt.end_location, node: StmtKind::Expr { value: Box::new((*value, locator).into()), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::StmtKind::Pass => Stmt { location: stmt.location, end_location: stmt.end_location, node: StmtKind::Pass, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::StmtKind::Return { value } => Stmt { location: stmt.location, end_location: stmt.end_location, node: StmtKind::Return { value: value.map(|v| (*v, locator).into()), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::StmtKind::Assign { targets, value, type_comment, } => Stmt { location: stmt.location, end_location: stmt.end_location, node: StmtKind::Assign { targets: targets .into_iter() .map(|node| (node, locator).into()) .collect(), value: Box::new((*value, locator).into()), type_comment, }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::StmtKind::ClassDef { name, bases, keywords, body, decorator_list, } => { // Find the start and end of the `body`. let body = { let (body_location, body_end_location) = expand_indented_block( stmt.location, body.last().unwrap().end_location.unwrap(), locator, ); Body { location: body_location, end_location: Some(body_end_location), node: body .into_iter() .map(|node| (node, locator).into()) .collect(), trivia: vec![], parentheses: Parenthesize::Never, } }; Stmt { location: stmt.location, end_location: body.end_location, node: StmtKind::ClassDef { name, bases: bases .into_iter() .map(|node| (node, locator).into()) .collect(), keywords: keywords .into_iter() .map(|node| (node, locator).into()) .collect(), body, decorator_list: decorator_list .into_iter() .map(|node| (node, locator).into()) .collect(), }, trivia: vec![], parentheses: Parenthesize::Never, } } rustpython_parser::ast::StmtKind::If { test, body, orelse } => { // Find the start and end of the `body`. let body = { let (body_location, body_end_location) = expand_indented_block( stmt.location, body.last().unwrap().end_location.unwrap(), locator, ); Body { location: body_location, end_location: Some(body_end_location), node: body .into_iter() .map(|node| (node, locator).into()) .collect(), trivia: vec![], parentheses: Parenthesize::Never, } }; if orelse.is_empty() { // No `else` block. Stmt { location: stmt.location, end_location: body.end_location, node: StmtKind::If { test: Box::new((*test, locator).into()), body, orelse: None, is_elif: false, }, trivia: vec![], parentheses: Parenthesize::Never, } } else { if is_elif(&orelse, locator) { // Find the start and end of the `elif`. let mut elif: Body = (orelse, locator).into(); if let StmtKind::If { is_elif, .. } = &mut elif.node.first_mut().unwrap().node { *is_elif = true; }; Stmt { location: stmt.location, end_location: elif.end_location, node: StmtKind::If { test: Box::new((*test, locator).into()), body, orelse: Some(elif), is_elif: false, }, trivia: vec![], parentheses: Parenthesize::Never, } } else { // Find the start and end of the `else`. let (orelse_location, orelse_end_location) = expand_indented_block( body.end_location.unwrap(), orelse.last().unwrap().end_location.unwrap(), locator, ); let orelse = Body { location: orelse_location, end_location: Some(orelse_end_location), node: orelse .into_iter() .map(|node| (node, locator).into()) .collect(), trivia: vec![], parentheses: Parenthesize::Never, }; Stmt { location: stmt.location, end_location: orelse.end_location, node: StmtKind::If { test: Box::new((*test, locator).into()), body, orelse: Some(orelse), is_elif: false, }, trivia: vec![], parentheses: Parenthesize::Never, } } } } rustpython_parser::ast::StmtKind::Assert { test, msg } => Stmt { location: stmt.location, end_location: stmt.end_location, node: StmtKind::Assert { test: Box::new((*test, locator).into()), msg: msg.map(|node| Box::new((*node, locator).into())), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::StmtKind::FunctionDef { name, args, body, decorator_list, returns, type_comment, } => { // Find the start and end of the `body`. let body = { let (body_location, body_end_location) = expand_indented_block( stmt.location, body.last().unwrap().end_location.unwrap(), locator, ); Body { location: body_location, end_location: Some(body_end_location), node: body .into_iter() .map(|node| (node, locator).into()) .collect(), trivia: vec![], parentheses: Parenthesize::Never, } }; Stmt { location: decorator_list.first().map_or(stmt.location, |d| d.location), end_location: body.end_location, node: StmtKind::FunctionDef { name, args: Box::new((*args, locator).into()), body, decorator_list: decorator_list .into_iter() .map(|node| (node, locator).into()) .collect(), returns: returns.map(|r| Box::new((*r, locator).into())), type_comment, }, trivia: vec![], parentheses: Parenthesize::Never, } } rustpython_parser::ast::StmtKind::AsyncFunctionDef { name, args, body, decorator_list, returns, type_comment, } => { // Find the start and end of the `body`. let body = { let (body_location, body_end_location) = expand_indented_block( stmt.location, body.last().unwrap().end_location.unwrap(), locator, ); Body { location: body_location, end_location: Some(body_end_location), node: body .into_iter() .map(|node| (node, locator).into()) .collect(), trivia: vec![], parentheses: Parenthesize::Never, } }; Stmt { location: decorator_list .first() .map_or(stmt.location, |expr| expr.location), end_location: body.end_location, node: StmtKind::AsyncFunctionDef { name, args: Box::new((*args, locator).into()), body, decorator_list: decorator_list .into_iter() .map(|node| (node, locator).into()) .collect(), returns: returns.map(|r| Box::new((*r, locator).into())), type_comment, }, trivia: vec![], parentheses: Parenthesize::Never, } } rustpython_parser::ast::StmtKind::Delete { targets } => Stmt { location: stmt.location, end_location: stmt.end_location, node: StmtKind::Delete { targets: targets .into_iter() .map(|node| (node, locator).into()) .collect(), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::StmtKind::AugAssign { target, op, value } => Stmt { location: stmt.location, end_location: stmt.end_location, node: StmtKind::AugAssign { op: { let target_tok = match &op { rustpython_parser::ast::Operator::Add => { rustpython_parser::Tok::PlusEqual } rustpython_parser::ast::Operator::Sub => { rustpython_parser::Tok::MinusEqual } rustpython_parser::ast::Operator::Mult => { rustpython_parser::Tok::StarEqual } rustpython_parser::ast::Operator::MatMult => { rustpython_parser::Tok::AtEqual } rustpython_parser::ast::Operator::Div => { rustpython_parser::Tok::SlashEqual } rustpython_parser::ast::Operator::Mod => { rustpython_parser::Tok::PercentEqual } rustpython_parser::ast::Operator::Pow => { rustpython_parser::Tok::DoubleStarEqual } rustpython_parser::ast::Operator::LShift => { rustpython_parser::Tok::LeftShiftEqual } rustpython_parser::ast::Operator::RShift => { rustpython_parser::Tok::RightShiftEqual } rustpython_parser::ast::Operator::BitOr => { rustpython_parser::Tok::VbarEqual } rustpython_parser::ast::Operator::BitXor => { rustpython_parser::Tok::CircumflexEqual } rustpython_parser::ast::Operator::BitAnd => { rustpython_parser::Tok::AmperEqual } rustpython_parser::ast::Operator::FloorDiv => { rustpython_parser::Tok::DoubleSlashEqual } }; let (op_location, op_end_location) = find_tok( target.end_location.unwrap(), value.location, locator, |tok| tok == target_tok, ); Operator::new(op_location, op_end_location, (&op).into()) }, target: Box::new((*target, locator).into()), value: Box::new((*value, locator).into()), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::StmtKind::AnnAssign { target, annotation, value, simple, } => Stmt { location: stmt.location, end_location: stmt.end_location, node: StmtKind::AnnAssign { target: Box::new((*target, locator).into()), annotation: Box::new((*annotation, locator).into()), value: value.map(|node| Box::new((*node, locator).into())), simple, }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::StmtKind::For { target, iter, body, orelse, type_comment, } => { // Find the start and end of the `body`. let body = { let (body_location, body_end_location) = expand_indented_block( stmt.location, body.last().unwrap().end_location.unwrap(), locator, ); Body { location: body_location, end_location: Some(body_end_location), node: body .into_iter() .map(|node| (node, locator).into()) .collect(), trivia: vec![], parentheses: Parenthesize::Never, } }; // Find the start and end of the `orelse`. let orelse = (!orelse.is_empty()).then(|| { let (orelse_location, orelse_end_location) = expand_indented_block( body.end_location.unwrap(), orelse.last().unwrap().end_location.unwrap(), locator, ); Body { location: orelse_location, end_location: Some(orelse_end_location), node: orelse .into_iter() .map(|node| (node, locator).into()) .collect(), trivia: vec![], parentheses: Parenthesize::Never, } }); Stmt { location: stmt.location, end_location: orelse.as_ref().unwrap_or(&body).end_location, node: StmtKind::For { target: Box::new((*target, locator).into()), iter: Box::new((*iter, locator).into()), body, orelse, type_comment, }, trivia: vec![], parentheses: Parenthesize::Never, } } rustpython_parser::ast::StmtKind::AsyncFor { target, iter, body, orelse, type_comment, } => { // Find the start and end of the `body`. let body = { let (body_location, body_end_location) = expand_indented_block( stmt.location, body.last().unwrap().end_location.unwrap(), locator, ); Body { location: body_location, end_location: Some(body_end_location), node: body .into_iter() .map(|node| (node, locator).into()) .collect(), trivia: vec![], parentheses: Parenthesize::Never, } }; // Find the start and end of the `orelse`. let orelse = (!orelse.is_empty()).then(|| { let (orelse_location, orelse_end_location) = expand_indented_block( body.end_location.unwrap(), orelse.last().unwrap().end_location.unwrap(), locator, ); Body { location: orelse_location, end_location: Some(orelse_end_location), node: orelse .into_iter() .map(|node| (node, locator).into()) .collect(), trivia: vec![], parentheses: Parenthesize::Never, } }); Stmt { location: stmt.location, end_location: orelse.as_ref().unwrap_or(&body).end_location, node: StmtKind::AsyncFor { target: Box::new((*target, locator).into()), iter: Box::new((*iter, locator).into()), body, orelse, type_comment, }, trivia: vec![], parentheses: Parenthesize::Never, } } rustpython_parser::ast::StmtKind::While { test, body, orelse } => { // Find the start and end of the `body`. let body = { let (body_location, body_end_location) = expand_indented_block( stmt.location, body.last().unwrap().end_location.unwrap(), locator, ); Body { location: body_location, end_location: Some(body_end_location), node: body .into_iter() .map(|node| (node, locator).into()) .collect(), trivia: vec![], parentheses: Parenthesize::Never, } }; // Find the start and end of the `orelse`. let orelse = (!orelse.is_empty()).then(|| { let (orelse_location, orelse_end_location) = expand_indented_block( body.end_location.unwrap(), orelse.last().unwrap().end_location.unwrap(), locator, ); Body { location: orelse_location, end_location: Some(orelse_end_location), node: orelse .into_iter() .map(|node| (node, locator).into()) .collect(), trivia: vec![], parentheses: Parenthesize::Never, } }); Stmt { location: stmt.location, end_location: orelse.as_ref().unwrap_or(&body).end_location, node: StmtKind::While { test: Box::new((*test, locator).into()), body, orelse, }, trivia: vec![], parentheses: Parenthesize::Never, } } rustpython_parser::ast::StmtKind::With { items, body, type_comment, } => { // Find the start and end of the `body`. let body = { let (body_location, body_end_location) = expand_indented_block( stmt.location, body.last().unwrap().end_location.unwrap(), locator, ); Body { location: body_location, end_location: Some(body_end_location), node: body .into_iter() .map(|node| (node, locator).into()) .collect(), trivia: vec![], parentheses: Parenthesize::Never, } }; Stmt { location: stmt.location, end_location: body.end_location, node: StmtKind::With { items: items .into_iter() .map(|node| (node, locator).into()) .collect(), body, type_comment, }, trivia: vec![], parentheses: Parenthesize::Never, } } rustpython_parser::ast::StmtKind::AsyncWith { items, body, type_comment, } => { // Find the start and end of the `body`. let body = { let (body_location, body_end_location) = expand_indented_block( stmt.location, body.last().unwrap().end_location.unwrap(), locator, ); Body { location: body_location, end_location: Some(body_end_location), node: body .into_iter() .map(|node| (node, locator).into()) .collect(), trivia: vec![], parentheses: Parenthesize::Never, } }; Stmt { location: stmt.location, end_location: body.end_location, node: StmtKind::AsyncWith { items: items .into_iter() .map(|node| (node, locator).into()) .collect(), body, type_comment, }, trivia: vec![], parentheses: Parenthesize::Never, } } rustpython_parser::ast::StmtKind::Match { subject, cases } => Stmt { location: stmt.location, end_location: stmt.end_location, node: StmtKind::Match { subject: Box::new((*subject, locator).into()), cases: cases .into_iter() .map(|node| (node, locator).into()) .collect(), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::StmtKind::Raise { exc, cause } => Stmt { location: stmt.location, end_location: stmt.end_location, node: StmtKind::Raise { exc: exc.map(|exc| Box::new((*exc, locator).into())), cause: cause.map(|cause| Box::new((*cause, locator).into())), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::StmtKind::Try { body, handlers, orelse, finalbody, } => { // Find the start and end of the `body`. let body = { let (body_location, body_end_location) = expand_indented_block( stmt.location, body.last().unwrap().end_location.unwrap(), locator, ); Body { location: body_location, end_location: Some(body_end_location), node: body .into_iter() .map(|node| (node, locator).into()) .collect(), trivia: vec![], parentheses: Parenthesize::Never, } }; let handlers: Vec = handlers .into_iter() .map(|node| (node, locator).into()) .collect(); // Find the start and end of the `orelse`. let orelse = (!orelse.is_empty()).then(|| { let (orelse_location, orelse_end_location) = expand_indented_block( handlers .last() .map_or(body.end_location.unwrap(), |handler| { handler.end_location.unwrap() }), orelse.last().unwrap().end_location.unwrap(), locator, ); Body { location: orelse_location, end_location: Some(orelse_end_location), node: orelse .into_iter() .map(|node| (node, locator).into()) .collect(), trivia: vec![], parentheses: Parenthesize::Never, } }); // Find the start and end of the `finalbody`. let finalbody = (!finalbody.is_empty()).then(|| { let (finalbody_location, finalbody_end_location) = expand_indented_block( orelse.as_ref().map_or( handlers .last() .map_or(body.end_location.unwrap(), |handler| { handler.end_location.unwrap() }), |orelse| orelse.end_location.unwrap(), ), finalbody.last().unwrap().end_location.unwrap(), locator, ); Body { location: finalbody_location, end_location: Some(finalbody_end_location), node: finalbody .into_iter() .map(|node| (node, locator).into()) .collect(), trivia: vec![], parentheses: Parenthesize::Never, } }); let end_location = finalbody.as_ref().map_or( orelse.as_ref().map_or( handlers .last() .map_or(body.end_location.unwrap(), |handler| { handler.end_location.unwrap() }), |orelse| orelse.end_location.unwrap(), ), |finalbody| finalbody.end_location.unwrap(), ); Stmt { location: stmt.location, end_location: Some(end_location), node: StmtKind::Try { body, handlers, orelse, finalbody, }, trivia: vec![], parentheses: Parenthesize::Never, } } rustpython_parser::ast::StmtKind::TryStar { body, handlers, orelse, finalbody, } => { // Find the start and end of the `body`. let body = { let (body_location, body_end_location) = expand_indented_block( stmt.location, body.last().unwrap().end_location.unwrap(), locator, ); Body { location: body_location, end_location: Some(body_end_location), node: body .into_iter() .map(|node| (node, locator).into()) .collect(), trivia: vec![], parentheses: Parenthesize::Never, } }; let handlers: Vec = handlers .into_iter() .map(|node| (node, locator).into()) .collect(); // Find the start and end of the `orelse`. let orelse = (!orelse.is_empty()).then(|| { let (orelse_location, orelse_end_location) = expand_indented_block( handlers .last() .map_or(body.end_location.unwrap(), |handler| { handler.end_location.unwrap() }), orelse.last().unwrap().end_location.unwrap(), locator, ); Body { location: orelse_location, end_location: Some(orelse_end_location), node: orelse .into_iter() .map(|node| (node, locator).into()) .collect(), trivia: vec![], parentheses: Parenthesize::Never, } }); // Find the start and end of the `finalbody`. let finalbody = (!finalbody.is_empty()).then(|| { let (finalbody_location, finalbody_end_location) = expand_indented_block( orelse.as_ref().map_or( handlers .last() .map_or(body.end_location.unwrap(), |handler| { handler.end_location.unwrap() }), |orelse| orelse.end_location.unwrap(), ), finalbody.last().unwrap().end_location.unwrap(), locator, ); Body { location: finalbody_location, end_location: Some(finalbody_end_location), node: finalbody .into_iter() .map(|node| (node, locator).into()) .collect(), trivia: vec![], parentheses: Parenthesize::Never, } }); let end_location = finalbody.as_ref().map_or( orelse.as_ref().map_or( handlers .last() .map_or(body.end_location.unwrap(), |handler| { handler.end_location.unwrap() }), |orelse| orelse.end_location.unwrap(), ), |finalbody| finalbody.end_location.unwrap(), ); Stmt { location: stmt.location, end_location: Some(end_location), node: StmtKind::TryStar { body, handlers, orelse, finalbody, }, trivia: vec![], parentheses: Parenthesize::Never, } } rustpython_parser::ast::StmtKind::Import { names } => Stmt { location: stmt.location, end_location: stmt.end_location, node: StmtKind::Import { names: names .into_iter() .map(|node| (node, locator).into()) .collect(), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::StmtKind::ImportFrom { module, names, level, } => Stmt { location: stmt.location, end_location: stmt.end_location, node: StmtKind::ImportFrom { module, names: names .into_iter() .map(|node| (node, locator).into()) .collect(), level, }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::StmtKind::Global { names } => Stmt { location: stmt.location, end_location: stmt.end_location, node: StmtKind::Global { names }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::StmtKind::Nonlocal { names } => Stmt { location: stmt.location, end_location: stmt.end_location, node: StmtKind::Nonlocal { names }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::StmtKind::Break => Stmt { location: stmt.location, end_location: stmt.end_location, node: StmtKind::Break, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::StmtKind::Continue => Stmt { location: stmt.location, end_location: stmt.end_location, node: StmtKind::Continue, trivia: vec![], parentheses: Parenthesize::Never, }, } } } impl From<(rustpython_parser::ast::Keyword, &Locator<'_>)> for Keyword { fn from((keyword, locator): (rustpython_parser::ast::Keyword, &Locator)) -> Self { Keyword { location: keyword.location, end_location: keyword.end_location, node: KeywordData { arg: keyword.node.arg, value: (keyword.node.value, locator).into(), }, trivia: vec![], parentheses: Parenthesize::Never, } } } impl From<(rustpython_parser::ast::Arg, &Locator<'_>)> for Arg { fn from((arg, locator): (rustpython_parser::ast::Arg, &Locator)) -> Self { Arg { location: arg.location, end_location: arg.end_location, node: ArgData { arg: arg.node.arg, annotation: arg .node .annotation .map(|node| Box::new((*node, locator).into())), type_comment: arg.node.type_comment, }, trivia: vec![], parentheses: Parenthesize::Never, } } } impl From<(rustpython_parser::ast::Arguments, &Locator<'_>)> for Arguments { fn from((arguments, locator): (rustpython_parser::ast::Arguments, &Locator)) -> Self { Arguments { posonlyargs: arguments .posonlyargs .into_iter() .map(|node| (node, locator).into()) .collect(), args: arguments .args .into_iter() .map(|node| (node, locator).into()) .collect(), vararg: arguments .vararg .map(|node| Box::new((*node, locator).into())), kwonlyargs: arguments .kwonlyargs .into_iter() .map(|node| (node, locator).into()) .collect(), kw_defaults: arguments .kw_defaults .into_iter() .map(|node| (node, locator).into()) .collect(), kwarg: arguments .kwarg .map(|node| Box::new((*node, locator).into())), defaults: arguments .defaults .into_iter() .map(|node| (node, locator).into()) .collect(), } } } impl From<(rustpython_parser::ast::Comprehension, &Locator<'_>)> for Comprehension { fn from((comprehension, locator): (rustpython_parser::ast::Comprehension, &Locator)) -> Self { Comprehension { target: (comprehension.target, locator).into(), iter: (comprehension.iter, locator).into(), ifs: comprehension .ifs .into_iter() .map(|node| (node, locator).into()) .collect(), is_async: comprehension.is_async, } } } impl From<(rustpython_parser::ast::Expr, &Locator<'_>)> for Expr { fn from((expr, locator): (rustpython_parser::ast::Expr, &Locator)) -> Self { match expr.node { rustpython_parser::ast::ExprKind::Name { id, ctx } => Expr { location: expr.location, end_location: expr.end_location, node: ExprKind::Name { id, ctx: ctx.into(), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::ExprKind::BoolOp { op, values } => Expr { location: expr.location, end_location: expr.end_location, node: ExprKind::BoolOp { ops: values .iter() .tuple_windows() .map(|(left, right)| { let target_tok = match &op { rustpython_parser::ast::Boolop::And => rustpython_parser::Tok::And, rustpython_parser::ast::Boolop::Or => rustpython_parser::Tok::Or, }; let (op_location, op_end_location) = find_tok( left.end_location.unwrap(), right.location, locator, |tok| tok == target_tok, ); BoolOp::new(op_location, op_end_location, (&op).into()) }) .collect(), values: values .into_iter() .map(|node| (node, locator).into()) .collect(), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::ExprKind::NamedExpr { target, value } => Expr { location: expr.location, end_location: expr.end_location, node: ExprKind::NamedExpr { target: Box::new((*target, locator).into()), value: Box::new((*value, locator).into()), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::ExprKind::BinOp { left, op, right } => Expr { location: expr.location, end_location: expr.end_location, node: ExprKind::BinOp { op: { let target_tok = match &op { rustpython_parser::ast::Operator::Add => rustpython_parser::Tok::Plus, rustpython_parser::ast::Operator::Sub => rustpython_parser::Tok::Minus, rustpython_parser::ast::Operator::Mult => rustpython_parser::Tok::Star, rustpython_parser::ast::Operator::MatMult => rustpython_parser::Tok::At, rustpython_parser::ast::Operator::Div => rustpython_parser::Tok::Slash, rustpython_parser::ast::Operator::Mod => { rustpython_parser::Tok::Percent } rustpython_parser::ast::Operator::Pow => { rustpython_parser::Tok::DoubleStar } rustpython_parser::ast::Operator::LShift => { rustpython_parser::Tok::LeftShift } rustpython_parser::ast::Operator::RShift => { rustpython_parser::Tok::RightShift } rustpython_parser::ast::Operator::BitOr => rustpython_parser::Tok::Vbar, rustpython_parser::ast::Operator::BitXor => { rustpython_parser::Tok::CircumFlex } rustpython_parser::ast::Operator::BitAnd => { rustpython_parser::Tok::Amper } rustpython_parser::ast::Operator::FloorDiv => { rustpython_parser::Tok::DoubleSlash } }; let (op_location, op_end_location) = find_tok(left.end_location.unwrap(), right.location, locator, |tok| { tok == target_tok }); Operator::new(op_location, op_end_location, (&op).into()) }, left: Box::new((*left, locator).into()), right: Box::new((*right, locator).into()), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::ExprKind::UnaryOp { op, operand } => Expr { location: expr.location, end_location: expr.end_location, node: ExprKind::UnaryOp { op: { let target_tok = match &op { rustpython_parser::ast::Unaryop::Invert => { rustpython_parser::Tok::Tilde } rustpython_parser::ast::Unaryop::Not => rustpython_parser::Tok::Not, rustpython_parser::ast::Unaryop::UAdd => rustpython_parser::Tok::Plus, rustpython_parser::ast::Unaryop::USub => rustpython_parser::Tok::Minus, }; let (op_location, op_end_location) = find_tok(expr.location, operand.location, locator, |tok| { tok == target_tok }); UnaryOp::new(op_location, op_end_location, (&op).into()) }, operand: Box::new((*operand, locator).into()), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::ExprKind::Lambda { args, body } => Expr { location: expr.location, end_location: expr.end_location, node: ExprKind::Lambda { args: Box::new((*args, locator).into()), body: Box::new((*body, locator).into()), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::ExprKind::IfExp { test, body, orelse } => Expr { location: expr.location, end_location: expr.end_location, node: ExprKind::IfExp { test: Box::new((*test, locator).into()), body: Box::new((*body, locator).into()), orelse: Box::new((*orelse, locator).into()), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::ExprKind::Dict { keys, values } => Expr { location: expr.location, end_location: expr.end_location, node: ExprKind::Dict { keys: keys .into_iter() .map(|key| key.map(|node| (node, locator).into())) .collect(), values: values .into_iter() .map(|node| (node, locator).into()) .collect(), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::ExprKind::Set { elts } => Expr { location: expr.location, end_location: expr.end_location, node: ExprKind::Set { elts: elts .into_iter() .map(|node| (node, locator).into()) .collect(), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::ExprKind::ListComp { elt, generators } => Expr { location: expr.location, end_location: expr.end_location, node: ExprKind::ListComp { elt: Box::new((*elt, locator).into()), generators: generators .into_iter() .map(|node| (node, locator).into()) .collect(), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::ExprKind::SetComp { elt, generators } => Expr { location: expr.location, end_location: expr.end_location, node: ExprKind::SetComp { elt: Box::new((*elt, locator).into()), generators: generators .into_iter() .map(|node| (node, locator).into()) .collect(), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::ExprKind::DictComp { key, value, generators, } => Expr { location: expr.location, end_location: expr.end_location, node: ExprKind::DictComp { key: Box::new((*key, locator).into()), value: Box::new((*value, locator).into()), generators: generators .into_iter() .map(|node| (node, locator).into()) .collect(), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::ExprKind::GeneratorExp { elt, generators } => Expr { location: expr.location, end_location: expr.end_location, node: ExprKind::GeneratorExp { elt: Box::new((*elt, locator).into()), generators: generators .into_iter() .map(|node| (node, locator).into()) .collect(), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::ExprKind::Await { value } => Expr { location: expr.location, end_location: expr.end_location, node: ExprKind::Await { value: Box::new((*value, locator).into()), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::ExprKind::Yield { value } => Expr { location: expr.location, end_location: expr.end_location, node: ExprKind::Yield { value: value.map(|v| Box::new((*v, locator).into())), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::ExprKind::YieldFrom { value } => Expr { location: expr.location, end_location: expr.end_location, node: ExprKind::YieldFrom { value: Box::new((*value, locator).into()), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::ExprKind::Compare { left, ops, comparators, } => Expr { location: expr.location, end_location: expr.end_location, node: ExprKind::Compare { ops: iter::once(left.as_ref()) .chain(comparators.iter()) .tuple_windows() .zip(ops.into_iter()) .map(|((left, right), op)| { let target_tok = match &op { rustpython_parser::ast::Cmpop::Eq => { rustpython_parser::Tok::EqEqual } rustpython_parser::ast::Cmpop::NotEq => { rustpython_parser::Tok::NotEqual } rustpython_parser::ast::Cmpop::Lt => rustpython_parser::Tok::Less, rustpython_parser::ast::Cmpop::LtE => { rustpython_parser::Tok::LessEqual } rustpython_parser::ast::Cmpop::Gt => { rustpython_parser::Tok::Greater } rustpython_parser::ast::Cmpop::GtE => { rustpython_parser::Tok::GreaterEqual } rustpython_parser::ast::Cmpop::Is => rustpython_parser::Tok::Is, // TODO(charlie): Break this into two tokens. rustpython_parser::ast::Cmpop::IsNot => rustpython_parser::Tok::Is, rustpython_parser::ast::Cmpop::In => rustpython_parser::Tok::In, // TODO(charlie): Break this into two tokens. rustpython_parser::ast::Cmpop::NotIn => rustpython_parser::Tok::In, }; let (op_location, op_end_location) = find_tok( left.end_location.unwrap(), right.location, locator, |tok| tok == target_tok, ); CmpOp::new(op_location, op_end_location, (&op).into()) }) .collect(), left: Box::new((*left, locator).into()), comparators: comparators .into_iter() .map(|node| (node, locator).into()) .collect(), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::ExprKind::Call { func, args, keywords, } => Expr { location: expr.location, end_location: expr.end_location, node: ExprKind::Call { func: Box::new((*func, locator).into()), args: args .into_iter() .map(|node| (node, locator).into()) .collect(), keywords: keywords .into_iter() .map(|node| (node, locator).into()) .collect(), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::ExprKind::FormattedValue { value, conversion, format_spec, } => Expr { location: expr.location, end_location: expr.end_location, node: ExprKind::FormattedValue { value: Box::new((*value, locator).into()), conversion, format_spec: format_spec.map(|f| Box::new((*f, locator).into())), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::ExprKind::JoinedStr { values } => Expr { location: expr.location, end_location: expr.end_location, node: ExprKind::JoinedStr { values: values .into_iter() .map(|node| (node, locator).into()) .collect(), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::ExprKind::Constant { value, kind } => Expr { location: expr.location, end_location: expr.end_location, node: ExprKind::Constant { value, kind }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::ExprKind::Attribute { value, attr, ctx } => Expr { location: expr.location, end_location: expr.end_location, node: ExprKind::Attribute { value: Box::new((*value, locator).into()), attr, ctx: ctx.into(), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::ExprKind::Subscript { value, slice, ctx } => Expr { location: expr.location, end_location: expr.end_location, node: ExprKind::Subscript { value: Box::new((*value, locator).into()), slice: Box::new((*slice, locator).into()), ctx: ctx.into(), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::ExprKind::Starred { value, ctx } => Expr { location: expr.location, end_location: expr.end_location, node: ExprKind::Starred { value: Box::new((*value, locator).into()), ctx: ctx.into(), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::ExprKind::List { elts, ctx } => Expr { location: expr.location, end_location: expr.end_location, node: ExprKind::List { elts: elts .into_iter() .map(|node| (node, locator).into()) .collect(), ctx: ctx.into(), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::ExprKind::Tuple { elts, ctx } => Expr { location: expr.location, end_location: expr.end_location, node: ExprKind::Tuple { elts: elts .into_iter() .map(|node| (node, locator).into()) .collect(), ctx: ctx.into(), }, trivia: vec![], parentheses: Parenthesize::Never, }, rustpython_parser::ast::ExprKind::Slice { lower, upper, step } => { // Locate the colon tokens, which indicate the number of index segments. let tokens = rustpython_parser::lexer::lex_located( locator.slice(Range::new(expr.location, expr.end_location.unwrap())), Mode::Module, expr.location, ); // Find the first and (if it exists) second colon in the slice, avoiding any // semicolons within nested slices, and any lambda expressions. let mut first_colon = None; let mut second_colon = None; let mut lambda = 0; let mut nesting = 0; for (start, tok, ..) in tokens.flatten() { match tok { rustpython_parser::Tok::Lambda if nesting == 0 => lambda += 1, rustpython_parser::Tok::Colon if nesting == 0 => { if lambda > 0 { lambda -= 1; } else { if first_colon.is_none() { first_colon = Some(start); } else { second_colon = Some(start); break; } } } rustpython_parser::Tok::Lpar | rustpython_parser::Tok::Lsqb | rustpython_parser::Tok::Lbrace => nesting += 1, rustpython_parser::Tok::Rpar | rustpython_parser::Tok::Rsqb | rustpython_parser::Tok::Rbrace => nesting -= 1, _ => {} } } let lower = SliceIndex::new( expr.location, first_colon.unwrap(), lower.map_or(SliceIndexKind::Empty, |node| SliceIndexKind::Index { value: Box::new((*node, locator).into()), }), ); let upper = SliceIndex::new( first_colon.unwrap(), second_colon.unwrap_or(expr.end_location.unwrap()), upper.map_or(SliceIndexKind::Empty, |node| SliceIndexKind::Index { value: Box::new((*node, locator).into()), }), ); let step = second_colon.map(|second_colon| { SliceIndex::new( second_colon, expr.end_location.unwrap(), step.map_or(SliceIndexKind::Empty, |node| SliceIndexKind::Index { value: Box::new((*node, locator).into()), }), ) }); Expr { location: expr.location, end_location: expr.end_location, node: ExprKind::Slice { lower, upper, step }, trivia: vec![], parentheses: Parenthesize::Never, } } } } }