forked from rubocop/rubocop
/
block_node.rb
121 lines (105 loc) · 3.49 KB
/
block_node.rb
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
# 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
# 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.
#
# @return [Array<Node>]
def arguments
if numblock_type?
[] # Numbered parameters have no block arguments.
else
node_parts[1]
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
end
end
end