rbs 0.7.0 → 0.11.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 +34 -0
- data/Gemfile +1 -0
- data/README.md +1 -1
- data/docs/syntax.md +14 -1
- data/lib/rbs/ast/comment.rb +6 -0
- data/lib/rbs/ast/members.rb +3 -8
- data/lib/rbs/cli.rb +83 -4
- data/lib/rbs/definition.rb +15 -5
- data/lib/rbs/definition_builder.rb +32 -13
- data/lib/rbs/environment.rb +0 -1
- data/lib/rbs/environment_loader.rb +55 -35
- data/lib/rbs/location.rb +15 -0
- data/lib/rbs/parser.y +30 -20
- data/lib/rbs/prototype/rb.rb +0 -1
- data/lib/rbs/prototype/rbi.rb +0 -2
- data/lib/rbs/prototype/runtime.rb +0 -4
- 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 +37 -15
- data/lib/rbs/test/setup_helper.rb +44 -0
- data/lib/rbs/test/tester.rb +61 -15
- data/lib/rbs/test/type_check.rb +45 -17
- data/lib/rbs/validator.rb +4 -0
- data/lib/rbs/version.rb +1 -1
- data/lib/rbs/writer.rb +7 -3
- data/stdlib/builtin/array.rbs +2 -1
- data/stdlib/builtin/enumerable.rbs +2 -2
- data/stdlib/builtin/hash.rbs +1 -1
- data/stdlib/date/date.rbs +1056 -0
- data/stdlib/date/date_time.rbs +582 -0
- data/stdlib/json/json.rbs +6 -0
- data/stdlib/pty/pty.rbs +159 -0
- data/stdlib/zlib/zlib.rbs +392 -0
- metadata +7 -2
data/lib/rbs/environment.rb
CHANGED
@@ -75,29 +75,16 @@ module RBS
|
|
75
75
|
self.class.gem_sig_path(name, version)
|
76
76
|
end
|
77
77
|
|
78
|
-
def each_signature(path
|
78
|
+
def each_signature(path, immediate: true, &block)
|
79
79
|
if block_given?
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
yield path
|
85
|
-
end
|
86
|
-
when path.directory?
|
87
|
-
path.children.each do |child|
|
88
|
-
each_signature child, immediate: false, &block
|
89
|
-
end
|
80
|
+
case
|
81
|
+
when path.file?
|
82
|
+
if path.extname == ".rbs" || immediate
|
83
|
+
yield path
|
90
84
|
end
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
when Pathname
|
95
|
-
each_signature path, immediate: immediate, &block
|
96
|
-
when LibraryPath
|
97
|
-
each_signature path.path, immediate: immediate, &block
|
98
|
-
when GemPath
|
99
|
-
each_signature path.path, immediate: immediate, &block
|
100
|
-
end
|
85
|
+
when path.directory?
|
86
|
+
path.children.each do |child|
|
87
|
+
each_signature child, immediate: false, &block
|
101
88
|
end
|
102
89
|
end
|
103
90
|
else
|
@@ -105,32 +92,65 @@ module RBS
|
|
105
92
|
end
|
106
93
|
end
|
107
94
|
|
108
|
-
def
|
109
|
-
|
95
|
+
def each_library_path
|
96
|
+
paths.each do |path|
|
97
|
+
case path
|
98
|
+
when Pathname
|
99
|
+
yield path, path
|
100
|
+
when LibraryPath
|
101
|
+
yield path, path.path
|
102
|
+
when GemPath
|
103
|
+
yield path, path.path
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def no_builtin!(skip = true)
|
109
|
+
@no_builtin = skip
|
110
|
+
self
|
110
111
|
end
|
111
112
|
|
112
113
|
def no_builtin?
|
113
114
|
@no_builtin
|
114
115
|
end
|
115
116
|
|
116
|
-
def
|
117
|
-
|
117
|
+
def each_decl
|
118
|
+
if block_given?
|
119
|
+
signature_files = []
|
118
120
|
|
119
|
-
|
120
|
-
|
121
|
-
|
121
|
+
unless no_builtin?
|
122
|
+
each_signature(stdlib_root + "builtin") do |path|
|
123
|
+
signature_files << [:stdlib, path]
|
124
|
+
end
|
125
|
+
end
|
122
126
|
|
123
|
-
|
124
|
-
|
127
|
+
each_library_path do |library_path, pathname|
|
128
|
+
each_signature(pathname) do |path|
|
129
|
+
signature_files << [library_path, path]
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
signature_files.each do |lib_path, file_path|
|
134
|
+
buffer = Buffer.new(name: file_path.to_s, content: file_path.read)
|
135
|
+
Parser.parse_signature(buffer).each do |decl|
|
136
|
+
yield decl, buffer, file_path, lib_path
|
137
|
+
end
|
138
|
+
end
|
139
|
+
else
|
140
|
+
enum_for :each_decl
|
125
141
|
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def load(env:)
|
145
|
+
loadeds = []
|
126
146
|
|
127
|
-
|
128
|
-
buffer = Buffer.new(name: file.to_s, content: file.read)
|
147
|
+
each_decl do |decl, buffer, file_path, lib_path|
|
129
148
|
env.buffers.push(buffer)
|
130
|
-
|
131
|
-
|
132
|
-
end
|
149
|
+
env << decl
|
150
|
+
loadeds << [decl, file_path, lib_path]
|
133
151
|
end
|
152
|
+
|
153
|
+
loadeds
|
134
154
|
end
|
135
155
|
end
|
136
156
|
end
|
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,22 +443,29 @@ 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
|
-
comment: leading_comment(val[0].first&.location || val[
|
448
|
-
|
449
|
-
overload: !!val[2]
|
461
|
+
comment: leading_comment(val[0].first&.location || val[2]&.location || val[3].location),
|
462
|
+
overload: overload || !!val[2]
|
450
463
|
)
|
451
464
|
}
|
452
465
|
|
453
466
|
attributes:
|
454
|
-
{ result = [] }
|
455
467
|
| attributes kINCOMPATIBLE {
|
456
|
-
|
468
|
+
RBS.logger.warn "`incompatible` method attribute is deprecated and ignored."
|
457
469
|
}
|
458
470
|
|
459
471
|
method_kind:
|
@@ -463,7 +475,7 @@ rule
|
|
463
475
|
|
464
476
|
method_types:
|
465
477
|
method_type { result = [val[0]] }
|
466
|
-
|
|
478
|
+
| kDOT3 { result = [LocatedValue.new(value: :dot3, location: val[0].location)] }
|
467
479
|
| method_type kBAR method_types {
|
468
480
|
result = val[2].unshift(val[0])
|
469
481
|
}
|
@@ -548,7 +560,7 @@ rule
|
|
548
560
|
kCLASS | kVOID | kNIL | kTRUE | kFALSE | kANY | kUNTYPED | kTOP | kBOT | kINSTANCE | kBOOL | kSINGLETON
|
549
561
|
| kTYPE | kMODULE | kPRIVATE | kPUBLIC | kEND | kINCLUDE | kEXTEND | kPREPEND
|
550
562
|
| kATTRREADER | kATTRACCESSOR | kATTRWRITER | kDEF | kEXTENSION | kSELF | kINCOMPATIBLE
|
551
|
-
| kUNCHECKED | kINTERFACE |
|
563
|
+
| kUNCHECKED | kINTERFACE | kALIAS | kOUT | kIN | kOVERLOAD
|
552
564
|
|
553
565
|
module_type_params:
|
554
566
|
{ result = nil }
|
@@ -1150,15 +1162,13 @@ def leading_comment(location)
|
|
1150
1162
|
end
|
1151
1163
|
|
1152
1164
|
def push_comment(string, location)
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
new_comment = AST::Comment.new(string:
|
1158
|
-
|
1165
|
+
if (comment = leading_comment(location)) && comment.location.start_column == location.start_column
|
1166
|
+
comment.concat(string: "#{string}\n", location: location)
|
1167
|
+
@comments[comment.location.end_line] = comment
|
1168
|
+
else
|
1169
|
+
new_comment = AST::Comment.new(string: "#{string}\n", location: location)
|
1170
|
+
@comments[new_comment.location.end_line] = new_comment
|
1159
1171
|
end
|
1160
|
-
|
1161
|
-
@comments[new_comment.location.end_line] = new_comment
|
1162
1172
|
end
|
1163
1173
|
|
1164
1174
|
def new_token(type, value = input.matched)
|
@@ -1218,7 +1228,6 @@ KEYWORDS = {
|
|
1218
1228
|
"attr_reader" => :kATTRREADER,
|
1219
1229
|
"attr_writer" => :kATTRWRITER,
|
1220
1230
|
"attr_accessor" => :kATTRACCESSOR,
|
1221
|
-
"super" => :kSUPER,
|
1222
1231
|
"public" => :kPUBLIC,
|
1223
1232
|
"private" => :kPRIVATE,
|
1224
1233
|
"alias" => :kALIAS,
|
@@ -1267,6 +1276,7 @@ PUNCTS = {
|
|
1267
1276
|
"!" => :kEXCLAMATION,
|
1268
1277
|
"**" => :kSTAR2,
|
1269
1278
|
"*" => :kSTAR,
|
1279
|
+
"..." => :kDOT3,
|
1270
1280
|
"." => :kDOT,
|
1271
1281
|
"<" => :kLT,
|
1272
1282
|
"-@" => :tOPERATOR,
|
data/lib/rbs/prototype/rb.rb
CHANGED
data/lib/rbs/prototype/rbi.rb
CHANGED
@@ -173,7 +173,6 @@ module RBS
|
|
173
173
|
types: types,
|
174
174
|
kind: :singleton,
|
175
175
|
comment: comment,
|
176
|
-
attributes: [],
|
177
176
|
overload: false
|
178
177
|
)
|
179
178
|
end
|
@@ -194,7 +193,6 @@ module RBS
|
|
194
193
|
types: types,
|
195
194
|
kind: :instance,
|
196
195
|
comment: comment,
|
197
|
-
attributes: [],
|
198
196
|
overload: false
|
199
197
|
)
|
200
198
|
end
|
@@ -154,7 +154,6 @@ module RBS
|
|
154
154
|
location: nil,
|
155
155
|
comment: method.comments[0],
|
156
156
|
annotations: method.annotations,
|
157
|
-
attributes: method.attributes,
|
158
157
|
overload: false
|
159
158
|
)
|
160
159
|
return
|
@@ -193,7 +192,6 @@ module RBS
|
|
193
192
|
location: nil,
|
194
193
|
comment: nil,
|
195
194
|
annotations: [],
|
196
|
-
attributes: [],
|
197
195
|
overload: false
|
198
196
|
)
|
199
197
|
end
|
@@ -227,7 +225,6 @@ module RBS
|
|
227
225
|
location: nil,
|
228
226
|
comment: nil,
|
229
227
|
annotations: [],
|
230
|
-
attributes: [],
|
231
228
|
overload: false
|
232
229
|
)
|
233
230
|
end
|
@@ -262,7 +259,6 @@ module RBS
|
|
262
259
|
location: nil,
|
263
260
|
comment: nil,
|
264
261
|
annotations: [],
|
265
|
-
attributes: [],
|
266
262
|
overload: false
|
267
263
|
)
|
268
264
|
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,34 @@
|
|
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
|
+
double_class = to_double_class(ENV['RBS_TEST_DOUBLE_SUITE'])
|
17
|
+
unchecked_classes = (ENV['RBS_TEST_UNCHECKED_CLASSES'] || '').split(',').map! { |unchecked_class| unchecked_class.strip }.push(*double_class)
|
18
|
+
rescue InvalidSampleSizeError => exception
|
19
|
+
RBS.logger.error exception.message
|
20
|
+
exit 1
|
21
|
+
end
|
22
|
+
|
23
|
+
if filter.empty?
|
16
24
|
STDERR.puts "rbs/test/setup handles the following environment variables:"
|
17
25
|
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
26
|
STDERR.puts " [OPTIONAL] RBS_TEST_SKIP: skip testing classes"
|
19
27
|
STDERR.puts " [OPTIONAL] RBS_TEST_OPT: options for signatures (`-r` for libraries or `-I` for signatures)"
|
20
28
|
STDERR.puts " [OPTIONAL] RBS_TEST_LOGLEVEL: one of debug|info|warn|error|fatal (defaults to info)"
|
21
|
-
STDERR.puts " [OPTIONAL]
|
29
|
+
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)"
|
30
|
+
STDERR.puts " [OPTIONAL] RBS_TEST_DOUBLE_SUITE: sets the double suite in use (currently supported: minitest | rspec)"
|
31
|
+
STDERR.puts " [OPTIONAL] RBS_TEST_UNCHECKED_CLASSES: sets the classes that would not be checked"
|
22
32
|
exit 1
|
23
33
|
end
|
24
34
|
|
@@ -32,26 +42,38 @@ env = RBS::Environment.from_loader(loader).resolve_type_names
|
|
32
42
|
|
33
43
|
def match(filter, name)
|
34
44
|
if filter.end_with?("*")
|
35
|
-
|
45
|
+
size = filter.size
|
46
|
+
name.start_with?(filter[0, size - 1]) || name == filter[0, size-3]
|
36
47
|
else
|
37
48
|
filter == name
|
38
49
|
end
|
39
50
|
end
|
40
51
|
|
41
|
-
|
52
|
+
def to_absolute_typename(type_name)
|
53
|
+
RBS::Factory.new().type_name(type_name).absolute!
|
54
|
+
end
|
55
|
+
|
42
56
|
tester = RBS::Test::Tester.new(env: env)
|
43
57
|
|
58
|
+
module_name = Module.instance_method(:name)
|
59
|
+
|
44
60
|
TracePoint.trace :end do |tp|
|
45
|
-
class_name = tp.self.
|
61
|
+
class_name = module_name.bind(tp.self).call&.yield_self {|name| to_absolute_typename name }
|
46
62
|
|
47
63
|
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
|
64
|
+
if filter.any? {|f| match(to_absolute_typename(f).to_s, class_name.to_s) } && skips.none? {|f| match(f, class_name.to_s) }
|
65
|
+
if env.class_decls.key?(class_name)
|
66
|
+
logger.info "Setting up hooks for #{class_name}"
|
67
|
+
tester.install!(tp.self, sample_size: sample_size, unchecked_classes: unchecked_classes)
|
54
68
|
end
|
55
69
|
end
|
56
70
|
end
|
57
71
|
end
|
72
|
+
|
73
|
+
at_exit do
|
74
|
+
if $!.nil? || $!.is_a?(SystemExit) && $!.success?
|
75
|
+
if tester.targets.empty?
|
76
|
+
logger.debug { "No type checker was installed!" }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,44 @@
|
|
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
|
+
|
28
|
+
def to_double_class(double_suite)
|
29
|
+
return nil unless double_suite
|
30
|
+
|
31
|
+
case double_suite.downcase.strip
|
32
|
+
when 'rspec'
|
33
|
+
['::RSpec::Mocks::Double']
|
34
|
+
when 'minitest'
|
35
|
+
['::Minitest::Mock']
|
36
|
+
else
|
37
|
+
RBS.logger.warn "Unknown test suite - defaults to nil"
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|