diff --git a/snapshots/3.3-3.4/end_block_exit.txt b/snapshots/3.3-3.4/end_block_exit.txt new file mode 100644 index 0000000000..04e2c3b668 --- /dev/null +++ b/snapshots/3.3-3.4/end_block_exit.txt @@ -0,0 +1,33 @@ +@ ProgramNode (location: (1,0)-(7,1)) +├── flags: ∅ +├── locals: [] +└── statements: + @ StatementsNode (location: (1,0)-(7,1)) + ├── flags: ∅ + └── body: (length: 2) + ├── @ PostExecutionNode (location: (1,0)-(3,1)) + │ ├── flags: newline + │ ├── statements: + │ │ @ StatementsNode (location: (2,2)-(2,8)) + │ │ ├── flags: ∅ + │ │ └── body: (length: 1) + │ │ └── @ ReturnNode (location: (2,2)-(2,8)) + │ │ ├── flags: newline + │ │ ├── keyword_loc: (2,2)-(2,8) = "return" + │ │ └── arguments: ∅ + │ ├── keyword_loc: (1,0)-(1,3) = "END" + │ ├── opening_loc: (1,4)-(1,5) = "{" + │ └── closing_loc: (3,0)-(3,1) = "}" + └── @ PostExecutionNode (location: (5,0)-(7,1)) + ├── flags: newline + ├── statements: + │ @ StatementsNode (location: (6,2)-(6,7)) + │ ├── flags: ∅ + │ └── body: (length: 1) + │ └── @ BreakNode (location: (6,2)-(6,7)) + │ ├── flags: newline + │ ├── arguments: ∅ + │ └── keyword_loc: (6,2)-(6,7) = "break" + ├── keyword_loc: (5,0)-(5,3) = "END" + ├── opening_loc: (5,4)-(5,5) = "{" + └── closing_loc: (7,0)-(7,1) = "}" diff --git a/snapshots/end_block_exit.txt b/snapshots/end_block_exit.txt new file mode 100644 index 0000000000..4acc5f4218 --- /dev/null +++ b/snapshots/end_block_exit.txt @@ -0,0 +1,20 @@ +@ ProgramNode (location: (1,0)-(3,1)) +├── flags: ∅ +├── locals: [] +└── statements: + @ StatementsNode (location: (1,0)-(3,1)) + ├── flags: ∅ + └── body: (length: 1) + └── @ PostExecutionNode (location: (1,0)-(3,1)) + ├── flags: newline + ├── statements: + │ @ StatementsNode (location: (2,2)-(2,6)) + │ ├── flags: ∅ + │ └── body: (length: 1) + │ └── @ NextNode (location: (2,2)-(2,6)) + │ ├── flags: newline + │ ├── arguments: ∅ + │ └── keyword_loc: (2,2)-(2,6) = "next" + ├── keyword_loc: (1,0)-(1,3) = "END" + ├── opening_loc: (1,4)-(1,5) = "{" + └── closing_loc: (3,0)-(3,1) = "}" diff --git a/src/prism.c b/src/prism.c index a87932f1b7..f2df9cb947 100644 --- a/src/prism.c +++ b/src/prism.c @@ -15777,7 +15777,6 @@ parse_block_exit(pm_parser_t *parser, pm_node_t *node) { case PM_CONTEXT_LAMBDA_ENSURE: case PM_CONTEXT_LAMBDA_RESCUE: case PM_CONTEXT_LOOP_PREDICATE: - case PM_CONTEXT_POSTEXE: case PM_CONTEXT_UNTIL: case PM_CONTEXT_WHILE: // These are the good cases. We're allowed to have a block exit @@ -15790,10 +15789,21 @@ parse_block_exit(pm_parser_t *parser, pm_node_t *node) { case PM_CONTEXT_DEF_RESCUE: case PM_CONTEXT_MAIN: case PM_CONTEXT_PREEXE: + case PM_CONTEXT_POSTEXE: case PM_CONTEXT_SCLASS: case PM_CONTEXT_SCLASS_ELSE: case PM_CONTEXT_SCLASS_ENSURE: case PM_CONTEXT_SCLASS_RESCUE: + // https://bugs.ruby-lang.org/issues/20409 + if (context_node->context == PM_CONTEXT_POSTEXE) { + if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_0) { + return; + } + if (PM_NODE_TYPE_P(node, PM_NEXT_NODE)) { + return; + } + } + // These are the bad cases. We're not allowed to have a block // exit in these contexts. // diff --git a/test/prism/errors/4.0/end_block_exit.txt b/test/prism/errors/4.0/end_block_exit.txt new file mode 100644 index 0000000000..f8fc59d1a3 --- /dev/null +++ b/test/prism/errors/4.0/end_block_exit.txt @@ -0,0 +1,5 @@ +END { + break + ^~~~~ Invalid break +} + diff --git a/test/prism/errors_test.rb b/test/prism/errors_test.rb index 9abed92652..f54217e4fb 100644 --- a/test/prism/errors_test.rb +++ b/test/prism/errors_test.rb @@ -9,6 +9,11 @@ class ErrorsTest < TestCase base = File.expand_path("errors", __dir__) filepaths = Dir["**/*.txt", base: base] + PARSE_Y_EXCLUDES = [ + # https://bugs.ruby-lang.org/issues/20409 + "#{base}/3.5/end_block_exit.txt" + ] + filepaths.each do |filepath| ruby_versions_for(filepath).each do |version| define_method(:"test_#{version}_#{File.basename(filepath, ".txt")}") do @@ -70,7 +75,9 @@ def assert_errors(filepath, version) expected = File.read(filepath, binmode: true, external_encoding: Encoding::UTF_8) source = expected.lines.grep_v(/^\s*\^/).join.gsub(/\n*\z/, "") - refute_valid_syntax(source) if current_major_minor == version + if current_major_minor == version && !PARSE_Y_EXCLUDES.include?(filepath) + refute_valid_syntax(source) + end result = Prism.parse(source, version: version) errors = result.errors diff --git a/test/prism/fixtures/3.3-3.4/end_block_exit.txt b/test/prism/fixtures/3.3-3.4/end_block_exit.txt new file mode 100644 index 0000000000..53afa1e2f4 --- /dev/null +++ b/test/prism/fixtures/3.3-3.4/end_block_exit.txt @@ -0,0 +1,7 @@ +END { + return +} + +END { + break +} diff --git a/test/prism/fixtures/end_block_exit.txt b/test/prism/fixtures/end_block_exit.txt new file mode 100644 index 0000000000..1f64dcb6ed --- /dev/null +++ b/test/prism/fixtures/end_block_exit.txt @@ -0,0 +1,3 @@ +END { + next +}