diff --git a/lib/prism/translation/ripper.rb b/lib/prism/translation/ripper.rb index 2f66bab97e..dd443e207f 100644 --- a/lib/prism/translation/ripper.rb +++ b/lib/prism/translation/ripper.rb @@ -28,7 +28,6 @@ module Translation # - on_lbracket # - on_lparen # - on_nl - # - on_op # - on_operator_ambiguous # - on_rbrace # - on_rbracket @@ -647,6 +646,10 @@ def visit_alias_global_variable_node(node) # ^^^^^^^^^ def visit_alternation_pattern_node(node) left = visit_pattern_node(node.left) + + bounds(node.operator_loc) + on_op("|") + right = visit_pattern_node(node.right) bounds(node.location) @@ -667,10 +670,14 @@ def visit_alternation_pattern_node(node) # ^^^^^^^ def visit_and_node(node) left = visit(node.left) + + bounds(node.operator_loc) if node.operator == "and" - bounds(node.operator_loc) on_kw("and") + else + on_op("&&") end + right = visit(node.right) bounds(node.location) @@ -844,6 +851,9 @@ def visit_array_pattern_node(node) requireds = visit_all(node.requireds) if node.requireds.any? rest = if (rest_node = node.rest).is_a?(SplatNode) + bounds(rest_node.operator_loc) + on_op("*") + if rest_node.expression.nil? bounds(rest_node.location) on_var_field(nil) @@ -869,6 +879,12 @@ def visit_arguments_node(node) # ^^^^ def visit_assoc_node(node) key = visit(node.key) + + if node.operator_loc + bounds(node.operator_loc) + on_op("=>") + end + value = visit(node.value) bounds(node.location) @@ -881,6 +897,9 @@ def visit_assoc_node(node) # { **foo } # ^^^^^ def visit_assoc_splat_node(node) + bounds(node.operator_loc) + on_op("**") + value = visit(node.value) bounds(node.location) @@ -974,6 +993,8 @@ def visit_begin_node(node) # foo(&bar) # ^^^^ def visit_block_argument_node(node) + bounds(node.operator_loc) + on_op("&") visit(node.expression) end @@ -987,13 +1008,13 @@ def visit_block_local_variable_node(node) # Visit a BlockNode. def visit_block_node(node) braces = node.opening == "{" - parameters = visit(node.parameters) - unless braces bounds(node.opening_loc) on_kw("do") end + parameters = visit(node.parameters) + body = case node.body when nil @@ -1032,6 +1053,9 @@ def visit_block_node(node) # def foo(&bar); end # ^^^^ def visit_block_parameter_node(node) + bounds(node.operator_loc) + on_op("&") + if node.name_loc.nil? bounds(node.location) on_blockarg(nil) @@ -1046,6 +1070,9 @@ def visit_block_parameter_node(node) # A block's parameters. def visit_block_parameters_node(node) + bounds(node.opening_loc) + on_op("|") + parameters = if node.parameters.nil? on_params(nil, nil, nil, nil, nil, nil, nil) @@ -1060,6 +1087,9 @@ def visit_block_parameters_node(node) false end + bounds(node.closing_loc) + on_op("|") + bounds(node.location) on_block_var(parameters, locals) end @@ -1126,6 +1156,9 @@ def visit_call_node(node) end end + bounds(node.equal_loc) + on_op("=") + bounds(node.location) call = on_aref_field(receiver, arguments) value = visit_write_value(last_argument) @@ -1133,13 +1166,15 @@ def visit_call_node(node) bounds(last_argument.location) on_assign(call, value) when :-@, :+@, :~ - receiver = visit(node.receiver) + bounds(node.message_loc) + on_op(node.message) + receiver = visit(node.receiver) bounds(node.location) on_unary(node.name, receiver) when :! + bounds(node.message_loc) if node.message == "not" - bounds(node.message_loc) on_kw("not") receiver = @@ -1150,6 +1185,8 @@ def visit_call_node(node) bounds(node.location) on_unary(:not, receiver) else + on_op("!") + receiver = visit(node.receiver) bounds(node.location) @@ -1157,6 +1194,10 @@ def visit_call_node(node) end when *BINARY_OPERATORS receiver = visit(node.receiver) + + bounds(node.message_loc) + on_op(node.message) + value = visit(node.arguments.arguments.first) bounds(node.location) @@ -1203,6 +1244,11 @@ def visit_call_node(node) visit_token(node.message, false) end + if node.equal_loc + bounds(node.equal_loc) + on_op("=") + end + if node.name.end_with?("=") && !node.message.end_with?("=") && !node.arguments.nil? && node.block.nil? value = visit_write_value(node.arguments.arguments.first) @@ -1354,6 +1400,9 @@ def visit_call_target_node(node) if node.call_operator == "::" receiver = visit(node.receiver) + bounds(node.call_operator_loc) + on_op("::") + bounds(node.message_loc) message = visit_token(node.message) @@ -1377,6 +1426,10 @@ def visit_call_target_node(node) # ^^^^^^^^^^ def visit_capture_pattern_node(node) value = visit(node.value) + + bounds(node.operator_loc) + on_op("=>") + target = visit(node.target) bounds(node.location) @@ -1447,6 +1500,11 @@ def visit_class_node(node) visit(node.constant_path) end + if node.inheritance_operator_loc + bounds(node.inheritance_operator_loc) + on_op("<") + end + superclass = visit(node.superclass) bodystmt = visit_body_node(node.superclass&.location || node.constant_path.location, node.body, node.superclass.nil?) @@ -1472,6 +1530,10 @@ def visit_class_variable_read_node(node) def visit_class_variable_write_node(node) bounds(node.name_loc) target = on_var_field(on_cvar(node.name.to_s)) + + bounds(node.operator_loc) + on_op("=") + value = visit_write_value(node.value) bounds(node.location) @@ -1542,6 +1604,10 @@ def visit_constant_read_node(node) def visit_constant_write_node(node) bounds(node.name_loc) target = on_var_field(on_const(node.name.to_s)) + + bounds(node.operator_loc) + on_op("=") + value = visit_write_value(node.value) bounds(node.location) @@ -1601,6 +1667,11 @@ def visit_constant_target_node(node) # ^^^^^^^^ def visit_constant_path_node(node) if node.parent.nil? + if node.delimiter_loc + bounds(node.delimiter_loc) + on_op("::") + end + bounds(node.name_loc) child = on_const(node.name.to_s) @@ -1609,6 +1680,9 @@ def visit_constant_path_node(node) else parent = visit(node.parent) + bounds(node.delimiter_loc) + on_op("::") + bounds(node.name_loc) child = on_const(node.name.to_s) @@ -1624,6 +1698,10 @@ def visit_constant_path_node(node) # ^^^^^^^^ ^^^^^^^^ def visit_constant_path_write_node(node) target = visit_constant_path_write_node_target(node.target) + + bounds(node.operator_loc) + on_op("=") + value = visit_write_value(node.value) bounds(node.location) @@ -1633,6 +1711,11 @@ def visit_constant_path_write_node(node) # Visit a constant path that is part of a write node. private def visit_constant_path_write_node_target(node) if node.parent.nil? + if node.delimiter_loc + bounds(node.delimiter_loc) + on_op("::") + end + bounds(node.name_loc) child = on_const(node.name.to_s) @@ -1641,6 +1724,9 @@ def visit_constant_path_write_node(node) else parent = visit(node.parent) + bounds(node.delimiter_loc) + on_op("::") + bounds(node.name_loc) child = on_const(node.name.to_s) @@ -1726,6 +1812,11 @@ def visit_def_node(node) parameters = on_paren(parameters) end + if node.equal_loc + bounds(node.equal_loc) + on_op("=") + end + bodystmt = if node.equal_loc.nil? visit_body_node(node.rparen_loc || node.end_keyword_loc, node.body) @@ -1864,6 +1955,10 @@ def visit_false_node(node) # ^^^^^^^^^^^ def visit_find_pattern_node(node) constant = visit(node.constant) + + bounds(node.left.operator_loc) + on_op("*") + left = if node.left.expression.nil? bounds(node.left.location) @@ -1873,6 +1968,10 @@ def visit_find_pattern_node(node) end requireds = visit_all(node.requireds) if node.requireds.any? + + bounds(node.right.operator_loc) + on_op("*") + right = if node.right.expression.nil? bounds(node.right.location) @@ -1889,6 +1988,10 @@ def visit_find_pattern_node(node) # ^^^^^^^^^^ def visit_flip_flop_node(node) left = visit(node.left) + + bounds(node.operator_loc) + on_op(node.operator) + right = visit(node.right) bounds(node.location) @@ -1939,6 +2042,7 @@ def visit_for_node(node) # ^^^ def visit_forwarding_arguments_node(node) bounds(node.location) + on_op("...") on_args_forward end @@ -1946,6 +2050,7 @@ def visit_forwarding_arguments_node(node) # ^^^ def visit_forwarding_parameter_node(node) bounds(node.location) + on_op("...") on_args_forward end @@ -1984,6 +2089,10 @@ def visit_global_variable_read_node(node) def visit_global_variable_write_node(node) bounds(node.name_loc) target = on_var_field(on_gvar(node.name.to_s)) + + bounds(node.operator_loc) + on_op("=") + value = visit_write_value(node.value) bounds(node.location) @@ -2080,6 +2189,8 @@ def visit_hash_pattern_node(node) rest = case node.rest when AssocSplatNode + bounds(node.rest.operator_loc) + on_op("**") visit(node.rest.value) when NoKeywordsParameterNode bounds(node.rest.location) @@ -2101,7 +2212,15 @@ def visit_hash_pattern_node(node) def visit_if_node(node) if node.then_keyword == "?" predicate = visit(node.predicate) + + bounds(node.then_keyword_loc) + on_op("?") + truthy = visit(node.statements.body.first) + + bounds(node.subsequent.else_keyword_loc) + on_op(":") + falsy = visit(node.subsequent.statements.body.first) bounds(node.location) @@ -2261,6 +2380,10 @@ def visit_instance_variable_read_node(node) def visit_instance_variable_write_node(node) bounds(node.name_loc) target = on_var_field(on_ivar(node.name.to_s)) + + bounds(node.operator_loc) + on_op("=") + value = visit_write_value(node.value) bounds(node.location) @@ -2464,6 +2587,9 @@ def visit_keyword_hash_node(node) # def foo(**); end # ^^ def visit_keyword_rest_parameter_node(node) + bounds(node.operator_loc) + on_op("**") + if node.name_loc.nil? bounds(node.location) on_kwrest_param(nil) @@ -2559,6 +2685,10 @@ def visit_local_variable_read_node(node) def visit_local_variable_write_node(node) bounds(node.name_loc) target = on_var_field(on_ident(node.name_loc.slice)) + + bounds(node.operator_loc) + on_op("=") + value = visit_write_value(node.value) bounds(node.location) @@ -2644,6 +2774,10 @@ def visit_match_predicate_node(node) # ^^^^^^^^^^ def visit_match_required_node(node) value = visit(node.value) + + bounds(node.operator_loc) + on_op("=>") + pattern = on_in(visit_pattern_node(node.pattern), nil, nil) on_case(value, pattern) @@ -2744,6 +2878,9 @@ def visit_multi_write_node(node) bounds(node.location) targets = visit_multi_target_node_targets(node.lefts, node.rest, node.rights, true) + bounds(node.operator_loc) + on_op("=") + unless node.lparen_loc.nil? bounds(node.lparen_loc) targets = on_mlhs_paren(targets) @@ -2785,6 +2922,8 @@ def visit_nil_node(node) # def foo(&nil); end # ^^^^ def visit_no_block_parameter_node(node) + bounds(node.operator_loc) + on_op("&") bounds(node.keyword_loc) on_kw("nil") bounds(node.location) @@ -2794,6 +2933,8 @@ def visit_no_block_parameter_node(node) # def foo(**nil); end # ^^^^^ def visit_no_keywords_parameter_node(node) + bounds(node.operator_loc) + on_op("**") bounds(node.keyword_loc) on_kw("nil") bounds(node.location) @@ -2829,6 +2970,10 @@ def visit_optional_keyword_parameter_node(node) def visit_optional_parameter_node(node) bounds(node.name_loc) name = visit_token(node.name.to_s) + + bounds(node.operator_loc) + on_op("=") + value = visit(node.value) [name, value] @@ -2838,10 +2983,14 @@ def visit_optional_parameter_node(node) # ^^^^^^ def visit_or_node(node) left = visit(node.left) + + bounds(node.operator_loc) if node.operator == "or" - bounds(node.operator_loc) on_kw("or") + else + on_op("||") end + right = visit(node.right) bounds(node.location) @@ -2892,6 +3041,9 @@ def visit_parentheses_node(node) # foo => ^(bar) # ^^^^^^ def visit_pinned_expression_node(node) + bounds(node.operator_loc) + on_op("^") + expression = visit(node.expression) bounds(node.location) @@ -2901,6 +3053,9 @@ def visit_pinned_expression_node(node) # foo = 1 and bar => ^foo # ^^^^ def visit_pinned_variable_node(node) + bounds(node.operator_loc) + on_op("^") + visit(node.variable) end @@ -2954,6 +3109,10 @@ def visit_program_node(node) # ^^^^ def visit_range_node(node) left = visit(node.left) + + bounds(node.operator_loc) + on_op(node.operator) + right = visit(node.right) bounds(node.location) @@ -3070,6 +3229,11 @@ def visit_rescue_node(node) end end + if node.operator_loc + bounds(node.operator_loc) + on_op("=>") + end + reference = visit(node.reference) statements = if node.statements.nil? @@ -3091,6 +3255,9 @@ def visit_rescue_node(node) # def foo(*); end # ^ def visit_rest_parameter_node(node) + bounds(node.operator_loc) + on_op("*") + if node.name_loc.nil? bounds(node.location) on_rest_param(nil) @@ -3145,6 +3312,8 @@ def visit_shareable_constant_node(node) def visit_singleton_class_node(node) bounds(node.class_keyword_loc) on_kw("class") + bounds(node.operator_loc) + on_op("<<") expression = visit(node.expression) bodystmt = visit_body_node(node.body&.location || node.end_keyword_loc, node.body) @@ -3186,6 +3355,8 @@ def visit_source_line_node(node) # def foo(*); bar(*); end # ^ def visit_splat_node(node) + bounds(node.operator_loc) + on_op("*") visit(node.expression) end @@ -3676,6 +3847,9 @@ def visit_number_node(node) location = node.location if slice[0] == "-" + bounds(location.copy(length: 1)) + on_op("-") + bounds(location.copy(start_offset: location.start_offset + 1)) value = yield slice[1..-1] diff --git a/test/prism/ruby/ripper_test.rb b/test/prism/ruby/ripper_test.rb index 1d20bceb40..6abe1bb2e5 100644 --- a/test/prism/ruby/ripper_test.rb +++ b/test/prism/ruby/ripper_test.rb @@ -137,20 +137,22 @@ def test_lex_ignored_missing_heredoc_end end # Events that are currently not emitted - UNSUPPORTED_EVENTS = %i[comma ignored_nl label_end lbrace lbracket lparen nl op rbrace rbracket rparen semicolon sp words_sep ignored_sp] + UNSUPPORTED_EVENTS = %i[comma ignored_nl label_end lbrace lbracket lparen nl rbrace rbracket rparen semicolon sp words_sep ignored_sp] SUPPORTED_EVENTS = Translation::Ripper::EVENTS - UNSUPPORTED_EVENTS # Events that assert against their line/column - CHECK_LOCATION_EVENTS = %i[kw] + CHECK_LOCATION_EVENTS = %i[kw op] IGNORE_FOR_SORT_EVENTS = %i[ stmts_new stmts_add bodystmt void_stmt args_new args_add args_add_star args_add_block arg_paren method_add_arg - mlhs_new mlhs_add_star + mlhs_new mlhs_add mlhs_add_star mlhs_add_post + mrhs_new mrhs_add mrhs_add_star mrhs_new_from_args word_new words_new symbols_new qwords_new qsymbols_new xstring_new regexp_new words_add symbols_add qwords_add qsymbols_add regexp_end tstring_end heredoc_end call command fcall vcall field aref_field var_field var_ref block_var ident params string_content heredoc_dedent unary binary dyna_symbol + excessed_comma rest_param comment magic_comment embdoc embdoc_beg embdoc_end arg_ambiguous ] SORT_IGNORE = { @@ -189,7 +191,6 @@ def test_lex_ignored_missing_heredoc_end const_path_ref: ["unparser/corpus/literal/defs.txt"], do_block: ["whitequark/super_block.txt"], embexpr_end: ["seattlerb/str_interp_ternary_or_label.txt"], - rest_param: ["whitequark/send_lambda.txt"], top_const_field: [ "seattlerb/const_3_op_asgn_or.txt", "seattlerb/const_op_asgn_and1.txt", @@ -197,13 +198,9 @@ def test_lex_ignored_missing_heredoc_end "whitequark/const_op_asgn.txt", ], mlhs_paren: ["unparser/corpus/literal/for.txt"], - mlhs_add: [ - "whitequark/for_mlhs.txt", - ], kw: [ "defined.txt", "for.txt", - "seattlerb/block_kw__required.txt", "seattlerb/case_in_42.txt", "seattlerb/case_in_67.txt", "seattlerb/case_in_86_2.txt", @@ -220,6 +217,11 @@ def test_lex_ignored_missing_heredoc_end "whitequark/super_block.txt", "write_command_operator.txt", ], + op: [ + "ranges.txt", + "ternary_operator.txt", + "whitequark/args_args_assocs.txt", + ] } SORT_IGNORE.default = [] SORT_EVENTS = SUPPORTED_EVENTS - IGNORE_FOR_SORT_EVENTS @@ -235,6 +237,7 @@ def initialize(...) def sorted_events @events.select do |e,| next false if e == :kw && @events.any? { |e,| e == :if_mod || e == :while_mod || e == :until_mod || e == :rescue || e == :rescue_mod || e == :while || e == :ensure } + next false if e == :op && @events.any? { |e,| e == :const_path_field || e == :const_ref || e == :top_const_field || e == :top_const_ref } SORT_EVENTS.include?(e) && !SORT_IGNORE[e].include?(filename) end end