use mir_codebase::storage::Visibility;
use mir_codebase::Codebase;
use mir_issues::{Issue, IssueKind, Location, Severity};
const MAGIC_METHODS: &[&str] = &[
"__construct",
"__destruct",
"__call",
"__callStatic",
"__get",
"__set",
"__isset",
"__unset",
"__sleep",
"__wakeup",
"__serialize",
"__unserialize",
"__toString",
"__invoke",
"__set_state",
"__clone",
"__debugInfo",
];
pub struct DeadCodeAnalyzer<'a> {
codebase: &'a Codebase,
}
impl<'a> DeadCodeAnalyzer<'a> {
pub fn new(codebase: &'a Codebase) -> Self {
Self { codebase }
}
pub fn analyze(&self) -> Vec<Issue> {
let mut issues = Vec::new();
for entry in self.codebase.classes.iter() {
let cls = entry.value();
let fqcn = cls.fqcn.as_ref();
for (method_name, method) in &cls.own_methods {
if method.visibility != Visibility::Private {
continue;
}
let name = method_name.as_ref();
if MAGIC_METHODS.contains(&name) {
continue;
}
if !self.codebase.is_method_referenced(fqcn, name) {
let (file, line) = location_from_storage(&method.location);
issues.push(Issue::new(
IssueKind::UnusedMethod {
class: fqcn.to_string(),
method: name.to_string(),
},
Location {
file,
line,
col_start: 0,
col_end: 0,
},
));
}
}
for (prop_name, prop) in &cls.own_properties {
if prop.visibility != Visibility::Private {
continue;
}
let name = prop_name.as_ref();
if !self.codebase.is_property_referenced(fqcn, name) {
let (file, line) = location_from_storage(&prop.location);
issues.push(Issue::new(
IssueKind::UnusedProperty {
class: fqcn.to_string(),
property: name.to_string(),
},
Location {
file,
line,
col_start: 0,
col_end: 0,
},
));
}
}
}
for issue in &mut issues {
issue.severity = Severity::Info;
}
issues
}
}
fn location_from_storage(
loc: &Option<mir_codebase::storage::Location>,
) -> (std::sync::Arc<str>, u32) {
match loc {
Some(l) => (l.file.clone(), 1), None => (std::sync::Arc::from("<unknown>"), 1),
}
}