module YARP::Debug

def self.yarp_locals(source)

sets of local variables that were encountered.
For the given source, parses with YARP and returns a list of all of the
def self.yarp_locals(source)
  locals = []
  stack = [YARP.parse(source).value]
  while (node = stack.pop)
    case node
    when BlockNode, DefNode, LambdaNode
      names = node.locals
      params = node.parameters
      params = params&.parameters unless node.is_a?(DefNode)
      # YARP places parameters in the same order that they appear in the
      # source. CRuby places them in the order that they need to appear
      # according to their own internal calling convention. We mimic that
      # order here so that we can compare properly.
      if params
        sorted = [
          *params.requireds.grep(RequiredParameterNode).map(&:constant_id),
          *params.optionals.map(&:constant_id),
          *((params.rest.name ? params.rest.name.to_sym : :*) if params.rest && params.rest.operator != ","),
          *params.posts.grep(RequiredParameterNode).map(&:constant_id),
          *params.keywords.reject(&:value).map { |param| param.name.chomp(":").to_sym },
          *params.keywords.select(&:value).map { |param| param.name.chomp(":").to_sym }
        ]
        # TODO: When we get a ... parameter, we should be pushing * and &
        # onto the local list. We don't do that yet, so we need to add them
        # in here.
        if params.keyword_rest.is_a?(ForwardingParameterNode)
          sorted.push(:*, :&, :"...")
        end
        # Recurse down the parameter tree to find any destructured
        # parameters and add them after the other parameters.
        param_stack = params.requireds.concat(params.posts).grep(RequiredDestructuredParameterNode).reverse
        while (param = param_stack.pop)
          case param
          when RequiredDestructuredParameterNode
            param_stack.concat(param.parameters.reverse)
          when RequiredParameterNode
            sorted << param.constant_id
          when SplatNode
            sorted << param.expression.constant_id if param.expression
          end
        end
        names = sorted.concat(names - sorted)
      end
      locals << names
    when ClassNode, ModuleNode, ProgramNode, SingletonClassNode
      locals << node.locals
    when ForNode
      locals << []
    when PostExecutionNode
      locals.push([], [])
    when InterpolatedRegularExpressionNode
      locals << [] if node.once?
    end
    stack.concat(node.child_nodes.compact)
  end
  locals
end