rbs 0.7.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -325,7 +325,6 @@ module RBS
325
325
  comment: member.comment,
326
326
  overload: member.overload?,
327
327
  annotations: member.annotations,
328
- attributes: member.attributes,
329
328
  location: member.location
330
329
  )
331
330
  when AST::Members::AttrAccessor
@@ -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 = nil, immediate: true, &block)
78
+ def each_signature(path, immediate: true, &block)
79
79
  if block_given?
80
- if path
81
- case
82
- when path.file?
83
- if path.extname == ".rbs" || immediate
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
- else
92
- paths.each do |path|
93
- case path
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 no_builtin!
109
- @no_builtin = true
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 load(env:)
117
- signature_files = []
117
+ def each_decl
118
+ if block_given?
119
+ signature_files = []
118
120
 
119
- unless no_builtin?
120
- signature_files.push(*each_signature(stdlib_root + "builtin"))
121
- end
121
+ unless no_builtin?
122
+ each_signature(stdlib_root + "builtin") do |path|
123
+ signature_files << [:stdlib, path]
124
+ end
125
+ end
122
126
 
123
- each_signature do |path|
124
- signature_files.push path
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
- signature_files.each do |file|
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
- Parser.parse_signature(buffer).each do |decl|
131
- env << decl
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
@@ -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 &&
@@ -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 kSUPER
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: { result = nil } | kOVERLOAD
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: types,
458
+ types: val[6],
445
459
  annotations: val[0],
446
460
  location: location,
447
- comment: leading_comment(val[0].first&.location || val[1].first&.location || val[2]&.location || val[3].location),
448
- attributes: val[1].map(&:value),
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
- result = val[0].push(val[1])
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
- | kSUPER { result = [LocatedValue.new(value: :super, location: val[0].location)] }
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 | kSUPER | kALIAS | kOUT | kIN | kOVERLOAD
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
- new_comment = AST::Comment.new(string: string+"\n", location: location)
1154
-
1155
- if (prev_comment = leading_comment(location)) && prev_comment.location.start_column == location.start_column
1156
- @comments.delete prev_comment.location.end_line
1157
- new_comment = AST::Comment.new(string: prev_comment.string + new_comment.string,
1158
- location: prev_comment.location + new_comment.location)
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,
@@ -130,7 +130,6 @@ module RBS
130
130
  types: types,
131
131
  kind: kind,
132
132
  comment: comments[node.first_lineno - 1],
133
- attributes: [],
134
133
  overload: false
135
134
  )
136
135
 
@@ -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
@@ -5,6 +5,7 @@ require "rbs/test/errors"
5
5
  require "rbs/test/type_check"
6
6
  require "rbs/test/tester"
7
7
  require "rbs/test/hook"
8
+ require "rbs/test/setup_helper"
8
9
 
9
10
  module RBS
10
11
  module Test
@@ -28,7 +28,11 @@ module RBS
28
28
  end
29
29
 
30
30
  def self.inspect_(obj)
31
- Test::INSPECT.bind(obj).call
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)
@@ -7,6 +7,7 @@ module RBS
7
7
  OPERATORS = {
8
8
  :== => "eqeq",
9
9
  :=== => "eqeqeq",
10
+ :!= => "noteq",
10
11
  :+ => "plus",
11
12
  :- => "minus",
12
13
  :* => "star",
@@ -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("RBS_TEST_TARGET").split(",")
12
- skips = (ENV["RBS_TEST_SKIP"] || "").split(",")
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
- rescue
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] RBS_TEST_NO_SAMPLE: if set, the type checker tests all the values of a collection"
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
- name.start_with?(filter[0, filter.size - 1]) || name == filter[0, filter.size-3]
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
- factory = RBS::Factory.new()
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.name&.yield_self {|name| factory.type_name(name).absolute! }
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 tester.checkers.none? {|hook| hook.klass == tp.self }
50
- if env.class_decls.key?(class_name)
51
- logger.info "Setting up hooks for #{class_name}"
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