# frozen_string_literal: truemoduleRuboCopmoduleCopmoduleStyle# This cop checks for extra underscores in variable assignment.## @example# # bad# a, b, _ = foo()# a, b, _, = foo()# a, _, _ = foo()# a, _, _, = foo()## # good# a, b, = foo()# a, = foo()# *a, b, _ = foo()# # => We need to know to not include 2 variables in a# a, *b, _ = foo()# # => The correction `a, *b, = foo()` is a syntax error## # good if AllowNamedUnderscoreVariables is true# a, b, _something = foo()classTrailingUnderscoreVariable<CopincludeSurroundingSpaceincludeRangeHelpMSG='Do not use trailing `_`s in parallel assignment. '\'Prefer `%<code>s`.'UNDERSCORE='_'defon_masgn(node)ranges=unneeded_ranges(node)ranges.eachdo|range|good_code=node.sourceoffset=range.begin_pos-node.source_range.begin_posgood_code[offset,range.size]=''add_offense(node,location: range,message: format(MSG,code: good_code))endenddefautocorrect(node)ranges=unneeded_ranges(node)lambdado|corrector|ranges.each{|range|corrector.remove(range)ifrange}endendprivatedeffind_first_offense(variables)first_offense=find_first_possible_offense(variables.reverse)returnunlessfirst_offensereturnifsplat_variable_before?(first_offense,variables)first_offenseenddeffind_first_possible_offense(variables)variables.reduce(nil)do|offense,variable|breakoffenseunless%i[lvasgn splat].include?(variable.type)var,=*variablevar,=*varifallow_named_underscore_variablesbreakoffenseunlessvar==:_elsebreakoffenseunlessvar.to_s.start_with?(UNDERSCORE)endvariableendenddefsplat_variable_before?(first_offense,variables)# Account for cases like `_, *rest, _`, where we would otherwise get# the index of the first underscore.first_offense_index=reverse_index(variables,first_offense)variables[0...first_offense_index].any?(&:splat_type?)enddefreverse_index(collection,item)collection.size-1-collection.reverse.index(item)enddefallow_named_underscore_variables@allow_named_underscore_variables||=cop_config['AllowNamedUnderscoreVariables']enddefunneeded_ranges(node)node.masgn_type??(mlhs_node,=*node):mlhs_node=nodevariables=*mlhs_nodemain_offense=main_node_offense(node)ifmain_offense.nil?children_offenses(variables)elsechildren_offenses(variables)<<main_offenseendenddefmain_node_offense(node)node.masgn_type??(mlhs_node,right=*node):mlhs_node=nodevariables=*mlhs_nodefirst_offense=find_first_offense(variables)returnunlessfirst_offenseifunused_variables_only?(first_offense,variables)returnunused_range(node.type,mlhs_node,right)endifUtil.parentheses?(mlhs_node)returnrange_for_parentheses(first_offense,mlhs_node)endrange_between(first_offense.source_range.begin_pos,node.loc.operator.begin_pos)enddefchildren_offenses(variables)variables.select(&:mlhs_type?).flat_map{|v|unneeded_ranges(v)}enddefunused_variables_only?(offense,variables)offense.source_range==variables.first.source_rangeenddefunused_range(node_type,mlhs_node,right)start_range=mlhs_node.source_range.begin_posend_range=casenode_typewhen:masgnright.source_range.begin_poswhen:mlhsmlhs_node.source_range.end_posendrange_between(start_range,end_range)enddefrange_for_parentheses(offense,left)range_between(offense.source_range.begin_pos-1,left.loc.expression.end_pos-1)endendendendend