use tree_sitter::Node;
use super::{Entry, Write, WriteKind};
impl Write {
pub(crate) fn at(ident: Node<'_>, kind: WriteKind, rhs: Option<(usize, usize)>) -> Self {
Self {
byte: ident.start_byte(),
node_id: ident.id(),
kind,
rhs,
unconditional: straight_line(ident),
}
}
}
impl Entry {
pub fn write_is_read(&self, idx: usize) -> bool {
let Some(w) = self.writes.get(idx) else {
return false;
};
if self
.next_unconditional(idx)
.is_some_and(|n| n.kind == WriteKind::OpAssign)
{
return true;
}
let end = self.write_lifetime_end(idx);
self.reads
.iter()
.any(|r| !r.under_defined && r.byte > w.byte && r.byte < end)
}
pub fn unread_writes(&self) -> impl Iterator<Item = &Write> {
self.writes
.iter()
.enumerate()
.filter(|(i, _)| !self.write_is_read(*i))
.map(|(_, w)| w)
}
pub fn single_live_plain(&self) -> Option<&Write> {
let mut live = self
.writes
.iter()
.enumerate()
.filter(|(i, w)| w.kind == WriteKind::Plain && self.write_is_read(*i));
let (_, w) = live.next()?;
live.next().is_none().then_some(w)
}
pub fn has_value_read(&self) -> bool {
self.reads.iter().any(|r| !r.under_defined)
}
fn next_unconditional(&self, idx: usize) -> Option<&Write> {
self.writes.iter().skip(idx + 1).find(|w| w.unconditional)
}
fn write_lifetime_end(&self, idx: usize) -> usize {
match self.next_unconditional(idx) {
None => usize::MAX,
Some(next) if next.kind == WriteKind::Plain => {
next.rhs.map(|(_, end)| end).unwrap_or(next.byte)
}
Some(next) => next.byte,
}
}
}
fn straight_line(node: Node<'_>) -> bool {
const VETO: [&str; 14] = [
"if",
"unless",
"if_modifier",
"unless_modifier",
"conditional",
"while",
"until",
"while_modifier",
"until_modifier",
"for",
"rescue",
"rescue_modifier",
"in_clause",
"when",
];
const OWNERS: [&str; 8] = [
"method",
"singleton_method",
"class",
"module",
"singleton_class",
"block",
"do_block",
"lambda",
];
let mut cur = Some(node);
while let Some(n) = cur {
if VETO.contains(&n.kind()) {
return false;
}
if OWNERS.contains(&n.kind()) {
return true;
}
cur = n.parent();
}
true
}