herb 0.8.6-x86_64-linux-gnu → 0.8.8-x86_64-linux-gnu

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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +7 -0
  3. data/config.yml +12 -0
  4. data/ext/herb/error_helpers.c +1 -1
  5. data/ext/herb/extconf.rb +0 -4
  6. data/ext/herb/extension_helpers.c +1 -1
  7. data/ext/herb/nodes.c +18 -10
  8. data/lib/herb/3.0/herb.so +0 -0
  9. data/lib/herb/3.1/herb.so +0 -0
  10. data/lib/herb/3.2/herb.so +0 -0
  11. data/lib/herb/3.3/herb.so +0 -0
  12. data/lib/herb/3.4/herb.so +0 -0
  13. data/lib/herb/4.0/herb.so +0 -0
  14. data/lib/herb/ast/nodes.rb +32 -8
  15. data/lib/herb/engine/debug_visitor.rb +1 -1
  16. data/lib/herb/version.rb +1 -1
  17. data/lib/herb.rb +30 -3
  18. data/sig/herb/ast/nodes.rbs +16 -8
  19. data/sig/serialized_ast_nodes.rbs +4 -0
  20. data/src/analyze.c +137 -42
  21. data/src/analyze_helpers.c +80 -12
  22. data/src/analyzed_ruby.c +1 -0
  23. data/src/ast_node.c +1 -1
  24. data/src/ast_nodes.c +65 -161
  25. data/src/ast_pretty_print.c +52 -0
  26. data/src/errors.c +5 -5
  27. data/src/extract.c +2 -2
  28. data/src/herb.c +2 -2
  29. data/src/include/analyze_helpers.h +7 -0
  30. data/src/include/analyzed_ruby.h +1 -0
  31. data/src/include/ast_nodes.h +8 -4
  32. data/src/include/location.h +4 -0
  33. data/src/include/prism_helpers.h +6 -0
  34. data/src/include/util/hb_narray.h +1 -0
  35. data/src/include/version.h +1 -1
  36. data/src/location.c +16 -0
  37. data/src/parser.c +9 -9
  38. data/src/parser_helpers.c +4 -4
  39. data/src/pretty_print.c +6 -6
  40. data/src/prism_helpers.c +188 -0
  41. data/src/util/hb_array.c +1 -0
  42. data/src/util/hb_narray.c +6 -0
  43. data/src/visitor.c +26 -26
  44. data/templates/ext/herb/error_helpers.c.erb +1 -1
  45. data/templates/ext/herb/nodes.c.erb +3 -1
  46. data/templates/java/error_helpers.c.erb +1 -1
  47. data/templates/java/nodes.c.erb +5 -3
  48. data/templates/java/org/herb/ast/Nodes.java.erb +11 -0
  49. data/templates/javascript/packages/core/src/nodes.ts.erb +14 -0
  50. data/templates/javascript/packages/node/extension/error_helpers.cpp.erb +1 -1
  51. data/templates/javascript/packages/node/extension/nodes.cpp.erb +10 -1
  52. data/templates/lib/herb/ast/nodes.rb.erb +4 -0
  53. data/templates/rust/src/ast/nodes.rs.erb +12 -2
  54. data/templates/rust/src/nodes.rs.erb +4 -0
  55. data/templates/src/ast_nodes.c.erb +7 -7
  56. data/templates/src/ast_pretty_print.c.erb +14 -0
  57. data/templates/src/errors.c.erb +5 -5
  58. data/templates/src/visitor.c.erb +1 -1
  59. data/templates/template.rb +11 -0
  60. data/templates/wasm/error_helpers.cpp.erb +1 -1
  61. data/templates/wasm/nodes.cpp.erb +7 -1
  62. data/vendor/prism/include/prism/version.h +2 -2
  63. data/vendor/prism/src/prism.c +48 -27
  64. data/vendor/prism/templates/java/org/prism/Loader.java.erb +1 -1
  65. data/vendor/prism/templates/javascript/src/deserialize.js.erb +1 -1
  66. data/vendor/prism/templates/lib/prism/compiler.rb.erb +2 -2
  67. data/vendor/prism/templates/lib/prism/node.rb.erb +24 -1
  68. data/vendor/prism/templates/lib/prism/serialize.rb.erb +1 -1
  69. data/vendor/prism/templates/lib/prism/visitor.rb.erb +2 -2
  70. data/vendor/prism/templates/sig/prism/node.rbs.erb +1 -0
  71. metadata +2 -2
data/src/analyze.c CHANGED
@@ -43,6 +43,7 @@ static analyzed_ruby_T* herb_analyze_ruby(hb_string_T source) {
43
43
  search_rescue_nodes(analyzed);
44
44
  search_ensure_nodes(analyzed);
45
45
  search_yield_nodes(analyzed->root, analyzed);
46
+ search_then_keywords(analyzed->root, analyzed);
46
47
  search_block_closing_nodes(analyzed);
47
48
 
48
49
  if (!analyzed->valid) { pm_visit_node(analyzed->root, search_unclosed_control_flows, analyzed); }
@@ -112,6 +113,14 @@ typedef struct {
112
113
  const uint8_t* source_start;
113
114
  } location_walker_context_t;
114
115
 
116
+ static bool control_type_is_block(control_type_t type) {
117
+ return type == CONTROL_TYPE_BLOCK;
118
+ }
119
+
120
+ static bool control_type_is_yield(control_type_t type) {
121
+ return type == CONTROL_TYPE_YIELD;
122
+ }
123
+
115
124
  static bool find_earliest_control_keyword_walker(const pm_node_t* node, void* data) {
116
125
  if (!node) { return true; }
117
126
 
@@ -194,14 +203,12 @@ static bool find_earliest_control_keyword_walker(const pm_node_t* node, void* da
194
203
 
195
204
  if (call->block != NULL && call->block->type == PM_BLOCK_NODE) {
196
205
  pm_block_node_t* block_node = (pm_block_node_t*) call->block;
197
- size_t opening_length = block_node->opening_loc.end - block_node->opening_loc.start;
198
- bool has_do_opening =
199
- opening_length == 2 && block_node->opening_loc.start[0] == 'd' && block_node->opening_loc.start[1] == 'o';
200
- bool has_brace_opening = opening_length == 1 && block_node->opening_loc.start[0] == '{';
201
- bool has_closing_location = block_node->closing_loc.start != NULL && block_node->closing_loc.end != NULL
202
- && (block_node->closing_loc.end - block_node->closing_loc.start) > 0;
203
-
204
- if (has_do_opening || (has_brace_opening && !has_closing_location)) {
206
+
207
+ bool has_do_opening = is_do_block(block_node->opening_loc);
208
+ bool has_brace_opening = is_brace_block(block_node->opening_loc);
209
+ bool has_valid_brace_closing = is_closing_brace(block_node->closing_loc);
210
+
211
+ if (has_do_opening || (has_brace_opening && !has_valid_brace_closing)) {
205
212
  current_type = CONTROL_TYPE_BLOCK;
206
213
  keyword_offset = (uint32_t) (node->location.start - context->source_start);
207
214
  }
@@ -221,7 +228,17 @@ static bool find_earliest_control_keyword_walker(const pm_node_t* node, void* da
221
228
  }
222
229
 
223
230
  if (keyword_offset != UINT32_MAX) {
224
- if (!result->found || keyword_offset < result->offset) {
231
+ bool should_update = !result->found;
232
+
233
+ if (result->found) {
234
+ if (control_type_is_block(current_type) && control_type_is_yield(result->type)) {
235
+ should_update = true;
236
+ } else if (!(control_type_is_yield(current_type) && control_type_is_block(result->type))) {
237
+ should_update = keyword_offset < result->offset;
238
+ }
239
+ }
240
+
241
+ if (should_update) {
225
242
  result->type = current_type;
226
243
  result->offset = keyword_offset;
227
244
  result->found = true;
@@ -308,7 +325,7 @@ static AST_NODE_T* create_control_node(
308
325
 
309
326
  if (end_node) {
310
327
  end_position = end_node->base.location.end;
311
- } else if (children && children->size > 0) {
328
+ } else if (hb_array_size(children) > 0) {
312
329
  AST_NODE_T* last_child = hb_array_last(children);
313
330
  end_position = last_child->location.end;
314
331
  } else if (subsequent) {
@@ -319,6 +336,34 @@ static AST_NODE_T* create_control_node(
319
336
  token_T* content = erb_node->content;
320
337
  token_T* tag_closing = erb_node->tag_closing;
321
338
 
339
+ location_T* then_keyword = NULL;
340
+
341
+ if (control_type == CONTROL_TYPE_IF || control_type == CONTROL_TYPE_ELSIF || control_type == CONTROL_TYPE_UNLESS
342
+ || control_type == CONTROL_TYPE_WHEN || control_type == CONTROL_TYPE_IN) {
343
+ const char* source = content ? content->value : NULL;
344
+
345
+ if (control_type == CONTROL_TYPE_WHEN || control_type == CONTROL_TYPE_IN) {
346
+ if (source != NULL && strstr(source, "then") != NULL) {
347
+ then_keyword = get_then_keyword_location_wrapped(source, control_type == CONTROL_TYPE_IN);
348
+ }
349
+ } else if (control_type == CONTROL_TYPE_ELSIF) {
350
+ if (source != NULL && strstr(source, "then") != NULL) {
351
+ then_keyword = get_then_keyword_location_elsif_wrapped(source);
352
+ }
353
+ } else {
354
+ then_keyword = get_then_keyword_location(erb_node->analyzed_ruby, source);
355
+ }
356
+
357
+ if (then_keyword != NULL && content != NULL) {
358
+ position_T content_start = content->location.start;
359
+
360
+ then_keyword->start.line = content_start.line + then_keyword->start.line - 1;
361
+ then_keyword->start.column = content_start.column + then_keyword->start.column;
362
+ then_keyword->end.line = content_start.line + then_keyword->end.line - 1;
363
+ then_keyword->end.column = content_start.column + then_keyword->end.column;
364
+ }
365
+ }
366
+
322
367
  switch (control_type) {
323
368
  case CONTROL_TYPE_IF:
324
369
  case CONTROL_TYPE_ELSIF: {
@@ -326,6 +371,7 @@ static AST_NODE_T* create_control_node(
326
371
  tag_opening,
327
372
  content,
328
373
  tag_closing,
374
+ then_keyword,
329
375
  children,
330
376
  subsequent,
331
377
  end_node,
@@ -350,7 +396,7 @@ static AST_NODE_T* create_control_node(
350
396
  hb_array_T* in_conditions = hb_array_init(8);
351
397
  hb_array_T* non_when_non_in_children = hb_array_init(8);
352
398
 
353
- for (size_t i = 0; i < children->size; i++) {
399
+ for (size_t i = 0; i < hb_array_size(children); i++) {
354
400
  AST_NODE_T* child = hb_array_get(children, i);
355
401
 
356
402
  if (child && child->type == AST_ERB_WHEN_NODE) {
@@ -364,7 +410,7 @@ static AST_NODE_T* create_control_node(
364
410
 
365
411
  hb_array_free(&children);
366
412
 
367
- if (in_conditions->size > 0) {
413
+ if (hb_array_size(in_conditions) > 0) {
368
414
  hb_array_free(&when_conditions);
369
415
 
370
416
  return (AST_NODE_T*) ast_erb_case_match_node_init(
@@ -398,15 +444,29 @@ static AST_NODE_T* create_control_node(
398
444
  }
399
445
 
400
446
  case CONTROL_TYPE_WHEN: {
401
- return (
402
- AST_NODE_T*
403
- ) ast_erb_when_node_init(tag_opening, content, tag_closing, children, start_position, end_position, errors);
447
+ return (AST_NODE_T*) ast_erb_when_node_init(
448
+ tag_opening,
449
+ content,
450
+ tag_closing,
451
+ then_keyword,
452
+ children,
453
+ start_position,
454
+ end_position,
455
+ errors
456
+ );
404
457
  }
405
458
 
406
459
  case CONTROL_TYPE_IN: {
407
- return (
408
- AST_NODE_T*
409
- ) ast_erb_in_node_init(tag_opening, content, tag_closing, children, start_position, end_position, errors);
460
+ return (AST_NODE_T*) ast_erb_in_node_init(
461
+ tag_opening,
462
+ content,
463
+ tag_closing,
464
+ then_keyword,
465
+ children,
466
+ start_position,
467
+ end_position,
468
+ errors
469
+ );
410
470
  }
411
471
 
412
472
  case CONTROL_TYPE_BEGIN: {
@@ -471,6 +531,7 @@ static AST_NODE_T* create_control_node(
471
531
  tag_opening,
472
532
  content,
473
533
  tag_closing,
534
+ then_keyword,
474
535
  children,
475
536
  else_clause,
476
537
  end_node,
@@ -560,7 +621,7 @@ static size_t process_control_structure(
560
621
  hb_array_T* in_conditions = hb_array_init(8);
561
622
  hb_array_T* non_when_non_in_children = hb_array_init(8);
562
623
 
563
- while (index < array->size) {
624
+ while (index < hb_array_size(array)) {
564
625
  AST_NODE_T* next_node = hb_array_get(array, index);
565
626
 
566
627
  if (!next_node) { break; }
@@ -576,7 +637,7 @@ static size_t process_control_structure(
576
637
  index++;
577
638
  }
578
639
 
579
- while (index < array->size) {
640
+ while (index < hb_array_size(array)) {
580
641
  AST_NODE_T* next_node = hb_array_get(array, index);
581
642
 
582
643
  if (!next_node) { break; }
@@ -599,10 +660,27 @@ static size_t process_control_structure(
599
660
  hb_array_T* when_errors = erb_content->base.errors;
600
661
  erb_content->base.errors = NULL;
601
662
 
663
+ location_T* then_keyword = NULL;
664
+ const char* source = erb_content->content ? erb_content->content->value : NULL;
665
+
666
+ if (source != NULL && strstr(source, "then") != NULL) {
667
+ then_keyword = get_then_keyword_location_wrapped(source, false);
668
+
669
+ if (then_keyword != NULL && erb_content->content != NULL) {
670
+ position_T content_start = erb_content->content->location.start;
671
+
672
+ then_keyword->start.line = content_start.line + then_keyword->start.line - 1;
673
+ then_keyword->start.column = content_start.column + then_keyword->start.column;
674
+ then_keyword->end.line = content_start.line + then_keyword->end.line - 1;
675
+ then_keyword->end.column = content_start.column + then_keyword->end.column;
676
+ }
677
+ }
678
+
602
679
  AST_ERB_WHEN_NODE_T* when_node = ast_erb_when_node_init(
603
680
  erb_content->tag_opening,
604
681
  erb_content->content,
605
682
  erb_content->tag_closing,
683
+ then_keyword,
606
684
  when_statements,
607
685
  erb_content->tag_opening->location.start,
608
686
  erb_content->tag_closing->location.end,
@@ -623,10 +701,27 @@ static size_t process_control_structure(
623
701
  hb_array_T* in_errors = erb_content->base.errors;
624
702
  erb_content->base.errors = NULL;
625
703
 
704
+ location_T* in_then_keyword = NULL;
705
+ const char* in_source = erb_content->content ? erb_content->content->value : NULL;
706
+
707
+ if (in_source != NULL && strstr(in_source, "then") != NULL) {
708
+ in_then_keyword = get_then_keyword_location_wrapped(in_source, true);
709
+
710
+ if (in_then_keyword != NULL && erb_content->content != NULL) {
711
+ position_T content_start = erb_content->content->location.start;
712
+
713
+ in_then_keyword->start.line = content_start.line + in_then_keyword->start.line - 1;
714
+ in_then_keyword->start.column = content_start.column + in_then_keyword->start.column;
715
+ in_then_keyword->end.line = content_start.line + in_then_keyword->end.line - 1;
716
+ in_then_keyword->end.column = content_start.column + in_then_keyword->end.column;
717
+ }
718
+ }
719
+
626
720
  AST_ERB_IN_NODE_T* in_node = ast_erb_in_node_init(
627
721
  erb_content->tag_opening,
628
722
  erb_content->content,
629
723
  erb_content->tag_closing,
724
+ in_then_keyword,
630
725
  in_statements,
631
726
  erb_content->tag_opening->location.start,
632
727
  erb_content->tag_closing->location.end,
@@ -648,7 +743,7 @@ static size_t process_control_structure(
648
743
 
649
744
  AST_ERB_ELSE_NODE_T* else_clause = NULL;
650
745
 
651
- if (index < array->size) {
746
+ if (index < hb_array_size(array)) {
652
747
  AST_NODE_T* next_node = hb_array_get(array, index);
653
748
 
654
749
  if (next_node && next_node->type == AST_ERB_CONTENT_NODE) {
@@ -682,7 +777,7 @@ static size_t process_control_structure(
682
777
 
683
778
  AST_ERB_END_NODE_T* end_node = NULL;
684
779
 
685
- if (index < array->size) {
780
+ if (index < hb_array_size(array)) {
686
781
  AST_NODE_T* potential_end = hb_array_get(array, index);
687
782
 
688
783
  if (potential_end && potential_end->type == AST_ERB_CONTENT_NODE) {
@@ -715,15 +810,15 @@ static size_t process_control_structure(
715
810
  end_position = end_node->base.location.end;
716
811
  } else if (else_clause) {
717
812
  end_position = else_clause->base.location.end;
718
- } else if (when_conditions->size > 0) {
813
+ } else if (hb_array_size(when_conditions) > 0) {
719
814
  AST_NODE_T* last_when = hb_array_last(when_conditions);
720
815
  end_position = last_when->location.end;
721
- } else if (in_conditions->size > 0) {
816
+ } else if (hb_array_size(in_conditions) > 0) {
722
817
  AST_NODE_T* last_in = hb_array_last(in_conditions);
723
818
  end_position = last_in->location.end;
724
819
  }
725
820
 
726
- if (in_conditions->size > 0) {
821
+ if (hb_array_size(in_conditions) > 0) {
727
822
  hb_array_T* case_match_errors = erb_node->base.errors;
728
823
  erb_node->base.errors = NULL;
729
824
 
@@ -781,7 +876,7 @@ static size_t process_control_structure(
781
876
  AST_ERB_ELSE_NODE_T* else_clause = NULL;
782
877
  AST_ERB_ENSURE_NODE_T* ensure_clause = NULL;
783
878
 
784
- if (index < array->size) {
879
+ if (index < hb_array_size(array)) {
785
880
  AST_NODE_T* next_node = hb_array_get(array, index);
786
881
 
787
882
  if (next_node && next_node->type == AST_ERB_CONTENT_NODE) {
@@ -796,7 +891,7 @@ static size_t process_control_structure(
796
891
  }
797
892
  }
798
893
 
799
- if (index < array->size) {
894
+ if (index < hb_array_size(array)) {
800
895
  AST_NODE_T* next_node = hb_array_get(array, index);
801
896
 
802
897
  if (next_node && next_node->type == AST_ERB_CONTENT_NODE) {
@@ -828,7 +923,7 @@ static size_t process_control_structure(
828
923
  }
829
924
  }
830
925
 
831
- if (index < array->size) {
926
+ if (index < hb_array_size(array)) {
832
927
  AST_NODE_T* next_node = hb_array_get(array, index);
833
928
 
834
929
  if (next_node && next_node->type == AST_ERB_CONTENT_NODE) {
@@ -840,7 +935,7 @@ static size_t process_control_structure(
840
935
 
841
936
  index++;
842
937
 
843
- while (index < array->size) {
938
+ while (index < hb_array_size(array)) {
844
939
  AST_NODE_T* child = hb_array_get(array, index);
845
940
 
846
941
  if (!child) { break; }
@@ -876,7 +971,7 @@ static size_t process_control_structure(
876
971
 
877
972
  AST_ERB_END_NODE_T* end_node = NULL;
878
973
 
879
- if (index < array->size) {
974
+ if (index < hb_array_size(array)) {
880
975
  AST_NODE_T* potential_end = hb_array_get(array, index);
881
976
 
882
977
  if (potential_end && potential_end->type == AST_ERB_CONTENT_NODE) {
@@ -943,7 +1038,7 @@ static size_t process_control_structure(
943
1038
 
944
1039
  AST_ERB_END_NODE_T* end_node = NULL;
945
1040
 
946
- if (index < array->size) {
1041
+ if (index < hb_array_size(array)) {
947
1042
  AST_NODE_T* potential_close = hb_array_get(array, index);
948
1043
 
949
1044
  if (potential_close && potential_close->type == AST_ERB_CONTENT_NODE) {
@@ -975,7 +1070,7 @@ static size_t process_control_structure(
975
1070
 
976
1071
  if (end_node) {
977
1072
  end_position = end_node->base.location.end;
978
- } else if (children && children->size > 0) {
1073
+ } else if (hb_array_size(children) > 0) {
979
1074
  AST_NODE_T* last_child = hb_array_last(children);
980
1075
  end_position = last_child->location.end;
981
1076
  }
@@ -1005,7 +1100,7 @@ static size_t process_control_structure(
1005
1100
  AST_NODE_T* subsequent = NULL;
1006
1101
  AST_ERB_END_NODE_T* end_node = NULL;
1007
1102
 
1008
- if (index < array->size) {
1103
+ if (index < hb_array_size(array)) {
1009
1104
  AST_NODE_T* next_node = hb_array_get(array, index);
1010
1105
 
1011
1106
  if (next_node && next_node->type == AST_ERB_CONTENT_NODE) {
@@ -1018,7 +1113,7 @@ static size_t process_control_structure(
1018
1113
  }
1019
1114
  }
1020
1115
 
1021
- if (index < array->size) {
1116
+ if (index < hb_array_size(array)) {
1022
1117
  AST_NODE_T* potential_end = hb_array_get(array, index);
1023
1118
 
1024
1119
  if (potential_end && potential_end->type == AST_ERB_CONTENT_NODE) {
@@ -1080,7 +1175,7 @@ static size_t process_subsequent_block(
1080
1175
  hb_array_free(&children);
1081
1176
  }
1082
1177
 
1083
- if (index < array->size) {
1178
+ if (index < hb_array_size(array)) {
1084
1179
  AST_NODE_T* next_node = hb_array_get(array, index);
1085
1180
 
1086
1181
  if (next_node && next_node->type == AST_ERB_CONTENT_NODE) {
@@ -1138,7 +1233,7 @@ static size_t process_block_children(
1138
1233
  analyze_ruby_context_T* context,
1139
1234
  control_type_t parent_type
1140
1235
  ) {
1141
- while (index < array->size) {
1236
+ while (index < hb_array_size(array)) {
1142
1237
  AST_NODE_T* child = hb_array_get(array, index);
1143
1238
 
1144
1239
  if (!child) { break; }
@@ -1160,7 +1255,7 @@ static size_t process_block_children(
1160
1255
  hb_array_T* temp_array = hb_array_init(1);
1161
1256
  size_t new_index = process_control_structure(node, array, index, temp_array, context, child_type);
1162
1257
 
1163
- if (temp_array->size > 0) { hb_array_append(children_array, hb_array_first(temp_array)); }
1258
+ if (hb_array_size(temp_array) > 0) { hb_array_append(children_array, hb_array_first(temp_array)); }
1164
1259
 
1165
1260
  hb_array_free(&temp_array);
1166
1261
 
@@ -1176,10 +1271,10 @@ static size_t process_block_children(
1176
1271
  }
1177
1272
 
1178
1273
  hb_array_T* rewrite_node_array(AST_NODE_T* node, hb_array_T* array, analyze_ruby_context_T* context) {
1179
- hb_array_T* new_array = hb_array_init(array->size);
1274
+ hb_array_T* new_array = hb_array_init(hb_array_size(array));
1180
1275
  size_t index = 0;
1181
1276
 
1182
- while (index < array->size) {
1277
+ while (index < hb_array_size(array)) {
1183
1278
  AST_NODE_T* item = hb_array_get(array, index);
1184
1279
 
1185
1280
  if (!item) { break; }
@@ -1314,7 +1409,7 @@ static bool detect_invalid_erb_structures(const AST_NODE_T* node, void* data) {
1314
1409
  if (if_node->end_node == NULL) { check_erb_node_for_missing_end(node); }
1315
1410
 
1316
1411
  if (if_node->statements != NULL) {
1317
- for (size_t i = 0; i < if_node->statements->size; i++) {
1412
+ for (size_t i = 0; i < hb_array_size(if_node->statements); i++) {
1318
1413
  AST_NODE_T* statement = (AST_NODE_T*) hb_array_get(if_node->statements, i);
1319
1414
 
1320
1415
  if (statement != NULL) { herb_visit_node(statement, detect_invalid_erb_structures, context); }
@@ -1346,7 +1441,7 @@ static bool detect_invalid_erb_structures(const AST_NODE_T* node, void* data) {
1346
1441
  const AST_ERB_IF_NODE_T* elsif_node = (const AST_ERB_IF_NODE_T*) subsequent;
1347
1442
 
1348
1443
  if (elsif_node->statements != NULL) {
1349
- for (size_t i = 0; i < elsif_node->statements->size; i++) {
1444
+ for (size_t i = 0; i < hb_array_size(elsif_node->statements); i++) {
1350
1445
  AST_NODE_T* statement = (AST_NODE_T*) hb_array_get(elsif_node->statements, i);
1351
1446
 
1352
1447
  if (statement != NULL) { herb_visit_node(statement, detect_invalid_erb_structures, context); }
@@ -1358,7 +1453,7 @@ static bool detect_invalid_erb_structures(const AST_NODE_T* node, void* data) {
1358
1453
  const AST_ERB_ELSE_NODE_T* else_node = (const AST_ERB_ELSE_NODE_T*) subsequent;
1359
1454
 
1360
1455
  if (else_node->statements != NULL) {
1361
- for (size_t i = 0; i < else_node->statements->size; i++) {
1456
+ for (size_t i = 0; i < hb_array_size(else_node->statements); i++) {
1362
1457
  AST_NODE_T* statement = (AST_NODE_T*) hb_array_get(else_node->statements, i);
1363
1458
 
1364
1459
  if (statement != NULL) { herb_visit_node(statement, detect_invalid_erb_structures, context); }
@@ -76,6 +76,10 @@ bool has_yield_node(analyzed_ruby_T* analyzed) {
76
76
  return analyzed->yield_node_count > 0;
77
77
  }
78
78
 
79
+ bool has_then_keyword(analyzed_ruby_T* analyzed) {
80
+ return analyzed->then_keyword_count > 0;
81
+ }
82
+
79
83
  bool has_error_message(analyzed_ruby_T* anlayzed, const char* message) {
80
84
  for (const pm_diagnostic_t* error = (const pm_diagnostic_t*) anlayzed->parser.error_list.head; error != NULL;
81
85
  error = (const pm_diagnostic_t*) error->node.next) {
@@ -102,13 +106,13 @@ bool search_if_nodes(const pm_node_t* node, void* data) {
102
106
  return false;
103
107
  }
104
108
 
105
- static bool is_do_block(pm_location_t opening_location) {
109
+ bool is_do_block(pm_location_t opening_location) {
106
110
  size_t length = opening_location.end - opening_location.start;
107
111
 
108
112
  return length == 2 && opening_location.start[0] == 'd' && opening_location.start[1] == 'o';
109
113
  }
110
114
 
111
- static bool is_brace_block(pm_location_t opening_location) {
115
+ bool is_brace_block(pm_location_t opening_location) {
112
116
  size_t length = opening_location.end - opening_location.start;
113
117
 
114
118
  return length == 1 && opening_location.start[0] == '{';
@@ -118,6 +122,32 @@ static bool has_location(pm_location_t location) {
118
122
  return location.start != NULL && location.end != NULL && (location.end - location.start) > 0;
119
123
  }
120
124
 
125
+ static bool is_end_keyword(pm_location_t location) {
126
+ if (location.start == NULL || location.end == NULL) { return false; }
127
+
128
+ size_t length = location.end - location.start;
129
+
130
+ return length == 3 && location.start[0] == 'e' && location.start[1] == 'n' && location.start[2] == 'd';
131
+ }
132
+
133
+ bool is_closing_brace(pm_location_t location) {
134
+ if (location.start == NULL || location.end == NULL) { return false; }
135
+
136
+ size_t length = location.end - location.start;
137
+
138
+ return length == 1 && location.start[0] == '}';
139
+ }
140
+
141
+ bool has_valid_block_closing(pm_location_t opening_loc, pm_location_t closing_loc) {
142
+ if (is_do_block(opening_loc)) {
143
+ return is_end_keyword(closing_loc);
144
+ } else if (is_brace_block(opening_loc)) {
145
+ return is_closing_brace(closing_loc);
146
+ }
147
+
148
+ return false;
149
+ }
150
+
121
151
  bool search_block_nodes(const pm_node_t* node, void* data) {
122
152
  analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
123
153
 
@@ -125,7 +155,7 @@ bool search_block_nodes(const pm_node_t* node, void* data) {
125
155
  pm_block_node_t* block_node = (pm_block_node_t*) node;
126
156
 
127
157
  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);
158
+ bool is_unclosed = !has_valid_block_closing(block_node->opening_loc, block_node->closing_loc);
129
159
 
130
160
  if (has_opening && is_unclosed) { analyzed->block_node_count++; }
131
161
  }
@@ -299,6 +329,42 @@ bool search_yield_nodes(const pm_node_t* node, void* data) {
299
329
  return false;
300
330
  }
301
331
 
332
+ bool search_then_keywords(const pm_node_t* node, void* data) {
333
+ analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
334
+
335
+ switch (node->type) {
336
+ case PM_IF_NODE: {
337
+ const pm_if_node_t* if_node = (const pm_if_node_t*) node;
338
+ if (if_node->then_keyword_loc.start != NULL && if_node->then_keyword_loc.end != NULL) {
339
+ analyzed->then_keyword_count++;
340
+ }
341
+ break;
342
+ }
343
+
344
+ case PM_UNLESS_NODE: {
345
+ const pm_unless_node_t* unless_node = (const pm_unless_node_t*) node;
346
+ if (unless_node->then_keyword_loc.start != NULL && unless_node->then_keyword_loc.end != NULL) {
347
+ analyzed->then_keyword_count++;
348
+ }
349
+ break;
350
+ }
351
+
352
+ case PM_WHEN_NODE: {
353
+ const pm_when_node_t* when_node = (const pm_when_node_t*) node;
354
+ if (when_node->then_keyword_loc.start != NULL && when_node->then_keyword_loc.end != NULL) {
355
+ analyzed->then_keyword_count++;
356
+ }
357
+ break;
358
+ }
359
+
360
+ default: break;
361
+ }
362
+
363
+ pm_visit_child_nodes(node, search_then_keywords, analyzed);
364
+
365
+ return false;
366
+ }
367
+
302
368
  static bool is_postfix_conditional(const pm_statements_node_t* statements, pm_location_t keyword_location) {
303
369
  if (statements == NULL) { return false; }
304
370
 
@@ -314,7 +380,7 @@ bool search_unclosed_control_flows(const pm_node_t* node, void* data) {
314
380
  case PM_IF_NODE: {
315
381
  const pm_if_node_t* if_node = (const pm_if_node_t*) node;
316
382
 
317
- if (has_location(if_node->if_keyword_loc) && !has_location(if_node->end_keyword_loc)) {
383
+ if (has_location(if_node->if_keyword_loc) && !is_end_keyword(if_node->end_keyword_loc)) {
318
384
  if (!is_postfix_conditional(if_node->statements, if_node->if_keyword_loc)) {
319
385
  analyzed->unclosed_control_flow_count++;
320
386
  }
@@ -326,7 +392,7 @@ bool search_unclosed_control_flows(const pm_node_t* node, void* data) {
326
392
  case PM_UNLESS_NODE: {
327
393
  const pm_unless_node_t* unless_node = (const pm_unless_node_t*) node;
328
394
 
329
- if (has_location(unless_node->keyword_loc) && !has_location(unless_node->end_keyword_loc)) {
395
+ if (has_location(unless_node->keyword_loc) && !is_end_keyword(unless_node->end_keyword_loc)) {
330
396
  if (!is_postfix_conditional(unless_node->statements, unless_node->keyword_loc)) {
331
397
  analyzed->unclosed_control_flow_count++;
332
398
  }
@@ -338,7 +404,7 @@ bool search_unclosed_control_flows(const pm_node_t* node, void* data) {
338
404
  case PM_CASE_NODE: {
339
405
  const pm_case_node_t* case_node = (const pm_case_node_t*) node;
340
406
 
341
- if (has_location(case_node->case_keyword_loc) && !has_location(case_node->end_keyword_loc)) {
407
+ if (has_location(case_node->case_keyword_loc) && !is_end_keyword(case_node->end_keyword_loc)) {
342
408
  analyzed->unclosed_control_flow_count++;
343
409
  }
344
410
 
@@ -348,7 +414,7 @@ bool search_unclosed_control_flows(const pm_node_t* node, void* data) {
348
414
  case PM_CASE_MATCH_NODE: {
349
415
  const pm_case_match_node_t* case_match_node = (const pm_case_match_node_t*) node;
350
416
 
351
- if (has_location(case_match_node->case_keyword_loc) && !has_location(case_match_node->end_keyword_loc)) {
417
+ if (has_location(case_match_node->case_keyword_loc) && !is_end_keyword(case_match_node->end_keyword_loc)) {
352
418
  analyzed->unclosed_control_flow_count++;
353
419
  }
354
420
 
@@ -358,7 +424,7 @@ bool search_unclosed_control_flows(const pm_node_t* node, void* data) {
358
424
  case PM_WHILE_NODE: {
359
425
  const pm_while_node_t* while_node = (const pm_while_node_t*) node;
360
426
 
361
- if (has_location(while_node->keyword_loc) && !has_location(while_node->closing_loc)) {
427
+ if (has_location(while_node->keyword_loc) && !is_end_keyword(while_node->closing_loc)) {
362
428
  analyzed->unclosed_control_flow_count++;
363
429
  }
364
430
 
@@ -368,7 +434,7 @@ bool search_unclosed_control_flows(const pm_node_t* node, void* data) {
368
434
  case PM_UNTIL_NODE: {
369
435
  const pm_until_node_t* until_node = (const pm_until_node_t*) node;
370
436
 
371
- if (has_location(until_node->keyword_loc) && !has_location(until_node->closing_loc)) {
437
+ if (has_location(until_node->keyword_loc) && !is_end_keyword(until_node->closing_loc)) {
372
438
  analyzed->unclosed_control_flow_count++;
373
439
  }
374
440
 
@@ -378,7 +444,7 @@ bool search_unclosed_control_flows(const pm_node_t* node, void* data) {
378
444
  case PM_FOR_NODE: {
379
445
  const pm_for_node_t* for_node = (const pm_for_node_t*) node;
380
446
 
381
- if (has_location(for_node->for_keyword_loc) && !has_location(for_node->end_keyword_loc)) {
447
+ if (has_location(for_node->for_keyword_loc) && !is_end_keyword(for_node->end_keyword_loc)) {
382
448
  analyzed->unclosed_control_flow_count++;
383
449
  }
384
450
 
@@ -388,7 +454,7 @@ bool search_unclosed_control_flows(const pm_node_t* node, void* data) {
388
454
  case PM_BEGIN_NODE: {
389
455
  const pm_begin_node_t* begin_node = (const pm_begin_node_t*) node;
390
456
 
391
- if (has_location(begin_node->begin_keyword_loc) && !has_location(begin_node->end_keyword_loc)) {
457
+ if (has_location(begin_node->begin_keyword_loc) && !is_end_keyword(begin_node->end_keyword_loc)) {
392
458
  analyzed->unclosed_control_flow_count++;
393
459
  }
394
460
 
@@ -399,7 +465,9 @@ bool search_unclosed_control_flows(const pm_node_t* node, void* data) {
399
465
  const pm_block_node_t* block_node = (const pm_block_node_t*) node;
400
466
  bool has_opening = is_do_block(block_node->opening_loc) || is_brace_block(block_node->opening_loc);
401
467
 
402
- if (has_opening && !has_location(block_node->closing_loc)) { analyzed->unclosed_control_flow_count++; }
468
+ if (has_opening && !has_valid_block_closing(block_node->opening_loc, block_node->closing_loc)) {
469
+ analyzed->unclosed_control_flow_count++;
470
+ }
403
471
  break;
404
472
  }
405
473
 
data/src/analyzed_ruby.c CHANGED
@@ -30,6 +30,7 @@ analyzed_ruby_T* init_analyzed_ruby(hb_string_T source) {
30
30
  analyzed->ensure_node_count = 0;
31
31
  analyzed->unless_node_count = 0;
32
32
  analyzed->yield_node_count = 0;
33
+ analyzed->then_keyword_count = 0;
33
34
  analyzed->unclosed_control_flow_count = 0;
34
35
 
35
36
  return analyzed;
data/src/ast_node.c CHANGED
@@ -43,7 +43,7 @@ ast_node_type_T ast_node_type(const AST_NODE_T* node) {
43
43
  }
44
44
 
45
45
  size_t ast_node_errors_count(const AST_NODE_T* node) {
46
- return node->errors->size;
46
+ return hb_array_size(node->errors);
47
47
  }
48
48
 
49
49
  hb_array_T* ast_node_errors(const AST_NODE_T* node) {