summaryrefslogtreecommitdiff
diff options
authorKevin Newton <[email protected]>2024-07-02 12:23:02 -0400
committerKevin Newton <[email protected]>2024-07-11 14:25:54 -0400
commit678dd769e560db9cebff41218e3413e6688aaf12 (patch)
tree3ac667a83526122c72001802a88974aa2c8b8d99
parentc0ad0c3e43816688c72dbb93a7a49e39c87082f5 (diff)
[ruby/prism] Reconfigure error tests
https://github.com/ruby/prism/commit/fb7e1ebb7f
-rw-r--r--lib/prism/parse_result.rb7
-rw-r--r--lib/prism/parse_result/errors.rb60
-rw-r--r--test/prism/errors/1_2_3.txt11
-rw-r--r--test/prism/errors/aliasing_global_variable_with_global_number_variable.txt3
-rw-r--r--test/prism/errors/aliasing_global_variable_with_non_global_variable.txt3
-rw-r--r--test/prism/errors/aliasing_non_global_variable_with_global_variable.txt3
-rw-r--r--test/prism/errors/alnum_delimiters.txt3
-rw-r--r--test/prism/errors/alnum_delimiters_2.txt3
-rw-r--r--test/prism/errors/alnum_delimiters_3.txt3
-rw-r--r--test/prism/errors/alnum_delimiters_4.txt3
-rw-r--r--test/prism/errors/alnum_delimiters_5.txt3
-rw-r--r--test/prism/errors/alnum_delimiters_6.txt3
-rw-r--r--test/prism/errors/alnum_delimiters_7.txt3
-rw-r--r--test/prism/errors/alnum_delimiters_8.txt3
-rw-r--r--test/prism/errors/alnum_delimiters_9.txt3
-rw-r--r--test/prism/errors/argument_after_ellipsis.txt3
-rw-r--r--test/prism/errors/argument_forwarding_only_effects_its_own_internals.txt3
-rw-r--r--test/prism/errors/argument_forwarding_when_parent_is_not_forwarding.txt3
-rw-r--r--test/prism/errors/arguments_after_block.txt3
-rw-r--r--test/prism/errors/arguments_binding_power_for_and.txt5
-rw-r--r--test/prism/errors/assign_to_numbered_parameter.txt11
-rw-r--r--test/prism/errors/bad_arguments.txt6
-rw-r--r--test/prism/errors/begin_at_toplevel.txt3
-rw-r--r--test/prism/errors/binary_range_with_left_unary_range.txt7
-rw-r--r--test/prism/errors/block_arg_and_block.txt3
-rw-r--r--test/prism/errors/block_beginning_with_brace_and_ending_with_end.txt6
-rw-r--r--test/prism/errors/break_1.txt4
-rw-r--r--test/prism/errors/break_1_2_3.txt8
-rw-r--r--test/prism/errors/call_with_block_and_write.txt4
-rw-r--r--test/prism/errors/call_with_block_operator_write.txt4
-rw-r--r--test/prism/errors/call_with_block_or_write.txt4
-rw-r--r--test/prism/errors/cannot_assign_to_a_reserved_numbered_parameter.txt14
-rw-r--r--test/prism/errors/case_without_clauses.txt4
-rw-r--r--test/prism/errors/case_without_when_clauses_errors_on_else_clause.txt5
-rw-r--r--test/prism/errors/check_value_expression.txt20
-rw-r--r--test/prism/errors/class_definition_in_method_body.txt3
-rw-r--r--test/prism/errors/class_definition_in_method_defs.txt7
-rw-r--r--test/prism/errors/class_name.txt3
-rw-r--r--test/prism/errors/command_call_in.txt7
-rw-r--r--test/prism/errors/command_calls.txt3
-rw-r--r--test/prism/errors/command_calls_10.txt3
-rw-r--r--test/prism/errors/command_calls_11.txt3
-rw-r--r--test/prism/errors/command_calls_12.txt3
-rw-r--r--test/prism/errors/command_calls_13.txt3
-rw-r--r--test/prism/errors/command_calls_14.txt3
-rw-r--r--test/prism/errors/command_calls_15.txt3
-rw-r--r--test/prism/errors/command_calls_16.txt3
-rw-r--r--test/prism/errors/command_calls_17.txt5
-rw-r--r--test/prism/errors/command_calls_18.txt3
-rw-r--r--test/prism/errors/command_calls_19.txt3
-rw-r--r--test/prism/errors/command_calls_2.txt6
-rw-r--r--test/prism/errors/command_calls_20.txt3
-rw-r--r--test/prism/errors/command_calls_21.txt5
-rw-r--r--test/prism/errors/command_calls_22.txt3
-rw-r--r--test/prism/errors/command_calls_23.txt3
-rw-r--r--test/prism/errors/command_calls_24.txt5
-rw-r--r--test/prism/errors/command_calls_25.txt8
-rw-r--r--test/prism/errors/command_calls_26.txt3
-rw-r--r--test/prism/errors/command_calls_27.txt3
-rw-r--r--test/prism/errors/command_calls_28.txt3
-rw-r--r--test/prism/errors/command_calls_29.txt3
-rw-r--r--test/prism/errors/command_calls_3.txt3
-rw-r--r--test/prism/errors/command_calls_30.txt3
-rw-r--r--test/prism/errors/command_calls_4.txt3
-rw-r--r--test/prism/errors/command_calls_5.txt3
-rw-r--r--test/prism/errors/command_calls_6.txt6
-rw-r--r--test/prism/errors/command_calls_7.txt6
-rw-r--r--test/prism/errors/command_calls_8.txt6
-rw-r--r--test/prism/errors/command_calls_9.txt6
-rw-r--r--test/prism/errors/conditional_predicate_closed.txt6
-rw-r--r--test/prism/errors/constant_assignment_in_method.txt3
-rw-r--r--test/prism/errors/constant_path_with_invalid_token_after.txt4
-rw-r--r--test/prism/errors/content_after_unterminated_heredoc.txt2
-rw-r--r--test/prism/errors/cr_without_lf_in_percent_expression.txt3
-rw-r--r--test/prism/errors/def_with_empty_expression_receiver.txt3
-rw-r--r--test/prism/errors/def_with_expression_receiver_and_no_identifier.txt4
-rw-r--r--test/prism/errors/def_with_multiple_statements_receiver.txt10
-rw-r--r--test/prism/errors/defining_numbered_parameter.txt3
-rw-r--r--test/prism/errors/defining_numbered_parameter_2.txt3
-rw-r--r--test/prism/errors/do_not_allow_characters_other_than_0_9_a_f_and_A_F_in_u_Unicode_character_notation.txt4
-rw-r--r--test/prism/errors/do_not_allow_forward_arguments_in_blocks.txt3
-rw-r--r--test/prism/errors/do_not_allow_forward_arguments_in_lambda_literals.txt3
-rw-r--r--test/prism/errors/do_not_allow_more_than_6_hexadecimal_digits_in_u_Unicode_character_notation.txt3
-rw-r--r--test/prism/errors/do_not_allow_multiple_codepoints_in_a_single_character_literal.txt3
-rw-r--r--test/prism/errors/do_not_allow_trailing_commas_in_lambda_parameters.txt3
-rw-r--r--test/prism/errors/do_not_allow_trailing_commas_in_method_parameters.txt3
-rw-r--r--test/prism/errors/dont_allow_return_inside_class_body.txt3
-rw-r--r--test/prism/errors/dont_allow_return_inside_module_body.txt3
-rw-r--r--test/prism/errors/dont_allow_setting_to_back_and_nth_reference.txt7
-rw-r--r--test/prism/errors/double_arguments_forwarding.txt4
-rw-r--r--test/prism/errors/double_scope_numbered_parameters.txt3
-rw-r--r--test/prism/errors/double_splat_followed_by_splat_argument.txt3
-rw-r--r--test/prism/errors/duplicate_pattern_capture.txt17
-rw-r--r--test/prism/errors/duplicate_pattern_hash_key.txt4
-rw-r--r--test/prism/errors/duplicate_pattern_hash_key_2.txt3
-rw-r--r--test/prism/errors/duplicated_parameter_names.txt3
-rw-r--r--test/prism/errors/duplicated_parameter_names_2.txt3
-rw-r--r--test/prism/errors/duplicated_parameter_names_3.txt3
-rw-r--r--test/prism/errors/duplicated_parameter_names_4.txt3
-rw-r--r--test/prism/errors/duplicated_parameter_names_5.txt3
-rw-r--r--test/prism/errors/ellipsis_in_no_paren_call.txt3
-rw-r--r--test/prism/errors/for_loops_index_missing.txt5
-rw-r--r--test/prism/errors/for_loops_only_end.txt5
-rw-r--r--test/prism/errors/forwarding_arg_after_keyword_rest.txt3
-rw-r--r--test/prism/errors/forwarding_arg_and_block.txt3
-rw-r--r--test/prism/errors/incomplete_instance_var_string.txt4
-rw-r--r--test/prism/errors/index_call_with_block_and_write.txt5
-rw-r--r--test/prism/errors/index_call_with_block_operator_write.txt5
-rw-r--r--test/prism/errors/index_call_with_block_or_write.txt5
-rw-r--r--test/prism/errors/interpolated_regular_expression_with_unknown_regexp_options.txt3
-rw-r--r--test/prism/errors/invalid_global_variable_write.txt4
-rw-r--r--test/prism/errors/invalid_hex_escape.txt3
-rw-r--r--test/prism/errors/invalid_multi_target.txt3
-rw-r--r--test/prism/errors/invalid_multi_target_10.txt3
-rw-r--r--test/prism/errors/invalid_multi_target_11.txt3
-rw-r--r--test/prism/errors/invalid_multi_target_12.txt3
-rw-r--r--test/prism/errors/invalid_multi_target_13.txt3
-rw-r--r--test/prism/errors/invalid_multi_target_14.txt3
-rw-r--r--test/prism/errors/invalid_multi_target_15.txt3
-rw-r--r--test/prism/errors/invalid_multi_target_16.txt3
-rw-r--r--test/prism/errors/invalid_multi_target_17.txt3
-rw-r--r--test/prism/errors/invalid_multi_target_18.txt3
-rw-r--r--test/prism/errors/invalid_multi_target_19.txt3
-rw-r--r--test/prism/errors/invalid_multi_target_2.txt3
-rw-r--r--test/prism/errors/invalid_multi_target_20.txt3
-rw-r--r--test/prism/errors/invalid_multi_target_3.txt3
-rw-r--r--test/prism/errors/invalid_multi_target_4.txt3
-rw-r--r--test/prism/errors/invalid_multi_target_5.txt3
-rw-r--r--test/prism/errors/invalid_multi_target_6.txt3
-rw-r--r--test/prism/errors/invalid_multi_target_7.txt3
-rw-r--r--test/prism/errors/invalid_multi_target_8.txt4
-rw-r--r--test/prism/errors/invalid_multi_target_9.txt4
-rw-r--r--test/prism/errors/invalid_number_underscores.txt3
-rw-r--r--test/prism/errors/invalid_number_underscores_10.txt3
-rw-r--r--test/prism/errors/invalid_number_underscores_11.txt3
-rw-r--r--test/prism/errors/invalid_number_underscores_12.txt3
-rw-r--r--test/prism/errors/invalid_number_underscores_2.txt3
-rw-r--r--test/prism/errors/invalid_number_underscores_3.txt3
-rw-r--r--test/prism/errors/invalid_number_underscores_4.txt3
-rw-r--r--test/prism/errors/invalid_number_underscores_5.txt3
-rw-r--r--test/prism/errors/invalid_number_underscores_6.txt3
-rw-r--r--test/prism/errors/invalid_number_underscores_7.txt3
-rw-r--r--test/prism/errors/invalid_number_underscores_8.txt3
-rw-r--r--test/prism/errors/invalid_number_underscores_9.txt3
-rw-r--r--test/prism/errors/invalid_operator_write_dot.txt3
-rw-r--r--test/prism/errors/invalid_operator_write_fcall.txt3
-rw-r--r--test/prism/errors/it_with_ordinary_parameter.txt3
-rw-r--r--test/prism/errors/keywords_parameters_before_required_parameters.txt4
-rw-r--r--test/prism/errors/loop_conditional_is_closed.txt4
-rw-r--r--test/prism/errors/match_plus.txt7
-rw-r--r--test/prism/errors/method_parameters_after_arguments_forwarding.txt4
-rw-r--r--test/prism/errors/method_parameters_after_block.txt4
-rw-r--r--test/prism/errors/method_with_arguments_after_anonymous_block.txt4
-rw-r--r--test/prism/errors/missing_terminator_in_parentheses.txt3
-rw-r--r--test/prism/errors/module_definition_in_method_body.txt3
-rw-r--r--test/prism/errors/module_definition_in_method_body_within_block.txt7
-rw-r--r--test/prism/errors/module_definition_in_method_defs.txt7
-rw-r--r--test/prism/errors/module_name_recoverable.txt4
-rw-r--r--test/prism/errors/multiple_error_in_parameters_order.txt5
-rw-r--r--test/prism/errors/next_1.txt4
-rw-r--r--test/prism/errors/next_1_2_3.txt8
-rw-r--r--test/prism/errors/non_assoc_equality.txt19
-rw-r--r--test/prism/errors/non_assoc_range.txt4
-rw-r--r--test/prism/errors/numbered_parameters_in_block_arguments.txt3
-rw-r--r--test/prism/errors/optional_block_parameters_with_unary_operator.txt3
-rw-r--r--test/prism/errors/optional_block_parameters_with_unary_operator_2.txt3
-rw-r--r--test/prism/errors/optional_block_parameters_with_unary_operator_3.txt3
-rw-r--r--test/prism/errors/optional_block_parameters_with_unary_operator_4.txt3
-rw-r--r--test/prism/errors/parameter_name_ending_with_bang_or_question_mark.txt4
-rw-r--r--test/prism/errors/pre_execution_context.txt4
-rw-r--r--test/prism/errors/pre_execution_missing_brace.txt3
-rw-r--r--test/prism/errors/range_and_bin_op.txt4
-rw-r--r--test/prism/errors/range_and_bin_op_2.txt4
-rw-r--r--test/prism/errors/range_and_bin_op_3.txt3
-rw-r--r--test/prism/errors/range_and_bin_op_4.txt4
-rw-r--r--test/prism/errors/range_and_bin_op_5.txt5
-rw-r--r--test/prism/errors/range_and_bin_op_6.txt3
-rw-r--r--test/prism/errors/range_and_bin_op_7.txt3
-rw-r--r--test/prism/errors/range_and_bin_op_8.txt4
-rw-r--r--test/prism/errors/rational_number_with_exponential_portion.txt4
-rw-r--r--test/prism/errors/regular_expression_with_unknown_regexp_options.txt3
-rw-r--r--test/prism/errors/repeated_parameter_name_in_destructured_params.txt3
-rw-r--r--test/prism/errors/rest_keywords_parameters_before_required_parameters.txt4
-rw-r--r--test/prism/errors/return_1.txt3
-rw-r--r--test/prism/errors/return_1_2_3.txt7
-rw-r--r--test/prism/errors/returning_to_optional_parameters_multiple_times.txt4
-rw-r--r--test/prism/errors/semicolon_after_inheritance_operator.txt3
-rw-r--r--test/prism/errors/setter_method_cannot_be_defined_in_an_endless_method_definition.txt3
-rw-r--r--test/prism/errors/shadow_args_in_block.txt3
-rw-r--r--test/prism/errors/shadow_args_in_lambda.txt5
-rw-r--r--test/prism/errors/singleton_method_for_literals.txt39
-rw-r--r--test/prism/errors/splat_argument_after_keyword_argument.txt3
-rw-r--r--test/prism/errors/statement_at_non_statement.txt9
-rw-r--r--test/prism/errors/statement_operators.txt25
-rw-r--r--test/prism/errors/switching_to_named_arguments_twice.txt5
-rw-r--r--test/prism/errors/switching_to_optional_arguments_twice.txt5
-rw-r--r--test/prism/errors/symbol_in_hash.txt3
-rw-r--r--test/prism/errors/symbol_in_keyword_parameter.txt3
-rw-r--r--test/prism/errors/targeting_numbered_parameter.txt3
-rw-r--r--test/prism/errors/top_level_constant_starting_with_downcased_identifier.txt4
-rw-r--r--test/prism/errors/top_level_constant_with_downcased_identifier.txt4
-rw-r--r--test/prism/errors/trailing_comma_in_calls.txt3
-rw-r--r--test/prism/errors/unexpected_block.txt3
-rw-r--r--test/prism/errors/unterminated_W_list.txt3
-rw-r--r--test/prism/errors/unterminated_argument_expression.txt5
-rw-r--r--test/prism/errors/unterminated_embdoc.txt3
-rw-r--r--test/prism/errors/unterminated_embdoc_2.txt3
-rw-r--r--test/prism/errors/unterminated_empty_string.txt3
-rw-r--r--test/prism/errors/unterminated_global_variable.txt3
-rw-r--r--test/prism/errors/unterminated_global_variable_2.txt3
-rw-r--r--test/prism/errors/unterminated_i_list.txt3
-rw-r--r--test/prism/errors/unterminated_interpolated_string.txt3
-rw-r--r--test/prism/errors/unterminated_interpolated_symbol.txt3
-rw-r--r--test/prism/errors/unterminated_parenthesized_expression.txt4
-rw-r--r--test/prism/errors/unterminated_regular_expression.txt3
-rw-r--r--test/prism/errors/unterminated_regular_expression_with_heredoc.txt4
-rw-r--r--test/prism/errors/unterminated_s_symbol.txt3
-rw-r--r--test/prism/errors/unterminated_string.txt3
-rw-r--r--test/prism/errors/unterminated_unicode_brackets_should_be_a_syntax_error.txt3
-rw-r--r--test/prism/errors/unterminated_xstring.txt3
-rw-r--r--test/prism/errors/void_value_expression_in_arguments.txt17
-rw-r--r--test/prism/errors/void_value_expression_in_array.txt15
-rw-r--r--test/prism/errors/void_value_expression_in_assignment.txt9
-rw-r--r--test/prism/errors/void_value_expression_in_begin_statement.txt21
-rw-r--r--test/prism/errors/void_value_expression_in_binary_call.txt11
-rw-r--r--test/prism/errors/void_value_expression_in_call.txt11
-rw-r--r--test/prism/errors/void_value_expression_in_constant_path.txt5
-rw-r--r--test/prism/errors/void_value_expression_in_def.txt10
-rw-r--r--test/prism/errors/void_value_expression_in_expression.txt19
-rw-r--r--test/prism/errors/void_value_expression_in_hash.txt9
-rw-r--r--test/prism/errors/void_value_expression_in_modifier.txt13
-rw-r--r--test/prism/errors/void_value_expression_in_statement.txt26
-rw-r--r--test/prism/errors/void_value_expression_in_unary_call.txt5
-rw-r--r--test/prism/errors/while_endless_method.txt5
-rw-r--r--test/prism/errors/writing_numbered_parameter.txt3
-rw-r--r--test/prism/errors_test.rb2280
-rw-r--r--test/prism/fixtures/patterns.txt3
-rw-r--r--test/prism/result/warnings_test.rb43
238 files changed, 1272 insertions, 2236 deletions
diff --git a/lib/prism/parse_result.rb b/lib/prism/parse_result.rb
index 798fde09e5..cc4f942283 100644
--- a/lib/prism/parse_result.rb
+++ b/lib/prism/parse_result.rb
@@ -575,6 +575,7 @@ module Prism
# This is a result specific to the `parse` and `parse_file` methods.
class ParseResult < Result
autoload :Comments, "prism/parse_result/comments"
+ autoload :Errors, "prism/parse_result/errors"
autoload :Newlines, "prism/parse_result/newlines"
private_constant :Comments
@@ -604,6 +605,12 @@ module Prism
def mark_newlines!
value.accept(Newlines.new(source.offsets.size)) # steep:ignore
end
+
+ # Returns a string representation of the syntax tree with the errors
+ # displayed inline.
+ def errors_format
+ Errors.new(self).format
+ end
end
# This is a result specific to the `lex` and `lex_file` methods.
diff --git a/lib/prism/parse_result/errors.rb b/lib/prism/parse_result/errors.rb
new file mode 100644
index 0000000000..40dda3c264
--- /dev/null
+++ b/lib/prism/parse_result/errors.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+require "stringio"
+
+module Prism
+ class ParseResult < Result
+ class Errors
+ attr_reader :parse_result
+
+ def initialize(parse_result)
+ @parse_result = parse_result
+ end
+
+ def format
+ error_lines = {}
+ parse_result.errors.each do |error|
+ location = error.location
+ (location.start_line..location.end_line).each do |line|
+ error_lines[line] ||= []
+ error_lines[line] << error
+ end
+ end
+
+ source_lines = parse_result.source.source.lines
+ source_lines << "" if error_lines.key?(source_lines.size + 1)
+
+ io = StringIO.new
+ source_lines.each.with_index(1) do |line, line_number|
+ io.puts(line)
+
+ (error_lines.delete(line_number) || []).each do |error|
+ location = error.location
+
+ case line_number
+ when location.start_line
+ io.print(" " * location.start_column + "^")
+
+ if location.start_line == location.end_line
+ if location.start_column != location.end_column
+ io.print("~" * (location.end_column - location.start_column - 1))
+ end
+
+ io.puts(" " + error.message)
+ else
+ io.puts("~" * (line.bytesize - location.start_column))
+ end
+ when location.end_line
+ io.puts("~" * location.end_column + " " + error.message)
+ else
+ io.puts("~" * line.bytesize)
+ end
+ end
+ end
+
+ io.puts
+ io.string
+ end
+ end
+ end
+end
diff --git a/test/prism/errors/1_2_3.txt b/test/prism/errors/1_2_3.txt
new file mode 100644
index 0000000000..345452911f
--- /dev/null
+++ b/test/prism/errors/1_2_3.txt
@@ -0,0 +1,11 @@
+(1, 2, 3)
+ ^ unexpected ',', expecting end-of-input
+ ^ unexpected ',', ignoring it
+ ^ expected a matching `)`
+ ^ unexpected ',', expecting end-of-input
+ ^ unexpected ',', ignoring it
+ ^ unexpected ',', expecting end-of-input
+ ^ unexpected ',', ignoring it
+ ^ unexpected ')', expecting end-of-input
+ ^ unexpected ')', ignoring it
+
diff --git a/test/prism/errors/aliasing_global_variable_with_global_number_variable.txt b/test/prism/errors/aliasing_global_variable_with_global_number_variable.txt
new file mode 100644
index 0000000000..2f40a6a328
--- /dev/null
+++ b/test/prism/errors/aliasing_global_variable_with_global_number_variable.txt
@@ -0,0 +1,3 @@
+alias $a $1
+ ^~ invalid argument being passed to `alias`; can't make alias for the number variables
+
diff --git a/test/prism/errors/aliasing_global_variable_with_non_global_variable.txt b/test/prism/errors/aliasing_global_variable_with_non_global_variable.txt
new file mode 100644
index 0000000000..b6f013bab5
--- /dev/null
+++ b/test/prism/errors/aliasing_global_variable_with_non_global_variable.txt
@@ -0,0 +1,3 @@
+alias $a b
+ ^ invalid argument being passed to `alias`; expected a bare word, symbol, constant, or global variable
+
diff --git a/test/prism/errors/aliasing_non_global_variable_with_global_variable.txt b/test/prism/errors/aliasing_non_global_variable_with_global_variable.txt
new file mode 100644
index 0000000000..8863f342f0
--- /dev/null
+++ b/test/prism/errors/aliasing_non_global_variable_with_global_variable.txt
@@ -0,0 +1,3 @@
+alias a $b
+ ^~ invalid argument being passed to `alias`; expected a bare word, symbol, constant, or global variable
+
diff --git a/test/prism/errors/alnum_delimiters.txt b/test/prism/errors/alnum_delimiters.txt
new file mode 100644
index 0000000000..c9ed06ae51
--- /dev/null
+++ b/test/prism/errors/alnum_delimiters.txt
@@ -0,0 +1,3 @@
+%qXfooX
+^ unknown type of %string
+
diff --git a/test/prism/errors/alnum_delimiters_2.txt b/test/prism/errors/alnum_delimiters_2.txt
new file mode 100644
index 0000000000..3f78b434d6
--- /dev/null
+++ b/test/prism/errors/alnum_delimiters_2.txt
@@ -0,0 +1,3 @@
+%QXfooX
+^ unknown type of %string
+
diff --git a/test/prism/errors/alnum_delimiters_3.txt b/test/prism/errors/alnum_delimiters_3.txt
new file mode 100644
index 0000000000..55ef8d29a5
--- /dev/null
+++ b/test/prism/errors/alnum_delimiters_3.txt
@@ -0,0 +1,3 @@
+%wXfooX
+^ unknown type of %string
+
diff --git a/test/prism/errors/alnum_delimiters_4.txt b/test/prism/errors/alnum_delimiters_4.txt
new file mode 100644
index 0000000000..603b54debd
--- /dev/null
+++ b/test/prism/errors/alnum_delimiters_4.txt
@@ -0,0 +1,3 @@
+%WxfooX
+^ unknown type of %string
+
diff --git a/test/prism/errors/alnum_delimiters_5.txt b/test/prism/errors/alnum_delimiters_5.txt
new file mode 100644
index 0000000000..31c344ea90
--- /dev/null
+++ b/test/prism/errors/alnum_delimiters_5.txt
@@ -0,0 +1,3 @@
+%iXfooX
+^ unknown type of %string
+
diff --git a/test/prism/errors/alnum_delimiters_6.txt b/test/prism/errors/alnum_delimiters_6.txt
new file mode 100644
index 0000000000..79ffbbb1b8
--- /dev/null
+++ b/test/prism/errors/alnum_delimiters_6.txt
@@ -0,0 +1,3 @@
+%IXfooX
+^ unknown type of %string
+
diff --git a/test/prism/errors/alnum_delimiters_7.txt b/test/prism/errors/alnum_delimiters_7.txt
new file mode 100644
index 0000000000..809192e031
--- /dev/null
+++ b/test/prism/errors/alnum_delimiters_7.txt
@@ -0,0 +1,3 @@
+%xXfooX
+^ unknown type of %string
+
diff --git a/test/prism/errors/alnum_delimiters_8.txt b/test/prism/errors/alnum_delimiters_8.txt
new file mode 100644
index 0000000000..abfcf119c0
--- /dev/null
+++ b/test/prism/errors/alnum_delimiters_8.txt
@@ -0,0 +1,3 @@
+%rXfooX
+^ unknown type of %string
+
diff --git a/test/prism/errors/alnum_delimiters_9.txt b/test/prism/errors/alnum_delimiters_9.txt
new file mode 100644
index 0000000000..ae56d7be4f
--- /dev/null
+++ b/test/prism/errors/alnum_delimiters_9.txt
@@ -0,0 +1,3 @@
+%sXfooX
+^ unknown type of %string
+
diff --git a/test/prism/errors/argument_after_ellipsis.txt b/test/prism/errors/argument_after_ellipsis.txt
new file mode 100644
index 0000000000..3d708648a4
--- /dev/null
+++ b/test/prism/errors/argument_after_ellipsis.txt
@@ -0,0 +1,3 @@
+def foo(...); foo(..., 1); end
+ ^ unexpected argument after `...`
+
diff --git a/test/prism/errors/argument_forwarding_only_effects_its_own_internals.txt b/test/prism/errors/argument_forwarding_only_effects_its_own_internals.txt
new file mode 100644
index 0000000000..9c3f0ae3f7
--- /dev/null
+++ b/test/prism/errors/argument_forwarding_only_effects_its_own_internals.txt
@@ -0,0 +1,3 @@
+def a(...); b(...); end; def c(x, y, z); b(...); end
+ ^~~ unexpected ... when the parent method is not forwarding
+
diff --git a/test/prism/errors/argument_forwarding_when_parent_is_not_forwarding.txt b/test/prism/errors/argument_forwarding_when_parent_is_not_forwarding.txt
new file mode 100644
index 0000000000..017ba7eec9
--- /dev/null
+++ b/test/prism/errors/argument_forwarding_when_parent_is_not_forwarding.txt
@@ -0,0 +1,3 @@
+def a(x, y, z); b(...); end
+ ^~~ unexpected ... when the parent method is not forwarding
+
diff --git a/test/prism/errors/arguments_after_block.txt b/test/prism/errors/arguments_after_block.txt
new file mode 100644
index 0000000000..2d5e06ff77
--- /dev/null
+++ b/test/prism/errors/arguments_after_block.txt
@@ -0,0 +1,3 @@
+a(&block, foo)
+ ^~~ unexpected argument after a block argument
+
diff --git a/test/prism/errors/arguments_binding_power_for_and.txt b/test/prism/errors/arguments_binding_power_for_and.txt
new file mode 100644
index 0000000000..0585a091f4
--- /dev/null
+++ b/test/prism/errors/arguments_binding_power_for_and.txt
@@ -0,0 +1,5 @@
+foo(*bar and baz)
+ ^~~ unexpected 'and'; expected a `)` to close the arguments
+ ^ unexpected ')', expecting end-of-input
+ ^ unexpected ')', ignoring it
+
diff --git a/test/prism/errors/assign_to_numbered_parameter.txt b/test/prism/errors/assign_to_numbered_parameter.txt
new file mode 100644
index 0000000000..74cc0c4032
--- /dev/null
+++ b/test/prism/errors/assign_to_numbered_parameter.txt
@@ -0,0 +1,11 @@
+a in _1
+ ^~ _1 is reserved for numbered parameters
+a => _1
+ ^~ _1 is reserved for numbered parameters
+1 => a, _1
+ ^~ _1 is reserved for numbered parameters
+1 in a, _1
+ ^~ _1 is reserved for numbered parameters
+/(?<_1>)/ =~ a
+ ^~ _1 is reserved for numbered parameters
+
diff --git a/test/prism/errors/bad_arguments.txt b/test/prism/errors/bad_arguments.txt
new file mode 100644
index 0000000000..ea19efd3c8
--- /dev/null
+++ b/test/prism/errors/bad_arguments.txt
@@ -0,0 +1,6 @@
+def foo(A, @a, $A, @@a);end
+ ^ invalid formal argument; formal argument cannot be a constant
+ ^~ invalid formal argument; formal argument cannot be an instance variable
+ ^~ invalid formal argument; formal argument cannot be a global variable
+ ^~~ invalid formal argument; formal argument cannot be a class variable
+
diff --git a/test/prism/errors/begin_at_toplevel.txt b/test/prism/errors/begin_at_toplevel.txt
new file mode 100644
index 0000000000..ce3d3b8d00
--- /dev/null
+++ b/test/prism/errors/begin_at_toplevel.txt
@@ -0,0 +1,3 @@
+def foo; BEGIN {}; end
+ ^~~~~ BEGIN is permitted only at toplevel
+
diff --git a/test/prism/errors/binary_range_with_left_unary_range.txt b/test/prism/errors/binary_range_with_left_unary_range.txt
new file mode 100644
index 0000000000..37e41f3971
--- /dev/null
+++ b/test/prism/errors/binary_range_with_left_unary_range.txt
@@ -0,0 +1,7 @@
+..1..
+ ^~ unexpected range operator; .. and ... are non-associative and cannot be chained
+...1..
+ ^~ unexpected range operator; .. and ... are non-associative and cannot be chained
+ ^~ unexpected .., expecting end-of-input
+ ^~ unexpected .., ignoring it
+
diff --git a/test/prism/errors/block_arg_and_block.txt b/test/prism/errors/block_arg_and_block.txt
new file mode 100644
index 0000000000..c355c40475
--- /dev/null
+++ b/test/prism/errors/block_arg_and_block.txt
@@ -0,0 +1,3 @@
+foo(&1) { }
+ ^~~ both block arg and actual block given; only one block is allowed
+
diff --git a/test/prism/errors/block_beginning_with_brace_and_ending_with_end.txt b/test/prism/errors/block_beginning_with_brace_and_ending_with_end.txt
new file mode 100644
index 0000000000..f0fa964c8a
--- /dev/null
+++ b/test/prism/errors/block_beginning_with_brace_and_ending_with_end.txt
@@ -0,0 +1,6 @@
+x.each { x end
+ ^~~ unexpected 'end', expecting end-of-input
+ ^~~ unexpected 'end', ignoring it
+ ^ unexpected end-of-input, assuming it is closing the parent top level context
+ ^ expected a block beginning with `{` to end with `}`
+
diff --git a/test/prism/errors/break_1.txt b/test/prism/errors/break_1.txt
new file mode 100644
index 0000000000..e7b26ad3a0
--- /dev/null
+++ b/test/prism/errors/break_1.txt
@@ -0,0 +1,4 @@
+break 1,;
+ ^ expected an argument
+^~~~~~~~ Invalid break
+
diff --git a/test/prism/errors/break_1_2_3.txt b/test/prism/errors/break_1_2_3.txt
new file mode 100644
index 0000000000..817207cbfe
--- /dev/null
+++ b/test/prism/errors/break_1_2_3.txt
@@ -0,0 +1,8 @@
+break(1, 2, 3)
+ ^ unexpected ',', expecting end-of-input
+ ^ unexpected ',', ignoring it
+ ^ expected a matching `)`
+ ^ unexpected ')', expecting end-of-input
+ ^ unexpected ')', ignoring it
+^~~~~~~~~~~~~ Invalid break
+
diff --git a/test/prism/errors/call_with_block_and_write.txt b/test/prism/errors/call_with_block_and_write.txt
new file mode 100644
index 0000000000..f63d94770e
--- /dev/null
+++ b/test/prism/errors/call_with_block_and_write.txt
@@ -0,0 +1,4 @@
+foo {} &&= 1
+^~~~~~ unexpected write target
+ ^~~ unexpected operator after a call with a block
+
diff --git a/test/prism/errors/call_with_block_operator_write.txt b/test/prism/errors/call_with_block_operator_write.txt
new file mode 100644
index 0000000000..3c36050b34
--- /dev/null
+++ b/test/prism/errors/call_with_block_operator_write.txt
@@ -0,0 +1,4 @@
+foo {} += 1
+^~~~~~ unexpected write target
+ ^~ unexpected operator after a call with a block
+
diff --git a/test/prism/errors/call_with_block_or_write.txt b/test/prism/errors/call_with_block_or_write.txt
new file mode 100644
index 0000000000..2cced0db75
--- /dev/null
+++ b/test/prism/errors/call_with_block_or_write.txt
@@ -0,0 +1,4 @@
+foo {} ||= 1
+^~~~~~ unexpected write target
+ ^~~ unexpected operator after a call with a block
+
diff --git a/test/prism/errors/cannot_assign_to_a_reserved_numbered_parameter.txt b/test/prism/errors/cannot_assign_to_a_reserved_numbered_parameter.txt
new file mode 100644
index 0000000000..750915fb1f
--- /dev/null
+++ b/test/prism/errors/cannot_assign_to_a_reserved_numbered_parameter.txt
@@ -0,0 +1,14 @@
+begin
+ _1=:a;_2=:a;_3=:a;_4=:a;_5=:a
+ ^~ _1 is reserved for numbered parameters
+ ^~ _2 is reserved for numbered parameters
+ ^~ _3 is reserved for numbered parameters
+ ^~ _4 is reserved for numbered parameters
+ ^~ _5 is reserved for numbered parameters
+ _6=:a;_7=:a;_8=:a;_9=:a;_10=:a
+ ^~ _6 is reserved for numbered parameters
+ ^~ _7 is reserved for numbered parameters
+ ^~ _8 is reserved for numbered parameters
+ ^~ _9 is reserved for numbered parameters
+end
+
diff --git a/test/prism/errors/case_without_clauses.txt b/test/prism/errors/case_without_clauses.txt
new file mode 100644
index 0000000000..3bbbfdd97f
--- /dev/null
+++ b/test/prism/errors/case_without_clauses.txt
@@ -0,0 +1,4 @@
+case :a
+^~~~ expected a `when` or `in` clause after `case`
+end
+
diff --git a/test/prism/errors/case_without_when_clauses_errors_on_else_clause.txt b/test/prism/errors/case_without_when_clauses_errors_on_else_clause.txt
new file mode 100644
index 0000000000..c5a1179fb9
--- /dev/null
+++ b/test/prism/errors/case_without_when_clauses_errors_on_else_clause.txt
@@ -0,0 +1,5 @@
+case :a
+^~~~ expected a `when` or `in` clause after `case`
+else
+end
+
diff --git a/test/prism/errors/check_value_expression.txt b/test/prism/errors/check_value_expression.txt
new file mode 100644
index 0000000000..33a472d94c
--- /dev/null
+++ b/test/prism/errors/check_value_expression.txt
@@ -0,0 +1,20 @@
+1 => ^(return)
+ ^~~~~~ unexpected void value expression
+while true
+ 1 => ^(break)
+ ^~~~~ unexpected void value expression
+ 1 => ^(next)
+ ^~~~ unexpected void value expression
+ 1 => ^(redo)
+ ^~~~ unexpected void value expression
+ 1 => ^(retry)
+ ^~~~~ Invalid retry without rescue
+ ^~~~~ unexpected void value expression
+ 1 => ^(2 => a)
+ ^~~~~~ unexpected void value expression
+end
+1 => ^(if 1; (return) else (return) end)
+ ^~~~~~ unexpected void value expression
+1 => ^(unless 1; (return) else (return) end)
+ ^~~~~~ unexpected void value expression
+
diff --git a/test/prism/errors/class_definition_in_method_body.txt b/test/prism/errors/class_definition_in_method_body.txt
new file mode 100644
index 0000000000..fcdc5746ee
--- /dev/null
+++ b/test/prism/errors/class_definition_in_method_body.txt
@@ -0,0 +1,3 @@
+def foo;class A;end;end
+ ^~~~~ unexpected class definition in method body
+
diff --git a/test/prism/errors/class_definition_in_method_defs.txt b/test/prism/errors/class_definition_in_method_defs.txt
new file mode 100644
index 0000000000..23bee0b6fb
--- /dev/null
+++ b/test/prism/errors/class_definition_in_method_defs.txt
@@ -0,0 +1,7 @@
+def foo(bar = class A;end);end
+ ^~~~~ unexpected class definition in method body
+def foo;rescue;class A;end;end
+ ^~~~~ unexpected class definition in method body
+def foo;ensure;class A;end;end
+ ^~~~~ unexpected class definition in method body
+
diff --git a/test/prism/errors/class_name.txt b/test/prism/errors/class_name.txt
new file mode 100644
index 0000000000..8b75896ddb
--- /dev/null
+++ b/test/prism/errors/class_name.txt
@@ -0,0 +1,3 @@
+class 0.X end
+ ^~~ unexpected constant path after `class`; class/module name must be CONSTANT
+
diff --git a/test/prism/errors/command_call_in.txt b/test/prism/errors/command_call_in.txt
new file mode 100644
index 0000000000..a4357028c6
--- /dev/null
+++ b/test/prism/errors/command_call_in.txt
@@ -0,0 +1,7 @@
+foo 1 in a
+ ^ unexpected `in` keyword in arguments
+ ^ unexpected local variable or method, expecting end-of-input
+a = foo 2 in b
+ ^ unexpected `in` keyword in arguments
+ ^ unexpected local variable or method, expecting end-of-input
+
diff --git a/test/prism/errors/command_calls.txt b/test/prism/errors/command_calls.txt
new file mode 100644
index 0000000000..19812a1d0a
--- /dev/null
+++ b/test/prism/errors/command_calls.txt
@@ -0,0 +1,3 @@
+[a b]
+ ^ unexpected local variable or method; expected a `,` separator for the array elements
+
diff --git a/test/prism/errors/command_calls_10.txt b/test/prism/errors/command_calls_10.txt
new file mode 100644
index 0000000000..f4d9f0fabc
--- /dev/null
+++ b/test/prism/errors/command_calls_10.txt
@@ -0,0 +1,3 @@
++a b
+ ^ unexpected local variable or method, expecting end-of-input
+
diff --git a/test/prism/errors/command_calls_11.txt b/test/prism/errors/command_calls_11.txt
new file mode 100644
index 0000000000..868476c0c3
--- /dev/null
+++ b/test/prism/errors/command_calls_11.txt
@@ -0,0 +1,3 @@
+a + b c
+ ^ unexpected local variable or method, expecting end-of-input
+
diff --git a/test/prism/errors/command_calls_12.txt b/test/prism/errors/command_calls_12.txt
new file mode 100644
index 0000000000..50c9ae88e3
--- /dev/null
+++ b/test/prism/errors/command_calls_12.txt
@@ -0,0 +1,3 @@
+a && b c
+ ^ unexpected local variable or method, expecting end-of-input
+
diff --git a/test/prism/errors/command_calls_13.txt b/test/prism/errors/command_calls_13.txt
new file mode 100644
index 0000000000..50dc4a84a0
--- /dev/null
+++ b/test/prism/errors/command_calls_13.txt
@@ -0,0 +1,3 @@
+a =~ b c
+ ^ unexpected local variable or method, expecting end-of-input
+
diff --git a/test/prism/errors/command_calls_14.txt b/test/prism/errors/command_calls_14.txt
new file mode 100644
index 0000000000..1b16fd3245
--- /dev/null
+++ b/test/prism/errors/command_calls_14.txt
@@ -0,0 +1,3 @@
+a = b, c d
+ ^ unexpected local variable or method, expecting end-of-input
+
diff --git a/test/prism/errors/command_calls_15.txt b/test/prism/errors/command_calls_15.txt
new file mode 100644
index 0000000000..d2409fd002
--- /dev/null
+++ b/test/prism/errors/command_calls_15.txt
@@ -0,0 +1,3 @@
+a = *b c
+ ^ unexpected local variable or method, expecting end-of-input
+
diff --git a/test/prism/errors/command_calls_16.txt b/test/prism/errors/command_calls_16.txt
new file mode 100644
index 0000000000..ceb07dfe30
--- /dev/null
+++ b/test/prism/errors/command_calls_16.txt
@@ -0,0 +1,3 @@
+a, b = c = d f
+ ^ unexpected local variable or method, expecting end-of-input
+
diff --git a/test/prism/errors/command_calls_17.txt b/test/prism/errors/command_calls_17.txt
new file mode 100644
index 0000000000..a78ac0985d
--- /dev/null
+++ b/test/prism/errors/command_calls_17.txt
@@ -0,0 +1,5 @@
+a ? b c : d e
+ ^ expected a `:` after the true expression of a ternary operator
+ ^ unexpected ':', expecting end-of-input
+ ^ unexpected ':', ignoring it
+
diff --git a/test/prism/errors/command_calls_18.txt b/test/prism/errors/command_calls_18.txt
new file mode 100644
index 0000000000..393e7e0ae6
--- /dev/null
+++ b/test/prism/errors/command_calls_18.txt
@@ -0,0 +1,3 @@
+defined? a b
+ ^ unexpected local variable or method, expecting end-of-input
+
diff --git a/test/prism/errors/command_calls_19.txt b/test/prism/errors/command_calls_19.txt
new file mode 100644
index 0000000000..e045187f1e
--- /dev/null
+++ b/test/prism/errors/command_calls_19.txt
@@ -0,0 +1,3 @@
+! ! a b
+ ^ unexpected local variable or method, expecting end-of-input
+
diff --git a/test/prism/errors/command_calls_2.txt b/test/prism/errors/command_calls_2.txt
new file mode 100644
index 0000000000..b0983c015b
--- /dev/null
+++ b/test/prism/errors/command_calls_2.txt
@@ -0,0 +1,6 @@
+{a: b c}
+ ^ expected a `}` to close the hash literal
+ ^ unexpected local variable or method, expecting end-of-input
+ ^ unexpected '}', expecting end-of-input
+ ^ unexpected '}', ignoring it
+
diff --git a/test/prism/errors/command_calls_20.txt b/test/prism/errors/command_calls_20.txt
new file mode 100644
index 0000000000..3058ebce96
--- /dev/null
+++ b/test/prism/errors/command_calls_20.txt
@@ -0,0 +1,3 @@
+def f a = b c; end
+ ^ expected a delimiter to close the parameters
+
diff --git a/test/prism/errors/command_calls_21.txt b/test/prism/errors/command_calls_21.txt
new file mode 100644
index 0000000000..73d8f83539
--- /dev/null
+++ b/test/prism/errors/command_calls_21.txt
@@ -0,0 +1,5 @@
+def f(a = b c); end
+ ^ unexpected local variable or method; expected a `)` to close the parameters
+ ^ unexpected ')', expecting end-of-input
+ ^ unexpected ')', ignoring it
+
diff --git a/test/prism/errors/command_calls_22.txt b/test/prism/errors/command_calls_22.txt
new file mode 100644
index 0000000000..5a234e04e8
--- /dev/null
+++ b/test/prism/errors/command_calls_22.txt
@@ -0,0 +1,3 @@
+a = b rescue c d
+ ^ unexpected local variable or method, expecting end-of-input
+
diff --git a/test/prism/errors/command_calls_23.txt b/test/prism/errors/command_calls_23.txt
new file mode 100644
index 0000000000..db85589ffd
--- /dev/null
+++ b/test/prism/errors/command_calls_23.txt
@@ -0,0 +1,3 @@
+def a = b rescue c d
+ ^ unexpected local variable or method, expecting end-of-input
+
diff --git a/test/prism/errors/command_calls_24.txt b/test/prism/errors/command_calls_24.txt
new file mode 100644
index 0000000000..3046b36dc1
--- /dev/null
+++ b/test/prism/errors/command_calls_24.txt
@@ -0,0 +1,5 @@
+->a=b c{}
+ ^ expected a `do` keyword or a `{` to open the lambda block
+ ^ unexpected end-of-input, assuming it is closing the parent top level context
+ ^ expected a lambda block beginning with `do` to end with `end`
+
diff --git a/test/prism/errors/command_calls_25.txt b/test/prism/errors/command_calls_25.txt
new file mode 100644
index 0000000000..5fddd90fdd
--- /dev/null
+++ b/test/prism/errors/command_calls_25.txt
@@ -0,0 +1,8 @@
+->(a=b c){}
+ ^ expected a matching `)`
+ ^ expected a `do` keyword or a `{` to open the lambda block
+ ^ unexpected ')', expecting end-of-input
+ ^ unexpected ')', ignoring it
+ ^ unexpected end-of-input, assuming it is closing the parent top level context
+ ^ expected a lambda block beginning with `do` to end with `end`
+
diff --git a/test/prism/errors/command_calls_26.txt b/test/prism/errors/command_calls_26.txt
new file mode 100644
index 0000000000..29ed4cb903
--- /dev/null
+++ b/test/prism/errors/command_calls_26.txt
@@ -0,0 +1,3 @@
+case; when a b; end
+ ^ expected a delimiter after the predicates of a `when` clause
+
diff --git a/test/prism/errors/command_calls_27.txt b/test/prism/errors/command_calls_27.txt
new file mode 100644
index 0000000000..8d1c3ee077
--- /dev/null
+++ b/test/prism/errors/command_calls_27.txt
@@ -0,0 +1,3 @@
+case; in a if a b; end
+^~~~ expected a predicate for a case matching statement
+
diff --git a/test/prism/errors/command_calls_28.txt b/test/prism/errors/command_calls_28.txt
new file mode 100644
index 0000000000..4bfe88d67b
--- /dev/null
+++ b/test/prism/errors/command_calls_28.txt
@@ -0,0 +1,3 @@
+case; in a unless a b; end
+^~~~ expected a predicate for a case matching statement
+
diff --git a/test/prism/errors/command_calls_29.txt b/test/prism/errors/command_calls_29.txt
new file mode 100644
index 0000000000..eae012ab44
--- /dev/null
+++ b/test/prism/errors/command_calls_29.txt
@@ -0,0 +1,3 @@
+begin; rescue a b; end
+ ^ expected a closing delimiter for the `rescue` clause
+
diff --git a/test/prism/errors/command_calls_3.txt b/test/prism/errors/command_calls_3.txt
new file mode 100644
index 0000000000..77af72b904
--- /dev/null
+++ b/test/prism/errors/command_calls_3.txt
@@ -0,0 +1,3 @@
+...a b
+ ^ unexpected local variable or method, expecting end-of-input
+
diff --git a/test/prism/errors/command_calls_30.txt b/test/prism/errors/command_calls_30.txt
new file mode 100644
index 0000000000..48e35685cb
--- /dev/null
+++ b/test/prism/errors/command_calls_30.txt
@@ -0,0 +1,3 @@
+begin; rescue a b => c; end
+ ^ expected a closing delimiter for the `rescue` clause
+
diff --git a/test/prism/errors/command_calls_4.txt b/test/prism/errors/command_calls_4.txt
new file mode 100644
index 0000000000..4be14e57e4
--- /dev/null
+++ b/test/prism/errors/command_calls_4.txt
@@ -0,0 +1,3 @@
+if ...a b; end
+ ^ expected `then` or `;` or '\n'
+
diff --git a/test/prism/errors/command_calls_5.txt b/test/prism/errors/command_calls_5.txt
new file mode 100644
index 0000000000..799a6c1136
--- /dev/null
+++ b/test/prism/errors/command_calls_5.txt
@@ -0,0 +1,3 @@
+a b, c d
+ ^ unexpected local variable or method, expecting end-of-input
+
diff --git a/test/prism/errors/command_calls_6.txt b/test/prism/errors/command_calls_6.txt
new file mode 100644
index 0000000000..6f09d36e94
--- /dev/null
+++ b/test/prism/errors/command_calls_6.txt
@@ -0,0 +1,6 @@
+a(b, c d)
+ ^ unexpected local variable or method; expected a `)` to close the arguments
+ ^ unexpected local variable or method, expecting end-of-input
+ ^ unexpected ')', expecting end-of-input
+ ^ unexpected ')', ignoring it
+
diff --git a/test/prism/errors/command_calls_7.txt b/test/prism/errors/command_calls_7.txt
new file mode 100644
index 0000000000..b5d74209fa
--- /dev/null
+++ b/test/prism/errors/command_calls_7.txt
@@ -0,0 +1,6 @@
+a(*b c)
+ ^ unexpected local variable or method; expected a `)` to close the arguments
+ ^ unexpected local variable or method, expecting end-of-input
+ ^ unexpected ')', expecting end-of-input
+ ^ unexpected ')', ignoring it
+
diff --git a/test/prism/errors/command_calls_8.txt b/test/prism/errors/command_calls_8.txt
new file mode 100644
index 0000000000..e574063e72
--- /dev/null
+++ b/test/prism/errors/command_calls_8.txt
@@ -0,0 +1,6 @@
+a(**b c)
+ ^ unexpected local variable or method; expected a `)` to close the arguments
+ ^ unexpected local variable or method, expecting end-of-input
+ ^ unexpected ')', expecting end-of-input
+ ^ unexpected ')', ignoring it
+
diff --git a/test/prism/errors/command_calls_9.txt b/test/prism/errors/command_calls_9.txt
new file mode 100644
index 0000000000..69515d959c
--- /dev/null
+++ b/test/prism/errors/command_calls_9.txt
@@ -0,0 +1,6 @@
+a(&b c)
+ ^ unexpected local variable or method; expected a `)` to close the arguments
+ ^ unexpected local variable or method, expecting end-of-input
+ ^ unexpected ')', expecting end-of-input
+ ^ unexpected ')', ignoring it
+
diff --git a/test/prism/errors/conditional_predicate_closed.txt b/test/prism/errors/conditional_predicate_closed.txt
new file mode 100644
index 0000000000..6655fd2b1c
--- /dev/null
+++ b/test/prism/errors/conditional_predicate_closed.txt
@@ -0,0 +1,6 @@
+if 0 0; elsif 0 0; end
+ ^ expected `then` or `;` or '\n'
+ ^ expected `then` or `;` or '\n'
+unless 0 0; end
+ ^ expected `then` or `;` or '\n'
+
diff --git a/test/prism/errors/constant_assignment_in_method.txt b/test/prism/errors/constant_assignment_in_method.txt
new file mode 100644
index 0000000000..1ee49bffe6
--- /dev/null
+++ b/test/prism/errors/constant_assignment_in_method.txt
@@ -0,0 +1,3 @@
+def foo();A=1;end
+ ^~~ dynamic constant assignment
+
diff --git a/test/prism/errors/constant_path_with_invalid_token_after.txt b/test/prism/errors/constant_path_with_invalid_token_after.txt
new file mode 100644
index 0000000000..acb6dba30a
--- /dev/null
+++ b/test/prism/errors/constant_path_with_invalid_token_after.txt
@@ -0,0 +1,4 @@
+A::$b
+ ^ expected a constant after the `::` operator
+ ^~ unexpected global variable, expecting end-of-input
+
diff --git a/test/prism/errors/content_after_unterminated_heredoc.txt b/test/prism/errors/content_after_unterminated_heredoc.txt
new file mode 100644
index 0000000000..43815cd9d0
--- /dev/null
+++ b/test/prism/errors/content_after_unterminated_heredoc.txt
@@ -0,0 +1,2 @@
+<<~FOO.foo
+ ^~~ unterminated heredoc; can't find string "FOO" anywhere before EOF
diff --git a/test/prism/errors/cr_without_lf_in_percent_expression.txt b/test/prism/errors/cr_without_lf_in_percent_expression.txt
new file mode 100644
index 0000000000..903f8b4b4a
--- /dev/null
+++ b/test/prism/errors/cr_without_lf_in_percent_expression.txt
@@ -0,0 +1,3 @@
+%
+ ^ unterminated string meets end of file
+
diff --git a/test/prism/errors/def_with_empty_expression_receiver.txt b/test/prism/errors/def_with_empty_expression_receiver.txt
new file mode 100644
index 0000000000..153fe8a1c6
--- /dev/null
+++ b/test/prism/errors/def_with_empty_expression_receiver.txt
@@ -0,0 +1,3 @@
+def ().a; end
+ ^ expected a receiver for the method definition
+
diff --git a/test/prism/errors/def_with_expression_receiver_and_no_identifier.txt b/test/prism/errors/def_with_expression_receiver_and_no_identifier.txt
new file mode 100644
index 0000000000..1aefc07f1a
--- /dev/null
+++ b/test/prism/errors/def_with_expression_receiver_and_no_identifier.txt
@@ -0,0 +1,4 @@
+def (a); end
+ ^ expected a `.` or `::` after the receiver in a method definition
+ ^ unexpected ';'; expected a method name
+
diff --git a/test/prism/errors/def_with_multiple_statements_receiver.txt b/test/prism/errors/def_with_multiple_statements_receiver.txt
new file mode 100644
index 0000000000..80c9d4c190
--- /dev/null
+++ b/test/prism/errors/def_with_multiple_statements_receiver.txt
@@ -0,0 +1,10 @@
+def (
+a
+b
+^ expected a matching `)`
+^ expected a `.` or `::` after the receiver in a method definition
+ ^ expected a delimiter to close the parameters
+).c; end
+^ unexpected ')', ignoring it
+ ^ unexpected '.', ignoring it
+
diff --git a/test/prism/errors/defining_numbered_parameter.txt b/test/prism/errors/defining_numbered_parameter.txt
new file mode 100644
index 0000000000..2bf05d9563
--- /dev/null
+++ b/test/prism/errors/defining_numbered_parameter.txt
@@ -0,0 +1,3 @@
+def _1; end
+ ^~ _1 is reserved for numbered parameters
+
diff --git a/test/prism/errors/defining_numbered_parameter_2.txt b/test/prism/errors/defining_numbered_parameter_2.txt
new file mode 100644
index 0000000000..dc4739b126
--- /dev/null
+++ b/test/prism/errors/defining_numbered_parameter_2.txt
@@ -0,0 +1,3 @@
+def self._1; end
+ ^~ _1 is reserved for numbered parameters
+
diff --git a/test/prism/errors/do_not_allow_characters_other_than_0_9_a_f_and_A_F_in_u_Unicode_character_notation.txt b/test/prism/errors/do_not_allow_characters_other_than_0_9_a_f_and_A_F_in_u_Unicode_character_notation.txt
new file mode 100644
index 0000000000..953b9589d1
--- /dev/null
+++ b/test/prism/errors/do_not_allow_characters_other_than_0_9_a_f_and_A_F_in_u_Unicode_character_notation.txt
@@ -0,0 +1,4 @@
+"\u{000z}"
+ ^ invalid Unicode escape sequence
+ ^ unterminated Unicode escape
+
diff --git a/test/prism/errors/do_not_allow_forward_arguments_in_blocks.txt b/test/prism/errors/do_not_allow_forward_arguments_in_blocks.txt
new file mode 100644
index 0000000000..df49557617
--- /dev/null
+++ b/test/prism/errors/do_not_allow_forward_arguments_in_blocks.txt
@@ -0,0 +1,3 @@
+a {|...|}
+ ^~~ unexpected ... when the parent method is not forwarding
+
diff --git a/test/prism/errors/do_not_allow_forward_arguments_in_lambda_literals.txt b/test/prism/errors/do_not_allow_forward_arguments_in_lambda_literals.txt
new file mode 100644
index 0000000000..c2405a5c66
--- /dev/null
+++ b/test/prism/errors/do_not_allow_forward_arguments_in_lambda_literals.txt
@@ -0,0 +1,3 @@
+->(...) {}
+ ^~~ unexpected ... when the parent method is not forwarding
+
diff --git a/test/prism/errors/do_not_allow_more_than_6_hexadecimal_digits_in_u_Unicode_character_notation.txt b/test/prism/errors/do_not_allow_more_than_6_hexadecimal_digits_in_u_Unicode_character_notation.txt
new file mode 100644
index 0000000000..50795c9353
--- /dev/null
+++ b/test/prism/errors/do_not_allow_more_than_6_hexadecimal_digits_in_u_Unicode_character_notation.txt
@@ -0,0 +1,3 @@
+"\u{0000001}"
+ ^~~~~~~ invalid Unicode escape sequence; maximum length is 6 digits
+
diff --git a/test/prism/errors/do_not_allow_multiple_codepoints_in_a_single_character_literal.txt b/test/prism/errors/do_not_allow_multiple_codepoints_in_a_single_character_literal.txt
new file mode 100644
index 0000000000..1a93dc6c69
--- /dev/null
+++ b/test/prism/errors/do_not_allow_multiple_codepoints_in_a_single_character_literal.txt
@@ -0,0 +1,3 @@
+?\u{0001 0002}
+ ^~~ invalid Unicode escape sequence; Multiple codepoints at single character literal are disallowed
+
diff --git a/test/prism/errors/do_not_allow_trailing_commas_in_lambda_parameters.txt b/test/prism/errors/do_not_allow_trailing_commas_in_lambda_parameters.txt
new file mode 100644
index 0000000000..11f23f0345
--- /dev/null
+++ b/test/prism/errors/do_not_allow_trailing_commas_in_lambda_parameters.txt
@@ -0,0 +1,3 @@
+-> (a, b, ) {}
+ ^ unexpected `,` in parameters
+
diff --git a/test/prism/errors/do_not_allow_trailing_commas_in_method_parameters.txt b/test/prism/errors/do_not_allow_trailing_commas_in_method_parameters.txt
new file mode 100644
index 0000000000..c0fec0c704
--- /dev/null
+++ b/test/prism/errors/do_not_allow_trailing_commas_in_method_parameters.txt
@@ -0,0 +1,3 @@
+def foo(a,b,c,);end
+ ^ unexpected `,` in parameters
+
diff --git a/test/prism/errors/dont_allow_return_inside_class_body.txt b/test/prism/errors/dont_allow_return_inside_class_body.txt
new file mode 100644
index 0000000000..286eba2103
--- /dev/null
+++ b/test/prism/errors/dont_allow_return_inside_class_body.txt
@@ -0,0 +1,3 @@
+class A; return; end
+ ^~~~~~ Invalid return in class/module body
+
diff --git a/test/prism/errors/dont_allow_return_inside_module_body.txt b/test/prism/errors/dont_allow_return_inside_module_body.txt
new file mode 100644
index 0000000000..85dd619a93
--- /dev/null
+++ b/test/prism/errors/dont_allow_return_inside_module_body.txt
@@ -0,0 +1,3 @@
+module A; return; end
+ ^~~~~~ Invalid return in class/module body
+
diff --git a/test/prism/errors/dont_allow_setting_to_back_and_nth_reference.txt b/test/prism/errors/dont_allow_setting_to_back_and_nth_reference.txt
new file mode 100644
index 0000000000..71b5b94589
--- /dev/null
+++ b/test/prism/errors/dont_allow_setting_to_back_and_nth_reference.txt
@@ -0,0 +1,7 @@
+begin
+$+ = nil
+^~ Can't set variable $+
+$1466 = nil
+^~~~~ Can't set variable $1466
+end
+
diff --git a/test/prism/errors/double_arguments_forwarding.txt b/test/prism/errors/double_arguments_forwarding.txt
new file mode 100644
index 0000000000..29c78f8c80
--- /dev/null
+++ b/test/prism/errors/double_arguments_forwarding.txt
@@ -0,0 +1,4 @@
+def foo(..., ...)
+ ^~~ unexpected parameter order
+end
+
diff --git a/test/prism/errors/double_scope_numbered_parameters.txt b/test/prism/errors/double_scope_numbered_parameters.txt
new file mode 100644
index 0000000000..0bb9df4ede
--- /dev/null
+++ b/test/prism/errors/double_scope_numbered_parameters.txt
@@ -0,0 +1,3 @@
+-> { _1 + -> { _2 } }
+ ^~ numbered parameter is already used in outer block
+
diff --git a/test/prism/errors/double_splat_followed_by_splat_argument.txt b/test/prism/errors/double_splat_followed_by_splat_argument.txt
new file mode 100644
index 0000000000..b2aec1167e
--- /dev/null
+++ b/test/prism/errors/double_splat_followed_by_splat_argument.txt
@@ -0,0 +1,3 @@
+a(**kwargs, *args)
+ ^~~~~ unexpected `*` splat argument after a `**` keyword splat argument
+
diff --git a/test/prism/errors/duplicate_pattern_capture.txt b/test/prism/errors/duplicate_pattern_capture.txt
new file mode 100644
index 0000000000..4b48fd3118
--- /dev/null
+++ b/test/prism/errors/duplicate_pattern_capture.txt
@@ -0,0 +1,17 @@
+case (); in [a, a]; end
+ ^ duplicated variable name
+case (); in [a, *a]; end
+ ^ duplicated variable name
+case (); in {a: a, b: a}; end
+ ^ duplicated variable name
+case (); in {a: a, **a}; end
+ ^ duplicated variable name
+case (); in [a, {a:}]; end
+ ^ duplicated variable name
+case (); in [a, {a: {a: {a: [a]}}}]; end
+ ^ duplicated variable name
+case (); in a => a; end
+ ^ duplicated variable name
+case (); in [A => a, {a: b => a}]; end
+ ^ duplicated variable name
+
diff --git a/test/prism/errors/duplicate_pattern_hash_key.txt b/test/prism/errors/duplicate_pattern_hash_key.txt
new file mode 100644
index 0000000000..201b51234f
--- /dev/null
+++ b/test/prism/errors/duplicate_pattern_hash_key.txt
@@ -0,0 +1,4 @@
+case (); in {a:, a:}; end
+ ^~ duplicated key name
+ ^ duplicated variable name
+
diff --git a/test/prism/errors/duplicate_pattern_hash_key_2.txt b/test/prism/errors/duplicate_pattern_hash_key_2.txt
new file mode 100644
index 0000000000..66756c454a
--- /dev/null
+++ b/test/prism/errors/duplicate_pattern_hash_key_2.txt
@@ -0,0 +1,3 @@
+case (); in {a:1, a:2}; end
+ ^~ duplicated key name
+
diff --git a/test/prism/errors/duplicated_parameter_names.txt b/test/prism/errors/duplicated_parameter_names.txt
new file mode 100644
index 0000000000..7b82685ca3
--- /dev/null
+++ b/test/prism/errors/duplicated_parameter_names.txt
@@ -0,0 +1,3 @@
+def foo(a,b,a);end
+ ^ duplicated argument name
+
diff --git a/test/prism/errors/duplicated_parameter_names_2.txt b/test/prism/errors/duplicated_parameter_names_2.txt
new file mode 100644
index 0000000000..8396993d56
--- /dev/null
+++ b/test/prism/errors/duplicated_parameter_names_2.txt
@@ -0,0 +1,3 @@
+def foo(a,b,*a);end
+ ^ duplicated argument name
+
diff --git a/test/prism/errors/duplicated_parameter_names_3.txt b/test/prism/errors/duplicated_parameter_names_3.txt
new file mode 100644
index 0000000000..437a6623c3
--- /dev/null
+++ b/test/prism/errors/duplicated_parameter_names_3.txt
@@ -0,0 +1,3 @@
+def foo(a,b,**a);end
+ ^ duplicated argument name
+
diff --git a/test/prism/errors/duplicated_parameter_names_4.txt b/test/prism/errors/duplicated_parameter_names_4.txt
new file mode 100644
index 0000000000..a420dd8a69
--- /dev/null
+++ b/test/prism/errors/duplicated_parameter_names_4.txt
@@ -0,0 +1,3 @@
+def foo(a,b,&a);end
+ ^ duplicated argument name
+
diff --git a/test/prism/errors/duplicated_parameter_names_5.txt b/test/prism/errors/duplicated_parameter_names_5.txt
new file mode 100644
index 0000000000..694d3a668c
--- /dev/null
+++ b/test/prism/errors/duplicated_parameter_names_5.txt
@@ -0,0 +1,3 @@
+def foo(a = 1,b,*c);end
+ ^ unexpected parameter `*`
+
diff --git a/test/prism/errors/ellipsis_in_no_paren_call.txt b/test/prism/errors/ellipsis_in_no_paren_call.txt
new file mode 100644
index 0000000000..87a847d192
--- /dev/null
+++ b/test/prism/errors/ellipsis_in_no_paren_call.txt
@@ -0,0 +1,3 @@
+def foo(...); foo 1, ...; end
+ ^~~ unexpected `...` in an non-parenthesized call
+
diff --git a/test/prism/errors/for_loops_index_missing.txt b/test/prism/errors/for_loops_index_missing.txt
new file mode 100644
index 0000000000..a57c22b044
--- /dev/null
+++ b/test/prism/errors/for_loops_index_missing.txt
@@ -0,0 +1,5 @@
+for in 1..10
+^~~ expected an index after `for`
+i
+end
+
diff --git a/test/prism/errors/for_loops_only_end.txt b/test/prism/errors/for_loops_only_end.txt
new file mode 100644
index 0000000000..94cc5270b5
--- /dev/null
+++ b/test/prism/errors/for_loops_only_end.txt
@@ -0,0 +1,5 @@
+for end
+^~~ expected an index after `for`
+ ^ expected an `in` after the index in a `for` statement
+ ^ expected a collection after the `in` in a `for` statement
+
diff --git a/test/prism/errors/forwarding_arg_after_keyword_rest.txt b/test/prism/errors/forwarding_arg_after_keyword_rest.txt
new file mode 100644
index 0000000000..86fe4aad93
--- /dev/null
+++ b/test/prism/errors/forwarding_arg_after_keyword_rest.txt
@@ -0,0 +1,3 @@
+def f(**,...);end
+ ^~~ unexpected parameter order
+
diff --git a/test/prism/errors/forwarding_arg_and_block.txt b/test/prism/errors/forwarding_arg_and_block.txt
new file mode 100644
index 0000000000..65c75a5d7c
--- /dev/null
+++ b/test/prism/errors/forwarding_arg_and_block.txt
@@ -0,0 +1,3 @@
+def foo(...) = foo(...) { }
+ ^~~ both block arg and actual block given; only one block is allowed
+
diff --git a/test/prism/errors/incomplete_instance_var_string.txt b/test/prism/errors/incomplete_instance_var_string.txt
new file mode 100644
index 0000000000..b28947fc0e
--- /dev/null
+++ b/test/prism/errors/incomplete_instance_var_string.txt
@@ -0,0 +1,4 @@
+%@#@@#
+ ^ '@' without identifiers is not allowed as an instance variable name
+ ^ unexpected instance variable, expecting end-of-input
+
diff --git a/test/prism/errors/index_call_with_block_and_write.txt b/test/prism/errors/index_call_with_block_and_write.txt
new file mode 100644
index 0000000000..3d92fbfea7
--- /dev/null
+++ b/test/prism/errors/index_call_with_block_and_write.txt
@@ -0,0 +1,5 @@
+foo[1] {} &&= 1
+^~~~~~~~~ unexpected write target
+ ^~~ unexpected operator after a call with arguments
+ ^~~ unexpected operator after a call with a block
+
diff --git a/test/prism/errors/index_call_with_block_operator_write.txt b/test/prism/errors/index_call_with_block_operator_write.txt
new file mode 100644
index 0000000000..96c413cd39
--- /dev/null
+++ b/test/prism/errors/index_call_with_block_operator_write.txt
@@ -0,0 +1,5 @@
+foo[1] {} += 1
+^~~~~~~~~ unexpected write target
+ ^~ unexpected operator after a call with arguments
+ ^~ unexpected operator after a call with a block
+
diff --git a/test/prism/errors/index_call_with_block_or_write.txt b/test/prism/errors/index_call_with_block_or_write.txt
new file mode 100644
index 0000000000..2d250fba06
--- /dev/null
+++ b/test/prism/errors/index_call_with_block_or_write.txt
@@ -0,0 +1,5 @@
+foo[1] {} ||= 1
+^~~~~~~~~ unexpected write target
+ ^~~ unexpected operator after a call with arguments
+ ^~~ unexpected operator after a call with a block
+
diff --git a/test/prism/errors/interpolated_regular_expression_with_unknown_regexp_options.txt b/test/prism/errors/interpolated_regular_expression_with_unknown_regexp_options.txt
new file mode 100644
index 0000000000..8e78753b1c
--- /dev/null
+++ b/test/prism/errors/interpolated_regular_expression_with_unknown_regexp_options.txt
@@ -0,0 +1,3 @@
+/#{foo}/AZaz
+ ^~~~~ unknown regexp options - AZaz
+
diff --git a/test/prism/errors/invalid_global_variable_write.txt b/test/prism/errors/invalid_global_variable_write.txt
new file mode 100644
index 0000000000..9d9018bcf1
--- /dev/null
+++ b/test/prism/errors/invalid_global_variable_write.txt
@@ -0,0 +1,4 @@
+$',
+^~ Can't set variable $'
+^~ unexpected write target
+
diff --git a/test/prism/errors/invalid_hex_escape.txt b/test/prism/errors/invalid_hex_escape.txt
new file mode 100644
index 0000000000..4fb847f6d2
--- /dev/null
+++ b/test/prism/errors/invalid_hex_escape.txt
@@ -0,0 +1,3 @@
+"\xx"
+ ^~ invalid hex escape sequence
+
diff --git a/test/prism/errors/invalid_multi_target.txt b/test/prism/errors/invalid_multi_target.txt
new file mode 100644
index 0000000000..9756278b0c
--- /dev/null
+++ b/test/prism/errors/invalid_multi_target.txt
@@ -0,0 +1,3 @@
+foo,
+^~~ unexpected write target
+
diff --git a/test/prism/errors/invalid_multi_target_10.txt b/test/prism/errors/invalid_multi_target_10.txt
new file mode 100644
index 0000000000..0e87b67d36
--- /dev/null
+++ b/test/prism/errors/invalid_multi_target_10.txt
@@ -0,0 +1,3 @@
+Foo,
+^~~ unexpected write target
+
diff --git a/test/prism/errors/invalid_multi_target_11.txt b/test/prism/errors/invalid_multi_target_11.txt
new file mode 100644
index 0000000000..8185cde79e
--- /dev/null
+++ b/test/prism/errors/invalid_multi_target_11.txt
@@ -0,0 +1,3 @@
+::Foo,
+^~~~~ unexpected write target
+
diff --git a/test/prism/errors/invalid_multi_target_12.txt b/test/prism/errors/invalid_multi_target_12.txt
new file mode 100644
index 0000000000..f511a8a76f
--- /dev/null
+++ b/test/prism/errors/invalid_multi_target_12.txt
@@ -0,0 +1,3 @@
+Foo::Foo,
+^~~~~~~~ unexpected write target
+
diff --git a/test/prism/errors/invalid_multi_target_13.txt b/test/prism/errors/invalid_multi_target_13.txt
new file mode 100644
index 0000000000..7c9a3fb4e1
--- /dev/null
+++ b/test/prism/errors/invalid_multi_target_13.txt
@@ -0,0 +1,3 @@
+Foo::foo,
+^~~~~~~~ unexpected write target
+
diff --git a/test/prism/errors/invalid_multi_target_14.txt b/test/prism/errors/invalid_multi_target_14.txt
new file mode 100644
index 0000000000..88dc08de92
--- /dev/null
+++ b/test/prism/errors/invalid_multi_target_14.txt
@@ -0,0 +1,3 @@
+foo[foo],
+^~~~~~~~ unexpected write target
+
diff --git a/test/prism/errors/invalid_multi_target_15.txt b/test/prism/errors/invalid_multi_target_15.txt
new file mode 100644
index 0000000000..c140833467
--- /dev/null
+++ b/test/prism/errors/invalid_multi_target_15.txt
@@ -0,0 +1,3 @@
+(foo, bar)
+^~~~~~~~~~ unexpected write target
+
diff --git a/test/prism/errors/invalid_multi_target_16.txt b/test/prism/errors/invalid_multi_target_16.txt
new file mode 100644
index 0000000000..20ea56331f
--- /dev/null
+++ b/test/prism/errors/invalid_multi_target_16.txt
@@ -0,0 +1,3 @@
+foo((foo, bar))
+ ^~~~~~~~~~ unexpected write target
+
diff --git a/test/prism/errors/invalid_multi_target_17.txt b/test/prism/errors/invalid_multi_target_17.txt
new file mode 100644
index 0000000000..da1ced0c59
--- /dev/null
+++ b/test/prism/errors/invalid_multi_target_17.txt
@@ -0,0 +1,3 @@
+foo((*))
+ ^~~ unexpected write target
+
diff --git a/test/prism/errors/invalid_multi_target_18.txt b/test/prism/errors/invalid_multi_target_18.txt
new file mode 100644
index 0000000000..2beed193b4
--- /dev/null
+++ b/test/prism/errors/invalid_multi_target_18.txt
@@ -0,0 +1,3 @@
+foo(((foo, bar), *))
+ ^~~~~~~~~~~~~~~ unexpected write target
+
diff --git a/test/prism/errors/invalid_multi_target_19.txt b/test/prism/errors/invalid_multi_target_19.txt
new file mode 100644
index 0000000000..b5e3e6999a
--- /dev/null
+++ b/test/prism/errors/invalid_multi_target_19.txt
@@ -0,0 +1,3 @@
+(foo, bar) + 1
+^~~~~~~~~~ unexpected write target
+
diff --git a/test/prism/errors/invalid_multi_target_2.txt b/test/prism/errors/invalid_multi_target_2.txt
new file mode 100644
index 0000000000..68a7bbc305
--- /dev/null
+++ b/test/prism/errors/invalid_multi_target_2.txt
@@ -0,0 +1,3 @@
+foo = 1; foo,
+ ^~~ unexpected write target
+
diff --git a/test/prism/errors/invalid_multi_target_20.txt b/test/prism/errors/invalid_multi_target_20.txt
new file mode 100644
index 0000000000..e800bcf204
--- /dev/null
+++ b/test/prism/errors/invalid_multi_target_20.txt
@@ -0,0 +1,3 @@
+(foo, bar) in baz
+^~~~~~~~~~ unexpected write target
+
diff --git a/test/prism/errors/invalid_multi_target_3.txt b/test/prism/errors/invalid_multi_target_3.txt
new file mode 100644
index 0000000000..51e6207603
--- /dev/null
+++ b/test/prism/errors/invalid_multi_target_3.txt
@@ -0,0 +1,3 @@
+foo.bar,
+^~~~~~~ unexpected write target
+
diff --git a/test/prism/errors/invalid_multi_target_4.txt b/test/prism/errors/invalid_multi_target_4.txt
new file mode 100644
index 0000000000..f4c3599ffe
--- /dev/null
+++ b/test/prism/errors/invalid_multi_target_4.txt
@@ -0,0 +1,3 @@
+*foo,
+^~~~ unexpected write target
+
diff --git a/test/prism/errors/invalid_multi_target_5.txt b/test/prism/errors/invalid_multi_target_5.txt
new file mode 100644
index 0000000000..5d143a3f5d
--- /dev/null
+++ b/test/prism/errors/invalid_multi_target_5.txt
@@ -0,0 +1,3 @@
+@foo,
+^~~~ unexpected write target
+
diff --git a/test/prism/errors/invalid_multi_target_6.txt b/test/prism/errors/invalid_multi_target_6.txt
new file mode 100644
index 0000000000..6d15893f57
--- /dev/null
+++ b/test/prism/errors/invalid_multi_target_6.txt
@@ -0,0 +1,3 @@
+@@foo,
+^~~~~ unexpected write target
+
diff --git a/test/prism/errors/invalid_multi_target_7.txt b/test/prism/errors/invalid_multi_target_7.txt
new file mode 100644
index 0000000000..451f9f0a00
--- /dev/null
+++ b/test/prism/errors/invalid_multi_target_7.txt
@@ -0,0 +1,3 @@
+$foo,
+^~~~ unexpected write target
+
diff --git a/test/prism/errors/invalid_multi_target_8.txt b/test/prism/errors/invalid_multi_target_8.txt
new file mode 100644
index 0000000000..fdbe272f9a
--- /dev/null
+++ b/test/prism/errors/invalid_multi_target_8.txt
@@ -0,0 +1,4 @@
+$1,
+^~ Can't set variable $1
+^~ unexpected write target
+
diff --git a/test/prism/errors/invalid_multi_target_9.txt b/test/prism/errors/invalid_multi_target_9.txt
new file mode 100644
index 0000000000..038f355c5d
--- /dev/null
+++ b/test/prism/errors/invalid_multi_target_9.txt
@@ -0,0 +1,4 @@
+$+,
+^~ Can't set variable $+
+^~ unexpected write target
+
diff --git a/test/prism/errors/invalid_number_underscores.txt b/test/prism/errors/invalid_number_underscores.txt
new file mode 100644
index 0000000000..8fc79ed7a2
--- /dev/null
+++ b/test/prism/errors/invalid_number_underscores.txt
@@ -0,0 +1,3 @@
+1__1
+ ^ invalid underscore placement in number
+
diff --git a/test/prism/errors/invalid_number_underscores_10.txt b/test/prism/errors/invalid_number_underscores_10.txt
new file mode 100644
index 0000000000..53b0cc0719
--- /dev/null
+++ b/test/prism/errors/invalid_number_underscores_10.txt
@@ -0,0 +1,3 @@
+01_1_
+ ^ trailing '_' in number
+
diff --git a/test/prism/errors/invalid_number_underscores_11.txt b/test/prism/errors/invalid_number_underscores_11.txt
new file mode 100644
index 0000000000..469110f86f
--- /dev/null
+++ b/test/prism/errors/invalid_number_underscores_11.txt
@@ -0,0 +1,3 @@
+0d1_1_
+ ^ trailing '_' in number
+
diff --git a/test/prism/errors/invalid_number_underscores_12.txt b/test/prism/errors/invalid_number_underscores_12.txt
new file mode 100644
index 0000000000..a9b63a4b6c
--- /dev/null
+++ b/test/prism/errors/invalid_number_underscores_12.txt
@@ -0,0 +1,3 @@
+0x1_1_
+ ^ trailing '_' in number
+
diff --git a/test/prism/errors/invalid_number_underscores_2.txt b/test/prism/errors/invalid_number_underscores_2.txt
new file mode 100644
index 0000000000..2762e08790
--- /dev/null
+++ b/test/prism/errors/invalid_number_underscores_2.txt
@@ -0,0 +1,3 @@
+0b1__1
+ ^ invalid underscore placement in number
+
diff --git a/test/prism/errors/invalid_number_underscores_3.txt b/test/prism/errors/invalid_number_underscores_3.txt
new file mode 100644
index 0000000000..23f1e0b10b
--- /dev/null
+++ b/test/prism/errors/invalid_number_underscores_3.txt
@@ -0,0 +1,3 @@
+0o1__1
+ ^ invalid underscore placement in number
+
diff --git a/test/prism/errors/invalid_number_underscores_4.txt b/test/prism/errors/invalid_number_underscores_4.txt
new file mode 100644
index 0000000000..ced149752f
--- /dev/null
+++ b/test/prism/errors/invalid_number_underscores_4.txt
@@ -0,0 +1,3 @@
+01__1
+ ^ invalid underscore placement in number
+
diff --git a/test/prism/errors/invalid_number_underscores_5.txt b/test/prism/errors/invalid_number_underscores_5.txt
new file mode 100644
index 0000000000..5e3f2bf682
--- /dev/null
+++ b/test/prism/errors/invalid_number_underscores_5.txt
@@ -0,0 +1,3 @@
+0d1__1
+ ^ invalid underscore placement in number
+
diff --git a/test/prism/errors/invalid_number_underscores_6.txt b/test/prism/errors/invalid_number_underscores_6.txt
new file mode 100644
index 0000000000..225b654248
--- /dev/null
+++ b/test/prism/errors/invalid_number_underscores_6.txt
@@ -0,0 +1,3 @@
+0x1__1
+ ^ invalid underscore placement in number
+
diff --git a/test/prism/errors/invalid_number_underscores_7.txt b/test/prism/errors/invalid_number_underscores_7.txt
new file mode 100644
index 0000000000..d953b4cbc4
--- /dev/null
+++ b/test/prism/errors/invalid_number_underscores_7.txt
@@ -0,0 +1,3 @@
+1_1_
+ ^ trailing '_' in number
+
diff --git a/test/prism/errors/invalid_number_underscores_8.txt b/test/prism/errors/invalid_number_underscores_8.txt
new file mode 100644
index 0000000000..cbdcd95d8f
--- /dev/null
+++ b/test/prism/errors/invalid_number_underscores_8.txt
@@ -0,0 +1,3 @@
+0b1_1_
+ ^ trailing '_' in number
+
diff --git a/test/prism/errors/invalid_number_underscores_9.txt b/test/prism/errors/invalid_number_underscores_9.txt
new file mode 100644
index 0000000000..173282ffb2
--- /dev/null
+++ b/test/prism/errors/invalid_number_underscores_9.txt
@@ -0,0 +1,3 @@
+0o1_1_
+ ^ trailing '_' in number
+
diff --git a/test/prism/errors/invalid_operator_write_dot.txt b/test/prism/errors/invalid_operator_write_dot.txt
new file mode 100644
index 0000000000..666817e60f
--- /dev/null
+++ b/test/prism/errors/invalid_operator_write_dot.txt
@@ -0,0 +1,3 @@
+foo.+= 1
+ ^ unexpected write target
+
diff --git a/test/prism/errors/invalid_operator_write_fcall.txt b/test/prism/errors/invalid_operator_write_fcall.txt
new file mode 100644
index 0000000000..2748bf3291
--- /dev/null
+++ b/test/prism/errors/invalid_operator_write_fcall.txt
@@ -0,0 +1,3 @@
+foo! += 1
+^~~~ unexpected write target
+
diff --git a/test/prism/errors/it_with_ordinary_parameter.txt b/test/prism/errors/it_with_ordinary_parameter.txt
new file mode 100644
index 0000000000..0fc34e9cc8
--- /dev/null
+++ b/test/prism/errors/it_with_ordinary_parameter.txt
@@ -0,0 +1,3 @@
+proc { || it }
+ ^~ `it` is not allowed when an ordinary parameter is defined
+
diff --git a/test/prism/errors/keywords_parameters_before_required_parameters.txt b/test/prism/errors/keywords_parameters_before_required_parameters.txt
new file mode 100644
index 0000000000..42d036e950
--- /dev/null
+++ b/test/prism/errors/keywords_parameters_before_required_parameters.txt
@@ -0,0 +1,4 @@
+def foo(b:, a)
+ ^ unexpected parameter order
+end
+
diff --git a/test/prism/errors/loop_conditional_is_closed.txt b/test/prism/errors/loop_conditional_is_closed.txt
new file mode 100644
index 0000000000..2be1353319
--- /dev/null
+++ b/test/prism/errors/loop_conditional_is_closed.txt
@@ -0,0 +1,4 @@
+while 0 0; foo; end; until 0 0; foo; end
+ ^ expected a predicate expression for the `while` statement
+ ^ expected a predicate expression for the `until` statement
+
diff --git a/test/prism/errors/match_plus.txt b/test/prism/errors/match_plus.txt
new file mode 100644
index 0000000000..5e349a96ad
--- /dev/null
+++ b/test/prism/errors/match_plus.txt
@@ -0,0 +1,7 @@
+a in b + c
+ ^ unexpected '+', expecting end-of-input
+ ^ unexpected '+', ignoring it
+a => b + c
+ ^ unexpected '+', expecting end-of-input
+ ^ unexpected '+', ignoring it
+
diff --git a/test/prism/errors/method_parameters_after_arguments_forwarding.txt b/test/prism/errors/method_parameters_after_arguments_forwarding.txt
new file mode 100644
index 0000000000..ec2aefda1d
--- /dev/null
+++ b/test/prism/errors/method_parameters_after_arguments_forwarding.txt
@@ -0,0 +1,4 @@
+def foo(..., a)
+ ^ unexpected parameter order
+end
+
diff --git a/test/prism/errors/method_parameters_after_block.txt b/test/prism/errors/method_parameters_after_block.txt
new file mode 100644
index 0000000000..6e2091d5d1
--- /dev/null
+++ b/test/prism/errors/method_parameters_after_block.txt
@@ -0,0 +1,4 @@
+def foo(&block, a)
+ ^ unexpected parameter order
+end
+
diff --git a/test/prism/errors/method_with_arguments_after_anonymous_block.txt b/test/prism/errors/method_with_arguments_after_anonymous_block.txt
new file mode 100644
index 0000000000..0d986b3c01
--- /dev/null
+++ b/test/prism/errors/method_with_arguments_after_anonymous_block.txt
@@ -0,0 +1,4 @@
+def foo(&, a)
+ ^ unexpected parameter order
+end
+
diff --git a/test/prism/errors/missing_terminator_in_parentheses.txt b/test/prism/errors/missing_terminator_in_parentheses.txt
new file mode 100644
index 0000000000..af4b698f0c
--- /dev/null
+++ b/test/prism/errors/missing_terminator_in_parentheses.txt
@@ -0,0 +1,3 @@
+(0 0)
+ ^ unexpected integer, expecting end-of-input
+
diff --git a/test/prism/errors/module_definition_in_method_body.txt b/test/prism/errors/module_definition_in_method_body.txt
new file mode 100644
index 0000000000..59900c96dd
--- /dev/null
+++ b/test/prism/errors/module_definition_in_method_body.txt
@@ -0,0 +1,3 @@
+def foo;module A;end;end
+ ^~~~~~ unexpected module definition in method body
+
diff --git a/test/prism/errors/module_definition_in_method_body_within_block.txt b/test/prism/errors/module_definition_in_method_body_within_block.txt
new file mode 100644
index 0000000000..204be35607
--- /dev/null
+++ b/test/prism/errors/module_definition_in_method_body_within_block.txt
@@ -0,0 +1,7 @@
+def foo
+ bar do
+ module Foo;end
+ ^~~~~~ unexpected module definition in method body
+ end
+end
+
diff --git a/test/prism/errors/module_definition_in_method_defs.txt b/test/prism/errors/module_definition_in_method_defs.txt
new file mode 100644
index 0000000000..c5a6a8a2e8
--- /dev/null
+++ b/test/prism/errors/module_definition_in_method_defs.txt
@@ -0,0 +1,7 @@
+def foo(bar = module A;end);end
+ ^~~~~~ unexpected module definition in method body
+def foo;rescue;module A;end;end
+ ^~~~~~ unexpected module definition in method body
+def foo;ensure;module A;end;end
+ ^~~~~~ unexpected module definition in method body
+
diff --git a/test/prism/errors/module_name_recoverable.txt b/test/prism/errors/module_name_recoverable.txt
new file mode 100644
index 0000000000..58a28a60c5
--- /dev/null
+++ b/test/prism/errors/module_name_recoverable.txt
@@ -0,0 +1,4 @@
+module Parent module end
+ ^~~~~~ unexpected constant path after `module`; class/module name must be CONSTANT
+ ^~~ unexpected 'end', assuming it is closing the parent module definition
+
diff --git a/test/prism/errors/multiple_error_in_parameters_order.txt b/test/prism/errors/multiple_error_in_parameters_order.txt
new file mode 100644
index 0000000000..5dae0a105a
--- /dev/null
+++ b/test/prism/errors/multiple_error_in_parameters_order.txt
@@ -0,0 +1,5 @@
+def foo(**args, a, b:)
+ ^ unexpected parameter order
+ ^~ unexpected parameter order
+end
+
diff --git a/test/prism/errors/next_1.txt b/test/prism/errors/next_1.txt
new file mode 100644
index 0000000000..b56b7f6ae6
--- /dev/null
+++ b/test/prism/errors/next_1.txt
@@ -0,0 +1,4 @@
+next 1,;
+ ^ expected an argument
+^~~~~~~ Invalid next
+
diff --git a/test/prism/errors/next_1_2_3.txt b/test/prism/errors/next_1_2_3.txt
new file mode 100644
index 0000000000..7abe577ab3
--- /dev/null
+++ b/test/prism/errors/next_1_2_3.txt
@@ -0,0 +1,8 @@
+next(1, 2, 3)
+ ^ unexpected ',', expecting end-of-input
+ ^ unexpected ',', ignoring it
+ ^ expected a matching `)`
+ ^ unexpected ')', expecting end-of-input
+ ^ unexpected ')', ignoring it
+^~~~~~~~~~~~ Invalid next
+
diff --git a/test/prism/errors/non_assoc_equality.txt b/test/prism/errors/non_assoc_equality.txt
new file mode 100644
index 0000000000..6ce8da88d6
--- /dev/null
+++ b/test/prism/errors/non_assoc_equality.txt
@@ -0,0 +1,19 @@
+1 == 2 == 3
+ ^~ unexpected '==', expecting end-of-input
+ ^~ unexpected '==', ignoring it
+1 != 2 != 3
+ ^~ unexpected '!=', expecting end-of-input
+ ^~ unexpected '!=', ignoring it
+1 === 2 === 3
+ ^~~ unexpected '===', expecting end-of-input
+ ^~~ unexpected '===', ignoring it
+1 =~ 2 =~ 3
+ ^~ unexpected '=~', expecting end-of-input
+ ^~ unexpected '=~', ignoring it
+1 !~ 2 !~ 3
+ ^~ unexpected '!~', expecting end-of-input
+ ^~ unexpected '!~', ignoring it
+1 <=> 2 <=> 3
+ ^~~ unexpected '<=>', expecting end-of-input
+ ^~~ unexpected '<=>', ignoring it
+
diff --git a/test/prism/errors/non_assoc_range.txt b/test/prism/errors/non_assoc_range.txt
new file mode 100644
index 0000000000..072cf6d3c6
--- /dev/null
+++ b/test/prism/errors/non_assoc_range.txt
@@ -0,0 +1,4 @@
+1....2
+ ^ unexpected '.', expecting end-of-input
+ ^ unexpected '.', ignoring it
+
diff --git a/test/prism/errors/numbered_parameters_in_block_arguments.txt b/test/prism/errors/numbered_parameters_in_block_arguments.txt
new file mode 100644
index 0000000000..d01999c53e
--- /dev/null
+++ b/test/prism/errors/numbered_parameters_in_block_arguments.txt
@@ -0,0 +1,3 @@
+foo { |_1| }
+ ^~ _1 is reserved for numbered parameters
+
diff --git a/test/prism/errors/optional_block_parameters_with_unary_operator.txt b/test/prism/errors/optional_block_parameters_with_unary_operator.txt
new file mode 100644
index 0000000000..fd45f12648
--- /dev/null
+++ b/test/prism/errors/optional_block_parameters_with_unary_operator.txt
@@ -0,0 +1,3 @@
+foo { |a = +b| }
+ ^ unexpected '+'; unary calls are not allowed in this context
+
diff --git a/test/prism/errors/optional_block_parameters_with_unary_operator_2.txt b/test/prism/errors/optional_block_parameters_with_unary_operator_2.txt
new file mode 100644
index 0000000000..ba98f36f84
--- /dev/null
+++ b/test/prism/errors/optional_block_parameters_with_unary_operator_2.txt
@@ -0,0 +1,3 @@
+foo { |a = -b| }
+ ^ unexpected '-'; unary calls are not allowed in this context
+
diff --git a/test/prism/errors/optional_block_parameters_with_unary_operator_3.txt b/test/prism/errors/optional_block_parameters_with_unary_operator_3.txt
new file mode 100644
index 0000000000..9770059891
--- /dev/null
+++ b/test/prism/errors/optional_block_parameters_with_unary_operator_3.txt
@@ -0,0 +1,3 @@
+foo { |a = !b| }
+ ^ unexpected '!'; unary calls are not allowed in this context
+
diff --git a/test/prism/errors/optional_block_parameters_with_unary_operator_4.txt b/test/prism/errors/optional_block_parameters_with_unary_operator_4.txt
new file mode 100644
index 0000000000..9bec68d7b3
--- /dev/null
+++ b/test/prism/errors/optional_block_parameters_with_unary_operator_4.txt
@@ -0,0 +1,3 @@
+foo { |a = ~b| }
+ ^ unexpected '~'; unary calls are not allowed in this context
+
diff --git a/test/prism/errors/parameter_name_ending_with_bang_or_question_mark.txt b/test/prism/errors/parameter_name_ending_with_bang_or_question_mark.txt
new file mode 100644
index 0000000000..db4fd6928a
--- /dev/null
+++ b/test/prism/errors/parameter_name_ending_with_bang_or_question_mark.txt
@@ -0,0 +1,4 @@
+def foo(x!,y?); end
+ ^~ unexpected name for a parameter
+ ^~ unexpected name for a parameter
+
diff --git a/test/prism/errors/pre_execution_context.txt b/test/prism/errors/pre_execution_context.txt
new file mode 100644
index 0000000000..18b11bae97
--- /dev/null
+++ b/test/prism/errors/pre_execution_context.txt
@@ -0,0 +1,4 @@
+BEGIN { 1 + }
+ ^ unexpected '}'; expected an expression after the operator
+ ^ unexpected '}', assuming it is closing the parent 'BEGIN' block
+
diff --git a/test/prism/errors/pre_execution_missing_brace.txt b/test/prism/errors/pre_execution_missing_brace.txt
new file mode 100644
index 0000000000..e51cd0732e
--- /dev/null
+++ b/test/prism/errors/pre_execution_missing_brace.txt
@@ -0,0 +1,3 @@
+BEGIN 1 }
+ ^ expected a `{` after `BEGIN`
+
diff --git a/test/prism/errors/range_and_bin_op.txt b/test/prism/errors/range_and_bin_op.txt
new file mode 100644
index 0000000000..4a7a396d0d
--- /dev/null
+++ b/test/prism/errors/range_and_bin_op.txt
@@ -0,0 +1,4 @@
+1..2..3
+ ^~ unexpected .., expecting end-of-input
+ ^~ unexpected .., ignoring it
+
diff --git a/test/prism/errors/range_and_bin_op_2.txt b/test/prism/errors/range_and_bin_op_2.txt
new file mode 100644
index 0000000000..f2a31dcf82
--- /dev/null
+++ b/test/prism/errors/range_and_bin_op_2.txt
@@ -0,0 +1,4 @@
+1..2..
+ ^~ unexpected .., expecting end-of-input
+ ^~ unexpected .., ignoring it
+
diff --git a/test/prism/errors/range_and_bin_op_3.txt b/test/prism/errors/range_and_bin_op_3.txt
new file mode 100644
index 0000000000..34390d0776
--- /dev/null
+++ b/test/prism/errors/range_and_bin_op_3.txt
@@ -0,0 +1,3 @@
+1.. || 2
+ ^ unexpected '|'; expected an expression after the operator
+
diff --git a/test/prism/errors/range_and_bin_op_4.txt b/test/prism/errors/range_and_bin_op_4.txt
new file mode 100644
index 0000000000..56226480cf
--- /dev/null
+++ b/test/prism/errors/range_and_bin_op_4.txt
@@ -0,0 +1,4 @@
+1.. & 2
+ ^ unexpected '&', expecting end-of-input
+ ^ unexpected '&', ignoring it
+
diff --git a/test/prism/errors/range_and_bin_op_5.txt b/test/prism/errors/range_and_bin_op_5.txt
new file mode 100644
index 0000000000..bc8b467914
--- /dev/null
+++ b/test/prism/errors/range_and_bin_op_5.txt
@@ -0,0 +1,5 @@
+1.. * 2
+ ^ unexpected *, expecting end-of-input
+ ^ unexpected write target
+ ^~~ unexpected write target
+
diff --git a/test/prism/errors/range_and_bin_op_6.txt b/test/prism/errors/range_and_bin_op_6.txt
new file mode 100644
index 0000000000..5cdd7a4f44
--- /dev/null
+++ b/test/prism/errors/range_and_bin_op_6.txt
@@ -0,0 +1,3 @@
+1.. / 2
+ ^ unterminated regexp meets end of file; expected a closing delimiter
+
diff --git a/test/prism/errors/range_and_bin_op_7.txt b/test/prism/errors/range_and_bin_op_7.txt
new file mode 100644
index 0000000000..3f91b5e97f
--- /dev/null
+++ b/test/prism/errors/range_and_bin_op_7.txt
@@ -0,0 +1,3 @@
+1.. % 2
+ ^ unterminated string meets end of file
+
diff --git a/test/prism/errors/range_and_bin_op_8.txt b/test/prism/errors/range_and_bin_op_8.txt
new file mode 100644
index 0000000000..afbf3719d5
--- /dev/null
+++ b/test/prism/errors/range_and_bin_op_8.txt
@@ -0,0 +1,4 @@
+1.. ** 2
+ ^~ unexpected **, expecting end-of-input
+ ^~ unexpected **, ignoring it
+
diff --git a/test/prism/errors/rational_number_with_exponential_portion.txt b/test/prism/errors/rational_number_with_exponential_portion.txt
new file mode 100644
index 0000000000..01a03d538f
--- /dev/null
+++ b/test/prism/errors/rational_number_with_exponential_portion.txt
@@ -0,0 +1,4 @@
+1e1r; 1e1ri
+ ^ unexpected local variable or method, expecting end-of-input
+ ^~ unexpected local variable or method, expecting end-of-input
+
diff --git a/test/prism/errors/regular_expression_with_unknown_regexp_options.txt b/test/prism/errors/regular_expression_with_unknown_regexp_options.txt
new file mode 100644
index 0000000000..c37291ca40
--- /dev/null
+++ b/test/prism/errors/regular_expression_with_unknown_regexp_options.txt
@@ -0,0 +1,3 @@
+/foo/AZaz
+ ^~~~~ unknown regexp options - AZaz
+
diff --git a/test/prism/errors/repeated_parameter_name_in_destructured_params.txt b/test/prism/errors/repeated_parameter_name_in_destructured_params.txt
new file mode 100644
index 0000000000..766c235325
--- /dev/null
+++ b/test/prism/errors/repeated_parameter_name_in_destructured_params.txt
@@ -0,0 +1,3 @@
+def f(a, (b, (a))); end
+ ^ duplicated argument name
+
diff --git a/test/prism/errors/rest_keywords_parameters_before_required_parameters.txt b/test/prism/errors/rest_keywords_parameters_before_required_parameters.txt
new file mode 100644
index 0000000000..406f326712
--- /dev/null
+++ b/test/prism/errors/rest_keywords_parameters_before_required_parameters.txt
@@ -0,0 +1,4 @@
+def foo(**rest, b:)
+ ^~ unexpected parameter order
+end
+
diff --git a/test/prism/errors/return_1.txt b/test/prism/errors/return_1.txt
new file mode 100644
index 0000000000..b4ce8f1f2a
--- /dev/null
+++ b/test/prism/errors/return_1.txt
@@ -0,0 +1,3 @@
+return 1,;
+ ^ expected an argument
+
diff --git a/test/prism/errors/return_1_2_3.txt b/test/prism/errors/return_1_2_3.txt
new file mode 100644
index 0000000000..8f6dbaf194
--- /dev/null
+++ b/test/prism/errors/return_1_2_3.txt
@@ -0,0 +1,7 @@
+return(1, 2, 3)
+ ^ unexpected ',', expecting end-of-input
+ ^ unexpected ',', ignoring it
+ ^ expected a matching `)`
+ ^ unexpected ')', expecting end-of-input
+ ^ unexpected ')', ignoring it
+
diff --git a/test/prism/errors/returning_to_optional_parameters_multiple_times.txt b/test/prism/errors/returning_to_optional_parameters_multiple_times.txt
new file mode 100644
index 0000000000..83ca731850
--- /dev/null
+++ b/test/prism/errors/returning_to_optional_parameters_multiple_times.txt
@@ -0,0 +1,4 @@
+def foo(a, b = 1, c, d = 2, e)
+ ^ unexpected parameter order
+end
+
diff --git a/test/prism/errors/semicolon_after_inheritance_operator.txt b/test/prism/errors/semicolon_after_inheritance_operator.txt
new file mode 100644
index 0000000000..6b67e6048a
--- /dev/null
+++ b/test/prism/errors/semicolon_after_inheritance_operator.txt
@@ -0,0 +1,3 @@
+class Foo < Bar end
+ ^ unexpected `end`, expecting ';' or '\n'
+
diff --git a/test/prism/errors/setter_method_cannot_be_defined_in_an_endless_method_definition.txt b/test/prism/errors/setter_method_cannot_be_defined_in_an_endless_method_definition.txt
new file mode 100644
index 0000000000..c4440ccc7e
--- /dev/null
+++ b/test/prism/errors/setter_method_cannot_be_defined_in_an_endless_method_definition.txt
@@ -0,0 +1,3 @@
+def a=() = 42
+ ^~ invalid method name; a setter method cannot be defined in an endless method definition
+
diff --git a/test/prism/errors/shadow_args_in_block.txt b/test/prism/errors/shadow_args_in_block.txt
new file mode 100644
index 0000000000..1e7d5f9cd4
--- /dev/null
+++ b/test/prism/errors/shadow_args_in_block.txt
@@ -0,0 +1,3 @@
+tap{|a;a|}
+ ^ duplicated argument name
+
diff --git a/test/prism/errors/shadow_args_in_lambda.txt b/test/prism/errors/shadow_args_in_lambda.txt
new file mode 100644
index 0000000000..2399a0ebd5
--- /dev/null
+++ b/test/prism/errors/shadow_args_in_lambda.txt
@@ -0,0 +1,5 @@
+->a;b{}
+ ^ expected a `do` keyword or a `{` to open the lambda block
+ ^ unexpected end-of-input, assuming it is closing the parent top level context
+ ^ expected a lambda block beginning with `do` to end with `end`
+
diff --git a/test/prism/errors/singleton_method_for_literals.txt b/test/prism/errors/singleton_method_for_literals.txt
new file mode 100644
index 0000000000..6247b4f025
--- /dev/null
+++ b/test/prism/errors/singleton_method_for_literals.txt
@@ -0,0 +1,39 @@
+def (1).g; end
+ ^ cannot define singleton method for literals
+def ((a; 1)).foo; end
+ ^ cannot define singleton method for literals
+def ((return; 1)).bar; end
+ ^ cannot define singleton method for literals
+def (((1))).foo; end
+ ^ cannot define singleton method for literals
+def (__FILE__).foo; end
+ ^~~~~~~~ cannot define singleton method for literals
+def (__ENCODING__).foo; end
+ ^~~~~~~~~~~~ cannot define singleton method for literals
+def (__LINE__).foo; end
+ ^~~~~~~~ cannot define singleton method for literals
+def ("foo").foo; end
+ ^~~~~ cannot define singleton method for literals
+def (3.14).foo; end
+ ^~~~ cannot define singleton method for literals
+def (3.14i).foo; end
+ ^~~~~ cannot define singleton method for literals
+def (:foo).foo; end
+ ^~~~ cannot define singleton method for literals
+def (:'foo').foo; end
+ ^~~~~~ cannot define singleton method for literals
+def (:'f{o}').foo; end
+ ^~~~~~~ cannot define singleton method for literals
+def ('foo').foo; end
+ ^~~~~ cannot define singleton method for literals
+def ("foo").foo; end
+ ^~~~~ cannot define singleton method for literals
+def ("#{fo}o").foo; end
+ ^~~~~~~~ cannot define singleton method for literals
+def (/foo/).foo; end
+ ^~~~~ cannot define singleton method for literals
+def (/f#{oo}/).foo; end
+ ^~~~~~~~ cannot define singleton method for literals
+def ([1]).foo; end
+ ^~~ cannot define singleton method for literals
+
diff --git a/test/prism/errors/splat_argument_after_keyword_argument.txt b/test/prism/errors/splat_argument_after_keyword_argument.txt
new file mode 100644
index 0000000000..fd2dbd0003
--- /dev/null
+++ b/test/prism/errors/splat_argument_after_keyword_argument.txt
@@ -0,0 +1,3 @@
+a(foo: bar, *args)
+ ^~~~~ unexpected `*` splat argument after a `**` keyword splat argument
+
diff --git a/test/prism/errors/statement_at_non_statement.txt b/test/prism/errors/statement_at_non_statement.txt
new file mode 100644
index 0000000000..40fe7d862e
--- /dev/null
+++ b/test/prism/errors/statement_at_non_statement.txt
@@ -0,0 +1,9 @@
+foo(alias x y)
+ ^~~~~ unexpected an `alias` at a non-statement position
+foo(BEGIN { bar })
+ ^~~~~ unexpected a `BEGIN` at a non-statement position
+foo(END { bar })
+ ^~~ unexpected an `END` at a non-statement position
+foo(undef x)
+ ^~~~~ unexpected an `undef` at a non-statement position
+
diff --git a/test/prism/errors/statement_operators.txt b/test/prism/errors/statement_operators.txt
new file mode 100644
index 0000000000..04b7c57477
--- /dev/null
+++ b/test/prism/errors/statement_operators.txt
@@ -0,0 +1,25 @@
+alias x y + 1
+ ^ unexpected '+', expecting end-of-input
+ ^ unexpected '+', ignoring it
+alias x y.z
+ ^ unexpected '.', expecting end-of-input
+ ^ unexpected '.', ignoring it
+BEGIN { bar } + 1
+ ^ unexpected '+', expecting end-of-input
+ ^ unexpected '+', ignoring it
+BEGIN { bar }.z
+ ^ unexpected '.', expecting end-of-input
+ ^ unexpected '.', ignoring it
+END { bar } + 1
+ ^ unexpected '+', expecting end-of-input
+ ^ unexpected '+', ignoring it
+END { bar }.z
+ ^ unexpected '.', expecting end-of-input
+ ^ unexpected '.', ignoring it
+undef x + 1
+ ^ unexpected '+', expecting end-of-input
+ ^ unexpected '+', ignoring it
+undef x.z
+ ^ unexpected '.', expecting end-of-input
+ ^ unexpected '.', ignoring it
+
diff --git a/test/prism/errors/switching_to_named_arguments_twice.txt b/test/prism/errors/switching_to_named_arguments_twice.txt
new file mode 100644
index 0000000000..5dae0a105a
--- /dev/null
+++ b/test/prism/errors/switching_to_named_arguments_twice.txt
@@ -0,0 +1,5 @@
+def foo(**args, a, b:)
+ ^ unexpected parameter order
+ ^~ unexpected parameter order
+end
+
diff --git a/test/prism/errors/switching_to_optional_arguments_twice.txt b/test/prism/errors/switching_to_optional_arguments_twice.txt
new file mode 100644
index 0000000000..5dae0a105a
--- /dev/null
+++ b/test/prism/errors/switching_to_optional_arguments_twice.txt
@@ -0,0 +1,5 @@
+def foo(**args, a, b:)
+ ^ unexpected parameter order
+ ^~ unexpected parameter order
+end
+
diff --git a/test/prism/errors/symbol_in_hash.txt b/test/prism/errors/symbol_in_hash.txt
new file mode 100644
index 0000000000..148040aa61
--- /dev/null
+++ b/test/prism/errors/symbol_in_hash.txt
@@ -0,0 +1,3 @@
+{x:'y':}
+ ^~ unexpected label terminator, expected a string literal terminator
+
diff --git a/test/prism/errors/symbol_in_keyword_parameter.txt b/test/prism/errors/symbol_in_keyword_parameter.txt
new file mode 100644
index 0000000000..22d03cccb2
--- /dev/null
+++ b/test/prism/errors/symbol_in_keyword_parameter.txt
@@ -0,0 +1,3 @@
+def foo(x:'y':); end
+ ^~ unexpected label terminator, expected a string literal terminator
+
diff --git a/test/prism/errors/targeting_numbered_parameter.txt b/test/prism/errors/targeting_numbered_parameter.txt
new file mode 100644
index 0000000000..39c40ad7b9
--- /dev/null
+++ b/test/prism/errors/targeting_numbered_parameter.txt
@@ -0,0 +1,3 @@
+-> { _1, = 0 }
+ ^~ _1 is reserved for numbered parameters
+
diff --git a/test/prism/errors/top_level_constant_starting_with_downcased_identifier.txt b/test/prism/errors/top_level_constant_starting_with_downcased_identifier.txt
new file mode 100644
index 0000000000..b7b54dd74e
--- /dev/null
+++ b/test/prism/errors/top_level_constant_starting_with_downcased_identifier.txt
@@ -0,0 +1,4 @@
+::foo::A
+ ^ expected a constant after the `::` operator
+ ^~~ unexpected local variable or method, expecting end-of-input
+
diff --git a/test/prism/errors/top_level_constant_with_downcased_identifier.txt b/test/prism/errors/top_level_constant_with_downcased_identifier.txt
new file mode 100644
index 0000000000..032bcfaebb
--- /dev/null
+++ b/test/prism/errors/top_level_constant_with_downcased_identifier.txt
@@ -0,0 +1,4 @@
+::foo
+ ^ expected a constant after the `::` operator
+ ^~~ unexpected local variable or method, expecting end-of-input
+
diff --git a/test/prism/errors/trailing_comma_in_calls.txt b/test/prism/errors/trailing_comma_in_calls.txt
new file mode 100644
index 0000000000..44e8a1f0b3
--- /dev/null
+++ b/test/prism/errors/trailing_comma_in_calls.txt
@@ -0,0 +1,3 @@
+foo 1,
+ ^ expected an argument
+
diff --git a/test/prism/errors/unexpected_block.txt b/test/prism/errors/unexpected_block.txt
new file mode 100644
index 0000000000..2c0741cd30
--- /dev/null
+++ b/test/prism/errors/unexpected_block.txt
@@ -0,0 +1,3 @@
+def foo = yield(&:+)
+ ^~~ block argument should not be given
+
diff --git a/test/prism/errors/unterminated_W_list.txt b/test/prism/errors/unterminated_W_list.txt
new file mode 100644
index 0000000000..89cab68abb
--- /dev/null
+++ b/test/prism/errors/unterminated_W_list.txt
@@ -0,0 +1,3 @@
+%w[
+^~~ unterminated list; expected a closing delimiter for the `%w`
+
diff --git a/test/prism/errors/unterminated_argument_expression.txt b/test/prism/errors/unterminated_argument_expression.txt
new file mode 100644
index 0000000000..c250a94bec
--- /dev/null
+++ b/test/prism/errors/unterminated_argument_expression.txt
@@ -0,0 +1,5 @@
+a %
+ ^ unterminated quoted string meets end of file
+ ^ unexpected end-of-input; expected an expression after the operator
+ ^ unexpected end-of-input, assuming it is closing the parent top level context
+
diff --git a/test/prism/errors/unterminated_embdoc.txt b/test/prism/errors/unterminated_embdoc.txt
new file mode 100644
index 0000000000..1dd9ea3ac4
--- /dev/null
+++ b/test/prism/errors/unterminated_embdoc.txt
@@ -0,0 +1,3 @@
+=begin
+^~~~~~ embedded document meets end of file
+
diff --git a/test/prism/errors/unterminated_embdoc_2.txt b/test/prism/errors/unterminated_embdoc_2.txt
new file mode 100644
index 0000000000..1dd9ea3ac4
--- /dev/null
+++ b/test/prism/errors/unterminated_embdoc_2.txt
@@ -0,0 +1,3 @@
+=begin
+^~~~~~ embedded document meets end of file
+
diff --git a/test/prism/errors/unterminated_empty_string.txt b/test/prism/errors/unterminated_empty_string.txt
new file mode 100644
index 0000000000..597102f7ee
--- /dev/null
+++ b/test/prism/errors/unterminated_empty_string.txt
@@ -0,0 +1,3 @@
+"
+ ^ unterminated string meets end of file
+
diff --git a/test/prism/errors/unterminated_global_variable.txt b/test/prism/errors/unterminated_global_variable.txt
new file mode 100644
index 0000000000..ce3e960b2e
--- /dev/null
+++ b/test/prism/errors/unterminated_global_variable.txt
@@ -0,0 +1,3 @@
+$
+^ '$' without identifiers is not allowed as a global variable name
+
diff --git a/test/prism/errors/unterminated_global_variable_2.txt b/test/prism/errors/unterminated_global_variable_2.txt
new file mode 100644
index 0000000000..302293b538
--- /dev/null
+++ b/test/prism/errors/unterminated_global_variable_2.txt
@@ -0,0 +1,3 @@
+$
+^ '$' without identifiers is not allowed as a global variable name
+
diff --git a/test/prism/errors/unterminated_i_list.txt b/test/prism/errors/unterminated_i_list.txt
new file mode 100644
index 0000000000..c48be9971d
--- /dev/null
+++ b/test/prism/errors/unterminated_i_list.txt
@@ -0,0 +1,3 @@
+%i[
+^~~ unterminated list; expected a closing delimiter for the `%i`
+
diff --git a/test/prism/errors/unterminated_interpolated_string.txt b/test/prism/errors/unterminated_interpolated_string.txt
new file mode 100644
index 0000000000..e74a4c9e20
--- /dev/null
+++ b/test/prism/errors/unterminated_interpolated_string.txt
@@ -0,0 +1,3 @@
+"hello
+ ^ unterminated string meets end of file
+
diff --git a/test/prism/errors/unterminated_interpolated_symbol.txt b/test/prism/errors/unterminated_interpolated_symbol.txt
new file mode 100644
index 0000000000..faa7597280
--- /dev/null
+++ b/test/prism/errors/unterminated_interpolated_symbol.txt
@@ -0,0 +1,3 @@
+:"#
+ ^ unterminated symbol; expected a closing delimiter for the interpolated symbol
+
diff --git a/test/prism/errors/unterminated_parenthesized_expression.txt b/test/prism/errors/unterminated_parenthesized_expression.txt
new file mode 100644
index 0000000000..9025eec453
--- /dev/null
+++ b/test/prism/errors/unterminated_parenthesized_expression.txt
@@ -0,0 +1,4 @@
+(1 + 2
+ ^ unexpected end-of-input, assuming it is closing the parent top level context
+ ^ expected a matching `)`
+
diff --git a/test/prism/errors/unterminated_regular_expression.txt b/test/prism/errors/unterminated_regular_expression.txt
new file mode 100644
index 0000000000..48f3a30810
--- /dev/null
+++ b/test/prism/errors/unterminated_regular_expression.txt
@@ -0,0 +1,3 @@
+/hello
+^ unterminated regexp meets end of file; expected a closing delimiter
+
diff --git a/test/prism/errors/unterminated_regular_expression_with_heredoc.txt b/test/prism/errors/unterminated_regular_expression_with_heredoc.txt
new file mode 100644
index 0000000000..d4688d6c9e
--- /dev/null
+++ b/test/prism/errors/unterminated_regular_expression_with_heredoc.txt
@@ -0,0 +1,4 @@
+<<-END + /b
+ ^ unterminated regexp meets end of file; expected a closing delimiter
+END
+
diff --git a/test/prism/errors/unterminated_s_symbol.txt b/test/prism/errors/unterminated_s_symbol.txt
new file mode 100644
index 0000000000..0f4be932b3
--- /dev/null
+++ b/test/prism/errors/unterminated_s_symbol.txt
@@ -0,0 +1,3 @@
+%s[abc
+^~~ unterminated quoted string; expected a closing delimiter for the dynamic symbol
+
diff --git a/test/prism/errors/unterminated_string.txt b/test/prism/errors/unterminated_string.txt
new file mode 100644
index 0000000000..89c0a08b3e
--- /dev/null
+++ b/test/prism/errors/unterminated_string.txt
@@ -0,0 +1,3 @@
+'hello
+^ unterminated string meets end of file
+
diff --git a/test/prism/errors/unterminated_unicode_brackets_should_be_a_syntax_error.txt b/test/prism/errors/unterminated_unicode_brackets_should_be_a_syntax_error.txt
new file mode 100644
index 0000000000..1a65c6149a
--- /dev/null
+++ b/test/prism/errors/unterminated_unicode_brackets_should_be_a_syntax_error.txt
@@ -0,0 +1,3 @@
+?\u{3
+ ^~~~ unterminated Unicode escape
+
diff --git a/test/prism/errors/unterminated_xstring.txt b/test/prism/errors/unterminated_xstring.txt
new file mode 100644
index 0000000000..ccd529774c
--- /dev/null
+++ b/test/prism/errors/unterminated_xstring.txt
@@ -0,0 +1,3 @@
+`hello
+^ expected a closing delimiter for the `%x` or backtick string
+
diff --git a/test/prism/errors/void_value_expression_in_arguments.txt b/test/prism/errors/void_value_expression_in_arguments.txt
new file mode 100644
index 0000000000..f57aee1454
--- /dev/null
+++ b/test/prism/errors/void_value_expression_in_arguments.txt
@@ -0,0 +1,17 @@
+foo(return)
+ ^~~~~~ unexpected void value expression
+foo(1, return)
+ ^~~~~~ unexpected void value expression
+foo(*return)
+ ^~~~~~ unexpected void value expression
+foo(**return)
+ ^~~~~~ unexpected void value expression
+foo(&return)
+ ^~~~~~ unexpected void value expression
+foo(return => 1)
+ ^~~~~~ unexpected void value expression
+foo(:a => return)
+ ^~~~~~ unexpected void value expression
+foo(a: return)
+ ^~~~~~ unexpected void value expression
+
diff --git a/test/prism/errors/void_value_expression_in_array.txt b/test/prism/errors/void_value_expression_in_array.txt
new file mode 100644
index 0000000000..a0e86fb135
--- /dev/null
+++ b/test/prism/errors/void_value_expression_in_array.txt
@@ -0,0 +1,15 @@
+[return]
+ ^~~~~~ unexpected void value expression
+[1, return]
+ ^~~~~~ unexpected void value expression
+[ return => 1 ]
+ ^~~~~~ unexpected void value expression
+[ 1 => return ]
+ ^~~~~~ unexpected void value expression
+[ a: return ]
+ ^~~~~~ unexpected void value expression
+[ *return ]
+ ^~~~~~ unexpected void value expression
+[ **return ]
+ ^~~~~~ unexpected void value expression
+
diff --git a/test/prism/errors/void_value_expression_in_assignment.txt b/test/prism/errors/void_value_expression_in_assignment.txt
new file mode 100644
index 0000000000..c651d7f39e
--- /dev/null
+++ b/test/prism/errors/void_value_expression_in_assignment.txt
@@ -0,0 +1,9 @@
+a = return
+ ^~~~~~ unexpected void value expression
+a = 1, return
+ ^~~~~~ unexpected void value expression
+a, b = return, 1
+ ^~~~~~ unexpected void value expression
+a, b = 1, *return
+ ^~~~~~ unexpected void value expression
+
diff --git a/test/prism/errors/void_value_expression_in_begin_statement.txt b/test/prism/errors/void_value_expression_in_begin_statement.txt
new file mode 100644
index 0000000000..4ad40b684d
--- /dev/null
+++ b/test/prism/errors/void_value_expression_in_begin_statement.txt
@@ -0,0 +1,21 @@
+x = return 1
+ ^~~~~~~~ unexpected void value expression
+x = return, 1
+ ^~~~~~ unexpected void value expression
+x = 1, return
+ ^~~~~~ unexpected void value expression
+x, y = return
+ ^~~~~~ unexpected void value expression
+x = begin return ensure end
+ ^~~~~~ unexpected void value expression
+x = begin ensure return end
+ ^~~~~~ unexpected void value expression
+x = begin return ensure return end
+ ^~~~~~ unexpected void value expression
+x = begin return; rescue; return end
+ ^~~~~~ unexpected void value expression
+x = begin return; rescue; return; else return end
+ ^~~~~~ unexpected void value expression
+x = begin; return; rescue; retry; end
+ ^~~~~~ unexpected void value expression
+
diff --git a/test/prism/errors/void_value_expression_in_binary_call.txt b/test/prism/errors/void_value_expression_in_binary_call.txt
new file mode 100644
index 0000000000..096b42be4d
--- /dev/null
+++ b/test/prism/errors/void_value_expression_in_binary_call.txt
@@ -0,0 +1,11 @@
+1 + (return)
+ ^~~~~~ unexpected void value expression
+(return) + 1
+ ^~~~~~ unexpected void value expression
+1 and (return)
+(return) and 1
+ ^~~~~~ unexpected void value expression
+1 or (return)
+(return) or 1
+ ^~~~~~ unexpected void value expression
+
diff --git a/test/prism/errors/void_value_expression_in_call.txt b/test/prism/errors/void_value_expression_in_call.txt
new file mode 100644
index 0000000000..90e6481c4c
--- /dev/null
+++ b/test/prism/errors/void_value_expression_in_call.txt
@@ -0,0 +1,11 @@
+(return).foo
+ ^~~~~~ unexpected void value expression
+(return).(1)
+ ^~~~~~ unexpected void value expression
+(return)[1]
+ ^~~~~~ unexpected void value expression
+(return)[1] = 2
+ ^~~~~~ unexpected void value expression
+(return)::foo
+ ^~~~~~ unexpected void value expression
+
diff --git a/test/prism/errors/void_value_expression_in_constant_path.txt b/test/prism/errors/void_value_expression_in_constant_path.txt
new file mode 100644
index 0000000000..1dab6902a2
--- /dev/null
+++ b/test/prism/errors/void_value_expression_in_constant_path.txt
@@ -0,0 +1,5 @@
+(return)::A
+ ^~~~~~ unexpected void value expression
+class (return)::A; end
+ ^~~~~~ unexpected void value expression
+
diff --git a/test/prism/errors/void_value_expression_in_def.txt b/test/prism/errors/void_value_expression_in_def.txt
new file mode 100644
index 0000000000..fed52a6677
--- /dev/null
+++ b/test/prism/errors/void_value_expression_in_def.txt
@@ -0,0 +1,10 @@
+def (return).x
+ ^~~~~~ unexpected void value expression
+end
+def x(a = return)
+ ^~~~~~ unexpected void value expression
+end
+def x(a: return)
+ ^~~~~~ unexpected void value expression
+end
+
diff --git a/test/prism/errors/void_value_expression_in_expression.txt b/test/prism/errors/void_value_expression_in_expression.txt
new file mode 100644
index 0000000000..f6165a7ba6
--- /dev/null
+++ b/test/prism/errors/void_value_expression_in_expression.txt
@@ -0,0 +1,19 @@
+(return) ? 1 : 1
+ ^~~~~~ unexpected void value expression
+(return)..1
+ ^~~~~~ unexpected void value expression
+1..(return)
+ ^~~~~~ unexpected void value expression
+(return)...1
+ ^~~~~~ unexpected void value expression
+1...(return)
+ ^~~~~~ unexpected void value expression
+(..(return))
+ ^~~~~~ unexpected void value expression
+(...(return))
+ ^~~~~~ unexpected void value expression
+((return)..)
+ ^~~~~~ unexpected void value expression
+((return)...)
+ ^~~~~~ unexpected void value expression
+
diff --git a/test/prism/errors/void_value_expression_in_hash.txt b/test/prism/errors/void_value_expression_in_hash.txt
new file mode 100644
index 0000000000..7795511443
--- /dev/null
+++ b/test/prism/errors/void_value_expression_in_hash.txt
@@ -0,0 +1,9 @@
+{ return => 1 }
+ ^~~~~~ unexpected void value expression
+{ 1 => return }
+ ^~~~~~ unexpected void value expression
+{ a: return }
+ ^~~~~~ unexpected void value expression
+{ **return }
+ ^~~~~~ unexpected void value expression
+
diff --git a/test/prism/errors/void_value_expression_in_modifier.txt b/test/prism/errors/void_value_expression_in_modifier.txt
new file mode 100644
index 0000000000..7d7b444e33
--- /dev/null
+++ b/test/prism/errors/void_value_expression_in_modifier.txt
@@ -0,0 +1,13 @@
+1 if (return)
+ ^~~~~~ unexpected void value expression
+1 unless (return)
+ ^~~~~~ unexpected void value expression
+1 while (return)
+ ^~~~~~ unexpected void value expression
+1 until (return)
+ ^~~~~~ unexpected void value expression
+(return) => a
+ ^~~~~~ unexpected void value expression
+(return) in a
+ ^~~~~~ unexpected void value expression
+
diff --git a/test/prism/errors/void_value_expression_in_statement.txt b/test/prism/errors/void_value_expression_in_statement.txt
new file mode 100644
index 0000000000..87dbfa5cc9
--- /dev/null
+++ b/test/prism/errors/void_value_expression_in_statement.txt
@@ -0,0 +1,26 @@
+if (return)
+ ^~~~~~ unexpected void value expression
+end
+unless (return)
+ ^~~~~~ unexpected void value expression
+end
+while (return)
+ ^~~~~~ unexpected void value expression
+end
+until (return)
+ ^~~~~~ unexpected void value expression
+end
+case (return)
+ ^~~~~~ unexpected void value expression
+when 1
+end
+class A < (return)
+ ^~~~~~ unexpected void value expression
+end
+class << (return)
+ ^~~~~~ unexpected void value expression
+end
+for x in (return)
+ ^~~~~~ unexpected void value expression
+end
+
diff --git a/test/prism/errors/void_value_expression_in_unary_call.txt b/test/prism/errors/void_value_expression_in_unary_call.txt
new file mode 100644
index 0000000000..61e849255c
--- /dev/null
+++ b/test/prism/errors/void_value_expression_in_unary_call.txt
@@ -0,0 +1,5 @@
++(return)
+ ^~~~~~ unexpected void value expression
+not return
+ ^~~~~~ unexpected void value expression
+
diff --git a/test/prism/errors/while_endless_method.txt b/test/prism/errors/while_endless_method.txt
new file mode 100644
index 0000000000..6f062d89d0
--- /dev/null
+++ b/test/prism/errors/while_endless_method.txt
@@ -0,0 +1,5 @@
+while def f = g do end
+ ^ expected a predicate expression for the `while` statement
+ ^ unexpected end-of-input, assuming it is closing the parent top level context
+ ^ expected an `end` to close the `while` statement
+
diff --git a/test/prism/errors/writing_numbered_parameter.txt b/test/prism/errors/writing_numbered_parameter.txt
new file mode 100644
index 0000000000..17dcc6e8f0
--- /dev/null
+++ b/test/prism/errors/writing_numbered_parameter.txt
@@ -0,0 +1,3 @@
+-> { _1 = 0 }
+ ^~ _1 is reserved for numbered parameters
+
diff --git a/test/prism/errors_test.rb b/test/prism/errors_test.rb
index 4e900e37f4..4c5f93cc9f 100644
--- a/test/prism/errors_test.rb
+++ b/test/prism/errors_test.rb
@@ -4,1985 +4,63 @@ require_relative "test_helper"
module Prism
class ErrorsTest < TestCase
- include DSL
+ base = File.expand_path("errors", __dir__)
+ filepaths = Dir["*.txt", base: base]
- def test_constant_path_with_invalid_token_after
- assert_error_messages "A::$b", [
- "expected a constant after the `::` operator",
- "unexpected global variable, expecting end-of-input"
+ if RUBY_VERSION < "3.0"
+ filepaths -= [
+ "cannot_assign_to_a_reserved_numbered_parameter.txt",
+ "writing_numbered_parameter.txt",
+ "targeting_numbered_parameter.txt",
+ "defining_numbered_parameter.txt",
+ "defining_numbered_parameter_2.txt",
+ "numbered_parameters_in_block_arguments.txt"
]
end
- def test_module_name_recoverable
- expected = ModuleNode(
- [],
- Location(),
- ConstantReadNode(:Parent),
- StatementsNode(
- [ModuleNode([], Location(), MissingNode(), nil, Location(), :"")]
- ),
- Location(),
- :Parent
- )
-
- assert_errors expected, "module Parent module end", [
- ["unexpected constant path after `module`; class/module name must be CONSTANT", 14..20],
- ["unexpected 'end', assuming it is closing the parent module definition", 21..24]
- ]
- end
-
- def test_for_loops_index_missing
- expected = ForNode(
- MissingNode(),
- expression("1..10"),
- StatementsNode([expression("i")]),
- Location(),
- Location(),
- nil,
- Location()
- )
-
- assert_errors expected, "for in 1..10\ni\nend", [
- ["expected an index after `for`", 0..3]
- ]
- end
-
- def test_for_loops_only_end
- expected = ForNode(
- MissingNode(),
- MissingNode(),
- nil,
- Location(),
- Location(),
- nil,
- Location()
- )
-
- assert_errors expected, "for end", [
- ["expected an index after `for`", 0..3],
- ["expected an `in` after the index in a `for` statement", 3..3],
- ["expected a collection after the `in` in a `for` statement", 3..3]
- ]
- end
-
- def test_pre_execution_missing_brace
- expected = PreExecutionNode(
- StatementsNode([expression("1")]),
- Location(),
- Location(),
- Location()
- )
-
- assert_errors expected, "BEGIN 1 }", [
- ["expected a `{` after `BEGIN`", 5..5]
- ]
- end
-
- def test_pre_execution_context
- expected = PreExecutionNode(
- StatementsNode([
- CallNode(
- 0,
- expression("1"),
- nil,
- :+,
- Location(),
- nil,
- ArgumentsNode(0, [MissingNode()]),
- nil,
- nil
- )
- ]),
- Location(),
- Location(),
- Location()
- )
-
- assert_errors expected, "BEGIN { 1 + }", [
- ["unexpected '}'; expected an expression after the operator", 12..13],
- ["unexpected '}', assuming it is closing the parent 'BEGIN' block", 12..13]
- ]
- end
-
- def test_unterminated_embdoc
- message = "embedded document meets end of file"
- assert_error_messages "=begin", [message]
- assert_error_messages "=begin\n", [message]
-
- refute_error_messages "=begin\n=end"
- refute_error_messages "=begin\n=end\0"
- refute_error_messages "=begin\n=end\C-d"
- refute_error_messages "=begin\n=end\C-z"
- end
-
- def test_unterminated_i_list
- assert_errors expression("%i["), "%i[", [
- ["unterminated list; expected a closing delimiter for the `%i`", 0..3]
- ]
- end
-
- def test_unterminated_w_list
- assert_errors expression("%w["), "%w[", [
- ["unterminated list; expected a closing delimiter for the `%w`", 0..3]
- ]
- end
-
- def test_unterminated_W_list
- assert_errors expression("%W["), "%W[", [
- ["unterminated list; expected a closing delimiter for the `%W`", 0..3]
- ]
- end
-
- def test_unterminated_regular_expression
- assert_errors expression("/hello"), "/hello", [
- ["unterminated regexp meets end of file; expected a closing delimiter", 0..1]
- ]
- end
-
- def test_unterminated_regular_expression_with_heredoc
- source = "<<-END + /b\nEND\n"
-
- assert_errors expression(source), source, [
- ["unterminated regexp meets end of file; expected a closing delimiter", 9..10]
- ]
- end
-
- def test_unterminated_xstring
- assert_errors expression("`hello"), "`hello", [
- ["expected a closing delimiter for the `%x` or backtick string", 0..1]
- ]
- end
-
- def test_unterminated_interpolated_string
- expr = expression('"hello')
- assert_errors expr, '"hello', [
- ["unterminated string meets end of file", 6..6]
- ]
- assert_equal expr.unescaped, "hello"
- assert_equal expr.closing, ""
- end
-
- def test_unterminated_string
- expr = expression("'hello")
- assert_errors expr, "'hello", [
- ["unterminated string meets end of file", 0..1]
- ]
- assert_equal expr.unescaped, "hello"
- assert_equal expr.closing, ""
- end
-
- def test_unterminated_empty_string
- expr = expression('"')
- assert_errors expr, '"', [
- ["unterminated string meets end of file", 1..1]
- ]
- assert_equal expr.unescaped, ""
- assert_equal expr.closing, ""
- end
-
- def test_incomplete_instance_var_string
- assert_errors expression('%@#@@#'), '%@#@@#', [
- ["'@' without identifiers is not allowed as an instance variable name", 4..5],
- ["unexpected instance variable, expecting end-of-input", 4..5]
- ]
- end
-
- def test_unterminated_s_symbol
- assert_errors expression("%s[abc"), "%s[abc", [
- ["unterminated quoted string; expected a closing delimiter for the dynamic symbol", 0..3]
- ]
- end
-
- def test_unterminated_parenthesized_expression
- assert_errors expression('(1 + 2'), '(1 + 2', [
- ["unexpected end-of-input, assuming it is closing the parent top level context", 6..6],
- ["expected a matching `)`", 6..6]
- ]
- end
-
- def test_missing_terminator_in_parentheses
- assert_error_messages "(0 0)", [
- "unexpected integer, expecting end-of-input"
- ]
- end
-
- def test_unterminated_argument_expression
- assert_errors expression('a %'), 'a %', [
- ["unterminated quoted string meets end of file", 2..3],
- ["unexpected end-of-input; expected an expression after the operator", 3..3],
- ["unexpected end-of-input, assuming it is closing the parent top level context", 3..3]
- ]
- end
-
- def test_unterminated_interpolated_symbol
- assert_error_messages ":\"#", [
- "unterminated symbol; expected a closing delimiter for the interpolated symbol"
- ]
- end
-
- def test_cr_without_lf_in_percent_expression
- assert_errors expression("%\r"), "%\r", [
- ["unterminated string meets end of file", 2..2],
- ]
- end
-
- def test_1_2_3
- assert_errors expression("(1, 2, 3)"), "(1, 2, 3)", [
- ["unexpected ',', expecting end-of-input", 2..3],
- ["unexpected ',', ignoring it", 2..3],
- ["expected a matching `)`", 2..2],
- ["unexpected ',', expecting end-of-input", 2..3],
- ["unexpected ',', ignoring it", 2..3],
- ["unexpected ',', expecting end-of-input", 5..6],
- ["unexpected ',', ignoring it", 5..6],
- ["unexpected ')', expecting end-of-input", 8..9],
- ["unexpected ')', ignoring it", 8..9]
- ]
- end
-
- def test_return_1_2_3
- assert_error_messages "return(1, 2, 3)", [
- "unexpected ',', expecting end-of-input",
- "unexpected ',', ignoring it",
- "expected a matching `)`",
- "unexpected ')', expecting end-of-input",
- "unexpected ')', ignoring it"
- ]
- end
-
- def test_return_1
- assert_errors expression("return 1,;"), "return 1,;", [
- ["expected an argument", 8..9]
- ]
- end
-
- def test_next_1_2_3
- assert_errors expression("next(1, 2, 3)"), "next(1, 2, 3)", [
- ["unexpected ',', expecting end-of-input", 6..7],
- ["unexpected ',', ignoring it", 6..7],
- ["expected a matching `)`", 6..6],
- ["unexpected ')', expecting end-of-input", 12..13],
- ["unexpected ')', ignoring it", 12..13],
- ["Invalid next", 0..12]
- ]
- end
-
- def test_next_1
- assert_errors expression("next 1,;"), "next 1,;", [
- ["expected an argument", 6..7],
- ["Invalid next", 0..7]
- ]
- end
-
- def test_break_1_2_3
- assert_errors expression("break(1, 2, 3)"), "break(1, 2, 3)", [
- ["unexpected ',', expecting end-of-input", 7..8],
- ["unexpected ',', ignoring it", 7..8],
- ["expected a matching `)`", 7..7],
- ["unexpected ')', expecting end-of-input", 13..14],
- ["unexpected ')', ignoring it", 13..14],
- ["Invalid break", 0..13]
- ]
- end
-
- def test_break_1
- assert_errors expression("break 1,;"), "break 1,;", [
- ["expected an argument", 7..8],
- ["Invalid break", 0..8]
- ]
- end
-
- def test_argument_forwarding_when_parent_is_not_forwarding
- assert_errors expression('def a(x, y, z); b(...); end'), 'def a(x, y, z); b(...); end', [
- ["unexpected ... when the parent method is not forwarding", 18..21]
- ]
- end
-
- def test_argument_forwarding_only_effects_its_own_internals
- assert_errors expression('def a(...); b(...); end; def c(x, y, z); b(...); end'),
- 'def a(...); b(...); end; def c(x, y, z); b(...); end', [
- ["unexpected ... when the parent method is not forwarding", 43..46]
- ]
- end
-
- def test_top_level_constant_with_downcased_identifier
- assert_error_messages "::foo", [
- "expected a constant after the `::` operator",
- "unexpected local variable or method, expecting end-of-input"
- ]
- end
-
- def test_top_level_constant_starting_with_downcased_identifier
- assert_error_messages "::foo::A", [
- "expected a constant after the `::` operator",
- "unexpected local variable or method, expecting end-of-input"
- ]
- end
-
- def test_aliasing_global_variable_with_non_global_variable
- assert_errors expression("alias $a b"), "alias $a b", [
- ["invalid argument being passed to `alias`; expected a bare word, symbol, constant, or global variable", 9..10]
- ]
- end
-
- def test_aliasing_non_global_variable_with_global_variable
- assert_errors expression("alias a $b"), "alias a $b", [
- ["invalid argument being passed to `alias`; expected a bare word, symbol, constant, or global variable", 8..10]
- ]
- end
-
- def test_aliasing_global_variable_with_global_number_variable
- assert_errors expression("alias $a $1"), "alias $a $1", [
- ["invalid argument being passed to `alias`; can't make alias for the number variables", 9..11]
- ]
- end
-
- def test_def_with_expression_receiver_and_no_identifier
- assert_errors expression("def (a); end"), "def (a); end", [
- ["expected a `.` or `::` after the receiver in a method definition", 7..7],
- ["unexpected ';'; expected a method name", 7..8]
- ]
- end
-
- def test_def_with_multiple_statements_receiver
- assert_errors expression("def (\na\nb\n).c; end"), "def (\na\nb\n).c; end", [
- ["expected a matching `)`", 8..8],
- ["expected a `.` or `::` after the receiver in a method definition", 8..8],
- ["expected a delimiter to close the parameters", 9..9],
- ["unexpected ')', ignoring it", 10..11],
- ["unexpected '.', ignoring it", 11..12]
- ]
- end
-
- def test_def_with_empty_expression_receiver
- assert_errors expression("def ().a; end"), "def ().a; end", [
- ["expected a receiver for the method definition", 4..5]
- ]
- end
-
- def test_block_beginning_with_brace_and_ending_with_end
- assert_error_messages "x.each { x end", [
- "unexpected 'end', expecting end-of-input",
- "unexpected 'end', ignoring it",
- "unexpected end-of-input, assuming it is closing the parent top level context",
- "expected a block beginning with `{` to end with `}`"
- ]
- end
-
- def test_double_splat_followed_by_splat_argument
- expected = CallNode(
- CallNodeFlags::IGNORE_VISIBILITY,
- nil,
- nil,
- :a,
- Location(),
- Location(),
- ArgumentsNode(
- ArgumentsNodeFlags::CONTAINS_KEYWORDS | ArgumentsNodeFlags::CONTAINS_KEYWORD_SPLAT,
- [
- KeywordHashNode(0, [AssocSplatNode(expression("kwargs"), Location())]),
- SplatNode(Location(), expression("args"))
- ]
- ),
- Location(),
- nil
- )
-
- assert_errors expected, "a(**kwargs, *args)", [
- ["unexpected `*` splat argument after a `**` keyword splat argument", 12..17]
- ]
- end
-
- def test_arguments_after_block
- expected = CallNode(
- CallNodeFlags::IGNORE_VISIBILITY,
- nil,
- nil,
- :a,
- Location(),
- Location(),
- ArgumentsNode(0, [expression("foo")]),
- Location(),
- BlockArgumentNode(expression("block"), Location())
- )
-
- assert_errors expected, "a(&block, foo)", [
- ["unexpected argument after a block argument", 10..13]
- ]
- end
-
- def test_arguments_binding_power_for_and
- assert_error_messages "foo(*bar and baz)", [
- "unexpected 'and'; expected a `)` to close the arguments",
- "unexpected ')', expecting end-of-input",
- "unexpected ')', ignoring it"
- ]
- end
-
- def test_splat_argument_after_keyword_argument
- expected = CallNode(
- CallNodeFlags::IGNORE_VISIBILITY,
- nil,
- nil,
- :a,
- Location(),
- Location(),
- ArgumentsNode(ArgumentsNodeFlags::CONTAINS_KEYWORDS, [
- KeywordHashNode(1, [
- AssocNode(
- SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, nil, Location(), Location(), "foo"),
- expression("bar"),
- nil
- )
- ]),
- SplatNode(Location(), expression("args"))
- ]),
- Location(),
- nil
- )
-
- assert_errors expected, "a(foo: bar, *args)", [
- ["unexpected `*` splat argument after a `**` keyword splat argument", 12..17]
- ]
- end
-
- def test_module_definition_in_method_body
- expected = DefNode(
- :foo,
- Location(),
- nil,
- nil,
- StatementsNode([ModuleNode([], Location(), ConstantReadNode(:A), nil, Location(), :A)]),
- [],
- Location(),
- nil,
- nil,
- nil,
- nil,
- Location()
- )
-
- assert_errors expected, "def foo;module A;end;end", [
- ["unexpected module definition in method body", 8..14]
- ]
- end
-
- def test_module_definition_in_method_body_within_block
- expected = DefNode(
- :foo,
- Location(),
- nil,
- nil,
- StatementsNode(
- [CallNode(
- CallNodeFlags::IGNORE_VISIBILITY,
- nil,
- nil,
- :bar,
- Location(),
- nil,
- nil,
- nil,
- BlockNode(
- [],
- nil,
- StatementsNode([ModuleNode([], Location(), ConstantReadNode(:Foo), nil, Location(), :Foo)]),
- Location(),
- Location()
- )
- )]
- ),
- [],
- Location(),
- nil,
- nil,
- nil,
- nil,
- Location()
- )
-
- assert_errors expected, <<~RUBY, [["unexpected module definition in method body", 21..27]]
- def foo
- bar do
- module Foo;end
- end
- end
- RUBY
- end
-
- def test_module_definition_in_method_defs
- source = <<~RUBY
- def foo(bar = module A;end);end
- def foo;rescue;module A;end;end
- def foo;ensure;module A;end;end
- RUBY
- message = "unexpected module definition in method body"
- assert_errors expression(source), source, [
- [message, 14..20],
- [message, 47..53],
- [message, 79..85],
- ]
- end
-
- def test_class_definition_in_method_body
- expected = DefNode(
- :foo,
- Location(),
- nil,
- nil,
- StatementsNode(
- [ClassNode(
- [],
- Location(),
- ConstantReadNode(:A),
- nil,
- nil,
- nil,
- Location(),
- :A
- )]
- ),
- [],
- Location(),
- nil,
- nil,
- nil,
- nil,
- Location()
- )
-
- assert_errors expected, "def foo;class A;end;end", [
- ["unexpected class definition in method body", 8..13]
- ]
+ filepaths.each do |filepath|
+ define_method(:"test_#{File.basename(filepath, ".txt")}") do
+ assert_errors(File.join(base, filepath))
+ end
end
- def test_class_definition_in_method_defs
+ def test_embdoc_ending
source = <<~RUBY
- def foo(bar = class A;end);end
- def foo;rescue;class A;end;end
- def foo;ensure;class A;end;end
+ =begin\n=end
+ =begin\n=end\0
+ =begin\n=end\C-d
+ =begin\n=end\C-z
RUBY
- message = "unexpected class definition in method body"
- assert_errors expression(source), source, [
- [message, 14..19],
- [message, 46..51],
- [message, 77..82],
- ]
- end
-
- def test_bad_arguments
- expected = DefNode(
- :foo,
- Location(),
- nil,
- ParametersNode([
- RequiredParameterNode(0, :A),
- RequiredParameterNode(0, :@a),
- RequiredParameterNode(0, :$A),
- RequiredParameterNode(0, :@@a),
- ], [], nil, [], [], nil, nil),
- nil,
- [:A, :@a, :$A, :@@a],
- Location(),
- nil,
- Location(),
- Location(),
- nil,
- Location()
- )
-
- assert_errors expected, "def foo(A, @a, $A, @@a);end", [
- ["invalid formal argument; formal argument cannot be a constant", 8..9],
- ["invalid formal argument; formal argument cannot be an instance variable", 11..13],
- ["invalid formal argument; formal argument cannot be a global variable", 15..17],
- ["invalid formal argument; formal argument cannot be a class variable", 19..22],
- ]
- end
- if RUBY_VERSION >= "3.0"
- def test_cannot_assign_to_a_reserved_numbered_parameter
- expected = BeginNode(
- Location(),
- StatementsNode([
- LocalVariableWriteNode(:_1, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()),
- LocalVariableWriteNode(:_2, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()),
- LocalVariableWriteNode(:_3, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()),
- LocalVariableWriteNode(:_4, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()),
- LocalVariableWriteNode(:_5, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()),
- LocalVariableWriteNode(:_6, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()),
- LocalVariableWriteNode(:_7, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()),
- LocalVariableWriteNode(:_8, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()),
- LocalVariableWriteNode(:_9, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location()),
- LocalVariableWriteNode(:_10, 0, Location(), SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"), Location())
- ]),
- nil,
- nil,
- nil,
- Location()
- )
- source = <<~RUBY
- begin
- _1=:a;_2=:a;_3=:a;_4=:a;_5=:a
- _6=:a;_7=:a;_8=:a;_9=:a;_10=:a
- end
- RUBY
- assert_errors expected, source, [
- ["_1 is reserved for numbered parameters", 8..10],
- ["_2 is reserved for numbered parameters", 14..16],
- ["_3 is reserved for numbered parameters", 20..22],
- ["_4 is reserved for numbered parameters", 26..28],
- ["_5 is reserved for numbered parameters", 32..34],
- ["_6 is reserved for numbered parameters", 40..42],
- ["_7 is reserved for numbered parameters", 46..48],
- ["_8 is reserved for numbered parameters", 52..54],
- ["_9 is reserved for numbered parameters", 58..60],
- ]
+ source.each_line do |line|
+ assert_valid_syntax(source)
+ assert_predicate Prism.parse(source), :success?
end
end
- def test_do_not_allow_trailing_commas_in_method_parameters
- expected = DefNode(
- :foo,
- Location(),
- nil,
- ParametersNode(
- [RequiredParameterNode(0, :a), RequiredParameterNode(0, :b), RequiredParameterNode(0, :c)],
- [],
- nil,
- [],
- [],
- nil,
- nil
- ),
- nil,
- [:a, :b, :c],
- Location(),
- nil,
- Location(),
- Location(),
- nil,
- Location()
- )
-
- assert_errors expected, "def foo(a,b,c,);end", [
- ["unexpected `,` in parameters", 13..14]
- ]
- end
-
- def test_do_not_allow_trailing_commas_in_lambda_parameters
- expected = LambdaNode(
- [:a, :b],
- Location(),
- Location(),
- Location(),
- BlockParametersNode(
- ParametersNode([RequiredParameterNode(0, :a), RequiredParameterNode(0, :b)], [], nil, [], [], nil, nil),
- [],
- Location(),
- Location()
- ),
- nil
- )
- assert_errors expected, "-> (a, b, ) {}", [
- ["unexpected `,` in parameters", 8..9]
- ]
- end
-
- def test_do_not_allow_multiple_codepoints_in_a_single_character_literal
- expected = StringNode(StringFlags::FORCED_UTF8_ENCODING, Location(), Location(), nil, "\u0001\u0002")
-
- assert_errors expected, '?\u{0001 0002}', [
- ["invalid Unicode escape sequence; Multiple codepoints at single character literal are disallowed", 9..12]
- ]
+ def test_unterminated_string_closing
+ statement = Prism.parse_statement("'hello")
+ assert_equal statement.unescaped, "hello"
+ assert_empty statement.closing
end
- def test_invalid_hex_escape
- assert_errors expression('"\\xx"'), '"\\xx"', [
- ["invalid hex escape sequence", 1..3],
- ]
+ def test_unterminated_interpolated_string_closing
+ statement = Prism.parse_statement('"hello')
+ assert_equal statement.unescaped, "hello"
+ assert_empty statement.closing
end
- def test_do_not_allow_more_than_6_hexadecimal_digits_in_u_Unicode_character_notation
- expected = StringNode(0, Location(), Location(), Location(), "\u0001")
-
- assert_errors expected, '"\u{0000001}"', [
- ["invalid Unicode escape sequence; maximum length is 6 digits", 4..11],
- ]
- end
-
- def test_do_not_allow_characters_other_than_0_9_a_f_and_A_F_in_u_Unicode_character_notation
- expected = StringNode(0, Location(), Location(), Location(), "\u0000z}")
-
- assert_errors expected, '"\u{000z}"', [
- ["invalid Unicode escape sequence", 7..7],
- ["unterminated Unicode escape", 7..7]
- ]
- end
-
- def test_unterminated_unicode_brackets_should_be_a_syntax_error
- assert_errors expression('?\\u{3'), '?\\u{3', [
- ["unterminated Unicode escape", 1..5],
- ]
- end
-
- def test_method_parameters_after_block
- expected = DefNode(
- :foo,
- Location(),
- nil,
- ParametersNode(
- [],
- [],
- nil,
- [RequiredParameterNode(0, :a)],
- [],
- nil,
- BlockParameterNode(0, :block, Location(), Location())
- ),
- nil,
- [:block, :a],
- Location(),
- nil,
- Location(),
- Location(),
- nil,
- Location()
- )
- assert_errors expected, "def foo(&block, a)\nend", [
- ["unexpected parameter order", 16..17]
- ]
- end
-
- def test_method_with_arguments_after_anonymous_block
- expected = DefNode(
- :foo,
- Location(),
- nil,
- ParametersNode([], [], nil, [RequiredParameterNode(0, :a)], [], nil, BlockParameterNode(0, nil, nil, Location())),
- nil,
- [:a],
- Location(),
- nil,
- Location(),
- Location(),
- nil,
- Location()
- )
-
- assert_errors expected, "def foo(&, a)\nend", [
- ["unexpected parameter order", 11..12]
- ]
- end
-
- def test_method_parameters_after_arguments_forwarding
- expected = DefNode(
- :foo,
- Location(),
- nil,
- ParametersNode(
- [],
- [],
- nil,
- [RequiredParameterNode(0, :a)],
- [],
- ForwardingParameterNode(),
- nil
- ),
- nil,
- [:a],
- Location(),
- nil,
- Location(),
- Location(),
- nil,
- Location()
- )
- assert_errors expected, "def foo(..., a)\nend", [
- ["unexpected parameter order", 13..14]
- ]
- end
-
- def test_keywords_parameters_before_required_parameters
- expected = DefNode(
- :foo,
- Location(),
- nil,
- ParametersNode(
- [],
- [],
- nil,
- [RequiredParameterNode(0, :a)],
- [RequiredKeywordParameterNode(0, :b, Location())],
- nil,
- nil
- ),
- nil,
- [:b, :a],
- Location(),
- nil,
- Location(),
- Location(),
- nil,
- Location()
- )
- assert_errors expected, "def foo(b:, a)\nend", [
- ["unexpected parameter order", 12..13]
- ]
- end
-
- def test_rest_keywords_parameters_before_required_parameters
- expected = DefNode(
- :foo,
- Location(),
- nil,
- ParametersNode(
- [],
- [],
- nil,
- [],
- [RequiredKeywordParameterNode(0, :b, Location())],
- KeywordRestParameterNode(0, :rest, Location(), Location()),
- nil
- ),
- nil,
- [:rest, :b],
- Location(),
- nil,
- Location(),
- Location(),
- nil,
- Location()
- )
-
- assert_errors expected, "def foo(**rest, b:)\nend", [
- ["unexpected parameter order", 16..18]
- ]
- end
-
- def test_double_arguments_forwarding
- expected = DefNode(
- :foo,
- Location(),
- nil,
- ParametersNode([], [], nil, [ForwardingParameterNode()], [], ForwardingParameterNode(), nil),
- nil,
- [],
- Location(),
- nil,
- Location(),
- Location(),
- nil,
- Location()
- )
-
- assert_errors expected, "def foo(..., ...)\nend", [
- ["unexpected parameter order", 13..16]
- ]
- end
-
- def test_multiple_error_in_parameters_order
- expected = DefNode(
- :foo,
- Location(),
- nil,
- ParametersNode(
- [],
- [],
- nil,
- [RequiredParameterNode(0, :a)],
- [RequiredKeywordParameterNode(0, :b, Location())],
- KeywordRestParameterNode(0, :args, Location(), Location()),
- nil
- ),
- nil,
- [:args, :a, :b],
- Location(),
- nil,
- Location(),
- Location(),
- nil,
- Location()
- )
-
- assert_errors expected, "def foo(**args, a, b:)\nend", [
- ["unexpected parameter order", 16..17],
- ["unexpected parameter order", 19..21]
- ]
- end
-
- def test_switching_to_optional_arguments_twice
- expected = DefNode(
- :foo,
- Location(),
- nil,
- ParametersNode(
- [],
- [],
- nil,
- [RequiredParameterNode(0, :a)],
- [RequiredKeywordParameterNode(0, :b, Location())],
- KeywordRestParameterNode(0, :args, Location(), Location()),
- nil
- ),
- nil,
- [:args, :a, :b],
- Location(),
- nil,
- Location(),
- Location(),
- nil,
- Location(),
- )
-
- assert_errors expected, "def foo(**args, a, b:)\nend", [
- ["unexpected parameter order", 16..17],
- ["unexpected parameter order", 19..21]
- ]
- end
-
- def test_switching_to_named_arguments_twice
- expected = DefNode(
- :foo,
- Location(),
- nil,
- ParametersNode(
- [],
- [],
- nil,
- [RequiredParameterNode(0, :a)],
- [RequiredKeywordParameterNode(0, :b, Location())],
- KeywordRestParameterNode(0, :args, Location(), Location()),
- nil
- ),
- nil,
- [:args, :a, :b],
- Location(),
- nil,
- Location(),
- Location(),
- nil,
- Location(),
- )
-
- assert_errors expected, "def foo(**args, a, b:)\nend", [
- ["unexpected parameter order", 16..17],
- ["unexpected parameter order", 19..21]
- ]
- end
-
- def test_returning_to_optional_parameters_multiple_times
- expected = DefNode(
- :foo,
- Location(),
- nil,
- ParametersNode(
- [RequiredParameterNode(0, :a)],
- [
- OptionalParameterNode(0, :b, Location(), Location(), IntegerNode(IntegerBaseFlags::DECIMAL, 1)),
- OptionalParameterNode(0, :d, Location(), Location(), IntegerNode(IntegerBaseFlags::DECIMAL, 2))
- ],
- nil,
- [RequiredParameterNode(0, :c), RequiredParameterNode(0, :e)],
- [],
- nil,
- nil
- ),
- nil,
- [:a, :b, :c, :d, :e],
- Location(),
- nil,
- Location(),
- Location(),
- nil,
- Location(),
- )
-
- assert_errors expected, "def foo(a, b = 1, c, d = 2, e)\nend", [
- ["unexpected parameter order", 23..24]
- ]
- end
-
- def test_case_without_when_clauses_errors_on_else_clause
- expected = CaseMatchNode(
- SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"),
- [],
- ElseNode(Location(), nil, Location()),
- Location(),
- Location()
- )
-
- assert_errors expected, "case :a\nelse\nend", [
- ["expected a `when` or `in` clause after `case`", 0..4]
- ]
- end
-
- def test_case_without_clauses
- expected = CaseNode(
- SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, Location(), Location(), nil, "a"),
- [],
- nil,
- Location(),
- Location()
- )
-
- assert_errors expected, "case :a\nend", [
- ["expected a `when` or `in` clause after `case`", 0..4]
- ]
- end
-
- def test_setter_method_cannot_be_defined_in_an_endless_method_definition
- expected = DefNode(
- :a=,
- Location(),
- nil,
- nil,
- StatementsNode([IntegerNode(IntegerBaseFlags::DECIMAL, 42)]),
- [],
- Location(),
- nil,
- Location(),
- Location(),
- Location(),
- nil
- )
-
- assert_errors expected, "def a=() = 42", [
- ["invalid method name; a setter method cannot be defined in an endless method definition", 4..6]
- ]
- end
-
- def test_do_not_allow_forward_arguments_in_lambda_literals
- expected = LambdaNode(
- [],
- Location(),
- Location(),
- Location(),
- BlockParametersNode(ParametersNode([], [], nil, [], [], ForwardingParameterNode(), nil), [], Location(), Location()),
- nil
- )
-
- assert_errors expected, "->(...) {}", [
- ["unexpected ... when the parent method is not forwarding", 3..6]
- ]
- end
-
- def test_do_not_allow_forward_arguments_in_blocks
- expected = CallNode(
- CallNodeFlags::IGNORE_VISIBILITY,
- nil,
- nil,
- :a,
- Location(),
- nil,
- nil,
- nil,
- BlockNode(
- [],
- BlockParametersNode(ParametersNode([], [], nil, [], [], ForwardingParameterNode(), nil), [], Location(), Location()),
- nil,
- Location(),
- Location()
- )
- )
-
- assert_errors expected, "a {|...|}", [
- ["unexpected ... when the parent method is not forwarding", 4..7]
- ]
- end
-
- def test_dont_allow_return_inside_class_body
- expected = ClassNode(
- [],
- Location(),
- ConstantReadNode(:A),
- nil,
- nil,
- StatementsNode([ReturnNode(0, Location(), nil)]),
- Location(),
- :A
- )
-
- assert_errors expected, "class A; return; end", [
- ["Invalid return in class/module body", 9..15]
- ]
- end
-
- def test_dont_allow_return_inside_module_body
- expected = ModuleNode(
- [],
- Location(),
- ConstantReadNode(:A),
- StatementsNode([ReturnNode(0, Location(), nil)]),
- Location(),
- :A
- )
-
- assert_errors expected, "module A; return; end", [
- ["Invalid return in class/module body", 10..16]
- ]
- end
-
- def test_dont_allow_setting_to_back_and_nth_reference
- expected = BeginNode(
- Location(),
- StatementsNode([
- GlobalVariableWriteNode(:$+, Location(), NilNode(), Location()),
- GlobalVariableWriteNode(:$1466, Location(), NilNode(), Location())
- ]),
- nil,
- nil,
- nil,
- Location()
- )
-
- assert_errors expected, "begin\n$+ = nil\n$1466 = nil\nend", [
- ["Can't set variable $+", 6..8],
- ["Can't set variable $1466", 15..20]
- ]
- end
-
- def test_duplicated_parameter_names
- expected = DefNode(
- :foo,
- Location(),
- nil,
- ParametersNode([RequiredParameterNode(0, :a), RequiredParameterNode(0, :b), RequiredParameterNode(ParameterFlags::REPEATED_PARAMETER, :a)], [], nil, [], [], nil, nil),
- nil,
- [:a, :b],
- Location(),
- nil,
- Location(),
- Location(),
- nil,
- Location()
- )
-
- assert_errors expected, "def foo(a,b,a);end", [
- ["duplicated argument name", 12..13]
- ]
-
- expected = DefNode(
- :foo,
- Location(),
- nil,
- ParametersNode([RequiredParameterNode(0, :a), RequiredParameterNode(0, :b)], [], RestParameterNode(ParameterFlags::REPEATED_PARAMETER, :a, Location(), Location()), [], [], nil, nil),
- nil,
- [:a, :b],
- Location(),
- nil,
- Location(),
- Location(),
- nil,
- Location()
- )
-
- assert_errors expected, "def foo(a,b,*a);end", [
- ["duplicated argument name", 13..14]
- ]
-
- expected = DefNode(
- :foo,
- Location(),
- nil,
- ParametersNode([RequiredParameterNode(0, :a), RequiredParameterNode(0, :b)], [], nil, [], [], KeywordRestParameterNode(ParameterFlags::REPEATED_PARAMETER, :a, Location(), Location()), nil),
- nil,
- [:a, :b],
- Location(),
- nil,
- Location(),
- Location(),
- nil,
- Location()
- )
-
- assert_errors expected, "def foo(a,b,**a);end", [
- ["duplicated argument name", 14..15]
- ]
-
- expected = DefNode(
- :foo,
- Location(),
- nil,
- ParametersNode([RequiredParameterNode(0, :a), RequiredParameterNode(0, :b)], [], nil, [], [], nil, BlockParameterNode(ParameterFlags::REPEATED_PARAMETER, :a, Location(), Location())),
- nil,
- [:a, :b],
- Location(),
- nil,
- Location(),
- Location(),
- nil,
- Location()
- )
-
- assert_errors expected, "def foo(a,b,&a);end", [
- ["duplicated argument name", 13..14]
- ]
-
- expected = DefNode(
- :foo,
- Location(),
- nil,
- ParametersNode([], [OptionalParameterNode(0, :a, Location(), Location(), IntegerNode(IntegerBaseFlags::DECIMAL, 1))], RestParameterNode(0, :c, Location(), Location()), [RequiredParameterNode(0, :b)], [], nil, nil),
- nil,
- [:a, :b, :c],
- Location(),
- nil,
- Location(),
- Location(),
- nil,
- Location()
- )
-
- assert_errors expected, "def foo(a = 1,b,*c);end", [["unexpected parameter `*`", 16..17]]
- end
-
- def test_content_after_unterminated_heredoc
- receiver = StringNode(0, Location(), Location(), Location(), "")
- expected = CallNode(0, receiver, Location(), :foo, Location(), nil, nil, nil, nil)
-
- assert_errors expected, "<<~FOO.foo\n", [
- ["unterminated heredoc; can't find string \"FOO\" anywhere before EOF", 3..6]
- ]
+ def test_unterminated_empty_string_closing
+ statement = Prism.parse_statement('"')
+ assert_empty statement.unescaped
+ assert_empty statement.closing
end
def test_invalid_message_name
assert_equal :"", Prism.parse_statement("+.@foo,+=foo").write_name
end
- def test_invalid_operator_write_fcall
- source = "foo! += 1"
- assert_errors expression(source), source, [
- ["unexpected write target", 0..4]
- ]
- end
-
- def test_invalid_operator_write_dot
- source = "foo.+= 1"
- assert_errors expression(source), source, [
- ["unexpected write target", 5..6]
- ]
- end
-
- def test_unterminated_global_variable
- message = "'$' without identifiers is not allowed as a global variable name"
-
- assert_errors expression("$"), "$", [[message, 0..1]]
- assert_errors expression("$ "), "$ ", [[message, 0..1]]
- end
-
- def test_invalid_global_variable_write
- assert_errors expression("$',"), "$',", [
- ["Can't set variable $'", 0..2],
- ["unexpected write target", 0..2]
- ]
- end
-
- def test_invalid_multi_target
- error_messages = ["unexpected write target"]
-
- assert_error_messages "foo,", error_messages
- assert_error_messages "foo = 1; foo,", error_messages
- assert_error_messages "foo.bar,", error_messages
- assert_error_messages "*foo,", error_messages
- assert_error_messages "@foo,", error_messages
- assert_error_messages "@@foo,", error_messages
- assert_error_messages "$foo,", error_messages
- assert_error_messages "$1,", ["Can't set variable $1", *error_messages]
- assert_error_messages "$+,", ["Can't set variable $+", *error_messages]
- assert_error_messages "Foo,", error_messages
- assert_error_messages "::Foo,", error_messages
- assert_error_messages "Foo::Foo,", error_messages
- assert_error_messages "Foo::foo,", error_messages
- assert_error_messages "foo[foo],", error_messages
- assert_error_messages "(foo, bar)", error_messages
- assert_error_messages "foo((foo, bar))", error_messages
- assert_error_messages "foo((*))", error_messages
- assert_error_messages "foo(((foo, bar), *))", error_messages
- assert_error_messages "(foo, bar) + 1", error_messages
- assert_error_messages "(foo, bar) in baz", error_messages
- end
-
- def test_call_with_block_and_write
- source = "foo {} &&= 1"
- assert_errors expression(source), source, [
- ["unexpected write target", 0..6],
- ["unexpected operator after a call with a block", 7..10]
- ]
- end
-
- def test_call_with_block_or_write
- source = "foo {} ||= 1"
- assert_errors expression(source), source, [
- ["unexpected write target", 0..6],
- ["unexpected operator after a call with a block", 7..10]
- ]
- end
-
- def test_call_with_block_operator_write
- source = "foo {} += 1"
- assert_errors expression(source), source, [
- ["unexpected write target", 0..6],
- ["unexpected operator after a call with a block", 7..9]
- ]
- end
-
- def test_index_call_with_block_and_write
- source = "foo[1] {} &&= 1"
- assert_errors expression(source), source, [
- ["unexpected write target", 0..9],
- ["unexpected operator after a call with arguments", 10..13],
- ["unexpected operator after a call with a block", 10..13]
- ]
- end
-
- def test_index_call_with_block_or_write
- source = "foo[1] {} ||= 1"
- assert_errors expression(source), source, [
- ["unexpected write target", 0..9],
- ["unexpected operator after a call with arguments", 10..13],
- ["unexpected operator after a call with a block", 10..13]
- ]
- end
-
- def test_index_call_with_block_operator_write
- source = "foo[1] {} += 1"
- assert_errors expression(source), source, [
- ["unexpected write target", 0..9],
- ["unexpected operator after a call with arguments", 10..12],
- ["unexpected operator after a call with a block", 10..12]
- ]
- end
-
- if RUBY_VERSION >= "3.0"
- def test_writing_numbered_parameter
- assert_error_messages "-> { _1 = 0 }", [
- "_1 is reserved for numbered parameters"
- ]
- end
-
- def test_targeting_numbered_parameter
- assert_error_messages "-> { _1, = 0 }", [
- "_1 is reserved for numbered parameters"
- ]
- end
-
- def test_defining_numbered_parameter
- error_messages = ["_1 is reserved for numbered parameters"]
-
- assert_error_messages "def _1; end", error_messages
- assert_error_messages "def self._1; end", error_messages
- end
- end
-
- def test_double_scope_numbered_parameters
- source = "-> { _1 + -> { _2 } }"
- errors = [["numbered parameter is already used in outer block", 15..17]]
-
- assert_errors expression(source), source, errors
- end
-
- def test_invalid_number_underscores
- error_messages = ["invalid underscore placement in number"]
- assert_error_messages "1__1", error_messages
- assert_error_messages "0b1__1", error_messages
- assert_error_messages "0o1__1", error_messages
- assert_error_messages "01__1", error_messages
- assert_error_messages "0d1__1", error_messages
- assert_error_messages "0x1__1", error_messages
-
- error_messages = ["trailing '_' in number"]
- assert_error_messages "1_1_", error_messages
- assert_error_messages "0b1_1_", error_messages
- assert_error_messages "0o1_1_", error_messages
- assert_error_messages "01_1_", error_messages
- assert_error_messages "0d1_1_", error_messages
- assert_error_messages "0x1_1_", error_messages
- end
-
- def test_alnum_delimiters
- error_messages = ["unknown type of %string"]
-
- assert_error_messages "%qXfooX", error_messages
- assert_error_messages "%QXfooX", error_messages
- assert_error_messages "%wXfooX", error_messages
- assert_error_messages "%WxfooX", error_messages
- assert_error_messages "%iXfooX", error_messages
- assert_error_messages "%IXfooX", error_messages
- assert_error_messages "%xXfooX", error_messages
- assert_error_messages "%rXfooX", error_messages
- assert_error_messages "%sXfooX", error_messages
- end
-
- def test_begin_at_toplevel
- source = "def foo; BEGIN {}; end"
- assert_errors expression(source), source, [
- ["BEGIN is permitted only at toplevel", 9..14],
- ]
- end
-
- if RUBY_VERSION >= "3.0"
- def test_numbered_parameters_in_block_arguments
- source = "foo { |_1| }"
- assert_errors expression(source), source, [
- ["_1 is reserved for numbered parameters", 7..9],
- ]
- end
- end
-
- def test_conditional_predicate_closed
- source = "if 0 0; elsif 0 0; end\nunless 0 0; end"
- assert_errors expression(source), source, [
- ["expected `then` or `;` or '\\n" + "'", 5..6],
- ["expected `then` or `;` or '\\n" + "'", 16..17],
- ["expected `then` or `;` or '\\n" + "'", 32..33],
- ]
- end
-
- def test_parameter_name_ending_with_bang_or_question_mark
- source = "def foo(x!,y?); end"
- errors = [
- ["unexpected name for a parameter", 8..10],
- ["unexpected name for a parameter", 11..13]
- ]
- assert_errors expression(source), source, errors
- end
-
- def test_class_name
- source = "class 0.X end"
- assert_errors expression(source), source, [
- ["unexpected constant path after `class`; class/module name must be CONSTANT", 6..9],
- ]
- end
-
- def test_loop_conditional_is_closed
- source = "while 0 0; foo; end; until 0 0; foo; end"
- assert_errors expression(source), source, [
- ["expected a predicate expression for the `while` statement", 7..7],
- ["expected a predicate expression for the `until` statement", 28..28],
- ]
- end
-
- def test_forwarding_arg_after_keyword_rest
- source = "def f(**,...);end"
- assert_errors expression(source), source, [
- ["unexpected parameter order", 9..12]
- ]
- end
-
- def test_semicolon_after_inheritance_operator
- source = "class Foo < Bar end"
- assert_errors expression(source), source, [
- ["unexpected `end`, expecting ';' or '\\n'", 15..15],
- ]
- end
-
- def test_shadow_args_in_lambda
- source = "->a;b{}"
-
- assert_errors expression(source), source, [
- ["expected a `do` keyword or a `{` to open the lambda block", 3..3],
- ["unexpected end-of-input, assuming it is closing the parent top level context", 7..7],
- ["expected a lambda block beginning with `do` to end with `end`", 7..7]
- ]
- end
-
- def test_shadow_args_in_block
- source = "tap{|a;a|}"
- assert_errors expression(source), source, [
- ["duplicated argument name", 7..8],
- ]
- end
-
- def test_repeated_parameter_name_in_destructured_params
- source = "def f(a, (b, (a))); end"
-
- assert_errors expression(source), source, [
- ["duplicated argument name", 14..15],
- ]
- end
-
- def test_assign_to_numbered_parameter
- source = <<~RUBY
- a in _1
- a => _1
- 1 => a, _1
- 1 in a, _1
- /(?<_1>)/ =~ a
- RUBY
-
- message = "_1 is reserved for numbered parameters"
- assert_errors expression(source), source, [
- [message, 5..7],
- [message, 13..15],
- [message, 24..26],
- [message, 35..37],
- [message, 42..44]
- ]
- end
-
- def test_symbol_in_keyword_parameter
- source = "def foo(x:'y':); end"
- assert_errors expression(source), source, [
- ["unexpected label terminator, expected a string literal terminator", 12..14]
- ]
- end
-
- def test_symbol_in_hash
- source = "{x:'y':}"
- assert_errors expression(source), source, [
- ["unexpected label terminator, expected a string literal terminator", 5..7]
- ]
- end
-
- def test_while_endless_method
- source = "while def f = g do end"
-
- assert_errors expression(source), source, [
- ["expected a predicate expression for the `while` statement", 22..22],
- ["unexpected end-of-input, assuming it is closing the parent top level context", 22..22],
- ["expected an `end` to close the `while` statement", 22..22]
- ]
- end
-
- def test_match_plus
- source = <<~RUBY
- a in b + c
- a => b + c
- RUBY
-
- assert_errors expression(source), source, [
- ["unexpected '+', expecting end-of-input", 7..8],
- ["unexpected '+', ignoring it", 7..8],
- ["unexpected '+', expecting end-of-input", 18..19],
- ["unexpected '+', ignoring it", 18..19]
- ]
- end
-
- def test_rational_number_with_exponential_portion
- source = '1e1r; 1e1ri'
-
- assert_errors expression(source), source, [
- ["unexpected local variable or method, expecting end-of-input", 3..4],
- ["unexpected local variable or method, expecting end-of-input", 9..11]
- ]
- end
-
- def test_check_value_expression
- source = <<~RUBY
- 1 => ^(return)
- while true
- 1 => ^(break)
- 1 => ^(next)
- 1 => ^(redo)
- 1 => ^(retry)
- 1 => ^(2 => a)
- end
- 1 => ^(if 1; (return) else (return) end)
- 1 => ^(unless 1; (return) else (return) end)
- RUBY
-
- message = "unexpected void value expression"
- assert_errors expression(source), source, [
- [message, 7..13],
- [message, 35..40],
- [message, 51..55],
- [message, 66..70],
- ["Invalid retry without rescue", 81..86],
- [message, 81..86],
- [message, 97..103],
- [message, 123..129],
- [message, 168..174],
- ]
- end
-
- def test_void_value_expression_in_statement
- source = <<~RUBY
- if (return)
- end
- unless (return)
- end
- while (return)
- end
- until (return)
- end
- case (return)
- when 1
- end
- class A < (return)
- end
- class << (return)
- end
- for x in (return)
- end
- RUBY
-
- message = 'unexpected void value expression'
- assert_errors expression(source), source, [
- [message, 4..10],
- [message, 24..30],
- [message, 43..49],
- [message, 62..68],
- [message, 80..86],
- [message, 110..116],
- [message, 132..138],
- [message, 154..160],
- ]
- end
-
- def test_void_value_expression_in_begin_statement
- source = <<~RUBY
- x = return 1
- x = return, 1
- x = 1, return
- x, y = return
- x = begin return ensure end
- x = begin ensure return end
- x = begin return ensure return end
- x = begin return; rescue; return end
- x = begin return; rescue; return; else return end
- x = begin; return; rescue; retry; end
- RUBY
-
- message = 'unexpected void value expression'
- assert_errors expression(source), source, [
- [message, 4..12],
- [message, 17..23],
- [message, 34..40],
- [message, 48..54],
- [message, 65..71],
- [message, 100..106],
- [message, 121..127],
- [message, 156..162],
- [message, 222..228],
- [message, 244..250],
- ]
-
- refute_error_messages("x = begin return; rescue; end")
- refute_error_messages("x = begin return; rescue; return; else end")
- refute_error_messages("x = begin; rescue; retry; end")
- refute_error_messages("x = begin 1; rescue; retry; ensure; end")
- refute_error_messages("x = begin 1; rescue; return; end")
- end
-
- def test_void_value_expression_in_def
- source = <<~RUBY
- def (return).x
- end
- def x(a = return)
- end
- def x(a: return)
- end
- RUBY
-
- message = 'unexpected void value expression'
- assert_errors expression(source), source, [
- [message, 5..11],
- [message, 29..35],
- [message, 50..56],
- ]
- end
-
- def test_void_value_expression_in_assignment
- source = <<~RUBY
- a = return
- a = 1, return
- a, b = return, 1
- a, b = 1, *return
- RUBY
-
- message = 'unexpected void value expression'
- assert_errors expression(source), source, [
- [message, 4..10],
- [message, 18..24],
- [message, 32..38],
- [message, 53..59],
- ]
- end
-
- def test_void_value_expression_in_modifier
- source = <<~RUBY
- 1 if (return)
- 1 unless (return)
- 1 while (return)
- 1 until (return)
- (return) => a
- (return) in a
- RUBY
-
- message = 'unexpected void value expression'
- assert_errors expression(source), source, [
- [message, 6..12],
- [message, 24..30],
- [message, 41..47],
- [message, 58..64],
- [message, 67..73],
- [message, 81..87]
- ]
- end
-
- def test_void_value_expression_in_expression
- source = <<~RUBY
- (return) ? 1 : 1
- (return)..1
- 1..(return)
- (return)...1
- 1...(return)
- (..(return))
- (...(return))
- ((return)..)
- ((return)...)
- RUBY
-
- message = 'unexpected void value expression'
- assert_errors expression(source), source, [
- [message, 1..7],
- [message, 18..24],
- [message, 33..39],
- [message, 42..48],
- [message, 59..65],
- [message, 71..77],
- [message, 85..91],
- [message, 96..102],
- [message, 109..115]
- ]
- end
-
- def test_void_value_expression_in_array
- source = <<~RUBY
- [return]
- [1, return]
- [ return => 1 ]
- [ 1 => return ]
- [ a: return ]
- [ *return ]
- [ **return ]
- RUBY
-
- message = 'unexpected void value expression'
- assert_errors expression(source), source, [
- [message, 1..7],
- [message, 13..19],
- [message, 23..29],
- [message, 44..50],
- [message, 58..64],
- [message, 70..76],
- [message, 83..89],
- ]
- end
-
- def test_void_value_expression_in_hash
- source = <<~RUBY
- { return => 1 }
- { 1 => return }
- { a: return }
- { **return }
- RUBY
-
- message = 'unexpected void value expression'
- assert_errors expression(source), source, [
- [message, 2..8],
- [message, 23..29],
- [message, 37..43],
- [message, 50..56],
- ]
- end
-
- def test_void_value_expression_in_call
- source = <<~RUBY
- (return).foo
- (return).(1)
- (return)[1]
- (return)[1] = 2
- (return)::foo
- RUBY
-
- message = 'unexpected void value expression'
- assert_errors expression(source), source, [
- [message, 1..7],
- [message, 14..20],
- [message, 27..33],
- [message, 39..45],
- [message, 55..61],
- ]
- end
-
- def test_void_value_expression_in_constant_path
- source = <<~RUBY
- (return)::A
- class (return)::A; end
- RUBY
-
- message = 'unexpected void value expression'
- assert_errors expression(source), source, [
- [message, 1..7],
- [message, 19..25],
- ]
- end
-
- def test_void_value_expression_in_arguments
- source = <<~RUBY
- foo(return)
- foo(1, return)
- foo(*return)
- foo(**return)
- foo(&return)
- foo(return => 1)
- foo(:a => return)
- foo(a: return)
- RUBY
-
- message = 'unexpected void value expression'
- assert_errors expression(source), source, [
- [message, 4..10],
- [message, 19..25],
- [message, 32..38],
- [message, 46..52],
- [message, 59..65],
- [message, 71..77],
- [message, 94..100],
- [message, 109..115],
- ]
- end
-
- def test_void_value_expression_in_unary_call
- source = <<~RUBY
- +(return)
- not return
- RUBY
-
- message = 'unexpected void value expression'
- assert_errors expression(source), source, [
- [message, 2..8],
- [message, 14..20],
- ]
- end
-
- def test_void_value_expression_in_binary_call
- source = <<~RUBY
- 1 + (return)
- (return) + 1
- 1 and (return)
- (return) and 1
- 1 or (return)
- (return) or 1
- RUBY
-
- message = 'unexpected void value expression'
- assert_errors expression(source), source, [
- [message, 5..11],
- [message, 14..20],
- [message, 42..48],
- [message, 71..77],
- ]
- end
-
- def test_trailing_comma_in_calls
- assert_errors expression("foo 1,"), "foo 1,", [
- ["expected an argument", 5..6]
- ]
- end
-
- def test_argument_after_ellipsis
- source = 'def foo(...); foo(..., 1); end'
- assert_errors expression(source), source, [
- ['unexpected argument after `...`', 23..24]
- ]
- end
-
- def test_ellipsis_in_no_paren_call
- source = 'def foo(...); foo 1, ...; end'
- assert_errors expression(source), source, [
- ['unexpected `...` in an non-parenthesized call', 21..24]
- ]
- end
-
- def test_non_assoc_range
- source = '1....2'
-
- assert_errors expression(source), source, [
- ["unexpected '.', expecting end-of-input", 4..5],
- ["unexpected '.', ignoring it", 4..5]
- ]
- end
-
- def test_upcase_end_in_def
- assert_warning_messages "def foo; END { }; end", [
- "END in method; use at_exit"
- ]
- end
-
- def test_warnings_verbosity
- warning = Prism.parse("def foo; END { }; end").warnings[0]
- assert_equal "END in method; use at_exit", warning.message
- assert_equal :default, warning.level
-
- warning = Prism.parse("foo /regexp/").warnings[0]
- assert_equal "ambiguous `/`; wrap regexp in parentheses or add a space after `/` operator", warning.message
- assert_equal :verbose, warning.level
- end
-
- def test_statement_operators
- source = <<~RUBY
- alias x y + 1
- alias x y.z
- BEGIN { bar } + 1
- BEGIN { bar }.z
- END { bar } + 1
- END { bar }.z
- undef x + 1
- undef x.z
- RUBY
-
- assert_errors expression(source), source, [
- ["unexpected '+', expecting end-of-input", 10..11],
- ["unexpected '+', ignoring it", 10..11],
- ["unexpected '.', expecting end-of-input", 23..24],
- ["unexpected '.', ignoring it", 23..24],
- ["unexpected '+', expecting end-of-input", 40..41],
- ["unexpected '+', ignoring it", 40..41],
- ["unexpected '.', expecting end-of-input", 57..58],
- ["unexpected '.', ignoring it", 57..58],
- ["unexpected '+', expecting end-of-input", 72..73],
- ["unexpected '+', ignoring it", 72..73],
- ["unexpected '.', expecting end-of-input", 87..88],
- ["unexpected '.', ignoring it", 87..88],
- ["unexpected '+', expecting end-of-input", 98..99],
- ["unexpected '+', ignoring it", 98..99],
- ["unexpected '.', expecting end-of-input", 109..110],
- ["unexpected '.', ignoring it", 109..110]
- ]
- end
-
- def test_statement_at_non_statement
- source = <<~RUBY
- foo(alias x y)
- foo(BEGIN { bar })
- foo(END { bar })
- foo(undef x)
- RUBY
- assert_errors expression(source), source, [
- ['unexpected an `alias` at a non-statement position', 4..9],
- ['unexpected a `BEGIN` at a non-statement position', 19..24],
- ['unexpected an `END` at a non-statement position', 38..41],
- ['unexpected an `undef` at a non-statement position', 55..60],
- ]
- end
-
- def test_binary_range_with_left_unary_range
- source = <<~RUBY
- ..1..
- ...1..
- RUBY
-
- assert_errors expression(source), source, [
- ["unexpected range operator; .. and ... are non-associative and cannot be chained", 3..5],
- ["unexpected range operator; .. and ... are non-associative and cannot be chained", 10..12],
- ["unexpected .., expecting end-of-input", 10..12],
- ["unexpected .., ignoring it", 10..12]
- ]
- end
-
- def test_circular_param
+ def test_circular_parameters
source = <<~RUBY
def foo(bar = bar) = 42
def foo(bar: bar) = 42
@@ -1990,294 +68,26 @@ module Prism
proc { |foo: foo| }
RUBY
- assert_errors(
- expression(source),
- source,
- [
- ["circular argument reference - bar", 8..11],
- ["circular argument reference - bar", 32..35],
- ["circular argument reference - foo", 55..58],
- ["circular argument reference - foo", 76..79]
- ],
- check_valid_syntax: false,
- version: "3.3.0"
- )
-
- refute_error_messages("def foo(bar: bar = 1); end")
- end
-
- def test_command_calls
- sources = <<~RUBY.lines
- [a b]
- {a: b c}
- ...a b
- if ...a b; end
- a b, c d
- a(b, c d)
- a(*b c)
- a(**b c)
- a(&b c)
- +a b
- a + b c
- a && b c
- a =~ b c
- a = b, c d
- a = *b c
- a, b = c = d f
- a ? b c : d e
- defined? a b
- ! ! a b
- def f a = b c; end
- def f(a = b c); end
- a = b rescue c d
- def a = b rescue c d
- ->a=b c{}
- ->(a=b c){}
- case; when a b; end
- case; in a if a b; end
- case; in a unless a b; end
- begin; rescue a b; end
- begin; rescue a b => c; end
- RUBY
-
- sources.each do |source|
- refute_valid_syntax(source)
- assert_false(Prism.parse(source).success?)
+ source.each_line do |line|
+ assert_predicate Prism.parse(line, version: "3.3.0"), :failure?
+ assert_predicate Prism.parse(line), :success?
end
end
- def test_range_and_bin_op
- sources = <<~RUBY.lines
- 1..2..3
- 1..2..
- 1.. || 2
- 1.. & 2
- 1.. * 2
- 1.. / 2
- 1.. % 2
- 1.. ** 2
- RUBY
-
- sources.each do |source|
- refute_valid_syntax(source)
- assert_false(Prism.parse(source).success?)
- end
- end
-
- def test_command_call_in
- source = <<~RUBY
- foo 1 in a
- a = foo 2 in b
- RUBY
-
- assert_errors expression(source), source, [
- ["unexpected `in` keyword in arguments", 9..10],
- ["unexpected local variable or method, expecting end-of-input", 9..10],
- ["unexpected `in` keyword in arguments", 24..25],
- ["unexpected local variable or method, expecting end-of-input", 24..25]
- ]
- end
-
- def test_constant_assignment_in_method
- source = 'def foo();A=1;end'
- assert_errors expression(source), source, [
- ['dynamic constant assignment', 10..13]
- ]
- end
-
- def test_non_assoc_equality
- source = <<~RUBY
- 1 == 2 == 3
- 1 != 2 != 3
- 1 === 2 === 3
- 1 =~ 2 =~ 3
- 1 !~ 2 !~ 3
- 1 <=> 2 <=> 3
- RUBY
-
- assert_errors expression(source), source, [
- ["unexpected '==', expecting end-of-input", 7..9],
- ["unexpected '==', ignoring it", 7..9],
- ["unexpected '!=', expecting end-of-input", 19..21],
- ["unexpected '!=', ignoring it", 19..21],
- ["unexpected '===', expecting end-of-input", 32..35],
- ["unexpected '===', ignoring it", 32..35],
- ["unexpected '=~', expecting end-of-input", 45..47],
- ["unexpected '=~', ignoring it", 45..47],
- ["unexpected '!~', expecting end-of-input", 57..59],
- ["unexpected '!~', ignoring it", 57..59],
- ["unexpected '<=>', expecting end-of-input", 70..73],
- ["unexpected '<=>', ignoring it", 70..73]
- ]
- end
-
- def test_block_arg_and_block
- source = 'foo(&1) { }'
- assert_errors expression(source), source, [
- ["both block arg and actual block given; only one block is allowed", 8..11]
- ]
- end
-
- def test_forwarding_arg_and_block
- source = 'def foo(...) = foo(...) { }'
- assert_errors expression(source), source, [
- ['both block arg and actual block given; only one block is allowed', 24..27]
- ]
- end
-
- def test_it_with_ordinary_parameter
- source = "proc { || it }"
- errors = [["`it` is not allowed when an ordinary parameter is defined", 10..12]]
-
- assert_errors expression(source), source, errors, check_valid_syntax: RUBY_VERSION >= "3.4.0"
- end
-
- def test_regular_expression_with_unknown_regexp_options
- source = "/foo/AZaz"
- errors = [["unknown regexp options - AZaz", 4..9]]
-
- assert_errors expression(source), source, errors
- end
-
- def test_interpolated_regular_expression_with_unknown_regexp_options
- source = "/\#{foo}/AZaz"
- errors = [["unknown regexp options - AZaz", 7..12]]
-
- assert_errors expression(source), source, errors
- end
-
- def test_singleton_method_for_literals
- source = <<~'RUBY'
- def (1).g; end
- def ((a; 1)).foo; end
- def ((return; 1)).bar; end
- def (((1))).foo; end
- def (__FILE__).foo; end
- def (__ENCODING__).foo; end
- def (__LINE__).foo; end
- def ("foo").foo; end
- def (3.14).foo; end
- def (3.14i).foo; end
- def (:foo).foo; end
- def (:'foo').foo; end
- def (:'f{o}').foo; end
- def ('foo').foo; end
- def ("foo").foo; end
- def ("#{fo}o").foo; end
- def (/foo/).foo; end
- def (/f#{oo}/).foo; end
- def ([1]).foo; end
- RUBY
- errors = [
- ["cannot define singleton method for literals", 5..6],
- ["cannot define singleton method for literals", 24..25],
- ["cannot define singleton method for literals", 51..52],
- ["cannot define singleton method for literals", 71..72],
- ["cannot define singleton method for literals", 90..98],
- ["cannot define singleton method for literals", 114..126],
- ["cannot define singleton method for literals", 142..150],
- ["cannot define singleton method for literals", 166..171],
- ["cannot define singleton method for literals", 187..191],
- ["cannot define singleton method for literals", 207..212],
- ["cannot define singleton method for literals", 228..232],
- ["cannot define singleton method for literals", 248..254],
- ["cannot define singleton method for literals", 270..277],
- ["cannot define singleton method for literals", 293..298],
- ["cannot define singleton method for literals", 314..319],
- ["cannot define singleton method for literals", 335..343],
- ["cannot define singleton method for literals", 359..364],
- ["cannot define singleton method for literals", 380..388],
- ["cannot define singleton method for literals", 404..407]
- ]
- assert_errors expression(source), source, errors
- end
-
- def test_assignment_to_literal_in_conditionals
- source = <<~RUBY
- if (a = 2); end
- if ($a = 2); end
- if (@a = 2); end
- if (@@a = 2); end
- if a elsif b = 2; end
- unless (a = 2); end
- unless ($a = 2); end
- unless (@a = 2); end
- unless (@@a = 2); end
- while (a = 2); end
- while ($a = 2); end
- while (@a = 2); end
- while (@@a = 2); end
- until (a = 2); end
- until ($a = 2); end
- until (@a = 2); end
- until (@@a = 2); end
- foo if a = 2
- foo if (a, b = 2)
- (@foo = 1) ? a : b
- !(a = 2)
- not a = 2
- RUBY
- assert_warning_messages source, [
- "found '= literal' in conditional, should be =="
- ] * source.lines.count
- end
-
- def test_duplicate_pattern_capture
- source = <<~RUBY
- case (); in [a, a]; end
- case (); in [a, *a]; end
- case (); in {a: a, b: a}; end
- case (); in {a: a, **a}; end
- case (); in [a, {a:}]; end
- case (); in [a, {a: {a: {a: [a]}}}]; end
- case (); in a => a; end
- case (); in [A => a, {a: b => a}]; end
- RUBY
-
- assert_error_messages source, Array.new(source.lines.length, "duplicated variable name")
- refute_error_messages "case (); in [_a, _a]; end"
- end
-
- def test_duplicate_pattern_hash_key
- assert_error_messages "case (); in {a:, a:}; end", ["duplicated key name", "duplicated variable name"]
- assert_error_messages "case (); in {a:1, a:2}; end", ["duplicated key name"]
- refute_error_messages "case (); in [{a:1}, {a:2}]; end"
- end
-
- def test_unexpected_block
- assert_error_messages "def foo = yield(&:+)", ["block argument should not be given"]
- end
-
private
- def assert_errors(expected, source, errors, check_valid_syntax: true, **options)
- refute_valid_syntax(source) if check_valid_syntax
+ def assert_errors(filepath)
+ expected = File.read(filepath)
- result = Prism.parse(source, **options)
- node = result.value.statements.body.last
-
- assert_equal_nodes(expected, node, compare_location: false)
- assert_equal(errors, result.errors.map { |e| [e.message, e.location.start_offset..e.location.end_offset] })
- end
-
- def assert_error_messages(source, errors)
+ source = expected.lines.grep_v(/^\s*\^/).join.gsub(/\n*\z/, "")
refute_valid_syntax(source)
- result = Prism.parse(source)
- assert_equal(errors, result.errors.map(&:message))
- end
-
- def refute_error_messages(source)
- assert_valid_syntax(source)
- assert Prism.parse_success?(source), "Expected #{source.inspect} to parse successfully"
- end
- def assert_warning_messages(source, warnings)
result = Prism.parse(source)
- assert_equal(warnings, result.warnings.map(&:message))
- end
+ errors = result.errors
+ refute_empty errors, "Expected errors in #{filepath}"
- def expression(source)
- Prism.parse(source).value.statements.body.last
+ actual = result.errors_format
+ assert_equal expected, actual, "Expected errors to match for #{filepath}"
end
end
end
diff --git a/test/prism/fixtures/patterns.txt b/test/prism/fixtures/patterns.txt
index 7ce3b9e4a8..29f8b4cf80 100644
--- a/test/prism/fixtures/patterns.txt
+++ b/test/prism/fixtures/patterns.txt
@@ -215,3 +215,6 @@ foo => Object[{x:}]
a,
b
) = c
+
+case (); in [_a, _a]; end
+case (); in [{a:1}, {a:2}]; end
diff --git a/test/prism/result/warnings_test.rb b/test/prism/result/warnings_test.rb
index ea062d4221..747440dd76 100644
--- a/test/prism/result/warnings_test.rb
+++ b/test/prism/result/warnings_test.rb
@@ -75,6 +75,37 @@ module Prism
assert_warning("1 if 2..foo", "integer")
end
+ def test_literal_in_conditionals
+ source = <<~RUBY
+ if (a = 2); a; end
+ if ($a = 2); end
+ if (@a = 2); end
+ if (@@a = 2); end
+ if a; elsif b = 2; b end
+ unless (a = 2); a; end
+ unless ($a = 2); end
+ unless (@a = 2); end
+ unless (@@a = 2); end
+ while (a = 2); a; end
+ while ($a = 2); end
+ while (@a = 2); end
+ while (@@a = 2); end
+ until (a = 2); a; end
+ until ($a = 2); end
+ until (@a = 2); end
+ until (@@a = 2); end
+ foo if (a, b = 2); [a, b]
+ foo if a = 2 and a
+ (@foo = 1) ? a : b
+ !(a = 2) and a
+ not a = 2 and a
+ RUBY
+
+ source.each_line(chomp: true) do |line|
+ assert_warning(line, "found '= literal' in conditional, should be ==")
+ end
+ end
+
def test_keyword_eol
assert_warning("if\ntrue; end", "end of line")
assert_warning("if true\nelsif\nfalse; end", "end of line")
@@ -227,12 +258,22 @@ module Prism
assert_warning("tap { redo; foo }", "statement not reached")
end
+ def test_warnings_verbosity
+ warning = Prism.parse("def foo; END { }; end").warnings.first
+ assert_equal "END in method; use at_exit", warning.message
+ assert_equal :default, warning.level
+
+ warning = Prism.parse("foo /regexp/").warnings.first
+ assert_equal "ambiguous `/`; wrap regexp in parentheses or add a space after `/` operator", warning.message
+ assert_equal :verbose, warning.level
+ end
+
private
def assert_warning(source, message)
warnings = Prism.parse(source).warnings
- assert_equal 1, warnings.length
+ assert_equal 1, warnings.length, "Expected only one warning in #{source.inspect}, got #{warnings.map(&:message).inspect}"
assert_include warnings.first.message, message
if defined?(RubyVM::AbstractSyntaxTree)