selene_lib/lints/
shadowing.rs

1use super::*;
2
3use full_moon::ast::Ast;
4use regex::Regex;
5use serde::Deserialize;
6
7#[derive(Clone, Deserialize)]
8#[serde(default)]
9pub struct ShadowingConfig {
10    ignore_pattern: String,
11}
12
13impl Default for ShadowingConfig {
14    fn default() -> Self {
15        Self {
16            ignore_pattern: "^_".to_owned(),
17        }
18    }
19}
20
21pub struct ShadowingLint {
22    ignore_pattern: Regex,
23}
24
25impl Lint for ShadowingLint {
26    type Config = ShadowingConfig;
27    type Error = regex::Error;
28
29    const SEVERITY: Severity = Severity::Warning;
30    const LINT_TYPE: LintType = LintType::Style;
31
32    fn new(config: Self::Config) -> Result<Self, Self::Error> {
33        Ok(ShadowingLint {
34            ignore_pattern: Regex::new(&config.ignore_pattern)?,
35        })
36    }
37
38    fn pass(&self, _: &Ast, _: &Context, ast_context: &AstContext) -> Vec<Diagnostic> {
39        let mut shadows = Vec::new();
40
41        for (_, variable) in &ast_context.scope_manager.variables {
42            if let Some(shadow_id) = variable.shadowed {
43                let shadow = &ast_context.scope_manager.variables[shadow_id];
44                let definition = shadow.identifiers[0];
45
46                let name = variable.name.to_owned();
47
48                if self.ignore_pattern.is_match(&name) || name == "..." {
49                    continue;
50                }
51
52                shadows.push(Shadow {
53                    first_defined: (definition.0 as u32, definition.1 as u32),
54                    name,
55                    range: variable.identifiers[0],
56                });
57            }
58        }
59
60        shadows
61            .iter()
62            .map(|shadow| {
63                Diagnostic::new_complete(
64                    "shadowing",
65                    format!("shadowing variable `{}`", shadow.name),
66                    Label::new(shadow.range),
67                    Vec::new(),
68                    vec![Label::new_with_message(
69                        shadow.first_defined,
70                        "previously defined here".to_owned(),
71                    )],
72                )
73            })
74            .collect()
75    }
76}
77
78struct Shadow {
79    first_defined: (u32, u32),
80    name: String,
81    range: (usize, usize),
82}
83
84#[cfg(test)]
85mod tests {
86    use super::{super::test_util::test_lint, *};
87
88    #[test]
89    fn test_shadowing() {
90        test_lint(
91            ShadowingLint::new(ShadowingConfig::default()).unwrap(),
92            "shadowing",
93            "shadowing",
94        );
95    }
96
97    #[test]
98    fn test_empty_else() {
99        test_lint(
100            ShadowingLint::new(ShadowingConfig::default()).unwrap(),
101            "shadowing",
102            "empty_else",
103        );
104    }
105}