@@ -2622,10 +2622,11 @@ pm_break_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_argument
26222622// There are certain flags that we want to use internally but don't want to
26232623// expose because they are not relevant beyond parsing. Therefore we'll define
26242624// them here and not define them in config.yml/a header file.
2625- static const pm_node_flags_t PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY = 0x4;
2626- static const pm_node_flags_t PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY = 0x40;
2627- static const pm_node_flags_t PM_CALL_NODE_FLAGS_COMPARISON = 0x80;
2628- static const pm_node_flags_t PM_CALL_NODE_FLAGS_INDEX = 0x100;
2625+ static const pm_node_flags_t PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY = (1 << 2);
2626+
2627+ static const pm_node_flags_t PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY = ((PM_CALL_NODE_FLAGS_LAST - 1) << 1);
2628+ static const pm_node_flags_t PM_CALL_NODE_FLAGS_COMPARISON = ((PM_CALL_NODE_FLAGS_LAST - 1) << 2);
2629+ static const pm_node_flags_t PM_CALL_NODE_FLAGS_INDEX = ((PM_CALL_NODE_FLAGS_LAST - 1) << 3);
26292630
26302631/**
26312632 * Allocate and initialize a new CallNode node. This sets everything to NULL or
@@ -5279,6 +5280,12 @@ pm_interpolated_string_node_append(pm_interpolated_string_node_t *node, pm_node_
52795280
52805281 switch (PM_NODE_TYPE(part)) {
52815282 case PM_STRING_NODE:
5283+ // If inner string is not frozen, it stops being a static literal. We should *not* clear other flags,
5284+ // because concatenating two frozen strings (`'foo' 'bar'`) is still frozen. This holds true for
5285+ // as long as this interpolation only consists of other string literals.
5286+ if (!PM_NODE_FLAG_P(part, PM_STRING_FLAGS_FROZEN)) {
5287+ pm_node_flag_unset((pm_node_t *) node, PM_NODE_FLAG_STATIC_LITERAL);
5288+ }
52825289 part->flags = (pm_node_flags_t) ((part->flags | PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN) & ~PM_STRING_FLAGS_MUTABLE);
52835290 break;
52845291 case PM_INTERPOLATED_STRING_NODE:
@@ -14443,6 +14450,17 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for
1444314450 if (accepted_newline) {
1444414451 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
1444514452 }
14453+
14454+ // If this is a command call and an argument takes a block,
14455+ // there can be no further arguments. For example,
14456+ // `foo(bar 1 do end, 2)` should be rejected.
14457+ if (PM_NODE_TYPE_P(argument, PM_CALL_NODE)) {
14458+ pm_call_node_t *call = (pm_call_node_t *) argument;
14459+ if (call->opening_loc.start == NULL && call->arguments != NULL && call->block != NULL) {
14460+ pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
14461+ break;
14462+ }
14463+ }
1444614464 } else {
1444714465 // If there is no comma at the end of the argument list then we're
1444814466 // done parsing arguments and can break out of this loop.
@@ -14594,6 +14612,18 @@ update_parameter_state(pm_parser_t *parser, pm_token_t *token, pm_parameters_ord
1459414612 return true;
1459514613}
1459614614
14615+ /**
14616+ * Ensures that after parsing a parameter, the next token is not `=`.
14617+ * Some parameters like `def(* = 1)` cannot become optional. When no parens
14618+ * are present like in `def * = 1`, this creates ambiguity with endless method definitions.
14619+ */
14620+ static inline void
14621+ refute_optional_parameter(pm_parser_t *parser) {
14622+ if (match1(parser, PM_TOKEN_EQUAL)) {
14623+ pm_parser_err_previous(parser, PM_ERR_DEF_ENDLESS_PARAMETERS);
14624+ }
14625+ }
14626+
1459714627/**
1459814628 * Parse a list of parameters on a method definition.
1459914629 */
@@ -14646,6 +14676,10 @@ parse_parameters(
1464614676 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_BLOCK;
1464714677 }
1464814678
14679+ if (!uses_parentheses) {
14680+ refute_optional_parameter(parser);
14681+ }
14682+
1464914683 pm_block_parameter_node_t *param = pm_block_parameter_node_create(parser, &name, &operator);
1465014684 if (repeated) {
1465114685 pm_node_flag_set_repeated_parameter((pm_node_t *)param);
@@ -14667,6 +14701,10 @@ parse_parameters(
1466714701 bool succeeded = update_parameter_state(parser, &parser->current, &order);
1466814702 parser_lex(parser);
1466914703
14704+ if (!uses_parentheses) {
14705+ refute_optional_parameter(parser);
14706+ }
14707+
1467014708 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_ALL;
1467114709 pm_forwarding_parameter_node_t *param = pm_forwarding_parameter_node_create(parser, &parser->previous);
1467214710
@@ -14848,6 +14886,10 @@ parse_parameters(
1484814886 context_pop(parser);
1484914887 pm_parameters_node_keywords_append(params, param);
1485014888
14889+ if (!uses_parentheses) {
14890+ refute_optional_parameter(parser);
14891+ }
14892+
1485114893 // If parsing the value of the parameter resulted in error recovery,
1485214894 // then we can put a missing node in its place and stop parsing the
1485314895 // parameters entirely now.
@@ -14879,6 +14921,10 @@ parse_parameters(
1487914921 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS;
1488014922 }
1488114923
14924+ if (!uses_parentheses) {
14925+ refute_optional_parameter(parser);
14926+ }
14927+
1488214928 pm_node_t *param = (pm_node_t *) pm_rest_parameter_node_create(parser, &operator, &name);
1488314929 if (repeated) {
1488414930 pm_node_flag_set_repeated_parameter(param);
@@ -14927,6 +14973,10 @@ parse_parameters(
1492714973 }
1492814974 }
1492914975
14976+ if (!uses_parentheses) {
14977+ refute_optional_parameter(parser);
14978+ }
14979+
1493014980 if (params->keyword_rest == NULL) {
1493114981 pm_parameters_node_keyword_rest_set(params, param);
1493214982 } else {
@@ -18491,20 +18541,28 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1849118541 return (pm_node_t *) node;
1849218542 }
1849318543 case PM_TOKEN_CHARACTER_LITERAL: {
18494- parser_lex(parser);
18495-
18496- pm_token_t opening = parser->previous;
18497- opening.type = PM_TOKEN_STRING_BEGIN;
18498- opening.end = opening.start + 1;
18499-
18500- pm_token_t content = parser->previous;
18501- content.type = PM_TOKEN_STRING_CONTENT;
18502- content.start = content.start + 1;
18503-
1850418544 pm_token_t closing = not_provided(parser);
18505- pm_node_t *node = (pm_node_t *) pm_string_node_create_current_string(parser, &opening, &content, &closing);
18545+ pm_node_t *node = (pm_node_t *) pm_string_node_create_current_string(
18546+ parser,
18547+ &(pm_token_t) {
18548+ .type = PM_TOKEN_STRING_BEGIN,
18549+ .start = parser->current.start,
18550+ .end = parser->current.start + 1
18551+ },
18552+ &(pm_token_t) {
18553+ .type = PM_TOKEN_STRING_CONTENT,
18554+ .start = parser->current.start + 1,
18555+ .end = parser->current.end
18556+ },
18557+ &closing
18558+ );
18559+
1850618560 pm_node_flag_set(node, parse_unescaped_encoding(parser));
1850718561
18562+ // Skip past the character literal here, since now we have handled
18563+ // parser->explicit_encoding correctly.
18564+ parser_lex(parser);
18565+
1850818566 // Characters can be followed by strings in which case they are
1850918567 // automatically concatenated.
1851018568 if (match1(parser, PM_TOKEN_STRING_BEGIN)) {
@@ -20901,7 +20959,7 @@ parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding
2090120959 bool permitted = true;
2090220960 if (previous_binding_power != PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_USTAR)) permitted = false;
2090320961
20904- pm_node_t *value = parse_starred_expression(parser, binding_power, previous_binding_power == PM_BINDING_POWER_ASSIGNMENT ? accepts_command_call : previous_binding_power < PM_BINDING_POWER_MATCH , diag_id, (uint16_t) (depth + 1));
20962+ pm_node_t *value = parse_starred_expression(parser, binding_power, previous_binding_power == PM_BINDING_POWER_ASSIGNMENT ? accepts_command_call : previous_binding_power < PM_BINDING_POWER_MODIFIER , diag_id, (uint16_t) (depth + 1));
2090520963 if (!permitted) pm_parser_err_node(parser, value, PM_ERR_UNEXPECTED_MULTI_WRITE);
2090620964
2090720965 parse_assignment_value_local(parser, value);
@@ -22498,9 +22556,10 @@ parse_program(pm_parser_t *parser) {
2249822556 statements = wrap_statements(parser, statements);
2249922557 } else {
2250022558 flush_block_exits(parser, previous_block_exits);
22501- pm_node_list_free(¤t_block_exits);
2250222559 }
2250322560
22561+ pm_node_list_free(¤t_block_exits);
22562+
2250422563 // If this is an empty file, then we're still going to parse all of the
2250522564 // statements in order to gather up all of the comments and such. Here we'll
2250622565 // correct the location information.
0 commit comments