use crate::linter::{Diagnostic, LintResult, Severity, Span};
const HTML_ENTITIES: [&str; 4] = ["&", "<", ">", """];
pub fn check(source: &str) -> LintResult {
let mut result = LintResult::new();
for (line_num, line) in source.lines().enumerate() {
let line_num = line_num + 1;
if line.trim_start().starts_with('#') {
continue;
}
if HTML_ENTITIES.iter().any(|entity| line.contains(entity)) {
let diagnostic = Diagnostic::new(
"SC1109",
Severity::Error,
"This is an unquoted HTML entity. Replace with the corresponding character",
Span::new(line_num, 1, line_num, line.len() + 1),
);
result.add(diagnostic);
}
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sc1109_detects_amp() {
let script = "test -f file && echo ok";
let result = check(script);
assert_eq!(result.diagnostics.len(), 1);
assert_eq!(result.diagnostics[0].code, "SC1109");
assert_eq!(result.diagnostics[0].severity, Severity::Error);
assert!(result.diagnostics[0].message.contains("HTML entity"));
}
#[test]
fn test_sc1109_detects_lt() {
let script = "if [ \"$a\" < \"$b\" ]; then echo less; fi";
let result = check(script);
assert_eq!(result.diagnostics.len(), 1);
}
#[test]
fn test_sc1109_detects_gt() {
let script = "if [ \"$a\" > \"$b\" ]; then echo more; fi";
let result = check(script);
assert_eq!(result.diagnostics.len(), 1);
}
#[test]
fn test_sc1109_detects_quot() {
let script = "echo "hello"";
let result = check(script);
assert_eq!(result.diagnostics.len(), 1);
}
#[test]
fn test_sc1109_no_html_entities() {
let script =
"#!/bin/sh\ntest -f file && echo ok\nif [ \"$a\" -lt \"$b\" ]; then echo less; fi";
let result = check(script);
assert_eq!(result.diagnostics.len(), 0);
}
#[test]
fn test_sc1109_skips_comments() {
let script = "# This & that in a comment";
let result = check(script);
assert_eq!(result.diagnostics.len(), 0);
}
#[test]
fn test_sc1109_ampersand_alone_ok() {
let script = "cmd &\ncmd1 && cmd2";
let result = check(script);
assert_eq!(result.diagnostics.len(), 0);
}
#[test]
fn test_sc1109_empty_source() {
let result = check("");
assert_eq!(result.diagnostics.len(), 0);
}
#[test]
fn test_sc1109_multiple_entities_one_line() {
let script = "echo "hello" && echo <ok>";
let result = check(script);
assert_eq!(result.diagnostics.len(), 1);
}
}