# frozen_string_literal: truemoduleRuboCopmoduleCopmoduleLayout# This cop checks whether the rescue and ensure keywords are aligned# properly.## @example## # bad# begin# something# rescue# puts 'error'# end## # good# begin# something# rescue# puts 'error'# endclassRescueEnsureAlignment<BaseincludeRangeHelpincludeEndKeywordAlignmentextendAutoCorrectorMSG='`%<kw_loc>s` at %<kw_loc_line>d, %<kw_loc_column>d is not '\'aligned with `%<beginning>s` at '\'%<begin_loc_line>d, %<begin_loc_column>d.'ANCESTOR_TYPES=%i[kwbegin def defs class module block].freezeANCESTOR_TYPES_WITH_ACCESS_MODIFIERS=%i[def defs].freezeALTERNATIVE_ACCESS_MODIFIERS=%i[public_class_method private_class_method].freezedefon_resbody(node)check(node)unlessmodifier?(node)enddefon_ensure(node)check(node)enddefon_new_investigation@modifier_locations=processed_source.tokens.each_with_object([])do|token,locations|nextunlesstoken.rescue_modifier?locations<<token.posendendprivate# Check alignment of node with rescue or ensure modifiers.defcheck(node)alignment_node=alignment_node(node)returnifalignment_node.nil?alignment_loc=alignment_location(alignment_node)kw_loc=node.loc.keywordreturnifalignment_loc.column==kw_loc.column||alignment_loc.line==kw_loc.lineadd_offense(kw_loc,message: format_message(alignment_node,alignment_loc,kw_loc))do|corrector|autocorrect(corrector,node,alignment_loc)endenddefautocorrect(corrector,node,alignment_location)whitespace=whitespace_range(node)# Some inline node is sitting before current node.returnnilunlesswhitespace.source.strip.empty?new_column=alignment_location.columncorrector.replace(whitespace,' '*new_column)enddefformat_message(alignment_node,alignment_loc,kw_loc)format(MSG,kw_loc: kw_loc.source,kw_loc_line: kw_loc.line,kw_loc_column: kw_loc.column,beginning: alignment_source(alignment_node,alignment_loc),begin_loc_line: alignment_loc.line,begin_loc_column: alignment_loc.column)enddefalignment_source(node,starting_loc)ending_loc=casenode.typewhen:block,:kwbeginnode.loc.beginwhen:def,:defs,:class,:module,:lvasgn,:ivasgn,:cvasgn,:gvasgn,:casgnnode.loc.namewhen:masgnmlhs_node,=*nodemlhs_node.loc.expressionelse# It is a wrapper with access modifier.node.child_nodes.first.loc.nameendrange_between(starting_loc.begin_pos,ending_loc.end_pos).sourceend# We will use ancestor or wrapper with access modifier.defalignment_node(node)ancestor_node=ancestor_node(node)returnancestor_nodeifancestor_node.nil?||ancestor_node.kwbegin_type?returnifancestor_node.respond_to?(:send_node)&&aligned_with_line_break_method?(ancestor_node,node)assignment_node=assignment_node(ancestor_node)returnassignment_nodeifsame_line?(ancestor_node,assignment_node)access_modifier_node=access_modifier_node(ancestor_node)returnaccess_modifier_nodeunlessaccess_modifier_node.nil?ancestor_nodeenddefancestor_node(node)node.each_ancestor(*ANCESTOR_TYPES).firstenddefaligned_with_line_break_method?(ancestor_node,node)send_node_loc=ancestor_node.send_node.locdo_keyword_line=ancestor_node.loc.begin.linerescue_keyword_column=node.loc.keyword.columnselector=send_node_loc.respond_to?(:selector)?send_node_loc.selector:send_node_locifaligned_with_leading_dot?(do_keyword_line,send_node_loc,rescue_keyword_column)returntrueenddo_keyword_line==selector.line&&rescue_keyword_column==selector.columnenddefaligned_with_leading_dot?(do_keyword_line,send_node_loc,rescue_keyword_column)returnfalseunlesssend_node_loc.respond_to?(:dot)&&(dot=send_node_loc.dot)do_keyword_line==dot.line&&rescue_keyword_column==dot.columnenddefassignment_node(node)assignment_node=node.ancestors.firstreturnnilunlessassignment_node&.assignment?assignment_nodeenddefaccess_modifier_node(node)returnnilunlessANCESTOR_TYPES_WITH_ACCESS_MODIFIERS.include?(node.type)access_modifier_node=node.ancestors.firstreturnnilunlessaccess_modifier?(access_modifier_node)access_modifier_nodeenddefmodifier?(node)returnfalseunless@modifier_locations.respond_to?(:include?)@modifier_locations.include?(node.loc.keyword)enddefwhitespace_range(node)begin_pos=node.loc.keyword.begin_poscurrent_column=node.loc.keyword.columnrange_between(begin_pos-current_column,begin_pos)enddefaccess_modifier?(node)returntrueifnode.respond_to?(:access_modifier?)&&node.access_modifier?returntrueifnode.respond_to?(:method_name)&&ALTERNATIVE_ACCESS_MODIFIERS.include?(node.method_name)falseenddefalignment_location(alignment_node)ifbegin_end_alignment_style=='start_of_line'start_line_range(alignment_node)elsealignment_node.loc.expressionendenddefbegin_end_alignment_stylebegin_end_alignment_conf=config.for_cop('Layout/BeginEndAlignment')begin_end_alignment_conf['Enabled']&&begin_end_alignment_conf['EnforcedStyleAlignWith']endendendendend