From 99a25b7f95b72162696812216008a050413fac0e Mon Sep 17 00:00:00 2001 From: Marco Roth Date: Mon, 23 Feb 2026 21:51:40 +0100 Subject: [PATCH] Parser: Improve ERB `case` node and `end` tag matching --- src/analyze.c | 2 +- test/parser/case_when_test.rb | 16 +++++ ..._when_4ec7986dc142768053964a35aca8fae4.txt | 34 ++++++++++ ..._when_9559f5a632c2f6780a6287b2b758f8c7.txt | 66 +++++++++++++++++++ 4 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 test/snapshots/parser/case_when_test/test_0008_case_without_when_4ec7986dc142768053964a35aca8fae4.txt create mode 100644 test/snapshots/parser/case_when_test/test_0009_case_with_single_when_9559f5a632c2f6780a6287b2b758f8c7.txt diff --git a/src/analyze.c b/src/analyze.c index 83ea6819b..9f84dd830 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -656,7 +656,7 @@ static size_t process_control_structure( AST_ERB_CONTENT_NODE_T* erb_content = (AST_ERB_CONTENT_NODE_T*) next_node; control_type_t next_type = detect_control_type(erb_content); - if (next_type == CONTROL_TYPE_WHEN || next_type == CONTROL_TYPE_IN) { break; } + if (next_type == CONTROL_TYPE_WHEN || next_type == CONTROL_TYPE_IN || next_type == CONTROL_TYPE_END) { break; } } hb_array_append(non_when_non_in_children, next_node); diff --git a/test/parser/case_when_test.rb b/test/parser/case_when_test.rb index d54a38780..bbf65173b 100644 --- a/test/parser/case_when_test.rb +++ b/test/parser/case_when_test.rb @@ -109,5 +109,21 @@ class CaseWhenTest < Minitest::Spec <% end %> HTML end + + test "case without when" do + assert_parsed_snapshot(<<~HTML) + <% case 1 %> + <% end %> + HTML + end + + test "case with single when" do + assert_parsed_snapshot(<<~HTML) + <% case 1 %> + <% when 1 %> +

one

+ <% end %> + HTML + end end end diff --git a/test/snapshots/parser/case_when_test/test_0008_case_without_when_4ec7986dc142768053964a35aca8fae4.txt b/test/snapshots/parser/case_when_test/test_0008_case_without_when_4ec7986dc142768053964a35aca8fae4.txt new file mode 100644 index 000000000..87bd0e679 --- /dev/null +++ b/test/snapshots/parser/case_when_test/test_0008_case_without_when_4ec7986dc142768053964a35aca8fae4.txt @@ -0,0 +1,34 @@ +--- +source: "Parser::CaseWhenTest#test_0008_case without when" +input: |2- +<% case 1 %> +<% end %> +--- +@ DocumentNode (location: (1:0)-(3:0)) +├── errors: (1 error) +│ └── @ RubyParseError (location: (1:3)-(1:7)) +│ ├── message: "case_missing_conditions: expected a `when` or `in` clause after `case`" +│ ├── error_message: "expected a `when` or `in` clause after `case`" +│ ├── diagnostic_id: "case_missing_conditions" +│ └── level: "syntax" +│ +└── children: (2 items) + ├── @ ERBCaseNode (location: (1:0)-(2:9)) + │ ├── tag_opening: "<%" (location: (1:0)-(1:2)) + │ ├── content: " case 1 " (location: (1:2)-(1:10)) + │ ├── tag_closing: "%>" (location: (1:10)-(1:12)) + │ ├── children: (1 item) + │ │ └── @ HTMLTextNode (location: (1:12)-(2:0)) + │ │ └── content: "\n" + │ │ + │ ├── conditions: [] + │ ├── else_clause: ∅ + │ └── end_node: + │ └── @ ERBEndNode (location: (2:0)-(2:9)) + │ ├── tag_opening: "<%" (location: (2:0)-(2:2)) + │ ├── content: " end " (location: (2:2)-(2:7)) + │ └── tag_closing: "%>" (location: (2:7)-(2:9)) + │ + │ + └── @ HTMLTextNode (location: (2:9)-(3:0)) + └── content: "\n" \ No newline at end of file diff --git a/test/snapshots/parser/case_when_test/test_0009_case_with_single_when_9559f5a632c2f6780a6287b2b758f8c7.txt b/test/snapshots/parser/case_when_test/test_0009_case_with_single_when_9559f5a632c2f6780a6287b2b758f8c7.txt new file mode 100644 index 000000000..684451cc2 --- /dev/null +++ b/test/snapshots/parser/case_when_test/test_0009_case_with_single_when_9559f5a632c2f6780a6287b2b758f8c7.txt @@ -0,0 +1,66 @@ +--- +source: "Parser::CaseWhenTest#test_0009_case with single when" +input: |2- +<% case 1 %> +<% when 1 %> +

one

+<% end %> +--- +@ DocumentNode (location: (1:0)-(5:0)) +└── children: (2 items) + ├── @ ERBCaseNode (location: (1:0)-(4:9)) + │ ├── tag_opening: "<%" (location: (1:0)-(1:2)) + │ ├── content: " case 1 " (location: (1:2)-(1:10)) + │ ├── tag_closing: "%>" (location: (1:10)-(1:12)) + │ ├── children: (1 item) + │ │ └── @ HTMLTextNode (location: (1:12)-(2:0)) + │ │ └── content: "\n" + │ │ + │ ├── conditions: (1 item) + │ │ └── @ ERBWhenNode (location: (2:0)-(2:12)) + │ │ ├── tag_opening: "<%" (location: (2:0)-(2:2)) + │ │ ├── content: " when 1 " (location: (2:2)-(2:10)) + │ │ ├── tag_closing: "%>" (location: (2:10)-(2:12)) + │ │ ├── then_keyword: ∅ + │ │ └── statements: (3 items) + │ │ ├── @ HTMLTextNode (location: (2:12)-(3:2)) + │ │ │ └── content: "\n " + │ │ │ + │ │ ├── @ HTMLElementNode (location: (3:2)-(3:12)) + │ │ │ ├── open_tag: + │ │ │ │ └── @ HTMLOpenTagNode (location: (3:2)-(3:5)) + │ │ │ │ ├── tag_opening: "<" (location: (3:2)-(3:3)) + │ │ │ │ ├── tag_name: "p" (location: (3:3)-(3:4)) + │ │ │ │ ├── tag_closing: ">" (location: (3:4)-(3:5)) + │ │ │ │ ├── children: [] + │ │ │ │ └── is_void: false + │ │ │ │ + │ │ │ ├── tag_name: "p" (location: (3:3)-(3:4)) + │ │ │ ├── body: (1 item) + │ │ │ │ └── @ HTMLTextNode (location: (3:5)-(3:8)) + │ │ │ │ └── content: "one" + │ │ │ │ + │ │ │ ├── close_tag: + │ │ │ │ └── @ HTMLCloseTagNode (location: (3:8)-(3:12)) + │ │ │ │ ├── tag_opening: "" (location: (3:11)-(3:12)) + │ │ │ │ + │ │ │ ├── is_void: false + │ │ │ └── source: "HTML" + │ │ │ + │ │ └── @ HTMLTextNode (location: (3:12)-(4:0)) + │ │ └── content: "\n" + │ │ + │ │ + │ ├── else_clause: ∅ + │ └── end_node: + │ └── @ ERBEndNode (location: (4:0)-(4:9)) + │ ├── tag_opening: "<%" (location: (4:0)-(4:2)) + │ ├── content: " end " (location: (4:2)-(4:7)) + │ └── tag_closing: "%>" (location: (4:7)-(4:9)) + │ + │ + └── @ HTMLTextNode (location: (4:9)-(5:0)) + └── content: "\n" \ No newline at end of file