use crate::context::LintContext;
use crate::diagnostic::Severity;
use crate::rule::{Rule, RuleCategory, RuleMeta};
use crate::visitor::parse_v_for_variables;
use vize_relief::ast::{DirectiveNode, ElementNode};
static META: RuleMeta = RuleMeta {
name: "vue/no-template-shadow",
description: "Disallow variable names that shadow variables in outer scope",
category: RuleCategory::StronglyRecommended,
fixable: false,
default_severity: Severity::Warning,
};
pub struct NoTemplateShadow;
impl Rule for NoTemplateShadow {
fn meta(&self) -> &'static RuleMeta {
&META
}
fn check_directive<'a>(
&self,
ctx: &mut LintContext<'a>,
_element: &ElementNode<'a>,
directive: &DirectiveNode<'a>,
) {
if directive.name.as_str() != "for" {
return;
}
let Some(ref exp) = directive.exp else {
return;
};
let vars = parse_v_for_variables(exp);
for var in &vars {
let var_name = var.as_str();
if ctx.is_parent_v_for_var(var_name) {
ctx.warn_with_help(
ctx.t_fmt("vue/no-template-shadow.message", &[("name", var_name)]),
&directive.loc,
ctx.t("vue/no-template-shadow.help"),
);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::linter::Linter;
use crate::rule::RuleRegistry;
fn create_linter() -> Linter {
let mut registry = RuleRegistry::new();
registry.register(Box::new(NoTemplateShadow));
Linter::with_registry(registry)
}
#[test]
fn test_valid_no_shadow() {
let linter = create_linter();
let result = linter.lint_template(
r#"<div v-for="item in items" :key="item.id"><span v-for="child in item.children" :key="child.id">{{ child }}</span></div>"#,
"test.vue",
);
assert_eq!(result.warning_count, 0);
}
#[test]
fn test_invalid_nested_shadow() {
let linter = create_linter();
let result = linter.lint_template(
r#"<div v-for="item in items" :key="item.id"><span v-for="item in item.children" :key="item.id">{{ item }}</span></div>"#,
"test.vue",
);
assert_eq!(result.warning_count, 1);
assert!(result.diagnostics[0].message.contains("shadows"));
}
#[test]
fn test_valid_different_names() {
let linter = create_linter();
let result = linter.lint_template(
r#"<div v-for="(item, index) in items" :key="index"><span v-for="(child, childIndex) in item.children" :key="childIndex">{{ child }}</span></div>"#,
"test.vue",
);
assert_eq!(result.warning_count, 0);
}
}