Welcome to mirror list, hosted at ThFree Co, Russian Federation.

inject_enterprise_edition_module.rb « cop « rubocop - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 7e3c44cc5fd893e45818852fa15e2da2cd403584 (plain)
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
# frozen_string_literal: true

module RuboCop
  module Cop
    # Cop that blacklists the injecting of extension specific modules before any lines which are not already injecting another module.
    # It allows multiple module injections as long as they're all at the end.
    class InjectEnterpriseEditionModule < RuboCop::Cop::Base
      extend RuboCop::Cop::AutoCorrector

      INVALID_LINE = 'Injecting extension modules must be done on the last line of this file' \
          ', outside of any class or module definitions'

      DISALLOWED_METHOD =
        'EE modules must be injected using `include_mod_with`, `extend_mod_with`, or `prepend_mod_with`'

      INVALID_ARGUMENT = 'extension modules to inject must be specified as a String'

      CHECK_LINE_METHODS =
        Set.new(%i[include_mod_with extend_mod_with prepend_mod_with]).freeze

      DISALLOW_METHODS = Set.new(%i[include extend prepend]).freeze

      COMMENT_OR_EMPTY_LINE = /^\s*(#.*|$)/.freeze

      CHECK_LINE_METHODS_REGEXP = Regexp.union((CHECK_LINE_METHODS + DISALLOW_METHODS).map(&:to_s) + [COMMENT_OR_EMPTY_LINE]).freeze

      def ee_const?(node)
        line = node.location.expression.source_line

        # We use `match?` here instead of RuboCop's AST matching, as this makes
        # it far easier to handle nested constants such as `EE::Foo::Bar::Baz`.
        line.match?(/(\s|\()('|")?(::)?(QA::)?EE::/)
      end

      def on_send(node)
        return unless check_method?(node)

        if DISALLOW_METHODS.include?(node.children[1])
          add_offense(node, message: DISALLOWED_METHOD, &corrector(node))
        else
          verify_line_number(node)
          verify_argument_type(node)
        end
      end

      def verify_line_number(node)
        line = node.location.line
        buffer = node.location.expression.source_buffer
        last_line = buffer.last_line
        lines = buffer.source.split("\n")
        # We allow multiple includes, extends and prepends as long as they're all at the end.
        allowed_line = (line...last_line).all? { |i| CHECK_LINE_METHODS_REGEXP.match?(lines[i - 1]) }

        if allowed_line
          ignore_node(node)
        elsif line < last_line
          add_offense(node, message: INVALID_LINE, &corrector(node))
        end
      end

      def verify_argument_type(node)
        argument = node.children[2]

        return if argument.str_type?

        add_offense(argument, message: INVALID_ARGUMENT, &corrector(argument))
      end

      def check_method?(node)
        name = node.children[1]

        if DISALLOW_METHODS.include?(name)
          ee_const?(node.children[2])
        else
          CHECK_LINE_METHODS.include?(name)
        end
      end

      private

      # Automatically correcting these offenses is not always possible, as
      # sometimes code needs to be refactored to make this work. As such, we
      # only allow developers to easily blacklist existing offenses.
      def corrector(node)
        lambda do |corrector|
          corrector.insert_after(
            node.source_range,
            " # rubocop: disable #{cop_name}"
          )
        end
      end
    end
  end
end