rbs 4.0.3 → 4.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.dockerignore +37 -0
- data/.github/dependabot.yml +1 -1
- data/.github/workflows/bundle-update.yml +2 -2
- data/.github/workflows/c-check.yml +14 -6
- data/.github/workflows/comments.yml +2 -2
- data/.github/workflows/dependabot.yml +2 -2
- data/.github/workflows/jruby.yml +74 -0
- data/.github/workflows/release-gems.yml +235 -0
- data/.github/workflows/ruby.yml +43 -3
- data/.github/workflows/rust.yml +13 -10
- data/.github/workflows/truffleruby.yml +54 -0
- data/.github/workflows/typecheck.yml +5 -2
- data/.github/workflows/wasm.yml +55 -0
- data/.github/workflows/windows.yml +5 -2
- data/.gitignore +8 -0
- data/CHANGELOG.md +113 -0
- data/Dockerfile.jruby +53 -0
- data/README.md +4 -4
- data/Rakefile +512 -82
- data/Steepfile +9 -0
- data/config.yml +26 -0
- data/core/array.rbs +243 -180
- data/core/builtin.rbs +7 -6
- data/core/class.rbs +5 -3
- data/core/enumerable.rbs +109 -109
- data/core/enumerator/product.rbs +5 -5
- data/core/enumerator.rbs +28 -28
- data/core/file.rbs +24 -1018
- data/core/file_constants.rbs +463 -0
- data/core/file_stat.rbs +534 -0
- data/core/float.rbs +0 -24
- data/core/hash.rbs +117 -101
- data/core/integer.rbs +21 -58
- data/core/io.rbs +25 -7
- data/core/kernel.rbs +26 -11
- data/core/match_data.rbs +1 -1
- data/core/module.rbs +88 -74
- data/core/numeric.rbs +3 -0
- data/core/object_space/weak_key_map.rbs +7 -7
- data/core/pathname.rbs +0 -10
- data/core/ractor.rbs +0 -10
- data/core/range.rbs +23 -23
- data/core/rbs/ops.rbs +154 -0
- data/core/rbs/unnamed/argf.rbs +3 -3
- data/core/ruby_vm.rbs +40 -0
- data/core/rubygems/errors.rbs +4 -1
- data/core/rubygems/requirement.rbs +0 -10
- data/core/rubygems/rubygems.rbs +4 -1
- data/core/rubygems/specification.rbs +8 -0
- data/core/rubygems/version.rbs +0 -160
- data/core/set.rbs +3 -3
- data/core/struct.rbs +16 -16
- data/core/thread.rbs +9 -14
- data/docs/CONTRIBUTING.md +2 -1
- data/docs/inline.md +65 -7
- data/docs/rbs_by_example.md +20 -20
- data/docs/release.md +247 -0
- data/docs/syntax.md +2 -2
- data/docs/wasm_serialization.md +80 -0
- data/ext/rbs_extension/ast_translation.c +1298 -956
- data/ext/rbs_extension/ast_translation.h +4 -0
- data/ext/rbs_extension/class_constants.c +2 -0
- data/ext/rbs_extension/class_constants.h +1 -0
- data/ext/rbs_extension/extconf.rb +1 -0
- data/ext/rbs_extension/legacy_location.c +11 -6
- data/ext/rbs_extension/main.c +139 -4
- data/include/rbs/ast.h +323 -298
- data/include/rbs/defines.h +18 -0
- data/include/rbs/lexer.h +1 -0
- data/include/rbs/serialize.h +39 -0
- data/lib/rbs/annotate/rdoc_annotator.rb +27 -31
- data/lib/rbs/ast/ruby/annotations.rb +42 -0
- data/lib/rbs/ast/ruby/comment_block.rb +6 -4
- data/lib/rbs/ast/ruby/declarations.rb +11 -1
- data/lib/rbs/ast/ruby/helpers/location_helper.rb +1 -1
- data/lib/rbs/ast/ruby/members.rb +40 -1
- data/lib/rbs/buffer.rb +48 -11
- data/lib/rbs/cli.rb +3 -5
- data/lib/rbs/collection/config/lockfile_generator.rb +14 -1
- data/lib/rbs/collection/sources/git.rb +6 -0
- data/lib/rbs/definition_builder/method_builder.rb +12 -6
- data/lib/rbs/environment.rb +10 -3
- data/lib/rbs/inline_parser.rb +54 -28
- data/lib/rbs/namespace.rb +47 -11
- data/lib/rbs/parser_aux.rb +4 -2
- data/lib/rbs/prototype/rbi.rb +193 -25
- data/lib/rbs/prototype/runtime.rb +2 -0
- data/lib/rbs/resolver/type_name_resolver.rb +12 -14
- data/lib/rbs/rewriter.rb +70 -0
- data/lib/rbs/test/type_check.rb +6 -1
- data/lib/rbs/type_name.rb +33 -13
- data/lib/rbs/unit_test/type_assertions.rb +14 -5
- data/lib/rbs/version.rb +1 -1
- data/lib/rbs/wasm/deserializer.rb +213 -0
- data/lib/rbs/wasm/location.rb +61 -0
- data/lib/rbs/wasm/parser.rb +137 -0
- data/lib/rbs/wasm/runtime.rb +196 -0
- data/lib/rbs/wasm/serialization_schema.rb +110 -0
- data/lib/rbs.rb +14 -2
- data/lib/rbs_jars.rb +39 -0
- data/lib/rdoc_plugin/parser.rb +5 -0
- data/rbs.gemspec +32 -2
- data/sig/annotate/rdoc_annotater.rbs +12 -9
- data/sig/ast/ruby/annotations.rbs +49 -0
- data/sig/ast/ruby/members.rbs +21 -1
- data/sig/buffer.rbs +19 -1
- data/sig/collection/config/lockfile_generator.rbs +2 -0
- data/sig/inline_parser.rbs +2 -0
- data/sig/manifest.yaml +0 -1
- data/sig/namespace.rbs +20 -0
- data/sig/parser.rbs +10 -0
- data/sig/prototype/rbi.rbs +33 -4
- data/sig/resolver/type_name_resolver.rbs +2 -4
- data/sig/rewriter.rbs +45 -0
- data/sig/typename.rbs +15 -0
- data/sig/unit_test/type_assertions.rbs +6 -2
- data/sig/wasm/deserializer.rbs +66 -0
- data/sig/wasm/serialization_schema.rbs +13 -0
- data/src/ast.c +186 -162
- data/src/lexer.c +220 -193
- data/src/lexer.re +2 -1
- data/src/lexstate.c +10 -1
- data/src/parser.c +61 -8
- data/src/serialize.c +958 -0
- data/src/util/rbs_allocator.c +14 -8
- data/stdlib/abbrev/0/array.rbs +1 -1
- data/stdlib/csv/0/csv.rbs +5 -5
- data/stdlib/delegate/0/delegator.rbs +2 -1
- data/stdlib/digest/0/digest.rbs +11 -5
- data/stdlib/erb/0/erb.rbs +1 -1
- data/stdlib/etc/0/etc.rbs +18 -4
- data/stdlib/fileutils/0/fileutils.rbs +21 -21
- data/stdlib/ipaddr/0/ipaddr.rbs +0 -5
- data/stdlib/json/0/json.rbs +6 -6
- data/stdlib/monitor/0/monitor.rbs +2 -2
- data/stdlib/openssl/0/openssl.rbs +46 -40
- data/stdlib/resolv/0/resolv.rbs +1 -1
- data/stdlib/shellwords/0/shellwords.rbs +1 -1
- data/stdlib/stringio/0/stringio.rbs +32 -10
- data/stdlib/strscan/0/string_scanner.rbs +74 -55
- data/stdlib/tempfile/0/manifest.yaml +3 -0
- data/stdlib/timeout/0/timeout.rbs +0 -5
- data/stdlib/tsort/0/cyclic.rbs +1 -1
- data/stdlib/tsort/0/interfaces.rbs +8 -8
- data/stdlib/tsort/0/tsort.rbs +9 -9
- data/stdlib/uri/0/generic.rbs +0 -5
- data/stdlib/zlib/0/gzip_reader.rbs +2 -2
- data/stdlib/zlib/0/zstream.rbs +0 -1
- data/wasm/README.md +93 -0
- data/wasm/rbs_wasm.c +423 -0
- metadata +29 -6
- data/.github/workflows/milestone.yml +0 -80
- data/.vscode/extensions.json +0 -5
- data/.vscode/settings.json +0 -19
|
@@ -29,11 +29,9 @@ module RBS
|
|
|
29
29
|
new(all_names, aliases)
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
-
def try_cache(
|
|
33
|
-
cache
|
|
34
|
-
|
|
35
|
-
cache[query] = result
|
|
36
|
-
end
|
|
32
|
+
def try_cache(type_name, context)
|
|
33
|
+
inner = cache[context] ||= {}
|
|
34
|
+
inner.fetch(type_name) { inner[type_name] = yield }
|
|
37
35
|
end
|
|
38
36
|
|
|
39
37
|
def resolve(type_name, context:)
|
|
@@ -41,7 +39,7 @@ module RBS
|
|
|
41
39
|
return type_name
|
|
42
40
|
end
|
|
43
41
|
|
|
44
|
-
try_cache(
|
|
42
|
+
try_cache(type_name, context) do
|
|
45
43
|
if type_name.class?
|
|
46
44
|
resolve_namespace0(type_name, context, Set.new) || nil
|
|
47
45
|
else
|
|
@@ -51,7 +49,7 @@ module RBS
|
|
|
51
49
|
resolve_type_name(type_name.name, context)
|
|
52
50
|
else
|
|
53
51
|
if namespace = resolve_namespace0(namespace.to_type_name, context, Set.new)
|
|
54
|
-
type_name = TypeName.
|
|
52
|
+
type_name = TypeName[namespace.to_namespace, type_name.name]
|
|
55
53
|
has_type_name?(type_name)
|
|
56
54
|
end
|
|
57
55
|
end
|
|
@@ -68,7 +66,7 @@ module RBS
|
|
|
68
66
|
raise "Type name must be a class name: #{type_name}"
|
|
69
67
|
end
|
|
70
68
|
|
|
71
|
-
try_cache(
|
|
69
|
+
try_cache(type_name, context) do
|
|
72
70
|
ns = resolve_namespace0(type_name, context, Set.new) or return ns
|
|
73
71
|
end
|
|
74
72
|
end
|
|
@@ -93,10 +91,10 @@ module RBS
|
|
|
93
91
|
resolve_type_name(type_name, outer)
|
|
94
92
|
else
|
|
95
93
|
has_type_name?(inner) or raise "Context must be normalized: #{inner.inspect}"
|
|
96
|
-
has_type_name?(TypeName.
|
|
94
|
+
has_type_name?(TypeName[inner.to_namespace, type_name]) || resolve_type_name(type_name, outer)
|
|
97
95
|
end
|
|
98
96
|
else
|
|
99
|
-
type_name = TypeName.
|
|
97
|
+
type_name = TypeName[Namespace.root, type_name]
|
|
100
98
|
has_type_name?(type_name)
|
|
101
99
|
end
|
|
102
100
|
end
|
|
@@ -109,11 +107,11 @@ module RBS
|
|
|
109
107
|
resolve_head_namespace(head, outer)
|
|
110
108
|
when TypeName
|
|
111
109
|
has_type_name?(inner) or raise "Context must be normalized: #{inner.inspect}"
|
|
112
|
-
type_name = TypeName.
|
|
110
|
+
type_name = TypeName[inner.to_namespace, head]
|
|
113
111
|
has_type_name?(type_name) || aliased_name?(type_name) || resolve_head_namespace(head, outer)
|
|
114
112
|
end
|
|
115
113
|
else
|
|
116
|
-
type_name = TypeName.
|
|
114
|
+
type_name = TypeName[Namespace.root, head]
|
|
117
115
|
has_type_name?(type_name) || aliased_name?(type_name)
|
|
118
116
|
end
|
|
119
117
|
end
|
|
@@ -140,7 +138,7 @@ module RBS
|
|
|
140
138
|
|
|
141
139
|
head =
|
|
142
140
|
if type_name.absolute?
|
|
143
|
-
root_name = TypeName.
|
|
141
|
+
root_name = TypeName[Namespace.root, head]
|
|
144
142
|
has_type_name?(root_name) || aliased_name?(root_name)
|
|
145
143
|
else
|
|
146
144
|
resolve_head_namespace(head, context)
|
|
@@ -152,7 +150,7 @@ module RBS
|
|
|
152
150
|
end
|
|
153
151
|
|
|
154
152
|
tail.inject(head) do |namespace, name|
|
|
155
|
-
type_name = TypeName.
|
|
153
|
+
type_name = TypeName[namespace.to_namespace, name]
|
|
156
154
|
case
|
|
157
155
|
when has_type_name?(type_name)
|
|
158
156
|
type_name
|
data/lib/rbs/rewriter.rb
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RBS
|
|
4
|
+
class Rewriter
|
|
5
|
+
attr_reader :buffer
|
|
6
|
+
|
|
7
|
+
def initialize(buffer)
|
|
8
|
+
raise "Rewriter only supports toplevel buffers" if buffer.parent
|
|
9
|
+
|
|
10
|
+
@buffer = buffer
|
|
11
|
+
@rewrites = []
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def rewrite(location, string)
|
|
15
|
+
@rewrites.each do |existing_location, _|
|
|
16
|
+
if location.start_pos < existing_location.end_pos && existing_location.start_pos < location.end_pos
|
|
17
|
+
raise "Overlapping rewrites: #{existing_location} and #{location}"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
@rewrites << [location, string]
|
|
22
|
+
self
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def add_comment(*locations, content:)
|
|
26
|
+
earliest = locations.min_by(&:start_pos) or raise "At least one location is required"
|
|
27
|
+
insert_pos = earliest.start_pos
|
|
28
|
+
indent = " " * earliest.start_column
|
|
29
|
+
|
|
30
|
+
formatted = format_comment(content, indent)
|
|
31
|
+
|
|
32
|
+
loc = Location.new(buffer, insert_pos, insert_pos)
|
|
33
|
+
rewrite(loc, "#{formatted}\n#{indent}")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def replace_comment(comment, content:)
|
|
37
|
+
location = comment.location or raise "Comment must have a location"
|
|
38
|
+
indent = " " * location.start_column
|
|
39
|
+
|
|
40
|
+
rewrite(location, format_comment(content, indent))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def delete_comment(comment)
|
|
44
|
+
location = comment.location or raise "Comment must have a location"
|
|
45
|
+
line_start = location.start_pos - location.start_column
|
|
46
|
+
line_end = location.end_pos + 1
|
|
47
|
+
loc = Location.new(buffer, line_start, line_end)
|
|
48
|
+
rewrite(loc, "")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def string
|
|
52
|
+
result = buffer.content.dup
|
|
53
|
+
|
|
54
|
+
@rewrites.sort_by { |location, _| location.start_pos }.reverse_each do |location, replacement|
|
|
55
|
+
result[location.start_pos...location.end_pos] = replacement
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
result
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
def format_comment(content, indent)
|
|
64
|
+
content.lines.map do |line|
|
|
65
|
+
line = line.chomp
|
|
66
|
+
line.empty? ? "#" : "# #{line}"
|
|
67
|
+
end.join("\n#{indent}")
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
data/lib/rbs/test/type_check.rb
CHANGED
|
@@ -356,7 +356,12 @@ module RBS
|
|
|
356
356
|
when Types::Variable
|
|
357
357
|
true
|
|
358
358
|
when Types::Literal
|
|
359
|
-
|
|
359
|
+
begin
|
|
360
|
+
type.literal == val
|
|
361
|
+
rescue NoMethodError
|
|
362
|
+
raise if defined?(val.==)
|
|
363
|
+
false
|
|
364
|
+
end
|
|
360
365
|
when Types::Union
|
|
361
366
|
type.types.any? {|type| value(val, type) }
|
|
362
367
|
when Types::Intersection
|
data/lib/rbs/type_name.rb
CHANGED
|
@@ -22,14 +22,40 @@ module RBS
|
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
+
# Process-wide flyweight cache. Two-level Hash keyed by canonical
|
|
26
|
+
# Namespace identity (outer uses `compare_by_identity`) and name
|
|
27
|
+
# Symbol.
|
|
28
|
+
@intern_mutex = Mutex.new
|
|
29
|
+
@intern_cache = {} #: Hash[Namespace, Hash[Symbol, TypeName]]
|
|
30
|
+
@intern_cache.compare_by_identity
|
|
31
|
+
|
|
32
|
+
# Returns a canonical `TypeName` instance for the given `namespace` /
|
|
33
|
+
# `name` pair. The namespace is canonicalized through `Namespace.[]`
|
|
34
|
+
# so identity-based lookup works regardless of the caller passing a
|
|
35
|
+
# fresh `Namespace.new` or an already-interned instance.
|
|
36
|
+
def self.[](namespace, name)
|
|
37
|
+
ns = Namespace[namespace.path, namespace.absolute?]
|
|
38
|
+
|
|
39
|
+
inner = @intern_cache[ns]
|
|
40
|
+
if inner && (cached = inner[name])
|
|
41
|
+
return cached
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
@intern_mutex.synchronize do
|
|
45
|
+
inner = (@intern_cache[ns] ||= {})
|
|
46
|
+
inner[name] ||= new(namespace: ns, name: name)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
25
50
|
def ==(other)
|
|
51
|
+
return true if equal?(other)
|
|
26
52
|
other.is_a?(self.class) && other.namespace == namespace && other.name == name
|
|
27
53
|
end
|
|
28
54
|
|
|
29
55
|
alias eql? ==
|
|
30
56
|
|
|
31
57
|
def hash
|
|
32
|
-
namespace.hash ^ name.hash
|
|
58
|
+
@hash ||= namespace.hash ^ name.hash
|
|
33
59
|
end
|
|
34
60
|
|
|
35
61
|
def to_s
|
|
@@ -53,7 +79,7 @@ module RBS
|
|
|
53
79
|
end
|
|
54
80
|
|
|
55
81
|
def absolute!
|
|
56
|
-
|
|
82
|
+
TypeName[namespace.absolute!, name]
|
|
57
83
|
end
|
|
58
84
|
|
|
59
85
|
def absolute?
|
|
@@ -61,7 +87,7 @@ module RBS
|
|
|
61
87
|
end
|
|
62
88
|
|
|
63
89
|
def relative!
|
|
64
|
-
|
|
90
|
+
TypeName[namespace.relative!, name]
|
|
65
91
|
end
|
|
66
92
|
|
|
67
93
|
def interface?
|
|
@@ -69,7 +95,7 @@ module RBS
|
|
|
69
95
|
end
|
|
70
96
|
|
|
71
97
|
def with_prefix(namespace)
|
|
72
|
-
|
|
98
|
+
TypeName[namespace + self.namespace, name]
|
|
73
99
|
end
|
|
74
100
|
|
|
75
101
|
def split
|
|
@@ -80,23 +106,17 @@ module RBS
|
|
|
80
106
|
if other.absolute?
|
|
81
107
|
other
|
|
82
108
|
else
|
|
83
|
-
TypeName.
|
|
84
|
-
namespace: self.to_namespace + other.namespace,
|
|
85
|
-
name: other.name
|
|
86
|
-
)
|
|
109
|
+
TypeName[self.to_namespace + other.namespace, other.name]
|
|
87
110
|
end
|
|
88
111
|
end
|
|
89
|
-
|
|
112
|
+
|
|
90
113
|
def self.parse(string)
|
|
91
114
|
absolute = string.start_with?("::")
|
|
92
115
|
|
|
93
116
|
*path, name = string.delete_prefix("::").split("::").map(&:to_sym)
|
|
94
117
|
raise unless name
|
|
95
118
|
|
|
96
|
-
TypeName
|
|
97
|
-
name: name,
|
|
98
|
-
namespace: RBS::Namespace.new(path: path, absolute: absolute)
|
|
99
|
-
)
|
|
119
|
+
TypeName[Namespace[path, absolute], name]
|
|
100
120
|
end
|
|
101
121
|
end
|
|
102
122
|
end
|
|
@@ -180,11 +180,11 @@ module RBS
|
|
|
180
180
|
)
|
|
181
181
|
errors = typecheck.method_call(method, method_type, trace, errors: [])
|
|
182
182
|
|
|
183
|
-
assert_empty errors.map {|x| RBS::Test::Errors.to_string(x) }, "Call trace does not match with given method type: #{trace.inspect}"
|
|
183
|
+
assert_empty errors.map {|x| RBS::Test::Errors.to_string(x) }, -> { "Call trace does not match with given method type: #{trace.inspect}" }
|
|
184
184
|
|
|
185
185
|
method_defs = method_defs(method)
|
|
186
186
|
all_errors = method_defs.map {|t| typecheck.method_call(method, t.type, trace, errors: [], annotations: t.each_annotation.to_a) }
|
|
187
|
-
assert all_errors.any? {|es| es.empty? }, "Call trace does not match one of method definitions:\n #{trace.inspect}\n #{method_defs.map(&:type).join(" | ")}"
|
|
187
|
+
assert all_errors.any? {|es| es.empty? }, -> { "Call trace does not match one of method definitions:\n #{trace.inspect}\n #{method_defs.map(&:type).join(" | ")}" }
|
|
188
188
|
|
|
189
189
|
raise exception if exception
|
|
190
190
|
|
|
@@ -204,11 +204,11 @@ module RBS
|
|
|
204
204
|
)
|
|
205
205
|
errors = typecheck.method_call(method, method_type, trace, errors: [])
|
|
206
206
|
|
|
207
|
-
assert_empty errors.map {|x| RBS::Test::Errors.to_string(x) }, "Call trace does not match with given method type: #{trace.inspect}"
|
|
207
|
+
assert_empty errors.map {|x| RBS::Test::Errors.to_string(x) }, -> { "Call trace does not match with given method type: #{trace.inspect}" }
|
|
208
208
|
|
|
209
209
|
method_defs = method_defs(method)
|
|
210
210
|
all_errors = method_defs.map {|t| typecheck.method_call(method, t.type, trace, errors: [], annotations: t.each_annotation.to_a) }
|
|
211
|
-
assert all_errors.any? {|es| es.empty? }, "Call trace does not match one of method definitions:\n #{trace.inspect}\n #{method_defs.map(&:type).join(" | ")}"
|
|
211
|
+
assert all_errors.any? {|es| es.empty? }, -> { "Call trace does not match one of method definitions:\n #{trace.inspect}\n #{method_defs.map(&:type).join(" | ")}" }
|
|
212
212
|
|
|
213
213
|
# Use `instnace_of?` instead of `is_a?` as we want to check for _the exact exception class_.
|
|
214
214
|
assert exception.instance_of? error_type
|
|
@@ -246,7 +246,7 @@ module RBS
|
|
|
246
246
|
|
|
247
247
|
method_defs = method_defs(method)
|
|
248
248
|
all_errors = method_defs.map {|t| typecheck.method_call(method, t.type, trace, errors: [], annotations: t.each_annotation.to_a) }
|
|
249
|
-
assert all_errors.all? {|es| es.size > 0 }, "Call trace unexpectedly matches one of method definitions:\n #{trace.inspect}\n #{method_defs.map(&:type).join(" | ")}"
|
|
249
|
+
assert all_errors.all? {|es| es.size > 0 }, -> { "Call trace unexpectedly matches one of method definitions:\n #{trace.inspect}\n #{method_defs.map(&:type).join(" | ")}" }
|
|
250
250
|
|
|
251
251
|
result
|
|
252
252
|
end
|
|
@@ -323,6 +323,15 @@ module RBS
|
|
|
323
323
|
assert typecheck.value(constant, definition_type), "`#{constant_name}` (#{constant.inspect}) must be compatible with RBS type definition `#{definition_type}`"
|
|
324
324
|
end
|
|
325
325
|
|
|
326
|
+
def assert_visibility(visibility, method)
|
|
327
|
+
_, definition = target
|
|
328
|
+
method_entry = definition.methods[method]
|
|
329
|
+
|
|
330
|
+
assert method_entry, "Method `#{method}` not found in RBS definition"
|
|
331
|
+
assert visibility == method_entry.accessibility,
|
|
332
|
+
"Expected `#{method}` to be #{visibility}, but was #{method_entry.accessibility}"
|
|
333
|
+
end
|
|
334
|
+
|
|
326
335
|
def assert_type(type, value)
|
|
327
336
|
typecheck = RBS::Test::TypeCheck.new(
|
|
328
337
|
self_class: value.class,
|
data/lib/rbs/version.rb
CHANGED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "serialization_schema"
|
|
4
|
+
|
|
5
|
+
module RBS
|
|
6
|
+
module WASM
|
|
7
|
+
# Rebuilds RBS::AST objects from the binary buffer produced by
|
|
8
|
+
# `rbs_serialize_node` (src/serialize.c), driven by the generated
|
|
9
|
+
# SerializationSchema. This is the pure-Ruby counterpart of the C extension's
|
|
10
|
+
# ast_translation.c, used when the parser runs inside WebAssembly (JRuby).
|
|
11
|
+
#
|
|
12
|
+
# All locations are reconstructed through the public RBS::Location API, so the
|
|
13
|
+
# same code works whether RBS::Location is backed by the C extension (CRuby)
|
|
14
|
+
# or by a pure-Ruby implementation (JRuby).
|
|
15
|
+
class Deserializer
|
|
16
|
+
# Deserialize a buffer produced for a whole signature, returning
|
|
17
|
+
# `[directives, declarations]` to match RBS::Parser._parse_signature.
|
|
18
|
+
def self.deserialize(bytes, buffer)
|
|
19
|
+
new(bytes, buffer).read_node
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Deserialize a bare node list (rbs_serialize_node_list), e.g. the result
|
|
23
|
+
# of RBS::Parser._parse_type_params.
|
|
24
|
+
def self.deserialize_node_list(bytes, buffer)
|
|
25
|
+
new(bytes, buffer).read_node_list
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Deserialize the token stream produced by rbs_wasm_lex into the
|
|
29
|
+
# [type, location] pairs RBS::Parser._lex returns.
|
|
30
|
+
def self.deserialize_tokens(bytes, buffer)
|
|
31
|
+
new(bytes, buffer).read_tokens
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def initialize(bytes, buffer)
|
|
35
|
+
@bytes = bytes
|
|
36
|
+
@buffer = buffer
|
|
37
|
+
# Symbols and rbs_string fields (comments, annotations) inherit the
|
|
38
|
+
# source encoding, matching ast_translation.c. String/Integer literal
|
|
39
|
+
# nodes are always UTF-8 (see read_node).
|
|
40
|
+
@encoding = buffer.content.encoding
|
|
41
|
+
@pos = 0
|
|
42
|
+
@class_cache = {} #: Hash[String, untyped]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def read_node
|
|
46
|
+
tag = read_u8
|
|
47
|
+
return nil if tag == 0
|
|
48
|
+
return read_string(@encoding).to_sym if tag == SerializationSchema::SYMBOL_TAG
|
|
49
|
+
|
|
50
|
+
entry = SerializationSchema::SCHEMA[tag] or raise "Unknown node tag: #{tag}"
|
|
51
|
+
|
|
52
|
+
case entry[0]
|
|
53
|
+
when :node then read_struct(entry)
|
|
54
|
+
when :bool then read_u8 != 0
|
|
55
|
+
when :integer then read_string(Encoding::UTF_8).to_i
|
|
56
|
+
when :string_value then read_string(Encoding::UTF_8)
|
|
57
|
+
when :record_field then [read_node, read_u8 != 0]
|
|
58
|
+
when :signature then [read_node_list, read_node_list]
|
|
59
|
+
when :namespace then RBS::Namespace[read_node_list, read_u8 != 0]
|
|
60
|
+
when :type_name then RBS::TypeName[read_node, read_node]
|
|
61
|
+
else raise "Unknown schema entry kind: #{entry[0].inspect}"
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def read_node_list
|
|
66
|
+
Array.new(read_count) { read_node }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# The lex stream has no leading count: read records until the buffer is
|
|
70
|
+
# exhausted. Each is a token type name followed by its character range.
|
|
71
|
+
def read_tokens
|
|
72
|
+
tokens = [] #: Array[[ Symbol, Location ]]
|
|
73
|
+
until @pos >= @bytes.bytesize
|
|
74
|
+
type = read_string(Encoding::UTF_8).to_sym
|
|
75
|
+
start_char = read_i32
|
|
76
|
+
end_char = read_i32
|
|
77
|
+
tokens << [type, RBS::Location.new(@buffer, start_char, end_char)]
|
|
78
|
+
end
|
|
79
|
+
tokens
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
def read_struct(entry)
|
|
85
|
+
_, class_name, expose_location, loc_children, fields, resolve_type_params = entry
|
|
86
|
+
|
|
87
|
+
location = read_location(loc_children) if expose_location
|
|
88
|
+
|
|
89
|
+
kwargs = {} #: Hash[Symbol, untyped]
|
|
90
|
+
(fields || []).each do |name, reader|
|
|
91
|
+
kwargs[name] = read_field(reader)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
RBS::AST::TypeParam.resolve_variables(kwargs[:type_params]) if resolve_type_params
|
|
95
|
+
|
|
96
|
+
klass = class_for(class_name)
|
|
97
|
+
if expose_location
|
|
98
|
+
klass.new(location: location, **kwargs)
|
|
99
|
+
else
|
|
100
|
+
klass.new(**kwargs)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def read_field(reader)
|
|
105
|
+
case reader
|
|
106
|
+
when :node then read_node
|
|
107
|
+
when :node_list then read_node_list
|
|
108
|
+
when :hash then read_hash
|
|
109
|
+
when :string then read_string(@encoding)
|
|
110
|
+
when :bool then read_u8 != 0
|
|
111
|
+
when :location_range then read_location_value
|
|
112
|
+
when :location_range_list then read_location_value_list
|
|
113
|
+
when :attr_ivar_name then read_attr_ivar_name
|
|
114
|
+
else # [:enum, [value_or_nil, ...]]
|
|
115
|
+
reader[1][read_u8]
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def read_hash
|
|
120
|
+
hash = {} #: Hash[untyped, untyped]
|
|
121
|
+
read_count.times do
|
|
122
|
+
key = read_node
|
|
123
|
+
hash[key] = read_node
|
|
124
|
+
end
|
|
125
|
+
hash
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# A count of nested items. Each item is at least one byte, so a count that
|
|
129
|
+
# exceeds the bytes remaining signals the cursor has drifted out of sync.
|
|
130
|
+
def read_count
|
|
131
|
+
count = read_u32
|
|
132
|
+
if count > @bytes.bytesize - @pos
|
|
133
|
+
raise "Corrupt buffer: count #{count} exceeds #{@bytes.bytesize - @pos} remaining bytes at offset #{@pos}"
|
|
134
|
+
end
|
|
135
|
+
count
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# The base location of a node, followed by its named child ranges.
|
|
139
|
+
def read_location(loc_children)
|
|
140
|
+
base = read_range
|
|
141
|
+
children = (loc_children || []).map { |name, required| [name, required, read_range] }
|
|
142
|
+
|
|
143
|
+
return nil unless base
|
|
144
|
+
|
|
145
|
+
location = RBS::Location.new(@buffer, base[0], base[1])
|
|
146
|
+
children.each do |name, required, range|
|
|
147
|
+
if required
|
|
148
|
+
location.add_required_child(name, range[0]...range[1]) if range
|
|
149
|
+
else
|
|
150
|
+
location.add_optional_child(name, range ? (range[0]...range[1]) : nil)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
location
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# A standalone location range field: nil or an RBS::Location without children.
|
|
157
|
+
def read_location_value
|
|
158
|
+
range = read_range
|
|
159
|
+
range && RBS::Location.new(@buffer, range[0], range[1])
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def read_location_value_list
|
|
163
|
+
Array.new(read_count) { read_location_value }
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def read_attr_ivar_name
|
|
167
|
+
case read_u8
|
|
168
|
+
when 0 then nil # inferred instance variable
|
|
169
|
+
when 1 then false # no instance variable
|
|
170
|
+
else read_string(@encoding).to_sym
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Reads a presence byte and, when present, the start/end character positions.
|
|
175
|
+
def read_range
|
|
176
|
+
return nil if read_u8 == 0
|
|
177
|
+
|
|
178
|
+
start_char = read_i32
|
|
179
|
+
end_char = read_i32
|
|
180
|
+
[start_char, end_char]
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def read_u8
|
|
184
|
+
byte = @bytes.getbyte(@pos) or raise "Unexpected end of buffer"
|
|
185
|
+
@pos += 1
|
|
186
|
+
byte
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def read_u32
|
|
190
|
+
value = @bytes.unpack1("L<", offset: @pos) #: Integer
|
|
191
|
+
@pos += 4
|
|
192
|
+
value
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def read_i32
|
|
196
|
+
value = @bytes.unpack1("l<", offset: @pos) #: Integer
|
|
197
|
+
@pos += 4
|
|
198
|
+
value
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def read_string(encoding)
|
|
202
|
+
length = read_u32
|
|
203
|
+
string = @bytes.byteslice(@pos, length) or raise "Unexpected end of buffer"
|
|
204
|
+
@pos += length
|
|
205
|
+
string.force_encoding(encoding)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def class_for(name)
|
|
209
|
+
@class_cache[name] ||= Object.const_get(name)
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RBS
|
|
4
|
+
# Pure-Ruby implementation of the primitives that back RBS::Location.
|
|
5
|
+
#
|
|
6
|
+
# On CRuby these come from the C extension (ext/rbs_extension/legacy_location.c).
|
|
7
|
+
# JRuby loads this instead, before rbs/location_aux.rb layers the public API on
|
|
8
|
+
# top, so RBS::Location behaves identically without the native extension.
|
|
9
|
+
class Location
|
|
10
|
+
attr_reader :buffer
|
|
11
|
+
|
|
12
|
+
def initialize(buffer, start_pos, end_pos)
|
|
13
|
+
@buffer = buffer
|
|
14
|
+
@start_pos = start_pos
|
|
15
|
+
@end_pos = end_pos
|
|
16
|
+
@required_children = {} #: Hash[Symbol, [ Integer, Integer ]]
|
|
17
|
+
@optional_children = {} #: Hash[Symbol, [ Integer, Integer ]?]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def _start_pos
|
|
21
|
+
@start_pos
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def _end_pos
|
|
25
|
+
@end_pos
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def _add_required_child(name, start_pos, end_pos)
|
|
29
|
+
@required_children[name] = [start_pos, end_pos]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def _add_optional_child(name, start_pos, end_pos)
|
|
33
|
+
@optional_children[name] = [start_pos, end_pos]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def _add_optional_no_child(name)
|
|
37
|
+
@optional_children[name] = nil
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def _required_keys
|
|
41
|
+
@required_children.keys
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def _optional_keys
|
|
45
|
+
@optional_children.keys
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def [](name)
|
|
49
|
+
if (range = @required_children[name])
|
|
50
|
+
return Location.new(@buffer, range[0], range[1])
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
if @optional_children.key?(name)
|
|
54
|
+
range = @optional_children[name]
|
|
55
|
+
return range && Location.new(@buffer, range[0], range[1])
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
raise "Unknown child name given: #{name}"
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|