use crate::linter::{Diagnostic, Fix, LintResult, Severity, Span};
const INEFFICIENT_PATTERNS: &[(&str, &str)] = &[
("$(shell cat ", "$(file < "), ("$(shell ls ", "$(wildcard "), ("$(shell echo ", "$(info "), ("$(shell pwd)", "$(CURDIR)"), ];
pub fn check(source: &str) -> LintResult {
let mut result = LintResult::new();
for (line_num, line) in source.lines().enumerate() {
for (pattern, replacement) in INEFFICIENT_PATTERNS {
if line.contains(pattern) {
let span = Span::new(line_num + 1, 1, line_num + 1, line.len());
let fix_replacement = create_fix(line, pattern, replacement);
let diag = Diagnostic::new(
"MAKE014",
Severity::Warning,
format!(
"Inefficient shell invocation '{}' - consider using '{}' instead",
pattern.trim(),
replacement.trim()
),
span,
)
.with_fix(Fix::new(&fix_replacement));
result.add(diag);
break; }
}
}
result
}
fn create_fix(line: &str, pattern: &str, replacement: &str) -> String {
line.replace(pattern, replacement)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_MAKE014_detects_shell_cat() {
let makefile = "VERSION = $(shell cat VERSION)";
let result = check(makefile);
assert_eq!(result.diagnostics.len(), 1);
let diag = &result.diagnostics[0];
assert_eq!(diag.code, "MAKE014");
assert_eq!(diag.severity, Severity::Warning);
assert!(diag.message.to_lowercase().contains("shell"));
}
#[test]
fn test_MAKE014_detects_shell_ls() {
let makefile = "FILES = $(shell ls *.c)";
let result = check(makefile);
assert_eq!(result.diagnostics.len(), 1);
}
#[test]
fn test_MAKE014_detects_shell_echo() {
let makefile = "MSG = $(shell echo hello)";
let result = check(makefile);
assert_eq!(result.diagnostics.len(), 1);
}
#[test]
fn test_MAKE014_detects_shell_pwd() {
let makefile = "DIR = $(shell pwd)";
let result = check(makefile);
assert_eq!(result.diagnostics.len(), 1);
}
#[test]
fn test_MAKE014_provides_fix() {
let makefile = "VERSION = $(shell cat VERSION)";
let result = check(makefile);
assert!(result.diagnostics[0].fix.is_some());
let fix = result.diagnostics[0].fix.as_ref().unwrap();
assert!(fix.replacement.contains("$(file <"));
}
#[test]
fn test_MAKE014_no_warning_for_efficient_commands() {
let makefile = "VERSION = $(file < VERSION)\nFILES = $(wildcard *.c)";
let result = check(makefile);
assert_eq!(result.diagnostics.len(), 0);
}
#[test]
fn test_MAKE014_detects_multiple_inefficiencies() {
let makefile = "VERSION = $(shell cat VERSION)\nFILES = $(shell ls *.c)";
let result = check(makefile);
assert_eq!(result.diagnostics.len(), 2);
}
#[test]
fn test_MAKE014_empty_makefile() {
let makefile = "";
let result = check(makefile);
assert_eq!(result.diagnostics.len(), 0);
}
}