diff --git a/prism/config.yml b/prism/config.yml index 16d042dbdc..9001b388ee 100644 --- a/prism/config.yml +++ b/prism/config.yml @@ -208,6 +208,7 @@ errors: - PARAMETER_UNEXPECTED_FWD - PARAMETER_UNEXPECTED_NO_KW - PARAMETER_WILD_LOOSE_COMMA + - PATTERN_ARRAY_MULTIPLE_RESTS - PATTERN_CAPTURE_DUPLICATE - PATTERN_EXPRESSION_AFTER_BRACKET - PATTERN_EXPRESSION_AFTER_COMMA @@ -219,6 +220,7 @@ errors: - PATTERN_EXPRESSION_AFTER_PIPE - PATTERN_EXPRESSION_AFTER_RANGE - PATTERN_EXPRESSION_AFTER_REST + - PATTERN_FIND_MISSING_INNER - PATTERN_HASH_IMPLICIT - PATTERN_HASH_KEY - PATTERN_HASH_KEY_DUPLICATE diff --git a/prism/prism.c b/prism/prism.c index 4932ffb2e4..6e93e2a961 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -17049,9 +17049,9 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag } if ((flags & PM_PARSE_PATTERN_MULTI) && match1(parser, PM_TOKEN_COMMA)) { - // If we have a comma, then we are now parsing either an array pattern or a - // find pattern. We need to parse all of the patterns, put them into a big - // list, and then determine which type of node we have. + // If we have a comma, then we are now parsing either an array pattern + // or a find pattern. We need to parse all of the patterns, put them + // into a big list, and then determine which type of node we have. pm_node_list_t nodes = { 0 }; pm_node_list_append(&nodes, node); @@ -17067,9 +17067,9 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag if (accept1(parser, PM_TOKEN_USTAR)) { node = (pm_node_t *) parse_pattern_rest(parser, captures); - // If we have already parsed a splat pattern, then this is an error. We - // will continue to parse the rest of the patterns, but we will indicate - // it as an error. + // If we have already parsed a splat pattern, then this is an + // error. We will continue to parse the rest of the patterns, + // but we will indicate it as an error. if (trailing_rest) { pm_parser_err_previous(parser, PM_ERR_PATTERN_REST); } @@ -17082,20 +17082,28 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag pm_node_list_append(&nodes, node); } - // If the first pattern and the last pattern are rest patterns, then we will - // call this a find pattern, regardless of how many rest patterns are in - // between because we know we already added the appropriate errors. - // Otherwise we will create an array pattern. - if (PM_NODE_TYPE_P(nodes.nodes[0], PM_SPLAT_NODE) && PM_NODE_TYPE_P(nodes.nodes[nodes.size - 1], PM_SPLAT_NODE)) { + // If the first pattern and the last pattern are rest patterns, then we + // will call this a find pattern, regardless of how many rest patterns + // are in between because we know we already added the appropriate + // errors. Otherwise we will create an array pattern. + if (leading_rest && PM_NODE_TYPE_P(nodes.nodes[nodes.size - 1], PM_SPLAT_NODE)) { node = (pm_node_t *) pm_find_pattern_node_create(parser, &nodes); + + if (nodes.size == 2) { + pm_parser_err_node(parser, node, PM_ERR_PATTERN_FIND_MISSING_INNER); + } } else { node = (pm_node_t *) pm_array_pattern_node_node_list_create(parser, &nodes); + + if (leading_rest && trailing_rest) { + pm_parser_err_node(parser, node, PM_ERR_PATTERN_ARRAY_MULTIPLE_RESTS); + } } xfree(nodes.nodes); } else if (leading_rest) { - // Otherwise, if we parsed a single splat pattern, then we know we have an - // array pattern, so we can go ahead and create that node. + // Otherwise, if we parsed a single splat pattern, then we know we have + // an array pattern, so we can go ahead and create that node. node = (pm_node_t *) pm_array_pattern_node_rest_create(parser, node); } diff --git a/prism/templates/src/diagnostic.c.erb b/prism/templates/src/diagnostic.c.erb index f94f83a7a1..b09aca1c23 100644 --- a/prism/templates/src/diagnostic.c.erb +++ b/prism/templates/src/diagnostic.c.erb @@ -290,6 +290,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_PARAMETER_UNEXPECTED_FWD] = { "unexpected `...` in parameters", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PARAMETER_WILD_LOOSE_COMMA] = { "unexpected `,` in parameters", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PARAMETER_UNEXPECTED_NO_KW] = { "unexpected **nil; no keywords marker disallowed after keywords", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_ARRAY_MULTIPLE_RESTS] = { "unexpected multiple '*' rest patterns in an array pattern", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PATTERN_CAPTURE_DUPLICATE] = { "duplicated variable name", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET] = { "expected a pattern expression after the `[` operator", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA] = { "expected a pattern expression after `,`", PM_ERROR_LEVEL_SYNTAX }, @@ -301,6 +302,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_PATTERN_EXPRESSION_AFTER_PIPE] = { "expected a pattern expression after the `|` operator", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE] = { "expected a pattern expression after the range operator", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PATTERN_EXPRESSION_AFTER_REST] = { "unexpected pattern expression after the `**` expression", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_PATTERN_FIND_MISSING_INNER] = { "find patterns need at least one required inner pattern", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PATTERN_HASH_IMPLICIT] = { "unexpected implicit hash in pattern; use '{' to delineate", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PATTERN_HASH_KEY] = { "unexpected %s; expected a key in the hash pattern", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PATTERN_HASH_KEY_DUPLICATE] = { "duplicated key name", PM_ERROR_LEVEL_SYNTAX },