Skip to content

Commit fe9262e

Browse files
committed
Port prototype rb generation to prism
This just leaves the runtime on rubyvm, which is deprecated and I won't port. Because of that, some helper methods are duplicated to work with both prism and rubyvm. I tested this on rails, and out of 3435 files only 28 differ. That is mainly for string literals that are divided by line continutions (previously they were embeded, now it's only saying it a string) Also some block optionality is changed for the better. For example `(callable || block || :itself.to_proc).call` now makes the block optional
1 parent 750a675 commit fe9262e

13 files changed

Lines changed: 902 additions & 889 deletions

File tree

lib/rbs/cli.rb

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,6 @@ def parse_logging_options(opts)
108108
opts
109109
end
110110

111-
def has_parser?(format)
112-
return true if format == "rbi"
113-
defined?(RubyVM::AbstractSyntaxTree) ? true : false
114-
end
115-
116111
def run(args)
117112
@original_args = args.dup
118113

@@ -684,10 +679,6 @@ def autoload(name, path)
684679
end
685680

686681
def run_prototype_file(format, args)
687-
availability = unless has_parser?(format)
688-
"\n** This command does not work on this interpreter (#{RUBY_ENGINE}) **\n"
689-
end
690-
691682
# @type var output_dir: Pathname?
692683
output_dir = nil
693684
# @type var base_dir: Pathname?
@@ -698,7 +689,7 @@ def run_prototype_file(format, args)
698689
opts = OptionParser.new
699690
opts.banner = <<EOU
700691
Usage: rbs prototype #{format} [files...]
701-
#{availability}
692+
702693
Generate RBS prototype from source code.
703694
It parses specified Ruby code and and generates RBS prototypes.
704695
@@ -729,11 +720,6 @@ def run_prototype_file(format, args)
729720

730721
opts.parse!(args)
731722

732-
unless has_parser?(format)
733-
stdout.puts "Not supported on this interpreter (#{RUBY_ENGINE})."
734-
return 1
735-
end
736-
737723
if args.empty?
738724
stdout.puts opts
739725
return 1
@@ -744,7 +730,7 @@ def run_prototype_file(format, args)
744730
when "rbi"
745731
Prototype::RBI
746732
when "rb"
747-
Prototype::RB.new()
733+
Prototype::RB
748734
else
749735
raise
750736
end

lib/rbs/prototype/helpers.rb

Lines changed: 0 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,6 @@ module Prototype
55
module Helpers
66
private
77

8-
def parse_comments(string, include_trailing:)
9-
Prism.parse_comments(string, version: "current").yield_self do |prism_comments| # steep:ignore UnexpectedKeywordArgument
10-
process_comments(prism_comments, include_trailing: include_trailing)
11-
end
12-
end
13-
148
def process_comments(comments, include_trailing:)
159
comments.each_with_object({}) do |comment, hash| #$ Hash[Integer, AST::Comment]
1610
# Skip EmbDoc comments
@@ -32,137 +26,6 @@ def process_comments(comments, include_trailing:)
3226
end
3327
end
3428
end
35-
36-
def block_from_body(node)
37-
_, args_node, body_node = node.children
38-
_pre_num, _pre_init, _opt, _first_post, _post_num, _post_init, _rest, _kw, _kwrest, block_var = args_from_node(args_node)
39-
40-
# @type var body_node: node?
41-
if body_node
42-
yields = any_node?(body_node) {|n| n.type == :YIELD }
43-
end
44-
45-
if yields || block_var
46-
required = true
47-
48-
if body_node
49-
if any_node?(body_node) {|n| n.type == :FCALL && n.children[0] == :block_given? && !n.children[1] }
50-
required = false
51-
end
52-
end
53-
54-
if _rest == :* && block_var == :&
55-
# ... is given
56-
required = false
57-
end
58-
59-
if block_var
60-
if body_node
61-
usage = NodeUsage.new(body_node)
62-
if usage.each_conditional_node.any? {|n| n.type == :LVAR && n.children[0] == block_var }
63-
required = false
64-
end
65-
end
66-
end
67-
68-
if yields
69-
function = Types::Function.empty(untyped)
70-
71-
yields.each do |yield_node|
72-
array_content = yield_node.children[0]&.children&.compact || []
73-
74-
# @type var keywords: node?
75-
positionals, keywords = if keyword_hash?(array_content.last)
76-
[array_content.take(array_content.size - 1), array_content.last]
77-
else
78-
[array_content, nil]
79-
end
80-
81-
if (diff = positionals.size - function.required_positionals.size) > 0
82-
diff.times do
83-
function.required_positionals << Types::Function::Param.new(
84-
type: untyped,
85-
name: nil
86-
)
87-
end
88-
end
89-
90-
if keywords
91-
keywords.children[0].children.each_slice(2) do |key_node, value_node|
92-
if key_node
93-
key = key_node.children[0]
94-
function.required_keywords[key] ||=
95-
Types::Function::Param.new(
96-
type: untyped,
97-
name: nil
98-
)
99-
end
100-
end
101-
end
102-
end
103-
else
104-
function = Types::UntypedFunction.new(return_type: untyped)
105-
end
106-
107-
108-
Types::Block.new(required: required, type: function, self_type: nil)
109-
end
110-
end
111-
112-
def each_child(node, &block)
113-
each_node node.children, &block
114-
end
115-
116-
def each_node(nodes)
117-
nodes.each do |child|
118-
if child.is_a?(RubyVM::AbstractSyntaxTree::Node)
119-
yield child
120-
end
121-
end
122-
end
123-
124-
def any_node?(node, nodes: [], &block)
125-
if yield(node)
126-
nodes << node
127-
end
128-
129-
each_child node do |child|
130-
any_node? child, nodes: nodes, &block
131-
end
132-
133-
nodes.empty? ? nil : nodes
134-
end
135-
136-
def keyword_hash?(node)
137-
if node && node.type == :HASH
138-
node.children[0].children.compact.each_slice(2).all? {|key, _|
139-
symbol_literal_node?(key)
140-
}
141-
else
142-
false
143-
end
144-
end
145-
146-
# NOTE: args_node may be a nil by a bug
147-
# https://bugs.ruby-lang.org/issues/17495
148-
def args_from_node(args_node)
149-
args_node&.children || [0, nil, nil, nil, 0, nil, nil, nil, nil, nil]
150-
end
151-
152-
def symbol_literal_node?(node)
153-
case node.type
154-
when :LIT
155-
if node.children[0].is_a?(Symbol)
156-
node.children[0]
157-
end
158-
when :SYM
159-
node.children[0]
160-
end
161-
end
162-
163-
def untyped
164-
@untyped ||= Types::Bases::Any.new(location: nil)
165-
end
16629
end
16730
end
16831
end

lib/rbs/prototype/node_usage.rb

Lines changed: 41 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -27,69 +27,54 @@ def calculate(node, conditional:)
2727
conditional_nodes << node
2828
end
2929

30-
case node.type
31-
when :IF, :UNLESS
32-
cond_node, true_node, false_node = node.children
33-
calculate(cond_node, conditional: true)
34-
calculate(true_node, conditional: conditional) if true_node
35-
calculate(false_node, conditional: conditional) if false_node
36-
when :AND, :OR
37-
left, right = node.children
38-
calculate(left, conditional: true)
39-
calculate(right, conditional: conditional)
40-
when :QCALL
41-
receiver, _, args = node.children
42-
calculate(receiver, conditional: true)
43-
calculate(args, conditional: false) if args
44-
when :WHILE
45-
cond, body = node.children
46-
calculate(cond, conditional: true)
47-
calculate(body, conditional: false) if body
48-
when :OP_ASGN_OR, :OP_ASGN_AND
49-
var, _, asgn = node.children
50-
calculate(var, conditional: true)
51-
calculate(asgn, conditional: conditional)
52-
when :LASGN, :IASGN, :GASGN
53-
_, lhs = node.children
54-
calculate(lhs, conditional: conditional) if lhs
55-
when :MASGN
56-
lhs, _ = node.children
57-
calculate(lhs, conditional: conditional)
58-
when :CDECL
59-
if node.children.size == 2
60-
_, lhs = node.children
61-
calculate(lhs, conditional: conditional)
62-
else
63-
const, _, lhs = node.children
64-
calculate(const, conditional: false)
65-
calculate(lhs, conditional: conditional)
66-
end
67-
when :SCOPE
68-
_, _, body = node.children
69-
calculate(body, conditional: conditional)
70-
when :CASE2
71-
_, *branches = node.children
72-
branches.each do |branch|
73-
if branch.type == :WHEN
74-
list, body = branch.children
75-
list.children.each do |child|
76-
if child
77-
calculate(child, conditional: true)
78-
end
79-
end
80-
calculate(body, conditional: conditional)
81-
else
82-
calculate(branch, conditional: conditional)
30+
case node
31+
in Prism::IfNode
32+
calculate(node.predicate, conditional: true)
33+
calculate(node.statements, conditional: conditional) if node.statements
34+
calculate(node.subsequent, conditional: conditional) if node.subsequent
35+
in Prism::UnlessNode
36+
calculate(node.predicate, conditional: true)
37+
calculate(node.statements, conditional: conditional) if node.statements
38+
calculate(node.else_clause, conditional: conditional) if node.else_clause
39+
in Prism::AndNode | Prism::OrNode
40+
calculate(node.left, conditional: true)
41+
calculate(node.right, conditional: conditional)
42+
in Prism::CallNode if node.safe_navigation?
43+
calculate(node.receiver, conditional: true) if node.receiver
44+
calculate(node.arguments, conditional: false) if node.arguments
45+
in Prism::WhileNode
46+
calculate(node.predicate, conditional: true)
47+
calculate(node.statements, conditional: false) if node.statements
48+
in Prism::ConstantOrWriteNode | Prism::ConstantAndWriteNode |
49+
Prism::GlobalVariableOrWriteNode | Prism::GlobalVariableAndWriteNode |
50+
Prism::InstanceVariableOrWriteNode | Prism::InstanceVariableAndWriteNode |
51+
Prism::LocalVariableOrWriteNode | Prism::LocalVariableAndWriteNode
52+
conditional_nodes << node
53+
calculate(node.value, conditional: conditional)
54+
in Prism::ConstantWriteNode | Prism::MultiWriteNode |
55+
Prism::LocalVariableWriteNode | Prism::InstanceVariableWriteNode | Prism::GlobalVariableWriteNode
56+
calculate(node.value, conditional: conditional)
57+
in Prism::ConstantPathWriteNode
58+
calculate(node.target, conditional: false)
59+
calculate(node.value, conditional: conditional)
60+
in Prism::BlockNode | Prism::ClassNode | Prism::DefNode | Prism::LambdaNode | Prism::ModuleNode | Prism::SingletonClassNode
61+
# Anything with locals
62+
calculate(node.body, conditional: conditional) if node.body
63+
in Prism::CaseNode[predicate: predicate] unless predicate
64+
node.conditions.each do |when_node|
65+
when_node.conditions.each do |child|
66+
calculate(child, conditional: true)
8367
end
68+
calculate(when_node.statements, conditional: conditional) if when_node.statements
8469
end
85-
when :BLOCK
86-
*nodes, last = node.children
70+
in Prism::StatementsNode
71+
*nodes, last = node.body
8772
nodes.each do |no|
8873
calculate(no, conditional: false)
8974
end
9075
calculate(last, conditional: conditional) if last
9176
else
92-
each_child(node) do |child|
77+
node.compact_child_nodes.each do |child|
9378
calculate(child, conditional: false)
9479
end
9580
end

0 commit comments

Comments
 (0)