@@ -254,19 +254,161 @@ def h.to_hash; {:b => 2, :c => 3}; end
254254 eval ( '{a: 1, :b => 2, "c" => 3, "d": 4, e: 5}' ) . should == h
255255 end
256256
257- it "works with methods and local vars" do
258- a = Class . new
259- a . class_eval ( <<-RUBY )
260- def bar
261- "baz"
262- end
257+ # Copied from Prism::Translation::Ripper
258+ keywords = [
259+ "alias" ,
260+ "and" ,
261+ "begin" ,
262+ "BEGIN" ,
263+ "break" ,
264+ "case" ,
265+ "class" ,
266+ "def" ,
267+ "defined?" ,
268+ "do" ,
269+ "else" ,
270+ "elsif" ,
271+ "end" ,
272+ "END" ,
273+ "ensure" ,
274+ "false" ,
275+ "for" ,
276+ "if" ,
277+ "in" ,
278+ "module" ,
279+ "next" ,
280+ "nil" ,
281+ "not" ,
282+ "or" ,
283+ "redo" ,
284+ "rescue" ,
285+ "retry" ,
286+ "return" ,
287+ "self" ,
288+ "super" ,
289+ "then" ,
290+ "true" ,
291+ "undef" ,
292+ "unless" ,
293+ "until" ,
294+ "when" ,
295+ "while" ,
296+ "yield" ,
297+ "__ENCODING__" ,
298+ "__FILE__" ,
299+ "__LINE__"
300+ ]
301+
302+ invalid_kw_param_names = [
303+ "BEGIN" ,
304+ "END" ,
305+ "defined?" ,
306+ ]
307+
308+ invalid_method_names = [
309+ "BEGIN" ,
310+ "END" ,
311+ "defined?" ,
312+ ]
313+
314+ # Evaluates the given Ruby source in a temporary Module, to prevent
315+ # the surrounding context from being polluted with the new methods.
316+ def sandboxed_eval ( ruby_src )
317+ Module
318+ # Allows instance methods defined by `ruby_src` to be called directly.
319+ . new { extend self }
320+ . class_eval ( ruby_src )
321+ end
263322
264- def foo(val)
265- {bar:, val:}
266- end
323+ it "can reference local variables" do
324+ a = 1
325+ b = 2
326+
327+ eval ( '{ a:, b: }.should == { a: 1, b: 2 }' )
328+ end
329+
330+ it "cannot find dynamically defined local variables" do
331+ b = binding
332+ b . local_variable_set ( :abc , "a dynamically defined local var" )
333+
334+ eval <<~RUBY
335+ # The local variable definitely exists:
336+ b.local_variable_get(:abc).should == "a dynamically defined local var"
337+ # but we can't get it via value omission:
338+ -> { { abc: } }.should raise_error(NameError)
267339 RUBY
340+ end
341+
342+ it "can call methods" do
343+ result = sandboxed_eval <<~RUBY
344+ def m = "a statically defined method"
268345
269- a . new . foo ( 1 ) . should == { bar : "baz" , val : 1 }
346+ { m: }
347+ RUBY
348+
349+ result . should == { m : "a statically defined method" }
350+ end
351+
352+ it "can find dynamically defined methods" do
353+ result = sandboxed_eval <<~RUBY
354+ define_method(:m) { "a dynamically defined method" }
355+
356+ { m: }
357+ RUBY
358+
359+ result . should == { m : "a dynamically defined method" }
360+ end
361+
362+ it "prefers local variables over methods" do
363+ result = sandboxed_eval <<~RUBY
364+ x = "from a local var"
365+ def x; "from a method"; end
366+ { x: }
367+ RUBY
368+
369+ result . should == { x : "from a local var" }
370+ end
371+
372+ describe "handling keywords" do
373+ keywords . each do |kw |
374+ describe "keyword '#{ kw } '" do
375+ # None of these keywords can be used as local variables,
376+ # so it's not possible to resolve them via shorthand Hash syntax.
377+ # See `reserved_keywords.rb`
378+
379+ unless invalid_kw_param_names . include? ( kw )
380+ it "can be used a keyword parameter name" do
381+ result = sandboxed_eval <<~RUBY
382+ def m(#{ kw } :) = { #{ kw } : }
383+
384+ m(#{ kw } : "an argument to '#{ kw } '")
385+ RUBY
386+
387+ result . should == { kw . to_sym => "an argument to '#{ kw } '" }
388+ end
389+ end
390+
391+ unless invalid_method_names . include? ( kw )
392+ it "can refer to a method called '#{ kw } '" do
393+ result = sandboxed_eval <<~RUBY
394+ def #{ kw } = "a method named '#{ kw } '"
395+
396+ { #{ kw } : }
397+ RUBY
398+
399+ result . should == { kw . to_sym => "a method named '#{ kw } '" }
400+ end
401+ end
402+ end
403+ end
404+
405+ describe "keyword 'self:'" do
406+ it "does not refer to actual 'self'" do
407+ eval <<~RUBY
408+ -> { { self: } }.should raise_error(NameError)
409+ RUBY
410+ end
411+ end
270412 end
271413
272414 it "raises a SyntaxError when the Hash key ends with `!`" do
0 commit comments