@@ -11676,11 +11676,32 @@ parse_statements(pm_parser_t *parser, pm_context_t context) {
11676
11676
return statements;
11677
11677
}
11678
11678
11679
+ /**
11680
+ * Add a node to a set of static literals that holds a set of hash keys. If the
11681
+ * node is a duplicate, then add an appropriate warning.
11682
+ */
11683
+ static void
11684
+ pm_hash_key_static_literals_add(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node) {
11685
+ const pm_node_t *duplicated = pm_static_literals_add(parser, literals, node);
11686
+
11687
+ if (duplicated != NULL) {
11688
+ pm_diagnostic_list_append_format(
11689
+ &parser->warning_list,
11690
+ duplicated->location.start,
11691
+ duplicated->location.end,
11692
+ PM_WARN_DUPLICATED_HASH_KEY,
11693
+ (int) (duplicated->location.end - duplicated->location.start),
11694
+ duplicated->location.start,
11695
+ pm_newline_list_line_column(&parser->newline_list, node->location.start, parser->start_line).line
11696
+ );
11697
+ }
11698
+ }
11699
+
11679
11700
/**
11680
11701
* Parse all of the elements of a hash. returns true if a double splat was found.
11681
11702
*/
11682
11703
static bool
11683
- parse_assocs(pm_parser_t *parser, pm_node_t *node) {
11704
+ parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node) {
11684
11705
assert(PM_NODE_TYPE_P(node, PM_HASH_NODE) || PM_NODE_TYPE_P(node, PM_KEYWORD_HASH_NODE));
11685
11706
bool contains_keyword_splat = false;
11686
11707
@@ -11709,6 +11730,8 @@ parse_assocs(pm_parser_t *parser, pm_node_t *node) {
11709
11730
parser_lex(parser);
11710
11731
11711
11732
pm_node_t *key = (pm_node_t *) pm_symbol_node_label_create(parser, &label);
11733
+ pm_hash_key_static_literals_add(parser, literals, key);
11734
+
11712
11735
pm_token_t operator = not_provided(parser);
11713
11736
pm_node_t *value = NULL;
11714
11737
@@ -11738,8 +11761,16 @@ parse_assocs(pm_parser_t *parser, pm_node_t *node) {
11738
11761
}
11739
11762
default: {
11740
11763
pm_node_t *key = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_HASH_KEY);
11741
- pm_token_t operator;
11742
11764
11765
+ // Hash keys that are strings are automatically frozen. We will
11766
+ // mark that here.
11767
+ if (PM_NODE_TYPE_P(key, PM_STRING_NODE)) {
11768
+ pm_node_flag_set(key, PM_STRING_FLAGS_FROZEN | PM_NODE_FLAG_STATIC_LITERAL);
11769
+ }
11770
+
11771
+ pm_hash_key_static_literals_add(parser, literals, key);
11772
+
11773
+ pm_token_t operator;
11743
11774
if (pm_symbol_node_label_p(key)) {
11744
11775
operator = not_provided(parser);
11745
11776
} else {
@@ -11773,6 +11804,7 @@ parse_assocs(pm_parser_t *parser, pm_node_t *node) {
11773
11804
// Otherwise by default we will exit out of this loop.
11774
11805
break;
11775
11806
}
11807
+
11776
11808
return contains_keyword_splat;
11777
11809
}
11778
11810
@@ -11830,12 +11862,17 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for
11830
11862
pm_keyword_hash_node_t *hash = pm_keyword_hash_node_create(parser);
11831
11863
argument = (pm_node_t *) hash;
11832
11864
11833
- bool contains_keyword_splat = parse_assocs(parser, (pm_node_t *) hash);
11834
- parsed_bare_hash = true;
11865
+ pm_static_literals_t literals = { 0 };
11866
+ bool contains_keyword_splat = parse_assocs(parser, &literals, (pm_node_t *) hash);
11867
+
11835
11868
parse_arguments_append(parser, arguments, argument);
11836
11869
if (contains_keyword_splat) {
11837
11870
pm_node_flag_set((pm_node_t *)arguments->arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT);
11838
11871
}
11872
+
11873
+ pm_static_literals_free(&literals);
11874
+ parsed_bare_hash = true;
11875
+
11839
11876
break;
11840
11877
}
11841
11878
case PM_TOKEN_UAMPERSAND: {
@@ -11925,10 +11962,14 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for
11925
11962
11926
11963
pm_keyword_hash_node_t *bare_hash = pm_keyword_hash_node_create(parser);
11927
11964
11965
+ // Create the set of static literals for this hash.
11966
+ pm_static_literals_t literals = { 0 };
11967
+ pm_hash_key_static_literals_add(parser, &literals, argument);
11968
+
11928
11969
// Finish parsing the one we are part way through
11929
11970
pm_node_t *value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_HASH_VALUE);
11930
-
11931
11971
argument = (pm_node_t *) pm_assoc_node_create(parser, argument, &operator, value);
11972
+
11932
11973
pm_keyword_hash_node_elements_append(bare_hash, argument);
11933
11974
argument = (pm_node_t *) bare_hash;
11934
11975
@@ -11937,9 +11978,10 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for
11937
11978
token_begins_expression_p(parser->current.type) ||
11938
11979
match2(parser, PM_TOKEN_USTAR_STAR, PM_TOKEN_LABEL)
11939
11980
)) {
11940
- contains_keyword_splat = parse_assocs(parser, (pm_node_t *) bare_hash);
11981
+ contains_keyword_splat = parse_assocs(parser, &literals, (pm_node_t *) bare_hash);
11941
11982
}
11942
11983
11984
+ pm_static_literals_free(&literals);
11943
11985
parsed_bare_hash = true;
11944
11986
} else if (accept1(parser, PM_TOKEN_KEYWORD_IN)) {
11945
11987
// TODO: Could we solve this with binding powers instead?
@@ -14661,13 +14703,14 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
14661
14703
pm_parser_err_current(parser, PM_ERR_EXPRESSION_BARE_HASH);
14662
14704
}
14663
14705
14664
- pm_keyword_hash_node_t *hash = pm_keyword_hash_node_create(parser);
14665
- element = (pm_node_t *)hash ;
14706
+ element = (pm_node_t *) pm_keyword_hash_node_create(parser);
14707
+ pm_static_literals_t literals = { 0 } ;
14666
14708
14667
14709
if (!match8(parser, PM_TOKEN_EOF, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_EOF, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_KEYWORD_DO, PM_TOKEN_PARENTHESIS_RIGHT)) {
14668
- parse_assocs(parser, (pm_node_t *) hash );
14710
+ parse_assocs(parser, &literals, element );
14669
14711
}
14670
14712
14713
+ pm_static_literals_free(&literals);
14671
14714
parsed_bare_hash = true;
14672
14715
} else {
14673
14716
element = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_ARRAY_EXPRESSION);
@@ -14678,6 +14721,8 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
14678
14721
}
14679
14722
14680
14723
pm_keyword_hash_node_t *hash = pm_keyword_hash_node_create(parser);
14724
+ pm_static_literals_t literals = { 0 };
14725
+ pm_hash_key_static_literals_add(parser, &literals, element);
14681
14726
14682
14727
pm_token_t operator;
14683
14728
if (parser->previous.type == PM_TOKEN_EQUAL_GREATER) {
@@ -14690,11 +14735,12 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
14690
14735
pm_node_t *assoc = (pm_node_t *) pm_assoc_node_create(parser, element, &operator, value);
14691
14736
pm_keyword_hash_node_elements_append(hash, assoc);
14692
14737
14693
- element = (pm_node_t *)hash;
14738
+ element = (pm_node_t *) hash;
14694
14739
if (accept1(parser, PM_TOKEN_COMMA) && !match1(parser, PM_TOKEN_BRACKET_RIGHT)) {
14695
- parse_assocs(parser, (pm_node_t *) hash );
14740
+ parse_assocs(parser, &literals, element );
14696
14741
}
14697
14742
14743
+ pm_static_literals_free(&literals);
14698
14744
parsed_bare_hash = true;
14699
14745
}
14700
14746
}
@@ -14840,17 +14886,20 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
14840
14886
case PM_TOKEN_BRACE_LEFT: {
14841
14887
pm_accepts_block_stack_push(parser, true);
14842
14888
parser_lex(parser);
14889
+
14843
14890
pm_hash_node_t *node = pm_hash_node_create(parser, &parser->previous);
14891
+ pm_static_literals_t literals = { 0 };
14844
14892
14845
14893
if (!match2(parser, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_EOF)) {
14846
- parse_assocs(parser, (pm_node_t *) node);
14894
+ parse_assocs(parser, &literals, (pm_node_t *) node);
14847
14895
accept1(parser, PM_TOKEN_NEWLINE);
14848
14896
}
14849
14897
14850
14898
pm_accepts_block_stack_pop(parser);
14851
14899
expect1(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_HASH_TERM);
14852
14900
pm_hash_node_closing_loc_set(node, &parser->previous);
14853
14901
14902
+ pm_static_literals_free(&literals);
14854
14903
return (pm_node_t *) node;
14855
14904
}
14856
14905
case PM_TOKEN_CHARACTER_LITERAL: {
0 commit comments