@herb-tools/node 0.8.9 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/binding.gyp +26 -8
  3. package/dist/herb-node.cjs +41 -12
  4. package/dist/herb-node.cjs.map +1 -1
  5. package/dist/herb-node.esm.js +8 -1
  6. package/dist/herb-node.esm.js.map +1 -1
  7. package/dist/types/node-backend.d.ts +3 -1
  8. package/extension/error_helpers.cpp +419 -71
  9. package/extension/error_helpers.h +14 -3
  10. package/extension/extension_helpers.cpp +38 -35
  11. package/extension/extension_helpers.h +2 -2
  12. package/extension/herb.cpp +183 -64
  13. package/extension/libherb/analyze/action_view/attribute_extraction_helpers.c +290 -0
  14. package/extension/libherb/analyze/action_view/attribute_extraction_helpers.h +36 -0
  15. package/extension/libherb/analyze/action_view/content_tag.c +70 -0
  16. package/extension/libherb/analyze/action_view/link_to.c +143 -0
  17. package/extension/libherb/analyze/action_view/registry.c +60 -0
  18. package/extension/libherb/analyze/action_view/tag.c +64 -0
  19. package/extension/libherb/analyze/action_view/tag_helper_handler.h +41 -0
  20. package/extension/libherb/analyze/action_view/tag_helper_node_builders.c +305 -0
  21. package/extension/libherb/analyze/action_view/tag_helper_node_builders.h +70 -0
  22. package/extension/libherb/analyze/action_view/tag_helpers.c +748 -0
  23. package/extension/libherb/analyze/action_view/tag_helpers.h +38 -0
  24. package/extension/libherb/analyze/action_view/turbo_frame_tag.c +88 -0
  25. package/extension/libherb/analyze/analyze.c +882 -0
  26. package/extension/libherb/{include → analyze}/analyze.h +14 -4
  27. package/extension/libherb/{analyzed_ruby.c → analyze/analyzed_ruby.c} +13 -11
  28. package/extension/libherb/{analyzed_ruby.h → analyze/analyzed_ruby.h} +3 -3
  29. package/extension/libherb/analyze/builders.c +343 -0
  30. package/extension/libherb/analyze/builders.h +27 -0
  31. package/extension/libherb/analyze/conditional_elements.c +594 -0
  32. package/extension/libherb/analyze/conditional_elements.h +9 -0
  33. package/extension/libherb/analyze/conditional_open_tags.c +640 -0
  34. package/extension/libherb/analyze/conditional_open_tags.h +9 -0
  35. package/extension/libherb/analyze/control_type.c +250 -0
  36. package/extension/libherb/analyze/control_type.h +14 -0
  37. package/extension/libherb/{analyze_helpers.c → analyze/helpers.c} +79 -31
  38. package/extension/libherb/{analyze_helpers.h → analyze/helpers.h} +22 -17
  39. package/extension/libherb/analyze/invalid_structures.c +193 -0
  40. package/extension/libherb/analyze/invalid_structures.h +11 -0
  41. package/extension/libherb/{analyze_missing_end.c → analyze/missing_end.c} +33 -22
  42. package/extension/libherb/analyze/parse_errors.c +84 -0
  43. package/extension/libherb/analyze/prism_annotate.c +397 -0
  44. package/extension/libherb/analyze/prism_annotate.h +16 -0
  45. package/extension/libherb/{analyze_transform.c → analyze/transform.c} +17 -3
  46. package/extension/libherb/ast_node.c +17 -7
  47. package/extension/libherb/ast_node.h +11 -5
  48. package/extension/libherb/ast_nodes.c +663 -388
  49. package/extension/libherb/ast_nodes.h +118 -39
  50. package/extension/libherb/ast_pretty_print.c +191 -7
  51. package/extension/libherb/ast_pretty_print.h +6 -1
  52. package/extension/libherb/element_source.h +3 -8
  53. package/extension/libherb/errors.c +1100 -507
  54. package/extension/libherb/errors.h +155 -54
  55. package/extension/libherb/extract.c +148 -49
  56. package/extension/libherb/extract.h +21 -5
  57. package/extension/libherb/herb.c +52 -34
  58. package/extension/libherb/herb.h +18 -6
  59. package/extension/libherb/herb_prism_node.h +13 -0
  60. package/extension/libherb/html_util.c +241 -12
  61. package/extension/libherb/html_util.h +7 -2
  62. package/extension/libherb/include/analyze/action_view/attribute_extraction_helpers.h +36 -0
  63. package/extension/libherb/include/analyze/action_view/tag_helper_handler.h +41 -0
  64. package/extension/libherb/include/analyze/action_view/tag_helper_node_builders.h +70 -0
  65. package/extension/libherb/include/analyze/action_view/tag_helpers.h +38 -0
  66. package/extension/libherb/{analyze.h → include/analyze/analyze.h} +14 -4
  67. package/extension/libherb/include/{analyzed_ruby.h → analyze/analyzed_ruby.h} +3 -3
  68. package/extension/libherb/include/analyze/builders.h +27 -0
  69. package/extension/libherb/include/analyze/conditional_elements.h +9 -0
  70. package/extension/libherb/include/analyze/conditional_open_tags.h +9 -0
  71. package/extension/libherb/include/analyze/control_type.h +14 -0
  72. package/extension/libherb/include/{analyze_helpers.h → analyze/helpers.h} +22 -17
  73. package/extension/libherb/include/analyze/invalid_structures.h +11 -0
  74. package/extension/libherb/include/analyze/prism_annotate.h +16 -0
  75. package/extension/libherb/include/ast_node.h +11 -5
  76. package/extension/libherb/include/ast_nodes.h +118 -39
  77. package/extension/libherb/include/ast_pretty_print.h +6 -1
  78. package/extension/libherb/include/element_source.h +3 -8
  79. package/extension/libherb/include/errors.h +155 -54
  80. package/extension/libherb/include/extract.h +21 -5
  81. package/extension/libherb/include/herb.h +18 -6
  82. package/extension/libherb/include/herb_prism_node.h +13 -0
  83. package/extension/libherb/include/html_util.h +7 -2
  84. package/extension/libherb/include/io.h +3 -1
  85. package/extension/libherb/include/lex_helpers.h +29 -0
  86. package/extension/libherb/include/lexer.h +1 -1
  87. package/extension/libherb/include/lexer_peek_helpers.h +87 -13
  88. package/extension/libherb/include/lexer_struct.h +2 -0
  89. package/extension/libherb/include/location.h +2 -1
  90. package/extension/libherb/include/parser.h +27 -2
  91. package/extension/libherb/include/parser_helpers.h +19 -3
  92. package/extension/libherb/include/pretty_print.h +10 -5
  93. package/extension/libherb/include/prism_context.h +45 -0
  94. package/extension/libherb/include/prism_helpers.h +10 -7
  95. package/extension/libherb/include/prism_serialized.h +12 -0
  96. package/extension/libherb/include/token.h +16 -4
  97. package/extension/libherb/include/token_struct.h +10 -3
  98. package/extension/libherb/include/utf8.h +2 -1
  99. package/extension/libherb/include/util/hb_allocator.h +78 -0
  100. package/extension/libherb/include/util/hb_arena.h +6 -1
  101. package/extension/libherb/include/util/hb_arena_debug.h +12 -1
  102. package/extension/libherb/include/util/hb_array.h +7 -3
  103. package/extension/libherb/include/util/hb_buffer.h +6 -4
  104. package/extension/libherb/include/util/hb_foreach.h +79 -0
  105. package/extension/libherb/include/util/hb_narray.h +8 -4
  106. package/extension/libherb/include/util/hb_string.h +56 -9
  107. package/extension/libherb/include/util/string.h +11 -0
  108. package/extension/libherb/include/util.h +6 -3
  109. package/extension/libherb/include/version.h +1 -1
  110. package/extension/libherb/io.c +3 -2
  111. package/extension/libherb/io.h +3 -1
  112. package/extension/libherb/lex_helpers.h +29 -0
  113. package/extension/libherb/lexer.c +42 -30
  114. package/extension/libherb/lexer.h +1 -1
  115. package/extension/libherb/lexer_peek_helpers.c +12 -74
  116. package/extension/libherb/lexer_peek_helpers.h +87 -13
  117. package/extension/libherb/lexer_struct.h +2 -0
  118. package/extension/libherb/location.c +2 -2
  119. package/extension/libherb/location.h +2 -1
  120. package/extension/libherb/main.c +79 -66
  121. package/extension/libherb/parser.c +784 -247
  122. package/extension/libherb/parser.h +27 -2
  123. package/extension/libherb/parser_helpers.c +110 -23
  124. package/extension/libherb/parser_helpers.h +19 -3
  125. package/extension/libherb/parser_match_tags.c +110 -49
  126. package/extension/libherb/pretty_print.c +29 -24
  127. package/extension/libherb/pretty_print.h +10 -5
  128. package/extension/libherb/prism_context.h +45 -0
  129. package/extension/libherb/prism_helpers.c +30 -27
  130. package/extension/libherb/prism_helpers.h +10 -7
  131. package/extension/libherb/prism_serialized.h +12 -0
  132. package/extension/libherb/ruby_parser.c +2 -0
  133. package/extension/libherb/token.c +151 -66
  134. package/extension/libherb/token.h +16 -4
  135. package/extension/libherb/token_matchers.c +0 -1
  136. package/extension/libherb/token_struct.h +10 -3
  137. package/extension/libherb/utf8.c +7 -6
  138. package/extension/libherb/utf8.h +2 -1
  139. package/extension/libherb/util/hb_allocator.c +341 -0
  140. package/extension/libherb/util/hb_allocator.h +78 -0
  141. package/extension/libherb/util/hb_arena.c +81 -56
  142. package/extension/libherb/util/hb_arena.h +6 -1
  143. package/extension/libherb/util/hb_arena_debug.c +32 -17
  144. package/extension/libherb/util/hb_arena_debug.h +12 -1
  145. package/extension/libherb/util/hb_array.c +30 -15
  146. package/extension/libherb/util/hb_array.h +7 -3
  147. package/extension/libherb/util/hb_buffer.c +17 -21
  148. package/extension/libherb/util/hb_buffer.h +6 -4
  149. package/extension/libherb/util/hb_foreach.h +79 -0
  150. package/extension/libherb/util/hb_narray.c +22 -7
  151. package/extension/libherb/util/hb_narray.h +8 -4
  152. package/extension/libherb/util/hb_string.c +49 -35
  153. package/extension/libherb/util/hb_string.h +56 -9
  154. package/extension/libherb/util/string.h +11 -0
  155. package/extension/libherb/util.c +21 -11
  156. package/extension/libherb/util.h +6 -3
  157. package/extension/libherb/version.h +1 -1
  158. package/extension/libherb/visitor.c +48 -1
  159. package/extension/nodes.cpp +451 -6
  160. package/extension/nodes.h +8 -1
  161. package/extension/prism/include/prism/ast.h +4 -4
  162. package/extension/prism/include/prism/version.h +2 -2
  163. package/extension/prism/src/prism.c +1 -1
  164. package/package.json +12 -8
  165. package/src/node-backend.ts +11 -1
  166. package/dist/types/index-cjs.d.cts +0 -1
  167. package/extension/libherb/analyze.c +0 -1594
  168. package/extension/libherb/element_source.c +0 -12
  169. package/extension/libherb/include/util/hb_system.h +0 -9
  170. package/extension/libherb/util/hb_system.c +0 -30
  171. package/extension/libherb/util/hb_system.h +0 -9
  172. package/src/index-cjs.cts +0 -22
  173. /package/dist/types/{index-esm.d.mts → index.d.ts} +0 -0
  174. /package/src/{index-esm.mts → index.ts} +0 -0
@@ -1,1594 +0,0 @@
1
- #include "include/analyze.h"
2
- #include "include/analyze_helpers.h"
3
- #include "include/analyzed_ruby.h"
4
- #include "include/ast_node.h"
5
- #include "include/ast_nodes.h"
6
- #include "include/errors.h"
7
- #include "include/extract.h"
8
- #include "include/location.h"
9
- #include "include/parser.h"
10
- #include "include/position.h"
11
- #include "include/pretty_print.h"
12
- #include "include/prism_helpers.h"
13
- #include "include/token_struct.h"
14
- #include "include/util.h"
15
- #include "include/util/hb_array.h"
16
- #include "include/util/hb_string.h"
17
- #include "include/visitor.h"
18
-
19
- #include <prism.h>
20
- #include <stdbool.h>
21
- #include <stdio.h>
22
- #include <stdlib.h>
23
- #include <string.h>
24
-
25
- static analyzed_ruby_T* herb_analyze_ruby(hb_string_T source) {
26
- analyzed_ruby_T* analyzed = init_analyzed_ruby(source);
27
-
28
- pm_visit_node(analyzed->root, search_if_nodes, analyzed);
29
- pm_visit_node(analyzed->root, search_block_nodes, analyzed);
30
- pm_visit_node(analyzed->root, search_case_nodes, analyzed);
31
- pm_visit_node(analyzed->root, search_case_match_nodes, analyzed);
32
- pm_visit_node(analyzed->root, search_while_nodes, analyzed);
33
- pm_visit_node(analyzed->root, search_for_nodes, analyzed);
34
- pm_visit_node(analyzed->root, search_until_nodes, analyzed);
35
- pm_visit_node(analyzed->root, search_begin_nodes, analyzed);
36
- pm_visit_node(analyzed->root, search_unless_nodes, analyzed);
37
-
38
- search_elsif_nodes(analyzed);
39
- search_else_nodes(analyzed);
40
- search_end_nodes(analyzed);
41
- search_when_nodes(analyzed);
42
- search_in_nodes(analyzed);
43
- search_rescue_nodes(analyzed);
44
- search_ensure_nodes(analyzed);
45
- search_yield_nodes(analyzed->root, analyzed);
46
- search_then_keywords(analyzed->root, analyzed);
47
- search_block_closing_nodes(analyzed);
48
-
49
- if (!analyzed->valid) { pm_visit_node(analyzed->root, search_unclosed_control_flows, analyzed); }
50
-
51
- return analyzed;
52
- }
53
-
54
- static bool analyze_erb_content(const AST_NODE_T* node, void* data) {
55
- if (node->type == AST_ERB_CONTENT_NODE) {
56
- AST_ERB_CONTENT_NODE_T* erb_content_node = (AST_ERB_CONTENT_NODE_T*) node;
57
-
58
- const char* opening = erb_content_node->tag_opening->value;
59
-
60
- if (strcmp(opening, "<%%") != 0 && strcmp(opening, "<%%=") != 0 && strcmp(opening, "<%#") != 0
61
- && strcmp(opening, "<%graphql") != 0) {
62
- analyzed_ruby_T* analyzed = herb_analyze_ruby(hb_string(erb_content_node->content->value));
63
-
64
- erb_content_node->parsed = true;
65
- erb_content_node->valid = analyzed->valid;
66
- erb_content_node->analyzed_ruby = analyzed;
67
-
68
- if (!analyzed->valid && analyzed->unclosed_control_flow_count >= 2) {
69
- append_erb_multiple_blocks_in_tag_error(
70
- erb_content_node->base.location.start,
71
- erb_content_node->base.location.end,
72
- erb_content_node->base.errors
73
- );
74
- }
75
- } else {
76
- erb_content_node->parsed = false;
77
- erb_content_node->valid = true;
78
- erb_content_node->analyzed_ruby = NULL;
79
- }
80
- }
81
-
82
- herb_visit_child_nodes(node, analyze_erb_content, data);
83
-
84
- return false;
85
- }
86
-
87
- static size_t process_block_children(
88
- AST_NODE_T* node,
89
- hb_array_T* array,
90
- size_t index,
91
- hb_array_T* children_array,
92
- analyze_ruby_context_T* context,
93
- control_type_t parent_type
94
- );
95
-
96
- static size_t process_subsequent_block(
97
- AST_NODE_T* node,
98
- hb_array_T* array,
99
- size_t index,
100
- AST_NODE_T** subsequent_out,
101
- analyze_ruby_context_T* context,
102
- control_type_t parent_type
103
- );
104
-
105
- typedef struct {
106
- control_type_t type;
107
- uint32_t offset;
108
- bool found;
109
- } earliest_control_keyword_t;
110
-
111
- typedef struct {
112
- earliest_control_keyword_t* result;
113
- const uint8_t* source_start;
114
- } location_walker_context_t;
115
-
116
- static bool control_type_is_block(control_type_t type) {
117
- return type == CONTROL_TYPE_BLOCK;
118
- }
119
-
120
- static bool control_type_is_yield(control_type_t type) {
121
- return type == CONTROL_TYPE_YIELD;
122
- }
123
-
124
- static bool find_earliest_control_keyword_walker(const pm_node_t* node, void* data) {
125
- if (!node) { return true; }
126
-
127
- location_walker_context_t* context = (location_walker_context_t*) data;
128
- earliest_control_keyword_t* result = context->result;
129
-
130
- control_type_t current_type = CONTROL_TYPE_UNKNOWN;
131
- uint32_t keyword_offset = UINT32_MAX;
132
-
133
- switch (node->type) {
134
- case PM_IF_NODE: {
135
- pm_if_node_t* if_node = (pm_if_node_t*) node;
136
- current_type = CONTROL_TYPE_IF;
137
- keyword_offset = (uint32_t) (if_node->if_keyword_loc.start - context->source_start);
138
- break;
139
- }
140
-
141
- case PM_UNLESS_NODE: {
142
- pm_unless_node_t* unless_node = (pm_unless_node_t*) node;
143
- current_type = CONTROL_TYPE_UNLESS;
144
- keyword_offset = (uint32_t) (unless_node->keyword_loc.start - context->source_start);
145
- break;
146
- }
147
-
148
- case PM_CASE_NODE: {
149
- pm_case_node_t* case_node = (pm_case_node_t*) node;
150
- current_type = CONTROL_TYPE_CASE;
151
- keyword_offset = (uint32_t) (case_node->case_keyword_loc.start - context->source_start);
152
- break;
153
- }
154
-
155
- case PM_CASE_MATCH_NODE: {
156
- pm_case_match_node_t* case_match_node = (pm_case_match_node_t*) node;
157
- current_type = CONTROL_TYPE_CASE_MATCH;
158
- keyword_offset = (uint32_t) (case_match_node->case_keyword_loc.start - context->source_start);
159
- break;
160
- }
161
-
162
- case PM_WHILE_NODE: {
163
- pm_while_node_t* while_node = (pm_while_node_t*) node;
164
- current_type = CONTROL_TYPE_WHILE;
165
- keyword_offset = (uint32_t) (while_node->keyword_loc.start - context->source_start);
166
- break;
167
- }
168
-
169
- case PM_UNTIL_NODE: {
170
- pm_until_node_t* until_node = (pm_until_node_t*) node;
171
- current_type = CONTROL_TYPE_UNTIL;
172
- keyword_offset = (uint32_t) (until_node->keyword_loc.start - context->source_start);
173
- break;
174
- }
175
-
176
- case PM_FOR_NODE: {
177
- pm_for_node_t* for_node = (pm_for_node_t*) node;
178
- current_type = CONTROL_TYPE_FOR;
179
- keyword_offset = (uint32_t) (for_node->for_keyword_loc.start - context->source_start);
180
- break;
181
- }
182
-
183
- case PM_BEGIN_NODE: {
184
- pm_begin_node_t* begin_node = (pm_begin_node_t*) node;
185
- current_type = CONTROL_TYPE_BEGIN;
186
-
187
- if (begin_node->begin_keyword_loc.start != NULL) {
188
- keyword_offset = (uint32_t) (begin_node->begin_keyword_loc.start - context->source_start);
189
- } else {
190
- keyword_offset = (uint32_t) (node->location.start - context->source_start);
191
- }
192
- break;
193
- }
194
-
195
- case PM_YIELD_NODE: {
196
- current_type = CONTROL_TYPE_YIELD;
197
- keyword_offset = (uint32_t) (node->location.start - context->source_start);
198
- break;
199
- }
200
-
201
- case PM_CALL_NODE: {
202
- pm_call_node_t* call = (pm_call_node_t*) node;
203
-
204
- if (call->block != NULL && call->block->type == PM_BLOCK_NODE) {
205
- pm_block_node_t* block_node = (pm_block_node_t*) call->block;
206
-
207
- bool has_do_opening = is_do_block(block_node->opening_loc);
208
- bool has_brace_opening = is_brace_block(block_node->opening_loc);
209
- bool has_valid_brace_closing = is_closing_brace(block_node->closing_loc);
210
-
211
- if (has_do_opening || (has_brace_opening && !has_valid_brace_closing)) {
212
- current_type = CONTROL_TYPE_BLOCK;
213
- keyword_offset = (uint32_t) (node->location.start - context->source_start);
214
- }
215
- }
216
- break;
217
- }
218
-
219
- case PM_NEXT_NODE:
220
- case PM_BREAK_NODE:
221
- case PM_RETURN_NODE: {
222
- current_type = CONTROL_TYPE_UNKNOWN;
223
- keyword_offset = (uint32_t) (node->location.start - context->source_start);
224
- break;
225
- }
226
-
227
- default: break;
228
- }
229
-
230
- if (keyword_offset != UINT32_MAX) {
231
- bool should_update = !result->found;
232
-
233
- if (result->found) {
234
- if (control_type_is_block(current_type) && control_type_is_yield(result->type)) {
235
- should_update = true;
236
- } else if (!(control_type_is_yield(current_type) && control_type_is_block(result->type))) {
237
- should_update = keyword_offset < result->offset;
238
- }
239
- }
240
-
241
- if (should_update) {
242
- result->type = current_type;
243
- result->offset = keyword_offset;
244
- result->found = true;
245
- }
246
- }
247
-
248
- return true;
249
- }
250
-
251
- static control_type_t find_earliest_control_keyword(pm_node_t* root, const uint8_t* source_start) {
252
- if (!root) { return CONTROL_TYPE_UNKNOWN; }
253
-
254
- earliest_control_keyword_t result = { .type = CONTROL_TYPE_UNKNOWN, .offset = UINT32_MAX, .found = false };
255
-
256
- location_walker_context_t context = { .result = &result, .source_start = source_start };
257
-
258
- pm_visit_node(root, find_earliest_control_keyword_walker, &context);
259
-
260
- return result.found ? result.type : CONTROL_TYPE_UNKNOWN;
261
- }
262
-
263
- static control_type_t detect_control_type(AST_ERB_CONTENT_NODE_T* erb_node) {
264
- if (!erb_node || erb_node->base.type != AST_ERB_CONTENT_NODE) { return CONTROL_TYPE_UNKNOWN; }
265
-
266
- analyzed_ruby_T* ruby = erb_node->analyzed_ruby;
267
-
268
- if (!ruby) { return CONTROL_TYPE_UNKNOWN; }
269
-
270
- if (ruby->valid) { return CONTROL_TYPE_UNKNOWN; }
271
-
272
- pm_node_t* root = ruby->root;
273
-
274
- if (has_elsif_node(ruby)) { return CONTROL_TYPE_ELSIF; }
275
- if (has_else_node(ruby)) { return CONTROL_TYPE_ELSE; }
276
- if (has_end(ruby)) { return CONTROL_TYPE_END; }
277
- if (has_when_node(ruby)) { return CONTROL_TYPE_WHEN; }
278
- if (has_in_node(ruby)) { return CONTROL_TYPE_IN; }
279
- if (has_rescue_node(ruby)) { return CONTROL_TYPE_RESCUE; }
280
- if (has_ensure_node(ruby)) { return CONTROL_TYPE_ENSURE; }
281
- if (has_block_closing(ruby)) { return CONTROL_TYPE_BLOCK_CLOSE; }
282
-
283
- if (ruby->unclosed_control_flow_count == 0 && !has_yield_node(ruby)) { return CONTROL_TYPE_UNKNOWN; }
284
-
285
- return find_earliest_control_keyword(root, ruby->parser.start);
286
- }
287
-
288
- static bool is_subsequent_type(control_type_t parent_type, control_type_t child_type) {
289
- switch (parent_type) {
290
- case CONTROL_TYPE_IF:
291
- case CONTROL_TYPE_ELSIF: return child_type == CONTROL_TYPE_ELSIF || child_type == CONTROL_TYPE_ELSE;
292
- case CONTROL_TYPE_CASE:
293
- case CONTROL_TYPE_CASE_MATCH: return child_type == CONTROL_TYPE_WHEN || child_type == CONTROL_TYPE_ELSE;
294
- case CONTROL_TYPE_BEGIN:
295
- return child_type == CONTROL_TYPE_RESCUE || child_type == CONTROL_TYPE_ELSE || child_type == CONTROL_TYPE_ENSURE;
296
- case CONTROL_TYPE_RESCUE: return child_type == CONTROL_TYPE_RESCUE;
297
- case CONTROL_TYPE_UNLESS: return child_type == CONTROL_TYPE_ELSE;
298
-
299
- default: return false;
300
- }
301
- }
302
-
303
- static bool is_terminator_type(control_type_t parent_type, control_type_t child_type) {
304
- if (child_type == CONTROL_TYPE_END) { return true; }
305
-
306
- switch (parent_type) {
307
- case CONTROL_TYPE_WHEN: return child_type == CONTROL_TYPE_WHEN || child_type == CONTROL_TYPE_ELSE;
308
- case CONTROL_TYPE_IN: return child_type == CONTROL_TYPE_IN || child_type == CONTROL_TYPE_ELSE;
309
- case CONTROL_TYPE_BLOCK: return child_type == CONTROL_TYPE_BLOCK_CLOSE;
310
-
311
- default: return is_subsequent_type(parent_type, child_type);
312
- }
313
- }
314
-
315
- static AST_NODE_T* create_control_node(
316
- AST_ERB_CONTENT_NODE_T* erb_node,
317
- hb_array_T* children,
318
- AST_NODE_T* subsequent,
319
- AST_ERB_END_NODE_T* end_node,
320
- control_type_t control_type
321
- ) {
322
- hb_array_T* errors = erb_node->base.errors;
323
- erb_node->base.errors = NULL;
324
-
325
- position_T start_position = erb_node->tag_opening->location.start;
326
- position_T end_position = erb_node->tag_closing->location.end;
327
-
328
- if (end_node) {
329
- end_position = end_node->base.location.end;
330
- } else if (hb_array_size(children) > 0) {
331
- AST_NODE_T* last_child = hb_array_last(children);
332
- end_position = last_child->location.end;
333
- } else if (subsequent) {
334
- end_position = subsequent->location.end;
335
- }
336
-
337
- token_T* tag_opening = erb_node->tag_opening;
338
- token_T* content = erb_node->content;
339
- token_T* tag_closing = erb_node->tag_closing;
340
-
341
- location_T* then_keyword = NULL;
342
-
343
- if (control_type == CONTROL_TYPE_IF || control_type == CONTROL_TYPE_ELSIF || control_type == CONTROL_TYPE_UNLESS
344
- || control_type == CONTROL_TYPE_WHEN || control_type == CONTROL_TYPE_IN) {
345
- const char* source = content ? content->value : NULL;
346
-
347
- if (control_type == CONTROL_TYPE_WHEN || control_type == CONTROL_TYPE_IN) {
348
- if (source != NULL && strstr(source, "then") != NULL) {
349
- then_keyword = get_then_keyword_location_wrapped(source, control_type == CONTROL_TYPE_IN);
350
- }
351
- } else if (control_type == CONTROL_TYPE_ELSIF) {
352
- if (source != NULL && strstr(source, "then") != NULL) {
353
- then_keyword = get_then_keyword_location_elsif_wrapped(source);
354
- }
355
- } else {
356
- then_keyword = get_then_keyword_location(erb_node->analyzed_ruby, source);
357
- }
358
-
359
- if (then_keyword != NULL && content != NULL) {
360
- position_T content_start = content->location.start;
361
-
362
- then_keyword->start.line = content_start.line + then_keyword->start.line - 1;
363
- then_keyword->start.column = content_start.column + then_keyword->start.column;
364
- then_keyword->end.line = content_start.line + then_keyword->end.line - 1;
365
- then_keyword->end.column = content_start.column + then_keyword->end.column;
366
- }
367
- }
368
-
369
- switch (control_type) {
370
- case CONTROL_TYPE_IF:
371
- case CONTROL_TYPE_ELSIF: {
372
- return (AST_NODE_T*) ast_erb_if_node_init(
373
- tag_opening,
374
- content,
375
- tag_closing,
376
- then_keyword,
377
- children,
378
- subsequent,
379
- end_node,
380
- start_position,
381
- end_position,
382
- errors
383
- );
384
- }
385
-
386
- case CONTROL_TYPE_ELSE: {
387
- return (
388
- AST_NODE_T*
389
- ) ast_erb_else_node_init(tag_opening, content, tag_closing, children, start_position, end_position, errors);
390
- }
391
-
392
- case CONTROL_TYPE_CASE:
393
- case CONTROL_TYPE_CASE_MATCH: {
394
- AST_ERB_ELSE_NODE_T* else_node = NULL;
395
- if (subsequent && subsequent->type == AST_ERB_ELSE_NODE) { else_node = (AST_ERB_ELSE_NODE_T*) subsequent; }
396
-
397
- hb_array_T* when_conditions = hb_array_init(8);
398
- hb_array_T* in_conditions = hb_array_init(8);
399
- hb_array_T* non_when_non_in_children = hb_array_init(8);
400
-
401
- for (size_t i = 0; i < hb_array_size(children); i++) {
402
- AST_NODE_T* child = hb_array_get(children, i);
403
-
404
- if (child && child->type == AST_ERB_WHEN_NODE) {
405
- hb_array_append(when_conditions, child);
406
- } else if (child && child->type == AST_ERB_IN_NODE) {
407
- hb_array_append(in_conditions, child);
408
- } else {
409
- hb_array_append(non_when_non_in_children, child);
410
- }
411
- }
412
-
413
- hb_array_free(&children);
414
-
415
- if (hb_array_size(in_conditions) > 0) {
416
- hb_array_free(&when_conditions);
417
-
418
- return (AST_NODE_T*) ast_erb_case_match_node_init(
419
- tag_opening,
420
- content,
421
- tag_closing,
422
- non_when_non_in_children,
423
- in_conditions,
424
- else_node,
425
- end_node,
426
- start_position,
427
- end_position,
428
- errors
429
- );
430
- } else {
431
- hb_array_free(&in_conditions);
432
-
433
- return (AST_NODE_T*) ast_erb_case_node_init(
434
- tag_opening,
435
- content,
436
- tag_closing,
437
- non_when_non_in_children,
438
- when_conditions,
439
- else_node,
440
- end_node,
441
- start_position,
442
- end_position,
443
- errors
444
- );
445
- }
446
- }
447
-
448
- case CONTROL_TYPE_WHEN: {
449
- return (AST_NODE_T*) ast_erb_when_node_init(
450
- tag_opening,
451
- content,
452
- tag_closing,
453
- then_keyword,
454
- children,
455
- start_position,
456
- end_position,
457
- errors
458
- );
459
- }
460
-
461
- case CONTROL_TYPE_IN: {
462
- return (AST_NODE_T*) ast_erb_in_node_init(
463
- tag_opening,
464
- content,
465
- tag_closing,
466
- then_keyword,
467
- children,
468
- start_position,
469
- end_position,
470
- errors
471
- );
472
- }
473
-
474
- case CONTROL_TYPE_BEGIN: {
475
- AST_ERB_RESCUE_NODE_T* rescue_clause = NULL;
476
- AST_ERB_ELSE_NODE_T* else_clause = NULL;
477
- AST_ERB_ENSURE_NODE_T* ensure_clause = NULL;
478
-
479
- if (subsequent) {
480
- if (subsequent->type == AST_ERB_RESCUE_NODE) {
481
- rescue_clause = (AST_ERB_RESCUE_NODE_T*) subsequent;
482
- } else if (subsequent->type == AST_ERB_ELSE_NODE) {
483
- else_clause = (AST_ERB_ELSE_NODE_T*) subsequent;
484
- } else if (subsequent->type == AST_ERB_ENSURE_NODE) {
485
- ensure_clause = (AST_ERB_ENSURE_NODE_T*) subsequent;
486
- }
487
- }
488
-
489
- return (AST_NODE_T*) ast_erb_begin_node_init(
490
- tag_opening,
491
- content,
492
- tag_closing,
493
- children,
494
- rescue_clause,
495
- else_clause,
496
- ensure_clause,
497
- end_node,
498
- start_position,
499
- end_position,
500
- errors
501
- );
502
- }
503
-
504
- case CONTROL_TYPE_RESCUE: {
505
- AST_ERB_RESCUE_NODE_T* rescue_node = NULL;
506
-
507
- if (rescue_node && subsequent->type == AST_ERB_RESCUE_NODE) { rescue_node = (AST_ERB_RESCUE_NODE_T*) subsequent; }
508
-
509
- return (AST_NODE_T*) ast_erb_rescue_node_init(
510
- tag_opening,
511
- content,
512
- tag_closing,
513
- children,
514
- rescue_node,
515
- start_position,
516
- end_position,
517
- errors
518
- );
519
- }
520
-
521
- case CONTROL_TYPE_ENSURE: {
522
- return (
523
- AST_NODE_T*
524
- ) ast_erb_ensure_node_init(tag_opening, content, tag_closing, children, start_position, end_position, errors);
525
- }
526
-
527
- case CONTROL_TYPE_UNLESS: {
528
- AST_ERB_ELSE_NODE_T* else_clause = NULL;
529
-
530
- if (subsequent && subsequent->type == AST_ERB_ELSE_NODE) { else_clause = (AST_ERB_ELSE_NODE_T*) subsequent; }
531
-
532
- return (AST_NODE_T*) ast_erb_unless_node_init(
533
- tag_opening,
534
- content,
535
- tag_closing,
536
- then_keyword,
537
- children,
538
- else_clause,
539
- end_node,
540
- start_position,
541
- end_position,
542
- errors
543
- );
544
- }
545
-
546
- case CONTROL_TYPE_WHILE: {
547
- return (AST_NODE_T*) ast_erb_while_node_init(
548
- tag_opening,
549
- content,
550
- tag_closing,
551
- children,
552
- end_node,
553
- start_position,
554
- end_position,
555
- errors
556
- );
557
- }
558
-
559
- case CONTROL_TYPE_UNTIL: {
560
- return (AST_NODE_T*) ast_erb_until_node_init(
561
- tag_opening,
562
- content,
563
- tag_closing,
564
- children,
565
- end_node,
566
- start_position,
567
- end_position,
568
- errors
569
- );
570
- }
571
-
572
- case CONTROL_TYPE_FOR: {
573
- return (AST_NODE_T*) ast_erb_for_node_init(
574
- tag_opening,
575
- content,
576
- tag_closing,
577
- children,
578
- end_node,
579
- start_position,
580
- end_position,
581
- errors
582
- );
583
- }
584
-
585
- case CONTROL_TYPE_BLOCK: {
586
- return (AST_NODE_T*) ast_erb_block_node_init(
587
- tag_opening,
588
- content,
589
- tag_closing,
590
- children,
591
- end_node,
592
- start_position,
593
- end_position,
594
- errors
595
- );
596
- }
597
-
598
- case CONTROL_TYPE_YIELD: {
599
- return (
600
- AST_NODE_T*
601
- ) ast_erb_yield_node_init(tag_opening, content, tag_closing, start_position, end_position, errors);
602
- }
603
-
604
- default: return NULL;
605
- }
606
- }
607
-
608
- static size_t process_control_structure(
609
- AST_NODE_T* node,
610
- hb_array_T* array,
611
- size_t index,
612
- hb_array_T* output_array,
613
- analyze_ruby_context_T* context,
614
- control_type_t initial_type
615
- ) {
616
- AST_ERB_CONTENT_NODE_T* erb_node = (AST_ERB_CONTENT_NODE_T*) hb_array_get(array, index);
617
- hb_array_T* children = hb_array_init(8);
618
-
619
- index++;
620
-
621
- if (initial_type == CONTROL_TYPE_CASE || initial_type == CONTROL_TYPE_CASE_MATCH) {
622
- hb_array_T* when_conditions = hb_array_init(8);
623
- hb_array_T* in_conditions = hb_array_init(8);
624
- hb_array_T* non_when_non_in_children = hb_array_init(8);
625
-
626
- while (index < hb_array_size(array)) {
627
- AST_NODE_T* next_node = hb_array_get(array, index);
628
-
629
- if (!next_node) { break; }
630
-
631
- if (next_node->type == AST_ERB_CONTENT_NODE) {
632
- AST_ERB_CONTENT_NODE_T* erb_content = (AST_ERB_CONTENT_NODE_T*) next_node;
633
- control_type_t next_type = detect_control_type(erb_content);
634
-
635
- if (next_type == CONTROL_TYPE_WHEN || next_type == CONTROL_TYPE_IN) { break; }
636
- }
637
-
638
- hb_array_append(non_when_non_in_children, next_node);
639
- index++;
640
- }
641
-
642
- while (index < hb_array_size(array)) {
643
- AST_NODE_T* next_node = hb_array_get(array, index);
644
-
645
- if (!next_node) { break; }
646
-
647
- if (next_node->type != AST_ERB_CONTENT_NODE) {
648
- hb_array_append(non_when_non_in_children, next_node);
649
- index++;
650
- continue;
651
- }
652
-
653
- AST_ERB_CONTENT_NODE_T* erb_content = (AST_ERB_CONTENT_NODE_T*) next_node;
654
- control_type_t next_type = detect_control_type(erb_content);
655
-
656
- if (next_type == CONTROL_TYPE_WHEN) {
657
- hb_array_T* when_statements = hb_array_init(8);
658
- index++;
659
-
660
- index = process_block_children(node, array, index, when_statements, context, CONTROL_TYPE_WHEN);
661
-
662
- hb_array_T* when_errors = erb_content->base.errors;
663
- erb_content->base.errors = NULL;
664
-
665
- location_T* then_keyword = NULL;
666
- const char* source = erb_content->content ? erb_content->content->value : NULL;
667
-
668
- if (source != NULL && strstr(source, "then") != NULL) {
669
- then_keyword = get_then_keyword_location_wrapped(source, false);
670
-
671
- if (then_keyword != NULL && erb_content->content != NULL) {
672
- position_T content_start = erb_content->content->location.start;
673
-
674
- then_keyword->start.line = content_start.line + then_keyword->start.line - 1;
675
- then_keyword->start.column = content_start.column + then_keyword->start.column;
676
- then_keyword->end.line = content_start.line + then_keyword->end.line - 1;
677
- then_keyword->end.column = content_start.column + then_keyword->end.column;
678
- }
679
- }
680
-
681
- AST_ERB_WHEN_NODE_T* when_node = ast_erb_when_node_init(
682
- erb_content->tag_opening,
683
- erb_content->content,
684
- erb_content->tag_closing,
685
- then_keyword,
686
- when_statements,
687
- erb_content->tag_opening->location.start,
688
- erb_content->tag_closing->location.end,
689
- when_errors
690
- );
691
-
692
- ast_node_free((AST_NODE_T*) erb_content);
693
-
694
- hb_array_append(when_conditions, (AST_NODE_T*) when_node);
695
-
696
- continue;
697
- } else if (next_type == CONTROL_TYPE_IN) {
698
- hb_array_T* in_statements = hb_array_init(8);
699
- index++;
700
-
701
- index = process_block_children(node, array, index, in_statements, context, CONTROL_TYPE_IN);
702
-
703
- hb_array_T* in_errors = erb_content->base.errors;
704
- erb_content->base.errors = NULL;
705
-
706
- location_T* in_then_keyword = NULL;
707
- const char* in_source = erb_content->content ? erb_content->content->value : NULL;
708
-
709
- if (in_source != NULL && strstr(in_source, "then") != NULL) {
710
- in_then_keyword = get_then_keyword_location_wrapped(in_source, true);
711
-
712
- if (in_then_keyword != NULL && erb_content->content != NULL) {
713
- position_T content_start = erb_content->content->location.start;
714
-
715
- in_then_keyword->start.line = content_start.line + in_then_keyword->start.line - 1;
716
- in_then_keyword->start.column = content_start.column + in_then_keyword->start.column;
717
- in_then_keyword->end.line = content_start.line + in_then_keyword->end.line - 1;
718
- in_then_keyword->end.column = content_start.column + in_then_keyword->end.column;
719
- }
720
- }
721
-
722
- AST_ERB_IN_NODE_T* in_node = ast_erb_in_node_init(
723
- erb_content->tag_opening,
724
- erb_content->content,
725
- erb_content->tag_closing,
726
- in_then_keyword,
727
- in_statements,
728
- erb_content->tag_opening->location.start,
729
- erb_content->tag_closing->location.end,
730
- in_errors
731
- );
732
-
733
- ast_node_free((AST_NODE_T*) erb_content);
734
-
735
- hb_array_append(in_conditions, (AST_NODE_T*) in_node);
736
-
737
- continue;
738
- } else if (next_type == CONTROL_TYPE_ELSE || next_type == CONTROL_TYPE_END) {
739
- break;
740
- } else {
741
- hb_array_append(non_when_non_in_children, next_node);
742
- index++;
743
- }
744
- }
745
-
746
- AST_ERB_ELSE_NODE_T* else_clause = NULL;
747
-
748
- if (index < hb_array_size(array)) {
749
- AST_NODE_T* next_node = hb_array_get(array, index);
750
-
751
- if (next_node && next_node->type == AST_ERB_CONTENT_NODE) {
752
- AST_ERB_CONTENT_NODE_T* next_erb = (AST_ERB_CONTENT_NODE_T*) next_node;
753
- control_type_t next_type = detect_control_type(next_erb);
754
-
755
- if (next_type == CONTROL_TYPE_ELSE) {
756
- hb_array_T* else_children = hb_array_init(8);
757
-
758
- index++;
759
-
760
- index = process_block_children(node, array, index, else_children, context, initial_type);
761
-
762
- hb_array_T* else_errors = next_erb->base.errors;
763
- next_erb->base.errors = NULL;
764
-
765
- else_clause = ast_erb_else_node_init(
766
- next_erb->tag_opening,
767
- next_erb->content,
768
- next_erb->tag_closing,
769
- else_children,
770
- next_erb->tag_opening->location.start,
771
- next_erb->tag_closing->location.end,
772
- else_errors
773
- );
774
-
775
- ast_node_free((AST_NODE_T*) next_erb);
776
- }
777
- }
778
- }
779
-
780
- AST_ERB_END_NODE_T* end_node = NULL;
781
-
782
- if (index < hb_array_size(array)) {
783
- AST_NODE_T* potential_end = hb_array_get(array, index);
784
-
785
- if (potential_end && potential_end->type == AST_ERB_CONTENT_NODE) {
786
- AST_ERB_CONTENT_NODE_T* end_erb = (AST_ERB_CONTENT_NODE_T*) potential_end;
787
-
788
- if (detect_control_type(end_erb) == CONTROL_TYPE_END) {
789
- hb_array_T* end_errors = end_erb->base.errors;
790
- end_erb->base.errors = NULL;
791
-
792
- end_node = ast_erb_end_node_init(
793
- end_erb->tag_opening,
794
- end_erb->content,
795
- end_erb->tag_closing,
796
- end_erb->tag_opening->location.start,
797
- end_erb->tag_closing->location.end,
798
- end_errors
799
- );
800
-
801
- ast_node_free((AST_NODE_T*) end_erb);
802
-
803
- index++;
804
- }
805
- }
806
- }
807
-
808
- position_T start_position = erb_node->tag_opening->location.start;
809
- position_T end_position = erb_node->tag_closing->location.end;
810
-
811
- if (end_node) {
812
- end_position = end_node->base.location.end;
813
- } else if (else_clause) {
814
- end_position = else_clause->base.location.end;
815
- } else if (hb_array_size(when_conditions) > 0) {
816
- AST_NODE_T* last_when = hb_array_last(when_conditions);
817
- end_position = last_when->location.end;
818
- } else if (hb_array_size(in_conditions) > 0) {
819
- AST_NODE_T* last_in = hb_array_last(in_conditions);
820
- end_position = last_in->location.end;
821
- }
822
-
823
- if (hb_array_size(in_conditions) > 0) {
824
- hb_array_T* case_match_errors = erb_node->base.errors;
825
- erb_node->base.errors = NULL;
826
-
827
- AST_ERB_CASE_MATCH_NODE_T* case_match_node = ast_erb_case_match_node_init(
828
- erb_node->tag_opening,
829
- erb_node->content,
830
- erb_node->tag_closing,
831
- non_when_non_in_children,
832
- in_conditions,
833
- else_clause,
834
- end_node,
835
- start_position,
836
- end_position,
837
- case_match_errors
838
- );
839
-
840
- ast_node_free((AST_NODE_T*) erb_node);
841
-
842
- hb_array_append(output_array, (AST_NODE_T*) case_match_node);
843
- hb_array_free(&when_conditions);
844
- hb_array_free(&children);
845
-
846
- return index;
847
- }
848
-
849
- hb_array_T* case_errors = erb_node->base.errors;
850
- erb_node->base.errors = NULL;
851
-
852
- AST_ERB_CASE_NODE_T* case_node = ast_erb_case_node_init(
853
- erb_node->tag_opening,
854
- erb_node->content,
855
- erb_node->tag_closing,
856
- non_when_non_in_children,
857
- when_conditions,
858
- else_clause,
859
- end_node,
860
- start_position,
861
- end_position,
862
- case_errors
863
- );
864
-
865
- ast_node_free((AST_NODE_T*) erb_node);
866
-
867
- hb_array_append(output_array, (AST_NODE_T*) case_node);
868
- hb_array_free(&in_conditions);
869
- hb_array_free(&children);
870
-
871
- return index;
872
- }
873
-
874
- if (initial_type == CONTROL_TYPE_BEGIN) {
875
- index = process_block_children(node, array, index, children, context, initial_type);
876
-
877
- AST_ERB_RESCUE_NODE_T* rescue_clause = NULL;
878
- AST_ERB_ELSE_NODE_T* else_clause = NULL;
879
- AST_ERB_ENSURE_NODE_T* ensure_clause = NULL;
880
-
881
- if (index < hb_array_size(array)) {
882
- AST_NODE_T* next_node = hb_array_get(array, index);
883
-
884
- if (next_node && next_node->type == AST_ERB_CONTENT_NODE) {
885
- AST_ERB_CONTENT_NODE_T* next_erb = (AST_ERB_CONTENT_NODE_T*) next_node;
886
- control_type_t next_type = detect_control_type(next_erb);
887
-
888
- if (next_type == CONTROL_TYPE_RESCUE) {
889
- AST_NODE_T* rescue_node = NULL;
890
- index = process_subsequent_block(node, array, index, &rescue_node, context, initial_type);
891
- rescue_clause = (AST_ERB_RESCUE_NODE_T*) rescue_node;
892
- }
893
- }
894
- }
895
-
896
- if (index < hb_array_size(array)) {
897
- AST_NODE_T* next_node = hb_array_get(array, index);
898
-
899
- if (next_node && next_node->type == AST_ERB_CONTENT_NODE) {
900
- AST_ERB_CONTENT_NODE_T* next_erb = (AST_ERB_CONTENT_NODE_T*) next_node;
901
- control_type_t next_type = detect_control_type(next_erb);
902
-
903
- if (next_type == CONTROL_TYPE_ELSE) {
904
- hb_array_T* else_children = hb_array_init(8);
905
-
906
- index++;
907
-
908
- index = process_block_children(node, array, index, else_children, context, initial_type);
909
-
910
- hb_array_T* else_errors = next_erb->base.errors;
911
- next_erb->base.errors = NULL;
912
-
913
- else_clause = ast_erb_else_node_init(
914
- next_erb->tag_opening,
915
- next_erb->content,
916
- next_erb->tag_closing,
917
- else_children,
918
- next_erb->tag_opening->location.start,
919
- next_erb->tag_closing->location.end,
920
- else_errors
921
- );
922
-
923
- ast_node_free((AST_NODE_T*) next_erb);
924
- }
925
- }
926
- }
927
-
928
- if (index < hb_array_size(array)) {
929
- AST_NODE_T* next_node = hb_array_get(array, index);
930
-
931
- if (next_node && next_node->type == AST_ERB_CONTENT_NODE) {
932
- AST_ERB_CONTENT_NODE_T* next_erb = (AST_ERB_CONTENT_NODE_T*) next_node;
933
- control_type_t next_type = detect_control_type(next_erb);
934
-
935
- if (next_type == CONTROL_TYPE_ENSURE) {
936
- hb_array_T* ensure_children = hb_array_init(8);
937
-
938
- index++;
939
-
940
- while (index < hb_array_size(array)) {
941
- AST_NODE_T* child = hb_array_get(array, index);
942
-
943
- if (!child) { break; }
944
-
945
- if (child->type == AST_ERB_CONTENT_NODE) {
946
- AST_ERB_CONTENT_NODE_T* child_erb = (AST_ERB_CONTENT_NODE_T*) child;
947
- control_type_t child_type = detect_control_type(child_erb);
948
-
949
- if (child_type == CONTROL_TYPE_END) { break; }
950
- }
951
-
952
- hb_array_append(ensure_children, child);
953
- index++;
954
- }
955
-
956
- hb_array_T* ensure_errors = next_erb->base.errors;
957
- next_erb->base.errors = NULL;
958
-
959
- ensure_clause = ast_erb_ensure_node_init(
960
- next_erb->tag_opening,
961
- next_erb->content,
962
- next_erb->tag_closing,
963
- ensure_children,
964
- next_erb->tag_opening->location.start,
965
- next_erb->tag_closing->location.end,
966
- ensure_errors
967
- );
968
-
969
- ast_node_free((AST_NODE_T*) next_erb);
970
- }
971
- }
972
- }
973
-
974
- AST_ERB_END_NODE_T* end_node = NULL;
975
-
976
- if (index < hb_array_size(array)) {
977
- AST_NODE_T* potential_end = hb_array_get(array, index);
978
-
979
- if (potential_end && potential_end->type == AST_ERB_CONTENT_NODE) {
980
- AST_ERB_CONTENT_NODE_T* end_erb = (AST_ERB_CONTENT_NODE_T*) potential_end;
981
-
982
- if (detect_control_type(end_erb) == CONTROL_TYPE_END) {
983
- hb_array_T* end_errors = end_erb->base.errors;
984
- end_erb->base.errors = NULL;
985
-
986
- end_node = ast_erb_end_node_init(
987
- end_erb->tag_opening,
988
- end_erb->content,
989
- end_erb->tag_closing,
990
- end_erb->tag_opening->location.start,
991
- end_erb->tag_closing->location.end,
992
- end_errors
993
- );
994
-
995
- ast_node_free((AST_NODE_T*) end_erb);
996
-
997
- index++;
998
- }
999
- }
1000
- }
1001
-
1002
- position_T start_position = erb_node->tag_opening->location.start;
1003
- position_T end_position = erb_node->tag_closing->location.end;
1004
-
1005
- if (end_node) {
1006
- end_position = end_node->base.location.end;
1007
- } else if (ensure_clause) {
1008
- end_position = ensure_clause->base.location.end;
1009
- } else if (else_clause) {
1010
- end_position = else_clause->base.location.end;
1011
- } else if (rescue_clause) {
1012
- end_position = rescue_clause->base.location.end;
1013
- }
1014
-
1015
- hb_array_T* begin_errors = erb_node->base.errors;
1016
- erb_node->base.errors = NULL;
1017
-
1018
- AST_ERB_BEGIN_NODE_T* begin_node = ast_erb_begin_node_init(
1019
- erb_node->tag_opening,
1020
- erb_node->content,
1021
- erb_node->tag_closing,
1022
- children,
1023
- rescue_clause,
1024
- else_clause,
1025
- ensure_clause,
1026
- end_node,
1027
- start_position,
1028
- end_position,
1029
- begin_errors
1030
- );
1031
-
1032
- ast_node_free((AST_NODE_T*) erb_node);
1033
-
1034
- hb_array_append(output_array, (AST_NODE_T*) begin_node);
1035
- return index;
1036
- }
1037
-
1038
- if (initial_type == CONTROL_TYPE_BLOCK) {
1039
- index = process_block_children(node, array, index, children, context, initial_type);
1040
-
1041
- AST_ERB_END_NODE_T* end_node = NULL;
1042
-
1043
- if (index < hb_array_size(array)) {
1044
- AST_NODE_T* potential_close = hb_array_get(array, index);
1045
-
1046
- if (potential_close && potential_close->type == AST_ERB_CONTENT_NODE) {
1047
- AST_ERB_CONTENT_NODE_T* close_erb = (AST_ERB_CONTENT_NODE_T*) potential_close;
1048
- control_type_t close_type = detect_control_type(close_erb);
1049
-
1050
- if (close_type == CONTROL_TYPE_BLOCK_CLOSE || close_type == CONTROL_TYPE_END) {
1051
- hb_array_T* end_errors = close_erb->base.errors;
1052
- close_erb->base.errors = NULL;
1053
-
1054
- end_node = ast_erb_end_node_init(
1055
- close_erb->tag_opening,
1056
- close_erb->content,
1057
- close_erb->tag_closing,
1058
- close_erb->tag_opening->location.start,
1059
- close_erb->tag_closing->location.end,
1060
- end_errors
1061
- );
1062
-
1063
- ast_node_free((AST_NODE_T*) close_erb);
1064
-
1065
- index++;
1066
- }
1067
- }
1068
- }
1069
-
1070
- position_T start_position = erb_node->tag_opening->location.start;
1071
- position_T end_position = erb_node->tag_closing->location.end;
1072
-
1073
- if (end_node) {
1074
- end_position = end_node->base.location.end;
1075
- } else if (hb_array_size(children) > 0) {
1076
- AST_NODE_T* last_child = hb_array_last(children);
1077
- end_position = last_child->location.end;
1078
- }
1079
-
1080
- hb_array_T* block_errors = erb_node->base.errors;
1081
- erb_node->base.errors = NULL;
1082
-
1083
- AST_ERB_BLOCK_NODE_T* block_node = ast_erb_block_node_init(
1084
- erb_node->tag_opening,
1085
- erb_node->content,
1086
- erb_node->tag_closing,
1087
- children,
1088
- end_node,
1089
- start_position,
1090
- end_position,
1091
- block_errors
1092
- );
1093
-
1094
- ast_node_free((AST_NODE_T*) erb_node);
1095
-
1096
- hb_array_append(output_array, (AST_NODE_T*) block_node);
1097
- return index;
1098
- }
1099
-
1100
- index = process_block_children(node, array, index, children, context, initial_type);
1101
-
1102
- AST_NODE_T* subsequent = NULL;
1103
- AST_ERB_END_NODE_T* end_node = NULL;
1104
-
1105
- if (index < hb_array_size(array)) {
1106
- AST_NODE_T* next_node = hb_array_get(array, index);
1107
-
1108
- if (next_node && next_node->type == AST_ERB_CONTENT_NODE) {
1109
- AST_ERB_CONTENT_NODE_T* next_erb = (AST_ERB_CONTENT_NODE_T*) next_node;
1110
- control_type_t next_type = detect_control_type(next_erb);
1111
-
1112
- if (is_subsequent_type(initial_type, next_type)) {
1113
- index = process_subsequent_block(node, array, index, &subsequent, context, initial_type);
1114
- }
1115
- }
1116
- }
1117
-
1118
- if (index < hb_array_size(array)) {
1119
- AST_NODE_T* potential_end = hb_array_get(array, index);
1120
-
1121
- if (potential_end && potential_end->type == AST_ERB_CONTENT_NODE) {
1122
- AST_ERB_CONTENT_NODE_T* end_erb = (AST_ERB_CONTENT_NODE_T*) potential_end;
1123
-
1124
- if (detect_control_type(end_erb) == CONTROL_TYPE_END) {
1125
- hb_array_T* end_errors = end_erb->base.errors;
1126
- end_erb->base.errors = NULL;
1127
-
1128
- end_node = ast_erb_end_node_init(
1129
- end_erb->tag_opening,
1130
- end_erb->content,
1131
- end_erb->tag_closing,
1132
- end_erb->tag_opening->location.start,
1133
- end_erb->tag_closing->location.end,
1134
- end_errors
1135
- );
1136
-
1137
- ast_node_free((AST_NODE_T*) end_erb);
1138
-
1139
- index++;
1140
- }
1141
- }
1142
- }
1143
-
1144
- AST_NODE_T* control_node = create_control_node(erb_node, children, subsequent, end_node, initial_type);
1145
-
1146
- if (control_node) {
1147
- ast_node_free((AST_NODE_T*) erb_node);
1148
- hb_array_append(output_array, control_node);
1149
- } else {
1150
- hb_array_free(&children);
1151
- }
1152
-
1153
- return index;
1154
- }
1155
-
1156
- static size_t process_subsequent_block(
1157
- AST_NODE_T* node,
1158
- hb_array_T* array,
1159
- size_t index,
1160
- AST_NODE_T** subsequent_out,
1161
- analyze_ruby_context_T* context,
1162
- control_type_t parent_type
1163
- ) {
1164
- AST_ERB_CONTENT_NODE_T* erb_node = (AST_ERB_CONTENT_NODE_T*) hb_array_get(array, index);
1165
- control_type_t type = detect_control_type(erb_node);
1166
- hb_array_T* children = hb_array_init(8);
1167
-
1168
- index++;
1169
-
1170
- index = process_block_children(node, array, index, children, context, parent_type);
1171
-
1172
- AST_NODE_T* subsequent_node = create_control_node(erb_node, children, NULL, NULL, type);
1173
-
1174
- if (subsequent_node) {
1175
- ast_node_free((AST_NODE_T*) erb_node);
1176
- } else {
1177
- hb_array_free(&children);
1178
- }
1179
-
1180
- if (index < hb_array_size(array)) {
1181
- AST_NODE_T* next_node = hb_array_get(array, index);
1182
-
1183
- if (next_node && next_node->type == AST_ERB_CONTENT_NODE) {
1184
- AST_ERB_CONTENT_NODE_T* next_erb = (AST_ERB_CONTENT_NODE_T*) next_node;
1185
- control_type_t next_type = detect_control_type(next_erb);
1186
-
1187
- if (is_subsequent_type(parent_type, next_type)
1188
- && !(type == CONTROL_TYPE_RESCUE && (next_type == CONTROL_TYPE_ELSE || next_type == CONTROL_TYPE_ENSURE))) {
1189
-
1190
- AST_NODE_T** next_subsequent = NULL;
1191
-
1192
- switch (type) {
1193
- case CONTROL_TYPE_ELSIF: {
1194
- if (subsequent_node->type == AST_ERB_IF_NODE) {
1195
- next_subsequent = &(((AST_ERB_IF_NODE_T*) subsequent_node)->subsequent);
1196
- }
1197
-
1198
- break;
1199
- }
1200
-
1201
- case CONTROL_TYPE_RESCUE: {
1202
- if (subsequent_node->type == AST_ERB_RESCUE_NODE && next_type == CONTROL_TYPE_RESCUE) {
1203
- AST_NODE_T* next_rescue_node = NULL;
1204
- index = process_subsequent_block(node, array, index, &next_rescue_node, context, parent_type);
1205
-
1206
- if (next_rescue_node) {
1207
- ((AST_ERB_RESCUE_NODE_T*) subsequent_node)->subsequent = (AST_ERB_RESCUE_NODE_T*) next_rescue_node;
1208
- }
1209
-
1210
- next_subsequent = NULL;
1211
- }
1212
-
1213
- break;
1214
- }
1215
-
1216
- default: break;
1217
- }
1218
-
1219
- if (next_subsequent) {
1220
- index = process_subsequent_block(node, array, index, next_subsequent, context, parent_type);
1221
- }
1222
- }
1223
- }
1224
- }
1225
-
1226
- *subsequent_out = subsequent_node;
1227
- return index;
1228
- }
1229
-
1230
- static size_t process_block_children(
1231
- AST_NODE_T* node,
1232
- hb_array_T* array,
1233
- size_t index,
1234
- hb_array_T* children_array,
1235
- analyze_ruby_context_T* context,
1236
- control_type_t parent_type
1237
- ) {
1238
- while (index < hb_array_size(array)) {
1239
- AST_NODE_T* child = hb_array_get(array, index);
1240
-
1241
- if (!child) { break; }
1242
-
1243
- if (child->type != AST_ERB_CONTENT_NODE) {
1244
- hb_array_append(children_array, child);
1245
- index++;
1246
- continue;
1247
- }
1248
-
1249
- AST_ERB_CONTENT_NODE_T* erb_content = (AST_ERB_CONTENT_NODE_T*) child;
1250
- control_type_t child_type = detect_control_type(erb_content);
1251
-
1252
- if (is_terminator_type(parent_type, child_type)) { break; }
1253
-
1254
- if (child_type == CONTROL_TYPE_IF || child_type == CONTROL_TYPE_CASE || child_type == CONTROL_TYPE_CASE_MATCH
1255
- || child_type == CONTROL_TYPE_BEGIN || child_type == CONTROL_TYPE_UNLESS || child_type == CONTROL_TYPE_WHILE
1256
- || child_type == CONTROL_TYPE_UNTIL || child_type == CONTROL_TYPE_FOR || child_type == CONTROL_TYPE_BLOCK) {
1257
- hb_array_T* temp_array = hb_array_init(1);
1258
- size_t new_index = process_control_structure(node, array, index, temp_array, context, child_type);
1259
-
1260
- if (hb_array_size(temp_array) > 0) { hb_array_append(children_array, hb_array_first(temp_array)); }
1261
-
1262
- hb_array_free(&temp_array);
1263
-
1264
- index = new_index;
1265
- continue;
1266
- }
1267
-
1268
- hb_array_append(children_array, child);
1269
- index++;
1270
- }
1271
-
1272
- return index;
1273
- }
1274
-
1275
- hb_array_T* rewrite_node_array(AST_NODE_T* node, hb_array_T* array, analyze_ruby_context_T* context) {
1276
- hb_array_T* new_array = hb_array_init(hb_array_size(array));
1277
- size_t index = 0;
1278
-
1279
- while (index < hb_array_size(array)) {
1280
- AST_NODE_T* item = hb_array_get(array, index);
1281
-
1282
- if (!item) { break; }
1283
-
1284
- if (item->type != AST_ERB_CONTENT_NODE) {
1285
- hb_array_append(new_array, item);
1286
- index++;
1287
- continue;
1288
- }
1289
-
1290
- AST_ERB_CONTENT_NODE_T* erb_node = (AST_ERB_CONTENT_NODE_T*) item;
1291
- control_type_t type = detect_control_type(erb_node);
1292
-
1293
- switch (type) {
1294
- case CONTROL_TYPE_IF:
1295
- case CONTROL_TYPE_CASE:
1296
- case CONTROL_TYPE_CASE_MATCH:
1297
- case CONTROL_TYPE_BEGIN:
1298
- case CONTROL_TYPE_UNLESS:
1299
- case CONTROL_TYPE_WHILE:
1300
- case CONTROL_TYPE_UNTIL:
1301
- case CONTROL_TYPE_FOR:
1302
- case CONTROL_TYPE_BLOCK:
1303
- index = process_control_structure(node, array, index, new_array, context, type);
1304
- continue;
1305
-
1306
- case CONTROL_TYPE_YIELD: {
1307
- AST_NODE_T* yield_node = create_control_node(erb_node, NULL, NULL, NULL, type);
1308
-
1309
- if (yield_node) {
1310
- ast_node_free((AST_NODE_T*) erb_node);
1311
- hb_array_append(new_array, yield_node);
1312
- } else {
1313
- hb_array_append(new_array, item);
1314
- }
1315
-
1316
- index++;
1317
- break;
1318
- }
1319
-
1320
- default:
1321
- hb_array_append(new_array, item);
1322
- index++;
1323
- break;
1324
- }
1325
- }
1326
-
1327
- return new_array;
1328
- }
1329
-
1330
- static bool detect_invalid_erb_structures(const AST_NODE_T* node, void* data) {
1331
- invalid_erb_context_T* context = (invalid_erb_context_T*) data;
1332
-
1333
- if (node->type == AST_HTML_ATTRIBUTE_NAME_NODE) { return false; }
1334
-
1335
- bool is_loop_node =
1336
- (node->type == AST_ERB_WHILE_NODE || node->type == AST_ERB_UNTIL_NODE || node->type == AST_ERB_FOR_NODE
1337
- || node->type == AST_ERB_BLOCK_NODE);
1338
-
1339
- bool is_begin_node = (node->type == AST_ERB_BEGIN_NODE);
1340
-
1341
- if (is_loop_node) { context->loop_depth++; }
1342
-
1343
- if (is_begin_node) { context->rescue_depth++; }
1344
-
1345
- if (node->type == AST_ERB_CONTENT_NODE) {
1346
- const AST_ERB_CONTENT_NODE_T* content_node = (const AST_ERB_CONTENT_NODE_T*) node;
1347
-
1348
- if (content_node->parsed && !content_node->valid && content_node->analyzed_ruby != NULL) {
1349
- analyzed_ruby_T* analyzed = content_node->analyzed_ruby;
1350
-
1351
- // =begin
1352
- if (has_error_message(analyzed, "embedded document meets end of file")) {
1353
- if (is_loop_node) { context->loop_depth--; }
1354
- if (is_begin_node) { context->rescue_depth--; }
1355
-
1356
- return true;
1357
- }
1358
-
1359
- // =end
1360
- if (has_error_message(analyzed, "unexpected '=', ignoring it")
1361
- && has_error_message(analyzed, "unexpected 'end', ignoring it")) {
1362
- if (is_loop_node) { context->loop_depth--; }
1363
- if (is_begin_node) { context->rescue_depth--; }
1364
-
1365
- return true;
1366
- }
1367
-
1368
- const char* keyword = NULL;
1369
-
1370
- if (context->loop_depth == 0) {
1371
- if (has_error_message(analyzed, "Invalid break")) {
1372
- keyword = "`<% break %>`";
1373
- } else if (has_error_message(analyzed, "Invalid next")) {
1374
- keyword = "`<% next %>`";
1375
- } else if (has_error_message(analyzed, "Invalid redo")) {
1376
- keyword = "`<% redo %>`";
1377
- }
1378
- } else {
1379
- if (has_error_message(analyzed, "Invalid redo") || has_error_message(analyzed, "Invalid break")
1380
- || has_error_message(analyzed, "Invalid next")) {
1381
-
1382
- if (is_loop_node) { context->loop_depth--; }
1383
- if (is_begin_node) { context->rescue_depth--; }
1384
-
1385
- return true;
1386
- }
1387
- }
1388
-
1389
- if (context->rescue_depth == 0) {
1390
- if (has_error_message(analyzed, "Invalid retry without rescue")) { keyword = "`<% retry %>`"; }
1391
- } else {
1392
- if (has_error_message(analyzed, "Invalid retry without rescue")) {
1393
- if (is_loop_node) { context->loop_depth--; }
1394
- if (is_begin_node) { context->rescue_depth--; }
1395
-
1396
- return true;
1397
- }
1398
- }
1399
-
1400
- if (keyword == NULL) { keyword = erb_keyword_from_analyzed_ruby(analyzed); }
1401
-
1402
- if (keyword != NULL && !token_value_empty(content_node->tag_closing)) {
1403
- append_erb_control_flow_scope_error(keyword, node->location.start, node->location.end, node->errors);
1404
- }
1405
- }
1406
- }
1407
-
1408
- if (node->type == AST_ERB_IF_NODE) {
1409
- const AST_ERB_IF_NODE_T* if_node = (const AST_ERB_IF_NODE_T*) node;
1410
-
1411
- if (if_node->end_node == NULL) { check_erb_node_for_missing_end(node); }
1412
-
1413
- if (if_node->statements != NULL) {
1414
- for (size_t i = 0; i < hb_array_size(if_node->statements); i++) {
1415
- AST_NODE_T* statement = (AST_NODE_T*) hb_array_get(if_node->statements, i);
1416
-
1417
- if (statement != NULL) { herb_visit_node(statement, detect_invalid_erb_structures, context); }
1418
- }
1419
- }
1420
-
1421
- AST_NODE_T* subsequent = if_node->subsequent;
1422
-
1423
- while (subsequent != NULL) {
1424
- if (subsequent->type == AST_ERB_CONTENT_NODE) {
1425
- const AST_ERB_CONTENT_NODE_T* content_node = (const AST_ERB_CONTENT_NODE_T*) subsequent;
1426
-
1427
- if (content_node->parsed && !content_node->valid && content_node->analyzed_ruby != NULL) {
1428
- analyzed_ruby_T* analyzed = content_node->analyzed_ruby;
1429
- const char* keyword = erb_keyword_from_analyzed_ruby(analyzed);
1430
-
1431
- if (!token_value_empty(content_node->tag_closing)) {
1432
- append_erb_control_flow_scope_error(
1433
- keyword,
1434
- subsequent->location.start,
1435
- subsequent->location.end,
1436
- subsequent->errors
1437
- );
1438
- }
1439
- }
1440
- }
1441
-
1442
- if (subsequent->type == AST_ERB_IF_NODE) {
1443
- const AST_ERB_IF_NODE_T* elsif_node = (const AST_ERB_IF_NODE_T*) subsequent;
1444
-
1445
- if (elsif_node->statements != NULL) {
1446
- for (size_t i = 0; i < hb_array_size(elsif_node->statements); i++) {
1447
- AST_NODE_T* statement = (AST_NODE_T*) hb_array_get(elsif_node->statements, i);
1448
-
1449
- if (statement != NULL) { herb_visit_node(statement, detect_invalid_erb_structures, context); }
1450
- }
1451
- }
1452
-
1453
- subsequent = elsif_node->subsequent;
1454
- } else if (subsequent->type == AST_ERB_ELSE_NODE) {
1455
- const AST_ERB_ELSE_NODE_T* else_node = (const AST_ERB_ELSE_NODE_T*) subsequent;
1456
-
1457
- if (else_node->statements != NULL) {
1458
- for (size_t i = 0; i < hb_array_size(else_node->statements); i++) {
1459
- AST_NODE_T* statement = (AST_NODE_T*) hb_array_get(else_node->statements, i);
1460
-
1461
- if (statement != NULL) { herb_visit_node(statement, detect_invalid_erb_structures, context); }
1462
- }
1463
- }
1464
-
1465
- break;
1466
- } else {
1467
- break;
1468
- }
1469
- }
1470
- }
1471
-
1472
- if (node->type == AST_ERB_UNLESS_NODE || node->type == AST_ERB_WHILE_NODE || node->type == AST_ERB_UNTIL_NODE
1473
- || node->type == AST_ERB_FOR_NODE || node->type == AST_ERB_CASE_NODE || node->type == AST_ERB_CASE_MATCH_NODE
1474
- || node->type == AST_ERB_BEGIN_NODE || node->type == AST_ERB_BLOCK_NODE || node->type == AST_ERB_ELSE_NODE) {
1475
- herb_visit_child_nodes(node, detect_invalid_erb_structures, context);
1476
- }
1477
-
1478
- if (node->type == AST_ERB_UNLESS_NODE || node->type == AST_ERB_WHILE_NODE || node->type == AST_ERB_UNTIL_NODE
1479
- || node->type == AST_ERB_FOR_NODE || node->type == AST_ERB_CASE_NODE || node->type == AST_ERB_CASE_MATCH_NODE
1480
- || node->type == AST_ERB_BEGIN_NODE || node->type == AST_ERB_BLOCK_NODE || node->type == AST_ERB_ELSE_NODE) {
1481
- check_erb_node_for_missing_end(node);
1482
-
1483
- if (is_loop_node) { context->loop_depth--; }
1484
- if (is_begin_node) { context->rescue_depth--; }
1485
-
1486
- return false;
1487
- }
1488
-
1489
- if (node->type == AST_ERB_IF_NODE) {
1490
- if (is_loop_node) { context->loop_depth--; }
1491
- if (is_begin_node) { context->rescue_depth--; }
1492
-
1493
- return false;
1494
- }
1495
-
1496
- bool result = true;
1497
-
1498
- if (is_loop_node) { context->loop_depth--; }
1499
- if (is_begin_node) { context->rescue_depth--; }
1500
-
1501
- return result;
1502
- }
1503
-
1504
- void herb_analyze_parse_tree(AST_DOCUMENT_NODE_T* document, const char* source) {
1505
- herb_visit_node((AST_NODE_T*) document, analyze_erb_content, NULL);
1506
-
1507
- analyze_ruby_context_T* context = malloc(sizeof(analyze_ruby_context_T));
1508
- context->document = document;
1509
- context->parent = NULL;
1510
- context->ruby_context_stack = hb_array_init(8);
1511
-
1512
- herb_visit_node((AST_NODE_T*) document, transform_erb_nodes, context);
1513
-
1514
- invalid_erb_context_T* invalid_context = malloc(sizeof(invalid_erb_context_T));
1515
- invalid_context->loop_depth = 0;
1516
- invalid_context->rescue_depth = 0;
1517
-
1518
- herb_visit_node((AST_NODE_T*) document, detect_invalid_erb_structures, invalid_context);
1519
-
1520
- herb_analyze_parse_errors(document, source);
1521
-
1522
- herb_parser_match_html_tags_post_analyze(document);
1523
-
1524
- hb_array_free(&context->ruby_context_stack);
1525
-
1526
- free(context);
1527
- free(invalid_context);
1528
- }
1529
-
1530
- static void parse_erb_content_errors(AST_NODE_T* erb_node, const char* source) {
1531
- if (!erb_node || erb_node->type != AST_ERB_CONTENT_NODE) { return; }
1532
- AST_ERB_CONTENT_NODE_T* content_node = (AST_ERB_CONTENT_NODE_T*) erb_node;
1533
-
1534
- if (!content_node->content || !content_node->content->value) { return; }
1535
-
1536
- const char* content = content_node->content->value;
1537
- if (strlen(content) == 0) { return; }
1538
-
1539
- pm_parser_t parser;
1540
- pm_options_t options = { 0, .partial_script = true };
1541
- pm_parser_init(&parser, (const uint8_t*) content, strlen(content), &options);
1542
-
1543
- pm_node_t* root = pm_parse(&parser);
1544
-
1545
- const pm_diagnostic_t* error = (const pm_diagnostic_t*) parser.error_list.head;
1546
-
1547
- if (error != NULL) {
1548
- RUBY_PARSE_ERROR_T* parse_error =
1549
- ruby_parse_error_from_prism_error_with_positions(error, erb_node->location.start, erb_node->location.end);
1550
-
1551
- hb_array_append(erb_node->errors, parse_error);
1552
- }
1553
-
1554
- pm_node_destroy(&parser, root);
1555
- pm_parser_free(&parser);
1556
- pm_options_free(&options);
1557
- }
1558
-
1559
- void herb_analyze_parse_errors(AST_DOCUMENT_NODE_T* document, const char* source) {
1560
- char* extracted_ruby = herb_extract_ruby_with_semicolons(source);
1561
-
1562
- if (!extracted_ruby) { return; }
1563
-
1564
- pm_parser_t parser;
1565
- pm_options_t options = { 0, .partial_script = true };
1566
- pm_parser_init(&parser, (const uint8_t*) extracted_ruby, strlen(extracted_ruby), &options);
1567
-
1568
- pm_node_t* root = pm_parse(&parser);
1569
-
1570
- for (const pm_diagnostic_t* error = (const pm_diagnostic_t*) parser.error_list.head; error != NULL;
1571
- error = (const pm_diagnostic_t*) error->node.next) {
1572
- size_t error_offset = (size_t) (error->location.start - parser.start);
1573
-
1574
- if (strstr(error->message, "unexpected ';'") != NULL) {
1575
- if (error_offset < strlen(extracted_ruby) && extracted_ruby[error_offset] == ';') {
1576
- if (error_offset >= strlen(source) || source[error_offset] != ';') {
1577
- AST_NODE_T* erb_node = find_erb_content_at_offset(document, source, error_offset);
1578
-
1579
- if (erb_node) { parse_erb_content_errors(erb_node, source); }
1580
-
1581
- continue;
1582
- }
1583
- }
1584
- }
1585
-
1586
- RUBY_PARSE_ERROR_T* parse_error = ruby_parse_error_from_prism_error(error, (AST_NODE_T*) document, source, &parser);
1587
- hb_array_append(document->base.errors, parse_error);
1588
- }
1589
-
1590
- pm_node_destroy(&parser, root);
1591
- pm_parser_free(&parser);
1592
- pm_options_free(&options);
1593
- free(extracted_ruby);
1594
- }