sorbet 0.5.5841

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.
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ module Sorbet::Private::RealStdlib
5
+ def self.real_is_a?(o, klass)
6
+ @real_is_a ||= Object.instance_method(:is_a?)
7
+ @real_is_a.bind(o).call(klass)
8
+ end
9
+
10
+ def self.real_constants(mod)
11
+ @real_constants ||= Module.instance_method(:constants)
12
+ @real_constants.bind(mod).call(false)
13
+ end
14
+
15
+ def self.real_object_id(o)
16
+ @real_object_id ||= Object.instance_method(:object_id)
17
+ @real_object_id.bind(o).call
18
+ end
19
+
20
+ def self.real_name(o)
21
+ @real_name ||= Module.instance_method(:name)
22
+ @real_name.bind(o).call
23
+ end
24
+
25
+ def self.real_ancestors(mod)
26
+ @real_ancestors ||= Module.instance_method(:ancestors)
27
+ @real_ancestors.bind(mod).call
28
+ end
29
+
30
+ def self.real_instance_methods(mod, arg)
31
+ @real_instance_methods ||= Module.instance_method(:instance_methods)
32
+ @real_instance_methods.bind(mod).call(arg)
33
+ end
34
+
35
+ def self.real_singleton_methods(mod, arg)
36
+ @real_singleton_methods ||= Module.instance_method(:singleton_methods)
37
+ @real_singleton_methods.bind(mod).call(arg)
38
+ end
39
+
40
+ def self.real_private_instance_methods(mod, arg)
41
+ @real_private_instance_methods ||= Module.instance_method(:private_instance_methods)
42
+ @real_private_instance_methods.bind(mod).call(arg)
43
+ end
44
+
45
+ def self.real_singleton_class(obj)
46
+ @real_singleton_class ||= Object.instance_method(:singleton_class)
47
+ @real_singleton_class.bind(obj).call
48
+ end
49
+
50
+ def self.real_spaceship(obj, arg)
51
+ @real_spaceship ||= Object.instance_method(:<=>)
52
+ @real_spaceship.bind(obj).call(arg)
53
+ end
54
+
55
+ def self.real_hash(o)
56
+ @real_hash ||= Object.instance_method(:hash)
57
+ @real_hash.bind(o).call
58
+ end
59
+
60
+ def self.real_superclass(o)
61
+ @real_superclass ||= Class.instance_method(:superclass)
62
+ @real_superclass.bind(o).call
63
+ end
64
+
65
+ def self.real_eqeq(obj, other)
66
+ @real_eqeq ||= Object.instance_method(:==)
67
+ @real_eqeq.bind(obj).call(other)
68
+ end
69
+
70
+ def self.real_autoload?(o, klass)
71
+ @real_autoload ||= Object.instance_method(:autoload?)
72
+ @real_autoload.bind(o).call(klass)
73
+ end
74
+
75
+ def self.real_const_get(obj, const, arg)
76
+ @real_const_get ||= Object.singleton_class.instance_method(:const_get)
77
+ @real_const_get.bind(obj).call(const, arg)
78
+ end
79
+
80
+ def self.real_method(obj, sym)
81
+ @real_method ||= Object.instance_method(:method)
82
+ @real_method.bind(obj).call(sym)
83
+ end
84
+ end
@@ -0,0 +1,181 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ require 'pathname'
5
+
6
+ require_relative './gem_loader'
7
+ require_relative './status'
8
+
9
+ class ExitCalledError < RuntimeError
10
+ end
11
+
12
+ class Sorbet::Private::RequireEverything
13
+ # Goes through the most common ways to require all your userland code
14
+ def self.require_everything
15
+ return if @already_ran
16
+ @already_ran = true
17
+ patch_kernel
18
+ load_rails
19
+ load_bundler # this comes second since some rails projects fail `Bundler.require' before rails is loaded
20
+ require_all_files
21
+ end
22
+
23
+ def self.load_rails
24
+ return unless rails?
25
+ require './config/application'
26
+ rails = Object.const_get(:Rails)
27
+ rails.application.require_environment!
28
+ rails.application.eager_load!
29
+ true
30
+ end
31
+
32
+ def self.load_bundler
33
+ return unless File.exist?('Gemfile')
34
+ begin
35
+ require 'bundler'
36
+ rescue LoadError
37
+ return
38
+ end
39
+ Sorbet::Private::GemLoader.require_all_gems
40
+ end
41
+
42
+ def self.require_all_files
43
+ excluded_paths = Set.new
44
+ excluded_paths += excluded_rails_files if rails?
45
+
46
+ abs_paths = rb_file_paths
47
+ errors = []
48
+ abs_paths.each_with_index do |abs_path, i|
49
+ # Executable files are likely not meant to be required.
50
+ # Some things we're trying to prevent against:
51
+ # - misbehaving require-time side effects (removing files, reading from stdin, etc.)
52
+ # - extra long runtime (making network requests, running a benchmark)
53
+ # While this isn't a perfect heuristic for these things, it's pretty good.
54
+ next if File.executable?(abs_path)
55
+ next if excluded_paths.include?(abs_path)
56
+
57
+ # Skip db/schema.rb, as requiring it can wipe the database. This is left
58
+ # out of exclude_rails_files, as it is possible to use the packages that
59
+ # generate it without using the whole rails ecosystem.
60
+ next if /db\/schema.rb$/.match(abs_path)
61
+
62
+ # Skip **/extconf.rb, as running it will emit build configuration artifacts
63
+ next if /\/extconf.rb$/.match(abs_path)
64
+
65
+ begin
66
+ my_require(abs_path, i+1, abs_paths.size)
67
+ rescue LoadError, NoMethodError, SyntaxError
68
+ next
69
+ rescue
70
+ errors << abs_path
71
+ next
72
+ end
73
+ end
74
+ # one more chance for order dependent things
75
+ errors.each_with_index do |abs_path, i|
76
+ begin
77
+ my_require(abs_path, i+1, errors.size)
78
+ rescue
79
+ end
80
+ end
81
+
82
+ Sorbet::Private::Status.done
83
+ end
84
+
85
+ def self.my_require(abs_path, numerator, denominator)
86
+ rel_path = Pathname.new(abs_path).relative_path_from(Pathname.new(Dir.pwd)).to_s
87
+ Sorbet::Private::Status.say("[#{numerator}/#{denominator}] require_relative './#{rel_path}'")
88
+ require_relative abs_path
89
+ end
90
+
91
+ def self.patch_kernel
92
+ Kernel.send(:define_method, :exit) do |*|
93
+ puts 'Kernel#exit was called while requiring ruby source files'
94
+ raise ExitCalledError.new
95
+ end
96
+
97
+ Kernel.send(:define_method, :at_exit) do |&block|
98
+ if File.split($0).last == 'rake'
99
+ # Let `rake test` work
100
+ super
101
+ return proc {}
102
+ end
103
+ # puts "Ignoring at_exit: #{block}"
104
+ proc {}
105
+ end
106
+ end
107
+
108
+ private
109
+
110
+ def self.rb_file_paths
111
+ srb = File.realpath("#{__dir__}/../bin/srb")
112
+ output = IO.popen([
113
+ srb,
114
+ "tc",
115
+ "-p",
116
+ "file-table-json",
117
+ "--stop-after=parser",
118
+ "--silence-dev-message",
119
+ "--no-error-count",
120
+ "-e",
121
+ "''",
122
+ ]) {|io| io.read}
123
+ # This returns a hash with structure:
124
+ # { files:
125
+ # [
126
+ # {
127
+ # "strict": ["Ignore"|"False"|"True"|"Strict"|"Strong"|"Stdlib"],
128
+ # "path": "./path/to/file",
129
+ # ...
130
+ # }
131
+ # ...
132
+ # ]
133
+ # }
134
+ parsed = JSON.parse(output)
135
+ parsed
136
+ .fetch('files', [])
137
+ .reject{|file| ["Ignore", "Stdlib"].include?(file["strict"])}
138
+ .map{|file| file["path"]}
139
+ .select{|path| File.file?(path)} # Some files have https:// paths. We ignore those here.
140
+ .select{|path| /.rb$/.match(path)}
141
+ .map{|path| File.expand_path(path)} # Requires absolute path
142
+ end
143
+
144
+ def self.excluded_rails_files
145
+ excluded_paths = Set.new
146
+
147
+ # Exclude files that have already been loaded by rails
148
+ self.rails_load_paths.each do |path|
149
+ excluded_paths += Dir.glob("#{path}/**/*.rb")
150
+ end
151
+
152
+ # Exclude initializers, as they have already been run by rails and
153
+ # can contain side-effects like monkey-patching that should
154
+ # only be run once.
155
+ excluded_paths += Dir.glob("#{Dir.pwd}/config/initializers/**/*.rb")
156
+ end
157
+
158
+ def self.rails_load_paths
159
+ rails = Object.const_get(:Rails)
160
+
161
+ # As per changes made to change the arity of this method:
162
+ # https://github.com/rails/rails/commit/b6e17b6a4b67ccc9fac5fe16741c3db720f00959
163
+ # This sets the `add_autoload_paths_to_load_path` parameter to `true` which will
164
+ # provide parity with older versions of Rails prior to the mentioned commit.
165
+ if Gem::Version.new(rails.version) >= Gem::Version.new('6.0.0.rc2')
166
+ rails.application.send(:_all_load_paths, true)
167
+ else
168
+ rails.application.send(:_all_load_paths)
169
+ end
170
+ end
171
+
172
+ def self.rails?
173
+ return false unless File.exist?('config/application.rb')
174
+ begin
175
+ require 'rails'
176
+ rescue LoadError
177
+ return false
178
+ end
179
+ true
180
+ end
181
+ end
@@ -0,0 +1,362 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ require 'bigdecimal'
5
+
6
+ class Sorbet::Private::Serialize
7
+ BLACKLIST_CONSTANTS = [
8
+ ['DidYouMean', :NameErrorCheckers], # https://github.com/yuki24/did_you_mean/commit/b72fdbe194401f1be21f8ad7b6e3f784a0ad197d
9
+ ['Net', :OpenSSL], # https://github.com/yuki24/did_you_mean/commit/b72fdbe194401f1be21f8ad7b6e3f784a0ad197d
10
+ ]
11
+
12
+ SPECIAL_METHOD_NAMES = %w[! ~ +@ ** -@ * / % + - << >> & | ^ < <= => > >= == === != =~ !~ <=> [] []= `]
13
+
14
+ def initialize(constant_cache)
15
+ @constant_cache = constant_cache
16
+ end
17
+
18
+ private def get_constants(mod, inherited=nil)
19
+ @real_constants ||= Module.instance_method(:constants)
20
+ if inherited.nil?
21
+ @real_constants.bind(mod).call
22
+ else
23
+ @real_constants.bind(mod).call(inherited)
24
+ end
25
+ end
26
+
27
+ def self.header(typed="true", subcommand="update")
28
+ buffer = []
29
+ buffer << "# This file is autogenerated. Do not edit it by hand. Regenerate it with:"
30
+ buffer << "# srb rbi #{subcommand}"
31
+ if typed
32
+ buffer << ""
33
+ buffer << "# typed: #{typed}"
34
+ end
35
+ buffer << ""
36
+ buffer.join("\n")
37
+ end
38
+
39
+ def class_or_module(class_name)
40
+ if !valid_class_name(class_name)
41
+ return " # Skipping serializing #{class_name} because it is an invalid name\n"
42
+ end
43
+
44
+ klass = @constant_cache.class_by_name(class_name)
45
+ is_nil = nil.equal?(klass)
46
+ raise "#{class_name} is not a Class or Module. Maybe it was miscategorized?" if is_nil
47
+
48
+ ret = String.new
49
+
50
+ superclass = Sorbet::Private::RealStdlib.real_is_a?(klass, Class) ? Sorbet::Private::RealStdlib.real_superclass(klass) : nil
51
+ if superclass
52
+ superclass_str = Sorbet::Private::RealStdlib.real_eqeq(superclass, Object) ? '' : @constant_cache.name_by_class(superclass)
53
+ else
54
+ superclass_str = ''
55
+ end
56
+ superclass_str = !superclass_str || superclass_str.empty? ? '' : " < #{superclass_str}"
57
+ ret << (Sorbet::Private::RealStdlib.real_is_a?(klass, Class) ? "class #{class_name}#{superclass_str}\n" : "module #{class_name}\n")
58
+
59
+ # We don't use .included_modules since that also has all the aweful things
60
+ # that are mixed into Object. This way we at least have a delimiter before
61
+ # the awefulness starts (the superclass).
62
+ Sorbet::Private::RealStdlib.real_ancestors(klass).each do |ancestor|
63
+ next if Sorbet::Private::RealStdlib.real_eqeq(ancestor, klass)
64
+ break if Sorbet::Private::RealStdlib.real_eqeq(ancestor, superclass)
65
+ ancestor_name = @constant_cache.name_by_class(ancestor)
66
+ next unless ancestor_name
67
+ next if ancestor_name == class_name
68
+ if Sorbet::Private::RealStdlib.real_is_a?(ancestor, Class)
69
+ ret << " # Skipping `include #{ancestor_name}` because it is a Class\n"
70
+ next
71
+ end
72
+ if !valid_class_name(ancestor_name)
73
+ ret << " # Skipping `include #{ancestor_name}` because it is an invalid name\n"
74
+ next
75
+ end
76
+ ret << " include ::#{ancestor_name}\n"
77
+ end
78
+ singleton_class = Sorbet::Private::RealStdlib.real_singleton_class(klass)
79
+ Sorbet::Private::RealStdlib.real_ancestors(singleton_class).each do |ancestor|
80
+ next if ancestor == Sorbet::Private::RealStdlib.real_singleton_class(klass)
81
+ break if superclass && ancestor == Sorbet::Private::RealStdlib.real_singleton_class(superclass)
82
+ break if ancestor == Module
83
+ break if ancestor == Object
84
+ ancestor_name = @constant_cache.name_by_class(ancestor)
85
+ next unless ancestor_name
86
+ if Sorbet::Private::RealStdlib.real_is_a?(ancestor, Class)
87
+ ret << " # Skipping `extend #{ancestor_name}` because it is a Class\n"
88
+ next
89
+ end
90
+ if !valid_class_name(ancestor_name)
91
+ ret << " # Skipping `extend #{ancestor_name}` because it is an invalid name\n"
92
+ next
93
+ end
94
+ ret << " extend ::#{ancestor_name}\n"
95
+ end
96
+
97
+ constants = []
98
+ # Declare all the type_members and type_templates
99
+ constants += get_constants(klass).uniq.map do |const_sym|
100
+ # We have to not pass `false` because `klass.constants` intentionally is
101
+ # pulling in all the ancestor constants
102
+ next if Sorbet::Private::ConstantLookupCache::DEPRECATED_CONSTANTS.include?("#{class_name}::#{const_sym}")
103
+ begin
104
+ value = klass.const_get(const_sym)
105
+ rescue LoadError, NameError, RuntimeError, ArgumentError => err
106
+ ret << "# Got #{err.class} when trying to get class constant symbol #{class_name}::#{const_sym}\n"
107
+ next
108
+ end
109
+ # next if !Sorbet::Private::RealStdlib.real_is_a?(value, T::Types::TypeVariable)
110
+ next if Sorbet::Private::RealStdlib.real_is_a?(value, Module)
111
+ next if !comparable?(value)
112
+ [const_sym, value]
113
+ end
114
+ constants += get_constants(klass, false).uniq.map do |const_sym|
115
+ next if BLACKLIST_CONSTANTS.include?([class_name, const_sym])
116
+ next if Sorbet::Private::ConstantLookupCache::DEPRECATED_CONSTANTS.include?("#{class_name}::#{const_sym}")
117
+ begin
118
+ value = klass.const_get(const_sym, false)
119
+ rescue LoadError, NameError, RuntimeError, ArgumentError => err
120
+ ret << "# Got #{err.class} when trying to get class constant symbol #{class_name}::#{const_sym}_\n"
121
+ next
122
+ end
123
+ next if Sorbet::Private::RealStdlib.real_is_a?(value, Module)
124
+ next if !comparable?(value)
125
+ [const_sym, value]
126
+ end
127
+ constants_sorted = constants.compact.sort_by do |const_sym, _value|
128
+ const_sym
129
+ end
130
+ constants_uniq = constants_sorted.uniq do |const_sym, _value|
131
+ const_sym.hash
132
+ end
133
+ constants_serialized = constants_uniq.map do |const_sym, value|
134
+ constant(const_sym, value)
135
+ end
136
+ ret << constants_serialized.join("\n")
137
+ ret << "\n\n" if !constants_serialized.empty?
138
+
139
+ methods = []
140
+ instance_methods = Sorbet::Private::RealStdlib.real_instance_methods(klass, false)
141
+ begin
142
+ initialize = klass.instance_method(:initialize)
143
+ rescue
144
+ initialize = nil
145
+ end
146
+ if initialize && initialize.owner == klass
147
+ # This method never apears in the reflection list...
148
+ instance_methods += [:initialize]
149
+ end
150
+ Sorbet::Private::RealStdlib.real_ancestors(klass).reject {|ancestor| @constant_cache.name_by_class(ancestor)}.each do |ancestor|
151
+ instance_methods += ancestor.instance_methods(false)
152
+ end
153
+
154
+ # uniq here is required because we populate additional methos from anonymous superclasses and there
155
+ # might be duplicates
156
+ methods += instance_methods.sort.uniq.map do |method_sym|
157
+ begin
158
+ method = klass.instance_method(method_sym)
159
+ rescue => e
160
+ ret << "# #{e}\n"
161
+ next
162
+ end
163
+ next if blacklisted_method(method)
164
+ next if ancestor_has_method(method, klass)
165
+ serialize_method(method)
166
+ end
167
+ # uniq is not required here, but added to be on the safe side
168
+ methods += Sorbet::Private::RealStdlib.real_singleton_methods(klass, false).sort.uniq.map do |method_sym|
169
+ begin
170
+ method = klass.singleton_method(method_sym)
171
+ rescue => e
172
+ ret << "# #{e}\n"
173
+ next
174
+ end
175
+ next if blacklisted_method(method)
176
+ next if ancestor_has_method(method, Sorbet::Private::RealStdlib.real_singleton_class(klass))
177
+ serialize_method(method, true)
178
+ end
179
+ ret << methods.join("\n")
180
+ ret << "end\n"
181
+
182
+ ret
183
+ end
184
+
185
+ def alias(base, other_name)
186
+ ret = String.new
187
+ ret << "#{other_name} = #{base}"
188
+ ret
189
+ end
190
+
191
+ def comparable?(value)
192
+ return false if Sorbet::Private::RealStdlib.real_is_a?(value, BigDecimal) && value.nan?
193
+ return false if Sorbet::Private::RealStdlib.real_is_a?(value, Float) && value.nan?
194
+ return false if Sorbet::Private::RealStdlib.real_is_a?(value, Complex)
195
+ true
196
+ end
197
+
198
+ def blacklisted_method(method)
199
+ method.name =~ /__validator__[0-9]{8}/ || method.name =~ /.*:.*/
200
+ end
201
+
202
+ def valid_method_name(name)
203
+ return true if SPECIAL_METHOD_NAMES.include?(name)
204
+ return false if name =~ /^\d/
205
+ name =~ /^[[:word:]]+[?!=]?$/
206
+ end
207
+
208
+ def ancestor_has_method(method, klass)
209
+ return false if !Sorbet::Private::RealStdlib.real_is_a?(klass, Class)
210
+ first_ancestor = klass.ancestors.find do |ancestor|
211
+ next if ancestor == klass
212
+ begin
213
+ ancestor.instance_method(method.name)
214
+ rescue NameError
215
+ nil
216
+ end
217
+ end
218
+ return false unless first_ancestor
219
+ first_ancestor.instance_method(method.name).parameters == method.parameters
220
+ end
221
+
222
+ def constant(const, value)
223
+ if KEYWORDS.include?(const.to_sym)
224
+ return "# Illegal constant name: #{const}"
225
+ end
226
+ if defined?(T::Types) && Sorbet::Private::RealStdlib.real_is_a?(value, T::Types::TypeMember)
227
+ value.variance == :invariant ? " #{const} = type_member" : " #{const} = type_member(#{value.variance.inspect})"
228
+ elsif defined?(T::Types) && Sorbet::Private::RealStdlib.real_is_a?(value, T::Types::TypeTemplate)
229
+ value.variance == :invariant ? " #{const} = type_template" : " #{const} = type_template(#{value.variance.inspect})"
230
+ else
231
+ " #{const} = ::T.let(nil, ::T.untyped)"
232
+ end
233
+ end
234
+
235
+ def serialize_method(method, static=false, with_sig: true)
236
+ name = method.name.to_s
237
+ if !valid_method_name(name)
238
+ return "# Illegal method name: #{name}\n"
239
+ end
240
+ parameters = from_method(method)
241
+ # a hack for appeasing Sorbet in the presence of the Enumerable interface
242
+ if name == 'each' && !parameters.any? {|(kind, _)| kind == :block}
243
+ parameters.push([:block, "blk"])
244
+ end
245
+ ret = String.new
246
+ ret << serialize_sig(parameters) if with_sig
247
+ args = parameters.map do |(kind, param_name)|
248
+ to_sig(kind, param_name)
249
+ end.compact.join(', ')
250
+ ret << " def #{static ? 'self.' : ''}#{name}(#{args}); end\n"
251
+ ret
252
+ end
253
+
254
+ def valid_class_name(name)
255
+ name.split("::").each do |piece|
256
+ return false if piece[0].upcase != piece[0]
257
+ end
258
+ return false if [
259
+ 'Sorbet::Private::GemGeneratorTracepoint::Tracer::ClassOverride',
260
+ 'Sorbet::Private::GemGeneratorTracepoint::Tracer::ModuleOverride',
261
+ 'Sorbet::Private::GemGeneratorTracepoint::Tracer::ObjectOverride',
262
+ ].include?(name)
263
+ true
264
+ end
265
+
266
+ def serialize_sig(parameters)
267
+ ret = String.new
268
+ if !parameters.empty?
269
+ ret << " sig do\n"
270
+ ret << " params(\n"
271
+ parameters.each do |(_kind, name)|
272
+ ret << " #{name}: ::T.untyped,\n"
273
+ end
274
+ ret << " )\n"
275
+ ret << " .returns(::T.untyped)\n"
276
+ ret << " end\n"
277
+ else
278
+ ret << " sig {returns(::T.untyped)}\n"
279
+ end
280
+ ret
281
+ end
282
+
283
+ # from https://docs.ruby-lang.org/en/2.4.0/keywords_rdoc.html
284
+ KEYWORDS = [
285
+ :__ENCODING__,
286
+ :__LINE__,
287
+ :__FILE__,
288
+ :BEGIN,
289
+ :END,
290
+ :alias,
291
+ :and,
292
+ :begin,
293
+ :break,
294
+ :case,
295
+ :class,
296
+ :def,
297
+ :defined?,
298
+ :do,
299
+ :else,
300
+ :elsif,
301
+ :end,
302
+ :ensure,
303
+ :false,
304
+ :for,
305
+ :if,
306
+ :in,
307
+ :module,
308
+ :next,
309
+ :nil,
310
+ :not,
311
+ :or,
312
+ :redo,
313
+ :rescue,
314
+ :retry,
315
+ :return,
316
+ :self,
317
+ :super,
318
+ :then,
319
+ :true,
320
+ :undef,
321
+ :unless,
322
+ :until,
323
+ :when,
324
+ :while,
325
+ :yield,
326
+ ]
327
+
328
+ def from_method(method)
329
+ uniq = 0
330
+ method.parameters.map.with_index do |(kind, name), index|
331
+ if !name
332
+ arg_name = method.name.to_s[0...-1]
333
+ if (!KEYWORDS.include?(arg_name.to_sym)) && method.name.to_s.end_with?('=') && arg_name =~ /\A[a-z_][a-z0-9A-Z_]*\Z/ && index == 0
334
+ name = arg_name
335
+ else
336
+ name = '_' + (uniq == 0 ? '' : uniq.to_s)
337
+ uniq += 1
338
+ end
339
+ end
340
+ [kind, name]
341
+ end
342
+ end
343
+
344
+ def to_sig(kind, name)
345
+ case kind
346
+ when :req
347
+ name.to_s
348
+ when :opt
349
+ "#{name}=T.unsafe(nil)"
350
+ when :rest
351
+ "*#{name}"
352
+ when :keyreq
353
+ "#{name}:"
354
+ when :key
355
+ "#{name}: T.unsafe(nil)"
356
+ when :keyrest
357
+ "**#{name}"
358
+ when :block
359
+ "&#{name}"
360
+ end
361
+ end
362
+ end