# frozen_string_literal: truemoduleRuboCopmoduleCopmoduleStyle# Use `next` to skip iteration instead of a condition at the end.## @example EnforcedStyle: skip_modifier_ifs (default)# # bad# [1, 2].each do |a|# if a == 1# puts a# end# end## # good# [1, 2].each do |a|# next unless a == 1# puts a# end## # good# [1, 2].each do |o|# puts o unless o == 1# end## @example EnforcedStyle: always# # With `always` all conditions at the end of an iteration needs to be# # replaced by next - with `skip_modifier_ifs` the modifier if like# # this one are ignored: `[1, 2].each { |a| return 'yes' if a == 1 }`## # bad# [1, 2].each do |o|# puts o unless o == 1# end## # bad# [1, 2].each do |a|# if a == 1# puts a# end# end## # good# [1, 2].each do |a|# next unless a == 1# puts a# endclassNext<CopincludeConfigurableEnforcedStyleincludeMinBodyLengthincludeRangeHelpMSG='Use `next` to skip iteration.'.freezeEXIT_TYPES=%i[break return].freezedefinvestigate(_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)enddefon_block(node)returnunlessnode.send_node.send_type?&&node.send_node.enumerator_method?check(node)enddefon_while(node)check(node)endaliason_untilon_whilealiason_foron_whiledefautocorrect(node)lambdado|corrector|ifnode.modifier_form?autocorrect_modifier(corrector,node)elseautocorrect_block(corrector,node)endendendprivatedefcheck(node)returnunlessnode.body&&ends_with_condition?(node.body)offending_node=offense_node(node.body)add_offense(offending_node,location: offense_location(offending_node))enddefends_with_condition?(body)returntrueifsimple_if_without_break?(body)body.begin_type?&&simple_if_without_break?(body.children.last)enddefsimple_if_without_break?(node)returnfalseunlessif_without_else?(node)returnfalseifif_else_children?(node)returnfalseifallowed_modifier_if?(node)!exit_body_type?(node)enddefallowed_modifier_if?(node)ifnode.modifier_form?style==:skip_modifier_ifselse!min_body_length?(node)endenddefif_else_children?(node)node.each_child_node(:if).any?(&:else?)enddefif_without_else?(node)node&&node.if_type?&&!node.ternary?&&!node.else?enddefexit_body_type?(node)returnfalseunlessnode.if_branchEXIT_TYPES.include?(node.if_branch.type)enddefoffense_node(body)*_,condition=*bodycondition&&condition.if_type??condition:bodyenddefoffense_location(offense_node)condition_expression,=*offense_nodeoffense_begin_pos=offense_node.source_range.beginoffense_begin_pos.join(condition_expression.source_range)enddefautocorrect_modifier(corrector,node)cond,if_body,else_body=*nodebody=if_body||else_bodyreplacement="next #{opposite_kw(if_body)}#{cond.source}\n"\"#{' '*node.source_range.column}#{body.source}"corrector.replace(node.source_range,replacement)enddefautocorrect_block(corrector,node)cond,if_body,=*nodenext_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))lines=reindentable_lines(node)returniflines.empty?reindent(lines,cond,corrector)enddefopposite_kw(if_body)if_body.nil??'if':'unless'enddefcond_range(node,cond)end_pos=ifnode.loc.beginnode.loc.begin.end_pos# after "then"elsecond.source_range.end_posendrange_between(node.source_range.begin_pos,end_pos)enddefend_range(node)source_buffer=node.source_range.source_bufferend_pos=node.loc.end.end_posbegin_pos=node.loc.end.begin_pos-node.loc.end.columnbegin_pos-=1ifend_followed_by_whitespace_only?(source_buffer,end_pos)range_between(begin_pos,end_pos)enddefend_followed_by_whitespace_only?(source_buffer,end_pos)source_buffer.source[end_pos..-1]=~/\A\s*$/enddefreindentable_lines(node)buffer=node.source_range.source_buffer# end_range starts with the final newline of the if bodylines=(node.source_range.line+1)...node.loc.end.linelines=lines.to_a-heredoc_lines(node)# Skip blank lineslines.reject{|lineno|buffer.source_line(lineno)=~/\A\s*\z/}end# Adjust indentation of `lines` to match `node`defreindent(lines,node,corrector)range=node.source_rangebuffer=range.source_buffertarget_indent=range.source_line=~/\S/delta=actual_indent(lines,buffer)-target_indentlines.eachdo|lineno|reindent_line(corrector,lineno,delta,buffer)endenddefactual_indent(lines,buffer)lines.map{|lineno|buffer.source_line(lineno)=~/\S/}.minenddefheredoc_lines(node)node.each_node(:dstr).select(&:heredoc?).map{|n|n.loc.heredoc_body}.flat_map{|b|(b.line...b.last_line).to_a}enddefreindent_line(corrector,lineno,delta,buffer)adjustment=delta+@reindented_lines[lineno]@reindented_lines[lineno]=adjustmentifadjustment>0corrector.remove_leading(buffer.line_range(lineno),adjustment)elsifadjustment<0corrector.insert_before(buffer.line_range(lineno),' '*-adjustment)endendendendendend