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)
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)
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)
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)
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)
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)
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)
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)
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