diff --git a/.gitmodules b/.gitmodules index 8287c5e3..f5477ea3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,9 +4,3 @@ [submodule "spec"] path = spec/ruby url = git@github.com:ruby/spec.git -[submodule "test/ruby-syntax-fixtures"] - path = test/ruby-syntax-fixtures - url = https://github.com/ruby-syntax-tree/ruby-syntax-fixtures -[submodule "test/suites/parser"] - path = test/suites/parser - url = https://github.com/whitequark/parser diff --git a/Rakefile b/Rakefile index cb96e7bf..aa8d29f6 100644 --- a/Rakefile +++ b/Rakefile @@ -4,18 +4,13 @@ require "bundler/gem_tasks" require "rake/testtask" require "syntax_tree/rake_tasks" +Rake.add_rakelib "tasks" + Rake::TestTask.new(:test) do |t| t.libs << "test" t.libs << "test/suites" t.libs << "lib" - - # These are our own tests. - test_files = FileList["test/**/*_test.rb"] - - # This is a big test file from the parser gem that tests its functionality. - test_files << "test/suites/parser/test/test_parser.rb" - - t.test_files = test_files + t.test_files = FileList["test/**/*_test.rb"] end task default: :test @@ -34,10 +29,3 @@ end SyntaxTree::Rake::CheckTask.new(&configure) SyntaxTree::Rake::WriteTask.new(&configure) - -desc "Run mspec tests using YARV emulation" -task :spec do - Dir["./spec/ruby/language/**/*_spec.rb"].each do |filepath| - sh "exe/yarv ./spec/mspec/bin/mspec-tag #{filepath}" - end -end diff --git a/lib/syntax_tree/node.rb b/lib/syntax_tree/node.rb index b1ecfdc7..ff8ee95a 100644 --- a/lib/syntax_tree/node.rb +++ b/lib/syntax_tree/node.rb @@ -11527,8 +11527,9 @@ def ===(other) # # To be clear, this method should just not exist. It's not good. It's a # place of shame. But it's necessary for now, so I'm keeping it. - def pin(parent) - replace = PinnedVarRef.new(value: value, location: location) + def pin(parent, pin) + replace = + PinnedVarRef.new(value: value, location: pin.location.to(location)) parent .deconstruct_keys([]) diff --git a/lib/syntax_tree/parser.rb b/lib/syntax_tree/parser.rb index 75af65bf..59128875 100644 --- a/lib/syntax_tree/parser.rb +++ b/lib/syntax_tree/parser.rb @@ -641,8 +641,7 @@ def visit(node) end def visit_var_ref(node) - pins.shift - node.pin(stack[-2]) + node.pin(stack[-2], pins.shift) end def self.visit(node, tokens) @@ -1683,6 +1682,22 @@ def on_float(value) # VarField right # ) -> FndPtn def on_fndptn(constant, left, values, right) + # The left and right of a find pattern are always going to be splats, so + # we're going to consume the * operators and use their location + # information to extend the location of the splats. + right, left = + [right, left].map do |node| + operator = consume_operator(:*) + location = + if node.value + operator.location.to(node.location) + else + operator.location + end + + node.copy(location: location) + end + # The opening of this find pattern is either going to be a left bracket, a # right left parenthesis, or the left splat. We're going to use this to # determine how to find the closing of the pattern, as well as determining @@ -1791,7 +1806,7 @@ def on_heredoc_beg(value) line: lineno, char: char_pos, column: current_column, - size: value.size + 1 + size: value.size ) # Here we're going to artificially create an extra node type so that if @@ -1826,7 +1841,7 @@ def on_heredoc_end(value) line: lineno, char: char_pos, column: current_column, - size: value.size + 1 + size: value.size ) heredoc_end = HeredocEnd.new(value: value.chomp, location: location) @@ -1841,9 +1856,9 @@ def on_heredoc_end(value) start_line: heredoc.location.start_line, start_char: heredoc.location.start_char, start_column: heredoc.location.start_column, - end_line: lineno, - end_char: char_pos, - end_column: current_column + end_line: location.end_line, + end_char: location.end_char, + end_column: location.end_column ) ) end @@ -2357,14 +2372,14 @@ def on_method_add_arg(call, arguments) # :call-seq: # on_method_add_block: ( - # (Break | Call | Command | CommandCall) call, + # (Break | Call | Command | CommandCall, Next) call, # Block block # ) -> Break | MethodAddBlock def on_method_add_block(call, block) location = call.location.to(block.location) case call - when Break + when Break, Next parts = call.arguments.parts node = parts.pop diff --git a/lib/syntax_tree/translation/parser.rb b/lib/syntax_tree/translation/parser.rb index 1e47b4e7..4a4b6ade 100644 --- a/lib/syntax_tree/translation/parser.rb +++ b/lib/syntax_tree/translation/parser.rb @@ -27,9 +27,9 @@ def visit_alias(node) s( :alias, [visit(node.left), visit(node.right)], - source_map_keyword( - keyword: source_range_length(node.location.start_char, 5), - expression: source_range_node(node) + source_map_keyword_bare( + source_range_length(node.location.start_char, 5), + source_range_node(node) ) ) end @@ -58,11 +58,7 @@ def visit_aref(node) [visit(node.collection)].concat(visit_all(node.index.parts)), source_map_index( begin_token: - source_range_find( - node.collection.location.end_char, - node.index.location.start_char, - "[" - ), + source_range_find_between(node.collection, node.index, "["), end_token: source_range_length(node.location.end_char, -1), expression: source_range_node(node) ) @@ -90,9 +86,9 @@ def visit_aref(node) source_map_send( selector: source_range( - source_range_find( - node.collection.location.end_char, - node.index.location.start_char, + source_range_find_between( + node.collection, + node.index, "[" ).begin_pos, node.location.end_char @@ -128,11 +124,7 @@ def visit_aref_field(node) [visit(node.collection)].concat(visit_all(node.index.parts)), source_map_index( begin_token: - source_range_find( - node.collection.location.end_char, - node.index.location.start_char, - "[" - ), + source_range_find_between(node.collection, node.index, "["), end_token: source_range_length(node.location.end_char, -1), expression: source_range_node(node) ) @@ -162,9 +154,9 @@ def visit_aref_field(node) source_map_send( selector: source_range( - source_range_find( - node.collection.location.end_char, - node.index.location.start_char, + source_range_find_between( + node.collection, + node.index, "[" ).begin_pos, node.location.end_char @@ -182,8 +174,8 @@ def visit_arg_block(node) :block_pass, [visit(node.value)], source_map_operator( - operator: source_range_length(node.location.start_char, 1), - expression: source_range_node(node) + source_range_length(node.location.start_char, 1), + source_range_node(node) ) ) end @@ -192,18 +184,14 @@ def visit_arg_block(node) def visit_arg_star(node) if stack[-3].is_a?(MLHSParen) && stack[-3].contents.is_a?(MLHS) if node.value.nil? - s( - :restarg, - [], - source_map_variable(expression: source_range_node(node)) - ) + s(:restarg, [], source_map_variable(nil, source_range_node(node))) else s( :restarg, [node.value.value.to_sym], source_map_variable( - name: source_range_node(node.value), - expression: source_range_node(node) + source_range_node(node.value), + source_range_node(node) ) ) end @@ -212,8 +200,8 @@ def visit_arg_star(node) :splat, node.value.nil? ? [] : [visit(node.value)], source_map_operator( - operator: source_range_length(node.location.start_char, 1), - expression: source_range_node(node) + source_range_length(node.location.start_char, 1), + source_range_node(node) ) ) end @@ -307,11 +295,7 @@ def visit_assign(node) target .location .with_operator( - source_range_find( - node.target.location.end_char, - node.value.location.start_char, - "=" - ) + source_range_find_between(node.target, node.value, "=") ) .with_expression(source_range_node(node)) @@ -324,19 +308,25 @@ def visit_assoc(node) expression = source_range(node.location.start_char, node.location.end_char - 1) + type, location = + if node.key.value.start_with?(/[A-Z]/) + [:const, source_map_constant(nil, expression, expression)] + else + [ + :send, + source_map_send(selector: expression, expression: expression) + ] + end + s( :pair, [ visit(node.key), - s( - node.key.value.start_with?(/[A-Z]/) ? :const : :send, - [nil, node.key.value.chomp(":").to_sym], - source_map_send(selector: expression, expression: expression) - ) + s(type, [nil, node.key.value.chomp(":").to_sym], location) ], source_map_operator( - operator: source_range_length(node.key.location.end_char, -1), - expression: source_range_node(node) + source_range_length(node.key.location.end_char, -1), + source_range_node(node) ) ) else @@ -344,8 +334,9 @@ def visit_assoc(node) :pair, [visit(node.key), visit(node.value)], source_map_operator( - operator: source_range_length(node.key.location.end_char, -1), - expression: source_range_node(node) + source_range_search_between(node.key, node.value, "=>") || + source_range_length(node.key.location.end_char, -1), + source_range_node(node) ) ) end @@ -357,8 +348,8 @@ def visit_assoc_splat(node) :kwsplat, [visit(node.value)], source_map_operator( - operator: source_range_length(node.location.start_char, 2), - expression: source_range_node(node) + source_range_length(node.location.start_char, 2), + source_range_node(node) ) ) end @@ -394,15 +385,14 @@ def visit_BEGIN(node) :preexe, [visit(node.statements)], source_map_keyword( - keyword: source_range_length(node.location.start_char, 5), - begin_token: - source_range_find( - node.location.start_char + 5, - node.statements.location.start_char, - "{" - ), - end_token: source_range_length(node.location.end_char, -1), - expression: source_range_node(node) + source_range_length(node.location.start_char, 5), + source_range_find( + node.location.start_char + 5, + node.statements.location.start_char, + "{" + ), + source_range_length(node.location.end_char, -1), + source_range_node(node) ) ) end @@ -450,13 +440,12 @@ def visit_binary(node) ), [visit(node.left), visit(node.right)], source_map_operator( - operator: - source_range_find( - node.left.location.end_char, - node.right.location.start_char, - node.operator.to_s - ), - expression: source_range_node(node) + source_range_find_between( + node.left, + node.right, + node.operator.to_s + ), + source_range_node(node) ) ) when :=~ @@ -471,13 +460,12 @@ def visit_binary(node) :match_with_lvasgn, [visit(node.left), visit(node.right)], source_map_operator( - operator: - source_range_find( - node.left.location.end_char, - node.right.location.start_char, - node.operator.to_s - ), - expression: source_range_node(node) + source_range_find_between( + node.left, + node.right, + node.operator.to_s + ), + source_range_node(node) ) ) else @@ -491,18 +479,14 @@ def visit_binary(node) # Visit a BlockArg node. def visit_blockarg(node) if node.name.nil? - s( - :blockarg, - [nil], - source_map_variable(expression: source_range_node(node)) - ) + s(:blockarg, [nil], source_map_variable(nil, source_range_node(node))) else s( :blockarg, [node.name.value.to_sym], source_map_variable( - name: source_range_node(node.name), - expression: source_range_node(node) + source_range_node(node.name), + source_range_node(node) ) ) end @@ -516,8 +500,8 @@ def visit_block_var(node) :shadowarg, [local.value.to_sym], source_map_variable( - name: source_range_node(local), - expression: source_range_node(local) + source_range_node(local), + source_range_node(local) ) ) end @@ -539,8 +523,8 @@ def visit_block_var(node) :arg, [required.value.to_sym], source_map_variable( - name: source_range_node(required), - expression: source_range_node(required) + source_range_node(required), + source_range_node(required) ) ) ], @@ -624,9 +608,9 @@ def visit_break(node) s( :break, visit_all(node.arguments.parts), - source_map_keyword( - keyword: source_range_length(node.location.start_char, 5), - expression: source_range_node(node) + source_map_keyword_bare( + source_range_length(node.location.start_char, 5), + source_range_node(node) ) ) end @@ -685,11 +669,7 @@ def visit_CHAR(node) def visit_class(node) operator = if node.superclass - source_range_find( - node.constant.location.end_char, - node.superclass.location.start_char, - "<" - ) + source_range_find_between(node.constant, node.superclass, "<") end s( @@ -824,8 +804,9 @@ def visit_const(node) :const, [nil, node.value.to_sym], source_map_constant( - name: source_range_node(node), - expression: source_range_node(node) + nil, + source_range_node(node), + source_range_node(node) ) ) end @@ -840,14 +821,9 @@ def visit_const_path_field(node) :casgn, [visit(node.parent), node.constant.value.to_sym], source_map_constant( - double_colon: - source_range_find( - node.parent.location.end_char, - node.constant.location.start_char, - "::" - ), - name: source_range_node(node.constant), - expression: source_range_node(node) + source_range_find_between(node.parent, node.constant, "::"), + source_range_node(node.constant), + source_range_node(node) ) ) end @@ -859,14 +835,9 @@ def visit_const_path_ref(node) :const, [visit(node.parent), node.constant.value.to_sym], source_map_constant( - double_colon: - source_range_find( - node.parent.location.end_char, - node.constant.location.start_char, - "::" - ), - name: source_range_node(node.constant), - expression: source_range_node(node) + source_range_find_between(node.parent, node.constant, "::"), + source_range_node(node.constant), + source_range_node(node) ) ) end @@ -877,8 +848,9 @@ def visit_const_ref(node) :const, [nil, node.constant.value.to_sym], source_map_constant( - name: source_range_node(node.constant), - expression: source_range_node(node) + nil, + source_range_node(node.constant), + source_range_node(node) ) ) end @@ -888,10 +860,7 @@ def visit_cvar(node) s( :cvar, [node.value.to_sym], - source_map_variable( - name: source_range_node(node), - expression: source_range_node(node) - ) + source_map_variable(source_range_node(node), source_range_node(node)) ) end @@ -931,9 +900,9 @@ def visit_def(node) source_map_method_definition( keyword: source_range_length(node.location.start_char, 3), assignment: - source_range_find( - (node.params || node.name).location.end_char, - node.bodystmt.location.start_char, + source_range_find_between( + (node.params || node.name), + node.bodystmt, "=" ), name: source_range_node(node.name), @@ -983,10 +952,10 @@ def visit_defined(node) :defined?, [visit(node.value)], source_map_keyword( - keyword: source_range_length(node.location.start_char, 8), - begin_token: begin_token, - end_token: end_token, - expression: source_range_node(node) + source_range_length(node.location.start_char, 8), + begin_token, + end_token, + source_range_node(node) ) ) end @@ -1061,15 +1030,14 @@ def visit_END(node) :postexe, [visit(node.statements)], source_map_keyword( - keyword: source_range_length(node.location.start_char, 3), - begin_token: - source_range_find( - node.location.start_char + 3, - node.statements.location.start_char, - "{" - ), - end_token: source_range_length(node.location.end_char, -1), - expression: source_range_node(node) + source_range_length(node.location.start_char, 3), + source_range_find( + node.location.start_char + 3, + node.statements.location.start_char, + "{" + ), + source_range_length(node.location.end_char, -1), + source_range_node(node) ) ) end @@ -1129,32 +1097,36 @@ def visit_float(node) s( :float, [node.value.to_f], - source_map_operator( - operator: operator, - expression: source_range_node(node) - ) + source_map_operator(operator, source_range_node(node)) ) end # Visit a FndPtn node. def visit_fndptn(node) - make_match_rest = ->(child) do - if child.is_a?(VarField) && child.value.nil? - s(:match_rest, [], nil) - else - s(:match_rest, [visit(child)], nil) + left, right = + [node.left, node.right].map do |child| + location = + source_map_operator( + source_range_length(child.location.start_char, 1), + source_range_node(child) + ) + + if child.is_a?(VarField) && child.value.nil? + s(:match_rest, [], location) + else + s(:match_rest, [visit(child)], location) + end end - end inner = s( :find_pattern, - [ - make_match_rest[node.left], - *visit_all(node.values), - make_match_rest[node.right] - ], - nil + [left, *visit_all(node.values), right], + source_map_collection( + begin_token: source_range_length(node.location.start_char, 1), + end_token: source_range_length(node.location.end_char, -1), + expression: source_range_node(node) + ) ) if node.constant @@ -1166,28 +1138,15 @@ def visit_fndptn(node) # Visit a For node. def visit_for(node) - begin_start = node.collection.location.end_char - begin_end = node.statements.location.start_char - - begin_token = - if buffer.source[begin_start...begin_end].include?("do") - source_range_find(begin_start, begin_end, "do") - end - s( :for, [visit(node.index), visit(node.collection), visit(node.statements)], source_map_for( - keyword: source_range_length(node.location.start_char, 3), - in_token: - source_range_find( - node.index.location.end_char, - node.collection.location.start_char, - "in" - ), - begin_token: begin_token, - end_token: source_range_length(node.location.end_char, -3), - expression: source_range_node(node) + source_range_length(node.location.start_char, 3), + source_range_find_between(node.index, node.collection, "in"), + source_range_search_between(node.collection, node.statements, "do"), + source_range_length(node.location.end_char, -3), + source_range_node(node) ) ) end @@ -1197,10 +1156,7 @@ def visit_gvar(node) s( :gvar, [node.value.to_sym], - source_map_variable( - name: source_range_node(node), - expression: source_range_node(node) - ) + source_map_variable(source_range_node(node), source_range_node(node)) ) end @@ -1303,15 +1259,32 @@ def visit_heredoc(node) end heredoc_segments.trim! + location = + source_map_heredoc( + source_range_node(node.beginning), + source_range( + if node.parts.empty? + node.beginning.location.end_char + else + node.parts.first.location.start_char + end, + node.ending.location.start_char + ), + source_range( + node.ending.location.start_char, + node.ending.location.end_char - 1 + ) + ) if node.beginning.value.match?(/`\w+`\z/) - s(:xstr, heredoc_segments.segments, nil) + s(:xstr, heredoc_segments.segments, location) elsif heredoc_segments.segments.length > 1 - s(:dstr, heredoc_segments.segments, nil) + s(:dstr, heredoc_segments.segments, location) elsif heredoc_segments.segments.empty? - s(:dstr, [], nil) + s(:dstr, [], location) else - heredoc_segments.segments.first + segment = heredoc_segments.segments.first + s(segment.type, segment.children, location) end end @@ -1353,10 +1326,7 @@ def visit_ident(node) s( :lvar, [node.value.to_sym], - source_map_variable( - name: source_range_node(node), - expression: source_range_node(node) - ) + source_map_variable(source_range_node(node), source_range_node(node)) ) end @@ -1389,14 +1359,9 @@ def visit_if(node) :if, [predicate, visit(node.statements), visit(node.consequent)], if node.modifier? - source_map_keyword( - keyword: - source_range_find( - node.statements.location.end_char, - node.predicate.location.start_char, - "if" - ), - expression: source_range_node(node) + source_map_keyword_bare( + source_range_find_between(node.statements, node.predicate, "if"), + source_range_node(node) ) else begin_start = node.predicate.location.end_char @@ -1410,6 +1375,8 @@ def visit_if(node) begin_token = if buffer.source[begin_start...begin_end].include?("then") source_range_find(begin_start, begin_end, "then") + elsif buffer.source[begin_start...begin_end].include?(";") + source_range_find(begin_start, begin_end, ";") end else_token = @@ -1450,7 +1417,7 @@ def visit_imaginary(node) # case. Maybe there's an API for this but I can't find it. eval(node.value) ], - source_map_operator(expression: source_range_node(node)) + source_map_operator(nil, source_range_node(node)) ) end @@ -1478,19 +1445,24 @@ def visit_in(node) nil ) else + begin_token = + source_range_search_between(node.pattern, node.statements, "then") + end_char = - if node.statements.empty? + if begin_token || node.statements.empty? node.statements.location.end_char - 1 else - node.statements.body.first.location.start_char + node.statements.body.last.location.start_char end s( :in_pattern, [visit(node.pattern), nil, visit(node.statements)], source_map_keyword( - keyword: source_range_length(node.location.start_char, 2), - expression: source_range(node.location.start_char, end_char) + source_range_length(node.location.start_char, 2), + begin_token, + nil, + source_range(node.location.start_char, end_char) ) ) end @@ -1506,10 +1478,7 @@ def visit_int(node) s( :int, [node.value.to_i], - source_map_operator( - operator: operator, - expression: source_range_node(node) - ) + source_map_operator(operator, source_range_node(node)) ) end @@ -1518,10 +1487,7 @@ def visit_ivar(node) s( :ivar, [node.value.to_sym], - source_map_variable( - name: source_range_node(node), - expression: source_range_node(node) - ) + source_map_variable(source_range_node(node), source_range_node(node)) ) end @@ -1548,18 +1514,14 @@ def visit_kw(node) # Visit a KwRestParam node. def visit_kwrest_param(node) if node.name.nil? - s( - :kwrestarg, - [], - source_map_variable(expression: source_range_node(node)) - ) + s(:kwrestarg, [], source_map_variable(nil, source_range_node(node))) else s( :kwrestarg, [node.name.value.to_sym], source_map_variable( - name: source_range_node(node.name), - expression: source_range_node(node) + source_range_node(node.name), + source_range_node(node) ) ) end @@ -1635,8 +1597,8 @@ def visit_lambda_var(node) :shadowarg, [local.value.to_sym], source_map_variable( - name: source_range_node(local), - expression: source_range_node(local) + source_range_node(local), + source_range_node(local) ) ) end @@ -1661,13 +1623,8 @@ def visit_massign(node) :masgn, [visit(node.target), visit(node.value)], source_map_operator( - operator: - source_range_find( - node.target.location.end_char, - node.value.location.start_char, - "=" - ), - expression: source_range_node(node) + source_range_find_between(node.target, node.value, "="), + source_range_node(node) ) ) end @@ -1722,8 +1679,8 @@ def visit_mlhs(node) :arg, [part.value.to_sym], source_map_variable( - name: source_range_node(part), - expression: source_range_node(part) + source_range_node(part), + source_range_node(part) ) ) else @@ -1778,9 +1735,9 @@ def visit_next(node) s( :next, visit_all(node.arguments.parts), - source_map_keyword( - keyword: source_range_length(node.location.start_char, 4), - expression: source_range_node(node) + source_map_keyword_bare( + source_range_length(node.location.start_char, 4), + source_range_node(node) ) ) end @@ -1839,10 +1796,45 @@ def visit_not(node) # Visit an OpAssign node. def visit_opassign(node) location = - source_map_variable( - name: source_range_node(node.target), - expression: source_range_node(node) - ).with_operator(source_range_node(node.operator)) + case node.target + when ARefField + source_map_index( + begin_token: + source_range_find( + node.target.collection.location.end_char, + if node.target.index + node.target.index.location.start_char + else + node.target.location.end_char + end, + "[" + ), + end_token: source_range_length(node.target.location.end_char, -1), + expression: source_range_node(node) + ) + when Field + source_map_send( + dot: + if node.target.operator == :"::" + source_range_find_between( + node.target.parent, + node.target.name, + "::" + ) + else + source_range_node(node.target.operator) + end, + selector: source_range_node(node.target.name), + expression: source_range_node(node) + ) + else + source_map_variable( + source_range_node(node.target), + source_range_node(node) + ) + end + + location = location.with_operator(source_range_node(node.operator)) case node.operator.value when "||=" @@ -1876,8 +1868,8 @@ def visit_params(node) :arg, [required.value.to_sym], source_map_variable( - name: source_range_node(required), - expression: source_range_node(required) + source_range_node(required), + source_range_node(required) ) ) end @@ -1889,16 +1881,9 @@ def visit_params(node) :optarg, [name.value.to_sym, visit(value)], source_map_variable( - name: source_range_node(name), - expression: - source_range_node(name).join(source_range_node(value)) - ).with_operator( - source_range_find( - name.location.end_char, - value.location.start_char, - "=" - ) - ) + source_range_node(name), + source_range_node(name).join(source_range_node(value)) + ).with_operator(source_range_find_between(name, value, "=")) ) end @@ -1912,8 +1897,8 @@ def visit_params(node) :arg, [post.value.to_sym], source_map_variable( - name: source_range_node(post), - expression: source_range_node(post) + source_range_node(post), + source_range_node(post) ) ) end @@ -1927,13 +1912,11 @@ def visit_params(node) :kwoptarg, [key, visit(value)], source_map_variable( - name: - source_range( - name.location.start_char, - name.location.end_char - 1 - ), - expression: - source_range_node(name).join(source_range_node(value)) + source_range( + name.location.start_char, + name.location.end_char - 1 + ), + source_range_node(name).join(source_range_node(value)) ) ) else @@ -1941,12 +1924,11 @@ def visit_params(node) :kwarg, [key], source_map_variable( - name: - source_range( - name.location.start_char, - name.location.end_char - 1 - ), - expression: source_range_node(name) + source_range( + name.location.start_char, + name.location.end_char - 1 + ), + source_range_node(name) ) ) end @@ -1960,8 +1942,8 @@ def visit_params(node) :kwnilarg, [], source_map_variable( - name: source_range_length(node.location.end_char, -3), - expression: source_range_node(node) + source_range_length(node.location.end_char, -3), + source_range_node(node) ) ) else @@ -2011,12 +1993,41 @@ def visit_paren(node) # Visit a PinnedBegin node. def visit_pinned_begin(node) - s(:pin, [s(:begin, [visit(node.statement)], nil)], nil) + s( + :pin, + [ + s( + :begin, + [visit(node.statement)], + source_map_collection( + begin_token: + source_range_length(node.location.start_char + 1, 1), + end_token: source_range_length(node.location.end_char, -1), + expression: + source_range( + node.location.start_char + 1, + node.location.end_char + ) + ) + ) + ], + source_map_send( + selector: source_range_length(node.location.start_char, 1), + expression: source_range_node(node) + ) + ) end # Visit a PinnedVarRef node. def visit_pinned_var_ref(node) - s(:pin, [visit(node.value)], nil) + s( + :pin, + [visit(node.value)], + source_map_send( + selector: source_range_length(node.location.start_char, 1), + expression: source_range_node(node) + ) + ) end # Visit a Program node. @@ -2057,8 +2068,8 @@ def visit_range(node) node.operator.value == ".." ? :irange : :erange, [visit(node.left), visit(node.right)], source_map_operator( - operator: source_range_node(node.operator), - expression: source_range_node(node) + source_range_node(node.operator), + source_range_node(node) ) ) end @@ -2069,8 +2080,8 @@ def visit_rassign(node) node.operator.value == "=>" ? :match_pattern : :match_pattern_p, [visit(node.value), visit(node.pattern)], source_map_operator( - operator: source_range_node(node.operator), - expression: source_range_node(node) + source_range_node(node.operator), + source_range_node(node) ) ) end @@ -2080,7 +2091,7 @@ def visit_rational(node) s( :rational, [node.value.to_r], - source_map_operator(expression: source_range_node(node)) + source_map_operator(nil, source_range_node(node)) ) end @@ -2089,9 +2100,9 @@ def visit_redo(node) s( :redo, [], - source_map_keyword( - keyword: source_range_node(node), - expression: source_range_node(node) + source_map_keyword_bare( + source_range_node(node), + source_range_node(node) ) ) end @@ -2245,11 +2256,7 @@ def visit_rescue(node) # Visit a RescueMod node. def visit_rescue_mod(node) keyword = - source_range_find( - node.statement.location.end_char, - node.value.location.start_char, - "rescue" - ) + source_range_find_between(node.statement, node.value, "rescue") s( :rescue, @@ -2276,16 +2283,12 @@ def visit_rest_param(node) :restarg, [node.name.value.to_sym], source_map_variable( - name: source_range_node(node.name), - expression: source_range_node(node) + source_range_node(node.name), + source_range_node(node) ) ) else - s( - :restarg, - [], - source_map_variable(expression: source_range_node(node)) - ) + s(:restarg, [], source_map_variable(nil, source_range_node(node))) end end @@ -2294,9 +2297,9 @@ def visit_retry(node) s( :retry, [], - source_map_keyword( - keyword: source_range_node(node), - expression: source_range_node(node) + source_map_keyword_bare( + source_range_node(node), + source_range_node(node) ) ) end @@ -2306,9 +2309,9 @@ def visit_return(node) s( :return, node.arguments ? visit_all(node.arguments.parts) : [], - source_map_keyword( - keyword: source_range_length(node.location.start_char, 6), - expression: source_range_node(node) + source_map_keyword_bare( + source_range_length(node.location.start_char, 6), + source_range_node(node) ) ) end @@ -2399,7 +2402,11 @@ def visit_string_literal(node) location = if node.quote source_map_collection( - begin_token: source_range_length(node.location.start_char, 1), + begin_token: + source_range_length( + node.location.start_char, + node.quote.length + ), end_token: source_range_length(node.location.end_char, -1), expression: source_range_node(node) ) @@ -2423,9 +2430,9 @@ def visit_super(node) s( :super, visit_all(node.arguments.parts), - source_map_keyword( - keyword: source_range_node(node), - expression: source_range_node(node) + source_map_keyword_bare( + source_range_length(node.location.start_char, 5), + source_range_node(node) ) ) else @@ -2435,15 +2442,14 @@ def visit_super(node) :super, [], source_map_keyword( - keyword: source_range_length(node.location.start_char, 5), - begin_token: - source_range_find( - node.location.start_char + 5, - node.location.end_char, - "(" - ), - end_token: source_range_length(node.location.end_char, -1), - expression: source_range_node(node) + source_range_length(node.location.start_char, 5), + source_range_find( + node.location.start_char + 5, + node.location.end_char, + "(" + ), + source_range_length(node.location.end_char, -1), + source_range_node(node) ) ) when ArgsForward @@ -2453,15 +2459,14 @@ def visit_super(node) :super, visit_all(node.arguments.arguments.parts), source_map_keyword( - keyword: source_range_length(node.location.start_char, 5), - begin_token: - source_range_find( - node.location.start_char + 5, - node.location.end_char, - "(" - ), - end_token: source_range_length(node.location.end_char, -1), - expression: source_range_node(node) + source_range_length(node.location.start_char, 5), + source_range_find( + node.location.start_char + 5, + node.location.end_char, + "(" + ), + source_range_length(node.location.end_char, -1), + source_range_node(node) ) ) end @@ -2526,9 +2531,9 @@ def visit_top_const_field(node) node.constant.value.to_sym ], source_map_constant( - double_colon: source_range_length(node.location.start_char, 2), - name: source_range_node(node.constant), - expression: source_range_node(node) + source_range_length(node.location.start_char, 2), + source_range_node(node.constant), + source_range_node(node) ) ) end @@ -2548,9 +2553,9 @@ def visit_top_const_ref(node) node.constant.value.to_sym ], source_map_constant( - double_colon: source_range_length(node.location.start_char, 2), - name: source_range_node(node.constant), - expression: source_range_node(node) + source_range_length(node.location.start_char, 2), + source_range_node(node.constant), + source_range_node(node) ) ) end @@ -2592,9 +2597,9 @@ def visit_undef(node) s( :undef, visit_all(node.symbols), - source_map_keyword( - keyword: source_range_length(node.location.start_char, 5), - expression: source_range_node(node) + source_map_keyword_bare( + source_range_length(node.location.start_char, 5), + source_range_node(node) ) ) end @@ -2624,14 +2629,13 @@ def visit_unless(node) :if, [predicate, visit(node.consequent), visit(node.statements)], if node.modifier? - source_map_keyword( - keyword: - source_range_find( - node.statements.location.end_char, - node.predicate.location.start_char, - "unless" - ), - expression: source_range_node(node) + source_map_keyword_bare( + source_range_find_between( + node.statements, + node.predicate, + "unless" + ), + source_range_node(node) ) else source_map_condition( @@ -2649,20 +2653,20 @@ def visit_until(node) loop_post?(node) ? :until_post : :until, [visit(node.predicate), visit(node.statements)], if node.modifier? - source_map_keyword( - keyword: - source_range_find( - node.statements.location.end_char, - node.predicate.location.start_char, - "until" - ), - expression: source_range_node(node) + source_map_keyword_bare( + source_range_find_between( + node.statements, + node.predicate, + "until" + ), + source_range_node(node) ) else source_map_keyword( - keyword: source_range_length(node.location.start_char, 5), - end_token: source_range_length(node.location.end_char, -3), - expression: source_range_node(node) + source_range_length(node.location.start_char, 5), + nil, + source_range_length(node.location.end_char, -3), + source_range_node(node) ) end ) @@ -2688,8 +2692,8 @@ def visit_var_field(node) :match_var, [name], source_map_variable( - name: source_range_node(node), - expression: source_range_node(node) + source_range_node(node.value), + source_range_node(node.value) ) ) elsif node.value.is_a?(Const) @@ -2697,15 +2701,16 @@ def visit_var_field(node) :casgn, [nil, name], source_map_constant( - name: source_range_node(node.value), - expression: source_range_node(node) + nil, + source_range_node(node.value), + source_range_node(node) ) ) else location = source_map_variable( - name: source_range_node(node), - expression: source_range_node(node) + source_range_node(node), + source_range_node(node) ) case node.value @@ -2747,17 +2752,26 @@ def visit_vcall(node) # Visit a When node. def visit_when(node) keyword = source_range_length(node.location.start_char, 4) + begin_token = + if buffer.source[node.statements.location.start_char] == ";" + source_range_length(node.statements.location.start_char, 1) + end + + end_char = + if node.statements.body.empty? + node.statements.location.end_char + else + node.statements.body.last.location.end_char + end s( :when, visit_all(node.arguments.parts) + [visit(node.statements)], source_map_keyword( - keyword: keyword, - expression: - source_range( - keyword.begin_pos, - node.statements.location.end_char - 1 - ) + keyword, + begin_token, + nil, + source_range(keyword.begin_pos, end_char) ) ) end @@ -2768,20 +2782,20 @@ def visit_while(node) loop_post?(node) ? :while_post : :while, [visit(node.predicate), visit(node.statements)], if node.modifier? - source_map_keyword( - keyword: - source_range_find( - node.statements.location.end_char, - node.predicate.location.start_char, - "while" - ), - expression: source_range_node(node) + source_map_keyword_bare( + source_range_find_between( + node.statements, + node.predicate, + "while" + ), + source_range_node(node) ) else source_map_keyword( - keyword: source_range_length(node.location.start_char, 5), - end_token: source_range_length(node.location.end_char, -3), - expression: source_range_node(node) + source_range_length(node.location.start_char, 5), + nil, + source_range_length(node.location.end_char, -3), + source_range_node(node) ) end ) @@ -2828,18 +2842,18 @@ def visit_yield(node) s( :yield, [], - source_map_keyword( - keyword: source_range_length(node.location.start_char, 5), - expression: source_range_node(node) + source_map_keyword_bare( + source_range_length(node.location.start_char, 5), + source_range_node(node) ) ) when Args s( :yield, visit_all(node.arguments.parts), - source_map_keyword( - keyword: source_range_length(node.location.start_char, 5), - expression: source_range_node(node) + source_map_keyword_bare( + source_range_length(node.location.start_char, 5), + source_range_node(node) ) ) else @@ -2847,11 +2861,10 @@ def visit_yield(node) :yield, visit_all(node.arguments.contents.parts), source_map_keyword( - keyword: source_range_length(node.location.start_char, 5), - begin_token: - source_range_length(node.arguments.location.start_char, 1), - end_token: source_range_length(node.location.end_char, -1), - expression: source_range_node(node) + source_range_length(node.location.start_char, 5), + source_range_length(node.arguments.location.start_char, 1), + source_range_length(node.location.end_char, -1), + source_range_node(node) ) ) end @@ -2862,9 +2875,9 @@ def visit_zsuper(node) s( :zsuper, [], - source_map_keyword( - keyword: source_range_length(node.location.start_char, 5), - expression: source_range_node(node) + source_map_keyword_bare( + source_range_length(node.location.start_char, 5), + source_range_node(node) ) ) end @@ -3029,7 +3042,7 @@ def source_map_condition( end # Constructs a new source map for a constant reference. - def source_map_constant(double_colon: nil, name: nil, expression:) + def source_map_constant(double_colon, name, expression) ::Parser::Source::Map::Constant.new(double_colon, name, expression) end @@ -3049,13 +3062,7 @@ def source_map_definition( end # Constructs a new source map for a for loop. - def source_map_for( - keyword: nil, - in_token: nil, - begin_token: nil, - end_token: nil, - expression: - ) + def source_map_for(keyword, in_token, begin_token, end_token, expression) ::Parser::Source::Map::For.new( keyword, in_token, @@ -3065,18 +3072,22 @@ def source_map_for( ) end + # Constructs a new source map for a heredoc. + def source_map_heredoc(expression, heredoc_body, heredoc_end) + ::Parser::Source::Map::Heredoc.new( + expression, + heredoc_body, + heredoc_end + ) + end + # Construct a source map for an index operation. def source_map_index(begin_token: nil, end_token: nil, expression:) ::Parser::Source::Map::Index.new(begin_token, end_token, expression) end # Constructs a new source map for the use of a keyword. - def source_map_keyword( - keyword: nil, - begin_token: nil, - end_token: nil, - expression: - ) + def source_map_keyword(keyword, begin_token, end_token, expression) ::Parser::Source::Map::Keyword.new( keyword, begin_token, @@ -3085,6 +3096,12 @@ def source_map_keyword( ) end + # Constructs a new source map for the use of a keyword without a begin or + # end token. + def source_map_keyword_bare(keyword, expression) + source_map_keyword(keyword, nil, nil, expression) + end + # Constructs a new source map for a method definition. def source_map_method_definition( keyword: nil, @@ -3105,7 +3122,7 @@ def source_map_method_definition( end # Constructs a new source map for an operator. - def source_map_operator(operator: nil, expression:) + def source_map_operator(operator, expression) ::Parser::Source::Map::Operator.new(operator, expression) end @@ -3142,7 +3159,7 @@ def source_map_send( end # Constructs a new source map for a variable. - def source_map_variable(name: nil, expression:) + def source_map_variable(name, expression) ::Parser::Source::Map::Variable.new(name, expression) end @@ -3152,16 +3169,48 @@ def source_range(start_char, end_char) end # Constructs a new source range by finding the given needle in the given - # range of the source. - def source_range_find(start_char, end_char, needle) + # range of the source. If the needle is not found, returns nil. + def source_range_search(start_char, end_char, needle) index = buffer.source[start_char...end_char].index(needle) - unless index + return unless index + + offset = start_char + index + source_range(offset, offset + needle.length) + end + + # Constructs a new source range by searching for the given needle between + # the end location of the start node and the start location of the end + # node. If the needle is not found, returns nil. + def source_range_search_between(start_node, end_node, needle) + source_range_search( + start_node.location.end_char, + end_node.location.start_char, + needle + ) + end + + # Constructs a new source range by finding the given needle in the given + # range of the source. If it needle is not found, raises an error. + def source_range_find(start_char, end_char, needle) + source_range = source_range_search(start_char, end_char, needle) + + unless source_range slice = buffer.source[start_char...end_char].inspect raise "Could not find #{needle.inspect} in #{slice}" end - offset = start_char + index - source_range(offset, offset + needle.length) + source_range + end + + # Constructs a new source range by finding the given needle between the + # end location of the start node and the start location of the end node. + # If the needle is not found, returns raises an error. + def source_range_find_between(start_node, end_node, needle) + source_range_find( + start_node.location.end_char, + end_node.location.start_char, + needle + ) end # Constructs a new source range from the given start offset and length. diff --git a/tasks/spec.rake b/tasks/spec.rake new file mode 100644 index 00000000..c361fe8e --- /dev/null +++ b/tasks/spec.rake @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +desc "Run mspec tests using YARV emulation" +task :spec do + specs = File.expand_path("../spec/ruby/language/**/*_spec.rb", __dir__) + + Dir[specs].each do |filepath| + sh "exe/yarv ./spec/mspec/bin/mspec-tag #{filepath}" + end +end diff --git a/tasks/whitequark.rake b/tasks/whitequark.rake new file mode 100644 index 00000000..4f7ee650 --- /dev/null +++ b/tasks/whitequark.rake @@ -0,0 +1,87 @@ +# frozen_string_literal: true + +# This file's purpose is to extract the examples from the whitequark/parser +# gem and generate a test file that we can use to ensure that our parser +# generates equivalent syntax trees when translating. To do this, it runs the +# parser's test suite but overrides the `assert_parses` method to collect the +# examples into a hash. Then, it writes out the hash to a file that we can use +# to generate our own tests. +# +# To run the test suite, it's important to note that we have to mirror both any +# APIs provided to the test suite (for example the ParseHelper module below). +# This is obviously relatively brittle, but it's effective for now. + +require "ast" + +module ParseHelper + # This object is going to collect all of the examples from the parser gem into + # a hash that we can use to generate our own tests. + COLLECTED = Hash.new { |hash, key| hash[key] = [] } + + include AST::Sexp + ALL_VERSIONS = %w[3.1 3.2] + + private + + def assert_context(*) + end + + def assert_diagnoses(*) + end + + def assert_diagnoses_many(*) + end + + def refute_diagnoses(*) + end + + def with_versions(*) + end + + def assert_parses(_ast, code, _source_maps = "", versions = ALL_VERSIONS) + # We're going to skip any examples that are for older Ruby versions + # that we do not support. + return if (versions & %w[3.1 3.2]).empty? + + entry = caller.find { _1.include?("test_parser.rb") } + _, lineno, name = *entry.match(/(\d+):in `(.+)'/) + + COLLECTED["#{name}:#{lineno}"] << code + end +end + +namespace :extract do + desc "Extract the whitequark/parser tests" + task :whitequark do + directory = File.expand_path("../tmp/parser", __dir__) + unless File.directory?(directory) + sh "git clone --depth 1 https://github.com/whitequark/parser #{directory}" + end + + mkdir_p "#{directory}/extract" + touch "#{directory}/extract/helper.rb" + touch "#{directory}/extract/parse_helper.rb" + touch "#{directory}/extract/extracted.txt" + $:.unshift "#{directory}/extract" + + require "parser/current" + require "minitest/autorun" + require_relative "#{directory}/test/test_parser" + + Minitest.after_run do + filepath = File.expand_path("../test/translation/parser.txt", __dir__) + + File.open(filepath, "w") do |file| + ParseHelper::COLLECTED.sort.each do |(key, codes)| + if codes.length == 1 + file.puts("!!! #{key}\n#{codes.first}") + else + codes.each_with_index do |code, index| + file.puts("!!! #{key}:#{index}\n#{code}") + end + end + end + end + end + end +end diff --git a/test/fixtures/next.rb b/test/fixtures/next.rb index be667951..79a8c62e 100644 --- a/test/fixtures/next.rb +++ b/test/fixtures/next.rb @@ -65,3 +65,10 @@ next([1, 2]) - next 1, 2 +% +next fun foo do end +- +next( + fun foo do + end +) diff --git a/test/node_test.rb b/test/node_test.rb index 9660b341..19fbeed2 100644 --- a/test/node_test.rb +++ b/test/node_test.rb @@ -60,7 +60,7 @@ def test_arg_paren_heredoc ARGUMENT SOURCE - at = location(lines: 1..3, chars: 6..28) + at = location(lines: 1..3, chars: 6..37) assert_node(ArgParen, source, at: at, &:arguments) end @@ -533,7 +533,7 @@ def test_heredoc HEREDOC SOURCE - at = location(lines: 1..3, chars: 0..22) + at = location(lines: 1..3, chars: 0..30) assert_node(Heredoc, source, at: at) end @@ -544,7 +544,7 @@ def test_heredoc_beg HEREDOC SOURCE - at = location(chars: 0..11) + at = location(chars: 0..10) assert_node(HeredocBeg, source, at: at, &:beginning) end @@ -555,7 +555,7 @@ def test_heredoc_end HEREDOC SOURCE - at = location(lines: 3..3, chars: 22..31, columns: 0..9) + at = location(lines: 3..3, chars: 22..30, columns: 0..8) assert_node(HeredocEnd, source, at: at, &:ending) end @@ -950,7 +950,7 @@ def test_var_field guard_version("3.1.0") do def test_pinned_var_ref source = "foo in ^bar" - at = location(chars: 8..11) + at = location(chars: 7..11) assert_node(PinnedVarRef, source, at: at, &:pattern) end @@ -1008,7 +1008,7 @@ def test_xstring_heredoc HEREDOC SOURCE - at = location(lines: 1..3, chars: 0..18) + at = location(lines: 1..3, chars: 0..26) assert_node(Heredoc, source, at: at) end diff --git a/test/ruby-syntax-fixtures b/test/ruby-syntax-fixtures deleted file mode 160000 index 5b333f5a..00000000 --- a/test/ruby-syntax-fixtures +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5b333f5a34d6fb08f88acc93b69c7d19b3fee8e7 diff --git a/test/ruby_syntax_fixtures_test.rb b/test/ruby_syntax_fixtures_test.rb deleted file mode 100644 index c5c13b27..00000000 --- a/test/ruby_syntax_fixtures_test.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -# The ruby-syntax-fixtures repository tests against the current Ruby syntax, so -# we don't execute this test unless we're running 3.2 or above. -return unless RUBY_VERSION >= "3.2" - -require_relative "test_helper" - -module SyntaxTree - class RubySyntaxFixturesTest < Minitest::Test - Dir[ - File.expand_path("ruby-syntax-fixtures/**/*.rb", __dir__) - ].each do |file| - define_method "test_ruby_syntax_fixtures_#{file}" do - refute_nil(SyntaxTree.parse(SyntaxTree.read(file))) - end - end - end -end diff --git a/test/suites/helper.rb b/test/suites/helper.rb deleted file mode 100644 index b0f8c427..00000000 --- a/test/suites/helper.rb +++ /dev/null @@ -1,3 +0,0 @@ -# frozen_string_literal: true - -require "parser/current" diff --git a/test/suites/parse_helper.rb b/test/suites/parse_helper.rb deleted file mode 100644 index 04fe8123..00000000 --- a/test/suites/parse_helper.rb +++ /dev/null @@ -1,175 +0,0 @@ -# frozen_string_literal: true - -module ParseHelper - include AST::Sexp - - CURRENT_VERSION = RUBY_VERSION.split(".")[0..1].join(".").freeze - ALL_VERSIONS = %w[1.8 1.9 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 3.0 3.1 3.2 mac ios] - - known_failures = [ - # I think this may be a bug in the parser gem's precedence calculation. - # Unary plus appears to be parsed as part of the number literal in CRuby, - # but parser is parsing it as a separate operator. - "test_unary_num_pow_precedence:3505", - - # Not much to be done about this. Basically, regular expressions with named - # capture groups that use the =~ operator inject local variables into the - # current scope. In the parser gem, it detects this and changes future - # references to that name to be a local variable instead of a potential - # method call. CRuby does not do this. - "test_lvar_injecting_match:3778", - - # This is failing because CRuby is not marking values captured in hash - # patterns as local variables, while the parser gem is. - "test_pattern_matching_hash:8971", - - # This is not actually allowed in the CRuby parser but the parser gem thinks - # it is allowed. - "test_pattern_matching_hash_with_string_keys:9016", - "test_pattern_matching_hash_with_string_keys:9027", - "test_pattern_matching_hash_with_string_keys:9038", - "test_pattern_matching_hash_with_string_keys:9060", - "test_pattern_matching_hash_with_string_keys:9071", - "test_pattern_matching_hash_with_string_keys:9082", - - # This happens with pattern matching where you're matching a literal value - # inside parentheses, which doesn't really do anything. Ripper doesn't - # capture that this value is inside a parentheses, so it's hard to translate - # properly. - "test_pattern_matching_expr_in_paren:9206", - - # These are also failing because of CRuby not marking values captured in - # hash patterns as local variables. - "test_pattern_matching_single_line_allowed_omission_of_parentheses:9205", - "test_pattern_matching_single_line_allowed_omission_of_parentheses:9581", - "test_pattern_matching_single_line_allowed_omission_of_parentheses:9611", - - # I'm not even sure what this is testing, because the code is invalid in - # CRuby. - "test_control_meta_escape_chars_in_regexp__since_31:*", - ] - - # These are failures that we need to take care of (or determine the reason - # that we're not going to handle them). - todo_failures = [ - "test_dedenting_heredoc:334", - "test_dedenting_heredoc:390", - "test_dedenting_heredoc:399", - "test_slash_newline_in_heredocs:7194", - "test_parser_slash_slash_n_escaping_in_literals:*", - "test_cond_match_current_line:4801", - "test_forwarded_restarg:*", - "test_forwarded_kwrestarg:*", - "test_forwarded_argument_with_restarg:*", - "test_forwarded_argument_with_kwrestarg:*" - ] - - if CURRENT_VERSION <= "2.7" - # I'm not sure why this is failing on 2.7.0, but we'll turn it off for now - # until we have more time to investigate. - todo_failures.push("test_pattern_matching_hash:*") - end - - if CURRENT_VERSION <= "3.0" - # In < 3.0, there are some changes to the way the parser gem handles - # forwarded args. We should eventually support this, but for now we're going - # to mark them as todo. - todo_failures.push( - "test_forward_arg:*", - "test_forward_args_legacy:*", - "test_endless_method_forwarded_args_legacy:*", - "test_trailing_forward_arg:*" - ) - end - - if CURRENT_VERSION == "3.1" - # This test actually fails on 3.1.0, even though it's marked as being since - # 3.1. So we're going to skip this test on 3.1, but leave it in for other - # versions. - known_failures.push( - "test_multiple_pattern_matches:11086", - "test_multiple_pattern_matches:11102" - ) - end - - # This is the list of all failures. - FAILURES = (known_failures + todo_failures).freeze - - private - - def assert_context(*) - end - - def assert_diagnoses(*) - end - - def assert_diagnoses_many(*) - end - - def refute_diagnoses(*) - end - - def with_versions(*) - end - - def assert_parses(_ast, code, _source_maps = "", versions = ALL_VERSIONS) - # We're going to skip any examples that aren't for the current version of - # Ruby. - return unless versions.include?(CURRENT_VERSION) - - # We're going to skip any examples that are for older Ruby versions that we - # do not support. - return if (versions & %w[3.1 3.2]).empty? - - caller(1, 3).each do |line| - _, lineno, name = *line.match(/(\d+):in `(.+)'/) - - # Return directly and don't do anything if it's a known failure. - return if FAILURES.include?("#{name}:#{lineno}") - return if FAILURES.include?("#{name}:*") - end - - expected = parse(code) - return if expected.nil? - - buffer = expected.location.expression.source_buffer - actual = SyntaxTree::Translation.to_parser(SyntaxTree.parse(code), buffer) - assert_equal(expected, actual) - end - - def parse(code) - parser = Parser::CurrentRuby.default_parser - parser.diagnostics.consumer = ->(*) {} - - buffer = Parser::Source::Buffer.new("(string)", 1) - buffer.source = code - - parser.parse(buffer) - rescue Parser::SyntaxError - end -end - -if ENV["PARSER_LOCATION"] - # Modify the source map == check so that it doesn't check against the node - # itself so we don't get into a recursive loop. - Parser::Source::Map.prepend( - Module.new do - def ==(other) - self.class == other.class && - (instance_variables - %i[@node]).map do |ivar| - instance_variable_get(ivar) == other.instance_variable_get(ivar) - end.reduce(:&) - end - end - ) - - # Next, ensure that we're comparing the nodes and also comparing the source - # ranges so that we're getting all of the necessary information. - Parser::AST::Node.prepend( - Module.new do - def ==(other) - super && (location == other.location) - end - end - ) -end diff --git a/test/suites/parser b/test/suites/parser deleted file mode 160000 index 8de8b7fa..00000000 --- a/test/suites/parser +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8de8b7fa7af471a2159860d6a0a5b615eac9c83c diff --git a/test/translation/parser.txt b/test/translation/parser.txt new file mode 100644 index 00000000..5e9e8d31 --- /dev/null +++ b/test/translation/parser.txt @@ -0,0 +1,1824 @@ +!!! assert_parses_args:2249:0 +def f (foo: 1, bar: 2, **baz, &b); end +!!! assert_parses_args:2249:1 +def f (foo: 1, &b); end +!!! assert_parses_args:2249:2 +def f **baz, &b; end +!!! assert_parses_args:2249:3 +def f *, **; end +!!! assert_parses_args:2249:4 +def f a, o=1, *r, &b; end +!!! assert_parses_args:2249:5 +def f a, o=1, *r, p, &b; end +!!! assert_parses_args:2249:6 +def f a, o=1, &b; end +!!! assert_parses_args:2249:7 +def f a, o=1, p, &b; end +!!! assert_parses_args:2249:8 +def f a, *r, &b; end +!!! assert_parses_args:2249:9 +def f a, *r, p, &b; end +!!! assert_parses_args:2249:10 +def f a, &b; end +!!! assert_parses_args:2249:11 +def f o=1, *r, &b; end +!!! assert_parses_args:2249:12 +def f o=1, *r, p, &b; end +!!! assert_parses_args:2249:13 +def f o=1, &b; end +!!! assert_parses_args:2249:14 +def f o=1, p, &b; end +!!! assert_parses_args:2249:15 +def f *r, &b; end +!!! assert_parses_args:2249:16 +def f *r, p, &b; end +!!! assert_parses_args:2249:17 +def f &b; end +!!! assert_parses_args:2249:18 +def f ; end +!!! assert_parses_args:2249:19 +def f (((a))); end +!!! assert_parses_args:2249:20 +def f ((a, a1)); end +!!! assert_parses_args:2249:21 +def f ((a, *r)); end +!!! assert_parses_args:2249:22 +def f ((a, *r, p)); end +!!! assert_parses_args:2249:23 +def f ((a, *)); end +!!! assert_parses_args:2249:24 +def f ((a, *, p)); end +!!! assert_parses_args:2249:25 +def f ((*r)); end +!!! assert_parses_args:2249:26 +def f ((*r, p)); end +!!! assert_parses_args:2249:27 +def f ((*)); end +!!! assert_parses_args:2249:28 +def f ((*, p)); end +!!! assert_parses_args:2249:29 +def f foo: +; end +!!! assert_parses_args:2249:30 +def f foo: -1 +; end +!!! assert_parses_blockargs:2506:0 +f{ |a| } +!!! assert_parses_blockargs:2506:1 +f{ |a, b,| } +!!! assert_parses_blockargs:2506:2 +f{ |a| } +!!! assert_parses_blockargs:2506:3 +f{ |foo:| } +!!! assert_parses_blockargs:2506:4 +f{ } +!!! assert_parses_blockargs:2506:5 +f{ | | } +!!! assert_parses_blockargs:2506:6 +f{ |;a| } +!!! assert_parses_blockargs:2506:7 +f{ |; +a +| } +!!! assert_parses_blockargs:2506:8 +f{ || } +!!! assert_parses_blockargs:2506:9 +f{ |a| } +!!! assert_parses_blockargs:2506:10 +f{ |a, c| } +!!! assert_parses_blockargs:2506:11 +f{ |a,| } +!!! assert_parses_blockargs:2506:12 +f{ |a, &b| } +!!! assert_parses_blockargs:2506:13 +f{ |a, *s, &b| } +!!! assert_parses_blockargs:2506:14 +f{ |a, *, &b| } +!!! assert_parses_blockargs:2506:15 +f{ |a, *s| } +!!! assert_parses_blockargs:2506:16 +f{ |a, *| } +!!! assert_parses_blockargs:2506:17 +f{ |*s, &b| } +!!! assert_parses_blockargs:2506:18 +f{ |*, &b| } +!!! assert_parses_blockargs:2506:19 +f{ |*s| } +!!! assert_parses_blockargs:2506:20 +f{ |*| } +!!! assert_parses_blockargs:2506:21 +f{ |&b| } +!!! assert_parses_blockargs:2506:22 +f{ |a, o=1, o1=2, *r, &b| } +!!! assert_parses_blockargs:2506:23 +f{ |a, o=1, *r, p, &b| } +!!! assert_parses_blockargs:2506:24 +f{ |a, o=1, &b| } +!!! assert_parses_blockargs:2506:25 +f{ |a, o=1, p, &b| } +!!! assert_parses_blockargs:2506:26 +f{ |a, *r, p, &b| } +!!! assert_parses_blockargs:2506:27 +f{ |o=1, *r, &b| } +!!! assert_parses_blockargs:2506:28 +f{ |o=1, *r, p, &b| } +!!! assert_parses_blockargs:2506:29 +f{ |o=1, &b| } +!!! assert_parses_blockargs:2506:30 +f{ |o=1, p, &b| } +!!! assert_parses_blockargs:2506:31 +f{ |*r, p, &b| } +!!! assert_parses_blockargs:2506:32 +f{ |foo: 1, bar: 2, **baz, &b| } +!!! assert_parses_blockargs:2506:33 +f{ |foo: 1, &b| } +!!! assert_parses_blockargs:2506:34 +f{ |**baz, &b| } +!!! assert_parses_pattern_match:8503:0 +case foo; in self then true; end +!!! assert_parses_pattern_match:8503:1 +case foo; in 1..2 then true; end +!!! assert_parses_pattern_match:8503:2 +case foo; in 1.. then true; end +!!! assert_parses_pattern_match:8503:3 +case foo; in ..2 then true; end +!!! assert_parses_pattern_match:8503:4 +case foo; in 1...2 then true; end +!!! assert_parses_pattern_match:8503:5 +case foo; in 1... then true; end +!!! assert_parses_pattern_match:8503:6 +case foo; in ...2 then true; end +!!! assert_parses_pattern_match:8503:7 +case foo; in [*x, 1 => a, *y] then true; end +!!! assert_parses_pattern_match:8503:8 +case foo; in String(*, 1, *) then true; end +!!! assert_parses_pattern_match:8503:9 +case foo; in Array[*, 1, *] then true; end +!!! assert_parses_pattern_match:8503:10 +case foo; in *, 42, * then true; end +!!! assert_parses_pattern_match:8503:11 +case foo; in x, then nil; end +!!! assert_parses_pattern_match:8503:12 +case foo; in *x then nil; end +!!! assert_parses_pattern_match:8503:13 +case foo; in * then nil; end +!!! assert_parses_pattern_match:8503:14 +case foo; in x, y then nil; end +!!! assert_parses_pattern_match:8503:15 +case foo; in x, y, then nil; end +!!! assert_parses_pattern_match:8503:16 +case foo; in x, *y, z then nil; end +!!! assert_parses_pattern_match:8503:17 +case foo; in *x, y, z then nil; end +!!! assert_parses_pattern_match:8503:18 +case foo; in 1, "a", [], {} then nil; end +!!! assert_parses_pattern_match:8503:19 +case foo; in ->{ 42 } then true; end +!!! assert_parses_pattern_match:8503:20 +case foo; in A(1, 2) then true; end +!!! assert_parses_pattern_match:8503:21 +case foo; in A(x:) then true; end +!!! assert_parses_pattern_match:8503:22 +case foo; in A() then true; end +!!! assert_parses_pattern_match:8503:23 +case foo; in A[1, 2] then true; end +!!! assert_parses_pattern_match:8503:24 +case foo; in A[x:] then true; end +!!! assert_parses_pattern_match:8503:25 +case foo; in A[] then true; end +!!! assert_parses_pattern_match:8503:26 +case foo; in x then x; end +!!! assert_parses_pattern_match:8503:27 +case foo; in {} then true; end +!!! assert_parses_pattern_match:8503:28 +case foo; in a: 1 then true; end +!!! assert_parses_pattern_match:8503:29 +case foo; in { a: 1 } then true; end +!!! assert_parses_pattern_match:8503:30 +case foo; in { a: 1, } then true; end +!!! assert_parses_pattern_match:8503:31 +case foo; in a: then true; end +!!! assert_parses_pattern_match:8503:32 +case foo; in **a then true; end +!!! assert_parses_pattern_match:8503:33 +case foo; in ** then true; end +!!! assert_parses_pattern_match:8503:34 +case foo; in a: 1, b: 2 then true; end +!!! assert_parses_pattern_match:8503:35 +case foo; in a:, b: then true; end +!!! assert_parses_pattern_match:8503:36 +case foo; in a: 1, _a:, ** then true; end +!!! assert_parses_pattern_match:8503:37 +case foo; + in {a: 1 + } + false + ; end +!!! assert_parses_pattern_match:8503:38 +case foo; + in {a: + 2} + false + ; end +!!! assert_parses_pattern_match:8503:39 +case foo; + in {Foo: 42 + } + false + ; end +!!! assert_parses_pattern_match:8503:40 +case foo; + in a: {b:}, c: + p c + ; end +!!! assert_parses_pattern_match:8503:41 +case foo; + in {a: + } + true + ; end +!!! assert_parses_pattern_match:8503:42 +case foo; in A then true; end +!!! assert_parses_pattern_match:8503:43 +case foo; in A::B then true; end +!!! assert_parses_pattern_match:8503:44 +case foo; in ::A then true; end +!!! assert_parses_pattern_match:8503:45 +case foo; in [x] then nil; end +!!! assert_parses_pattern_match:8503:46 +case foo; in [x,] then nil; end +!!! assert_parses_pattern_match:8503:47 +case foo; in [x, y] then true; end +!!! assert_parses_pattern_match:8503:48 +case foo; in [x, y,] then true; end +!!! assert_parses_pattern_match:8503:49 +case foo; in [x, y, *] then true; end +!!! assert_parses_pattern_match:8503:50 +case foo; in [x, y, *z] then true; end +!!! assert_parses_pattern_match:8503:51 +case foo; in [x, *y, z] then true; end +!!! assert_parses_pattern_match:8503:52 +case foo; in [x, *, y] then true; end +!!! assert_parses_pattern_match:8503:53 +case foo; in [*x, y] then true; end +!!! assert_parses_pattern_match:8503:54 +case foo; in [*, x] then true; end +!!! assert_parses_pattern_match:8503:55 +case foo; in (1) then true; end +!!! assert_parses_pattern_match:8503:56 +case foo; in x if true; nil; end +!!! assert_parses_pattern_match:8503:57 +case foo; in x unless true; nil; end +!!! assert_parses_pattern_match:8503:58 +case foo; in 1; end +!!! assert_parses_pattern_match:8503:59 +case foo; in ^foo then nil; end +!!! assert_parses_pattern_match:8503:60 +case foo; in "a": then true; end +!!! assert_parses_pattern_match:8503:61 +case foo; in "#{ 'a' }": then true; end +!!! assert_parses_pattern_match:8503:62 +case foo; in "#{ %q{a} }": then true; end +!!! assert_parses_pattern_match:8503:63 +case foo; in "#{ %Q{a} }": then true; end +!!! assert_parses_pattern_match:8503:64 +case foo; in "a": 1 then true; end +!!! assert_parses_pattern_match:8503:65 +case foo; in "#{ 'a' }": 1 then true; end +!!! assert_parses_pattern_match:8503:66 +case foo; in "#{ %q{a} }": 1 then true; end +!!! assert_parses_pattern_match:8503:67 +case foo; in "#{ %Q{a} }": 1 then true; end +!!! assert_parses_pattern_match:8503:68 +case foo; in ^(42) then nil; end +!!! assert_parses_pattern_match:8503:69 +case foo; in { foo: ^(42) } then nil; end +!!! assert_parses_pattern_match:8503:70 +case foo; in ^(0+0) then nil; end +!!! assert_parses_pattern_match:8503:71 +case foo; in ^@a; end +!!! assert_parses_pattern_match:8503:72 +case foo; in ^@@TestPatternMatching; end +!!! assert_parses_pattern_match:8503:73 +case foo; in ^$TestPatternMatching; end +!!! assert_parses_pattern_match:8503:74 +case foo; in ^(1 +); end +!!! assert_parses_pattern_match:8503:75 +case foo; in 1 | 2 then true; end +!!! assert_parses_pattern_match:8503:76 +case foo; in 1 => a then true; end +!!! assert_parses_pattern_match:8503:77 +case foo; in **nil then true; end +!!! block in test_endless_comparison_method:10392:0 +def ===(other) = do_something +!!! block in test_endless_comparison_method:10392:1 +def ==(other) = do_something +!!! block in test_endless_comparison_method:10392:2 +def !=(other) = do_something +!!! block in test_endless_comparison_method:10392:3 +def <=(other) = do_something +!!! block in test_endless_comparison_method:10392:4 +def >=(other) = do_something +!!! block in test_endless_comparison_method:10392:5 +def !=(other) = do_something +!!! block in test_parser_slash_slash_n_escaping_in_literals:7327:0 +'a\ +b' +!!! block in test_parser_slash_slash_n_escaping_in_literals:7327:1 +<<-'HERE' +a\ +b +HERE +!!! block in test_parser_slash_slash_n_escaping_in_literals:7327:2 +%q{a\ +b} +!!! block in test_parser_slash_slash_n_escaping_in_literals:7327:3 +"a\ +b" +!!! block in test_parser_slash_slash_n_escaping_in_literals:7327:4 +<<-"HERE" +a\ +b +HERE +!!! block in test_parser_slash_slash_n_escaping_in_literals:7327:5 +%{a\ +b} +!!! block in test_parser_slash_slash_n_escaping_in_literals:7327:6 +%Q{a\ +b} +!!! block in test_parser_slash_slash_n_escaping_in_literals:7327:7 +%w{a\ +b} +!!! block in test_parser_slash_slash_n_escaping_in_literals:7327:8 +%W{a\ +b} +!!! block in test_parser_slash_slash_n_escaping_in_literals:7327:9 +%i{a\ +b} +!!! block in test_parser_slash_slash_n_escaping_in_literals:7327:10 +%I{a\ +b} +!!! block in test_parser_slash_slash_n_escaping_in_literals:7327:11 +:'a\ +b' +!!! block in test_parser_slash_slash_n_escaping_in_literals:7327:12 +%s{a\ +b} +!!! block in test_parser_slash_slash_n_escaping_in_literals:7327:13 +:"a\ +b" +!!! block in test_parser_slash_slash_n_escaping_in_literals:7327:14 +/a\ +b/ +!!! block in test_parser_slash_slash_n_escaping_in_literals:7327:15 +%r{a\ +b} +!!! block in test_parser_slash_slash_n_escaping_in_literals:7327:16 +%x{a\ +b} +!!! block in test_parser_slash_slash_n_escaping_in_literals:7327:17 +`a\ +b` +!!! block in test_parser_slash_slash_n_escaping_in_literals:7327:18 +<<-`HERE` +a\ +b +HERE +!!! block in test_ruby_bug_11873_a:6017:0 +a b{c d}, :e do end +!!! block in test_ruby_bug_11873_a:6017:1 +a b{c d}, 1 do end +!!! block in test_ruby_bug_11873_a:6017:2 +a b{c d}, 1.0 do end +!!! block in test_ruby_bug_11873_a:6017:3 +a b{c d}, 1.0r do end +!!! block in test_ruby_bug_11873_a:6017:4 +a b{c d}, 1.0i do end +!!! block in test_ruby_bug_11873_a:6022:0 +a b{c(d)}, :e do end +!!! block in test_ruby_bug_11873_a:6022:1 +a b{c(d)}, 1 do end +!!! block in test_ruby_bug_11873_a:6022:2 +a b{c(d)}, 1.0 do end +!!! block in test_ruby_bug_11873_a:6022:3 +a b{c(d)}, 1.0r do end +!!! block in test_ruby_bug_11873_a:6022:4 +a b{c(d)}, 1.0i do end +!!! block in test_ruby_bug_11873_a:6036:0 +a b(c d), :e do end +!!! block in test_ruby_bug_11873_a:6036:1 +a b(c d), 1 do end +!!! block in test_ruby_bug_11873_a:6036:2 +a b(c d), 1.0 do end +!!! block in test_ruby_bug_11873_a:6036:3 +a b(c d), 1.0r do end +!!! block in test_ruby_bug_11873_a:6036:4 +a b(c d), 1.0i do end +!!! block in test_ruby_bug_11873_a:6041:0 +a b(c(d)), :e do end +!!! block in test_ruby_bug_11873_a:6041:1 +a b(c(d)), 1 do end +!!! block in test_ruby_bug_11873_a:6041:2 +a b(c(d)), 1.0 do end +!!! block in test_ruby_bug_11873_a:6041:3 +a b(c(d)), 1.0r do end +!!! block in test_ruby_bug_11873_a:6041:4 +a b(c(d)), 1.0i do end +!!! test___ENCODING__:1037 +__ENCODING__ +!!! test___ENCODING___legacy_:1046 +__ENCODING__ +!!! test_alias:2020 +alias :foo bar +!!! test_alias_gvar:2032 +alias $a $b +!!! test_alias_gvar:2037 +alias $a $+ +!!! test_ambiuous_quoted_label_in_ternary_operator:7204 +a ? b & '': nil +!!! test_and:4447 +foo and bar +!!! test_and:4453 +foo && bar +!!! test_and_asgn:1748 +foo.a &&= 1 +!!! test_and_asgn:1758 +foo[0, 1] &&= 2 +!!! test_and_or_masgn:4475 +foo && (a, b = bar) +!!! test_and_or_masgn:4484 +foo || (a, b = bar) +!!! test_anonymous_blockarg:10861 +def foo(&); bar(&); end +!!! test_arg:2055 +def f(foo); end +!!! test_arg:2066 +def f(foo, bar); end +!!! test_arg_duplicate_ignored:2958 +def foo(_, _); end +!!! test_arg_duplicate_ignored:2972 +def foo(_a, _a); end +!!! test_arg_label:3012 +def foo() a:b end +!!! test_arg_label:3019 +def foo + a:b end +!!! test_arg_label:3026 +f { || a:b } +!!! test_arg_scope:2238 +lambda{|;a|a} +!!! test_args_args_assocs:4077 +fun(foo, :foo => 1) +!!! test_args_args_assocs:4083 +fun(foo, :foo => 1, &baz) +!!! test_args_args_assocs_comma:4092 +foo[bar, :baz => 1,] +!!! test_args_args_comma:3941 +foo[bar,] +!!! test_args_args_star:3908 +fun(foo, *bar) +!!! test_args_args_star:3913 +fun(foo, *bar, &baz) +!!! test_args_assocs:4001 +fun(:foo => 1) +!!! test_args_assocs:4006 +fun(:foo => 1, &baz) +!!! test_args_assocs:4012 +self[:bar => 1] +!!! test_args_assocs:4021 +self.[]= foo, :a => 1 +!!! test_args_assocs:4031 +yield(:foo => 42) +!!! test_args_assocs:4039 +super(:foo => 42) +!!! test_args_assocs_comma:4068 +foo[:baz => 1,] +!!! test_args_assocs_legacy:3951 +fun(:foo => 1) +!!! test_args_assocs_legacy:3956 +fun(:foo => 1, &baz) +!!! test_args_assocs_legacy:3962 +self[:bar => 1] +!!! test_args_assocs_legacy:3971 +self.[]= foo, :a => 1 +!!! test_args_assocs_legacy:3981 +yield(:foo => 42) +!!! test_args_assocs_legacy:3989 +super(:foo => 42) +!!! test_args_block_pass:3934 +fun(&bar) +!!! test_args_cmd:3901 +fun(f bar) +!!! test_args_star:3921 +fun(*bar) +!!! test_args_star:3926 +fun(*bar, &baz) +!!! test_array_assocs:629 +[ 1 => 2 ] +!!! test_array_assocs:637 +[ 1, 2 => 3 ] +!!! test_array_plain:589 +[1, 2] +!!! test_array_splat:598 +[1, *foo, 2] +!!! test_array_splat:611 +[1, *foo] +!!! test_array_splat:622 +[*foo] +!!! test_array_symbols:695 +%i[foo bar] +!!! test_array_symbols_empty:732 +%i[] +!!! test_array_symbols_empty:740 +%I() +!!! test_array_symbols_interp:706 +%I[foo #{bar}] +!!! test_array_symbols_interp:721 +%I[foo#{bar}] +!!! test_array_words:647 +%w[foo bar] +!!! test_array_words_empty:682 +%w[] +!!! test_array_words_empty:689 +%W() +!!! test_array_words_interp:657 +%W[foo #{bar}] +!!! test_array_words_interp:671 +%W[foo #{bar}foo#@baz] +!!! test_asgn_cmd:1126 +foo = m foo +!!! test_asgn_cmd:1130 +foo = bar = m foo +!!! test_asgn_mrhs:1449 +foo = bar, 1 +!!! test_asgn_mrhs:1456 +foo = *bar +!!! test_asgn_mrhs:1461 +foo = baz, *bar +!!! test_back_ref:995 +$+ +!!! test_bang:3434 +!foo +!!! test_bang_cmd:3448 +!m foo +!!! test_begin_cmdarg:5526 +p begin 1.times do 1 end end +!!! test_beginless_erange_after_newline:935 +foo +...100 +!!! test_beginless_irange_after_newline:923 +foo +..100 +!!! test_beginless_range:903 +..100 +!!! test_beginless_range:912 +...100 +!!! test_blockarg:2187 +def f(&block); end +!!! test_break:5037 +break(foo) +!!! test_break:5051 +break foo +!!! test_break:5057 +break() +!!! test_break:5064 +break +!!! test_break_block:5072 +break fun foo do end +!!! test_bug_435:7067 +"#{-> foo {}}" +!!! test_bug_447:7046 +m [] do end +!!! test_bug_447:7055 +m [], 1 do end +!!! test_bug_452:7080 +td (1_500).toString(); td.num do; end +!!! test_bug_466:7096 +foo "#{(1+1).to_i}" do; end +!!! test_bug_473:7113 +m "#{[]}" +!!! test_bug_480:7124 +m "#{}#{()}" +!!! test_bug_481:7136 +m def x(); end; 1.tap do end +!!! test_bug_ascii_8bit_in_literal:5880 +# coding:utf-8 + "\xD0\xBF\xD1\x80\xD0\xBE\xD0\xB2\xD0\xB5\xD1\x80\xD0\xBA\xD0\xB0" +!!! test_bug_cmd_string_lookahead:5752 +desc "foo" do end +!!! test_bug_cmdarg:5549 +assert dogs +!!! test_bug_cmdarg:5554 +assert do: true +!!! test_bug_cmdarg:5562 +f x: -> do meth do end end +!!! test_bug_def_no_paren_eql_begin:5799 +def foo +=begin +=end +end +!!! test_bug_do_block_in_call_args:5762 +bar def foo; self.each do end end +!!! test_bug_do_block_in_cmdarg:5777 +tap (proc do end) +!!! test_bug_do_block_in_hash_brace:6569 +p :foo, {a: proc do end, b: proc do end} +!!! test_bug_do_block_in_hash_brace:6587 +p :foo, {:a => proc do end, b: proc do end} +!!! test_bug_do_block_in_hash_brace:6605 +p :foo, {"a": proc do end, b: proc do end} +!!! test_bug_do_block_in_hash_brace:6623 +p :foo, {proc do end => proc do end, b: proc do end} +!!! test_bug_do_block_in_hash_brace:6643 +p :foo, {** proc do end, b: proc do end} +!!! test_bug_heredoc_do:5835 +f <<-TABLE do +TABLE +end +!!! test_bug_interp_single:5789 +"#{1}" +!!! test_bug_interp_single:5793 +%W"#{1}" +!!! test_bug_lambda_leakage:6550 +->(scope) {}; scope +!!! test_bug_regex_verification:6563 +/#)/x +!!! test_bug_rescue_empty_else:5813 +begin; rescue LoadError; else; end +!!! test_bug_while_not_parens_do:5805 +while not (true) do end +!!! test_case_cond:4844 +case; when foo; 'foo'; end +!!! test_case_cond_else:4857 +case; when foo; 'foo'; else 'bar'; end +!!! test_case_expr:4816 +case foo; when 'bar'; bar; end +!!! test_case_expr_else:4830 +case foo; when 'bar'; bar; else baz; end +!!! test_casgn_scoped:1192 +Bar::Foo = 10 +!!! test_casgn_toplevel:1181 +::Foo = 10 +!!! test_casgn_unscoped:1203 +Foo = 10 +!!! test_character:248 +?a +!!! test_class:1827 +class Foo; end +!!! test_class:1837 +class Foo end +!!! test_class_definition_in_while_cond:6870 +while class Foo; tap do end; end; break; end +!!! test_class_definition_in_while_cond:6882 +while class Foo a = tap do end; end; break; end +!!! test_class_definition_in_while_cond:6895 +while class << self; tap do end; end; break; end +!!! test_class_definition_in_while_cond:6907 +while class << self; a = tap do end; end; break; end +!!! test_class_super:1848 +class Foo < Bar; end +!!! test_class_super_label:1860 +class Foo < a:b; end +!!! test_comments_before_leading_dot__27:7750 +a # +# +.foo +!!! test_comments_before_leading_dot__27:7757 +a # + # +.foo +!!! test_comments_before_leading_dot__27:7764 +a # +# +&.foo +!!! test_comments_before_leading_dot__27:7771 +a # + # +&.foo +!!! test_complex:156 +42i +!!! test_complex:162 +42ri +!!! test_complex:168 +42.1i +!!! test_complex:174 +42.1ri +!!! test_cond_begin:4686 +if (bar); foo; end +!!! test_cond_begin_masgn:4695 +if (bar; a, b = foo); end +!!! test_cond_eflipflop:4758 +if foo...bar; end +!!! test_cond_eflipflop:4772 +!(foo...bar) +!!! test_cond_iflipflop:4735 +if foo..bar; end +!!! test_cond_iflipflop:4749 +!(foo..bar) +!!! test_cond_match_current_line:4781 +if /wat/; end +!!! test_cond_match_current_line:4801 +!/wat/ +!!! test_const_op_asgn:1536 +A += 1 +!!! test_const_op_asgn:1542 +::A += 1 +!!! test_const_op_asgn:1550 +B::A += 1 +!!! test_const_op_asgn:1558 +def x; self::A ||= 1; end +!!! test_const_op_asgn:1567 +def x; ::A ||= 1; end +!!! test_const_scoped:1020 +Bar::Foo +!!! test_const_toplevel:1011 +::Foo +!!! test_const_unscoped:1029 +Foo +!!! test_control_meta_escape_chars_in_regexp__since_31:10686 +/\c\xFF/ +!!! test_control_meta_escape_chars_in_regexp__since_31:10692 +/\c\M-\xFF/ +!!! test_control_meta_escape_chars_in_regexp__since_31:10698 +/\C-\xFF/ +!!! test_control_meta_escape_chars_in_regexp__since_31:10704 +/\C-\M-\xFF/ +!!! test_control_meta_escape_chars_in_regexp__since_31:10710 +/\M-\xFF/ +!!! test_control_meta_escape_chars_in_regexp__since_31:10716 +/\M-\C-\xFF/ +!!! test_control_meta_escape_chars_in_regexp__since_31:10722 +/\M-\c\xFF/ +!!! test_cpath:1807 +module ::Foo; end +!!! test_cpath:1813 +module Bar::Foo; end +!!! test_cvar:973 +@@foo +!!! test_cvasgn:1106 +@@var = 10 +!!! test_dedenting_heredoc:297 +p <<~E +E +!!! test_dedenting_heredoc:304 +p <<~E + E +!!! test_dedenting_heredoc:311 +p <<~E + x +E +!!! test_dedenting_heredoc:318 +p <<~E + ð +E +!!! test_dedenting_heredoc:325 +p <<~E + x + y +E +!!! test_dedenting_heredoc:334 +p <<~E + x + y +E +!!! test_dedenting_heredoc:343 +p <<~E + x + y +E +!!! test_dedenting_heredoc:352 +p <<~E + x + y +E +!!! test_dedenting_heredoc:361 +p <<~E + x + y +E +!!! test_dedenting_heredoc:370 +p <<~E + x + +y +E +!!! test_dedenting_heredoc:380 +p <<~E + x + + y +E +!!! test_dedenting_heredoc:390 +p <<~E + x + \ y +E +!!! test_dedenting_heredoc:399 +p <<~E + x + \ y +E +!!! test_dedenting_heredoc:408 +p <<~"E" + x + #{foo} +E +!!! test_dedenting_heredoc:419 +p <<~`E` + x + #{foo} +E +!!! test_dedenting_heredoc:430 +p <<~"E" + x + #{" y"} +E +!!! test_dedenting_interpolating_heredoc_fake_line_continuation:459 +<<~'FOO' + baz\\ + qux +FOO +!!! test_dedenting_non_interpolating_heredoc_line_continuation:451 +<<~'FOO' + baz\ + qux +FOO +!!! test_def:1899 +def foo; end +!!! test_def:1907 +def String; end +!!! test_def:1911 +def String=; end +!!! test_def:1915 +def until; end +!!! test_def:1919 +def BEGIN; end +!!! test_def:1923 +def END; end +!!! test_defined:1058 +defined? foo +!!! test_defined:1064 +defined?(foo) +!!! test_defined:1072 +defined? @foo +!!! test_defs:1929 +def self.foo; end +!!! test_defs:1937 +def self::foo; end +!!! test_defs:1945 +def (foo).foo; end +!!! test_defs:1949 +def String.foo; end +!!! test_defs:1954 +def String::foo; end +!!! test_empty_stmt:60 +!!! test_endless_method:9786 +def foo() = 42 +!!! test_endless_method:9798 +def inc(x) = x + 1 +!!! test_endless_method:9811 +def obj.foo() = 42 +!!! test_endless_method:9823 +def obj.inc(x) = x + 1 +!!! test_endless_method_command_syntax:9880 +def foo = puts "Hello" +!!! test_endless_method_command_syntax:9892 +def foo() = puts "Hello" +!!! test_endless_method_command_syntax:9904 +def foo(x) = puts x +!!! test_endless_method_command_syntax:9917 +def obj.foo = puts "Hello" +!!! test_endless_method_command_syntax:9931 +def obj.foo() = puts "Hello" +!!! test_endless_method_command_syntax:9945 +def rescued(x) = raise "to be caught" rescue "instance #{x}" +!!! test_endless_method_command_syntax:9964 +def self.rescued(x) = raise "to be caught" rescue "class #{x}" +!!! test_endless_method_command_syntax:9985 +def obj.foo(x) = puts x +!!! test_endless_method_forwarded_args_legacy:9840 +def foo(...) = bar(...) +!!! test_endless_method_with_rescue_mod:9855 +def m() = 1 rescue 2 +!!! test_endless_method_with_rescue_mod:9866 +def self.m() = 1 rescue 2 +!!! test_endless_method_without_args:10404 +def foo = 42 +!!! test_endless_method_without_args:10412 +def foo = 42 rescue nil +!!! test_endless_method_without_args:10423 +def self.foo = 42 +!!! test_endless_method_without_args:10432 +def self.foo = 42 rescue nil +!!! test_ensure:5261 +begin; meth; ensure; bar; end +!!! test_ensure_empty:5274 +begin ensure end +!!! test_false:96 +false +!!! test_float:129 +1.33 +!!! test_float:134 +-1.33 +!!! test_for:5002 +for a in foo do p a; end +!!! test_for:5014 +for a in foo; p a; end +!!! test_for_mlhs:5023 +for a, b in foo; p a, b; end +!!! test_forward_arg:7899 +def foo(...); bar(...); end +!!! test_forward_arg_with_open_args:10745 +def foo ... +end +!!! test_forward_arg_with_open_args:10752 +def foo a, b = 1, ... +end +!!! test_forward_arg_with_open_args:10770 +def foo(a, ...) bar(...) end +!!! test_forward_arg_with_open_args:10781 +def foo a, ... + bar(...) +end +!!! test_forward_arg_with_open_args:10792 +def foo b = 1, ... + bar(...) +end +!!! test_forward_arg_with_open_args:10804 +def foo ...; bar(...); end +!!! test_forward_arg_with_open_args:10814 +def foo a, ...; bar(...); end +!!! test_forward_arg_with_open_args:10825 +def foo b = 1, ...; bar(...); end +!!! test_forward_arg_with_open_args:10837 +(def foo ... + bar(...) +end) +!!! test_forward_arg_with_open_args:10848 +(def foo ...; bar(...); end) +!!! test_forward_args_legacy:7863 +def foo(...); bar(...); end +!!! test_forward_args_legacy:7875 +def foo(...); super(...); end +!!! test_forward_args_legacy:7887 +def foo(...); end +!!! test_forwarded_argument_with_kwrestarg:10962 +def foo(argument, **); bar(argument, **); end +!!! test_forwarded_argument_with_restarg:10923 +def foo(argument, *); bar(argument, *); end +!!! test_forwarded_kwrestarg:10943 +def foo(**); bar(**); end +!!! test_forwarded_restarg:10905 +def foo(*); bar(*); end +!!! test_gvar:980 +$foo +!!! test_gvasgn:1116 +$var = 10 +!!! test_hash_empty:750 +{ } +!!! test_hash_hashrocket:759 +{ 1 => 2 } +!!! test_hash_hashrocket:768 +{ 1 => 2, :foo => "bar" } +!!! test_hash_kwsplat:821 +{ foo: 2, **bar } +!!! test_hash_label:776 +{ foo: 2 } +!!! test_hash_label_end:789 +{ 'foo': 2 } +!!! test_hash_label_end:802 +{ 'foo': 2, 'bar': {}} +!!! test_hash_label_end:810 +f(a ? "a":1) +!!! test_hash_pair_value_omission:10040 +{a:, b:} +!!! test_hash_pair_value_omission:10054 +{puts:} +!!! test_hash_pair_value_omission:10065 +{BAR:} +!!! test_heredoc:263 +<(**nil) {} +!!! test_kwoptarg:2124 +def f(foo: 1); end +!!! test_kwrestarg_named:2135 +def f(**foo); end +!!! test_kwrestarg_unnamed:2146 +def f(**); end +!!! test_lbrace_arg_after_command_args:7235 +let (:a) { m do; end } +!!! test_lparenarg_after_lvar__since_25:6679 +meth (-1.3).abs +!!! test_lparenarg_after_lvar__since_25:6688 +foo (-1.3).abs +!!! test_lvar:959 +foo +!!! test_lvar_injecting_match:3778 +/(?bar)/ =~ 'bar'; match +!!! test_lvasgn:1084 +var = 10; var +!!! test_masgn:1247 +foo, bar = 1, 2 +!!! test_masgn:1258 +(foo, bar) = 1, 2 +!!! test_masgn:1268 +foo, bar, baz = 1, 2 +!!! test_masgn_attr:1390 +self.a, self[1, 2] = foo +!!! test_masgn_attr:1403 +self::a, foo = foo +!!! test_masgn_attr:1411 +self.A, foo = foo +!!! test_masgn_cmd:1439 +foo, bar = m foo +!!! test_masgn_const:1421 +self::A, foo = foo +!!! test_masgn_const:1429 +::A, foo = foo +!!! test_masgn_nested:1365 +a, (b, c) = foo +!!! test_masgn_nested:1379 +((b, )) = foo +!!! test_masgn_splat:1279 +@foo, @@bar = *foo +!!! test_masgn_splat:1288 +a, b = *foo, bar +!!! test_masgn_splat:1296 +a, *b = bar +!!! test_masgn_splat:1302 +a, *b, c = bar +!!! test_masgn_splat:1313 +a, * = bar +!!! test_masgn_splat:1319 +a, *, c = bar +!!! test_masgn_splat:1330 +*b = bar +!!! test_masgn_splat:1336 +*b, c = bar +!!! test_masgn_splat:1346 +* = bar +!!! test_masgn_splat:1352 +*, c, d = bar +!!! test_method_definition_in_while_cond:6816 +while def foo; tap do end; end; break; end +!!! test_method_definition_in_while_cond:6828 +while def self.foo; tap do end; end; break; end +!!! test_method_definition_in_while_cond:6841 +while def foo a = tap do end; end; break; end +!!! test_method_definition_in_while_cond:6854 +while def self.foo a = tap do end; end; break; end +!!! test_module:1789 +module Foo; end +!!! test_multiple_pattern_matches:11086 +{a: 0} => a: +{a: 0} => a: +!!! test_multiple_pattern_matches:11102 +{a: 0} in a: +{a: 0} in a: +!!! test_newline_in_hash_argument:11035 +obj.set foo: +1 +!!! test_newline_in_hash_argument:11046 +obj.set "foo": +1 +!!! test_newline_in_hash_argument:11057 +case foo +in a: +0 +true +in "b": +0 +true +end +!!! test_next:5131 +next(foo) +!!! test_next:5145 +next foo +!!! test_next:5151 +next() +!!! test_next:5158 +next +!!! test_next_block:5166 +next fun foo do end +!!! test_nil:66 +nil +!!! test_nil_expression:73 +() +!!! test_nil_expression:80 +begin end +!!! test_non_lvar_injecting_match:3793 +/#{1}(?bar)/ =~ 'bar' +!!! test_not:3462 +not foo +!!! test_not:3468 +not(foo) +!!! test_not:3474 +not() +!!! test_not_cmd:3488 +not m foo +!!! test_not_masgn__24:4672 +!(a, b = foo) +!!! test_nth_ref:1002 +$10 +!!! test_numbered_args_after_27:7358 +m { _1 + _9 } +!!! test_numbered_args_after_27:7373 +m do _1 + _9 end +!!! test_numbered_args_after_27:7390 +-> { _1 + _9} +!!! test_numbered_args_after_27:7405 +-> do _1 + _9 end +!!! test_numparam_outside_block:7512 +class A; _1; end +!!! test_numparam_outside_block:7520 +module A; _1; end +!!! test_numparam_outside_block:7528 +class << foo; _1; end +!!! test_numparam_outside_block:7536 +def self.m; _1; end +!!! test_numparam_outside_block:7545 +_1 +!!! test_op_asgn:1606 +foo.a += 1 +!!! test_op_asgn:1616 +foo::a += 1 +!!! test_op_asgn:1622 +foo.A += 1 +!!! test_op_asgn_cmd:1630 +foo.a += m foo +!!! test_op_asgn_cmd:1636 +foo::a += m foo +!!! test_op_asgn_cmd:1642 +foo.A += m foo +!!! test_op_asgn_cmd:1654 +foo::A += m foo +!!! test_op_asgn_index:1664 +foo[0, 1] += 2 +!!! test_op_asgn_index_cmd:1678 +foo[0, 1] += m foo +!!! test_optarg:2074 +def f foo = 1; end +!!! test_optarg:2084 +def f(foo=1, bar=2); end +!!! test_or:4461 +foo or bar +!!! test_or:4467 +foo || bar +!!! test_or_asgn:1724 +foo.a ||= 1 +!!! test_or_asgn:1734 +foo[0, 1] ||= 2 +!!! test_parser_bug_272:6528 +a @b do |c|;end +!!! test_parser_bug_490:7151 +def m; class << self; class C; end; end; end +!!! test_parser_bug_490:7162 +def m; class << self; module M; end; end; end +!!! test_parser_bug_490:7173 +def m; class << self; A = nil; end; end +!!! test_parser_bug_507:7265 +m = -> *args do end +!!! test_parser_bug_518:7277 +class A < B +end +!!! test_parser_bug_525:7287 +m1 :k => m2 do; m3() do end; end +!!! test_parser_bug_604:7737 +m a + b do end +!!! test_parser_bug_640:443 +<<~FOO + baz\ + qux +FOO +!!! test_parser_bug_645:9774 +-> (arg={}) {} +!!! test_parser_bug_830:10630 +/\(/ +!!! test_parser_drops_truncated_parts_of_squiggly_heredoc:10446 +<<~HERE + #{} +HERE +!!! test_pattern_matching__FILE__LINE_literals:9473 + case [__FILE__, __LINE__ + 1, __ENCODING__] + in [__FILE__, __LINE__, __ENCODING__] + end +!!! test_pattern_matching_blank_else:9390 +case 1; in 2; 3; else; end +!!! test_pattern_matching_else:9376 +case 1; in 2; 3; else; 4; end +!!! test_pattern_matching_single_line:9540 +1 => [a]; a +!!! test_pattern_matching_single_line:9552 +1 in [a]; a +!!! test_pattern_matching_single_line_allowed_omission_of_parentheses:9566 +[1, 2] => a, b; a +!!! test_pattern_matching_single_line_allowed_omission_of_parentheses:9581 +{a: 1} => a:; a +!!! test_pattern_matching_single_line_allowed_omission_of_parentheses:9596 +[1, 2] in a, b; a +!!! test_pattern_matching_single_line_allowed_omission_of_parentheses:9611 +{a: 1} in a:; a +!!! test_pattern_matching_single_line_allowed_omission_of_parentheses:9626 +{key: :value} in key: value; value +!!! test_pattern_matching_single_line_allowed_omission_of_parentheses:9643 +{key: :value} => key: value; value +!!! test_postexe:5486 +END { 1 } +!!! test_preexe:5467 +BEGIN { 1 } +!!! test_procarg0:2803 +m { |foo| } +!!! test_procarg0:2812 +m { |(foo, bar)| } +!!! test_range_endless:869 +1.. +!!! test_range_endless:877 +1... +!!! test_range_exclusive:861 +1...2 +!!! test_range_inclusive:853 +1..2 +!!! test_rational:142 +42r +!!! test_rational:148 +42.1r +!!! test_redo:5178 +redo +!!! test_regex_interp:551 +/foo#{bar}baz/ +!!! test_regex_plain:541 +/source/im +!!! test_resbody_list:5398 +begin; meth; rescue Exception; bar; end +!!! test_resbody_list_mrhs:5411 +begin; meth; rescue Exception, foo; bar; end +!!! test_resbody_list_var:5444 +begin; meth; rescue foo => ex; bar; end +!!! test_resbody_var:5426 +begin; meth; rescue => ex; bar; end +!!! test_resbody_var:5434 +begin; meth; rescue => @ex; bar; end +!!! test_rescue:5188 +begin; meth; rescue; foo; end +!!! test_rescue_else:5203 +begin; meth; rescue; foo; else; bar; end +!!! test_rescue_else_ensure:5302 +begin; meth; rescue; baz; else foo; ensure; bar end +!!! test_rescue_ensure:5286 +begin; meth; rescue; baz; ensure; bar; end +!!! test_rescue_in_lambda_block:6928 +-> do rescue; end +!!! test_rescue_mod:5319 +meth rescue bar +!!! test_rescue_mod_asgn:5331 +foo = meth rescue bar +!!! test_rescue_mod_masgn:5345 +foo, bar = meth rescue [1, 2] +!!! test_rescue_mod_op_assign:5365 +foo += meth rescue bar +!!! test_rescue_without_begin_end:5381 +meth do; foo; rescue; bar; end +!!! test_restarg_named:2094 +def f(*foo); end +!!! test_restarg_unnamed:2104 +def f(*); end +!!! test_retry:5457 +retry +!!! test_return:5084 +return(foo) +!!! test_return:5098 +return foo +!!! test_return:5104 +return() +!!! test_return:5111 +return +!!! test_return_block:5119 +return fun foo do end +!!! test_ruby_bug_10279:5905 +{a: if true then 42 end} +!!! test_ruby_bug_10653:5915 +true ? 1.tap do |n| p n end : 0 +!!! test_ruby_bug_10653:5945 +false ? raise {} : tap {} +!!! test_ruby_bug_10653:5958 +false ? raise do end : tap do end +!!! test_ruby_bug_11107:5973 +p ->() do a() do end end +!!! test_ruby_bug_11380:5985 +p -> { :hello }, a: 1 do end +!!! test_ruby_bug_11873:6353 +a b{c d}, "x" do end +!!! test_ruby_bug_11873:6367 +a b(c d), "x" do end +!!! test_ruby_bug_11873:6380 +a b{c(d)}, "x" do end +!!! test_ruby_bug_11873:6394 +a b(c(d)), "x" do end +!!! test_ruby_bug_11873:6407 +a b{c d}, /x/ do end +!!! test_ruby_bug_11873:6421 +a b(c d), /x/ do end +!!! test_ruby_bug_11873:6434 +a b{c(d)}, /x/ do end +!!! test_ruby_bug_11873:6448 +a b(c(d)), /x/ do end +!!! test_ruby_bug_11873:6461 +a b{c d}, /x/m do end +!!! test_ruby_bug_11873:6475 +a b(c d), /x/m do end +!!! test_ruby_bug_11873:6488 +a b{c(d)}, /x/m do end +!!! test_ruby_bug_11873:6502 +a b(c(d)), /x/m do end +!!! test_ruby_bug_11873_b:6050 +p p{p(p);p p}, tap do end +!!! test_ruby_bug_11989:6069 +p <<~"E" + x\n y +E +!!! test_ruby_bug_11990:6078 +p <<~E " y" + x +E +!!! test_ruby_bug_12073:6089 +a = 1; a b: 1 +!!! test_ruby_bug_12073:6102 +def foo raise; raise A::B, ''; end +!!! test_ruby_bug_12402:6116 +foo = raise(bar) rescue nil +!!! test_ruby_bug_12402:6127 +foo += raise(bar) rescue nil +!!! test_ruby_bug_12402:6139 +foo[0] += raise(bar) rescue nil +!!! test_ruby_bug_12402:6153 +foo.m += raise(bar) rescue nil +!!! test_ruby_bug_12402:6166 +foo::m += raise(bar) rescue nil +!!! test_ruby_bug_12402:6179 +foo.C += raise(bar) rescue nil +!!! test_ruby_bug_12402:6192 +foo::C ||= raise(bar) rescue nil +!!! test_ruby_bug_12402:6205 +foo = raise bar rescue nil +!!! test_ruby_bug_12402:6216 +foo += raise bar rescue nil +!!! test_ruby_bug_12402:6228 +foo[0] += raise bar rescue nil +!!! test_ruby_bug_12402:6242 +foo.m += raise bar rescue nil +!!! test_ruby_bug_12402:6255 +foo::m += raise bar rescue nil +!!! test_ruby_bug_12402:6268 +foo.C += raise bar rescue nil +!!! test_ruby_bug_12402:6281 +foo::C ||= raise bar rescue nil +!!! test_ruby_bug_12669:6296 +a = b = raise :x +!!! test_ruby_bug_12669:6305 +a += b = raise :x +!!! test_ruby_bug_12669:6314 +a = b += raise :x +!!! test_ruby_bug_12669:6323 +a += b += raise :x +!!! test_ruby_bug_12686:6334 +f (g rescue nil) +!!! test_ruby_bug_13547:7018 +meth[] {} +!!! test_ruby_bug_14690:7250 +let () { m(a) do; end } +!!! test_ruby_bug_15789:7622 +m ->(a = ->{_1}) {a} +!!! test_ruby_bug_15789:7636 +m ->(a: ->{_1}) {a} +!!! test_ruby_bug_9669:5889 +def a b: +return +end +!!! test_ruby_bug_9669:5895 +o = { +a: +1 +} +!!! test_sclass:1884 +class << foo; nil; end +!!! test_self:952 +self +!!! test_send_attr_asgn:3528 +foo.a = 1 +!!! test_send_attr_asgn:3536 +foo::a = 1 +!!! test_send_attr_asgn:3544 +foo.A = 1 +!!! test_send_attr_asgn:3552 +foo::A = 1 +!!! test_send_attr_asgn_conditional:3751 +a&.b = 1 +!!! test_send_binary_op:3308 +foo + 1 +!!! test_send_binary_op:3314 +foo - 1 +!!! test_send_binary_op:3318 +foo * 1 +!!! test_send_binary_op:3322 +foo / 1 +!!! test_send_binary_op:3326 +foo % 1 +!!! test_send_binary_op:3330 +foo ** 1 +!!! test_send_binary_op:3334 +foo | 1 +!!! test_send_binary_op:3338 +foo ^ 1 +!!! test_send_binary_op:3342 +foo & 1 +!!! test_send_binary_op:3346 +foo <=> 1 +!!! test_send_binary_op:3350 +foo < 1 +!!! test_send_binary_op:3354 +foo <= 1 +!!! test_send_binary_op:3358 +foo > 1 +!!! test_send_binary_op:3362 +foo >= 1 +!!! test_send_binary_op:3366 +foo == 1 +!!! test_send_binary_op:3376 +foo != 1 +!!! test_send_binary_op:3382 +foo === 1 +!!! test_send_binary_op:3386 +foo =~ 1 +!!! test_send_binary_op:3396 +foo !~ 1 +!!! test_send_binary_op:3402 +foo << 1 +!!! test_send_binary_op:3406 +foo >> 1 +!!! test_send_block_chain_cmd:3201 +meth 1 do end.fun bar +!!! test_send_block_chain_cmd:3212 +meth 1 do end.fun(bar) +!!! test_send_block_chain_cmd:3225 +meth 1 do end::fun bar +!!! test_send_block_chain_cmd:3236 +meth 1 do end::fun(bar) +!!! test_send_block_chain_cmd:3249 +meth 1 do end.fun bar do end +!!! test_send_block_chain_cmd:3261 +meth 1 do end.fun(bar) {} +!!! test_send_block_chain_cmd:3273 +meth 1 do end.fun {} +!!! test_send_block_conditional:3759 +foo&.bar {} +!!! test_send_call:3721 +foo.(1) +!!! test_send_call:3731 +foo::(1) +!!! test_send_conditional:3743 +a&.b +!!! test_send_index:3562 +foo[1, 2] +!!! test_send_index_asgn:3591 +foo[1, 2] = 3 +!!! test_send_index_asgn_legacy:3603 +foo[1, 2] = 3 +!!! test_send_index_cmd:3584 +foo[m bar] +!!! test_send_index_legacy:3573 +foo[1, 2] +!!! test_send_lambda:3615 +->{ } +!!! test_send_lambda:3625 +-> * { } +!!! test_send_lambda:3636 +-> do end +!!! test_send_lambda_args:3648 +->(a) { } +!!! test_send_lambda_args:3662 +-> (a) { } +!!! test_send_lambda_args_noparen:3686 +-> a: 1 { } +!!! test_send_lambda_args_noparen:3695 +-> a: { } +!!! test_send_lambda_args_shadow:3673 +->(a; foo, bar) { } +!!! test_send_lambda_legacy:3707 +->{ } +!!! test_send_op_asgn_conditional:3770 +a&.b &&= 1 +!!! test_send_plain:3105 +foo.fun +!!! test_send_plain:3112 +foo::fun +!!! test_send_plain:3119 +foo::Fun() +!!! test_send_plain_cmd:3128 +foo.fun bar +!!! test_send_plain_cmd:3135 +foo::fun bar +!!! test_send_plain_cmd:3142 +foo::Fun bar +!!! test_send_self:3044 +fun +!!! test_send_self:3050 +fun! +!!! test_send_self:3056 +fun(1) +!!! test_send_self_block:3066 +fun { } +!!! test_send_self_block:3070 +fun() { } +!!! test_send_self_block:3074 +fun(1) { } +!!! test_send_self_block:3078 +fun do end +!!! test_send_unary_op:3412 +-foo +!!! test_send_unary_op:3418 ++foo +!!! test_send_unary_op:3422 +~foo +!!! test_slash_newline_in_heredocs:7186 +<<~E + 1 \ + 2 + 3 +E +!!! test_slash_newline_in_heredocs:7194 +<<-E + 1 \ + 2 + 3 +E +!!! test_space_args_arg:4132 +fun (1) +!!! test_space_args_arg_block:4146 +fun (1) {} +!!! test_space_args_arg_block:4160 +foo.fun (1) {} +!!! test_space_args_arg_block:4176 +foo::fun (1) {} +!!! test_space_args_arg_call:4198 +fun (1).to_i +!!! test_space_args_arg_newline:4138 +fun (1 +) +!!! test_space_args_block:4430 +fun () {} +!!! test_space_args_cmd:4125 +fun (f bar) +!!! test_string___FILE__:241 +__FILE__ +!!! test_string_concat:226 +"foo#@a" "bar" +!!! test_string_dvar:215 +"#@a #@@a #$a" +!!! test_string_interp:200 +"foo#{bar}baz" +!!! test_string_plain:184 +'foobar' +!!! test_string_plain:191 +%q(foobar) +!!! test_super:3807 +super(foo) +!!! test_super:3815 +super foo +!!! test_super:3821 +super() +!!! test_super_block:3839 +super foo, bar do end +!!! test_super_block:3845 +super do end +!!! test_symbol_interp:484 +:"foo#{bar}baz" +!!! test_symbol_plain:469 +:foo +!!! test_symbol_plain:475 +:'foo' +!!! test_ternary:4605 +foo ? 1 : 2 +!!! test_ternary_ambiguous_symbol:4614 +t=1;(foo)?t:T +!!! test_trailing_forward_arg:8022 +def foo(a, b, ...); bar(a, 42, ...); end +!!! test_true:89 +true +!!! test_unary_num_pow_precedence:3505 ++2.0 ** 10 +!!! test_unary_num_pow_precedence:3512 +-2 ** 10 +!!! test_unary_num_pow_precedence:3519 +-2.0 ** 10 +!!! test_undef:2003 +undef foo, :bar, :"foo#{1}" +!!! test_unless:4529 +unless foo then bar; end +!!! test_unless:4537 +unless foo; bar; end +!!! test_unless_else:4573 +unless foo then bar; else baz; end +!!! test_unless_else:4582 +unless foo; bar; else baz; end +!!! test_unless_mod:4546 +bar unless foo +!!! test_until:4948 +until foo do meth end +!!! test_until:4955 +until foo; meth end +!!! test_until_mod:4963 +meth until foo +!!! test_until_post:4978 +begin meth end until foo +!!! test_var_and_asgn:1714 +a &&= 1 +!!! test_var_op_asgn:1498 +a += 1 +!!! test_var_op_asgn:1504 +@a |= 1 +!!! test_var_op_asgn:1510 +@@var |= 10 +!!! test_var_op_asgn:1514 +def a; @@var |= 10; end +!!! test_var_op_asgn_cmd:1521 +foo += m foo +!!! test_var_or_asgn:1706 +a ||= 1 +!!! test_when_multi:4895 +case foo; when 'bar', 'baz'; bar; end +!!! test_when_splat:4904 +case foo; when 1, *baz; bar; when *foo; end +!!! test_when_then:4883 +case foo; when 'bar' then bar; end +!!! test_while:4924 +while foo do meth end +!!! test_while:4932 +while foo; meth end +!!! test_while_mod:4941 +meth while foo +!!! test_while_post:4970 +begin meth end while foo +!!! test_xstring_interp:524 +`foo#{bar}baz` +!!! test_xstring_plain:515 +`foobar` +!!! test_yield:3855 +yield(foo) +!!! test_yield:3863 +yield foo +!!! test_yield:3869 +yield() +!!! test_yield:3877 +yield +!!! test_zsuper:3831 +super diff --git a/test/translation/parser_test.rb b/test/translation/parser_test.rb new file mode 100644 index 00000000..576d4ac1 --- /dev/null +++ b/test/translation/parser_test.rb @@ -0,0 +1,168 @@ +# frozen_string_literal: true + +require_relative "../test_helper" +require "parser/current" + +Parser::Builders::Default.modernize + +module SyntaxTree + module Translation + class ParserTest < Minitest::Test + known_failures = [ + # I think this may be a bug in the parser gem's precedence calculation. + # Unary plus appears to be parsed as part of the number literal in + # CRuby, but parser is parsing it as a separate operator. + "test_unary_num_pow_precedence:3505", + + # Not much to be done about this. Basically, regular expressions with + # named capture groups that use the =~ operator inject local variables + # into the current scope. In the parser gem, it detects this and changes + # future references to that name to be a local variable instead of a + # potential method call. CRuby does not do this. + "test_lvar_injecting_match:3778", + + # This is failing because CRuby is not marking values captured in hash + # patterns as local variables, while the parser gem is. + "test_pattern_matching_hash:8971", + + # This is not actually allowed in the CRuby parser but the parser gem + # thinks it is allowed. + "test_pattern_matching_hash_with_string_keys:9016", + "test_pattern_matching_hash_with_string_keys:9027", + "test_pattern_matching_hash_with_string_keys:9038", + "test_pattern_matching_hash_with_string_keys:9060", + "test_pattern_matching_hash_with_string_keys:9071", + "test_pattern_matching_hash_with_string_keys:9082", + + # This happens with pattern matching where you're matching a literal + # value inside parentheses, which doesn't really do anything. Ripper + # doesn't capture that this value is inside a parentheses, so it's hard + # to translate properly. + "test_pattern_matching_expr_in_paren:9206", + + # These are also failing because of CRuby not marking values captured in + # hash patterns as local variables. + "test_pattern_matching_single_line_allowed_omission_of_parentheses:*", + + # I'm not even sure what this is testing, because the code is invalid in + # CRuby. + "test_control_meta_escape_chars_in_regexp__since_31:*", + ] + + todo_failures = [ + "test_dedenting_heredoc:334", + "test_dedenting_heredoc:390", + "test_dedenting_heredoc:399", + "test_slash_newline_in_heredocs:7194", + "test_parser_slash_slash_n_escaping_in_literals:*", + "test_cond_match_current_line:4801", + "test_forwarded_restarg:*", + "test_forwarded_kwrestarg:*", + "test_forwarded_argument_with_restarg:*", + "test_forwarded_argument_with_kwrestarg:*" + ] + + current_version = RUBY_VERSION.split(".")[0..1].join(".") + + if current_version <= "2.7" + # I'm not sure why this is failing on 2.7.0, but we'll turn it off for + # now until we have more time to investigate. + todo_failures.push( + "test_pattern_matching_hash:*", + "test_pattern_matching_single_line:9552" + ) + end + + if current_version <= "3.0" + # In < 3.0, there are some changes to the way the parser gem handles + # forwarded args. We should eventually support this, but for now we're + # going to mark them as todo. + todo_failures.push( + "test_forward_arg:*", + "test_forward_args_legacy:*", + "test_endless_method_forwarded_args_legacy:*", + "test_trailing_forward_arg:*", + "test_forward_arg_with_open_args:10770", + ) + end + + if current_version == "3.1" + # This test actually fails on 3.1.0, even though it's marked as being + # since 3.1. So we're going to skip this test on 3.1, but leave it in + # for other versions. + known_failures.push( + "test_multiple_pattern_matches:11086", + "test_multiple_pattern_matches:11102" + ) + end + + if current_version < "3.2" || RUBY_ENGINE == "truffleruby" + known_failures.push( + "test_if_while_after_class__since_32:11004", + "test_if_while_after_class__since_32:11014", + "test_newline_in_hash_argument:11057" + ) + end + + all_failures = known_failures + todo_failures + + File + .foreach(File.expand_path("parser.txt", __dir__), chomp: true) + .slice_before { |line| line.start_with?("!!!") } + .each do |(prefix, *lines)| + name = prefix[4..] + next if all_failures.any? { |pattern| File.fnmatch?(pattern, name) } + + define_method(name) { assert_parses(lines.join("\n")) } + end + + private + + def assert_parses(source) + parser = ::Parser::CurrentRuby.default_parser + parser.diagnostics.consumer = ->(*) {} + + buffer = ::Parser::Source::Buffer.new("(string)", 1) + buffer.source = source + + expected = + begin + parser.parse(buffer) + rescue ::Parser::SyntaxError + # We can get a syntax error if we're parsing a fixture that was + # designed for a later Ruby version but we're running an earlier + # Ruby version. In this case we can just return early from the test. + end + + return if expected.nil? + node = SyntaxTree.parse(source) + assert_equal expected, SyntaxTree::Translation.to_parser(node, buffer) + end + end + end +end + +if ENV["PARSER_LOCATION"] + # Modify the source map == check so that it doesn't check against the node + # itself so we don't get into a recursive loop. + Parser::Source::Map.prepend( + Module.new do + def ==(other) + self.class == other.class && + (instance_variables - %i[@node]).map do |ivar| + instance_variable_get(ivar) == other.instance_variable_get(ivar) + end.reduce(:&) + end + end + ) + + # Next, ensure that we're comparing the nodes and also comparing the source + # ranges so that we're getting all of the necessary information. + Parser::AST::Node.prepend( + Module.new do + def ==(other) + super && (location == other.location) + end + end + ) +end