use crate::linter::{Diagnostic, Fix, LintResult, Severity, Span};
const HARDCODED_PATHS: &[&str] = &[
"/usr/local/bin",
"/usr/local/lib",
"/usr/local/include",
"/usr/local/share",
];
pub fn check(source: &str) -> LintResult {
let mut result = LintResult::new();
for (line_num, line) in source.lines().enumerate() {
if !line.starts_with('\t') {
continue;
}
for path in HARDCODED_PATHS {
if line.contains(path) {
let span = Span::new(line_num + 1, 1, line_num + 1, line.len() + 1);
let fix_replacement = create_fix(line, path);
let diag = Diagnostic::new(
"MAKE009",
Severity::Warning,
format!(
"Hardcoded path '{}' reduces portability - consider using $(PREFIX)",
path
),
span,
)
.with_fix(Fix::new(&fix_replacement));
result.add(diag);
}
}
}
result
}
fn create_fix(line: &str, hardcoded_path: &str) -> String {
line.replace("/usr/local", "$(PREFIX)")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_MAKE009_detects_hardcoded_usr_local_bin() {
let makefile = "install:\n\tcp app /usr/local/bin/app";
let result = check(makefile);
assert_eq!(result.diagnostics.len(), 1);
let diag = &result.diagnostics[0];
assert_eq!(diag.code, "MAKE009");
assert_eq!(diag.severity, Severity::Warning);
assert!(diag.message.to_lowercase().contains("hardcoded"));
}
#[test]
fn test_MAKE009_no_warning_with_prefix_variable() {
let makefile = "PREFIX ?= /usr/local\n\ninstall:\n\tcp app $(PREFIX)/bin/app";
let result = check(makefile);
assert_eq!(result.diagnostics.len(), 0);
}
#[test]
fn test_MAKE009_provides_fix() {
let makefile = "install:\n\tcp app /usr/local/bin/app";
let result = check(makefile);
assert!(result.diagnostics[0].fix.is_some());
let fix = result.diagnostics[0].fix.as_ref().unwrap();
assert!(fix.replacement.contains("$(PREFIX)"));
}
#[test]
fn test_MAKE009_detects_hardcoded_lib_path() {
let makefile = "install:\n\tcp libfoo.so /usr/local/lib/libfoo.so";
let result = check(makefile);
assert_eq!(result.diagnostics.len(), 1);
}
#[test]
fn test_MAKE009_detects_multiple_hardcoded_paths() {
let makefile = "install:\n\tcp app /usr/local/bin/app\n\tcp lib.so /usr/local/lib/lib.so";
let result = check(makefile);
assert_eq!(result.diagnostics.len(), 2);
}
#[test]
fn test_MAKE009_no_warning_for_non_usr_local() {
let makefile = "install:\n\tcp app /opt/bin/app";
let result = check(makefile);
assert_eq!(result.diagnostics.len(), 0);
}
#[test]
fn test_MAKE009_detects_include_path() {
let makefile = "install:\n\tcp header.h /usr/local/include/header.h";
let result = check(makefile);
assert_eq!(result.diagnostics.len(), 1);
}
#[test]
fn test_MAKE009_empty_makefile() {
let makefile = "";
let result = check(makefile);
assert_eq!(result.diagnostics.len(), 0);
}
}