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