Files
ruff/crates/ruff_python_ast/src/stmt_if.rs
Charlie Marsh 1050142a58 Expand expressions to include parentheses in E712 (#6575)
## Summary

This PR exposes our `is_expression_parenthesized` logic such that we can
use it to expand expressions when autofixing to include their
parenthesized ranges.

This solution has a few drawbacks: (1) we need to compute parenthesized
ranges in more places, which also relies on backwards lexing; and (2) we
need to make use of this in any relevant fixes.

However, I still think it's worth pursuing. On (1), the implementation
is very contained, so IMO we can easily swap this out for a more
performant solution in the future if needed. On (2), this improves
correctness and fixes some bad syntax errors detected by fuzzing, which
means it has value even if it's not as robust as an _actual_
`ParenthesizedExpression` node in the AST itself.

Closes https://github.com/astral-sh/ruff/issues/4925.

## Test Plan

`cargo test` with new cases that previously failed the fuzzer.
2023-08-17 15:51:09 +00:00

49 lines
1.4 KiB
Rust

use crate::{ElifElseClause, Expr, Ranged, Stmt, StmtIf};
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
use ruff_text_size::TextRange;
use std::iter;
/// Return the `Range` of the first `Elif` or `Else` token in an `If` statement.
pub fn elif_else_range(clause: &ElifElseClause, contents: &str) -> Option<TextRange> {
let token = SimpleTokenizer::new(contents, clause.range)
.skip_trivia()
.next()?;
matches!(token.kind, SimpleTokenKind::Elif | SimpleTokenKind::Else).then_some(token.range())
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum BranchKind {
If,
Elif,
}
pub struct IfElifBranch<'a> {
pub kind: BranchKind,
pub test: &'a Expr,
pub body: &'a [Stmt],
pub range: TextRange,
}
impl Ranged for IfElifBranch<'_> {
fn range(&self) -> TextRange {
self.range
}
}
pub fn if_elif_branches(stmt_if: &StmtIf) -> impl Iterator<Item = IfElifBranch> {
iter::once(IfElifBranch {
kind: BranchKind::If,
test: stmt_if.test.as_ref(),
body: stmt_if.body.as_slice(),
range: TextRange::new(stmt_if.start(), stmt_if.body.last().unwrap().end()),
})
.chain(stmt_if.elif_else_clauses.iter().filter_map(|clause| {
Some(IfElifBranch {
kind: BranchKind::Elif,
test: clause.test.as_ref()?,
body: clause.body.as_slice(),
range: clause.range,
})
}))
}