File: block_node.rb

package info (click to toggle)
ruby-rubocop-ast 1.24.0-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 1,256 kB
  • sloc: ruby: 15,071; yacc: 90; makefile: 9
file content (149 lines) | stat: -rw-r--r-- 4,405 bytes parent folder | download
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# frozen_string_literal: true

module RuboCop
  module AST
    # A node extension for `block` nodes. This will be used in place of a plain
    # node when the builder constructs the AST, making its methods available
    # to all `send` nodes within RuboCop.
    #
    # A `block` node is essentially a method send with a block. Parser nests
    # the `send` node inside the `block` node.
    class BlockNode < Node
      include MethodIdentifierPredicates

      VOID_CONTEXT_METHODS = %i[each tap].freeze
      private_constant :VOID_CONTEXT_METHODS

      # The `send` node associated with this block.
      #
      # @return [SendNode] the `send` node associated with the `block` node
      def send_node
        node_parts[0]
      end

      # The arguments of this block.
      # Note that if the block has destructured arguments, `arguments` will
      # return a `mlhs` node, whereas `argument_list` will return only
      # actual argument nodes.
      #
      # @return [Array<Node>]
      def arguments
        if numblock_type?
          [].freeze # Numbered parameters have no block arguments.
        else
          node_parts[1]
        end
      end

      # Returns a collection of all descendants of this node that are
      # argument type nodes. See `ArgsNode#argument_list` for details.
      #
      # @return [Array<Node>]
      def argument_list
        if numblock_type?
          numbered_arguments
        else
          arguments.argument_list
        end
      end

      # The body of this block.
      #
      # @return [Node, nil] the body of the `block` node or `nil`
      def body
        node_parts[2]
      end

      # The name of the dispatched method as a symbol.
      #
      # @return [Symbol] the name of the dispatched method
      def method_name
        send_node.method_name
      end

      # Checks whether this block takes any arguments.
      #
      # @return [Boolean] whether this `block` node takes any arguments
      def arguments?
        !arguments.empty?
      end

      # Checks whether the `block` literal is delimited by curly braces.
      #
      # @return [Boolean] whether the `block` literal is enclosed in braces
      def braces?
        loc.end&.is?('}')
      end

      # Checks whether the `block` literal is delimited by `do`-`end` keywords.
      #
      # @return [Boolean] whether the `block` literal is enclosed in `do`-`end`
      def keywords?
        loc.end&.is?('end')
      end

      # The delimiters for this `block` literal.
      #
      # @return [Array<String>] the delimiters for the `block` literal
      def delimiters
        [loc.begin.source, loc.end.source].freeze
      end

      # The opening delimiter for this `block` literal.
      #
      # @return [String] the opening delimiter for the `block` literal
      def opening_delimiter
        delimiters.first
      end

      # The closing delimiter for this `block` literal.
      #
      # @return [String] the closing delimiter for the `block` literal
      def closing_delimiter
        delimiters.last
      end

      # Checks whether this is a single line block. This is overridden here
      # because the general version in `Node` does not work for `block` nodes.
      #
      # @return [Boolean] whether the `block` literal is on a single line
      def single_line?
        loc.begin.line == loc.end.line
      end

      # Checks whether this is a multiline block. This is overridden here
      # because the general version in `Node` does not work for `block` nodes.
      #
      # @return [Boolean] whether the `block` literal is on a several lines
      def multiline?
        !single_line?
      end

      # Checks whether this `block` literal belongs to a lambda.
      #
      # @return [Boolean] whether the `block` literal belongs to a lambda
      def lambda?
        send_node.method?(:lambda)
      end

      # Checks whether this node body is a void context.
      #
      # @return [Boolean] whether the `block` node body is a void context
      def void_context?
        VOID_CONTEXT_METHODS.include?(method_name)
      end

      private

      # Numbered arguments of this `numblock`.
      def numbered_arguments
        return [].freeze unless numblock_type?

        max_param = children[1]
        1.upto(max_param).map do |i|
          ArgNode.new(:arg, [:"_#{i}"])
        end.freeze
      end
    end
  end
end