use crate::linter::diagnostic::{Diagnostic, Fix, Severity, ViolationData};
use crate::linter::rules::{Example, Rule, RuleContext};
pub struct TrueFalseSymbol;
const EXAMPLES: &[Example] = &[Example {
caption: "`T` and `F` used as boolean shorthand:",
source: "x <- T\ny <- F\n",
}];
impl Rule for TrueFalseSymbol {
fn id(&self) -> &'static str {
"true-false-symbol"
}
fn description(&self) -> &'static str {
"Prefer the reserved literals `TRUE`/`FALSE` over the rebindable base \
symbols `T`/`F`.\n\n`T` and `F` are ordinary base-R bindings, not \
reserved words — `T <- FALSE` is legal — so relying on them as boolean \
shorthand is fragile. The fix is withheld when the name resolves to a \
local binding, since that is the user's own variable rather than the \
shorthand."
}
fn examples(&self) -> &'static [Example] {
EXAMPLES
}
fn check_file(&self, ctx: &RuleContext<'_>, sink: &mut Vec<Diagnostic>) {
for ident in ctx.model.idents() {
let replacement = match ident.name.as_str() {
"T" => "TRUE",
"F" => "FALSE",
_ => continue,
};
if ctx.model.resolve_local(ident).is_some() {
continue;
}
let r = ident.range;
sink.push(Diagnostic {
rule: "true-false-symbol",
severity: Severity::Warning,
path: Default::default(),
range: r,
message: ViolationData::new(
"true-false-symbol",
format!("use `{replacement}` instead of `{}`", ident.name),
)
.with_suggestion("`T`/`F` are rebindable; prefer the reserved literals."),
fix: Some(Fix::safe(
usize::from(r.start()),
usize::from(r.end()),
replacement.to_string(),
format!("Replace `{}` with `{replacement}`", ident.name),
)),
});
}
}
}