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
use crate::puppet_parser::range::Range;
use serde::{Deserialize, Serialize};

use crate::puppet_pp_lint::lint::{EarlyLintPass, LintError, LintPass};

#[derive(Clone, Serialize, Deserialize)]
pub struct LowerCaseVariable;

impl LintPass for LowerCaseVariable {
    fn name(&self) -> &str {
        "LowerCaseVariable"
    }
    fn description(&self) -> &str {
        "Warns if variable name is not lowercase"
    }
}

impl EarlyLintPass for LowerCaseVariable {
    fn check_term(
        &self,
        _ctx: &crate::puppet_pp_lint::ctx::Ctx,
        _is_assignment: bool,
        elt: &crate::puppet_lang::expression::Term<Range>,
    ) -> Vec<super::lint::LintError> {
        if let crate::puppet_lang::expression::TermVariant::Variable(var) = &elt.value {
            if var
                .identifier
                .name
                .iter()
                .any(|elt| elt.chars().any(|c| c.is_uppercase()))
            {
                return vec![LintError::new_with_url(
                    Box::new(self.clone()),
                    "Variable name with upper case letters.",
                    "https://puppet.com/docs/puppet/7/style_guide.html#style_guide_variables-variable-format",
                    &elt.extra,
                )];
            }
        }
        vec![]
    }
}

#[derive(Clone, Serialize, Deserialize)]
pub struct ReferenceToUndefinedValue;

impl LintPass for ReferenceToUndefinedValue {
    fn name(&self) -> &str {
        "ReferenceToUndefinedValue"
    }
    fn description(&self) -> &str {
        "Warns if variable is not defined in current context"
    }
}

impl EarlyLintPass for ReferenceToUndefinedValue {
    fn check_term(
        &self,
        ctx: &crate::puppet_pp_lint::ctx::Ctx,
        is_assignment: bool,
        elt: &crate::puppet_lang::expression::Term<Range>,
    ) -> Vec<super::lint::LintError> {
        let variable = match &elt.value {
            crate::puppet_lang::expression::TermVariant::Variable(v) => v,
            _ => return Vec::new(),
        };

        if !is_assignment && variable.identifier.name.len() == 1 {
            let varname = variable.identifier.name.first().unwrap();
            let variables = ctx.variables.borrow();

            match variables.get(varname) {
                None => {
                    return vec![LintError::new(
                        Box::new(self.clone()),
                        &format!(
                            "Reference to undefined value {:?}",
                            variable.identifier.name.join("::")
                        ),
                        &elt.extra,
                    )];
                }
                Some(var) => var.incr_use_count(),
            }
        }
        Vec::new()
    }
}