summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Newton <kddnewton@gmail.com>2024-05-01 17:33:22 -0400
committerKevin Newton <kddnewton@gmail.com>2024-05-01 19:19:07 -0400
commit5d1e4cd249c2589c95258fcd8c62d537eb810dd5 (patch)
treebf46fb7c9022c50f2904bdd8e9a3746a1dfc9fda
parent4e8ae5d32a19578701e26dbad093f350ed9a37b6 (diff)
[PRISM] Better error messages for unwriteable targets
-rw-r--r--prism/prism.c37
-rw-r--r--test/.excludes-prism/TestParse.rb7
2 files changed, 37 insertions, 7 deletions
diff --git a/prism/prism.c b/prism/prism.c
index f44ac73ea8..75bf37a16b 100644
--- a/prism/prism.c
+++ b/prism/prism.c
@@ -12950,6 +12950,32 @@ parse_write_name(pm_parser_t *parser, pm_constant_id_t *name_field) {
}
/**
+ * 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.
*/
static pm_node_t *
@@ -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")