diff --git a/Cargo.lock b/Cargo.lock index 89f6462ba6..b3d2df84d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2210,7 +2210,7 @@ dependencies = [ [[package]] name = "ruff_text_size" version = "0.0.0" -source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=4d03b9b5b212fc869e4cfda151414438186a7779#4d03b9b5b212fc869e4cfda151414438186a7779" +source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=5ef4ccd6322a8b7c5e8ead0ae3c89426d7be7d72#5ef4ccd6322a8b7c5e8ead0ae3c89426d7be7d72" dependencies = [ "schemars", "serde", @@ -2312,7 +2312,7 @@ dependencies = [ [[package]] name = "rustpython-ast" version = "0.2.0" -source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=4d03b9b5b212fc869e4cfda151414438186a7779#4d03b9b5b212fc869e4cfda151414438186a7779" +source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=5ef4ccd6322a8b7c5e8ead0ae3c89426d7be7d72#5ef4ccd6322a8b7c5e8ead0ae3c89426d7be7d72" dependencies = [ "is-macro", "num-bigint", @@ -2323,7 +2323,7 @@ dependencies = [ [[package]] name = "rustpython-format" version = "0.2.0" -source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=4d03b9b5b212fc869e4cfda151414438186a7779#4d03b9b5b212fc869e4cfda151414438186a7779" +source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=5ef4ccd6322a8b7c5e8ead0ae3c89426d7be7d72#5ef4ccd6322a8b7c5e8ead0ae3c89426d7be7d72" dependencies = [ "bitflags 2.3.3", "itertools", @@ -2335,7 +2335,7 @@ dependencies = [ [[package]] name = "rustpython-literal" version = "0.2.0" -source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=4d03b9b5b212fc869e4cfda151414438186a7779#4d03b9b5b212fc869e4cfda151414438186a7779" +source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=5ef4ccd6322a8b7c5e8ead0ae3c89426d7be7d72#5ef4ccd6322a8b7c5e8ead0ae3c89426d7be7d72" dependencies = [ "hexf-parse", "is-macro", @@ -2347,7 +2347,7 @@ dependencies = [ [[package]] name = "rustpython-parser" version = "0.2.0" -source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=4d03b9b5b212fc869e4cfda151414438186a7779#4d03b9b5b212fc869e4cfda151414438186a7779" +source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=5ef4ccd6322a8b7c5e8ead0ae3c89426d7be7d72#5ef4ccd6322a8b7c5e8ead0ae3c89426d7be7d72" dependencies = [ "anyhow", "is-macro", @@ -2370,7 +2370,7 @@ dependencies = [ [[package]] name = "rustpython-parser-core" version = "0.2.0" -source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=4d03b9b5b212fc869e4cfda151414438186a7779#4d03b9b5b212fc869e4cfda151414438186a7779" +source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=5ef4ccd6322a8b7c5e8ead0ae3c89426d7be7d72#5ef4ccd6322a8b7c5e8ead0ae3c89426d7be7d72" dependencies = [ "is-macro", "memchr", diff --git a/Cargo.toml b/Cargo.toml index a7b55367a5..6b3a8b40e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,11 +51,11 @@ wsl = { version = "0.1.0" } # v1.0.1 libcst = { git = "https://github.com/Instagram/LibCST.git", rev = "3cacca1a1029f05707e50703b49fe3dd860aa839", default-features = false } -ruff_text_size = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "4d03b9b5b212fc869e4cfda151414438186a7779" } -rustpython-ast = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "4d03b9b5b212fc869e4cfda151414438186a7779" , default-features = false, features = ["num-bigint"]} -rustpython-format = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "4d03b9b5b212fc869e4cfda151414438186a7779", default-features = false, features = ["num-bigint"] } -rustpython-literal = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "4d03b9b5b212fc869e4cfda151414438186a7779", default-features = false } -rustpython-parser = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "4d03b9b5b212fc869e4cfda151414438186a7779" , default-features = false, features = ["full-lexer", "num-bigint"] } +ruff_text_size = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "5ef4ccd6322a8b7c5e8ead0ae3c89426d7be7d72" } +rustpython-ast = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "5ef4ccd6322a8b7c5e8ead0ae3c89426d7be7d72" , default-features = false, features = ["num-bigint"]} +rustpython-format = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "5ef4ccd6322a8b7c5e8ead0ae3c89426d7be7d72", default-features = false, features = ["num-bigint"] } +rustpython-literal = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "5ef4ccd6322a8b7c5e8ead0ae3c89426d7be7d72", default-features = false } +rustpython-parser = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "5ef4ccd6322a8b7c5e8ead0ae3c89426d7be7d72" , default-features = false, features = ["full-lexer", "num-bigint"] } [profile.release] lto = "fat" diff --git a/crates/ruff/src/rules/ruff/rules/unreachable.rs b/crates/ruff/src/rules/ruff/rules/unreachable.rs index 872eb50c88..6b3e81fd5d 100644 --- a/crates/ruff/src/rules/ruff/rules/unreachable.rs +++ b/crates/ruff/src/rules/ruff/rules/unreachable.rs @@ -671,11 +671,13 @@ impl<'stmt> BasicBlocksBuilder<'stmt> { | Expr::Await(_) | Expr::Yield(_) | Expr::YieldFrom(_) => self.unconditional_next_block(after), + Expr::LineMagic(_) => todo!(), } } // The tough branches are done, here is an easy one. Stmt::Return(_) => NextBlock::Terminate, Stmt::TypeAlias(_) => todo!(), + Stmt::LineMagic(_) => todo!(), }; // Include any statements in the block that don't divert the control flow. @@ -922,6 +924,7 @@ fn needs_next_block(stmts: &[Stmt]) -> bool { | Stmt::TryStar(_) | Stmt::Assert(_) => true, Stmt::TypeAlias(_) => todo!(), + Stmt::LineMagic(_) => todo!(), } } @@ -957,6 +960,7 @@ fn is_control_flow_stmt(stmt: &Stmt) -> bool { | Stmt::Break(_) | Stmt::Continue(_) => true, Stmt::TypeAlias(_) => todo!(), + Stmt::LineMagic(_) => todo!(), } } diff --git a/crates/ruff_python_ast/src/comparable.rs b/crates/ruff_python_ast/src/comparable.rs index ceb2a1c48b..2825347f0d 100644 --- a/crates/ruff_python_ast/src/comparable.rs +++ b/crates/ruff_python_ast/src/comparable.rs @@ -652,6 +652,12 @@ pub struct ExprSlice<'a> { step: Option>>, } +#[derive(Debug, PartialEq, Eq, Hash)] +pub struct ExprLineMagic<'a> { + kind: ast::MagicKind, + value: &'a str, +} + #[derive(Debug, PartialEq, Eq, Hash)] pub enum ComparableExpr<'a> { BoolOp(ExprBoolOp<'a>), @@ -681,6 +687,7 @@ pub enum ComparableExpr<'a> { List(ExprList<'a>), Tuple(ExprTuple<'a>), Slice(ExprSlice<'a>), + LineMagic(ExprLineMagic<'a>), } impl<'a> From<&'a Box> for Box> { @@ -925,6 +932,14 @@ impl<'a> From<&'a ast::Expr> for ComparableExpr<'a> { upper: upper.as_ref().map(Into::into), step: step.as_ref().map(Into::into), }), + ast::Expr::LineMagic(ast::ExprLineMagic { + kind, + value, + range: _range, + }) => Self::LineMagic(ExprLineMagic { + kind: *kind, + value: value.as_str(), + }), } } } @@ -1155,6 +1170,12 @@ pub struct StmtExpr<'a> { value: ComparableExpr<'a>, } +#[derive(Debug, PartialEq, Eq, Hash)] +pub struct StmtLineMagic<'a> { + kind: ast::MagicKind, + value: &'a str, +} + #[derive(Debug, PartialEq, Eq, Hash)] pub enum ComparableStmt<'a> { FunctionDef(StmtFunctionDef<'a>), @@ -1181,6 +1202,7 @@ pub enum ComparableStmt<'a> { ImportFrom(StmtImportFrom<'a>), Global(StmtGlobal<'a>), Nonlocal(StmtNonlocal<'a>), + LineMagic(StmtLineMagic<'a>), Expr(StmtExpr<'a>), Pass, Break, @@ -1440,6 +1462,14 @@ impl<'a> From<&'a ast::Stmt> for ComparableStmt<'a> { }) => Self::Nonlocal(StmtNonlocal { names: names.iter().map(ast::Identifier::as_str).collect(), }), + ast::Stmt::LineMagic(ast::StmtLineMagic { + kind, + value, + range: _range, + }) => Self::LineMagic(StmtLineMagic { + kind: *kind, + value: value.as_str(), + }), ast::Stmt::Expr(ast::StmtExpr { value, range: _range, diff --git a/crates/ruff_python_ast/src/helpers.rs b/crates/ruff_python_ast/src/helpers.rs index 202989d4c2..b51bc23fc0 100644 --- a/crates/ruff_python_ast/src/helpers.rs +++ b/crates/ruff_python_ast/src/helpers.rs @@ -281,6 +281,7 @@ where .map_or(false, |value| any_over_expr(value, func)) } Expr::Name(_) | Expr::Constant(_) => false, + Expr::LineMagic(_) => false, } } @@ -583,6 +584,7 @@ where range: _range, }) => any_over_expr(value, func), Stmt::Pass(_) | Stmt::Break(_) | Stmt::Continue(_) => false, + Stmt::LineMagic(_) => false, } } diff --git a/crates/ruff_python_ast/src/node.rs b/crates/ruff_python_ast/src/node.rs index 076c299885..ed1ab59a9e 100644 --- a/crates/ruff_python_ast/src/node.rs +++ b/crates/ruff_python_ast/src/node.rs @@ -54,6 +54,7 @@ pub enum AnyNode { StmtPass(ast::StmtPass), StmtBreak(ast::StmtBreak), StmtContinue(ast::StmtContinue), + StmtLineMagic(ast::StmtLineMagic), ExprBoolOp(ast::ExprBoolOp), ExprNamedExpr(ast::ExprNamedExpr), ExprBinOp(ast::ExprBinOp), @@ -81,6 +82,7 @@ pub enum AnyNode { ExprList(ast::ExprList), ExprTuple(ast::ExprTuple), ExprSlice(ast::ExprSlice), + ExprLineMagic(ast::ExprLineMagic), ExceptHandlerExceptHandler(ast::ExceptHandlerExceptHandler), PatternMatchValue(ast::PatternMatchValue), PatternMatchSingleton(ast::PatternMatchSingleton), @@ -137,6 +139,7 @@ impl AnyNode { AnyNode::StmtPass(node) => Some(Stmt::Pass(node)), AnyNode::StmtBreak(node) => Some(Stmt::Break(node)), AnyNode::StmtContinue(node) => Some(Stmt::Continue(node)), + AnyNode::StmtLineMagic(node) => Some(Stmt::LineMagic(node)), AnyNode::ModModule(_) | AnyNode::ModInteractive(_) @@ -169,6 +172,7 @@ impl AnyNode { | AnyNode::ExprList(_) | AnyNode::ExprTuple(_) | AnyNode::ExprSlice(_) + | AnyNode::ExprLineMagic(_) | AnyNode::ExceptHandlerExceptHandler(_) | AnyNode::PatternMatchValue(_) | AnyNode::PatternMatchSingleton(_) @@ -224,6 +228,7 @@ impl AnyNode { AnyNode::ExprList(node) => Some(Expr::List(node)), AnyNode::ExprTuple(node) => Some(Expr::Tuple(node)), AnyNode::ExprSlice(node) => Some(Expr::Slice(node)), + AnyNode::ExprLineMagic(node) => Some(Expr::LineMagic(node)), AnyNode::ModModule(_) | AnyNode::ModInteractive(_) @@ -257,6 +262,7 @@ impl AnyNode { | AnyNode::StmtPass(_) | AnyNode::StmtBreak(_) | AnyNode::StmtContinue(_) + | AnyNode::StmtLineMagic(_) | AnyNode::ExceptHandlerExceptHandler(_) | AnyNode::PatternMatchValue(_) | AnyNode::PatternMatchSingleton(_) @@ -318,6 +324,7 @@ impl AnyNode { | AnyNode::StmtPass(_) | AnyNode::StmtBreak(_) | AnyNode::StmtContinue(_) + | AnyNode::StmtLineMagic(_) | AnyNode::ExprBoolOp(_) | AnyNode::ExprNamedExpr(_) | AnyNode::ExprBinOp(_) @@ -345,6 +352,7 @@ impl AnyNode { | AnyNode::ExprList(_) | AnyNode::ExprTuple(_) | AnyNode::ExprSlice(_) + | AnyNode::ExprLineMagic(_) | AnyNode::ExceptHandlerExceptHandler(_) | AnyNode::PatternMatchValue(_) | AnyNode::PatternMatchSingleton(_) @@ -414,6 +422,7 @@ impl AnyNode { | AnyNode::StmtPass(_) | AnyNode::StmtBreak(_) | AnyNode::StmtContinue(_) + | AnyNode::StmtLineMagic(_) | AnyNode::ExprBoolOp(_) | AnyNode::ExprNamedExpr(_) | AnyNode::ExprBinOp(_) @@ -441,6 +450,7 @@ impl AnyNode { | AnyNode::ExprList(_) | AnyNode::ExprTuple(_) | AnyNode::ExprSlice(_) + | AnyNode::ExprLineMagic(_) | AnyNode::ExceptHandlerExceptHandler(_) | AnyNode::TypeIgnoreTypeIgnore(_) | AnyNode::Comprehension(_) @@ -495,6 +505,7 @@ impl AnyNode { | AnyNode::StmtPass(_) | AnyNode::StmtBreak(_) | AnyNode::StmtContinue(_) + | AnyNode::StmtLineMagic(_) | AnyNode::ExprBoolOp(_) | AnyNode::ExprNamedExpr(_) | AnyNode::ExprBinOp(_) @@ -522,6 +533,7 @@ impl AnyNode { | AnyNode::ExprList(_) | AnyNode::ExprTuple(_) | AnyNode::ExprSlice(_) + | AnyNode::ExprLineMagic(_) | AnyNode::PatternMatchValue(_) | AnyNode::PatternMatchSingleton(_) | AnyNode::PatternMatchSequence(_) @@ -583,6 +595,7 @@ impl AnyNode { | AnyNode::StmtPass(_) | AnyNode::StmtBreak(_) | AnyNode::StmtContinue(_) + | AnyNode::StmtLineMagic(_) | AnyNode::ExprBoolOp(_) | AnyNode::ExprNamedExpr(_) | AnyNode::ExprBinOp(_) @@ -610,6 +623,7 @@ impl AnyNode { | AnyNode::ExprList(_) | AnyNode::ExprTuple(_) | AnyNode::ExprSlice(_) + | AnyNode::ExprLineMagic(_) | AnyNode::PatternMatchValue(_) | AnyNode::PatternMatchSingleton(_) | AnyNode::PatternMatchSequence(_) @@ -693,6 +707,7 @@ impl AnyNode { Self::StmtPass(node) => AnyNodeRef::StmtPass(node), Self::StmtBreak(node) => AnyNodeRef::StmtBreak(node), Self::StmtContinue(node) => AnyNodeRef::StmtContinue(node), + Self::StmtLineMagic(node) => AnyNodeRef::StmtLineMagic(node), Self::ExprBoolOp(node) => AnyNodeRef::ExprBoolOp(node), Self::ExprNamedExpr(node) => AnyNodeRef::ExprNamedExpr(node), Self::ExprBinOp(node) => AnyNodeRef::ExprBinOp(node), @@ -720,6 +735,7 @@ impl AnyNode { Self::ExprList(node) => AnyNodeRef::ExprList(node), Self::ExprTuple(node) => AnyNodeRef::ExprTuple(node), Self::ExprSlice(node) => AnyNodeRef::ExprSlice(node), + Self::ExprLineMagic(node) => AnyNodeRef::ExprLineMagic(node), Self::ExceptHandlerExceptHandler(node) => AnyNodeRef::ExceptHandlerExceptHandler(node), Self::PatternMatchValue(node) => AnyNodeRef::PatternMatchValue(node), Self::PatternMatchSingleton(node) => AnyNodeRef::PatternMatchSingleton(node), @@ -1676,6 +1692,34 @@ impl AstNode for ast::StmtContinue { AnyNode::from(self) } } +impl AstNode for ast::StmtLineMagic { + fn cast(kind: AnyNode) -> Option + where + Self: Sized, + { + if let AnyNode::StmtLineMagic(node) = kind { + Some(node) + } else { + None + } + } + + fn cast_ref(kind: AnyNodeRef) -> Option<&Self> { + if let AnyNodeRef::StmtLineMagic(node) = kind { + Some(node) + } else { + None + } + } + + fn as_any_node_ref(&self) -> AnyNodeRef { + AnyNodeRef::from(self) + } + + fn into_any_node(self) -> AnyNode { + AnyNode::from(self) + } +} impl AstNode for ast::ExprBoolOp { fn cast(kind: AnyNode) -> Option where @@ -2432,6 +2476,34 @@ impl AstNode for ast::ExprSlice { AnyNode::from(self) } } +impl AstNode for ast::ExprLineMagic { + fn cast(kind: AnyNode) -> Option + where + Self: Sized, + { + if let AnyNode::ExprLineMagic(node) = kind { + Some(node) + } else { + None + } + } + + fn cast_ref(kind: AnyNodeRef) -> Option<&Self> { + if let AnyNodeRef::ExprLineMagic(node) = kind { + Some(node) + } else { + None + } + } + + fn as_any_node_ref(&self) -> AnyNodeRef { + AnyNodeRef::from(self) + } + + fn into_any_node(self) -> AnyNode { + AnyNode::from(self) + } +} impl AstNode for ast::ExceptHandlerExceptHandler { fn cast(kind: AnyNode) -> Option where @@ -3081,6 +3153,7 @@ impl From for AnyNode { Stmt::Pass(node) => AnyNode::StmtPass(node), Stmt::Break(node) => AnyNode::StmtBreak(node), Stmt::Continue(node) => AnyNode::StmtContinue(node), + Stmt::LineMagic(node) => AnyNode::StmtLineMagic(node), } } } @@ -3115,6 +3188,7 @@ impl From for AnyNode { Expr::List(node) => AnyNode::ExprList(node), Expr::Tuple(node) => AnyNode::ExprTuple(node), Expr::Slice(node) => AnyNode::ExprSlice(node), + Expr::LineMagic(node) => AnyNode::ExprLineMagic(node), } } } @@ -3359,6 +3433,12 @@ impl From for AnyNode { } } +impl From for AnyNode { + fn from(node: ast::StmtLineMagic) -> Self { + AnyNode::StmtLineMagic(node) + } +} + impl From for AnyNode { fn from(node: ast::ExprBoolOp) -> Self { AnyNode::ExprBoolOp(node) @@ -3521,6 +3601,12 @@ impl From for AnyNode { } } +impl From for AnyNode { + fn from(node: ast::ExprLineMagic) -> Self { + AnyNode::ExprLineMagic(node) + } +} + impl From for AnyNode { fn from(node: ast::ExceptHandlerExceptHandler) -> Self { AnyNode::ExceptHandlerExceptHandler(node) @@ -3679,6 +3765,7 @@ impl Ranged for AnyNode { AnyNode::StmtPass(node) => node.range(), AnyNode::StmtBreak(node) => node.range(), AnyNode::StmtContinue(node) => node.range(), + AnyNode::StmtLineMagic(node) => node.range(), AnyNode::ExprBoolOp(node) => node.range(), AnyNode::ExprNamedExpr(node) => node.range(), AnyNode::ExprBinOp(node) => node.range(), @@ -3706,6 +3793,7 @@ impl Ranged for AnyNode { AnyNode::ExprList(node) => node.range(), AnyNode::ExprTuple(node) => node.range(), AnyNode::ExprSlice(node) => node.range(), + AnyNode::ExprLineMagic(node) => node.range(), AnyNode::ExceptHandlerExceptHandler(node) => node.range(), AnyNode::PatternMatchValue(node) => node.range(), AnyNode::PatternMatchSingleton(node) => node.range(), @@ -3767,6 +3855,7 @@ pub enum AnyNodeRef<'a> { StmtPass(&'a ast::StmtPass), StmtBreak(&'a ast::StmtBreak), StmtContinue(&'a ast::StmtContinue), + StmtLineMagic(&'a ast::StmtLineMagic), ExprBoolOp(&'a ast::ExprBoolOp), ExprNamedExpr(&'a ast::ExprNamedExpr), ExprBinOp(&'a ast::ExprBinOp), @@ -3794,6 +3883,7 @@ pub enum AnyNodeRef<'a> { ExprList(&'a ast::ExprList), ExprTuple(&'a ast::ExprTuple), ExprSlice(&'a ast::ExprSlice), + ExprLineMagic(&'a ast::ExprLineMagic), ExceptHandlerExceptHandler(&'a ast::ExceptHandlerExceptHandler), PatternMatchValue(&'a ast::PatternMatchValue), PatternMatchSingleton(&'a ast::PatternMatchSingleton), @@ -3854,6 +3944,7 @@ impl AnyNodeRef<'_> { AnyNodeRef::StmtPass(node) => NonNull::from(*node).cast(), AnyNodeRef::StmtBreak(node) => NonNull::from(*node).cast(), AnyNodeRef::StmtContinue(node) => NonNull::from(*node).cast(), + AnyNodeRef::StmtLineMagic(node) => NonNull::from(*node).cast(), AnyNodeRef::ExprBoolOp(node) => NonNull::from(*node).cast(), AnyNodeRef::ExprNamedExpr(node) => NonNull::from(*node).cast(), AnyNodeRef::ExprBinOp(node) => NonNull::from(*node).cast(), @@ -3881,6 +3972,7 @@ impl AnyNodeRef<'_> { AnyNodeRef::ExprList(node) => NonNull::from(*node).cast(), AnyNodeRef::ExprTuple(node) => NonNull::from(*node).cast(), AnyNodeRef::ExprSlice(node) => NonNull::from(*node).cast(), + AnyNodeRef::ExprLineMagic(node) => NonNull::from(*node).cast(), AnyNodeRef::ExceptHandlerExceptHandler(node) => NonNull::from(*node).cast(), AnyNodeRef::PatternMatchValue(node) => NonNull::from(*node).cast(), AnyNodeRef::PatternMatchSingleton(node) => NonNull::from(*node).cast(), @@ -3947,6 +4039,7 @@ impl AnyNodeRef<'_> { AnyNodeRef::StmtPass(_) => NodeKind::StmtPass, AnyNodeRef::StmtBreak(_) => NodeKind::StmtBreak, AnyNodeRef::StmtContinue(_) => NodeKind::StmtContinue, + AnyNodeRef::StmtLineMagic(_) => NodeKind::StmtLineMagic, AnyNodeRef::ExprBoolOp(_) => NodeKind::ExprBoolOp, AnyNodeRef::ExprNamedExpr(_) => NodeKind::ExprNamedExpr, AnyNodeRef::ExprBinOp(_) => NodeKind::ExprBinOp, @@ -3974,6 +4067,7 @@ impl AnyNodeRef<'_> { AnyNodeRef::ExprList(_) => NodeKind::ExprList, AnyNodeRef::ExprTuple(_) => NodeKind::ExprTuple, AnyNodeRef::ExprSlice(_) => NodeKind::ExprSlice, + AnyNodeRef::ExprLineMagic(_) => NodeKind::ExprLineMagic, AnyNodeRef::ExceptHandlerExceptHandler(_) => NodeKind::ExceptHandlerExceptHandler, AnyNodeRef::PatternMatchValue(_) => NodeKind::PatternMatchValue, AnyNodeRef::PatternMatchSingleton(_) => NodeKind::PatternMatchSingleton, @@ -4029,7 +4123,8 @@ impl AnyNodeRef<'_> { | AnyNodeRef::StmtExpr(_) | AnyNodeRef::StmtPass(_) | AnyNodeRef::StmtBreak(_) - | AnyNodeRef::StmtContinue(_) => true, + | AnyNodeRef::StmtContinue(_) + | AnyNodeRef::StmtLineMagic(_) => true, AnyNodeRef::ModModule(_) | AnyNodeRef::ModInteractive(_) @@ -4062,6 +4157,7 @@ impl AnyNodeRef<'_> { | AnyNodeRef::ExprList(_) | AnyNodeRef::ExprTuple(_) | AnyNodeRef::ExprSlice(_) + | AnyNodeRef::ExprLineMagic(_) | AnyNodeRef::ExceptHandlerExceptHandler(_) | AnyNodeRef::PatternMatchValue(_) | AnyNodeRef::PatternMatchSingleton(_) @@ -4116,7 +4212,8 @@ impl AnyNodeRef<'_> { | AnyNodeRef::ExprName(_) | AnyNodeRef::ExprList(_) | AnyNodeRef::ExprTuple(_) - | AnyNodeRef::ExprSlice(_) => true, + | AnyNodeRef::ExprSlice(_) + | AnyNodeRef::ExprLineMagic(_) => true, AnyNodeRef::ModModule(_) | AnyNodeRef::ModInteractive(_) @@ -4150,6 +4247,7 @@ impl AnyNodeRef<'_> { | AnyNodeRef::StmtPass(_) | AnyNodeRef::StmtBreak(_) | AnyNodeRef::StmtContinue(_) + | AnyNodeRef::StmtLineMagic(_) | AnyNodeRef::ExceptHandlerExceptHandler(_) | AnyNodeRef::PatternMatchValue(_) | AnyNodeRef::PatternMatchSingleton(_) @@ -4211,6 +4309,7 @@ impl AnyNodeRef<'_> { | AnyNodeRef::StmtPass(_) | AnyNodeRef::StmtBreak(_) | AnyNodeRef::StmtContinue(_) + | AnyNodeRef::StmtLineMagic(_) | AnyNodeRef::ExprBoolOp(_) | AnyNodeRef::ExprNamedExpr(_) | AnyNodeRef::ExprBinOp(_) @@ -4238,6 +4337,7 @@ impl AnyNodeRef<'_> { | AnyNodeRef::ExprList(_) | AnyNodeRef::ExprTuple(_) | AnyNodeRef::ExprSlice(_) + | AnyNodeRef::ExprLineMagic(_) | AnyNodeRef::ExceptHandlerExceptHandler(_) | AnyNodeRef::PatternMatchValue(_) | AnyNodeRef::PatternMatchSingleton(_) @@ -4307,6 +4407,7 @@ impl AnyNodeRef<'_> { | AnyNodeRef::StmtPass(_) | AnyNodeRef::StmtBreak(_) | AnyNodeRef::StmtContinue(_) + | AnyNodeRef::StmtLineMagic(_) | AnyNodeRef::ExprBoolOp(_) | AnyNodeRef::ExprNamedExpr(_) | AnyNodeRef::ExprBinOp(_) @@ -4334,6 +4435,7 @@ impl AnyNodeRef<'_> { | AnyNodeRef::ExprList(_) | AnyNodeRef::ExprTuple(_) | AnyNodeRef::ExprSlice(_) + | AnyNodeRef::ExprLineMagic(_) | AnyNodeRef::ExceptHandlerExceptHandler(_) | AnyNodeRef::TypeIgnoreTypeIgnore(_) | AnyNodeRef::Comprehension(_) @@ -4388,6 +4490,7 @@ impl AnyNodeRef<'_> { | AnyNodeRef::StmtPass(_) | AnyNodeRef::StmtBreak(_) | AnyNodeRef::StmtContinue(_) + | AnyNodeRef::StmtLineMagic(_) | AnyNodeRef::ExprBoolOp(_) | AnyNodeRef::ExprNamedExpr(_) | AnyNodeRef::ExprBinOp(_) @@ -4415,6 +4518,7 @@ impl AnyNodeRef<'_> { | AnyNodeRef::ExprList(_) | AnyNodeRef::ExprTuple(_) | AnyNodeRef::ExprSlice(_) + | AnyNodeRef::ExprLineMagic(_) | AnyNodeRef::PatternMatchValue(_) | AnyNodeRef::PatternMatchSingleton(_) | AnyNodeRef::PatternMatchSequence(_) @@ -4476,6 +4580,7 @@ impl AnyNodeRef<'_> { | AnyNodeRef::StmtPass(_) | AnyNodeRef::StmtBreak(_) | AnyNodeRef::StmtContinue(_) + | AnyNodeRef::StmtLineMagic(_) | AnyNodeRef::ExprBoolOp(_) | AnyNodeRef::ExprNamedExpr(_) | AnyNodeRef::ExprBinOp(_) @@ -4503,6 +4608,7 @@ impl AnyNodeRef<'_> { | AnyNodeRef::ExprList(_) | AnyNodeRef::ExprTuple(_) | AnyNodeRef::ExprSlice(_) + | AnyNodeRef::ExprLineMagic(_) | AnyNodeRef::PatternMatchValue(_) | AnyNodeRef::PatternMatchSingleton(_) | AnyNodeRef::PatternMatchSequence(_) @@ -4755,6 +4861,12 @@ impl<'a> From<&'a ast::StmtContinue> for AnyNodeRef<'a> { } } +impl<'a> From<&'a ast::StmtLineMagic> for AnyNodeRef<'a> { + fn from(node: &'a ast::StmtLineMagic) -> Self { + AnyNodeRef::StmtLineMagic(node) + } +} + impl<'a> From<&'a ast::ExprBoolOp> for AnyNodeRef<'a> { fn from(node: &'a ast::ExprBoolOp) -> Self { AnyNodeRef::ExprBoolOp(node) @@ -4917,6 +5029,12 @@ impl<'a> From<&'a ast::ExprSlice> for AnyNodeRef<'a> { } } +impl<'a> From<&'a ast::ExprLineMagic> for AnyNodeRef<'a> { + fn from(node: &'a ast::ExprLineMagic) -> Self { + AnyNodeRef::ExprLineMagic(node) + } +} + impl<'a> From<&'a ast::ExceptHandlerExceptHandler> for AnyNodeRef<'a> { fn from(node: &'a ast::ExceptHandlerExceptHandler) -> Self { AnyNodeRef::ExceptHandlerExceptHandler(node) @@ -5032,6 +5150,7 @@ impl<'a> From<&'a Stmt> for AnyNodeRef<'a> { Stmt::Pass(node) => AnyNodeRef::StmtPass(node), Stmt::Break(node) => AnyNodeRef::StmtBreak(node), Stmt::Continue(node) => AnyNodeRef::StmtContinue(node), + Stmt::LineMagic(node) => AnyNodeRef::StmtLineMagic(node), } } } @@ -5066,6 +5185,7 @@ impl<'a> From<&'a Expr> for AnyNodeRef<'a> { Expr::List(node) => AnyNodeRef::ExprList(node), Expr::Tuple(node) => AnyNodeRef::ExprTuple(node), Expr::Slice(node) => AnyNodeRef::ExprSlice(node), + Expr::LineMagic(node) => AnyNodeRef::ExprLineMagic(node), } } } @@ -5200,6 +5320,7 @@ impl Ranged for AnyNodeRef<'_> { AnyNodeRef::StmtPass(node) => node.range(), AnyNodeRef::StmtBreak(node) => node.range(), AnyNodeRef::StmtContinue(node) => node.range(), + AnyNodeRef::StmtLineMagic(node) => node.range(), AnyNodeRef::ExprBoolOp(node) => node.range(), AnyNodeRef::ExprNamedExpr(node) => node.range(), AnyNodeRef::ExprBinOp(node) => node.range(), @@ -5227,6 +5348,7 @@ impl Ranged for AnyNodeRef<'_> { AnyNodeRef::ExprList(node) => node.range(), AnyNodeRef::ExprTuple(node) => node.range(), AnyNodeRef::ExprSlice(node) => node.range(), + AnyNodeRef::ExprLineMagic(node) => node.range(), AnyNodeRef::ExceptHandlerExceptHandler(node) => node.range(), AnyNodeRef::PatternMatchValue(node) => node.range(), AnyNodeRef::PatternMatchSingleton(node) => node.range(), @@ -5284,6 +5406,7 @@ pub enum NodeKind { StmtImportFrom, StmtGlobal, StmtNonlocal, + StmtLineMagic, StmtExpr, StmtPass, StmtBreak, @@ -5315,6 +5438,7 @@ pub enum NodeKind { ExprList, ExprTuple, ExprSlice, + ExprLineMagic, ExceptHandlerExceptHandler, PatternMatchValue, PatternMatchSingleton, diff --git a/crates/ruff_python_ast/src/relocate.rs b/crates/ruff_python_ast/src/relocate.rs index b05b170ec4..4d16c4d8ba 100644 --- a/crates/ruff_python_ast/src/relocate.rs +++ b/crates/ruff_python_ast/src/relocate.rs @@ -200,5 +200,8 @@ pub fn relocate_expr(expr: &mut Expr, location: TextRange) { relocate_expr(expr, location); } } + Expr::LineMagic(ast::ExprLineMagic { range, .. }) => { + *range = location; + } } } diff --git a/crates/ruff_python_ast/src/source_code/generator.rs b/crates/ruff_python_ast/src/source_code/generator.rs index 778ee8762b..f386e2174e 100644 --- a/crates/ruff_python_ast/src/source_code/generator.rs +++ b/crates/ruff_python_ast/src/source_code/generator.rs @@ -720,6 +720,11 @@ impl<'a> Generator<'a> { self.p("continue"); }); } + Stmt::LineMagic(ast::StmtLineMagic { kind, value, .. }) => { + statement!({ + self.p(&format!("{kind}{value}")); + }); + } } } @@ -1270,6 +1275,9 @@ impl<'a> Generator<'a> { self.unparse_expr(step, precedence::SLICE); } } + Expr::LineMagic(ast::ExprLineMagic { kind, value, .. }) => { + self.p(&format!("{kind}{value}")); + } } } @@ -1467,8 +1475,8 @@ impl<'a> Generator<'a> { #[cfg(test)] mod tests { - use rustpython_ast::Stmt; - use rustpython_parser::Parse; + use rustpython_ast::{Mod, ModModule, Stmt}; + use rustpython_parser::{self, Mode, Parse}; use ruff_python_trivia::LineEnding; @@ -1497,6 +1505,22 @@ mod tests { generator.generate() } + fn jupyter_round_trip(contents: &str) -> String { + let indentation = Indentation::default(); + let quote = Quote::default(); + let line_ending = LineEnding::default(); + let ast = rustpython_parser::parse(contents, Mode::Jupyter, "").unwrap(); + let Mod::Module(ModModule { body, .. }) = ast else { + panic!("Source code didn't return ModModule") + }; + let [stmt] = body.as_slice() else { + panic!("Expected only one statement in source code") + }; + let mut generator = Generator::new(&indentation, quote, line_ending); + generator.unparse_stmt(stmt); + generator.generate() + } + macro_rules! assert_round_trip { ($contents:expr) => { assert_eq!( @@ -1506,6 +1530,19 @@ mod tests { }; } + #[test] + fn unparse_magic_commands() { + assert_eq!( + jupyter_round_trip("%matplotlib inline"), + "%matplotlib inline" + ); + assert_eq!( + jupyter_round_trip("%matplotlib \\\n inline"), + "%matplotlib inline" + ); + assert_eq!(jupyter_round_trip("dir = !pwd"), "dir = !pwd"); + } + #[test] fn unparse() { assert_round_trip!("{i for i in b async for i in a if await i for b in i}"); diff --git a/crates/ruff_python_ast/src/token_kind.rs b/crates/ruff_python_ast/src/token_kind.rs index 4c90f3c30f..b031915062 100644 --- a/crates/ruff_python_ast/src/token_kind.rs +++ b/crates/ruff_python_ast/src/token_kind.rs @@ -12,6 +12,8 @@ pub enum TokenKind { Complex, /// Token value for a string. String, + /// Token value for a Jupyter magic command. + MagicCommand, /// Token value for a comment. These are filtered out of the token stream prior to parsing. Comment, /// Token value for a newline. @@ -339,6 +341,7 @@ impl TokenKind { Tok::Float { .. } => TokenKind::Float, Tok::Complex { .. } => TokenKind::Complex, Tok::String { .. } => TokenKind::String, + Tok::MagicCommand { .. } => TokenKind::MagicCommand, Tok::Comment(_) => TokenKind::Comment, Tok::Newline => TokenKind::Newline, Tok::NonLogicalNewline => TokenKind::NonLogicalNewline, @@ -433,7 +436,6 @@ impl TokenKind { Tok::StartModule => TokenKind::StartModule, Tok::StartInteractive => TokenKind::StartInteractive, Tok::StartExpression => TokenKind::StartExpression, - Tok::MagicCommand { .. } => todo!(), } } } diff --git a/crates/ruff_python_ast/src/visitor.rs b/crates/ruff_python_ast/src/visitor.rs index 8fb17abbd1..ee05bab054 100644 --- a/crates/ruff_python_ast/src/visitor.rs +++ b/crates/ruff_python_ast/src/visitor.rs @@ -361,7 +361,7 @@ pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) { value, range: _range, }) => visitor.visit_expr(value), - Stmt::Pass(_) | Stmt::Break(_) | Stmt::Continue(_) => {} + Stmt::Pass(_) | Stmt::Break(_) | Stmt::Continue(_) | Stmt::LineMagic(_) => {} } } @@ -613,6 +613,7 @@ pub fn walk_expr<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, expr: &'a Expr) { visitor.visit_expr(expr); } } + Expr::LineMagic(_) => {} } } diff --git a/crates/ruff_python_ast/src/visitor/preorder.rs b/crates/ruff_python_ast/src/visitor/preorder.rs index b3570b205c..c4d277dbf2 100644 --- a/crates/ruff_python_ast/src/visitor/preorder.rs +++ b/crates/ruff_python_ast/src/visitor/preorder.rs @@ -428,7 +428,8 @@ where | Stmt::Break(_) | Stmt::Continue(_) | Stmt::Global(_) - | Stmt::Nonlocal(_) => {} + | Stmt::Nonlocal(_) + | Stmt::LineMagic(_) => {} } } @@ -723,6 +724,7 @@ where visitor.visit_expr(expr); } } + Expr::LineMagic(_) => (), } } diff --git a/crates/ruff_python_formatter/src/expression/mod.rs b/crates/ruff_python_formatter/src/expression/mod.rs index fae56271ac..b146a35f6b 100644 --- a/crates/ruff_python_formatter/src/expression/mod.rs +++ b/crates/ruff_python_formatter/src/expression/mod.rs @@ -92,6 +92,7 @@ impl FormatRule> for FormatExpr { Expr::List(expr) => expr.format().fmt(f), Expr::Tuple(expr) => expr.format().fmt(f), Expr::Slice(expr) => expr.format().fmt(f), + Expr::LineMagic(_) => todo!(), }); let parenthesize = match parentheses { @@ -233,6 +234,7 @@ impl NeedsParentheses for Expr { Expr::List(expr) => expr.needs_parentheses(parent, context), Expr::Tuple(expr) => expr.needs_parentheses(parent, context), Expr::Slice(expr) => expr.needs_parentheses(parent, context), + Expr::LineMagic(_) => todo!(), } } } @@ -405,6 +407,7 @@ impl<'input> CanOmitOptionalParenthesesVisitor<'input> { | Expr::Starred(_) | Expr::Name(_) | Expr::Slice(_) => {} + Expr::LineMagic(_) => todo!(), }; walk_expr(self, expr); diff --git a/crates/ruff_python_formatter/src/statement/mod.rs b/crates/ruff_python_formatter/src/statement/mod.rs index 850693e32d..8996aab81a 100644 --- a/crates/ruff_python_formatter/src/statement/mod.rs +++ b/crates/ruff_python_formatter/src/statement/mod.rs @@ -66,6 +66,7 @@ impl FormatRule> for FormatStmt { Stmt::Break(x) => x.format().fmt(f), Stmt::Continue(x) => x.format().fmt(f), Stmt::TypeAlias(x) => x.format().fmt(f), + Stmt::LineMagic(_) => todo!(), } } } diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index fc76344118..1936cac5dc 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -24,7 +24,7 @@ ruff_python_ast = { path = "../crates/ruff_python_ast" } ruff_python_formatter = { path = "../crates/ruff_python_formatter" } similar = { version = "2.2.1" } -rustpython-parser = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "4d03b9b5b212fc869e4cfda151414438186a7779" , default-features = false, features = ["full-lexer", "num-bigint"] } +rustpython-parser = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "5ef4ccd6322a8b7c5e8ead0ae3c89426d7be7d72" , default-features = false, features = ["full-lexer", "num-bigint"] } # Prevent this from interfering with workspaces [workspace]