# frozen_string_literal: truemoduleRuboCopmoduleCopclassVariableForce# Namespace for branch classes for each control structure.moduleBranchdefself.of(target_node,scope: nil)([target_node]+target_node.ancestors).eachdo|node|returnnilunlessnode.parentreturnnilunlessscope.include?(node)klass=CLASSES_BY_TYPE[node.parent.type]nextunlessklassbranch=klass.new(node,scope)returnbranchifbranch.branched?endnilend# Abstract base class for branch classes.# A branch represents a conditional branch in a scope.## @example# def some_scope# do_something # no branch## if foo# do_something # branch A# do_something # branch A# else# do_something # branch B# if bar# do_something # branch C (whose parent is branch B)# end# end## do_something # no branch# endBase=Struct.new(:child_node,:scope)dodefself.classes@classes||=[]enddefself.inherited(subclass)superclasses<<subclassenddefself.typename.split('::').last.gsub(/(.)([A-Z])/,'\1_\2').downcase.to_symenddefself.define_predicate(name,child_index: nil)define_method(name)dotarget_node=control_node.children[child_index]# We don't use Kernel#Array here# because it invokes Node#to_a rather than wrapping with an array.iftarget_node.is_a?(Array)target_node.any?{|node|node.equal?(child_node)}elsetarget_node.equal?(child_node)endendenddefcontrol_nodechild_node.parentenddefparentreturn@parentifinstance_variable_defined?(:@parent)@parent=Branch.of(control_node,scope: scope)enddefeach_ancestor(include_self: false,&block)returnto_enum(__method__,include_self: include_self)unlessblockyieldselfifinclude_selfscan_ancestors(&block)selfenddefbranched?!always_run?enddefalways_run?raiseNotImplementedErrorenddefmay_jump_to_other_branch?falseenddefmay_run_incompletely?falseenddefexclusive_with?(other)returnfalseunlessotherreturnfalseifmay_jump_to_other_branch?other.each_ancestor(include_self: true)do|other_ancestor|ifcontrol_node.equal?(other_ancestor.control_node)return!child_node.equal?(other_ancestor.child_node)endendifparentparent.exclusive_with?(other)elsefalseendenddef==(other)returnfalseunlessothercontrol_node.equal?(other.control_node)&&child_node.equal?(other.child_node)endalias_method:eql?,:==defhash[control_node.object_id,control_node.object_id].hashendprivatedefscan_ancestorsbranch=selfwhile(branch=branch.parent)yieldbranchendendend# Mix-in module for simple conditional control structures.moduleSimpleConditionaldefconditional_clause?raiseNotImplementedErrorenddefalways_run?conditional_clause?endend# if conditional_clause# truthy_body# else# falsey_body# end## unless conditional_clause# falsey_body# else# truthy_body# endclassIf<BaseincludeSimpleConditionaldefine_predicate:conditional_clause?,child_index: 0define_predicate:truthy_body?,child_index: 1define_predicate:falsey_body?,child_index: 2end# while conditional_clause# loop_body# endclassWhile<BaseincludeSimpleConditionaldefine_predicate:conditional_clause?,child_index: 0define_predicate:loop_body?,child_index: 1end# until conditional_clause# loop_body# endclassUntil<BaseincludeSimpleConditionaldefine_predicate:conditional_clause?,child_index: 0define_predicate:loop_body?,child_index: 1end# begin# loop_body# end while conditional_clauseclassWhilePost<BaseincludeSimpleConditionaldefine_predicate:conditional_clause?,child_index: 0define_predicate:loop_body?,child_index: 1end# begin# loop_body# end until conditional_clauseclassUntilPost<BaseincludeSimpleConditionaldefine_predicate:conditional_clause?,child_index: 0define_predicate:loop_body?,child_index: 1end# case target# when /pattern/ # when_clause# else# else_body# endclassCase<Basedefine_predicate:target?,child_index: 0define_predicate:when_clause?,child_index: 1..-2define_predicate:else_body?,child_index: -1defalways_run?target?endend# case target# in pattern # in_pattern# else# else_body# endclassCaseMatch<Basedefine_predicate:target?,child_index: 0define_predicate:in_pattern?,child_index: 1..-2define_predicate:else_body?,child_index: -1defalways_run?target?endend# for element in collection# loop_body# endclassFor<Basedefine_predicate:element?,child_index: 0define_predicate:collection?,child_index: 1define_predicate:loop_body?,child_index: 2defalways_run?element?||collection?endend# Mix-in module for logical operator control structures.moduleLogicalOperatordefalways_run?left_body?endend# left_body && right_bodyclassAnd<BaseincludeLogicalOperatordefine_predicate:left_body?,child_index: 0define_predicate:right_body?,child_index: 1end# left_body || right_bodyclassOr<BaseincludeLogicalOperatordefine_predicate:left_body?,child_index: 0define_predicate:right_body?,child_index: 1end# Mix-in module for exception handling control structures.moduleExceptionHandlerdefmay_jump_to_other_branch?main_body?enddefmay_run_incompletely?main_body?endend# begin# main_body# rescue StandardError => error # rescue_clause# else# else_body# endclassRescue<BaseincludeExceptionHandlerdefine_predicate:main_body?,child_index: 0define_predicate:rescue_clause?,child_index: 1..-2define_predicate:else_body?,child_index: -1defalways_run?falseendend# begin# main_body# ensure# ensure_body# endclassEnsure<BaseincludeExceptionHandlerdefine_predicate:main_body?,child_index: 0define_predicate:ensure_body?,child_index: -1defalways_run?ensure_body?endendCLASSES_BY_TYPE=Base.classes.each_with_object({})do|klass,classes|classes[klass.type]=klassendendendendend