Skip to content

Commit

Permalink
Merge pull request #1189 from ksss/ast
Browse files Browse the repository at this point in the history
Add signature for RubyVM::AbstractSyntaxTree
  • Loading branch information
soutaro committed Feb 14, 2023
2 parents 5806c02 + e69c68a commit cd46c1e
Show file tree
Hide file tree
Showing 12 changed files with 626 additions and 380 deletions.
139 changes: 139 additions & 0 deletions core/ruby_vm.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,142 @@ RubyVM::OPTS: Array[String]
#
class RubyVM::InstructionSequence < Object
end

# <!-- rdoc-file=ast.rb -->
# AbstractSyntaxTree provides methods to parse Ruby code into abstract syntax
# trees. The nodes in the tree are instances of
# RubyVM::AbstractSyntaxTree::Node.
#
# This module is MRI specific as it exposes implementation details of the MRI
# abstract syntax tree.
#
# This module is experimental and its API is not stable, therefore it might
# change without notice. As examples, the order of children nodes is not
# guaranteed, the number of children nodes might change, there is no way to
# access children nodes by name, etc.
#
# If you are looking for a stable API or an API working under multiple Ruby
# implementations, consider using the *parser* gem or Ripper. If you would like
# to make RubyVM::AbstractSyntaxTree stable, please join the discussion at
# https://bugs.ruby-lang.org/issues/14844.
#
module RubyVM::AbstractSyntaxTree
# <!--
# rdoc-file=ast.rb
# - RubyVM::AbstractSyntaxTree.parse(string) -> RubyVM::AbstractSyntaxTree::Node
# -->
# Parses the given *string* into an abstract syntax tree, returning the root
# node of that tree.
#
# SyntaxError is raised if the given *string* is invalid syntax.
#
# RubyVM::AbstractSyntaxTree.parse("x = 1 + 2")
# # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-1:9>
#
def self.parse: (String string, ?keep_script_lines: bool, ?error_tolerant: bool, ?keep_tokens: bool) -> Node

# <!--
# rdoc-file=ast.rb
# - RubyVM::AbstractSyntaxTree.parse_file(pathname) -> RubyVM::AbstractSyntaxTree::Node
# -->
# Reads the file from *pathname*, then parses it like ::parse, returning the
# root node of the abstract syntax tree.
#
# SyntaxError is raised if *pathname*'s contents are not valid Ruby syntax.
#
# RubyVM::AbstractSyntaxTree.parse_file("my-app/app.rb")
# # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-31:3>
#
def self.parse_file: (String | ::_ToPath string, ?keep_script_lines: bool, ?error_tolerant: bool, ?keep_tokens: bool) -> Node

# <!--
# rdoc-file=ast.rb
# - RubyVM::AbstractSyntaxTree.of(proc) -> RubyVM::AbstractSyntaxTree::Node
# - RubyVM::AbstractSyntaxTree.of(method) -> RubyVM::AbstractSyntaxTree::Node
# -->
# Returns AST nodes of the given *proc* or *method*.
#
# RubyVM::AbstractSyntaxTree.of(proc {1 + 2})
# # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:35-1:42>
#
# def hello
# puts "hello, world"
# end
#
# RubyVM::AbstractSyntaxTree.of(method(:hello))
# # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-3:3>
#
def self.of: (Proc | Method | UnboundMethod body, ?keep_script_lines: bool, ?error_tolerant: bool, ?keep_tokens: bool) -> Node?

def self.node_id_for_backtrace_location: (Thread::Backtrace::Location backtrace_location) -> Integer

# <!-- rdoc-file=ast.rb -->
# RubyVM::AbstractSyntaxTree::Node instances are created by parse methods in
# RubyVM::AbstractSyntaxTree.
#
# This class is MRI specific.
#
class Node
# <!--
# rdoc-file=ast.rb
# - node.type -> symbol
# -->
# Returns the type of this node as a symbol.
#
# root = RubyVM::AbstractSyntaxTree.parse("x = 1 + 2")
# root.type # => :SCOPE
# lasgn = root.children[2]
# lasgn.type # => :LASGN
# call = lasgn.children[1]
# call.type # => :OPCALL
#
def type: () -> Symbol

# <!--
# rdoc-file=ast.rb
# - node.first_lineno -> integer
# -->
# The line number in the source code where this AST's text began.
#
def first_lineno: () -> Integer

# <!--
# rdoc-file=ast.rb
# - node.first_column -> integer
# -->
# The column number in the source code where this AST's text began.
#
def first_column: () -> Integer

# <!--
# rdoc-file=ast.rb
# - node.last_lineno -> integer
# -->
# The line number in the source code where this AST's text ended.
#
def last_lineno: () -> Integer

# <!--
# rdoc-file=ast.rb
# - node.last_column -> integer
# -->
# The column number in the source code where this AST's text ended.
#
def last_column: () -> Integer

def tokens: () -> Array[[Integer, Symbol, String, [Integer, Integer, Integer, Integer]]]?

def all_tokens: () -> Array[[Integer, Symbol, String, [Integer, Integer, Integer, Integer]]]?

# <!--
# rdoc-file=ast.rb
# - node.children -> array
# -->
# Returns AST nodes under this one. Each kind of node has different children,
# depending on what kind of node it is.
#
# The returned array may contain other nodes or `nil`.
#
def children: () -> Array[untyped]
end
end
22 changes: 14 additions & 8 deletions test/stdlib/Dir_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,20 @@ def test_square_bracket
end

def test_chdir
assert_send_type "() -> Integer",
Dir, :chdir
assert_send_type "(::String) -> Integer",
Dir, :chdir, __dir__
assert_send_type "(::ToStr) -> Integer",
Dir, :chdir, ToStr.new(__dir__)
assert_send_type "(::ToStr) { (::String) -> 30 } -> 30",
Dir, :chdir, ToStr.new(__dir__) do 30 end
dir = Dir.pwd

begin
assert_send_type "() -> Integer",
Dir, :chdir
assert_send_type "(::String) -> Integer",
Dir, :chdir, __dir__
assert_send_type "(::ToStr) -> Integer",
Dir, :chdir, ToStr.new(__dir__)
assert_send_type "(::ToStr) { (::String) -> 30 } -> 30",
Dir, :chdir, ToStr.new(__dir__) do 30 end
ensure
Dir.chdir(dir)
end
end

def test_children
Expand Down
2 changes: 1 addition & 1 deletion test/stdlib/ERB_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,6 @@ class ERBDefMethodSingletonTest < Test::Unit::TestCase

def test_def_erb_method
assert_send_type "(String, String) -> untyped",
ERB::DefMethod, :def_erb_method, "render()", File.expand_path(__FILE__, "../..")
ERB::DefMethod, :def_erb_method, "render()", File.expand_path(__FILE__)
end
end
54 changes: 27 additions & 27 deletions test/stdlib/FileTest_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,175 +7,175 @@ class FileTestSingletonTest < Test::Unit::TestCase

def test_blockdev?
assert_send_type "(::String file_name) -> bool",
FileTest, :blockdev?, File.expand_path(__FILE__, "../..")
FileTest, :blockdev?, File.expand_path(__FILE__)
assert_send_type "(::IO file_name) -> bool",
FileTest, :blockdev?, io_open
end

def test_chardev?
assert_send_type "(::String file_name) -> bool",
FileTest, :chardev?, File.expand_path(__FILE__, "../..")
FileTest, :chardev?, File.expand_path(__FILE__)
assert_send_type "(::IO file_name) -> bool",
FileTest, :chardev?, io_open
end

def test_directory?
assert_send_type "(::String file_name) -> bool",
FileTest, :directory?, File.expand_path(__FILE__, "../..")
FileTest, :directory?, File.expand_path(__FILE__)
assert_send_type "(::IO file_name) -> bool",
FileTest, :directory?, io_open
end

def test_empty?
assert_send_type "(::String file_name) -> bool",
FileTest, :empty?, File.expand_path(__FILE__, "../..")
FileTest, :empty?, File.expand_path(__FILE__)
assert_send_type "(::IO file_name) -> bool",
FileTest, :empty?, io_open
end

def test_executable?
assert_send_type "(::String file_name) -> bool",
FileTest, :executable?, File.expand_path(__FILE__, "../..")
FileTest, :executable?, File.expand_path(__FILE__)
end

def test_executable_real?
assert_send_type "(::String file_name) -> bool",
FileTest, :executable_real?, File.expand_path(__FILE__, "../..")
FileTest, :executable_real?, File.expand_path(__FILE__)
end

def test_exist?
assert_send_type "(::String file_name) -> bool",
FileTest, :exist?, File.expand_path(__FILE__, "../..")
FileTest, :exist?, File.expand_path(__FILE__)
assert_send_type "(::IO file_name) -> bool",
FileTest, :exist?, io_open
end

def test_file?
assert_send_type "(::String file) -> bool",
FileTest, :file?, File.expand_path(__FILE__, "../..")
FileTest, :file?, File.expand_path(__FILE__)
assert_send_type "(::IO file) -> bool",
FileTest, :file?, io_open
end

def test_grpowned?
assert_send_type "(::String file_name) -> bool",
FileTest, :grpowned?, File.expand_path(__FILE__, "../..")
FileTest, :grpowned?, File.expand_path(__FILE__)
assert_send_type "(::IO file_name) -> bool",
FileTest, :grpowned?, io_open
end

def test_identical?
assert_send_type "(::String file_1, ::String file_2) -> bool",
FileTest, :identical?, File.expand_path(__FILE__, "../.."), File.expand_path(__FILE__, "../..")
FileTest, :identical?, File.expand_path(__FILE__), File.expand_path(__FILE__)
assert_send_type "(::IO file_1, ::IO file_2) -> bool",
FileTest, :identical?, io_open, io_open
end

def test_owned?
assert_send_type "(::String file_name) -> bool",
FileTest, :owned?, File.expand_path(__FILE__, "../..")
FileTest, :owned?, File.expand_path(__FILE__)
assert_send_type "(::IO file_name) -> bool",
FileTest, :owned?, io_open
end

def test_pipe?
assert_send_type "(::String file_name) -> bool",
FileTest, :pipe?, File.expand_path(__FILE__, "../..")
FileTest, :pipe?, File.expand_path(__FILE__)
assert_send_type "(::IO file_name) -> bool",
FileTest, :pipe?, io_open
end

def test_readable?
assert_send_type "(::String file_name) -> bool",
FileTest, :readable?, File.expand_path(__FILE__, "../..")
FileTest, :readable?, File.expand_path(__FILE__)
end

def test_readable_real?
assert_send_type "(::String file_name) -> bool",
FileTest, :readable_real?, File.expand_path(__FILE__, "../..")
FileTest, :readable_real?, File.expand_path(__FILE__)
end

def test_setgid?
assert_send_type "(::String file_name) -> bool",
FileTest, :setgid?, File.expand_path(__FILE__, "../..")
FileTest, :setgid?, File.expand_path(__FILE__)
assert_send_type "(::IO file_name) -> bool",
FileTest, :setgid?, io_open
end

def test_setuid?
assert_send_type "(::String file_name) -> bool",
FileTest, :setuid?, File.expand_path(__FILE__, "../..")
FileTest, :setuid?, File.expand_path(__FILE__)
assert_send_type "(::IO file_name) -> bool",
FileTest, :setuid?, io_open
end

def test_size
assert_send_type "(::String file_name) -> ::Integer",
FileTest, :size, File.expand_path(__FILE__, "../..")
FileTest, :size, File.expand_path(__FILE__)
assert_send_type "(::IO file_name) -> ::Integer",
FileTest, :size, io_open
end

def test_size?
assert_send_type "(::String file_name) -> ::Integer?",
FileTest, :size?, File.expand_path(__FILE__, "../..")
FileTest, :size?, File.expand_path(__FILE__)
assert_send_type "(::IO file_name) -> ::Integer?",
FileTest, :size?, io_open
end

def test_socket?
assert_send_type "(::String file_name) -> bool",
FileTest, :socket?, File.expand_path(__FILE__, "../..")
FileTest, :socket?, File.expand_path(__FILE__)
assert_send_type "(::IO file_name) -> bool",
FileTest, :socket?, io_open
end

def test_sticky?
assert_send_type "(::String file_name) -> bool",
FileTest, :sticky?, File.expand_path(__FILE__, "../..")
FileTest, :sticky?, File.expand_path(__FILE__)
assert_send_type "(::IO file_name) -> bool",
FileTest, :sticky?, io_open
end

def test_symlink?
assert_send_type "(::String file_name) -> bool",
FileTest, :symlink?, File.expand_path(__FILE__, "../..")
FileTest, :symlink?, File.expand_path(__FILE__)
end

def test_world_readable?
assert_send_type "(::String file_name) -> ::Integer?",
FileTest, :world_readable?, File.expand_path(__FILE__, "../..")
FileTest, :world_readable?, File.expand_path(__FILE__)
assert_send_type "(::IO file_name) -> ::Integer?",
FileTest, :world_readable?, io_open
end

def test_world_writable?
assert_send_type "(::String file_name) -> ::Integer?",
FileTest, :world_writable?, File.expand_path(__FILE__, "../..")
FileTest, :world_writable?, File.expand_path(__FILE__)
assert_send_type "(::IO file_name) -> ::Integer?",
FileTest, :world_writable?, io_open
end

def test_writable?
assert_send_type "(::String file_name) -> bool",
FileTest, :writable?, File.expand_path(__FILE__, "../..")
FileTest, :writable?, File.expand_path(__FILE__)
end

def test_writable_real?
assert_send_type "(::String file_name) -> bool",
FileTest, :writable_real?, File.expand_path(__FILE__, "../..")
FileTest, :writable_real?, File.expand_path(__FILE__)
end

def test_zero?
assert_send_type "(::String file_name) -> bool",
FileTest, :zero?, File.expand_path(__FILE__, "../..")
FileTest, :zero?, File.expand_path(__FILE__)
assert_send_type "(::IO file_name) -> bool",
FileTest, :zero?, io_open
end

private

def io_open
IO.open(IO.sysopen(File.expand_path(__FILE__, "../..")))
IO.open(IO.sysopen(File.expand_path(__FILE__)))
end
end
Loading

0 comments on commit cd46c1e

Please sign in to comment.