diff --git a/prism/prism.c b/prism/prism.c index f44ac73ea8..75bf37a16b 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -12949,6 +12949,32 @@ parse_write_name(pm_parser_t *parser, pm_constant_id_t *name_field) { *name_field = pm_constant_pool_insert_owned(&parser->constant_pool, name, length + 1); } +/** + * Certain expressions are not targetable, but in order to provide a better + * experience we give a specific error message. In order to maintain as much + * information in the tree as possible, we replace them with local variable + * writes. + */ +static pm_node_t * +parse_unwriteable_target(pm_parser_t *parser, pm_node_t *target) { + switch (PM_NODE_TYPE(target)) { + case PM_SOURCE_ENCODING_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING); break; + case PM_FALSE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE); break; + case PM_SOURCE_FILE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_FILE); break; + case PM_SOURCE_LINE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_LINE); break; + case PM_NIL_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_NIL); break; + case PM_SELF_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_SELF); break; + case PM_TRUE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE); break; + default: break; + } + + pm_constant_id_t name = pm_parser_constant_id_location(parser, target->location.start, target->location.end); + pm_local_variable_target_node_t *result = pm_local_variable_target_node_create(parser, &target->location, name, 0); + + pm_node_destroy(parser, target); + return (pm_node_t *) result; +} + /** * Convert the given node into a valid target node. */ @@ -12957,6 +12983,17 @@ parse_target(pm_parser_t *parser, pm_node_t *target) { switch (PM_NODE_TYPE(target)) { case PM_MISSING_NODE: return target; + case PM_SOURCE_ENCODING_NODE: + case PM_FALSE_NODE: + case PM_SOURCE_FILE_NODE: + case PM_SOURCE_LINE_NODE: + case PM_NIL_NODE: + case PM_SELF_NODE: + case PM_TRUE_NODE: { + // In these special cases, we have specific error messages and we + // will replace them with local variable writes. + return parse_unwriteable_target(parser, target); + } case PM_CLASS_VARIABLE_READ_NODE: assert(sizeof(pm_class_variable_target_node_t) == sizeof(pm_class_variable_read_node_t)); target->type = PM_CLASS_VARIABLE_TARGET_NODE; diff --git a/test/.excludes-prism/TestParse.rb b/test/.excludes-prism/TestParse.rb index 83af593b15..060f36d3c6 100644 --- a/test/.excludes-prism/TestParse.rb +++ b/test/.excludes-prism/TestParse.rb @@ -1,14 +1,9 @@ -exclude(:test_dynamic_constant_assignment, "unknown") -exclude(:test_else_without_rescue, "unknown") exclude(:test_error_def_in_argument, "unknown") -exclude(:test_float, "unknown") exclude(:test_global_variable, "unknown") exclude(:test_here_document, "unknown") -exclude(:test_heredoc_unterminated_interpolation, "unknown") exclude(:test_invalid_char, "unknown") exclude(:test_location_of_invalid_token, "unknown") exclude(:test_op_asgn1_with_block, "unknown") -exclude(:test_parse_string, "unknown") exclude(:test_percent, "unknown") exclude(:test_question, "unknown") exclude(:test_shareable_constant_value_ignored, "unknown") @@ -19,10 +14,8 @@ exclude(:test_shareable_constant_value_unfrozen, "ractor support") exclude(:test_shareable_constant_value_unshareable_literal, "ractor support") exclude(:test_string, "unknown") exclude(:test_truncated_source_line, "unknown") -exclude(:test_unassignable, "unknown") exclude(:test_unexpected_eof, "unknown") exclude(:test_unexpected_token_after_numeric, "unknown") exclude(:test_unterminated_regexp_error, "unknown") exclude(:test_unused_variable, "missing warning") exclude(:test_void_value_in_rhs, "unknown") -exclude(:test_words, "unknown")