use crate::code_lint::ast::{self, AstNode, BindingKind, ParsedFile};
use std::collections::HashSet;
#[must_use]
pub fn collect_renameable_bindings(file: &ParsedFile) -> Vec<AstNode<'_>> {
ast::collect_bindings(file)
.into_iter()
.filter(|binding| {
!matches!(
binding.kind,
BindingKind::Import | BindingKind::ContractMember
)
})
.map(|binding| binding.node)
.collect()
}
pub struct SuffixedBindingMatch<'a> {
pub node: AstNode<'a>,
pub name: String,
pub actual_suffix: String,
pub base_name: String,
}
#[must_use]
pub fn find_suffixed_bindings<'a, S: std::hash::BuildHasher>(
file: &'a ParsedFile,
banned_suffixes: &HashSet<String, S>,
) -> Vec<SuffixedBindingMatch<'a>> {
if banned_suffixes.is_empty() {
return Vec::new();
}
let mut sorted_suffixes: Vec<String> = banned_suffixes
.iter()
.map(|suffix| suffix.to_lowercase())
.collect();
sorted_suffixes
.sort_by(|left, right| right.len().cmp(&left.len()).then_with(|| left.cmp(right)));
let bindings = ast::collect_bindings(file);
let mut matches = Vec::new();
for binding in bindings {
if binding.kind != BindingKind::Value {
continue;
}
let node = binding.node;
let name = node.text().into_owned();
let name_lower = name.to_lowercase();
for suffix_lower in &sorted_suffixes {
if name.len() > suffix_lower.len() && name_lower.ends_with(suffix_lower.as_str()) {
let split_idx = name.len() - suffix_lower.len();
let Some((base_name, actual_suffix)) = name.split_at_checked(split_idx) else {
continue;
};
matches.push(SuffixedBindingMatch {
actual_suffix: actual_suffix.to_string(),
base_name: base_name.to_string(),
name: name.clone(),
node,
});
break;
}
}
}
matches
}