diff --git a/lib/parser/context.rb b/lib/parser/context.rb index ea45e190d..359f64a34 100644 --- a/lib/parser/context.rb +++ b/lib/parser/context.rb @@ -5,6 +5,7 @@ module Parser # # Supported states: # + :class - in the class body (class A; end) + # + :module - in the module body (module M; end) # + :sclass - in the singleton class body (class << obj; end) # + :def - in the method body (def m; end) # + :defs - in the singleton method body (def self.m; end) diff --git a/lib/parser/macruby.y b/lib/parser/macruby.y index 400d9d28c..013f40d1e 100644 --- a/lib/parser/macruby.y +++ b/lib/parser/macruby.y @@ -1042,11 +1042,15 @@ rule result = @builder.block(val[0], begin_t, args, body, end_t) } - | tLAMBDA lambda + | tLAMBDA + { + @context.push(:lambda) + } + lambda { lambda_call = @builder.call_lambda(val[0]) - args, (begin_t, body, end_t) = val[1] + args, (begin_t, body, end_t) = val[2] result = @builder.block(lambda_call, begin_t, args, body, end_t) } @@ -1160,6 +1164,7 @@ rule { @static_env.extend_static @lexer.push_cmdarg + @context.push(:module) } bodystmt kEND { @@ -1172,6 +1177,7 @@ rule @lexer.pop_cmdarg @static_env.unextend + @context.pop } | kDEF fname { @@ -1453,9 +1459,13 @@ rule lambda: { @static_env.extend_dynamic } - f_larglist lambda_body + f_larglist + { + @context.pop + } + lambda_body { - result = [ val[1], val[2] ] + result = [ val[1], val[3] ] @static_env.unextend } diff --git a/lib/parser/ruby18.y b/lib/parser/ruby18.y index 065f23b6e..c0c3af443 100644 --- a/lib/parser/ruby18.y +++ b/lib/parser/ruby18.y @@ -1156,6 +1156,7 @@ rule | kMODULE cpath { @static_env.extend_static + @context.push(:module) } bodystmt kEND { @@ -1167,6 +1168,7 @@ rule val[3], val[4]) @static_env.unextend + @context.pop } | kDEF fname { diff --git a/lib/parser/ruby19.y b/lib/parser/ruby19.y index 5220510d5..ae25c005a 100644 --- a/lib/parser/ruby19.y +++ b/lib/parser/ruby19.y @@ -1009,11 +1009,15 @@ rule result = @builder.block(val[0], begin_t, args, body, end_t) } - | tLAMBDA lambda + | tLAMBDA + { + @context.push(:lambda) + } + lambda { lambda_call = @builder.call_lambda(val[0]) - args, (begin_t, body, end_t) = val[1] + args, (begin_t, body, end_t) = val[2] result = @builder.block(lambda_call, begin_t, args, body, end_t) } @@ -1127,6 +1131,7 @@ rule { @static_env.extend_static @lexer.push_cmdarg + @context.push(:module) } bodystmt kEND { @@ -1139,6 +1144,7 @@ rule @lexer.pop_cmdarg @static_env.unextend + @context.pop } | kDEF fname { @@ -1433,9 +1439,13 @@ rule lambda: { @static_env.extend_dynamic } - f_larglist lambda_body + f_larglist + { + @context.pop + } + lambda_body { - result = [ val[1], val[2] ] + result = [ val[1], val[3] ] @static_env.unextend } diff --git a/lib/parser/ruby20.y b/lib/parser/ruby20.y index 1c5422d43..280526172 100644 --- a/lib/parser/ruby20.y +++ b/lib/parser/ruby20.y @@ -1039,11 +1039,15 @@ rule result = @builder.block(val[0], begin_t, args, body, end_t) } - | tLAMBDA lambda + | tLAMBDA + { + @context.push(:lambda) + } + lambda { lambda_call = @builder.call_lambda(val[0]) - args, (begin_t, body, end_t) = val[1] + args, (begin_t, body, end_t) = val[2] result = @builder.block(lambda_call, begin_t, args, body, end_t) } @@ -1157,6 +1161,7 @@ rule { @static_env.extend_static @lexer.push_cmdarg + @context.push(:module) } bodystmt kEND { @@ -1169,6 +1174,7 @@ rule @lexer.pop_cmdarg @static_env.unextend + @context.pop } | kDEF fname { @@ -1487,9 +1493,13 @@ opt_block_args_tail: lambda: { @static_env.extend_dynamic } - f_larglist lambda_body + f_larglist + { + @context.pop + } + lambda_body { - result = [ val[1], val[2] ] + result = [ val[1], val[3] ] @static_env.unextend } diff --git a/lib/parser/ruby21.y b/lib/parser/ruby21.y index 044942e84..618a0a75d 100644 --- a/lib/parser/ruby21.y +++ b/lib/parser/ruby21.y @@ -1029,11 +1029,15 @@ rule result = @builder.block(val[0], begin_t, args, body, end_t) } - | tLAMBDA lambda + | tLAMBDA + { + @context.push(:lambda) + } + lambda { lambda_call = @builder.call_lambda(val[0]) - args, (begin_t, body, end_t) = val[1] + args, (begin_t, body, end_t) = val[2] result = @builder.block(lambda_call, begin_t, args, body, end_t) } @@ -1147,6 +1151,7 @@ rule { @static_env.extend_static @lexer.push_cmdarg + @context.push(:module) } bodystmt kEND { @@ -1159,6 +1164,7 @@ rule @lexer.pop_cmdarg @static_env.unextend + @context.pop } | kDEF fname { @@ -1471,6 +1477,7 @@ opt_block_args_tail: { result = @lexer.cmdarg.dup @lexer.cmdarg.clear + @context.pop } lambda_body { diff --git a/lib/parser/ruby22.y b/lib/parser/ruby22.y index c0b9b3374..e88cff779 100644 --- a/lib/parser/ruby22.y +++ b/lib/parser/ruby22.y @@ -1028,11 +1028,15 @@ rule result = @builder.block(val[0], begin_t, args, body, end_t) } - | tLAMBDA lambda + | tLAMBDA + { + @context.push(:lambda) + } + lambda { lambda_call = @builder.call_lambda(val[0]) - args, (begin_t, body, end_t) = val[1] + args, (begin_t, body, end_t) = val[2] result = @builder.block(lambda_call, begin_t, args, body, end_t) } @@ -1146,6 +1150,7 @@ rule { @static_env.extend_static @lexer.push_cmdarg + @context.push(:module) } bodystmt kEND { @@ -1158,6 +1163,7 @@ rule @lexer.pop_cmdarg @static_env.unextend + @context.pop } | kDEF fname { @@ -1470,6 +1476,7 @@ opt_block_args_tail: { result = @lexer.cmdarg.dup @lexer.cmdarg.clear + @context.pop } lambda_body { diff --git a/lib/parser/ruby23.y b/lib/parser/ruby23.y index 7f92df8f7..d62457c54 100644 --- a/lib/parser/ruby23.y +++ b/lib/parser/ruby23.y @@ -1028,11 +1028,15 @@ rule result = @builder.block(val[0], begin_t, args, body, end_t) } - | tLAMBDA lambda + | tLAMBDA + { + @context.push(:lambda) + } + lambda { lambda_call = @builder.call_lambda(val[0]) - args, (begin_t, body, end_t) = val[1] + args, (begin_t, body, end_t) = val[2] result = @builder.block(lambda_call, begin_t, args, body, end_t) } @@ -1146,6 +1150,7 @@ rule { @static_env.extend_static @lexer.push_cmdarg + @context.push(:module) } bodystmt kEND { @@ -1158,6 +1163,7 @@ rule @lexer.pop_cmdarg @static_env.unextend + @context.pop } | kDEF fname { @@ -1470,6 +1476,7 @@ opt_block_args_tail: { result = @lexer.cmdarg.dup @lexer.cmdarg.clear + @context.pop } lambda_body { diff --git a/lib/parser/ruby24.y b/lib/parser/ruby24.y index 969ea3957..8069b4272 100644 --- a/lib/parser/ruby24.y +++ b/lib/parser/ruby24.y @@ -1047,11 +1047,15 @@ rule result = @builder.block(val[0], begin_t, args, body, end_t) } - | tLAMBDA lambda + | tLAMBDA + { + @context.push(:lambda) + } + lambda { lambda_call = @builder.call_lambda(val[0]) - args, (begin_t, body, end_t) = val[1] + args, (begin_t, body, end_t) = val[2] result = @builder.block(lambda_call, begin_t, args, body, end_t) } @@ -1165,6 +1169,7 @@ rule { @static_env.extend_static @lexer.cmdarg.push(false) + @context.push(:module) } bodystmt kEND { @@ -1177,6 +1182,7 @@ rule @lexer.cmdarg.pop @static_env.unextend + @context.pop } | kDEF fname { @@ -1487,6 +1493,7 @@ opt_block_args_tail: } f_larglist { + @context.pop @lexer.cmdarg.push(false) } lambda_body diff --git a/lib/parser/ruby25.y b/lib/parser/ruby25.y index e87a463c5..ee798ee63 100644 --- a/lib/parser/ruby25.y +++ b/lib/parser/ruby25.y @@ -1057,11 +1057,15 @@ rule result = @builder.block(val[0], begin_t, args, body, end_t) } - | tLAMBDA lambda + | tLAMBDA + { + @context.push(:lambda) + } + lambda { lambda_call = @builder.call_lambda(val[0]) - args, (begin_t, body, end_t) = val[1] + args, (begin_t, body, end_t) = val[2] result = @builder.block(lambda_call, begin_t, args, body, end_t) } @@ -1151,6 +1155,7 @@ rule { @static_env.extend_static @lexer.cmdarg.push(false) + @context.push(:module) } bodystmt kEND { @@ -1163,6 +1168,7 @@ rule @lexer.cmdarg.pop @static_env.unextend + @context.pop } | kDEF fname { @@ -1484,6 +1490,7 @@ opt_block_args_tail: } f_larglist { + @context.pop @lexer.cmdarg.push(false) } lambda_body diff --git a/lib/parser/ruby26.y b/lib/parser/ruby26.y index 92c8a61eb..a2bb7d2c6 100644 --- a/lib/parser/ruby26.y +++ b/lib/parser/ruby26.y @@ -1065,11 +1065,15 @@ rule result = @builder.block(val[0], begin_t, args, body, end_t) } - | tLAMBDA lambda + | tLAMBDA + { + @context.push(:lambda) + } + lambda { lambda_call = @builder.call_lambda(val[0]) - args, (begin_t, body, end_t) = val[1] + args, (begin_t, body, end_t) = val[2] result = @builder.block(lambda_call, begin_t, args, body, end_t) } @@ -1159,6 +1163,7 @@ rule { @static_env.extend_static @lexer.cmdarg.push(false) + @context.push(:module) } bodystmt kEND { @@ -1171,6 +1176,7 @@ rule @lexer.cmdarg.pop @static_env.unextend + @context.pop } | kDEF fname { @@ -1492,6 +1498,7 @@ opt_block_args_tail: } f_larglist { + @context.pop @lexer.cmdarg.push(false) } lambda_body diff --git a/lib/parser/ruby27.y b/lib/parser/ruby27.y index d6f662e28..a278b4d17 100644 --- a/lib/parser/ruby27.y +++ b/lib/parser/ruby27.y @@ -1109,11 +1109,15 @@ rule result = @builder.block(val[0], begin_t, args, body, end_t) } - | tLAMBDA lambda + | tLAMBDA + { + @context.push(:lambda) + } + lambda { lambda_call = @builder.call_lambda(val[0]) - args, (begin_t, body, end_t) = val[1] + args, (begin_t, body, end_t) = val[2] result = @builder.block(lambda_call, begin_t, args, body, end_t) } @@ -1211,6 +1215,7 @@ rule { @static_env.extend_static @lexer.cmdarg.push(false) + @context.push(:module) } bodystmt kEND { @@ -1223,6 +1228,7 @@ rule @lexer.cmdarg.pop @static_env.unextend + @context.pop } | kDEF fname { @@ -1538,7 +1544,6 @@ opt_block_args_tail: lambda: { @static_env.extend_dynamic @max_numparam_stack.push - @context.push(:lambda) } f_larglist { @@ -2089,11 +2094,15 @@ opt_block_args_tail: { result = @builder.accessible(val[0]) } - | tLAMBDA lambda + | tLAMBDA + { + @context.push(:lambda) + } + lambda { lambda_call = @builder.call_lambda(val[0]) - args, (begin_t, body, end_t) = val[1] + args, (begin_t, body, end_t) = val[2] result = @builder.block(lambda_call, begin_t, args, body, end_t) } diff --git a/lib/parser/ruby28.y b/lib/parser/ruby28.y index 6e622081d..c91836ecb 100644 --- a/lib/parser/ruby28.y +++ b/lib/parser/ruby28.y @@ -1307,6 +1307,7 @@ rule { @static_env.extend_static @lexer.cmdarg.push(false) + @context.push(:module) } bodystmt kEND { @@ -1319,6 +1320,7 @@ rule @lexer.cmdarg.pop @static_env.unextend + @context.pop } | defn_head f_arglist bodystmt kEND { diff --git a/lib/parser/rubymotion.y b/lib/parser/rubymotion.y index 08517fd5b..a56986132 100644 --- a/lib/parser/rubymotion.y +++ b/lib/parser/rubymotion.y @@ -1018,11 +1018,15 @@ rule result = @builder.block(val[0], begin_t, args, body, end_t) } - | tLAMBDA lambda + | tLAMBDA + { + @context.push(:lambda) + } + lambda { lambda_call = @builder.call_lambda(val[0]) - args, (begin_t, body, end_t) = val[1] + args, (begin_t, body, end_t) = val[2] result = @builder.block(lambda_call, begin_t, args, body, end_t) } @@ -1136,6 +1140,7 @@ rule { @static_env.extend_static @lexer.push_cmdarg + @context.push(:module) } bodystmt kEND { @@ -1148,6 +1153,7 @@ rule @lexer.pop_cmdarg @static_env.unextend + @context.pop } | kDEF fname { @@ -1429,9 +1435,13 @@ rule lambda: { @static_env.extend_dynamic } - f_larglist lambda_body + f_larglist + { + @context.pop + } + lambda_body { - result = [ val[1], val[2] ] + result = [ val[1], val[3] ] @static_env.unextend } diff --git a/parser.gemspec b/parser.gemspec index fe30ebd9e..f9546469b 100644 --- a/parser.gemspec +++ b/parser.gemspec @@ -42,7 +42,7 @@ Gem::Specification.new do |spec| spec.required_ruby_version = '>= 2.0.0' - spec.add_dependency 'ast', '~> 2.4.0' + spec.add_dependency 'ast', '~> 2.4.1' spec.add_development_dependency 'bundler', '>= 1.15', '< 3.0.0' spec.add_development_dependency 'rake', '~> 13.0.1' diff --git a/test/helper.rb b/test/helper.rb index 9d0537f5b..9e375d53c 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -77,3 +77,27 @@ def initialize(type, *) super end end + +# Special test extension that records a context of the parser +# for any node that is created +module NodeContextExt + module NodeExt + attr_reader :context + + def assign_properties(properties) + super + + if (context = properties[:context]) + @context = context + end + end + end + Parser::AST::Node.prepend(NodeExt) + + module BuilderExt + def n(type, children, source_map) + super.updated(nil, nil, context: @parser.context.stack.dup) + end + end + Parser::Builders::Default.prepend(BuilderExt) +end diff --git a/test/parse_helper.rb b/test/parse_helper.rb index 2aee358b5..2d4cd9105 100644 --- a/test/parse_helper.rb +++ b/test/parse_helper.rb @@ -241,13 +241,13 @@ def assert_context(context, code, versions=ALL_VERSIONS) with_versions(versions) do |version, parser| source_file = Parser::Source::Buffer.new('(assert_context)', source: code) - begin - parser.parse(source_file) - rescue Parser::SyntaxError - # do nothing; the diagnostic was reported - end + parsed_ast = parser.parse(source_file) - assert_equal parser.context.stack, context, "(#{version}) parsing context" + nodes = find_matching_nodes(parsed_ast) { |node| node.type == :send && node.children[1] == :get_context } + assert_equal 1, nodes.count, "there must exactly 1 `get_context()` call" + + node = nodes.first + assert_equal context, node.context, "(#{version}) expect parsing context to match" end end @@ -310,4 +310,14 @@ def traverse_ast(ast, path) matching_children[index] end end + + def find_matching_nodes(ast, &block) + return [] unless ast.is_a?(AST::Node) + + result = [] + result << ast if block.call(ast) + ast.children.each { |child| result += find_matching_nodes(child, &block) } + + result + end end diff --git a/test/test_parser.rb b/test/test_parser.rb index a9e717425..bb95cdd49 100644 --- a/test/test_parser.rb +++ b/test/test_parser.rb @@ -6551,39 +6551,64 @@ def test_lparenarg_after_lvar__since_25 def test_context_class [ - %q{class A;}, - %q{class A < B;} + %q{class A; get_context; end}, + %q{class A < B; get_context; end} ].each do |code| assert_context([:class], code, ALL_VERSIONS) end end + def test_context_module + assert_context( + [:module], + %q{module M; get_context; end}, + ALL_VERSIONS) + end + def test_context_sclass assert_context( [:sclass], - %q{class << foo;}, + %q{class << foo; get_context; end}, ALL_VERSIONS) end def test_context_def - assert_context( - [:def], - %q{def m;}, - ALL_VERSIONS) + [ + %q{def m; get_context; end}, + %q{def m(a = get_context); end} + ].each do |code| + assert_context([:def], code, ALL_VERSIONS) + end + + [ + %q{def m() = get_context}, + %q{def m(a = get_context) = 42} + ].each do |code| + assert_context([:def], code, SINCE_2_8) + end end def test_context_defs - assert_context( - [:defs], - %q{def foo.m;}, - ALL_VERSIONS) + [ + %q{def foo.m; get_context; end}, + %q{def foo.m(a = get_context); end} + ].each do |code| + assert_context([:defs], code, ALL_VERSIONS) + end + + [ + %q{def foo.m() = get_context}, + %q{def foo.m(a = get_context) = 42} + ].each do |code| + assert_context([:defs], code, SINCE_2_8) + end end def test_context_cmd_brace_block [ - 'tap foo {', - 'foo.tap foo {', - 'foo::tap foo {' + 'tap foo { get_context }', + 'foo.tap foo { get_context }', + 'foo::tap foo { get_context }' ].each do |code| assert_context([:block], code, ALL_VERSIONS) end @@ -6591,12 +6616,12 @@ def test_context_cmd_brace_block def test_context_brace_block [ - 'tap {', - 'foo.tap {', - 'foo::tap {', - 'tap do', - 'foo.tap do', - 'foo::tap do' + 'tap { get_context }', + 'foo.tap { get_context }', + 'foo::tap { get_context }', + 'tap do get_context end', + 'foo.tap do get_context end', + 'foo::tap do get_context end' ].each do |code| assert_context([:block], code, ALL_VERSIONS) end @@ -6604,9 +6629,9 @@ def test_context_brace_block def test_context_do_block [ - %q{tap 1 do}, - %q{foo.tap do}, - %q{foo::tap do} + %q{tap 1 do get_context end}, + %q{foo.tap do get_context end}, + %q{foo::tap do get_context end} ].each do |code| assert_context([:block], code, ALL_VERSIONS) end @@ -6614,8 +6639,12 @@ def test_context_do_block def test_context_lambda [ - '->() {', - '->() do' + '->() { get_context }', + '->() do get_context end', + '-> { get_context }', + '-> do get_context end', + '->(a = get_context) {}', + '->(a = get_context) do end' ].each do |code| assert_context([:lambda], code, SINCE_1_9) end @@ -6623,28 +6652,61 @@ def test_context_lambda def test_context_nested assert_context( - [:class, :sclass, :defs, :def, :block], - %q{class A; class << foo; def bar.m; def m; tap do}, + [:class, :module, :sclass, :defs, :def, :block], + %q{ + class A + module M + class << foo + def bar.m + def m + tap do + get_context + end + end + end + end + end + end + }, ALL_VERSIONS) assert_context( - [:class, :sclass, :defs, :def, :lambda, :block], - %q{class A; class << foo; def bar.m; def m; -> do; tap do}, + [:class, :module, :sclass, :defs, :def, :lambda, :block], + %q{ + class A + module M + class << foo + def bar.m + def m + -> do + tap do + get_context + end + end + end + end + end + end + end + }, SINCE_1_9) assert_context( [], %q{ class A - class << foo - def bar.m - def m - tap do + module M + class << foo + def bar.m + def m + tap do + end end end end end end + get_context }, ALL_VERSIONS) @@ -6652,17 +6714,20 @@ def m [], %q{ class A - class << foo - def bar.m - def m - -> do - tap do + module M + class << foo + def bar.m + def m + -> do + tap do + end end end end end end end + get_context }, SINCE_1_9) end