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