From fe9b42f024eb3724b0853c914916ea7a97fd30a6 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Fri, 15 Dec 2023 08:48:39 -0500 Subject: [PATCH] [ruby/prism] Invalid pinned locals in pattern matching https://github.com/ruby/prism/commit/3a67b37a56 --- prism/diagnostic.c | 1 + prism/diagnostic.h | 1 + prism/prism.c | 9 ++++++++- test/prism/fixtures/patterns.txt | 2 +- test/prism/location_test.rb | 2 +- test/prism/snapshots/patterns.txt | 24 ++++++++++++++++-------- 6 files changed, 28 insertions(+), 11 deletions(-) diff --git a/prism/diagnostic.c b/prism/diagnostic.c index b3a8282cdad1af..fd5dce5a04ce0c 100644 --- a/prism/diagnostic.c +++ b/prism/diagnostic.c @@ -192,6 +192,7 @@ static const char* const diagnostic_messages[PM_DIAGNOSTIC_ID_LEN] = { [PM_ERR_MODULE_TERM] = "expected an `end` to close the `module` statement", [PM_ERR_MULTI_ASSIGN_MULTI_SPLATS] = "multiple splats in multiple assignment", [PM_ERR_NOT_EXPRESSION] = "expected an expression after `not`", + [PM_ERR_NO_LOCAL_VARIABLE] = "%.*s: no such local variable", [PM_ERR_NUMBER_LITERAL_UNDERSCORE] = "number literal ending with a `_`", [PM_ERR_NUMBERED_PARAMETER_NOT_ALLOWED] = "numbered parameters are not allowed when an ordinary parameter is defined", [PM_ERR_NUMBERED_PARAMETER_OUTER_SCOPE] = "numbered parameter is already used in outer scope", diff --git a/prism/diagnostic.h b/prism/diagnostic.h index c88a704488c5b7..08a3284abff299 100644 --- a/prism/diagnostic.h +++ b/prism/diagnostic.h @@ -184,6 +184,7 @@ typedef enum { PM_ERR_MODULE_TERM, PM_ERR_MULTI_ASSIGN_MULTI_SPLATS, PM_ERR_NOT_EXPRESSION, + PM_ERR_NO_LOCAL_VARIABLE, PM_ERR_NUMBER_LITERAL_UNDERSCORE, PM_ERR_NUMBERED_PARAMETER_NOT_ALLOWED, PM_ERR_NUMBERED_PARAMETER_OUTER_SCOPE, diff --git a/prism/prism.c b/prism/prism.c index ec51bf06fa9acc..5746399ff78528 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -13352,8 +13352,15 @@ parse_pattern_primitive(pm_parser_t *parser, pm_diagnostic_id_t diag_id) { // expression to determine if it's a variable or an expression. switch (parser->current.type) { case PM_TOKEN_IDENTIFIER: { + int depth = pm_parser_local_depth(parser, &parser->current); + + if (depth == -1) { + depth = 0; + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_NO_LOCAL_VARIABLE, (int) (parser->current.end - parser->current.start), parser->current.start); + } + + pm_node_t *variable = (pm_node_t *) pm_local_variable_read_node_create(parser, &parser->current, (uint32_t) depth); parser_lex(parser); - pm_node_t *variable = (pm_node_t *) pm_local_variable_read_node_create(parser, &parser->previous, 0); return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable); } diff --git a/test/prism/fixtures/patterns.txt b/test/prism/fixtures/patterns.txt index d73c8d025e04d4..dcd6786d4b65c0 100644 --- a/test/prism/fixtures/patterns.txt +++ b/test/prism/fixtures/patterns.txt @@ -51,7 +51,7 @@ foo => __LINE__ .. __LINE__ foo => __ENCODING__ .. __ENCODING__ foo => -> { bar } .. -> { bar } -foo => ^bar +bar = 1; foo => ^bar foo => ^@bar foo => ^@@bar foo => ^$bar diff --git a/test/prism/location_test.rb b/test/prism/location_test.rb index 809c1bd8ae5f05..e5b75469255e45 100644 --- a/test/prism/location_test.rb +++ b/test/prism/location_test.rb @@ -673,7 +673,7 @@ def test_PinnedExpressionNode end def test_PinnedVariableNode - assert_location(PinnedVariableNode, "foo in ^bar", 7...11, &:pattern) + assert_location(PinnedVariableNode, "bar = 1; foo in ^bar", 16...20, &:pattern) end def test_PostExecutionNode diff --git a/test/prism/snapshots/patterns.txt b/test/prism/snapshots/patterns.txt index aacdbb250e4c96..13179be338ddc3 100644 --- a/test/prism/snapshots/patterns.txt +++ b/test/prism/snapshots/patterns.txt @@ -2,7 +2,7 @@ ├── locals: [:bar, :baz, :qux, :b, :a, :foo, :x] └── statements: @ StatementsNode (location: (1,0)-(202,19)) - └── body: (length: 175) + └── body: (length: 176) ├── @ MatchRequiredNode (location: (1,0)-(1,10)) │ ├── value: │ │ @ CallNode (location: (1,0)-(1,3)) @@ -1235,26 +1235,34 @@ │ │ │ └── depth: 1 │ │ └── operator_loc: (52,18)-(52,20) = ".." │ └── operator_loc: (52,4)-(52,6) = "=>" - ├── @ MatchRequiredNode (location: (54,0)-(54,11)) + ├── @ LocalVariableWriteNode (location: (54,0)-(54,7)) + │ ├── name: :bar + │ ├── depth: 0 + │ ├── name_loc: (54,0)-(54,3) = "bar" │ ├── value: - │ │ @ CallNode (location: (54,0)-(54,3)) + │ │ @ IntegerNode (location: (54,6)-(54,7)) + │ │ └── flags: decimal + │ └── operator_loc: (54,4)-(54,5) = "=" + ├── @ MatchRequiredNode (location: (54,9)-(54,20)) + │ ├── value: + │ │ @ CallNode (location: (54,9)-(54,12)) │ │ ├── flags: variable_call │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :foo - │ │ ├── message_loc: (54,0)-(54,3) = "foo" + │ │ ├── message_loc: (54,9)-(54,12) = "foo" │ │ ├── opening_loc: ∅ │ │ ├── arguments: ∅ │ │ ├── closing_loc: ∅ │ │ └── block: ∅ │ ├── pattern: - │ │ @ PinnedVariableNode (location: (54,7)-(54,11)) + │ │ @ PinnedVariableNode (location: (54,16)-(54,20)) │ │ ├── variable: - │ │ │ @ LocalVariableReadNode (location: (54,8)-(54,11)) + │ │ │ @ LocalVariableReadNode (location: (54,17)-(54,20)) │ │ │ ├── name: :bar │ │ │ └── depth: 0 - │ │ └── operator_loc: (54,7)-(54,8) = "^" - │ └── operator_loc: (54,4)-(54,6) = "=>" + │ │ └── operator_loc: (54,16)-(54,17) = "^" + │ └── operator_loc: (54,13)-(54,15) = "=>" ├── @ MatchRequiredNode (location: (55,0)-(55,12)) │ ├── value: │ │ @ CallNode (location: (55,0)-(55,3))