herb 0.8.4 → 0.8.5

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.
@@ -5,75 +5,75 @@
5
5
  #include "include/analyzed_ruby.h"
6
6
 
7
7
  bool has_if_node(analyzed_ruby_T* analyzed) {
8
- return analyzed->has_if_node;
8
+ return analyzed->if_node_count > 0;
9
9
  }
10
10
 
11
11
  bool has_elsif_node(analyzed_ruby_T* analyzed) {
12
- return analyzed->has_elsif_node;
12
+ return analyzed->elsif_node_count > 0;
13
13
  }
14
14
 
15
15
  bool has_else_node(analyzed_ruby_T* analyzed) {
16
- return analyzed->has_else_node;
16
+ return analyzed->else_node_count > 0;
17
17
  }
18
18
 
19
19
  bool has_end(analyzed_ruby_T* analyzed) {
20
- return analyzed->has_end;
20
+ return analyzed->end_count > 0;
21
21
  }
22
22
 
23
23
  bool has_block_node(analyzed_ruby_T* analyzed) {
24
- return analyzed->has_block_node;
24
+ return analyzed->block_node_count > 0;
25
25
  }
26
26
 
27
27
  bool has_block_closing(analyzed_ruby_T* analyzed) {
28
- return analyzed->has_block_closing;
28
+ return analyzed->block_closing_count > 0;
29
29
  }
30
30
 
31
31
  bool has_case_node(analyzed_ruby_T* analyzed) {
32
- return analyzed->has_case_node;
32
+ return analyzed->case_node_count > 0;
33
33
  }
34
34
 
35
35
  bool has_case_match_node(analyzed_ruby_T* analyzed) {
36
- return analyzed->has_case_match_node;
36
+ return analyzed->case_match_node_count > 0;
37
37
  }
38
38
 
39
39
  bool has_when_node(analyzed_ruby_T* analyzed) {
40
- return analyzed->has_when_node;
40
+ return analyzed->when_node_count > 0;
41
41
  }
42
42
 
43
43
  bool has_in_node(analyzed_ruby_T* analyzed) {
44
- return analyzed->has_in_node;
44
+ return analyzed->in_node_count > 0;
45
45
  }
46
46
 
47
47
  bool has_for_node(analyzed_ruby_T* analyzed) {
48
- return analyzed->has_for_node;
48
+ return analyzed->for_node_count > 0;
49
49
  }
50
50
 
51
51
  bool has_while_node(analyzed_ruby_T* analyzed) {
52
- return analyzed->has_while_node;
52
+ return analyzed->while_node_count > 0;
53
53
  }
54
54
 
55
55
  bool has_until_node(analyzed_ruby_T* analyzed) {
56
- return analyzed->has_until_node;
56
+ return analyzed->until_node_count > 0;
57
57
  }
58
58
 
59
59
  bool has_begin_node(analyzed_ruby_T* analyzed) {
60
- return analyzed->has_begin_node;
60
+ return analyzed->begin_node_count > 0;
61
61
  }
62
62
 
63
63
  bool has_rescue_node(analyzed_ruby_T* analyzed) {
64
- return analyzed->has_rescue_node;
64
+ return analyzed->rescue_node_count > 0;
65
65
  }
66
66
 
67
67
  bool has_ensure_node(analyzed_ruby_T* analyzed) {
68
- return analyzed->has_ensure_node;
68
+ return analyzed->ensure_node_count > 0;
69
69
  }
70
70
 
71
71
  bool has_unless_node(analyzed_ruby_T* analyzed) {
72
- return analyzed->has_unless_node;
72
+ return analyzed->unless_node_count > 0;
73
73
  }
74
74
 
75
75
  bool has_yield_node(analyzed_ruby_T* analyzed) {
76
- return analyzed->has_yield_node;
76
+ return analyzed->yield_node_count > 0;
77
77
  }
78
78
 
79
79
  bool has_error_message(analyzed_ruby_T* anlayzed, const char* message) {
@@ -94,11 +94,7 @@ bool search_if_nodes(const pm_node_t* node, void* data) {
94
94
  bool has_if_keyword = if_node->if_keyword_loc.start != NULL && if_node->if_keyword_loc.end != NULL;
95
95
  bool has_end_keyword = if_node->end_keyword_loc.start != NULL && if_node->end_keyword_loc.end != NULL;
96
96
 
97
- if (has_if_keyword && has_end_keyword) {
98
- analyzed->has_if_node = true;
99
-
100
- return true;
101
- }
97
+ if (has_if_keyword && has_end_keyword) { analyzed->if_node_count++; }
102
98
  }
103
99
 
104
100
  pm_visit_child_nodes(node, search_if_nodes, analyzed);
@@ -106,20 +102,32 @@ bool search_if_nodes(const pm_node_t* node, void* data) {
106
102
  return false;
107
103
  }
108
104
 
105
+ static bool is_do_block(pm_location_t opening_location) {
106
+ size_t length = opening_location.end - opening_location.start;
107
+
108
+ return length == 2 && opening_location.start[0] == 'd' && opening_location.start[1] == 'o';
109
+ }
110
+
111
+ static bool is_brace_block(pm_location_t opening_location) {
112
+ size_t length = opening_location.end - opening_location.start;
113
+
114
+ return length == 1 && opening_location.start[0] == '{';
115
+ }
116
+
117
+ static bool has_location(pm_location_t location) {
118
+ return location.start != NULL && location.end != NULL && (location.end - location.start) > 0;
119
+ }
120
+
109
121
  bool search_block_nodes(const pm_node_t* node, void* data) {
110
122
  analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
111
123
 
112
124
  if (node->type == PM_BLOCK_NODE) {
113
125
  pm_block_node_t* block_node = (pm_block_node_t*) node;
114
126
 
115
- size_t opening_length = block_node->opening_loc.end - block_node->opening_loc.start;
127
+ bool has_opening = is_do_block(block_node->opening_loc) || is_brace_block(block_node->opening_loc);
128
+ bool is_unclosed = !has_location(block_node->closing_loc);
116
129
 
117
- if ((opening_length == 2 && block_node->opening_loc.start[0] == 'd' && block_node->opening_loc.start[1] == 'o')
118
- || (opening_length == 1 && block_node->opening_loc.start[0] == '{')) {
119
- analyzed->has_block_node = true;
120
-
121
- return true;
122
- }
130
+ if (has_opening && is_unclosed) { analyzed->block_node_count++; }
123
131
  }
124
132
 
125
133
  pm_visit_child_nodes(node, search_block_nodes, analyzed);
@@ -130,12 +138,9 @@ bool search_block_nodes(const pm_node_t* node, void* data) {
130
138
  bool search_case_nodes(const pm_node_t* node, void* data) {
131
139
  analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
132
140
 
133
- if (node->type == PM_CASE_NODE) {
134
- analyzed->has_case_node = true;
135
- return true;
136
- } else {
137
- pm_visit_child_nodes(node, search_case_nodes, analyzed);
138
- }
141
+ if (node->type == PM_CASE_NODE) { analyzed->case_node_count++; }
142
+
143
+ pm_visit_child_nodes(node, search_case_nodes, analyzed);
139
144
 
140
145
  return false;
141
146
  }
@@ -143,12 +148,9 @@ bool search_case_nodes(const pm_node_t* node, void* data) {
143
148
  bool search_case_match_nodes(const pm_node_t* node, void* data) {
144
149
  analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
145
150
 
146
- if (node->type == PM_CASE_MATCH_NODE) {
147
- analyzed->has_case_match_node = true;
148
- return true;
149
- } else {
150
- pm_visit_child_nodes(node, search_case_match_nodes, analyzed);
151
- }
151
+ if (node->type == PM_CASE_MATCH_NODE) { analyzed->case_match_node_count++; }
152
+
153
+ pm_visit_child_nodes(node, search_case_match_nodes, analyzed);
152
154
 
153
155
  return false;
154
156
  }
@@ -156,12 +158,9 @@ bool search_case_match_nodes(const pm_node_t* node, void* data) {
156
158
  bool search_while_nodes(const pm_node_t* node, void* data) {
157
159
  analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
158
160
 
159
- if (node->type == PM_WHILE_NODE) {
160
- analyzed->has_while_node = true;
161
- return true;
162
- } else {
163
- pm_visit_child_nodes(node, search_while_nodes, analyzed);
164
- }
161
+ if (node->type == PM_WHILE_NODE) { analyzed->while_node_count++; }
162
+
163
+ pm_visit_child_nodes(node, search_while_nodes, analyzed);
165
164
 
166
165
  return false;
167
166
  }
@@ -169,12 +168,9 @@ bool search_while_nodes(const pm_node_t* node, void* data) {
169
168
  bool search_for_nodes(const pm_node_t* node, void* data) {
170
169
  analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
171
170
 
172
- if (node->type == PM_FOR_NODE) {
173
- analyzed->has_for_node = true;
174
- return true;
175
- } else {
176
- pm_visit_child_nodes(node, search_for_nodes, analyzed);
177
- }
171
+ if (node->type == PM_FOR_NODE) { analyzed->for_node_count++; }
172
+
173
+ pm_visit_child_nodes(node, search_for_nodes, analyzed);
178
174
 
179
175
  return false;
180
176
  }
@@ -182,12 +178,9 @@ bool search_for_nodes(const pm_node_t* node, void* data) {
182
178
  bool search_until_nodes(const pm_node_t* node, void* data) {
183
179
  analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
184
180
 
185
- if (node->type == PM_UNTIL_NODE) {
186
- analyzed->has_until_node = true;
187
- return true;
188
- } else {
189
- pm_visit_child_nodes(node, search_until_nodes, analyzed);
190
- }
181
+ if (node->type == PM_UNTIL_NODE) { analyzed->until_node_count++; }
182
+
183
+ pm_visit_child_nodes(node, search_until_nodes, analyzed);
191
184
 
192
185
  return false;
193
186
  }
@@ -195,12 +188,9 @@ bool search_until_nodes(const pm_node_t* node, void* data) {
195
188
  bool search_begin_nodes(const pm_node_t* node, void* data) {
196
189
  analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
197
190
 
198
- if (node->type == PM_BEGIN_NODE) {
199
- analyzed->has_begin_node = true;
200
- return true;
201
- } else {
202
- pm_visit_child_nodes(node, search_begin_nodes, analyzed);
203
- }
191
+ if (node->type == PM_BEGIN_NODE) { analyzed->begin_node_count++; }
192
+
193
+ pm_visit_child_nodes(node, search_begin_nodes, analyzed);
204
194
 
205
195
  return false;
206
196
  }
@@ -214,11 +204,7 @@ bool search_unless_nodes(const pm_node_t* node, void* data) {
214
204
  bool has_if_keyword = unless_node->keyword_loc.start != NULL && unless_node->keyword_loc.end != NULL;
215
205
  bool has_end_keyword = unless_node->end_keyword_loc.start != NULL && unless_node->end_keyword_loc.end != NULL;
216
206
 
217
- if (has_if_keyword && has_end_keyword) {
218
- analyzed->has_unless_node = true;
219
-
220
- return true;
221
- }
207
+ if (has_if_keyword && has_end_keyword) { analyzed->unless_node_count++; }
222
208
  }
223
209
 
224
210
  pm_visit_child_nodes(node, search_unless_nodes, analyzed);
@@ -228,7 +214,7 @@ bool search_unless_nodes(const pm_node_t* node, void* data) {
228
214
 
229
215
  bool search_elsif_nodes(analyzed_ruby_T* analyzed) {
230
216
  if (has_error_message(analyzed, "unexpected 'elsif', ignoring it")) {
231
- analyzed->has_elsif_node = true;
217
+ analyzed->elsif_node_count++;
232
218
  return true;
233
219
  }
234
220
 
@@ -237,7 +223,7 @@ bool search_elsif_nodes(analyzed_ruby_T* analyzed) {
237
223
 
238
224
  bool search_else_nodes(analyzed_ruby_T* analyzed) {
239
225
  if (has_error_message(analyzed, "unexpected 'else', ignoring it")) {
240
- analyzed->has_else_node = true;
226
+ analyzed->else_node_count++;
241
227
  return true;
242
228
  }
243
229
 
@@ -251,7 +237,7 @@ bool search_end_nodes(analyzed_ruby_T* analyzed) {
251
237
  return false;
252
238
  }
253
239
 
254
- analyzed->has_end = true;
240
+ analyzed->end_count++;
255
241
  return true;
256
242
  }
257
243
 
@@ -260,7 +246,7 @@ bool search_end_nodes(analyzed_ruby_T* analyzed) {
260
246
 
261
247
  bool search_block_closing_nodes(analyzed_ruby_T* analyzed) {
262
248
  if (has_error_message(analyzed, "unexpected '}', ignoring it")) {
263
- analyzed->has_block_closing = true;
249
+ analyzed->block_closing_count++;
264
250
  return true;
265
251
  }
266
252
 
@@ -269,7 +255,7 @@ bool search_block_closing_nodes(analyzed_ruby_T* analyzed) {
269
255
 
270
256
  bool search_when_nodes(analyzed_ruby_T* analyzed) {
271
257
  if (has_error_message(analyzed, "unexpected 'when', ignoring it")) {
272
- analyzed->has_when_node = true;
258
+ analyzed->when_node_count++;
273
259
  return true;
274
260
  }
275
261
 
@@ -278,7 +264,7 @@ bool search_when_nodes(analyzed_ruby_T* analyzed) {
278
264
 
279
265
  bool search_in_nodes(analyzed_ruby_T* analyzed) {
280
266
  if (has_error_message(analyzed, "unexpected 'in', ignoring it")) {
281
- analyzed->has_in_node = true;
267
+ analyzed->in_node_count++;
282
268
  return true;
283
269
  }
284
270
 
@@ -287,7 +273,7 @@ bool search_in_nodes(analyzed_ruby_T* analyzed) {
287
273
 
288
274
  bool search_rescue_nodes(analyzed_ruby_T* analyzed) {
289
275
  if (has_error_message(analyzed, "unexpected 'rescue', ignoring it")) {
290
- analyzed->has_rescue_node = true;
276
+ analyzed->rescue_node_count++;
291
277
  return true;
292
278
  }
293
279
 
@@ -296,7 +282,7 @@ bool search_rescue_nodes(analyzed_ruby_T* analyzed) {
296
282
 
297
283
  bool search_ensure_nodes(analyzed_ruby_T* analyzed) {
298
284
  if (has_error_message(analyzed, "unexpected 'ensure', ignoring it")) {
299
- analyzed->has_ensure_node = true;
285
+ analyzed->ensure_node_count++;
300
286
  return true;
301
287
  }
302
288
 
@@ -306,12 +292,121 @@ bool search_ensure_nodes(analyzed_ruby_T* analyzed) {
306
292
  bool search_yield_nodes(const pm_node_t* node, void* data) {
307
293
  analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
308
294
 
309
- if (node->type == PM_YIELD_NODE) {
310
- analyzed->has_yield_node = true;
311
- return true;
312
- } else {
313
- pm_visit_child_nodes(node, search_yield_nodes, analyzed);
295
+ if (node->type == PM_YIELD_NODE) { analyzed->yield_node_count++; }
296
+
297
+ pm_visit_child_nodes(node, search_yield_nodes, analyzed);
298
+
299
+ return false;
300
+ }
301
+
302
+ static bool is_postfix_conditional(const pm_statements_node_t* statements, pm_location_t keyword_location) {
303
+ if (statements == NULL) { return false; }
304
+
305
+ return statements->base.location.start < keyword_location.start;
306
+ }
307
+
308
+ bool search_unclosed_control_flows(const pm_node_t* node, void* data) {
309
+ analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
310
+
311
+ if (analyzed->unclosed_control_flow_count >= 2) { return false; }
312
+
313
+ switch (node->type) {
314
+ case PM_IF_NODE: {
315
+ const pm_if_node_t* if_node = (const pm_if_node_t*) node;
316
+
317
+ if (has_location(if_node->if_keyword_loc) && !has_location(if_node->end_keyword_loc)) {
318
+ if (!is_postfix_conditional(if_node->statements, if_node->if_keyword_loc)) {
319
+ analyzed->unclosed_control_flow_count++;
320
+ }
321
+ }
322
+
323
+ break;
324
+ }
325
+
326
+ case PM_UNLESS_NODE: {
327
+ const pm_unless_node_t* unless_node = (const pm_unless_node_t*) node;
328
+
329
+ if (has_location(unless_node->keyword_loc) && !has_location(unless_node->end_keyword_loc)) {
330
+ if (!is_postfix_conditional(unless_node->statements, unless_node->keyword_loc)) {
331
+ analyzed->unclosed_control_flow_count++;
332
+ }
333
+ }
334
+
335
+ break;
336
+ }
337
+
338
+ case PM_CASE_NODE: {
339
+ const pm_case_node_t* case_node = (const pm_case_node_t*) node;
340
+
341
+ if (has_location(case_node->case_keyword_loc) && !has_location(case_node->end_keyword_loc)) {
342
+ analyzed->unclosed_control_flow_count++;
343
+ }
344
+
345
+ break;
346
+ }
347
+
348
+ case PM_CASE_MATCH_NODE: {
349
+ const pm_case_match_node_t* case_match_node = (const pm_case_match_node_t*) node;
350
+
351
+ if (has_location(case_match_node->case_keyword_loc) && !has_location(case_match_node->end_keyword_loc)) {
352
+ analyzed->unclosed_control_flow_count++;
353
+ }
354
+
355
+ break;
356
+ }
357
+
358
+ case PM_WHILE_NODE: {
359
+ const pm_while_node_t* while_node = (const pm_while_node_t*) node;
360
+
361
+ if (has_location(while_node->keyword_loc) && !has_location(while_node->closing_loc)) {
362
+ analyzed->unclosed_control_flow_count++;
363
+ }
364
+
365
+ break;
366
+ }
367
+
368
+ case PM_UNTIL_NODE: {
369
+ const pm_until_node_t* until_node = (const pm_until_node_t*) node;
370
+
371
+ if (has_location(until_node->keyword_loc) && !has_location(until_node->closing_loc)) {
372
+ analyzed->unclosed_control_flow_count++;
373
+ }
374
+
375
+ break;
376
+ }
377
+
378
+ case PM_FOR_NODE: {
379
+ const pm_for_node_t* for_node = (const pm_for_node_t*) node;
380
+
381
+ if (has_location(for_node->for_keyword_loc) && !has_location(for_node->end_keyword_loc)) {
382
+ analyzed->unclosed_control_flow_count++;
383
+ }
384
+
385
+ break;
386
+ }
387
+
388
+ case PM_BEGIN_NODE: {
389
+ const pm_begin_node_t* begin_node = (const pm_begin_node_t*) node;
390
+
391
+ if (has_location(begin_node->begin_keyword_loc) && !has_location(begin_node->end_keyword_loc)) {
392
+ analyzed->unclosed_control_flow_count++;
393
+ }
394
+
395
+ break;
396
+ }
397
+
398
+ case PM_BLOCK_NODE: {
399
+ const pm_block_node_t* block_node = (const pm_block_node_t*) node;
400
+ bool has_opening = is_do_block(block_node->opening_loc) || is_brace_block(block_node->opening_loc);
401
+
402
+ if (has_opening && !has_location(block_node->closing_loc)) { analyzed->unclosed_control_flow_count++; }
403
+ break;
404
+ }
405
+
406
+ default: break;
314
407
  }
315
408
 
409
+ pm_visit_child_nodes(node, search_unclosed_control_flows, analyzed);
410
+
316
411
  return false;
317
412
  }
data/src/analyzed_ruby.c CHANGED
@@ -12,24 +12,25 @@ analyzed_ruby_T* init_analyzed_ruby(hb_string_T source) {
12
12
  analyzed->root = pm_parse(&analyzed->parser);
13
13
  analyzed->valid = (analyzed->parser.error_list.size == 0);
14
14
  analyzed->parsed = true;
15
- analyzed->has_if_node = false;
16
- analyzed->has_elsif_node = false;
17
- analyzed->has_else_node = false;
18
- analyzed->has_end = false;
19
- analyzed->has_block_node = false;
20
- analyzed->has_block_closing = false;
21
- analyzed->has_case_node = false;
22
- analyzed->has_case_match_node = false;
23
- analyzed->has_when_node = false;
24
- analyzed->has_in_node = false;
25
- analyzed->has_for_node = false;
26
- analyzed->has_while_node = false;
27
- analyzed->has_until_node = false;
28
- analyzed->has_begin_node = false;
29
- analyzed->has_rescue_node = false;
30
- analyzed->has_ensure_node = false;
31
- analyzed->has_unless_node = false;
32
- analyzed->has_yield_node = false;
15
+ analyzed->if_node_count = 0;
16
+ analyzed->elsif_node_count = 0;
17
+ analyzed->else_node_count = 0;
18
+ analyzed->end_count = 0;
19
+ analyzed->block_node_count = 0;
20
+ analyzed->block_closing_count = 0;
21
+ analyzed->case_node_count = 0;
22
+ analyzed->case_match_node_count = 0;
23
+ analyzed->when_node_count = 0;
24
+ analyzed->in_node_count = 0;
25
+ analyzed->for_node_count = 0;
26
+ analyzed->while_node_count = 0;
27
+ analyzed->until_node_count = 0;
28
+ analyzed->begin_node_count = 0;
29
+ analyzed->rescue_node_count = 0;
30
+ analyzed->ensure_node_count = 0;
31
+ analyzed->unless_node_count = 0;
32
+ analyzed->yield_node_count = 0;
33
+ analyzed->unclosed_control_flow_count = 0;
33
34
 
34
35
  return analyzed;
35
36
  }
@@ -45,19 +46,19 @@ void free_analyzed_ruby(analyzed_ruby_T* analyzed) {
45
46
  }
46
47
 
47
48
  const char* erb_keyword_from_analyzed_ruby(const analyzed_ruby_T* analyzed) {
48
- if (analyzed->has_end) {
49
+ if (analyzed->end_count > 0) {
49
50
  return "`<% end %>`";
50
- } else if (analyzed->has_else_node) {
51
+ } else if (analyzed->else_node_count > 0) {
51
52
  return "`<% else %>`";
52
- } else if (analyzed->has_elsif_node) {
53
+ } else if (analyzed->elsif_node_count > 0) {
53
54
  return "`<% elsif %>`";
54
- } else if (analyzed->has_when_node) {
55
+ } else if (analyzed->when_node_count > 0) {
55
56
  return "`<% when %>`";
56
- } else if (analyzed->has_in_node) {
57
+ } else if (analyzed->in_node_count > 0) {
57
58
  return "`<% in %>`";
58
- } else if (analyzed->has_rescue_node) {
59
+ } else if (analyzed->rescue_node_count > 0) {
59
60
  return "`<% rescue %>`";
60
- } else if (analyzed->has_ensure_node) {
61
+ } else if (analyzed->ensure_node_count > 0) {
61
62
  return "`<% ensure %>`";
62
63
  }
63
64
 
@@ -1,6 +1,7 @@
1
1
  // NOTE: This file is generated by the templates/template.rb script and should not
2
2
  // be modified manually. See /home/runner/work/herb/herb/templates/src/ast_pretty_print.c.erb
3
3
 
4
+ #include "include/analyze_helpers.h"
4
5
  #include "include/ast_node.h"
5
6
  #include "include/ast_nodes.h"
6
7
  #include "include/errors.h"
@@ -214,21 +215,21 @@ void ast_pretty_print_node(AST_NODE_T* node, const size_t indent, const size_t r
214
215
  pretty_print_token_property(erb_content_node->content, hb_string("content"), indent, relative_indent, false, buffer);
215
216
  pretty_print_token_property(erb_content_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer);
216
217
  if (erb_content_node->analyzed_ruby) {
217
- pretty_print_boolean_property(hb_string("if_node"), erb_content_node->analyzed_ruby->has_if_node, indent, relative_indent, false, buffer);
218
- pretty_print_boolean_property(hb_string("elsif_node"), erb_content_node->analyzed_ruby->has_elsif_node, indent, relative_indent, false, buffer);
219
- pretty_print_boolean_property(hb_string("else_node"), erb_content_node->analyzed_ruby->has_else_node, indent, relative_indent, false, buffer);
220
- pretty_print_boolean_property(hb_string("end"), erb_content_node->analyzed_ruby->has_end, indent, relative_indent, false, buffer);
221
- pretty_print_boolean_property(hb_string("block_node"), erb_content_node->analyzed_ruby->has_block_node, indent, relative_indent, false, buffer);
222
- pretty_print_boolean_property(hb_string("block_closing"), erb_content_node->analyzed_ruby->has_block_closing, indent, relative_indent, false, buffer);
223
- pretty_print_boolean_property(hb_string("case_node"), erb_content_node->analyzed_ruby->has_case_node, indent, relative_indent, false, buffer);
224
- pretty_print_boolean_property(hb_string("when_node"), erb_content_node->analyzed_ruby->has_when_node, indent, relative_indent, false, buffer);
225
- pretty_print_boolean_property(hb_string("for_node"), erb_content_node->analyzed_ruby->has_for_node, indent, relative_indent, false, buffer);
226
- pretty_print_boolean_property(hb_string("while_node"), erb_content_node->analyzed_ruby->has_while_node, indent, relative_indent, false, buffer);
227
- pretty_print_boolean_property(hb_string("until_node"), erb_content_node->analyzed_ruby->has_until_node, indent, relative_indent, false, buffer);
228
- pretty_print_boolean_property(hb_string("begin_node"), erb_content_node->analyzed_ruby->has_begin_node, indent, relative_indent, false, buffer);
229
- pretty_print_boolean_property(hb_string("rescue_node"), erb_content_node->analyzed_ruby->has_rescue_node, indent, relative_indent, false, buffer);
230
- pretty_print_boolean_property(hb_string("ensure_node"), erb_content_node->analyzed_ruby->has_ensure_node, indent, relative_indent, false, buffer);
231
- pretty_print_boolean_property(hb_string("unless_node"), erb_content_node->analyzed_ruby->has_unless_node, indent, relative_indent, false, buffer);
218
+ pretty_print_boolean_property(hb_string("if_node"), has_if_node(erb_content_node->analyzed_ruby), indent, relative_indent, false, buffer);
219
+ pretty_print_boolean_property(hb_string("elsif_node"), has_elsif_node(erb_content_node->analyzed_ruby), indent, relative_indent, false, buffer);
220
+ pretty_print_boolean_property(hb_string("else_node"), has_else_node(erb_content_node->analyzed_ruby), indent, relative_indent, false, buffer);
221
+ pretty_print_boolean_property(hb_string("end"), has_end(erb_content_node->analyzed_ruby), indent, relative_indent, false, buffer);
222
+ pretty_print_boolean_property(hb_string("block_node"), has_block_node(erb_content_node->analyzed_ruby), indent, relative_indent, false, buffer);
223
+ pretty_print_boolean_property(hb_string("block_closing"), has_block_closing(erb_content_node->analyzed_ruby), indent, relative_indent, false, buffer);
224
+ pretty_print_boolean_property(hb_string("case_node"), has_case_node(erb_content_node->analyzed_ruby), indent, relative_indent, false, buffer);
225
+ pretty_print_boolean_property(hb_string("when_node"), has_when_node(erb_content_node->analyzed_ruby), indent, relative_indent, false, buffer);
226
+ pretty_print_boolean_property(hb_string("for_node"), has_for_node(erb_content_node->analyzed_ruby), indent, relative_indent, false, buffer);
227
+ pretty_print_boolean_property(hb_string("while_node"), has_while_node(erb_content_node->analyzed_ruby), indent, relative_indent, false, buffer);
228
+ pretty_print_boolean_property(hb_string("until_node"), has_until_node(erb_content_node->analyzed_ruby), indent, relative_indent, false, buffer);
229
+ pretty_print_boolean_property(hb_string("begin_node"), has_begin_node(erb_content_node->analyzed_ruby), indent, relative_indent, false, buffer);
230
+ pretty_print_boolean_property(hb_string("rescue_node"), has_rescue_node(erb_content_node->analyzed_ruby), indent, relative_indent, false, buffer);
231
+ pretty_print_boolean_property(hb_string("ensure_node"), has_ensure_node(erb_content_node->analyzed_ruby), indent, relative_indent, false, buffer);
232
+ pretty_print_boolean_property(hb_string("unless_node"), has_unless_node(erb_content_node->analyzed_ruby), indent, relative_indent, false, buffer);
232
233
  } else {
233
234
  pretty_print_label(hb_string("analyzed_ruby"), indent, relative_indent, false, buffer);
234
235
  hb_buffer_append(buffer, " ∅\n");
data/src/errors.c CHANGED
@@ -497,6 +497,20 @@ void append_missingerb_end_tag_error(const char* keyword, position_T start, posi
497
497
  hb_array_append(errors, missingerb_end_tag_error_init(keyword, start, end));
498
498
  }
499
499
 
500
+ ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR_T* erb_multiple_blocks_in_tag_error_init(position_T start, position_T end) {
501
+ ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR_T* erb_multiple_blocks_in_tag_error = malloc(sizeof(ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR_T));
502
+
503
+ error_init(&erb_multiple_blocks_in_tag_error->base, ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR, start, end);
504
+
505
+ erb_multiple_blocks_in_tag_error->base.message = herb_strdup("Multiple unclosed control flow blocks in a single ERB tag. Split each block into its own ERB tag, or close all blocks within the same tag.");
506
+
507
+ return erb_multiple_blocks_in_tag_error;
508
+ }
509
+
510
+ void append_erb_multiple_blocks_in_tag_error(position_T start, position_T end, hb_array_T* errors) {
511
+ hb_array_append(errors, erb_multiple_blocks_in_tag_error_init(start, end));
512
+ }
513
+
500
514
  const char* error_type_to_string(ERROR_T* error) {
501
515
  switch (error->type) {
502
516
  case UNEXPECTED_ERROR: return "UNEXPECTED_ERROR";
@@ -510,6 +524,7 @@ const char* error_type_to_string(ERROR_T* error) {
510
524
  case RUBY_PARSE_ERROR: return "RUBY_PARSE_ERROR";
511
525
  case ERB_CONTROL_FLOW_SCOPE_ERROR: return "ERB_CONTROL_FLOW_SCOPE_ERROR";
512
526
  case MISSINGERB_END_TAG_ERROR: return "MISSINGERB_END_TAG_ERROR";
527
+ case ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR: return "ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR";
513
528
  }
514
529
 
515
530
  return "Unknown error_type_T";
@@ -528,6 +543,7 @@ const char* error_human_type(ERROR_T* error) {
528
543
  case RUBY_PARSE_ERROR: return "RubyParseError";
529
544
  case ERB_CONTROL_FLOW_SCOPE_ERROR: return "ERBControlFlowScopeError";
530
545
  case MISSINGERB_END_TAG_ERROR: return "MissingERBEndTagError";
546
+ case ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR: return "ERBMultipleBlocksInTagError";
531
547
  }
532
548
 
533
549
  return "Unknown error_type_T";
@@ -616,6 +632,12 @@ static void error_free_missingerb_end_tag_error(MISSINGERB_END_TAG_ERROR_T* miss
616
632
  error_free_base_error(&missingerb_end_tag_error->base);
617
633
  }
618
634
 
635
+ static void error_free_erb_multiple_blocks_in_tag_error(ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR_T* erb_multiple_blocks_in_tag_error) {
636
+ /* no ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR_T specific fields to free up */
637
+
638
+ error_free_base_error(&erb_multiple_blocks_in_tag_error->base);
639
+ }
640
+
619
641
  void error_free(ERROR_T* error) {
620
642
  if (!error) { return; }
621
643
 
@@ -631,6 +653,7 @@ void error_free(ERROR_T* error) {
631
653
  case RUBY_PARSE_ERROR: error_free_ruby_parse_error((RUBY_PARSE_ERROR_T*) error); break;
632
654
  case ERB_CONTROL_FLOW_SCOPE_ERROR: error_free_erb_control_flow_scope_error((ERB_CONTROL_FLOW_SCOPE_ERROR_T*) error); break;
633
655
  case MISSINGERB_END_TAG_ERROR: error_free_missingerb_end_tag_error((MISSINGERB_END_TAG_ERROR_T*) error); break;
656
+ case ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR: error_free_erb_multiple_blocks_in_tag_error((ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR_T*) error); break;
634
657
  }
635
658
  }
636
659
 
@@ -841,6 +864,19 @@ static void error_pretty_print_missingerb_end_tag_error(MISSINGERB_END_TAG_ERROR
841
864
  pretty_print_quoted_property(hb_string("keyword"), hb_string(error->keyword), indent, relative_indent, true, buffer);
842
865
  }
843
866
 
867
+ static void error_pretty_print_erb_multiple_blocks_in_tag_error(ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
868
+ if (!error) { return; }
869
+
870
+ hb_buffer_append(buffer, "@ ");
871
+ hb_buffer_append(buffer, error_human_type((ERROR_T*) error));
872
+ hb_buffer_append(buffer, " ");
873
+
874
+ pretty_print_location(error->base.location, buffer);
875
+ hb_buffer_append(buffer, "\n");
876
+
877
+ pretty_print_quoted_property(hb_string("message"), hb_string(error->base.message), indent, relative_indent, true, buffer);
878
+ }
879
+
844
880
  void error_pretty_print(ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
845
881
  if (!error) { return; }
846
882
 
@@ -856,5 +892,6 @@ void error_pretty_print(ERROR_T* error, const size_t indent, const size_t relati
856
892
  case RUBY_PARSE_ERROR: error_pretty_print_ruby_parse_error((RUBY_PARSE_ERROR_T*) error, indent, relative_indent, buffer); break;
857
893
  case ERB_CONTROL_FLOW_SCOPE_ERROR: error_pretty_print_erb_control_flow_scope_error((ERB_CONTROL_FLOW_SCOPE_ERROR_T*) error, indent, relative_indent, buffer); break;
858
894
  case MISSINGERB_END_TAG_ERROR: error_pretty_print_missingerb_end_tag_error((MISSINGERB_END_TAG_ERROR_T*) error, indent, relative_indent, buffer); break;
895
+ case ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR: error_pretty_print_erb_multiple_blocks_in_tag_error((ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR_T*) error, indent, relative_indent, buffer); break;
859
896
  }
860
897
  }
data/src/extract.c CHANGED
@@ -25,7 +25,8 @@ void herb_extract_ruby_to_buffer(const char* source, hb_buffer_T* output) {
25
25
  if (strcmp(token->value, "<%#") == 0) {
26
26
  skip_erb_content = true;
27
27
  is_comment_tag = true;
28
- } else if (strcmp(token->value, "<%%") == 0 || strcmp(token->value, "<%%=") == 0) {
28
+ } else if (strcmp(token->value, "<%%") == 0 || strcmp(token->value, "<%%=") == 0
29
+ || strcmp(token->value, "<%graphql") == 0) {
29
30
  skip_erb_content = true;
30
31
  is_comment_tag = false;
31
32
  } else {
@@ -46,6 +46,7 @@ bool search_in_nodes(analyzed_ruby_T* analyzed);
46
46
  bool search_rescue_nodes(analyzed_ruby_T* analyzed);
47
47
  bool search_ensure_nodes(analyzed_ruby_T* analyzed);
48
48
  bool search_yield_nodes(const pm_node_t* node, void* data);
49
+ bool search_unclosed_control_flows(const pm_node_t* node, void* data);
49
50
 
50
51
  void check_erb_node_for_missing_end(const AST_NODE_T* node);
51
52