Replace parents statement stack with a Nodes abstraction (#4233)

This commit is contained in:
Charlie Marsh 2023-05-06 12:12:41 -04:00 committed by GitHub
parent 2c91412321
commit c1f0661225
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 362 additions and 279 deletions

View file

@ -1,113 +0,0 @@
use std::cmp::Ordering;
use rustc_hash::FxHashMap;
use rustpython_parser::ast::ExcepthandlerKind::ExceptHandler;
use rustpython_parser::ast::{Stmt, StmtKind};
use crate::types::RefEquality;
/// Return the common ancestor of `left` and `right` below `stop`, or `None`.
fn common_ancestor<'a>(
left: RefEquality<'a, Stmt>,
right: RefEquality<'a, Stmt>,
stop: Option<RefEquality<'a, Stmt>>,
depths: &'a FxHashMap<RefEquality<'a, Stmt>, usize>,
child_to_parent: &'a FxHashMap<RefEquality<'a, Stmt>, RefEquality<'a, Stmt>>,
) -> Option<RefEquality<'a, Stmt>> {
if Some(left) == stop || Some(right) == stop {
return None;
}
if left == right {
return Some(left);
}
let left_depth = depths.get(&left)?;
let right_depth = depths.get(&right)?;
match left_depth.cmp(right_depth) {
Ordering::Less => common_ancestor(
left,
*child_to_parent.get(&right)?,
stop,
depths,
child_to_parent,
),
Ordering::Equal => common_ancestor(
*child_to_parent.get(&left)?,
*child_to_parent.get(&right)?,
stop,
depths,
child_to_parent,
),
Ordering::Greater => common_ancestor(
*child_to_parent.get(&left)?,
right,
stop,
depths,
child_to_parent,
),
}
}
/// Return the alternative branches for a given node.
fn alternatives(stmt: RefEquality<Stmt>) -> Vec<Vec<RefEquality<Stmt>>> {
match &stmt.as_ref().node {
StmtKind::If { body, .. } => vec![body.iter().map(RefEquality).collect()],
StmtKind::Try {
body,
handlers,
orelse,
..
}
| StmtKind::TryStar {
body,
handlers,
orelse,
..
} => vec![body.iter().chain(orelse.iter()).map(RefEquality).collect()]
.into_iter()
.chain(handlers.iter().map(|handler| {
let ExceptHandler { body, .. } = &handler.node;
body.iter().map(RefEquality).collect()
}))
.collect(),
StmtKind::Match { cases, .. } => cases
.iter()
.map(|case| case.body.iter().map(RefEquality).collect())
.collect(),
_ => vec![],
}
}
/// Return `true` if `stmt` is a descendent of any of the nodes in `ancestors`.
fn descendant_of<'a>(
stmt: RefEquality<'a, Stmt>,
ancestors: &[RefEquality<'a, Stmt>],
stop: RefEquality<'a, Stmt>,
depths: &FxHashMap<RefEquality<'a, Stmt>, usize>,
child_to_parent: &FxHashMap<RefEquality<'a, Stmt>, RefEquality<'a, Stmt>>,
) -> bool {
ancestors.iter().any(|ancestor| {
common_ancestor(stmt, *ancestor, Some(stop), depths, child_to_parent).is_some()
})
}
/// Return `true` if `left` and `right` are on different branches of an `if` or
/// `try` statement.
pub fn different_forks<'a>(
left: RefEquality<'a, Stmt>,
right: RefEquality<'a, Stmt>,
depths: &FxHashMap<RefEquality<'a, Stmt>, usize>,
child_to_parent: &FxHashMap<RefEquality<'a, Stmt>, RefEquality<'a, Stmt>>,
) -> bool {
if let Some(ancestor) = common_ancestor(left, right, None, depths, child_to_parent) {
for items in alternatives(ancestor) {
let l = descendant_of(left, &items, ancestor, depths, child_to_parent);
let r = descendant_of(right, &items, ancestor, depths, child_to_parent);
if l ^ r {
return true;
}
}
}
false
}

View file

@ -1,5 +1,4 @@
pub mod all;
pub mod branch_detection;
pub mod call_path;
pub mod cast;
pub mod comparable;

View file

@ -70,3 +70,15 @@ impl<'a> From<&RefEquality<'a, Expr>> for &'a Expr {
r.0
}
}
impl<'a> From<RefEquality<'a, Stmt>> for &'a Stmt {
fn from(r: RefEquality<'a, Stmt>) -> Self {
r.0
}
}
impl<'a> From<RefEquality<'a, Expr>> for &'a Expr {
fn from(r: RefEquality<'a, Expr>) -> Self {
r.0
}
}