/
empty_conditional_body.rb
133 lines (119 loc) · 3.67 KB
/
empty_conditional_body.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
122
123
124
125
126
127
128
129
130
131
132
133
# frozen_string_literal: true
module RuboCop
module Cop
module Lint
# Checks for the presence of `if`, `elsif` and `unless` branches without a body.
#
# NOTE: empty `else` branches are handled by `Style/EmptyElse`.
#
# @safety
# Autocorrection for this cop is not safe. The conditions for empty branches that
# the autocorrection removes may have side effects, or the logic in subsequent
# branches may change due to the removal of a previous condition.
#
# @example
# # bad
# if condition
# end
#
# # bad
# unless condition
# end
#
# # bad
# if condition
# do_something
# elsif other_condition
# end
#
# # good
# if condition
# do_something
# end
#
# # good
# unless condition
# do_something
# end
#
# # good
# if condition
# do_something
# elsif other_condition
# do_something_else
# end
#
# @example AllowComments: true (default)
# # good
# if condition
# do_something
# elsif other_condition
# # noop
# end
#
# @example AllowComments: false
# # bad
# if condition
# do_something
# elsif other_condition
# # noop
# end
#
class EmptyConditionalBody < Base
extend AutoCorrector
include CommentsHelp
include RangeHelp
MSG = 'Avoid `%<keyword>s` branches without a body.'
def on_if(node)
return if node.body
return if cop_config['AllowComments'] && contains_comments?(node)
add_offense(node, message: format(MSG, keyword: node.keyword)) do |corrector|
autocorrect(corrector, node)
end
end
private
def autocorrect(corrector, node)
remove_comments(corrector, node)
remove_empty_branch(corrector, node)
correct_other_branches(corrector, node)
end
def remove_comments(corrector, node)
comments_in_range(node).each do |comment|
range = range_by_whole_lines(comment.loc.expression, include_final_newline: true)
corrector.remove(range)
end
end
def remove_empty_branch(corrector, node)
corrector.remove(deletion_range(branch_range(node)))
end
def correct_other_branches(corrector, node)
return unless (node.if? || node.unless?) && node.else_branch
if node.else_branch.if_type?
# Replace an orphaned `elsif` with `if`
corrector.replace(node.else_branch.loc.keyword, 'if')
else
# Flip orphaned `else`
corrector.replace(node.loc.else, "#{node.inverse_keyword} #{node.condition.source}")
end
end
def branch_range(node)
if node.loc.else
node.source_range.with(end_pos: node.loc.else.begin_pos - 1)
else
node.source_range
end
end
def deletion_range(range)
# Collect a range between the start of the `if` node and the next relevant node,
# including final new line.
# Based on `RangeHelp#range_by_whole_lines` but allows the `if` to not start
# on the first column.
buffer = @processed_source.buffer
last_line = buffer.source_line(range.last_line)
end_offset = last_line.length - range.last_column + 1
range.adjust(end_pos: end_offset).intersect(buffer.source_range)
end
end
end
end
end