rbs 0.6.0 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|