class RuboCop::Cop::Style::Next
Use `next` to skip iteration instead of a condition at the end.
@example
# bad [1, 2].each do |a| if a == 1 do puts a end end # good [1, 2].each do |a| next unless a == 1 puts a end
Constants
- EACH_
- ENUMERATORS
- EXIT_TYPES
- MSG
Public Instance Methods
investigate(_processed_source)
click to toggle source
# File lib/rubocop/cop/style/next.rb, line 34 def investigate(_processed_source) # When correcting nested offenses, we need to keep track of how much # we have adjusted the indentation of each line @reindented_lines = Hash.new(0) end
on_block(node)
click to toggle source
# File lib/rubocop/cop/style/next.rb, line 40 def on_block(node) block_owner, _, body = *node return unless block_owner.send_type? return unless body && ends_with_condition?(body) _, method_name = *block_owner return unless enumerator?(method_name) offense_node = offense_node(body) add_offense(offense_node, offense_location(offense_node), MSG) end
on_for(node)
click to toggle source
# File lib/rubocop/cop/style/next.rb, line 61 def on_for(node) _, _, body = *node return unless body && ends_with_condition?(body) offense_node = offense_node(body) add_offense(offense_node, offense_location(offense_node), MSG) end
on_while(node)
click to toggle source
# File lib/rubocop/cop/style/next.rb, line 52 def on_while(node) _, body = *node return unless body && ends_with_condition?(body) offense_node = offense_node(body) add_offense(offense_node, offense_location(offense_node), MSG) end
Also aliased as: on_until
Private Instance Methods
autocorrect(node)
click to toggle source
# File lib/rubocop/cop/style/next.rb, line 108 def autocorrect(node) lambda do |corrector| if modifier_if?(node) autocorrect_modifier(corrector, node) else autocorrect_block(corrector, node) end end end
autocorrect_block(corrector, node)
click to toggle source
# File lib/rubocop/cop/style/next.rb, line 128 def autocorrect_block(corrector, node) cond, if_body, = *node next_code = "next #{opposite_kw(if_body)} #{cond.source}" corrector.insert_before(node.source_range, next_code) corrector.remove(cond_range(node, cond)) corrector.remove(end_range(node)) # end_range starts with the final newline of the if body reindent_lines = (node.source_range.line + 1)...node.loc.end.line reindent_lines = reindent_lines.to_a - heredoc_lines(node) reindent(reindent_lines, cond, corrector) end
autocorrect_modifier(corrector, node)
click to toggle source
# File lib/rubocop/cop/style/next.rb, line 118 def autocorrect_modifier(corrector, node) cond, if_body, else_body = *node body = if_body || else_body replacement = "next #{opposite_kw(if_body)} #{cond.source}\n" "#{' ' * node.source_range.column}#{body.source}" corrector.replace(node.source_range, replacement) end
cond_range(node, cond)
click to toggle source
# File lib/rubocop/cop/style/next.rb, line 147 def cond_range(node, cond) end_pos = if node.loc.begin node.loc.begin.end_pos # after "then" else cond.source_range.end_pos end Parser::Source::Range.new(node.source_range.source_buffer, node.source_range.begin_pos, end_pos) end
end_followed_by_whitespace_only?(source_buffer, end_pos)
click to toggle source
# File lib/rubocop/cop/style/next.rb, line 168 def end_followed_by_whitespace_only?(source_buffer, end_pos) source_buffer.source[end_pos..-1] =~ /\A\s*$/ end
end_range(node)
click to toggle source
# File lib/rubocop/cop/style/next.rb, line 158 def end_range(node) source_buffer = node.source_range.source_buffer end_pos = node.loc.end.end_pos begin_pos = node.loc.end.begin_pos - node.source_range.column begin_pos -= 1 if end_followed_by_whitespace_only?(source_buffer, end_pos) Parser::Source::Range.new(source_buffer, begin_pos, end_pos) end
ends_with_condition?(body)
click to toggle source
# File lib/rubocop/cop/style/next.rb, line 76 def ends_with_condition?(body) return true if simple_if_without_break?(body) body.begin_type? && simple_if_without_break?(body.children.last) end
enumerator?(method_name)
click to toggle source
# File lib/rubocop/cop/style/next.rb, line 71 def enumerator?(method_name) ENUMERATORS.include?(method_name) || method_name.to_s.start_with?(EACH_) end
heredoc_lines(node)
click to toggle source
# File lib/rubocop/cop/style/next.rb, line 202 def heredoc_lines(node) node.each_node(:dstr) .select { |n| n.loc.respond_to?(:heredoc_body) } .map { |n| n.loc.heredoc_body } .flat_map { |b| (b.line...b.last_line).to_a } end
offense_location(offense_node)
click to toggle source
# File lib/rubocop/cop/style/next.rb, line 102 def offense_location(offense_node) condition_expression, = *offense_node offense_begin_pos = offense_node.source_range.begin offense_begin_pos.join(condition_expression.source_range) end
offense_node(body)
click to toggle source
# File lib/rubocop/cop/style/next.rb, line 97 def offense_node(body) *_, condition = *body (condition && condition.if_type?) ? condition : body end
opposite_kw(if_body)
click to toggle source
# File lib/rubocop/cop/style/next.rb, line 143 def opposite_kw(if_body) if_body.nil? ? 'if' : 'unless' end
reindent(lines, node, corrector)
click to toggle source
Adjust indentation of `lines` to match `node`
# File lib/rubocop/cop/style/next.rb, line 173 def reindent(lines, node, corrector) range = node.source_range buffer = range.source_buffer target_indent = range.source_line =~ /\S/ # Skip blank lines lines.reject! { |lineno| buffer.source_line(lineno) =~ /\A\s*\z/ } return if lines.empty? actual_indent = lines.map do |lineno| buffer.source_line(lineno) =~ /\S/ end.min delta = actual_indent - target_indent lines.each do |lineno| adjustment = delta adjustment += @reindented_lines[lineno] @reindented_lines[lineno] = adjustment if adjustment > 0 corrector.remove_leading(buffer.line_range(lineno), adjustment) elsif adjustment < 0 corrector.insert_before(buffer.line_range(lineno), ' ' * -adjustment) end end end
simple_if_without_break?(node)
click to toggle source
# File lib/rubocop/cop/style/next.rb, line 82 def simple_if_without_break?(node) return false unless node.if_type? return false if ternary?(node) return false if if_else?(node) return false if style == :skip_modifier_ifs && modifier_if?(node) return false if !modifier_if?(node) && !min_body_length?(node) # The `if` node must have only `if` body since we excluded `if` with # `else` above. _conditional, if_body, _else_body = *node return true unless if_body !EXIT_TYPES.include?(if_body.type) end