rbs 0.6.0 → 0.10.0
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/CHANGELOG.md +32 -0
- data/lib/rbs/ast/comment.rb +6 -0
- data/lib/rbs/cli.rb +21 -3
- data/lib/rbs/constant_table.rb +1 -1
- data/lib/rbs/definition.rb +4 -2
- data/lib/rbs/definition_builder.rb +264 -96
- data/lib/rbs/environment.rb +1 -1
- data/lib/rbs/errors.rb +25 -0
- data/lib/rbs/location.rb +15 -0
- data/lib/rbs/parser.y +29 -17
- data/lib/rbs/prototype/rb.rb +1 -1
- data/lib/rbs/substitution.rb +6 -2
- data/lib/rbs/test.rb +1 -0
- data/lib/rbs/test/errors.rb +5 -1
- data/lib/rbs/test/hook.rb +1 -0
- data/lib/rbs/test/setup.rb +33 -15
- data/lib/rbs/test/setup_helper.rb +29 -0
- data/lib/rbs/test/tester.rb +59 -15
- data/lib/rbs/test/type_check.rb +32 -16
- data/lib/rbs/validator.rb +4 -0
- data/lib/rbs/version.rb +1 -1
- data/lib/rbs/writer.rb +7 -2
- data/stdlib/builtin/enumerable.rbs +2 -2
- data/stdlib/json/json.rbs +6 -0
- data/stdlib/pty/pty.rbs +159 -0
- data/stdlib/zlib/zlib.rbs +392 -0
- metadata +5 -2
data/lib/rbs/environment.rb
CHANGED
@@ -126,7 +126,7 @@ module RBS
|
|
126
126
|
|
127
127
|
def cache_name(cache, name:, decl:, outer:)
|
128
128
|
if cache.key?(name)
|
129
|
-
raise DuplicatedDeclarationError.new(name, decl, cache[name])
|
129
|
+
raise DuplicatedDeclarationError.new(name, decl, cache[name].decl)
|
130
130
|
end
|
131
131
|
|
132
132
|
cache[name] = SingleEntry.new(name: name, decl: decl, outer: outer)
|
data/lib/rbs/errors.rb
CHANGED
@@ -141,6 +141,31 @@ module RBS
|
|
141
141
|
end
|
142
142
|
end
|
143
143
|
|
144
|
+
class NoSelfTypeFoundError < StandardError
|
145
|
+
attr_reader :type_name
|
146
|
+
attr_reader :location
|
147
|
+
|
148
|
+
def initialize(type_name:, location:)
|
149
|
+
@type_name = type_name
|
150
|
+
@location = location
|
151
|
+
|
152
|
+
super "#{Location.to_string location}: Could not find self type: #{type_name}"
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.check!(self_type, env:)
|
156
|
+
type_name = self_type.name
|
157
|
+
|
158
|
+
dic = case
|
159
|
+
when type_name.class?
|
160
|
+
env.class_decls
|
161
|
+
when type_name.interface?
|
162
|
+
env.interface_decls
|
163
|
+
end
|
164
|
+
|
165
|
+
dic.key?(type_name) or raise new(type_name: type_name, location: self_type.location)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
144
169
|
class NoMixinFoundError < StandardError
|
145
170
|
attr_reader :type_name
|
146
171
|
attr_reader :member
|
data/lib/rbs/location.rb
CHANGED
@@ -77,6 +77,21 @@ module RBS
|
|
77
77
|
locations.inject {|l1, l2| l1 + l2 }
|
78
78
|
end
|
79
79
|
|
80
|
+
def concat(*others)
|
81
|
+
others.each { |other| self << other }
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
def <<(other)
|
86
|
+
if other
|
87
|
+
raise "Invalid concat: buffer=#{buffer.name}, other.buffer=#{other.buffer.name}" unless other.buffer == buffer
|
88
|
+
@end_pos = other.end_pos
|
89
|
+
@source = nil
|
90
|
+
@end_loc = nil
|
91
|
+
end
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
80
95
|
def pred?(loc)
|
81
96
|
loc.is_a?(Location) &&
|
82
97
|
loc.name == name &&
|
data/lib/rbs/parser.y
CHANGED
@@ -4,9 +4,9 @@ class RBS::Parser
|
|
4
4
|
tANNOTATION
|
5
5
|
tSTRING tSYMBOL tINTEGER tWRITE_ATTR
|
6
6
|
kLPAREN kRPAREN kLBRACKET kRBRACKET kLBRACE kRBRACE
|
7
|
-
kVOID kNIL kTRUE kFALSE kANY kUNTYPED kTOP kBOT kSELF kSELFQ kINSTANCE kCLASS kBOOL kSINGLETON kTYPE kDEF kMODULE
|
7
|
+
kVOID kNIL kTRUE kFALSE kANY kUNTYPED kTOP kBOT kSELF kSELFQ kINSTANCE kCLASS kBOOL kSINGLETON kTYPE kDEF kMODULE
|
8
8
|
kPRIVATE kPUBLIC kALIAS
|
9
|
-
kCOLON kCOLON2 kCOMMA kBAR kAMP kHAT kARROW kQUESTION kEXCLAMATION kSTAR kSTAR2 kFATARROW kEQ kDOT kLT
|
9
|
+
kCOLON kCOLON2 kCOMMA kBAR kAMP kHAT kARROW kQUESTION kEXCLAMATION kSTAR kSTAR2 kFATARROW kEQ kDOT kDOT3 kLT
|
10
10
|
kINTERFACE kEND kINCLUDE kEXTEND kATTRREADER kATTRWRITER kATTRACCESSOR tOPERATOR tQUOTEDMETHOD tQUOTEDIDENT
|
11
11
|
kPREPEND kEXTENSION kINCOMPATIBLE
|
12
12
|
type_TYPE type_SIGNATURE type_METHODTYPE tEOF
|
@@ -425,7 +425,12 @@ rule
|
|
425
425
|
comment: leading_comment(val[0].first&.location || location))
|
426
426
|
}
|
427
427
|
|
428
|
-
overload:
|
428
|
+
overload:
|
429
|
+
{ result = nil }
|
430
|
+
| kOVERLOAD {
|
431
|
+
RBS.logger.warn "`overload def` syntax is deprecated. Use `...` syntax instead."
|
432
|
+
result = val[0]
|
433
|
+
}
|
429
434
|
|
430
435
|
method_member:
|
431
436
|
annotations attributes overload kDEF method_kind def_name method_types {
|
@@ -438,15 +443,24 @@ rule
|
|
438
443
|
type
|
439
444
|
end
|
440
445
|
end
|
446
|
+
|
447
|
+
last_type = val[6].last
|
448
|
+
if last_type.is_a?(LocatedValue) && last_type.value == :dot3
|
449
|
+
overload = true
|
450
|
+
val[6].pop
|
451
|
+
else
|
452
|
+
overload = false
|
453
|
+
end
|
454
|
+
|
441
455
|
result = Members::MethodDefinition.new(
|
442
456
|
name: val[5].value,
|
443
457
|
kind: val[4],
|
444
|
-
types:
|
458
|
+
types: val[6],
|
445
459
|
annotations: val[0],
|
446
460
|
location: location,
|
447
461
|
comment: leading_comment(val[0].first&.location || val[1].first&.location || val[2]&.location || val[3].location),
|
448
462
|
attributes: val[1].map(&:value),
|
449
|
-
overload: !!val[2]
|
463
|
+
overload: overload || !!val[2]
|
450
464
|
)
|
451
465
|
}
|
452
466
|
|
@@ -463,7 +477,7 @@ rule
|
|
463
477
|
|
464
478
|
method_types:
|
465
479
|
method_type { result = [val[0]] }
|
466
|
-
|
|
480
|
+
| kDOT3 { result = [LocatedValue.new(value: :dot3, location: val[0].location)] }
|
467
481
|
| method_type kBAR method_types {
|
468
482
|
result = val[2].unshift(val[0])
|
469
483
|
}
|
@@ -520,7 +534,7 @@ rule
|
|
520
534
|
|
521
535
|
method_name:
|
522
536
|
tOPERATOR
|
523
|
-
| kAMP | kHAT | kSTAR | kLT | kEXCLAMATION | kSTAR2 | kBAR
|
537
|
+
| kAMP | kHAT | kSTAR | kLT | kEXCLAMATION | kSTAR2 | kBAR
|
524
538
|
| method_name0
|
525
539
|
| method_name0 kQUESTION {
|
526
540
|
unless val[0].location.pred?(val[1].location)
|
@@ -548,7 +562,7 @@ rule
|
|
548
562
|
kCLASS | kVOID | kNIL | kTRUE | kFALSE | kANY | kUNTYPED | kTOP | kBOT | kINSTANCE | kBOOL | kSINGLETON
|
549
563
|
| kTYPE | kMODULE | kPRIVATE | kPUBLIC | kEND | kINCLUDE | kEXTEND | kPREPEND
|
550
564
|
| kATTRREADER | kATTRACCESSOR | kATTRWRITER | kDEF | kEXTENSION | kSELF | kINCOMPATIBLE
|
551
|
-
| kUNCHECKED | kINTERFACE |
|
565
|
+
| kUNCHECKED | kINTERFACE | kALIAS | kOUT | kIN | kOVERLOAD
|
552
566
|
|
553
567
|
module_type_params:
|
554
568
|
{ result = nil }
|
@@ -1150,15 +1164,13 @@ def leading_comment(location)
|
|
1150
1164
|
end
|
1151
1165
|
|
1152
1166
|
def push_comment(string, location)
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
new_comment = AST::Comment.new(string:
|
1158
|
-
|
1167
|
+
if (comment = leading_comment(location)) && comment.location.start_column == location.start_column
|
1168
|
+
comment.concat(string: "#{string}\n", location: location)
|
1169
|
+
@comments[comment.location.end_line] = comment
|
1170
|
+
else
|
1171
|
+
new_comment = AST::Comment.new(string: "#{string}\n", location: location)
|
1172
|
+
@comments[new_comment.location.end_line] = new_comment
|
1159
1173
|
end
|
1160
|
-
|
1161
|
-
@comments[new_comment.location.end_line] = new_comment
|
1162
1174
|
end
|
1163
1175
|
|
1164
1176
|
def new_token(type, value = input.matched)
|
@@ -1218,7 +1230,6 @@ KEYWORDS = {
|
|
1218
1230
|
"attr_reader" => :kATTRREADER,
|
1219
1231
|
"attr_writer" => :kATTRWRITER,
|
1220
1232
|
"attr_accessor" => :kATTRACCESSOR,
|
1221
|
-
"super" => :kSUPER,
|
1222
1233
|
"public" => :kPUBLIC,
|
1223
1234
|
"private" => :kPRIVATE,
|
1224
1235
|
"alias" => :kALIAS,
|
@@ -1267,6 +1278,7 @@ PUNCTS = {
|
|
1267
1278
|
"!" => :kEXCLAMATION,
|
1268
1279
|
"**" => :kSTAR2,
|
1269
1280
|
"*" => :kSTAR,
|
1281
|
+
"..." => :kDOT3,
|
1270
1282
|
"." => :kDOT,
|
1271
1283
|
"<" => :kLT,
|
1272
1284
|
"-@" => :tOPERATOR,
|
data/lib/rbs/prototype/rb.rb
CHANGED
data/lib/rbs/substitution.rb
CHANGED
@@ -11,7 +11,7 @@ module RBS
|
|
11
11
|
mapping[from] = to
|
12
12
|
end
|
13
13
|
|
14
|
-
def self.build(variables, types, instance_type:
|
14
|
+
def self.build(variables, types, instance_type: nil, &block)
|
15
15
|
unless variables.size == types.size
|
16
16
|
raise "Broken substitution: variables=#{variables}, types=#{types}"
|
17
17
|
end
|
@@ -33,7 +33,11 @@ module RBS
|
|
33
33
|
when Types::Variable
|
34
34
|
mapping[ty.name] || ty
|
35
35
|
when Types::Bases::Instance
|
36
|
-
instance_type
|
36
|
+
if instance_type
|
37
|
+
instance_type
|
38
|
+
else
|
39
|
+
ty
|
40
|
+
end
|
37
41
|
else
|
38
42
|
ty
|
39
43
|
end
|
data/lib/rbs/test.rb
CHANGED
data/lib/rbs/test/errors.rb
CHANGED
@@ -28,7 +28,11 @@ module RBS
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def self.inspect_(obj)
|
31
|
-
|
31
|
+
if obj.respond_to?(:inspect)
|
32
|
+
obj.inspect
|
33
|
+
else
|
34
|
+
Test::INSPECT.bind(obj).call # For the case inspect is not defined (like BasicObject)
|
35
|
+
end
|
32
36
|
end
|
33
37
|
|
34
38
|
def self.to_string(error)
|
data/lib/rbs/test/hook.rb
CHANGED
data/lib/rbs/test/setup.rb
CHANGED
@@ -1,24 +1,30 @@
|
|
1
1
|
require "rbs"
|
2
2
|
require "rbs/test"
|
3
|
-
|
4
3
|
require "optparse"
|
5
4
|
require "shellwords"
|
6
5
|
|
6
|
+
include RBS::Test::SetupHelper
|
7
|
+
|
7
8
|
logger = Logger.new(STDERR)
|
8
9
|
|
9
10
|
begin
|
10
11
|
opts = Shellwords.shellsplit(ENV["RBS_TEST_OPT"] || "-I sig")
|
11
|
-
filter = ENV.fetch(
|
12
|
-
skips = (ENV[
|
13
|
-
sampling = !ENV.key?("RBS_TEST_NO_SAMPLE")
|
12
|
+
filter = ENV.fetch('RBS_TEST_TARGET', "").split(',').map! { |e| e.strip }
|
13
|
+
skips = (ENV['RBS_TEST_SKIP'] || '').split(',').map! { |e| e.strip }
|
14
14
|
RBS.logger_level = (ENV["RBS_TEST_LOGLEVEL"] || "info")
|
15
|
-
|
15
|
+
sample_size = get_sample_size(ENV['RBS_TEST_SAMPLE_SIZE'] || '')
|
16
|
+
rescue InvalidSampleSizeError => exception
|
17
|
+
RBS.logger.error exception.message
|
18
|
+
exit 1
|
19
|
+
end
|
20
|
+
|
21
|
+
if filter.empty?
|
16
22
|
STDERR.puts "rbs/test/setup handles the following environment variables:"
|
17
23
|
STDERR.puts " [REQUIRED] RBS_TEST_TARGET: test target class name, `Foo::Bar,Foo::Baz` for each class or `Foo::*` for all classes under `Foo`"
|
18
24
|
STDERR.puts " [OPTIONAL] RBS_TEST_SKIP: skip testing classes"
|
19
25
|
STDERR.puts " [OPTIONAL] RBS_TEST_OPT: options for signatures (`-r` for libraries or `-I` for signatures)"
|
20
26
|
STDERR.puts " [OPTIONAL] RBS_TEST_LOGLEVEL: one of debug|info|warn|error|fatal (defaults to info)"
|
21
|
-
STDERR.puts " [OPTIONAL]
|
27
|
+
STDERR.puts " [OPTIONAL] RBS_TEST_SAMPLE_SIZE: sets the amount of values in a collection to be type-checked (Set to `ALL` to type check all the values)"
|
22
28
|
exit 1
|
23
29
|
end
|
24
30
|
|
@@ -32,26 +38,38 @@ env = RBS::Environment.from_loader(loader).resolve_type_names
|
|
32
38
|
|
33
39
|
def match(filter, name)
|
34
40
|
if filter.end_with?("*")
|
35
|
-
|
41
|
+
size = filter.size
|
42
|
+
name.start_with?(filter[0, size - 1]) || name == filter[0, size-3]
|
36
43
|
else
|
37
44
|
filter == name
|
38
45
|
end
|
39
46
|
end
|
40
47
|
|
41
|
-
|
48
|
+
def to_absolute_typename(type_name)
|
49
|
+
RBS::Factory.new().type_name(type_name).absolute!
|
50
|
+
end
|
51
|
+
|
42
52
|
tester = RBS::Test::Tester.new(env: env)
|
43
53
|
|
54
|
+
module_name = Module.instance_method(:name)
|
55
|
+
|
44
56
|
TracePoint.trace :end do |tp|
|
45
|
-
class_name = tp.self.
|
57
|
+
class_name = module_name.bind(tp.self).call&.yield_self {|name| to_absolute_typename name }
|
46
58
|
|
47
59
|
if class_name
|
48
|
-
if filter.any? {|f| match(f, class_name.to_s) } && skips.none? {|f| match(f, class_name.to_s) }
|
49
|
-
if
|
50
|
-
|
51
|
-
|
52
|
-
tester.install!(tp.self, sampling: sampling)
|
53
|
-
end
|
60
|
+
if filter.any? {|f| match(to_absolute_typename(f).to_s, class_name.to_s) } && skips.none? {|f| match(f, class_name.to_s) }
|
61
|
+
if env.class_decls.key?(class_name)
|
62
|
+
logger.info "Setting up hooks for #{class_name}"
|
63
|
+
tester.install!(tp.self, sample_size: sample_size)
|
54
64
|
end
|
55
65
|
end
|
56
66
|
end
|
57
67
|
end
|
68
|
+
|
69
|
+
at_exit do
|
70
|
+
if $!.nil? || $!.is_a?(SystemExit) && $!.success?
|
71
|
+
if tester.targets.empty?
|
72
|
+
logger.debug { "No type checker was installed!" }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module RBS
|
2
|
+
module Test
|
3
|
+
module SetupHelper
|
4
|
+
class InvalidSampleSizeError < StandardError
|
5
|
+
attr_reader :string
|
6
|
+
|
7
|
+
def initialize(string)
|
8
|
+
@string = string
|
9
|
+
super("Sample size should be a positive integer: `#{string}`")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
DEFAULT_SAMPLE_SIZE = 100
|
14
|
+
|
15
|
+
def get_sample_size(string)
|
16
|
+
case string
|
17
|
+
when ""
|
18
|
+
DEFAULT_SAMPLE_SIZE
|
19
|
+
when 'ALL'
|
20
|
+
nil
|
21
|
+
else
|
22
|
+
int_size = string.to_i
|
23
|
+
raise InvalidSampleSizeError.new(string) unless int_size.positive?
|
24
|
+
int_size
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/rbs/test/tester.rb
CHANGED
@@ -2,11 +2,15 @@ module RBS
|
|
2
2
|
module Test
|
3
3
|
class Tester
|
4
4
|
attr_reader :env
|
5
|
-
attr_reader :
|
5
|
+
attr_reader :targets
|
6
|
+
attr_reader :instance_testers
|
7
|
+
attr_reader :singleton_testers
|
6
8
|
|
7
9
|
def initialize(env:)
|
8
10
|
@env = env
|
9
|
-
@
|
11
|
+
@targets = []
|
12
|
+
@instance_testers = {}
|
13
|
+
@singleton_testers = {}
|
10
14
|
end
|
11
15
|
|
12
16
|
def factory
|
@@ -17,34 +21,74 @@ module RBS
|
|
17
21
|
@builder ||= DefinitionBuilder.new(env: env)
|
18
22
|
end
|
19
23
|
|
20
|
-
def
|
24
|
+
def skip_method?(type_name, method)
|
25
|
+
if method.implemented_in == type_name
|
26
|
+
if method.annotations.any? {|a| a.string == "rbs:test:skip" }
|
27
|
+
:skip
|
28
|
+
else
|
29
|
+
false
|
30
|
+
end
|
31
|
+
else
|
32
|
+
if method.annotations.any? {|a| a.string == "rbs:test:target" }
|
33
|
+
false
|
34
|
+
else
|
35
|
+
:implemented_in
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def install!(klass, sample_size:)
|
21
41
|
RBS.logger.info { "Installing runtime type checker in #{klass}..." }
|
22
42
|
|
23
43
|
type_name = factory.type_name(klass.name).absolute!
|
24
44
|
|
25
45
|
builder.build_instance(type_name).tap do |definition|
|
26
46
|
instance_key = new_key(type_name, "InstanceChecker")
|
27
|
-
|
47
|
+
tester, set = instance_testers[klass] ||= [
|
48
|
+
MethodCallTester.new(klass, builder, definition, kind: :instance, sample_size: sample_size),
|
49
|
+
Set[]
|
50
|
+
]
|
51
|
+
Observer.register(instance_key, tester)
|
28
52
|
|
29
53
|
definition.methods.each do |name, method|
|
30
|
-
if
|
31
|
-
|
32
|
-
|
54
|
+
if reason = skip_method?(type_name, method)
|
55
|
+
unless reason == :implemented_in
|
56
|
+
RBS.logger.info { "Skipping ##{name} because of `#{reason}`..." }
|
57
|
+
end
|
58
|
+
else
|
59
|
+
if klass.instance_methods(false).include?(name) && !set.include?(name)
|
60
|
+
RBS.logger.info { "Setting up method hook in ##{name}..." }
|
61
|
+
Hook.hook_instance_method klass, name, key: instance_key
|
62
|
+
set << name
|
63
|
+
end
|
33
64
|
end
|
34
65
|
end
|
35
66
|
end
|
36
67
|
|
37
68
|
builder.build_singleton(type_name).tap do |definition|
|
38
69
|
singleton_key = new_key(type_name, "SingletonChecker")
|
39
|
-
|
70
|
+
tester, set = singleton_testers[klass] ||= [
|
71
|
+
MethodCallTester.new(klass.singleton_class, builder, definition, kind: :singleton, sample_size: sample_size),
|
72
|
+
Set[]
|
73
|
+
]
|
74
|
+
Observer.register(singleton_key, tester)
|
40
75
|
|
41
76
|
definition.methods.each do |name, method|
|
42
|
-
if
|
43
|
-
|
44
|
-
|
77
|
+
if reason = skip_method?(type_name, method)
|
78
|
+
unless reason == :implemented_in
|
79
|
+
RBS.logger.info { "Skipping .#{name} because of `#{reason}`..." }
|
80
|
+
end
|
81
|
+
else
|
82
|
+
if klass.methods(false).include?(name) && !set.include?(name)
|
83
|
+
RBS.logger.info { "Setting up method hook in .#{name}..." }
|
84
|
+
Hook.hook_singleton_method klass, name, key: singleton_key
|
85
|
+
set << name
|
86
|
+
end
|
45
87
|
end
|
46
88
|
end
|
47
89
|
end
|
90
|
+
|
91
|
+
targets << klass
|
48
92
|
end
|
49
93
|
|
50
94
|
def new_key(type_name, prefix)
|
@@ -66,14 +110,14 @@ module RBS
|
|
66
110
|
attr_reader :definition
|
67
111
|
attr_reader :builder
|
68
112
|
attr_reader :kind
|
69
|
-
attr_reader :
|
113
|
+
attr_reader :sample_size
|
70
114
|
|
71
|
-
def initialize(self_class, builder, definition, kind:,
|
115
|
+
def initialize(self_class, builder, definition, kind:, sample_size:)
|
72
116
|
@self_class = self_class
|
73
117
|
@definition = definition
|
74
118
|
@builder = builder
|
75
119
|
@kind = kind
|
76
|
-
@
|
120
|
+
@sample_size = sample_size
|
77
121
|
end
|
78
122
|
|
79
123
|
def env
|
@@ -81,7 +125,7 @@ module RBS
|
|
81
125
|
end
|
82
126
|
|
83
127
|
def check
|
84
|
-
@check ||= TypeCheck.new(self_class: self_class, builder: builder,
|
128
|
+
@check ||= TypeCheck.new(self_class: self_class, builder: builder, sample_size: sample_size)
|
85
129
|
end
|
86
130
|
|
87
131
|
def format_method_name(name)
|