Skip to content

Commit 1aaf60e

Browse files
authored
Fix Prism locations of ConstPattern (sorbet#9578)
* Add test for ConstPatterns * Fix ArrayPattern locations * Fix HashPattern locations
1 parent 4c9f835 commit 1aaf60e

File tree

7 files changed

+918
-150
lines changed

7 files changed

+918
-150
lines changed

parser/prism/Translator.cc

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3671,23 +3671,39 @@ unique_ptr<parser::Node> Translator::patternTranslate(pm_node_t *node) {
36713671

36723672
unique_ptr<parser::Node> arrayPattern = nullptr;
36733673

3674-
// When the pattern ends with an implicit rest node, we need to return an `ArrayPatternWithTail` instead
3675-
if (prismRestNode != nullptr && PM_NODE_TYPE_P(prismRestNode, PM_IMPLICIT_REST_NODE)) {
3676-
arrayPattern = make_unique<parser::ArrayPatternWithTail>(location, move(sorbetElements));
3677-
} else {
3678-
arrayPattern = make_unique<parser::ArrayPattern>(location, move(sorbetElements));
3679-
}
3680-
36813674
if (auto *prismConstant = arrayPatternNode->constant) {
36823675
// An array pattern can start with a constant that matches against a specific type,
3683-
// rather than any value whose `#deconstruct` results are matched by the pattern
3676+
// (rather than any value whose `#deconstruct` results are matched by the pattern).
36843677
// E.g. the `Point` in `in Point[1, 2]`
36853678
auto sorbetConstant = translate(prismConstant);
36863679

3680+
ENFORCE(arrayPatternNode->opening_loc.start && arrayPatternNode->closing_loc.end,
3681+
"Array pattern without parentheses or square brackets?");
3682+
3683+
// The `ArrayPattern` loc doesn't include the constant, if there is one.
3684+
// `Point[x: Integer => 1, y: Integer => 2]`
3685+
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ArrayPattern loc
3686+
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ConstPattern loc
3687+
auto arrayPatternLoc =
3688+
translateLoc(arrayPatternNode->opening_loc.start, arrayPatternNode->closing_loc.end);
3689+
3690+
// When the pattern ends with an implicit rest node, we need to return an `ArrayPatternWithTail`
3691+
// instead
3692+
if (prismRestNode != nullptr && PM_NODE_TYPE_P(prismRestNode, PM_IMPLICIT_REST_NODE)) {
3693+
arrayPattern = make_unique<parser::ArrayPatternWithTail>(arrayPatternLoc, move(sorbetElements));
3694+
} else {
3695+
arrayPattern = make_unique<parser::ArrayPattern>(arrayPatternLoc, move(sorbetElements));
3696+
}
3697+
36873698
return make_unique<parser::ConstPattern>(location, move(sorbetConstant), move(arrayPattern));
36883699
}
36893700

3690-
return arrayPattern;
3701+
// When the pattern ends with an implicit rest node, we need to return an `ArrayPatternWithTail` instead
3702+
if (prismRestNode != nullptr && PM_NODE_TYPE_P(prismRestNode, PM_IMPLICIT_REST_NODE)) {
3703+
return make_unique<parser::ArrayPatternWithTail>(location, move(sorbetElements));
3704+
} else {
3705+
return make_unique<parser::ArrayPattern>(location, move(sorbetElements));
3706+
}
36913707
}
36923708
case PM_CAPTURE_PATTERN_NODE: { // A variable capture such as the `Integer => i` in `in Integer => i`
36933709
auto capturePatternNode = down_cast<pm_capture_pattern_node>(node);
@@ -3755,18 +3771,28 @@ unique_ptr<parser::Node> Translator::patternTranslate(pm_node_t *node) {
37553771
}
37563772
}
37573773

3758-
auto hashPattern = make_unique<parser::HashPattern>(location, move(sorbetElements));
3759-
37603774
if (auto *prismConstant = hashPatternNode->constant) {
37613775
// A hash pattern can start with a constant that matches against a specific type,
3762-
// rather than any value whose `#deconstruct_keys` results are matched by the pattern
3776+
// (rather than any value whose `#deconstruct_keys` results are matched by the pattern).
37633777
// E.g. the `Point` in `in Point[x: Integer => 1, y: Integer => 2]`
37643778
auto sorbetConstant = translate(prismConstant);
37653779

3780+
ENFORCE(hashPatternNode->opening_loc.start && hashPatternNode->closing_loc.end,
3781+
"Hash pattern without parentheses or square brackets?");
3782+
3783+
// The `HashPattern` loc doesn't include the constant, if there is one.
3784+
// `Point[x: Integer => 1, y: Integer => 2]`
3785+
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ HashPattern loc
3786+
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ConstPattern loc
3787+
auto hashPatternLoc =
3788+
translateLoc(hashPatternNode->opening_loc.start, hashPatternNode->closing_loc.end);
3789+
3790+
auto hashPattern = make_unique<parser::HashPattern>(hashPatternLoc, move(sorbetElements));
3791+
37663792
return make_unique<parser::ConstPattern>(location, move(sorbetConstant), move(hashPattern));
37673793
}
37683794

3769-
return hashPattern;
3795+
return make_unique<parser::HashPattern>(location, move(sorbetElements));
37703796
}
37713797
case PM_IMPLICIT_NODE: {
37723798
auto implicitNode = down_cast<pm_implicit_node>(node);

test/prism_regression/case_match.parse-tree.exp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,28 @@ Begin {
211211
]
212212
}
213213
}
214+
InPattern {
215+
pattern = ConstPattern {
216+
scope = Const {
217+
scope = NULL
218+
name = <C <U Array>>
219+
}
220+
pattern = ArrayPattern {
221+
elts = [
222+
]
223+
}
224+
}
225+
guard = NULL
226+
body = Send {
227+
receiver = NULL
228+
method = <U puts>
229+
args = [
230+
String {
231+
val = <U empty!>
232+
}
233+
]
234+
}
235+
}
214236
InPattern {
215237
pattern = ConstPattern {
216238
scope = Const {
@@ -583,6 +605,28 @@ Begin {
583605
]
584606
}
585607
}
608+
InPattern {
609+
pattern = ConstPattern {
610+
scope = Const {
611+
scope = NULL
612+
name = <C <U Hash>>
613+
}
614+
pattern = ArrayPattern {
615+
elts = [
616+
]
617+
}
618+
}
619+
guard = NULL
620+
body = Send {
621+
receiver = NULL
622+
method = <U puts>
623+
args = [
624+
String {
625+
val = <U empty!>
626+
}
627+
]
628+
}
629+
}
586630
InPattern {
587631
pattern = ConstPattern {
588632
scope = Const {

test/prism_regression/case_match.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
puts "ends with six!"
2525
in [*, 7, *] # A "find pattern"
2626
puts "contains a seven!"
27+
in Array[] # Empty ArrayPattern inside a ConstPattern
28+
puts "empty!"
2729
in Array[first, second] # Requires the `array_like_thing` to be an `Array` specifically
2830
puts "An Array with first: #{first} and second: #{second}"
2931
in Point[x, y] # Requires the `array_like_thing` to be a `Point` specifically
@@ -45,6 +47,8 @@
4547
puts "#{m} has j or l!"
4648
in {"n1":, n2:, "n3":} => n4
4749
puts "#{n4} has n1, n2, and n3!"
50+
in Hash[] # This is still an empty ArrayPattern inside a ConstPattern, regardless of the constant being `Hash`.
51+
puts "empty!"
4852
in Hash[e: 5 => e] # Requires the `hash_like_thing` to be a `Hash` specifically
4953
puts "A Hash with e: #{e}"
5054
in Point[x: 6 => x, y: 7 => y] # Requires the `hash_like_thing` to be a `Point` specifically
@@ -81,3 +85,5 @@
8185
in c, d; c unless c == 3
8286
"in with 2 args, semicolon, and unless"
8387
end
88+
89+
# TODO: test ConstPattern with `()`, e.g. `Constant(1, 2)` and `Constant(k: v)`

0 commit comments

Comments
 (0)