Skip to content

Commit ced3b8d

Browse files
authored
Merge pull request #2144 from tenderlove/dup-param-flag
Add a "repeated flag" to parameter nodes
2 parents 3e36d5e + ed183ad commit ced3b8d

181 files changed

Lines changed: 1440 additions & 218 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

config.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,11 @@ flags:
378378
- name: BEGIN_MODIFIER
379379
comment: "a loop after a begin statement, so the body is executed first before the condition"
380380
comment: Flags for while and until loop nodes.
381+
- name: ParameterFlags
382+
values:
383+
- name: REPEATED_PARAMETER
384+
comment: "a parameter name that has been repeated in the method signature"
385+
comment: Flags for parameter nodes.
381386
- name: RangeFlags
382387
values:
383388
- name: EXCLUDE_END
@@ -648,6 +653,9 @@ nodes:
648653
^^^^^^^^^^
649654
- name: BlockLocalVariableNode
650655
fields:
656+
- name: flags
657+
type: flags
658+
kind: ParameterFlags
651659
- name: name
652660
type: constant
653661
comment: |
@@ -676,6 +684,9 @@ nodes:
676684
^^^^^^^^^^^^^^
677685
- name: BlockParameterNode
678686
fields:
687+
- name: flags
688+
type: flags
689+
kind: ParameterFlags
679690
- name: name
680691
type: constant?
681692
- name: name_loc
@@ -1923,6 +1934,9 @@ nodes:
19231934
^^^^
19241935
- name: KeywordRestParameterNode
19251936
fields:
1937+
- name: flags
1938+
type: flags
1939+
kind: ParameterFlags
19261940
- name: name
19271941
type: constant?
19281942
- name: name_loc
@@ -2221,6 +2235,9 @@ nodes:
22212235
^^
22222236
- name: OptionalKeywordParameterNode
22232237
fields:
2238+
- name: flags
2239+
type: flags
2240+
kind: ParameterFlags
22242241
- name: name
22252242
type: constant
22262243
- name: name_loc
@@ -2235,6 +2252,9 @@ nodes:
22352252
end
22362253
- name: OptionalParameterNode
22372254
fields:
2255+
- name: flags
2256+
type: flags
2257+
kind: ParameterFlags
22382258
- name: name
22392259
type: constant
22402260
- name: name_loc
@@ -2451,6 +2471,9 @@ nodes:
24512471
^^^^^^
24522472
- name: RequiredKeywordParameterNode
24532473
fields:
2474+
- name: flags
2475+
type: flags
2476+
kind: ParameterFlags
24542477
- name: name
24552478
type: constant
24562479
- name: name_loc
@@ -2463,6 +2486,9 @@ nodes:
24632486
end
24642487
- name: RequiredParameterNode
24652488
fields:
2489+
- name: flags
2490+
type: flags
2491+
kind: ParameterFlags
24662492
- name: name
24672493
type: constant
24682494
comment: |
@@ -2514,6 +2540,9 @@ nodes:
25142540
`ex` is in the `exception` field.
25152541
- name: RestParameterNode
25162542
fields:
2543+
- name: flags
2544+
type: flags
2545+
kind: ParameterFlags
25172546
- name: name
25182547
type: constant?
25192548
- name: name_loc

lib/prism/debug.rb

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,28 +138,32 @@ def self.prism_locals(source)
138138
*params.keywords.grep(OptionalKeywordParameterNode).map(&:name),
139139
]
140140

141+
sorted << AnonymousLocal if params.keywords.any?
142+
141143
if params.keyword_rest.is_a?(ForwardingParameterNode)
142144
sorted.push(:*, :&, :"...")
145+
elsif params.keyword_rest.is_a?(KeywordRestParameterNode)
146+
sorted << params.keyword_rest.name if params.keyword_rest.name
143147
end
144148

145-
sorted << AnonymousLocal if params.keywords.any?
146-
147149
# Recurse down the parameter tree to find any destructured
148150
# parameters and add them after the other parameters.
149151
param_stack = params.requireds.concat(params.posts).grep(MultiTargetNode).reverse
150152
while (param = param_stack.pop)
151153
case param
152154
when MultiTargetNode
153155
param_stack.concat(param.rights.reverse)
154-
param_stack << param.rest
156+
param_stack << param.rest if param.rest&.expression && !sorted.include?(param.rest.expression.name)
155157
param_stack.concat(param.lefts.reverse)
156158
when RequiredParameterNode
157159
sorted << param.name
158160
when SplatNode
159-
sorted << param.expression.name if param.expression
161+
sorted << param.expression.name
160162
end
161163
end
162164

165+
sorted << params.block.name if params.block&.name
166+
163167
names = sorted.concat(names - sorted)
164168
end
165169

src/prism.c

Lines changed: 71 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,22 @@ pm_node_flag_unset(pm_node_t *node, pm_node_flags_t flag) {
887887
node->flags &= (pm_node_flags_t) ~flag;
888888
}
889889

890+
/**
891+
* Set the repeated parameter flag on the given node.
892+
*/
893+
static inline void
894+
pm_node_flag_set_repeated_parameter(pm_node_t *node) {
895+
assert(PM_NODE_TYPE(node) == PM_BLOCK_LOCAL_VARIABLE_NODE ||
896+
PM_NODE_TYPE(node) == PM_BLOCK_PARAMETER_NODE ||
897+
PM_NODE_TYPE(node) == PM_KEYWORD_REST_PARAMETER_NODE ||
898+
PM_NODE_TYPE(node) == PM_OPTIONAL_KEYWORD_PARAMETER_NODE ||
899+
PM_NODE_TYPE(node) == PM_OPTIONAL_PARAMETER_NODE ||
900+
PM_NODE_TYPE(node) == PM_REQUIRED_KEYWORD_PARAMETER_NODE ||
901+
PM_NODE_TYPE(node) == PM_REQUIRED_PARAMETER_NODE ||
902+
PM_NODE_TYPE(node) == PM_REST_PARAMETER_NODE);
903+
904+
pm_node_flag_set(node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER);
905+
}
890906

891907
/******************************************************************************/
892908
/* Node creation functions */
@@ -5995,23 +6011,28 @@ pm_parser_local_add_owned(pm_parser_t *parser, const uint8_t *start, size_t leng
59956011
/**
59966012
* Add a parameter name to the current scope and check whether the name of the
59976013
* parameter is unique or not.
6014+
*
6015+
* Returns `true` if this is a duplicate parameter name, otherwise returns
6016+
* false.
59986017
*/
5999-
static void
6018+
static bool
60006019
pm_parser_parameter_name_check(pm_parser_t *parser, const pm_token_t *name) {
60016020
// We want to check whether the parameter name is a numbered parameter or
60026021
// not.
60036022
pm_refute_numbered_parameter(parser, name->start, name->end);
60046023

6005-
// We want to ignore any parameter name that starts with an underscore.
6006-
if ((name->start < name->end) && (*name->start == '_')) return;
6007-
60086024
// Otherwise we'll fetch the constant id for the parameter name and check
60096025
// whether it's already in the current scope.
60106026
pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, name);
60116027

60126028
if (pm_constant_id_list_includes(&parser->current_scope->locals, constant_id)) {
6013-
pm_parser_err_token(parser, name, PM_ERR_PARAMETER_NAME_REPEAT);
6029+
// Add an error if the parameter doesn't start with _ and has been seen before
6030+
if ((name->start < name->end) && (*name->start != '_')) {
6031+
pm_parser_err_token(parser, name, PM_ERR_PARAMETER_NAME_REPEAT);
6032+
}
6033+
return true;
60146034
}
6035+
return false;
60156036
}
60166037

60176038
/**
@@ -11466,7 +11487,9 @@ parse_required_destructured_parameter(pm_parser_t *parser) {
1146611487
if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
1146711488
pm_token_t name = parser->previous;
1146811489
value = (pm_node_t *) pm_required_parameter_node_create(parser, &name);
11469-
pm_parser_parameter_name_check(parser, &name);
11490+
if (pm_parser_parameter_name_check(parser, &name)) {
11491+
pm_node_flag_set_repeated_parameter(value);
11492+
}
1147011493
pm_parser_local_add_token(parser, &name);
1147111494
}
1147211495

@@ -11476,7 +11499,9 @@ parse_required_destructured_parameter(pm_parser_t *parser) {
1147611499
pm_token_t name = parser->previous;
1147711500

1147811501
param = (pm_node_t *) pm_required_parameter_node_create(parser, &name);
11479-
pm_parser_parameter_name_check(parser, &name);
11502+
if (pm_parser_parameter_name_check(parser, &name)) {
11503+
pm_node_flag_set_repeated_parameter(param);
11504+
}
1148011505
pm_parser_local_add_token(parser, &name);
1148111506
}
1148211507

@@ -11593,9 +11618,10 @@ parse_parameters(
1159311618
pm_token_t operator = parser->previous;
1159411619
pm_token_t name;
1159511620

11621+
bool repeated = false;
1159611622
if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
1159711623
name = parser->previous;
11598-
pm_parser_parameter_name_check(parser, &name);
11624+
repeated = pm_parser_parameter_name_check(parser, &name);
1159911625
pm_parser_local_add_token(parser, &name);
1160011626
} else {
1160111627
name = not_provided(parser);
@@ -11606,6 +11632,9 @@ parse_parameters(
1160611632
}
1160711633

1160811634
pm_block_parameter_node_t *param = pm_block_parameter_node_create(parser, &name, &operator);
11635+
if (repeated) {
11636+
pm_node_flag_set_repeated_parameter((pm_node_t *)param);
11637+
}
1160911638
if (params->block == NULL) {
1161011639
pm_parameters_node_block_set(params, param);
1161111640
} else {
@@ -11678,7 +11707,7 @@ parse_parameters(
1167811707
}
1167911708

1168011709
pm_token_t name = parser->previous;
11681-
pm_parser_parameter_name_check(parser, &name);
11710+
bool repeated = pm_parser_parameter_name_check(parser, &name);
1168211711
pm_parser_local_add_token(parser, &name);
1168311712

1168411713
if (accept1(parser, PM_TOKEN_EQUAL)) {
@@ -11689,6 +11718,9 @@ parse_parameters(
1168911718
pm_node_t *value = parse_value_expression(parser, binding_power, false, PM_ERR_PARAMETER_NO_DEFAULT);
1169011719

1169111720
pm_optional_parameter_node_t *param = pm_optional_parameter_node_create(parser, &name, &operator, value);
11721+
if (repeated) {
11722+
pm_node_flag_set_repeated_parameter((pm_node_t *)param);
11723+
}
1169211724
pm_parameters_node_optionals_append(params, param);
1169311725

1169411726
parser->current_param_name = old_param_name;
@@ -11703,9 +11735,15 @@ parse_parameters(
1170311735
}
1170411736
} else if (order > PM_PARAMETERS_ORDER_AFTER_OPTIONAL) {
1170511737
pm_required_parameter_node_t *param = pm_required_parameter_node_create(parser, &name);
11738+
if (repeated) {
11739+
pm_node_flag_set_repeated_parameter((pm_node_t *)param);
11740+
}
1170611741
pm_parameters_node_requireds_append(params, (pm_node_t *) param);
1170711742
} else {
1170811743
pm_required_parameter_node_t *param = pm_required_parameter_node_create(parser, &name);
11744+
if (repeated) {
11745+
pm_node_flag_set_repeated_parameter((pm_node_t *)param);
11746+
}
1170911747
pm_parameters_node_posts_append(params, (pm_node_t *) param);
1171011748
}
1171111749

@@ -11720,14 +11758,17 @@ parse_parameters(
1172011758
pm_token_t local = name;
1172111759
local.end -= 1;
1172211760

11723-
pm_parser_parameter_name_check(parser, &local);
11761+
bool repeated = pm_parser_parameter_name_check(parser, &local);
1172411762
pm_parser_local_add_token(parser, &local);
1172511763

1172611764
switch (parser->current.type) {
1172711765
case PM_TOKEN_COMMA:
1172811766
case PM_TOKEN_PARENTHESIS_RIGHT:
1172911767
case PM_TOKEN_PIPE: {
1173011768
pm_node_t *param = (pm_node_t *) pm_required_keyword_parameter_node_create(parser, &name);
11769+
if (repeated) {
11770+
pm_node_flag_set_repeated_parameter(param);
11771+
}
1173111772
pm_parameters_node_keywords_append(params, param);
1173211773
break;
1173311774
}
@@ -11739,6 +11780,9 @@ parse_parameters(
1173911780
}
1174011781

1174111782
pm_node_t *param = (pm_node_t *) pm_required_keyword_parameter_node_create(parser, &name);
11783+
if (repeated) {
11784+
pm_node_flag_set_repeated_parameter(param);
11785+
}
1174211786
pm_parameters_node_keywords_append(params, param);
1174311787
break;
1174411788
}
@@ -11758,6 +11802,9 @@ parse_parameters(
1175811802
param = (pm_node_t *) pm_required_keyword_parameter_node_create(parser, &name);
1175911803
}
1176011804

11805+
if (repeated) {
11806+
pm_node_flag_set_repeated_parameter(param);
11807+
}
1176111808
pm_parameters_node_keywords_append(params, param);
1176211809

1176311810
// If parsing the value of the parameter resulted in error recovery,
@@ -11780,10 +11827,10 @@ parse_parameters(
1178011827

1178111828
pm_token_t operator = parser->previous;
1178211829
pm_token_t name;
11783-
11830+
bool repeated = false;
1178411831
if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
1178511832
name = parser->previous;
11786-
pm_parser_parameter_name_check(parser, &name);
11833+
repeated = pm_parser_parameter_name_check(parser, &name);
1178711834
pm_parser_local_add_token(parser, &name);
1178811835
} else {
1178911836
name = not_provided(parser);
@@ -11794,6 +11841,9 @@ parse_parameters(
1179411841
}
1179511842

1179611843
pm_node_t *param = (pm_node_t *) pm_rest_parameter_node_create(parser, &operator, &name);
11844+
if (repeated) {
11845+
pm_node_flag_set_repeated_parameter(param);
11846+
}
1179711847
if (params->rest == NULL) {
1179811848
pm_parameters_node_rest_set(params, param);
1179911849
} else {
@@ -11816,9 +11866,10 @@ parse_parameters(
1181611866
} else {
1181711867
pm_token_t name;
1181811868

11869+
bool repeated = false;
1181911870
if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
1182011871
name = parser->previous;
11821-
pm_parser_parameter_name_check(parser, &name);
11872+
repeated = pm_parser_parameter_name_check(parser, &name);
1182211873
pm_parser_local_add_token(parser, &name);
1182311874
} else {
1182411875
name = not_provided(parser);
@@ -11829,6 +11880,9 @@ parse_parameters(
1182911880
}
1183011881

1183111882
param = (pm_node_t *) pm_keyword_rest_parameter_node_create(parser, &operator, &name);
11883+
if (repeated) {
11884+
pm_node_flag_set_repeated_parameter(param);
11885+
}
1183211886
}
1183311887

1183411888
if (params->keyword_rest == NULL) {
@@ -12064,10 +12118,13 @@ parse_block_parameters(
1206412118
if ((opening->type != PM_TOKEN_NOT_PROVIDED) && accept1(parser, PM_TOKEN_SEMICOLON)) {
1206512119
do {
1206612120
expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_BLOCK_PARAM_LOCAL_VARIABLE);
12067-
pm_parser_parameter_name_check(parser, &parser->previous);
12121+
bool repeated = pm_parser_parameter_name_check(parser, &parser->previous);
1206812122
pm_parser_local_add_token(parser, &parser->previous);
1206912123

1207012124
pm_block_local_variable_node_t *local = pm_block_local_variable_node_create(parser, &parser->previous);
12125+
if (repeated) {
12126+
pm_node_flag_set_repeated_parameter((pm_node_t *)local);
12127+
}
1207112128
pm_block_parameters_node_append_local(block_parameters, local);
1207212129
} while (accept1(parser, PM_TOKEN_COMMA));
1207312130
}

0 commit comments

Comments
 (0)