use gobject_ast::{Expression, Statement};
use crate::{
ast_context::AstContext,
config::Config,
rules::{Fix, Rule, Violation},
};
pub struct UnnecessaryNullCheck;
impl Rule for UnnecessaryNullCheck {
fn name(&self) -> &'static str {
"unnecessary_null_check"
}
fn description(&self) -> &'static str {
"Detect unnecessary NULL checks before g_free/g_clear_* functions"
}
fn category(&self) -> crate::rules::Category {
crate::rules::Category::Suspicious
}
fn fixable(&self) -> bool {
true
}
fn check_func_impl(
&self,
_ast_context: &AstContext,
_config: &Config,
func: &gobject_ast::top_level::FunctionDefItem,
file: &gobject_ast::FileModel,
violations: &mut Vec<Violation>,
) {
for stmt in &func.body_statements {
for if_stmt in stmt.iter_if_statements() {
self.check_if_statement(if_stmt, file, &file.source, violations);
}
}
}
}
impl UnnecessaryNullCheck {
fn check_if_statement(
&self,
if_stmt: &gobject_ast::IfStatement,
file: &gobject_ast::FileModel,
source: &[u8],
violations: &mut Vec<Violation>,
) {
if if_stmt.has_else() {
return;
}
let Some(checked_var) = if_stmt.extract_null_check_variable(source) else {
return;
};
if !if_stmt.has_single_statement() {
return;
}
let Statement::Expression(expr_stmt) = &if_stmt.then_body[0] else {
return;
};
let Expression::Call(call) = expr_stmt.as_ref() else {
return;
};
let Some(func_name) = call.function_name_str() else {
return;
};
if !func_name.starts_with("g_free") && !func_name.starts_with("g_clear_") {
return;
}
let references_var = call
.arguments
.iter()
.any(|gobject_ast::Argument::Expression(e)| e.contains_identifier(checked_var));
if !references_var {
return;
}
let loc = expr_stmt.location();
let stmt_end = loc.find_semicolon_end(source);
let stmt_text = std::str::from_utf8(&source[loc.start_byte..stmt_end]).unwrap_or_default();
let fix = Fix::new(
if_stmt.location.start_byte,
if_stmt.location.end_byte,
stmt_text.to_string(),
);
violations.push(self.violation_with_fix(
&file.path,
if_stmt.location.line,
if_stmt.location.column,
format!(
"Remove unnecessary NULL check before {} ({} handles NULL)",
func_name, func_name
),
fix,
));
}
}