Class: PuppetLint::Plugins::CheckClasses

Inherits:
CheckPlugin show all
Defined in:
lib/puppet-lint/plugins/check_classes.rb

Instance Attribute Summary

Attributes inherited from CheckPlugin

#errors, #warnings

Instance Method Summary collapse

Methods inherited from CheckPlugin

#error, #initialize, #run, #warn

Methods included from PuppetLint::Plugin

included

Constructor Details

This class inherits a constructor from PuppetLint::CheckPlugin

Instance Method Details

#test(data) ⇒ Object



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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/puppet-lint/plugins/check_classes.rb', line 2

def test(data)
  lexer = Puppet::Parser::Lexer.new
  lexer.string = data
  tokens = lexer.fullscan

  tokens.select { |r| r.first == :OUT_EDGE }.each do |token|
    warn "right-to-left (<-) relationship on line #{token.last[:line]}"
  end

  class_indexes = []
  defined_type_indexes = []
  tokens.each_index do |token_idx|
    if [:DEFINE, :CLASS].include? tokens[token_idx].first
      header_end_idx = tokens[token_idx..-1].index { |r| r.first == :LBRACE }
      lparen_idx = tokens[token_idx..(header_end_idx + token_idx)].index { |r| r.first == :LPAREN }
      rparen_idx = tokens[token_idx..(header_end_idx + token_idx)].rindex { |r| r.first == :RPAREN }

      unless lparen_idx.nil? or rparen_idx.nil?
        param_tokens = tokens[lparen_idx..rparen_idx]
        param_tokens.each_index do |param_tokens_idx|
          this_token = param_tokens[param_tokens_idx]
          next_token = param_tokens[param_tokens_idx+1]
          if this_token.first == :VARIABLE
            unless next_token.nil?
              if next_token.first == :COMMA or next_token.first == :RPAREN
                unless param_tokens[0..param_tokens_idx].rindex { |r| r.first == :EQUALS }.nil?
                  warn "optional parameter listed before required parameter on line #{this_token.last[:line]}"
                end
              end
            end
          end
        end
      end
    end

    if [:CLASS, :DEFINE].include? tokens[token_idx].first
      if tokens[token_idx].first == :CLASS
        if tokens[token_idx+2].first == :INHERITS
          class_name = tokens[token_idx+1].last[:value]
          inherited_class = tokens[token_idx+3].last[:value]

          unless class_name =~ /^#{inherited_class}::/
            warn "class inherits across namespaces on line #{tokens[token_idx].last[:line]}"
          end
        end
      end

      lbrace_count = 0
      tokens[token_idx+1..-1].each_index do |class_token_idx|
        idx = class_token_idx + token_idx
        if tokens[idx].first == :LBRACE
          lbrace_count += 1
        elsif tokens[idx].first == :RBRACE
          lbrace_count -= 1
          if lbrace_count == 0
            class_indexes << {:start => token_idx, :end => idx} if tokens[token_idx].first == :CLASS
            defined_type_indexes << {:start => token_idx, :end => idx} if tokens[token_idx].first == :DEFINE
            break
          end
        end
      end
    end
  end

  class_indexes.each do |class_idx|
    class_tokens = tokens[class_idx[:start]..class_idx[:end]]
    class_tokens[1..-1].select { |r| r.first == :CLASS }.each do |token|
      warn "class defined inside a class on line #{token.last[:line]}"
    end
    class_tokens[1..-1].select { |r| r.first == :DEFINE }.each do |token|
      warn "define defined inside a class on line #{token.last[:line]}"
    end
  end

  (class_indexes + defined_type_indexes).each do |idx|
    object_tokens = tokens[idx[:start]..idx[:end]]
    variables_in_scope = ['name']
    referenced_variables = []
    header_end_idx = object_tokens.index { |r| r.first == :LBRACE }
    lparen_idx = object_tokens[0..header_end_idx].index { |r| r.first == :LPAREN }
    rparen_idx = object_tokens[0..header_end_idx].rindex { |r| r.first == :RPAREN }

    unless lparen_idx.nil? or rparen_idx.nil?
      param_tokens = object_tokens[lparen_idx..rparen_idx]
      param_tokens.each_index do |param_tokens_idx|
        this_token = param_tokens[param_tokens_idx]
        next_token = param_tokens[param_tokens_idx+1]
        if this_token.first == :VARIABLE
          if [:COMMA, :EQUALS, :RPAREN].include? next_token.first
            variables_in_scope << this_token.last[:value]
          end
        end
      end
    end

    object_tokens.each_index do |object_token_idx|
      this_token = object_tokens[object_token_idx]
      next_token = object_tokens[object_token_idx + 1]

      if this_token.first == :VARIABLE
        if next_token.first == :EQUALS
          variables_in_scope << this_token.last[:value]
        else
          referenced_variables << this_token
        end
      end
    end

    referenced_variables.each do |token|
      unless token.last[:value].include? '::'
        unless variables_in_scope.include? token.last[:value]
          warn "top-scope variable being used without an explicit namespace on line #{token.last[:line]}"
        end
      end
    end
  end
end