fix, simplify, refactor

This commit is contained in:
Astro 2021-12-11 01:52:58 +01:00
parent 1874a16ad9
commit 62e1b35cd7
6 changed files with 99 additions and 100 deletions

View File

@ -7,20 +7,21 @@ use rowan::api::SyntaxNode;
#[derive(Debug, Clone)]
pub struct Binding {
pub name: Ident,
pub node: SyntaxNode<NixLanguage>,
pub body_node: SyntaxNode<NixLanguage>,
pub decl_node: SyntaxNode<NixLanguage>,
mortal: bool,
}
impl PartialEq for Binding {
fn eq(&self, other: &Self) -> bool {
self.node == other.node && self.name.as_str() == other.name.as_str()
self.decl_node == other.decl_node && self.name.as_str() == other.name.as_str()
}
}
impl Eq for Binding {}
impl Binding {
pub fn new(name: Ident, node: SyntaxNode<NixLanguage>, mortal: bool) -> Self {
Binding { name, node, mortal }
pub fn new(name: Ident, body_node: SyntaxNode<NixLanguage>, decl_node: SyntaxNode<NixLanguage>, mortal: bool) -> Self {
Binding { name, body_node, decl_node, mortal }
}
pub fn is_mortal(&self) -> bool {

View File

@ -53,21 +53,20 @@ impl Settings {
if let Some(scope) = Scope::new(node) {
if !(self.no_lambda_arg && scope.is_lambda_arg()) {
for binding in scope.bindings() {
let name_node = binding.name.node();
if binding.is_mortal()
&& !(self.no_underscore && binding.name.as_str().starts_with('_'))
&& !(self.no_lambda_pattern_names && scope.is_lambda_pattern_name(&binding.name))
&& !scope.bodies().any(|body| {
// exclude this binding's own node
body != binding.node &&
body != binding.body_node &&
// excluding already unused results
dead.get(&body).is_none() &&
// find if used anywhere
usage::find(&binding.name, &body)
}) {
dead.insert(binding.node.clone());
dead.insert(binding.body_node.clone());
results.insert(
binding.name.node().clone(),
binding.decl_node.clone(),
DeadCode {
scope: scope.clone(),
binding,

View File

@ -4,7 +4,6 @@ use rnix::{
NixLanguage, SyntaxKind,
};
use rowan::api::SyntaxNode;
use std::collections::HashMap;
#[derive(Debug)]
struct Edit {
@ -32,15 +31,10 @@ fn apply_edits<'a>(src: &str, edits: impl Iterator<Item = &'a Edit>) -> String {
/// assumes `node` to be presorted
pub fn edit_dead_code(
original: &str,
node: &SyntaxNode<NixLanguage>,
dead: impl Iterator<Item = DeadCode>,
) -> String {
let mut dead = dead
.map(|result| (result.binding.node.clone(), result))
.collect::<HashMap<_, _>>();
let mut edits = Vec::with_capacity(dead.len());
scan(node, &mut dead, &mut edits);
let mut edits = dead.filter_map(dead_to_edit)
.collect::<Vec<_>>();
edits.sort_unstable_by(|e1, e2| {
if e1.start == e2.start {
e1.end.cmp(&e2.end)
@ -62,91 +56,78 @@ pub fn edit_dead_code(
}
}
fn scan(
node: &SyntaxNode<NixLanguage>,
dead: &mut HashMap<SyntaxNode<NixLanguage>, DeadCode>,
edits: &mut Vec<Edit>,
) {
if let Some(dead_code) = dead.remove(node) {
let range = dead_code.binding.node.text_range();
let mut start = usize::from(range.start());
let mut end = usize::from(range.end());
let mut replace_node = node.clone();
let mut replacement = None;
match dead_code.scope {
Scope::LambdaPattern(pattern, _) => {
if pattern.at().map_or(false, |at| at.node() == node) {
if let Some(pattern_bind_node) = pattern.node().children().find(|child|
child.kind() == SyntaxKind::NODE_PAT_BIND
) {
// `dead @ { ... }`, `{ ... } @ dead` forms
let pattern_bind_range = pattern_bind_node.text_range();
start = usize::from(pattern_bind_range.start());
end = usize::from(pattern_bind_range.end());
// also remove trailing whitespace for this form
if let Some(next) = pattern_bind_node.next_sibling_or_token() {
if next.kind() == SyntaxKind::TOKEN_WHITESPACE {
end = usize::from(next.text_range().end());
}
fn dead_to_edit(
dead_code: DeadCode,
) -> Option<Edit> {
let range = dead_code.binding.decl_node.text_range();
let mut start = usize::from(range.start());
let mut end = usize::from(range.end());
let mut replace_node = dead_code.binding.decl_node.clone();
let mut replacement = None;
match dead_code.scope {
Scope::LambdaPattern(pattern, _) => {
if pattern.at().map_or(false, |at| *at.node() == dead_code.binding.decl_node) {
if let Some(pattern_bind_node) = pattern.node().children().find(|child|
child.kind() == SyntaxKind::NODE_PAT_BIND
) {
// `dead @ { ... }`, `{ ... } @ dead` forms
let pattern_bind_range = pattern_bind_node.text_range();
start = usize::from(pattern_bind_range.start());
end = usize::from(pattern_bind_range.end());
// also remove trailing whitespace for this form
if let Some(next) = pattern_bind_node.next_sibling_or_token() {
if next.kind() == SyntaxKind::TOKEN_WHITESPACE {
end = usize::from(next.text_range().end());
}
replacement = Some("".to_string());
replace_node = pattern_bind_node;
}
} else if let Some(next) = node.next_sibling_or_token() {
// { dead, ... } form
if next.kind() == SyntaxKind::TOKEN_COMMA {
end = usize::from(next.text_range().end());
replacement = Some("".to_string());
}
}
}
Scope::LambdaArg(name, _) => {
replacement = Some(format!("_{}", name.as_str()));
}
Scope::LetIn(let_in) => {
if let_in.entries().any(|entry| entry.node() == node) {
replacement = Some("".to_string());
} else if let Some(inherit) =
let_in.inherits().find(|inherit| inherit.node() == node)
{
if let Some(ident) = inherit
.idents()
.find(|ident| ident.as_str() == dead_code.binding.name.as_str())
{
let range = ident.node().text_range();
start = usize::from(range.start());
end = usize::from(range.end());
replace_node = ident.node().clone();
replacement = Some("".to_string());
}
replace_node = pattern_bind_node;
}
} else if let Some(next) = dead_code.binding.decl_node.next_sibling_or_token() {
// { dead, ... } form
if next.kind() == SyntaxKind::TOKEN_COMMA {
end = usize::from(next.text_range().end());
replacement = Some("".to_string());
}
}
Scope::RecAttrSet(_) => {}
}
if let Some(replacement) = replacement {
// remove whitespace before node
if let Some(prev) = replace_node.prev_sibling_or_token() {
if prev.kind() == SyntaxKind::TOKEN_WHITESPACE {
start = usize::from(prev.text_range().start());
}
Scope::LambdaArg(name, _) => {
replacement = Some(format!("_{}", name.as_str()));
}
Scope::LetIn(let_in) => {
if let_in.entries().any(|entry| *entry.node() == dead_code.binding.decl_node) {
replacement = Some("".to_string());
} else if let Some(ident) = let_in.inherits().flat_map(|inherit| {
inherit.idents()
.filter(|ident| *ident.node() == dead_code.binding.decl_node)
}).next() {
let range = ident.node().text_range();
start = usize::from(range.start());
end = usize::from(range.end());
replace_node = ident.node().clone();
replacement = Some("".to_string());
}
edits.push(Edit {
start,
end,
replacement,
});
}
Scope::RecAttrSet(_) => {}
}
// recurse through the AST
for node in node.children() {
scan(&node, dead, edits);
}
replacement.map(|replacement| {
// remove whitespace before node
if let Some(prev) = replace_node.prev_sibling_or_token() {
if prev.kind() == SyntaxKind::TOKEN_WHITESPACE {
start = usize::from(prev.text_range().start());
}
}
Edit {
start,
end,
replacement,
}
})
}
fn remove_empty_scopes(node: &SyntaxNode<NixLanguage>, edits: &mut Vec<Edit>) {

View File

@ -12,7 +12,7 @@ fn run(content: &str) -> String {
no_underscore: false,
}
.find_dead_code(&ast.node());
crate::edit::edit_dead_code(content, &ast.node(), results.into_iter())
crate::edit::edit_dead_code(content, results.into_iter())
}
macro_rules! no_edits {
@ -67,6 +67,12 @@ fn let_inherit_in_dead_only() {
assert_eq!(results, "alive");
}
#[test]
fn let_inherit_multi_in_dead_only() {
let results = run("let inherit dead1 dead2 dead3; in alive");
assert_eq!(results, "alive");
}
#[test]
fn let_inherit_from_in_alive() {
no_edits!("let inherit (x) alive; in alive");
@ -90,6 +96,12 @@ fn let_inherit_from_in_dead_only() {
assert_eq!(results, "alive");
}
#[test]
fn let_inherit_from_multi_in_dead_only() {
let results = run("let inherit (grave) dead1 dead2 dead3; in alive");
assert_eq!(results, "alive");
}
#[test]
fn lambda_arg_alive() {
no_edits!("alive: alive");

View File

@ -118,7 +118,7 @@ fn main() {
crate::report::Report::new(file.to_string(), &content, results.clone()).print();
}
if edit {
let new_ast = crate::edit::edit_dead_code(&content, &ast.node(), results.into_iter());
let new_ast = crate::edit::edit_dead_code(&content, results.into_iter());
fs::write(file, new_ast).expect("fs::write");
}
}

View File

@ -89,33 +89,36 @@ impl Scope {
.at()
.map(|name| {
let binding_node = name.node().clone();
Binding::new(name, binding_node, true)
Binding::new(name, binding_node.clone(), binding_node, true)
})
.into_iter()
.chain(pattern.entries().map(move |entry| {
let name = entry.name().expect("entry.name");
Binding::new(name, entry.node().clone(), mortal)
Binding::new(name, entry.node().clone(), entry.node().clone(), mortal)
})),
)
}
Scope::LambdaArg(name, _) => {
let mortal = !name.as_str().starts_with('_');
Box::new(Some(Binding::new(name.clone(), name.node().clone(), mortal)).into_iter())
Box::new(Some(Binding::new(name.clone(), name.node().clone(), name.node().clone(), mortal)).into_iter())
}
Scope::LetIn(let_in) => Box::new(
let_in
.inherits()
.flat_map(|inherit| {
let binding_node = if let Some(from) = inherit.from() {
let body_node = if let Some(from) = inherit.from() {
from.node().clone()
} else {
inherit.node().clone()
};
inherit
.idents()
.map(move |name| Binding::new(name, binding_node.clone(), true))
.map(move |name| {
let name_node = name.node().clone();
Binding::new(name, body_node.clone(), name_node, true)
})
})
.chain(let_in.entries().map(|entry| {
let key = entry
@ -125,7 +128,7 @@ impl Scope {
.next()
.expect("key.path.next");
let name = Ident::cast(key).expect("Ident::cast");
Binding::new(name, entry.node().clone(), true)
Binding::new(name, entry.node().clone(), entry.node().clone(), true)
})),
),
@ -136,7 +139,10 @@ impl Scope {
let binding_node = inherit.node().clone();
inherit
.idents()
.map(move |name| Binding::new(name, binding_node.clone(), false))
.map(move |name| {
let name_node = name.node().clone();
Binding::new(name, binding_node.clone(), name_node, false)
})
})
.chain(attr_set.entries().filter_map(|entry| {
let key = entry
@ -147,7 +153,7 @@ impl Scope {
.expect("key.path.next");
if key.kind() == SyntaxKind::NODE_IDENT {
let name = Ident::cast(key).expect("Ident::cast");
Some(Binding::new(name, entry.node().clone(), false))
Some(Binding::new(name, entry.node().clone(), entry.node().clone(), false))
} else {
None
}