@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
@@ -0,0 +1,250 @@
1
+ #include "../include/analyze/control_type.h"
2
+ #include "../include/analyze/analyze.h"
3
+ #include "../include/analyze/analyzed_ruby.h"
4
+ #include "../include/analyze/helpers.h"
5
+ #include "../include/ast_node.h"
6
+
7
+ #include <prism.h>
8
+ #include <stdbool.h>
9
+ #include <stdint.h>
10
+
11
+ typedef struct {
12
+ control_type_t type;
13
+ uint32_t offset;
14
+ bool found;
15
+ } earliest_control_keyword_T;
16
+
17
+ typedef struct {
18
+ earliest_control_keyword_T* result;
19
+ const uint8_t* source_start;
20
+ } location_walker_context_T;
21
+
22
+ static bool control_type_is_block(control_type_t type) {
23
+ return type == CONTROL_TYPE_BLOCK;
24
+ }
25
+
26
+ static bool control_type_is_yield(control_type_t type) {
27
+ return type == CONTROL_TYPE_YIELD;
28
+ }
29
+
30
+ static bool find_earliest_control_keyword_walker(const pm_node_t* node, void* data) {
31
+ if (!node) { return true; }
32
+
33
+ location_walker_context_T* context = (location_walker_context_T*) data;
34
+ earliest_control_keyword_T* result = context->result;
35
+
36
+ control_type_t current_type = CONTROL_TYPE_UNKNOWN;
37
+ uint32_t keyword_offset = UINT32_MAX;
38
+
39
+ switch (node->type) {
40
+ case PM_IF_NODE: {
41
+ pm_if_node_t* if_node = (pm_if_node_t*) node;
42
+ current_type = CONTROL_TYPE_IF;
43
+ keyword_offset = (uint32_t) (if_node->if_keyword_loc.start - context->source_start);
44
+ break;
45
+ }
46
+
47
+ case PM_UNLESS_NODE: {
48
+ pm_unless_node_t* unless_node = (pm_unless_node_t*) node;
49
+ current_type = CONTROL_TYPE_UNLESS;
50
+ keyword_offset = (uint32_t) (unless_node->keyword_loc.start - context->source_start);
51
+ break;
52
+ }
53
+
54
+ case PM_CASE_NODE: {
55
+ pm_case_node_t* case_node = (pm_case_node_t*) node;
56
+ current_type = CONTROL_TYPE_CASE;
57
+ keyword_offset = (uint32_t) (case_node->case_keyword_loc.start - context->source_start);
58
+ break;
59
+ }
60
+
61
+ case PM_CASE_MATCH_NODE: {
62
+ pm_case_match_node_t* case_match_node = (pm_case_match_node_t*) node;
63
+ current_type = CONTROL_TYPE_CASE_MATCH;
64
+ keyword_offset = (uint32_t) (case_match_node->case_keyword_loc.start - context->source_start);
65
+ break;
66
+ }
67
+
68
+ case PM_WHILE_NODE: {
69
+ pm_while_node_t* while_node = (pm_while_node_t*) node;
70
+ current_type = CONTROL_TYPE_WHILE;
71
+ keyword_offset = (uint32_t) (while_node->keyword_loc.start - context->source_start);
72
+ break;
73
+ }
74
+
75
+ case PM_UNTIL_NODE: {
76
+ pm_until_node_t* until_node = (pm_until_node_t*) node;
77
+ current_type = CONTROL_TYPE_UNTIL;
78
+ keyword_offset = (uint32_t) (until_node->keyword_loc.start - context->source_start);
79
+ break;
80
+ }
81
+
82
+ case PM_FOR_NODE: {
83
+ pm_for_node_t* for_node = (pm_for_node_t*) node;
84
+ current_type = CONTROL_TYPE_FOR;
85
+ keyword_offset = (uint32_t) (for_node->for_keyword_loc.start - context->source_start);
86
+ break;
87
+ }
88
+
89
+ case PM_BEGIN_NODE: {
90
+ pm_begin_node_t* begin_node = (pm_begin_node_t*) node;
91
+ current_type = CONTROL_TYPE_BEGIN;
92
+
93
+ if (begin_node->begin_keyword_loc.start != NULL) {
94
+ keyword_offset = (uint32_t) (begin_node->begin_keyword_loc.start - context->source_start);
95
+ } else {
96
+ keyword_offset = (uint32_t) (node->location.start - context->source_start);
97
+ }
98
+ break;
99
+ }
100
+
101
+ case PM_YIELD_NODE: {
102
+ current_type = CONTROL_TYPE_YIELD;
103
+ keyword_offset = (uint32_t) (node->location.start - context->source_start);
104
+ break;
105
+ }
106
+
107
+ case PM_CALL_NODE: {
108
+ pm_call_node_t* call = (pm_call_node_t*) node;
109
+
110
+ if (call->block != NULL && call->block->type == PM_BLOCK_NODE) {
111
+ pm_block_node_t* block_node = (pm_block_node_t*) call->block;
112
+
113
+ bool has_do_opening = is_do_block(block_node->opening_loc);
114
+ bool has_brace_opening = is_brace_block(block_node->opening_loc);
115
+ bool has_valid_brace_closing = is_closing_brace(block_node->closing_loc);
116
+
117
+ if (has_do_opening || (has_brace_opening && !has_valid_brace_closing)) {
118
+ current_type = CONTROL_TYPE_BLOCK;
119
+ keyword_offset = (uint32_t) (node->location.start - context->source_start);
120
+ }
121
+ }
122
+ break;
123
+ }
124
+
125
+ case PM_LAMBDA_NODE: {
126
+ pm_lambda_node_t* lambda = (pm_lambda_node_t*) node;
127
+
128
+ bool has_do_opening = is_do_block(lambda->opening_loc);
129
+ bool has_brace_opening = is_brace_block(lambda->opening_loc);
130
+ bool has_valid_brace_closing = is_closing_brace(lambda->closing_loc);
131
+
132
+ if (has_do_opening || (has_brace_opening && !has_valid_brace_closing)) {
133
+ current_type = CONTROL_TYPE_BLOCK;
134
+ keyword_offset = (uint32_t) (node->location.start - context->source_start);
135
+ }
136
+
137
+ break;
138
+ }
139
+
140
+ case PM_NEXT_NODE:
141
+ case PM_BREAK_NODE:
142
+ case PM_RETURN_NODE: {
143
+ current_type = CONTROL_TYPE_UNKNOWN;
144
+ keyword_offset = (uint32_t) (node->location.start - context->source_start);
145
+ break;
146
+ }
147
+
148
+ default: break;
149
+ }
150
+
151
+ if (keyword_offset != UINT32_MAX) {
152
+ bool should_update = !result->found;
153
+
154
+ if (result->found) {
155
+ if (control_type_is_block(current_type) && control_type_is_yield(result->type)) {
156
+ should_update = true;
157
+ } else if (!(control_type_is_yield(current_type) && control_type_is_block(result->type))) {
158
+ should_update = keyword_offset < result->offset;
159
+ }
160
+ }
161
+
162
+ if (should_update) {
163
+ result->type = current_type;
164
+ result->offset = keyword_offset;
165
+ result->found = true;
166
+ }
167
+ }
168
+
169
+ return true;
170
+ }
171
+
172
+ static control_type_t find_earliest_control_keyword(pm_node_t* root, const uint8_t* source_start) {
173
+ if (!root) { return CONTROL_TYPE_UNKNOWN; }
174
+
175
+ earliest_control_keyword_T result = { .type = CONTROL_TYPE_UNKNOWN, .offset = UINT32_MAX, .found = false };
176
+
177
+ location_walker_context_T context = { .result = &result, .source_start = source_start };
178
+
179
+ pm_visit_node(root, find_earliest_control_keyword_walker, &context);
180
+
181
+ return result.found ? result.type : CONTROL_TYPE_UNKNOWN;
182
+ }
183
+
184
+ control_type_t detect_control_type(AST_ERB_CONTENT_NODE_T* erb_node) {
185
+ if (!erb_node || erb_node->base.type != AST_ERB_CONTENT_NODE) { return CONTROL_TYPE_UNKNOWN; }
186
+ if (erb_node->tag_closing == NULL) { return CONTROL_TYPE_UNKNOWN; }
187
+
188
+ analyzed_ruby_T* ruby = erb_node->analyzed_ruby;
189
+
190
+ if (!ruby) { return CONTROL_TYPE_UNKNOWN; }
191
+ if (ruby->valid) { return CONTROL_TYPE_UNKNOWN; }
192
+
193
+ pm_node_t* root = ruby->root;
194
+
195
+ if (has_elsif_node(ruby)) { return CONTROL_TYPE_ELSIF; }
196
+ if (has_else_node(ruby)) { return CONTROL_TYPE_ELSE; }
197
+ if (has_end(ruby)) { return CONTROL_TYPE_END; }
198
+ if (has_when_node(ruby) && !has_case_node(ruby)) { return CONTROL_TYPE_WHEN; }
199
+ if (has_in_node(ruby) && !has_case_match_node(ruby)) { return CONTROL_TYPE_IN; }
200
+ if (has_rescue_node(ruby)) { return CONTROL_TYPE_RESCUE; }
201
+ if (has_ensure_node(ruby)) { return CONTROL_TYPE_ENSURE; }
202
+ if (has_block_closing(ruby)) { return CONTROL_TYPE_BLOCK_CLOSE; }
203
+
204
+ if (ruby->unclosed_control_flow_count == 0 && !has_yield_node(ruby)) { return CONTROL_TYPE_UNKNOWN; }
205
+
206
+ return find_earliest_control_keyword(root, ruby->parser.start);
207
+ }
208
+
209
+ bool is_subsequent_type(control_type_t parent_type, control_type_t child_type) {
210
+ switch (parent_type) {
211
+ case CONTROL_TYPE_IF:
212
+ case CONTROL_TYPE_ELSIF: return child_type == CONTROL_TYPE_ELSIF || child_type == CONTROL_TYPE_ELSE;
213
+ case CONTROL_TYPE_CASE:
214
+ case CONTROL_TYPE_CASE_MATCH: return child_type == CONTROL_TYPE_WHEN || child_type == CONTROL_TYPE_ELSE;
215
+ case CONTROL_TYPE_BEGIN:
216
+ return child_type == CONTROL_TYPE_RESCUE || child_type == CONTROL_TYPE_ELSE || child_type == CONTROL_TYPE_ENSURE;
217
+ case CONTROL_TYPE_RESCUE: return child_type == CONTROL_TYPE_RESCUE;
218
+ case CONTROL_TYPE_UNLESS: return child_type == CONTROL_TYPE_ELSE;
219
+
220
+ default: return false;
221
+ }
222
+ }
223
+
224
+ bool is_terminator_type(control_type_t parent_type, control_type_t child_type) {
225
+ if (child_type == CONTROL_TYPE_END) { return true; }
226
+
227
+ switch (parent_type) {
228
+ case CONTROL_TYPE_WHEN: return child_type == CONTROL_TYPE_WHEN || child_type == CONTROL_TYPE_ELSE;
229
+ case CONTROL_TYPE_IN: return child_type == CONTROL_TYPE_IN || child_type == CONTROL_TYPE_ELSE;
230
+ case CONTROL_TYPE_BLOCK: return child_type == CONTROL_TYPE_BLOCK_CLOSE;
231
+
232
+ default: return is_subsequent_type(parent_type, child_type);
233
+ }
234
+ }
235
+
236
+ bool is_compound_control_type(control_type_t type) {
237
+ switch (type) {
238
+ case CONTROL_TYPE_IF:
239
+ case CONTROL_TYPE_CASE:
240
+ case CONTROL_TYPE_CASE_MATCH:
241
+ case CONTROL_TYPE_BEGIN:
242
+ case CONTROL_TYPE_UNLESS:
243
+ case CONTROL_TYPE_WHILE:
244
+ case CONTROL_TYPE_UNTIL:
245
+ case CONTROL_TYPE_FOR:
246
+ case CONTROL_TYPE_BLOCK: return true;
247
+
248
+ default: return false;
249
+ }
250
+ }
@@ -0,0 +1,14 @@
1
+ #ifndef HERB_ANALYZE_CONTROL_TYPE_H
2
+ #define HERB_ANALYZE_CONTROL_TYPE_H
3
+
4
+ #include "../ast_nodes.h"
5
+ #include "analyze.h"
6
+
7
+ #include <stdbool.h>
8
+
9
+ control_type_t detect_control_type(AST_ERB_CONTENT_NODE_T* erb_node);
10
+ bool is_subsequent_type(control_type_t parent_type, control_type_t child_type);
11
+ bool is_terminator_type(control_type_t parent_type, control_type_t child_type);
12
+ bool is_compound_control_type(control_type_t type);
13
+
14
+ #endif
@@ -2,87 +2,92 @@
2
2
  #include <stdbool.h>
3
3
  #include <string.h>
4
4
 
5
- #include "include/analyzed_ruby.h"
6
- #include "include/util/string.h"
5
+ #include "../include/analyze/analyzed_ruby.h"
6
+ #include "../include/util/string.h"
7
7
 
8
8
  bool has_if_node(analyzed_ruby_T* analyzed) {
9
- return analyzed->if_node_count > 0;
9
+ return analyzed && analyzed->if_node_count > 0;
10
10
  }
11
11
 
12
12
  bool has_elsif_node(analyzed_ruby_T* analyzed) {
13
- return analyzed->elsif_node_count > 0;
13
+ return analyzed && analyzed->elsif_node_count > 0;
14
14
  }
15
15
 
16
16
  bool has_else_node(analyzed_ruby_T* analyzed) {
17
- return analyzed->else_node_count > 0;
17
+ return analyzed && analyzed->else_node_count > 0;
18
18
  }
19
19
 
20
20
  bool has_end(analyzed_ruby_T* analyzed) {
21
- return analyzed->end_count > 0;
21
+ return analyzed && analyzed->end_count > 0;
22
22
  }
23
23
 
24
24
  bool has_block_node(analyzed_ruby_T* analyzed) {
25
- return analyzed->block_node_count > 0;
25
+ return analyzed && analyzed->block_node_count > 0;
26
26
  }
27
27
 
28
28
  bool has_block_closing(analyzed_ruby_T* analyzed) {
29
- return analyzed->block_closing_count > 0;
29
+ return analyzed && analyzed->block_closing_count > 0;
30
30
  }
31
31
 
32
32
  bool has_case_node(analyzed_ruby_T* analyzed) {
33
- return analyzed->case_node_count > 0;
33
+ return analyzed && analyzed->case_node_count > 0;
34
34
  }
35
35
 
36
36
  bool has_case_match_node(analyzed_ruby_T* analyzed) {
37
- return analyzed->case_match_node_count > 0;
37
+ return analyzed && analyzed->case_match_node_count > 0;
38
38
  }
39
39
 
40
40
  bool has_when_node(analyzed_ruby_T* analyzed) {
41
- return analyzed->when_node_count > 0;
41
+ return analyzed && analyzed->when_node_count > 0;
42
42
  }
43
43
 
44
44
  bool has_in_node(analyzed_ruby_T* analyzed) {
45
- return analyzed->in_node_count > 0;
45
+ return analyzed && analyzed->in_node_count > 0;
46
46
  }
47
47
 
48
48
  bool has_for_node(analyzed_ruby_T* analyzed) {
49
- return analyzed->for_node_count > 0;
49
+ return analyzed && analyzed->for_node_count > 0;
50
50
  }
51
51
 
52
52
  bool has_while_node(analyzed_ruby_T* analyzed) {
53
- return analyzed->while_node_count > 0;
53
+ return analyzed && analyzed->while_node_count > 0;
54
54
  }
55
55
 
56
56
  bool has_until_node(analyzed_ruby_T* analyzed) {
57
- return analyzed->until_node_count > 0;
57
+ return analyzed && analyzed->until_node_count > 0;
58
58
  }
59
59
 
60
60
  bool has_begin_node(analyzed_ruby_T* analyzed) {
61
- return analyzed->begin_node_count > 0;
61
+ return analyzed && analyzed->begin_node_count > 0;
62
62
  }
63
63
 
64
64
  bool has_rescue_node(analyzed_ruby_T* analyzed) {
65
- return analyzed->rescue_node_count > 0;
65
+ return analyzed && analyzed->rescue_node_count > 0;
66
66
  }
67
67
 
68
68
  bool has_ensure_node(analyzed_ruby_T* analyzed) {
69
- return analyzed->ensure_node_count > 0;
69
+ return analyzed && analyzed->ensure_node_count > 0;
70
70
  }
71
71
 
72
72
  bool has_unless_node(analyzed_ruby_T* analyzed) {
73
- return analyzed->unless_node_count > 0;
73
+ return analyzed && analyzed->unless_node_count > 0;
74
74
  }
75
75
 
76
76
  bool has_yield_node(analyzed_ruby_T* analyzed) {
77
- return analyzed->yield_node_count > 0;
77
+ return analyzed && analyzed->yield_node_count > 0;
78
78
  }
79
79
 
80
80
  bool has_then_keyword(analyzed_ruby_T* analyzed) {
81
- return analyzed->then_keyword_count > 0;
81
+ return analyzed && analyzed->then_keyword_count > 0;
82
82
  }
83
83
 
84
- bool has_error_message(analyzed_ruby_T* anlayzed, const char* message) {
85
- for (const pm_diagnostic_t* error = (const pm_diagnostic_t*) anlayzed->parser.error_list.head; error != NULL;
84
+ bool has_inline_case_condition(analyzed_ruby_T* analyzed) {
85
+ return (has_case_node(analyzed) && has_when_node(analyzed))
86
+ || (has_case_match_node(analyzed) && has_in_node(analyzed));
87
+ }
88
+
89
+ bool has_error_message(analyzed_ruby_T* analyzed, const char* message) {
90
+ for (const pm_diagnostic_t* error = (const pm_diagnostic_t*) analyzed->parser.error_list.head; error != NULL;
86
91
  error = (const pm_diagnostic_t*) error->node.next) {
87
92
  if (string_equals(error->message, message)) { return true; }
88
93
  }
@@ -161,6 +166,15 @@ bool search_block_nodes(const pm_node_t* node, void* data) {
161
166
  if (has_opening && is_unclosed) { analyzed->block_node_count++; }
162
167
  }
163
168
 
169
+ if (node->type == PM_LAMBDA_NODE) {
170
+ pm_lambda_node_t* lambda_node = (pm_lambda_node_t*) node;
171
+
172
+ bool has_opening = is_do_block(lambda_node->opening_loc) || is_brace_block(lambda_node->opening_loc);
173
+ bool is_unclosed = !has_valid_block_closing(lambda_node->opening_loc, lambda_node->closing_loc);
174
+
175
+ if (has_opening && is_unclosed) { analyzed->block_node_count++; }
176
+ }
177
+
164
178
  pm_visit_child_nodes(node, search_block_nodes, analyzed);
165
179
 
166
180
  return false;
@@ -494,6 +508,17 @@ bool search_unclosed_control_flows(const pm_node_t* node, void* data) {
494
508
  break;
495
509
  }
496
510
 
511
+ case PM_LAMBDA_NODE: {
512
+ const pm_lambda_node_t* lambda_node = (const pm_lambda_node_t*) node;
513
+ bool has_opening = is_do_block(lambda_node->opening_loc) || is_brace_block(lambda_node->opening_loc);
514
+
515
+ if (has_opening && !has_valid_block_closing(lambda_node->opening_loc, lambda_node->closing_loc)) {
516
+ analyzed->unclosed_control_flow_count++;
517
+ }
518
+
519
+ break;
520
+ }
521
+
497
522
  default: break;
498
523
  }
499
524
 
@@ -4,8 +4,9 @@
4
4
  #include <prism.h>
5
5
  #include <stdbool.h>
6
6
 
7
+ #include "../ast_node.h"
8
+ #include "../util/hb_allocator.h"
7
9
  #include "analyzed_ruby.h"
8
- #include "ast_node.h"
9
10
 
10
11
  bool has_if_node(analyzed_ruby_T* analyzed);
11
12
  bool has_elsif_node(analyzed_ruby_T* analyzed);
@@ -26,6 +27,7 @@ bool has_ensure_node(analyzed_ruby_T* analyzed);
26
27
  bool has_unless_node(analyzed_ruby_T* analyzed);
27
28
  bool has_yield_node(analyzed_ruby_T* analyzed);
28
29
  bool has_then_keyword(analyzed_ruby_T* analyzed);
30
+ bool has_inline_case_condition(analyzed_ruby_T* analyzed);
29
31
 
30
32
  bool has_error_message(analyzed_ruby_T* anlayzed, const char* message);
31
33
 
@@ -58,6 +60,6 @@ bool search_unexpected_in_nodes(analyzed_ruby_T* analyzed);
58
60
  bool search_unexpected_rescue_nodes(analyzed_ruby_T* analyzed);
59
61
  bool search_unexpected_when_nodes(analyzed_ruby_T* analyzed);
60
62
 
61
- void check_erb_node_for_missing_end(const AST_NODE_T* node);
63
+ void check_erb_node_for_missing_end(const AST_NODE_T* node, hb_allocator_T* allocator);
62
64
 
63
65
  #endif
@@ -0,0 +1,193 @@
1
+ #include "../include/analyze/invalid_structures.h"
2
+ #include "../include/analyze/analyze.h"
3
+ #include "../include/analyze/analyzed_ruby.h"
4
+ #include "../include/analyze/helpers.h"
5
+ #include "../include/ast_node.h"
6
+ #include "../include/ast_nodes.h"
7
+ #include "../include/errors.h"
8
+ #include "../include/token_struct.h"
9
+ #include "../include/util/hb_array.h"
10
+ #include "../include/util/hb_string.h"
11
+ #include "../include/visitor.h"
12
+
13
+ #include <stdbool.h>
14
+ #include <stddef.h>
15
+
16
+ bool detect_invalid_erb_structures(const AST_NODE_T* node, void* data) {
17
+ invalid_erb_context_T* context = (invalid_erb_context_T*) data;
18
+
19
+ if (node->type == AST_HTML_ATTRIBUTE_NAME_NODE) { return false; }
20
+
21
+ bool is_begin_node = (node->type == AST_ERB_BEGIN_NODE);
22
+ bool is_loop_node =
23
+ (node->type == AST_ERB_WHILE_NODE || node->type == AST_ERB_UNTIL_NODE || node->type == AST_ERB_FOR_NODE
24
+ || node->type == AST_ERB_BLOCK_NODE);
25
+
26
+ if (is_loop_node) { context->loop_depth++; }
27
+ if (is_begin_node) { context->rescue_depth++; }
28
+
29
+ if (node->type == AST_ERB_CONTENT_NODE) {
30
+ const AST_ERB_CONTENT_NODE_T* content_node = (const AST_ERB_CONTENT_NODE_T*) node;
31
+
32
+ if (content_node->parsed && !content_node->valid && content_node->analyzed_ruby != NULL) {
33
+ analyzed_ruby_T* analyzed = content_node->analyzed_ruby;
34
+
35
+ // =begin
36
+ if (has_error_message(analyzed, "embedded document meets end of file")) {
37
+ if (is_loop_node) { context->loop_depth--; }
38
+ if (is_begin_node) { context->rescue_depth--; }
39
+
40
+ return true;
41
+ }
42
+
43
+ // =end
44
+ if (has_error_message(analyzed, "unexpected '=', ignoring it")
45
+ && has_error_message(analyzed, "unexpected 'end', ignoring it")) {
46
+ if (is_loop_node) { context->loop_depth--; }
47
+ if (is_begin_node) { context->rescue_depth--; }
48
+
49
+ return true;
50
+ }
51
+
52
+ hb_string_T keyword = HB_STRING_NULL;
53
+
54
+ if (context->loop_depth == 0) {
55
+ if (has_error_message(analyzed, "Invalid break")) {
56
+ keyword = hb_string("`<% break %>`");
57
+ } else if (has_error_message(analyzed, "Invalid next")) {
58
+ keyword = hb_string("`<% next %>`");
59
+ } else if (has_error_message(analyzed, "Invalid redo")) {
60
+ keyword = hb_string("`<% redo %>`");
61
+ }
62
+ } else {
63
+ if (has_error_message(analyzed, "Invalid redo") || has_error_message(analyzed, "Invalid break")
64
+ || has_error_message(analyzed, "Invalid next")) {
65
+
66
+ if (is_loop_node) { context->loop_depth--; }
67
+ if (is_begin_node) { context->rescue_depth--; }
68
+
69
+ return true;
70
+ }
71
+ }
72
+
73
+ if (context->rescue_depth == 0) {
74
+ if (has_error_message(analyzed, "Invalid retry without rescue")) { keyword = hb_string("`<% retry %>`"); }
75
+ } else {
76
+ if (has_error_message(analyzed, "Invalid retry without rescue")) {
77
+ if (is_loop_node) { context->loop_depth--; }
78
+ if (is_begin_node) { context->rescue_depth--; }
79
+
80
+ return true;
81
+ }
82
+ }
83
+
84
+ if (hb_string_is_null(keyword)) { keyword = erb_keyword_from_analyzed_ruby(analyzed); }
85
+
86
+ if (!hb_string_is_null(keyword) && !token_value_empty(content_node->tag_closing)) {
87
+ append_erb_control_flow_scope_error(
88
+ keyword,
89
+ node->location.start,
90
+ node->location.end,
91
+ context->allocator,
92
+ node->errors
93
+ );
94
+ }
95
+ }
96
+ }
97
+
98
+ if (node->type == AST_ERB_IF_NODE) {
99
+ const AST_ERB_IF_NODE_T* if_node = (const AST_ERB_IF_NODE_T*) node;
100
+
101
+ if (if_node->end_node == NULL) { check_erb_node_for_missing_end(node, context->allocator); }
102
+
103
+ if (if_node->statements != NULL) {
104
+ for (size_t i = 0; i < hb_array_size(if_node->statements); i++) {
105
+ AST_NODE_T* statement = (AST_NODE_T*) hb_array_get(if_node->statements, i);
106
+
107
+ if (statement != NULL) { herb_visit_node(statement, detect_invalid_erb_structures, context); }
108
+ }
109
+ }
110
+
111
+ AST_NODE_T* subsequent = if_node->subsequent;
112
+
113
+ while (subsequent != NULL) {
114
+ if (subsequent->type == AST_ERB_CONTENT_NODE) {
115
+ const AST_ERB_CONTENT_NODE_T* content_node = (const AST_ERB_CONTENT_NODE_T*) subsequent;
116
+
117
+ if (content_node->parsed && !content_node->valid && content_node->analyzed_ruby != NULL) {
118
+ analyzed_ruby_T* analyzed = content_node->analyzed_ruby;
119
+ hb_string_T keyword = erb_keyword_from_analyzed_ruby(analyzed);
120
+
121
+ if (!token_value_empty(content_node->tag_closing)) {
122
+ append_erb_control_flow_scope_error(
123
+ keyword,
124
+ subsequent->location.start,
125
+ subsequent->location.end,
126
+ context->allocator,
127
+ subsequent->errors
128
+ );
129
+ }
130
+ }
131
+ }
132
+
133
+ if (subsequent->type == AST_ERB_IF_NODE) {
134
+ const AST_ERB_IF_NODE_T* elsif_node = (const AST_ERB_IF_NODE_T*) subsequent;
135
+
136
+ if (elsif_node->statements != NULL) {
137
+ for (size_t i = 0; i < hb_array_size(elsif_node->statements); i++) {
138
+ AST_NODE_T* statement = (AST_NODE_T*) hb_array_get(elsif_node->statements, i);
139
+
140
+ if (statement != NULL) { herb_visit_node(statement, detect_invalid_erb_structures, context); }
141
+ }
142
+ }
143
+
144
+ subsequent = elsif_node->subsequent;
145
+ } else if (subsequent->type == AST_ERB_ELSE_NODE) {
146
+ const AST_ERB_ELSE_NODE_T* else_node = (const AST_ERB_ELSE_NODE_T*) subsequent;
147
+
148
+ if (else_node->statements != NULL) {
149
+ for (size_t i = 0; i < hb_array_size(else_node->statements); i++) {
150
+ AST_NODE_T* statement = (AST_NODE_T*) hb_array_get(else_node->statements, i);
151
+
152
+ if (statement != NULL) { herb_visit_node(statement, detect_invalid_erb_structures, context); }
153
+ }
154
+ }
155
+
156
+ break;
157
+ } else {
158
+ break;
159
+ }
160
+ }
161
+ }
162
+
163
+ if (node->type == AST_ERB_UNLESS_NODE || node->type == AST_ERB_WHILE_NODE || node->type == AST_ERB_UNTIL_NODE
164
+ || node->type == AST_ERB_FOR_NODE || node->type == AST_ERB_CASE_NODE || node->type == AST_ERB_CASE_MATCH_NODE
165
+ || node->type == AST_ERB_BEGIN_NODE || node->type == AST_ERB_BLOCK_NODE || node->type == AST_ERB_ELSE_NODE) {
166
+ herb_visit_child_nodes(node, detect_invalid_erb_structures, context);
167
+ }
168
+
169
+ if (node->type == AST_ERB_UNLESS_NODE || node->type == AST_ERB_WHILE_NODE || node->type == AST_ERB_UNTIL_NODE
170
+ || node->type == AST_ERB_FOR_NODE || node->type == AST_ERB_CASE_NODE || node->type == AST_ERB_CASE_MATCH_NODE
171
+ || node->type == AST_ERB_BEGIN_NODE || node->type == AST_ERB_BLOCK_NODE || node->type == AST_ERB_ELSE_NODE) {
172
+ check_erb_node_for_missing_end(node, context->allocator);
173
+
174
+ if (is_loop_node) { context->loop_depth--; }
175
+ if (is_begin_node) { context->rescue_depth--; }
176
+
177
+ return false;
178
+ }
179
+
180
+ if (node->type == AST_ERB_IF_NODE) {
181
+ if (is_loop_node) { context->loop_depth--; }
182
+ if (is_begin_node) { context->rescue_depth--; }
183
+
184
+ return false;
185
+ }
186
+
187
+ bool result = true;
188
+
189
+ if (is_loop_node) { context->loop_depth--; }
190
+ if (is_begin_node) { context->rescue_depth--; }
191
+
192
+ return result;
193
+ }
@@ -0,0 +1,11 @@
1
+ #ifndef HERB_ANALYZE_INVALID_STRUCTURES_H
2
+ #define HERB_ANALYZE_INVALID_STRUCTURES_H
3
+
4
+ #include "../ast_node.h"
5
+ #include "analyze.h"
6
+
7
+ #include <stdbool.h>
8
+
9
+ bool detect_invalid_erb_structures(const AST_NODE_T* node, void* data);
10
+
11
+ #endif