module SyntaxTree::WithScope

def add_argument_definitions(list)

def add_argument_definitions(list)
  list.each do |param|
    case param
    when ArgStar
      value = param.value
      current_scope.add_local_definition(value, :argument) if value
    when MLHSParen
      add_argument_definitions(param.contents.parts)
    else
      current_scope.add_local_definition(param, :argument)
    end
  end
end

def initialize(*args, **kwargs, &block)

def initialize(*args, **kwargs, &block)
  super
  @current_scope = Scope.new(0)
  @next_scope_id = 0
end

def next_scope_id

def next_scope_id
  @next_scope_id += 1
end

def visit_binary(node)

Visit for capturing local variables defined in regex named capture groups
def visit_binary(node)
  if node.operator == :=~
    left = node.left
    if left.is_a?(RegexpLiteral) && left.parts.length == 1 &&
         left.parts.first.is_a?(TStringContent)
      content = left.parts.first
      value = content.value
      location = content.location
      start_line = location.start_line
      Regexp
        .new(value, Regexp::FIXEDENCODING)
        .names
        .each do |name|
          offset = value.index(/\(\?<#{Regexp.escape(name)}>/)
          line = start_line + value[0...offset].count("\n")
          # We need to add 3 to account for these three characters
          # prefixing a named capture (?<
          column = location.start_column + offset + 3
          if value[0...offset].include?("\n")
            column =
              value[0...offset].length - value[0...offset].rindex("\n") +
                3 - 1
          end
          ident_location =
            Location.new(
              start_line: line,
              start_char: location.start_char + offset,
              start_column: column,
              end_line: line,
              end_char: location.start_char + offset + name.length,
              end_column: column + name.length
            )
          identifier = Ident.new(value: name, location: ident_location)
          current_scope.add_local_definition(identifier, :variable)
        end
    end
  end
  super
end

def visit_block_var(node)

def visit_block_var(node)
  node.locals.each do |local|
    current_scope.add_local_definition(local, :variable)
  end
  super
end

def visit_blockarg(node)

def visit_blockarg(node)
  name = node.name
  current_scope.add_local_definition(name, :argument) if name
  super
end

def visit_class(node)

and method definitions.
Visits for nodes that create new scopes, such as classes, modules
def visit_class(node)
  with_scope { super }
end

def visit_def(node)

def visit_def(node)
  with_scope { super }
end

def visit_kwrest_param(node)

def visit_kwrest_param(node)
  name = node.name
  current_scope.add_local_definition(name, :argument) if name
  super
end

def visit_method_add_block(node)

itself happens in the same scope.
inside of the block needs a fresh scope. The method invocation
When we find a method invocation with a block, only the code that happens
def visit_method_add_block(node)
  visit(node.call)
  with_scope(current_scope) { visit(node.block) }
end

def visit_module(node)

def visit_module(node)
  with_scope { super }
end

def visit_params(node)

arguments.
Visit for keeping track of local arguments, such as method and block
def visit_params(node)
  add_argument_definitions(node.requireds)
  node.posts.each do |param|
    current_scope.add_local_definition(param, :argument)
  end
  node.keywords.each do |param|
    current_scope.add_local_definition(param.first, :argument)
  end
  node.optionals.each do |param|
    current_scope.add_local_definition(param.first, :argument)
  end
  super
end

def visit_pinned_var_ref(node)

Visit for keeping track of local variable definitions
def visit_pinned_var_ref(node)
  value = node.value
  current_scope.add_local_usage(value, :variable) if value.is_a?(Ident)
  super
end

def visit_rest_param(node)

def visit_rest_param(node)
  name = node.name
  current_scope.add_local_definition(name, :argument) if name
  super
end

def visit_var_field(node)

Visit for keeping track of local variable definitions
def visit_var_field(node)
  value = node.value
  current_scope.add_local_definition(value, :variable) if value.is_a?(Ident)
  super
end

def visit_var_ref(node)

Visits for keeping track of variable and argument usages
def visit_var_ref(node)
  value = node.value
  if value.is_a?(Ident)
    definition = current_scope.find_local(value.value)
    current_scope.add_local_usage(value, definition.type) if definition
  end
  super
end

def visit_vcall(node)

When using regex named capture groups, vcalls might actually be a variable
def visit_vcall(node)
  value = node.value
  definition = current_scope.find_local(value.value)
  current_scope.add_local_usage(value, definition.type) if definition
  super
end

def with_scope(parent_scope = nil)

def with_scope(parent_scope = nil)
  previous_scope = @current_scope
  @current_scope = Scope.new(next_scope_id, parent_scope)
  yield
ensure
  @current_scope = previous_scope
end