lib/rbi/parser.rb in rbi-0.0.7 vs lib/rbi/parser.rb in rbi-0.0.8

- old
+ new

@@ -41,15 +41,27 @@ sig { params(path: String).returns(Tree) } def self.parse_file(path) Parser.new.parse_file(path) end + sig { params(paths: T::Array[String]).returns(T::Array[Tree]) } + def self.parse_files(paths) + parser = Parser.new + paths.map { |path| parser.parse_file(path) } + end + sig { params(string: String).returns(Tree) } def parse_string(string) parse(string, file: "-") end + sig { params(strings: T::Array[String]).returns(T::Array[Tree]) } + def self.parse_strings(strings) + parser = Parser.new + strings.map { |string| parser.parse_string(string) } + end + sig { params(path: String).returns(Tree) } def parse_file(path) parse(::File.read(path), file: path) end @@ -57,14 +69,13 @@ sig { params(content: String, file: String).returns(Tree) } def parse(content, file:) node, comments = Unparser.parse_with_comments(content) assoc = ::Parser::Source::Comment.associate_locations(node, comments) - builder = TreeBuilder.new(file: file, comments: assoc) - builder.separate_header_comments + builder = TreeBuilder.new(file: file, comments: comments, nodes_comments_assoc: assoc) builder.visit(node) - builder.assoc_dangling_comments(comments) + builder.post_process builder.tree rescue ::Parser::SyntaxError => e raise ParseError.new(e.message, Loc.from_ast_loc(file, e.diagnostic.location)) end end @@ -103,22 +114,32 @@ attr_reader :tree sig do params( file: String, - comments: T::Hash[::Parser::Source::Map, T::Array[::Parser::Source::Comment]] + comments: T::Array[::Parser::Source::Comment], + nodes_comments_assoc: T::Hash[::Parser::Source::Map, T::Array[::Parser::Source::Comment]] ).void end - def initialize(file:, comments: {}) + def initialize(file:, comments: [], nodes_comments_assoc: {}) super() @file = file @comments = comments + @nodes_comments_assoc = nodes_comments_assoc @tree = T.let(Tree.new, Tree) @scopes_stack = T.let([@tree], T::Array[Tree]) @last_sigs = T.let([], T::Array[RBI::Sig]) + + separate_header_comments end + sig { void } + def post_process + assoc_dangling_comments + set_root_tree_loc + end + sig { override.params(node: T.nilable(Object)).void } def visit(node) return unless node.is_a?(AST::Node) case node.type when :module, :class, :sclass @@ -144,50 +165,10 @@ else visit_all(node.children) end end - sig { void } - def separate_header_comments - return if @comments.empty? - - keep = [] - node = T.must(@comments.keys.first) - comments = T.must(@comments.values.first) - - last_line = T.let(nil, T.nilable(Integer)) - comments.reverse.each do |comment| - comment_line = comment.location.last_line - - break if last_line && comment_line < last_line - 1 || - !last_line && comment_line < node.first_line - 1 - - keep << comment - last_line = comment_line - end - - @comments[node] = keep.reverse - end - - sig { params(comments: T::Array[::Parser::Source::Comment]).void } - def assoc_dangling_comments(comments) - last_line = T.let(nil, T.nilable(Integer)) - (comments - @comments.values.flatten).each do |comment| - comment_line = comment.location.last_line - text = comment.text[1..-1].strip - loc = Loc.from_ast_loc(@file, comment.location) - - if last_line && comment_line > last_line + 1 - # Preserve empty lines in file headers - tree.comments << BlankLine.new(loc: loc) - end - - tree.comments << Comment.new(text, loc: loc) - last_line = comment_line - end - end - private sig { params(node: AST::Node).returns(Scope) } def parse_scope(node) loc = node_loc(node) @@ -330,14 +311,32 @@ TStructProp.new(name, type, default: default_value, loc: loc, comments: comments) when :const name, type, default_value = parse_tstruct_prop(node) TStructConst.new(name, type, default: default_value, loc: loc, comments: comments) else - raise ParseError.new("Unsupported send node with name `#{method_name}`", loc) + args = parse_send_args(node) + Send.new(method_name.to_s, args, loc: loc, comments: comments) end end + sig { params(node: AST::Node).returns(T::Array[Arg]) } + def parse_send_args(node) + args = T.let([], T::Array[Arg]) + node.children[2..-1].each do |child| + if child.type == :kwargs + child.children.each do |pair| + keyword = pair.children.first.children.last.to_s + value = parse_expr(pair.children.last) + args << KwArg.new(keyword, value) + end + else + args << Arg.new(parse_expr(child)) + end + end + args + end + sig { params(node: AST::Node).returns(T.nilable(RBI::Node)) } def parse_block(node) name = node.children[0].children[1] case name @@ -433,11 +432,11 @@ Loc.from_ast_loc(@file, node.location) end sig { params(node: AST::Node).returns(T::Array[Comment]) } def node_comments(node) - comments = @comments[node.location] + comments = @nodes_comments_assoc[node.location] return [] unless comments comments.map do |comment| text = comment.text[1..-1].strip loc = Loc.from_ast_loc(@file, comment.location) Comment.new(text, loc: loc) @@ -452,9 +451,63 @@ sig { returns(T::Array[Sig]) } def current_sigs sigs = @last_sigs.dup @last_sigs.clear sigs + end + + sig { void } + def assoc_dangling_comments + last_line = T.let(nil, T.nilable(Integer)) + (@comments - @nodes_comments_assoc.values.flatten).each do |comment| + comment_line = comment.location.last_line + text = comment.text[1..-1].strip + loc = Loc.from_ast_loc(@file, comment.location) + + if last_line && comment_line > last_line + 1 + # Preserve empty lines in file headers + tree.comments << BlankLine.new(loc: loc) + end + + tree.comments << Comment.new(text, loc: loc) + last_line = comment_line + end + end + + sig { void } + def separate_header_comments + return if @nodes_comments_assoc.empty? + + keep = [] + node = T.must(@nodes_comments_assoc.keys.first) + comments = T.must(@nodes_comments_assoc.values.first) + + last_line = T.let(nil, T.nilable(Integer)) + comments.reverse.each do |comment| + comment_line = comment.location.last_line + + break if last_line && comment_line < last_line - 1 || + !last_line && comment_line < node.first_line - 1 + + keep << comment + last_line = comment_line + end + + @nodes_comments_assoc[node] = keep.reverse + end + + sig { void } + def set_root_tree_loc + first_loc = tree.nodes.first&.loc + last_loc = tree.nodes.last&.loc + + @tree.loc = Loc.new( + file: @file, + begin_line: first_loc&.begin_line || 0, + begin_column: first_loc&.begin_column || 0, + end_line: last_loc&.end_line || 0, + end_column: last_loc&.end_column || 0 + ) end end class ConstBuilder < ASTVisitor extend T::Sig