herb 0.9.3-arm-linux-gnu → 0.9.5-arm-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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/config.yml +57 -21
  3. data/ext/herb/nodes.c +93 -55
  4. data/lib/herb/3.0/herb.so +0 -0
  5. data/lib/herb/3.1/herb.so +0 -0
  6. data/lib/herb/3.2/herb.so +0 -0
  7. data/lib/herb/3.3/herb.so +0 -0
  8. data/lib/herb/3.4/herb.so +0 -0
  9. data/lib/herb/4.0/herb.so +0 -0
  10. data/lib/herb/ast/nodes.rb +212 -78
  11. data/lib/herb/engine/compiler.rb +52 -26
  12. data/lib/herb/engine.rb +3 -0
  13. data/lib/herb/project.rb +58 -17
  14. data/lib/herb/version.rb +1 -1
  15. data/lib/herb/visitor.rb +8 -2
  16. data/sig/herb/ast/nodes.rbs +85 -34
  17. data/sig/herb/engine/compiler.rbs +16 -0
  18. data/sig/herb/engine.rbs +3 -0
  19. data/sig/herb/visitor.rbs +5 -2
  20. data/sig/serialized_ast_nodes.rbs +20 -9
  21. data/src/analyze/action_view/javascript_tag.c +38 -0
  22. data/src/analyze/action_view/tag_helper_node_builders.c +23 -2
  23. data/src/analyze/action_view/tag_helpers.c +53 -14
  24. data/src/analyze/analyze.c +23 -3
  25. data/src/analyze/analyze_helpers.c +406 -0
  26. data/src/analyze/builders.c +1 -0
  27. data/src/analyze/missing_end.c +16 -0
  28. data/src/analyze/parse_errors.c +43 -1
  29. data/src/analyze/render_nodes.c +231 -35
  30. data/src/analyze/strict_locals.c +22 -324
  31. data/src/analyze/transform.c +23 -2
  32. data/src/ast/ast_nodes.c +114 -57
  33. data/src/ast/ast_pretty_print.c +109 -25
  34. data/src/include/analyze/action_view/tag_helper_handler.h +3 -0
  35. data/src/include/analyze/action_view/tag_helper_node_builders.h +7 -0
  36. data/src/include/analyze/analyze.h +6 -1
  37. data/src/include/analyze/helpers.h +18 -0
  38. data/src/include/ast/ast_nodes.h +27 -13
  39. data/src/include/version.h +1 -1
  40. data/src/parser/match_tags.c +37 -6
  41. data/src/parser.c +8 -0
  42. data/src/visitor.c +50 -7
  43. metadata +1 -1
@@ -639,7 +639,15 @@ static AST_NODE_T* transform_tag_helper_with_attributes(
639
639
  id_is_ruby_expression ? create_html_attribute_with_ruby_literal("id", id_value, id_start, id_end, allocator)
640
640
  : create_html_attribute_node("id", id_value, id_start, id_end, allocator);
641
641
 
642
- if (id_attribute) { attributes = prepend_attribute(attributes, (AST_NODE_T*) id_attribute, allocator); }
642
+ if (id_attribute) {
643
+ AST_NODE_T* src_node = remove_attribute_by_name(attributes, "src");
644
+ AST_NODE_T* target_node = remove_attribute_by_name(attributes, "target");
645
+
646
+ hb_array_append(attributes, (AST_NODE_T*) id_attribute);
647
+
648
+ if (src_node) { hb_array_append(attributes, src_node); }
649
+ if (target_node) { hb_array_append(attributes, target_node); }
650
+ }
643
651
 
644
652
  hb_allocator_dealloc(allocator, id_value);
645
653
  }
@@ -679,7 +687,7 @@ static AST_NODE_T* transform_tag_helper_with_attributes(
679
687
  ? create_html_attribute_node("src", source_attribute_value, source_start, source_end, allocator)
680
688
  : create_html_attribute_with_ruby_literal("src", source_attribute_value, source_start, source_end, allocator);
681
689
 
682
- if (source_attribute) { attributes = prepend_attribute(attributes, (AST_NODE_T*) source_attribute, allocator); }
690
+ if (source_attribute) { hb_array_append(attributes, (AST_NODE_T*) source_attribute); }
683
691
 
684
692
  if (source_attribute_value != source_value) { hb_allocator_dealloc(allocator, source_attribute_value); }
685
693
  hb_allocator_dealloc(allocator, source_value);
@@ -723,7 +731,7 @@ static AST_NODE_T* transform_tag_helper_with_attributes(
723
731
  ? create_html_attribute_node("src", source_attribute_value, source_start, source_end, allocator)
724
732
  : create_html_attribute_with_ruby_literal("src", source_attribute_value, source_start, source_end, allocator);
725
733
 
726
- if (source_attribute) { attributes = prepend_attribute(attributes, (AST_NODE_T*) source_attribute, allocator); }
734
+ if (source_attribute) { hb_array_append(attributes, (AST_NODE_T*) source_attribute); }
727
735
  if (source_attribute_value != source_value) { hb_allocator_dealloc(allocator, source_attribute_value); }
728
736
 
729
737
  hb_allocator_dealloc(allocator, source_value);
@@ -798,14 +806,37 @@ static AST_NODE_T* transform_tag_helper_with_attributes(
798
806
  );
799
807
  }
800
808
 
801
- append_body_content_node(
802
- body,
803
- helper_content,
804
- content_is_ruby_expression,
805
- erb_node->base.location.start,
806
- erb_node->base.location.end,
807
- allocator
808
- );
809
+ if (string_equals(handler->name, "javascript_tag")) {
810
+ hb_array_T* cdata_children = hb_array_init(1, allocator);
811
+
812
+ append_body_content_node(
813
+ cdata_children,
814
+ helper_content,
815
+ content_is_ruby_expression,
816
+ erb_node->base.location.start,
817
+ erb_node->base.location.end,
818
+ allocator
819
+ );
820
+
821
+ AST_CDATA_NODE_T* cdata_node = create_javascript_cdata_node(
822
+ cdata_children,
823
+ erb_node->base.location.start,
824
+ erb_node->base.location.end,
825
+ allocator
826
+ );
827
+
828
+ if (cdata_node) { hb_array_append(body, (AST_NODE_T*) cdata_node); }
829
+ } else {
830
+ append_body_content_node(
831
+ body,
832
+ helper_content,
833
+ content_is_ruby_expression,
834
+ erb_node->base.location.start,
835
+ erb_node->base.location.end,
836
+ allocator
837
+ );
838
+ }
839
+
809
840
  hb_allocator_dealloc(allocator, helper_content);
810
841
  }
811
842
 
@@ -1117,7 +1148,7 @@ static AST_NODE_T* transform_erb_block_to_tag_helper(
1117
1148
  AST_HTML_ATTRIBUTE_NODE_T* href_attribute =
1118
1149
  create_href_attribute(href, href_is_ruby_expression, href_start, href_end, allocator);
1119
1150
 
1120
- if (href_attribute) { attributes = prepend_attribute(attributes, (AST_NODE_T*) href_attribute, allocator); }
1151
+ if (href_attribute) { hb_array_append(attributes, (AST_NODE_T*) href_attribute); }
1121
1152
 
1122
1153
  hb_allocator_dealloc(allocator, href);
1123
1154
  }
@@ -1138,7 +1169,15 @@ static AST_NODE_T* transform_erb_block_to_tag_helper(
1138
1169
  id_is_ruby_expression ? create_html_attribute_with_ruby_literal("id", id_value, id_start, id_end, allocator)
1139
1170
  : create_html_attribute_node("id", id_value, id_start, id_end, allocator);
1140
1171
 
1141
- if (id_attribute) { attributes = prepend_attribute(attributes, (AST_NODE_T*) id_attribute, allocator); }
1172
+ if (id_attribute) {
1173
+ AST_NODE_T* src_node = remove_attribute_by_name(attributes, "src");
1174
+ AST_NODE_T* target_node = remove_attribute_by_name(attributes, "target");
1175
+
1176
+ hb_array_append(attributes, (AST_NODE_T*) id_attribute);
1177
+
1178
+ if (src_node) { hb_array_append(attributes, src_node); }
1179
+ if (target_node) { hb_array_append(attributes, target_node); }
1180
+ }
1142
1181
 
1143
1182
  hb_allocator_dealloc(allocator, id_value);
1144
1183
  }
@@ -1395,7 +1434,7 @@ static AST_NODE_T* transform_link_to_helper(
1395
1434
  AST_HTML_ATTRIBUTE_NODE_T* href_attribute =
1396
1435
  create_href_attribute(href, href_is_ruby_expression, href_start, href_end, allocator);
1397
1436
 
1398
- if (href_attribute) { attributes = prepend_attribute(attributes, (AST_NODE_T*) href_attribute, allocator); }
1437
+ if (href_attribute) { hb_array_append(attributes, (AST_NODE_T*) href_attribute); }
1399
1438
  if (!info->content) {
1400
1439
  href_for_body = hb_allocator_strdup(allocator, href);
1401
1440
  href_for_body_is_ruby_expression = href_is_ruby_expression;
@@ -75,8 +75,7 @@ static bool analyze_erb_content(const AST_NODE_T* node, void* data) {
75
75
 
76
76
  hb_string_T opening = erb_content_node->tag_opening->value;
77
77
 
78
- if (!hb_string_equals(opening, hb_string("<%%")) && !hb_string_equals(opening, hb_string("<%%="))
79
- && !hb_string_equals(opening, hb_string("<%#")) && !hb_string_equals(opening, hb_string("<%graphql"))) {
78
+ if (!hb_string_equals(opening, hb_string("<%#")) && !hb_string_equals(opening, hb_string("<%graphql"))) {
80
79
  analyzed_ruby_T* analyzed = herb_analyze_ruby(erb_content_node->content->value);
81
80
 
82
81
  erb_content_node->parsed = true;
@@ -737,12 +736,32 @@ static size_t process_block_structure(
737
736
  hb_array_T* block_errors = erb_node->base.errors;
738
737
  erb_node->base.errors = NULL;
739
738
 
739
+ // Filter out "incomplete block" Prism errors since we've matched the block with its end tag.
740
+ if (block_errors) {
741
+ for (size_t error_index = hb_array_size(block_errors); error_index > 0; error_index--) {
742
+ ERROR_T* error = hb_array_get(block_errors, error_index - 1);
743
+ if (!error || error->type != RUBY_PARSE_ERROR) { continue; }
744
+
745
+ RUBY_PARSE_ERROR_T* parse_error = (RUBY_PARSE_ERROR_T*) error;
746
+
747
+ if (string_equals(parse_error->diagnostic_id.data, "block_term_end")
748
+ || string_equals(parse_error->diagnostic_id.data, "block_term_brace")
749
+ || string_equals(parse_error->diagnostic_id.data, "unexpected_token_close_context")) {
750
+ hb_array_remove(block_errors, error_index - 1);
751
+ }
752
+ }
753
+ }
754
+
755
+ hb_array_T* block_arguments =
756
+ extract_block_arguments_from_erb_node(erb_node, context->source, block_errors, allocator);
757
+
740
758
  AST_ERB_BLOCK_NODE_T* block_node = ast_erb_block_node_init(
741
759
  erb_node->tag_opening,
742
760
  erb_node->content,
743
761
  erb_node->tag_closing,
744
762
  HERB_PRISM_NODE_EMPTY,
745
763
  children,
764
+ block_arguments,
746
765
  rescue_clause,
747
766
  else_clause,
748
767
  ensure_clause,
@@ -943,6 +962,7 @@ hb_array_T* get_node_children_array(const AST_NODE_T* node) {
943
962
  case AST_ERB_ENSURE_NODE: return ((AST_ERB_ENSURE_NODE_T*) node)->statements;
944
963
  case AST_ERB_CASE_NODE: return ((AST_ERB_CASE_NODE_T*) node)->children;
945
964
  case AST_ERB_WHEN_NODE: return ((AST_ERB_WHEN_NODE_T*) node)->statements;
965
+ case AST_ERB_RENDER_NODE: return ((AST_ERB_RENDER_NODE_T*) node)->body;
946
966
  default: return NULL;
947
967
  }
948
968
  }
@@ -1032,7 +1052,7 @@ void herb_analyze_parse_tree(
1032
1052
 
1033
1053
  herb_visit_node((AST_NODE_T*) document, detect_invalid_erb_structures, &invalid_context);
1034
1054
 
1035
- herb_analyze_parse_errors(document, source, allocator);
1055
+ herb_analyze_parse_errors(document, source, options, allocator);
1036
1056
 
1037
1057
  herb_parser_match_html_tags_post_analyze(document, options, allocator);
1038
1058
 
@@ -2,8 +2,14 @@
2
2
  #include <stdbool.h>
3
3
  #include <string.h>
4
4
 
5
+ #include "../include/analyze/action_view/tag_helper_node_builders.h"
6
+ #include "../include/analyze/action_view/tag_helpers.h"
5
7
  #include "../include/analyze/analyzed_ruby.h"
8
+ #include "../include/analyze/helpers.h"
9
+ #include "../include/ast/ast_nodes.h"
10
+ #include "../include/lib/hb_array.h"
6
11
  #include "../include/lib/string.h"
12
+ #include "../include/prism/prism_helpers.h"
7
13
 
8
14
  bool has_if_node(analyzed_ruby_T* analyzed) {
9
15
  return analyzed && analyzed->if_node_count > 0;
@@ -526,3 +532,403 @@ bool search_unclosed_control_flows(const pm_node_t* node, void* data) {
526
532
 
527
533
  return false;
528
534
  }
535
+
536
+ static pm_block_node_t* find_first_block_node(pm_node_t* node) {
537
+ if (!node) { return NULL; }
538
+
539
+ if (node->type == PM_CALL_NODE) {
540
+ pm_call_node_t* call = (pm_call_node_t*) node;
541
+
542
+ if (call->block && call->block->type == PM_BLOCK_NODE) { return (pm_block_node_t*) call->block; }
543
+ }
544
+
545
+ if (node->type == PM_PROGRAM_NODE) {
546
+ pm_program_node_t* program = (pm_program_node_t*) node;
547
+
548
+ if (program->statements && program->statements->body.size > 0) {
549
+ return find_first_block_node(program->statements->body.nodes[0]);
550
+ }
551
+ }
552
+
553
+ if (node->type == PM_STATEMENTS_NODE) {
554
+ pm_statements_node_t* statements = (pm_statements_node_t*) node;
555
+
556
+ if (statements->body.size > 0) { return find_first_block_node(statements->body.nodes[0]); }
557
+ }
558
+
559
+ return NULL;
560
+ }
561
+
562
+ static position_T prism_to_source_position(
563
+ const uint8_t* prism_pointer,
564
+ const uint8_t* prism_source_start,
565
+ size_t source_base_offset,
566
+ const char* source
567
+ ) {
568
+ if (!source || !prism_source_start) { return (position_T) { .line = 1, .column = 0 }; }
569
+
570
+ size_t prism_offset = (size_t) (prism_pointer - prism_source_start);
571
+ return byte_offset_to_position(source, source_base_offset + prism_offset);
572
+ }
573
+
574
+ static token_T* create_parameter_name_token(
575
+ pm_location_t location,
576
+ const char* name,
577
+ const uint8_t* prism_source_start,
578
+ size_t source_base_offset,
579
+ const char* source,
580
+ hb_allocator_T* allocator
581
+ ) {
582
+ position_T start = prism_to_source_position(location.start, prism_source_start, source_base_offset, source);
583
+ position_T end = prism_to_source_position(location.end, prism_source_start, source_base_offset, source);
584
+
585
+ return create_synthetic_token(allocator, name, TOKEN_IDENTIFIER, start, end);
586
+ }
587
+
588
+ static void append_parameter(
589
+ hb_array_T* result,
590
+ token_T* name_token,
591
+ AST_RUBY_LITERAL_NODE_T* default_value,
592
+ const char* kind,
593
+ bool required,
594
+ position_T start,
595
+ position_T end,
596
+ hb_allocator_T* allocator
597
+ ) {
598
+ hb_array_append(
599
+ result,
600
+ ast_ruby_parameter_node_init(
601
+ name_token,
602
+ default_value,
603
+ hb_string(kind),
604
+ required,
605
+ start,
606
+ end,
607
+ hb_array_init(0, allocator),
608
+ allocator
609
+ )
610
+ );
611
+ }
612
+
613
+ hb_array_T* extract_parameters_from_prism(
614
+ pm_parameters_node_t* parameters,
615
+ pm_parser_t* parser,
616
+ const char* source,
617
+ size_t source_base_offset,
618
+ const uint8_t* prism_source_start,
619
+ hb_allocator_T* allocator
620
+ ) {
621
+ if (!parameters) { return hb_array_init(0, allocator); }
622
+
623
+ size_t count = parameters->requireds.size + parameters->optionals.size + parameters->keywords.size;
624
+ if (parameters->rest) { count++; }
625
+ if (parameters->keyword_rest) { count++; }
626
+ if (parameters->block) { count++; }
627
+
628
+ hb_array_T* result = hb_array_init(count, allocator);
629
+
630
+ // Required positional: |item|
631
+ for (size_t index = 0; index < parameters->requireds.size; index++) {
632
+ pm_node_t* node = parameters->requireds.nodes[index];
633
+ if (node->type != PM_REQUIRED_PARAMETER_NODE) { continue; }
634
+
635
+ pm_required_parameter_node_t* required = (pm_required_parameter_node_t*) node;
636
+ pm_constant_t* constant = pm_constant_pool_id_to_constant(&parser->constant_pool, required->name);
637
+ if (!constant) { continue; }
638
+
639
+ char* name = hb_allocator_strndup(allocator, (const char*) constant->start, constant->length);
640
+ position_T start = prism_to_source_position(node->location.start, prism_source_start, source_base_offset, source);
641
+ position_T end = prism_to_source_position(node->location.end, prism_source_start, source_base_offset, source);
642
+
643
+ append_parameter(
644
+ result,
645
+ create_parameter_name_token(node->location, name, prism_source_start, source_base_offset, source, allocator),
646
+ NULL,
647
+ "positional",
648
+ true,
649
+ start,
650
+ end,
651
+ allocator
652
+ );
653
+
654
+ hb_allocator_dealloc(allocator, name);
655
+ }
656
+
657
+ // Optional positional: |item = nil|
658
+ for (size_t index = 0; index < parameters->optionals.size; index++) {
659
+ pm_node_t* node = parameters->optionals.nodes[index];
660
+ if (node->type != PM_OPTIONAL_PARAMETER_NODE) { continue; }
661
+
662
+ pm_optional_parameter_node_t* optional = (pm_optional_parameter_node_t*) node;
663
+ size_t name_length = (size_t) (optional->name_loc.end - optional->name_loc.start);
664
+ char* name = hb_allocator_strndup(allocator, (const char*) optional->name_loc.start, name_length);
665
+
666
+ position_T start = prism_to_source_position(node->location.start, prism_source_start, source_base_offset, source);
667
+ position_T end = prism_to_source_position(node->location.end, prism_source_start, source_base_offset, source);
668
+
669
+ AST_RUBY_LITERAL_NODE_T* default_value = NULL;
670
+
671
+ if (optional->value) {
672
+ size_t value_length = (size_t) (optional->value->location.end - optional->value->location.start);
673
+ char* value_string = hb_allocator_strndup(allocator, (const char*) optional->value->location.start, value_length);
674
+ position_T value_start =
675
+ prism_to_source_position(optional->value->location.start, prism_source_start, source_base_offset, source);
676
+ position_T value_end =
677
+ prism_to_source_position(optional->value->location.end, prism_source_start, source_base_offset, source);
678
+
679
+ default_value = ast_ruby_literal_node_init(
680
+ hb_string_from_c_string(value_string),
681
+ value_start,
682
+ value_end,
683
+ hb_array_init(0, allocator),
684
+ allocator
685
+ );
686
+ hb_allocator_dealloc(allocator, value_string);
687
+ }
688
+
689
+ append_parameter(
690
+ result,
691
+ create_parameter_name_token(optional->name_loc, name, prism_source_start, source_base_offset, source, allocator),
692
+ default_value,
693
+ "positional",
694
+ false,
695
+ start,
696
+ end,
697
+ allocator
698
+ );
699
+
700
+ hb_allocator_dealloc(allocator, name);
701
+ }
702
+
703
+ // Rest: |*items|
704
+ if (parameters->rest && parameters->rest->type == PM_REST_PARAMETER_NODE) {
705
+ pm_rest_parameter_node_t* rest = (pm_rest_parameter_node_t*) parameters->rest;
706
+
707
+ if (rest->name) {
708
+ size_t name_length = (size_t) (rest->name_loc.end - rest->name_loc.start);
709
+ char* name = hb_allocator_strndup(allocator, (const char*) rest->name_loc.start, name_length);
710
+
711
+ position_T start =
712
+ prism_to_source_position(parameters->rest->location.start, prism_source_start, source_base_offset, source);
713
+ position_T end =
714
+ prism_to_source_position(parameters->rest->location.end, prism_source_start, source_base_offset, source);
715
+
716
+ append_parameter(
717
+ result,
718
+ create_parameter_name_token(rest->name_loc, name, prism_source_start, source_base_offset, source, allocator),
719
+ NULL,
720
+ "rest",
721
+ false,
722
+ start,
723
+ end,
724
+ allocator
725
+ );
726
+
727
+ hb_allocator_dealloc(allocator, name);
728
+ }
729
+ }
730
+
731
+ // Keywords: |name:| or |title: "default"|
732
+ for (size_t index = 0; index < parameters->keywords.size; index++) {
733
+ pm_node_t* keyword = parameters->keywords.nodes[index];
734
+
735
+ pm_location_t name_location;
736
+ bool is_required = false;
737
+ AST_RUBY_LITERAL_NODE_T* default_value = NULL;
738
+
739
+ if (keyword->type == PM_REQUIRED_KEYWORD_PARAMETER_NODE) {
740
+ name_location = ((pm_required_keyword_parameter_node_t*) keyword)->name_loc;
741
+ is_required = true;
742
+ } else if (keyword->type == PM_OPTIONAL_KEYWORD_PARAMETER_NODE) {
743
+ pm_optional_keyword_parameter_node_t* optional_keyword = (pm_optional_keyword_parameter_node_t*) keyword;
744
+ name_location = optional_keyword->name_loc;
745
+
746
+ if (optional_keyword->value) {
747
+ size_t value_length =
748
+ (size_t) (optional_keyword->value->location.end - optional_keyword->value->location.start);
749
+ char* value_string =
750
+ hb_allocator_strndup(allocator, (const char*) optional_keyword->value->location.start, value_length);
751
+
752
+ position_T value_start = prism_to_source_position(
753
+ optional_keyword->value->location.start,
754
+ prism_source_start,
755
+ source_base_offset,
756
+ source
757
+ );
758
+
759
+ position_T value_end = prism_to_source_position(
760
+ optional_keyword->value->location.end,
761
+ prism_source_start,
762
+ source_base_offset,
763
+ source
764
+ );
765
+
766
+ default_value = ast_ruby_literal_node_init(
767
+ hb_string_from_c_string(value_string),
768
+ value_start,
769
+ value_end,
770
+ hb_array_init(0, allocator),
771
+ allocator
772
+ );
773
+
774
+ hb_allocator_dealloc(allocator, value_string);
775
+ }
776
+ } else {
777
+ continue;
778
+ }
779
+
780
+ size_t name_length = (size_t) (name_location.end - name_location.start);
781
+ if (name_length > 0 && name_location.start[name_length - 1] == ':') { name_length--; }
782
+ char* name = hb_allocator_strndup(allocator, (const char*) name_location.start, name_length);
783
+
784
+ position_T start =
785
+ prism_to_source_position(keyword->location.start, prism_source_start, source_base_offset, source);
786
+ position_T end = prism_to_source_position(keyword->location.end, prism_source_start, source_base_offset, source);
787
+
788
+ append_parameter(
789
+ result,
790
+ create_parameter_name_token(name_location, name, prism_source_start, source_base_offset, source, allocator),
791
+ default_value,
792
+ "keyword",
793
+ is_required,
794
+ start,
795
+ end,
796
+ allocator
797
+ );
798
+
799
+ hb_allocator_dealloc(allocator, name);
800
+ }
801
+
802
+ // Keyword rest: |**opts| or |**|
803
+ if (parameters->keyword_rest && parameters->keyword_rest->type == PM_KEYWORD_REST_PARAMETER_NODE) {
804
+ pm_keyword_rest_parameter_node_t* keyword_rest = (pm_keyword_rest_parameter_node_t*) parameters->keyword_rest;
805
+
806
+ position_T start = prism_to_source_position(
807
+ parameters->keyword_rest->location.start,
808
+ prism_source_start,
809
+ source_base_offset,
810
+ source
811
+ );
812
+
813
+ position_T end =
814
+ prism_to_source_position(parameters->keyword_rest->location.end, prism_source_start, source_base_offset, source);
815
+
816
+ if (keyword_rest->name) {
817
+ size_t name_length = (size_t) (keyword_rest->name_loc.end - keyword_rest->name_loc.start);
818
+ char* name = hb_allocator_strndup(allocator, (const char*) keyword_rest->name_loc.start, name_length);
819
+
820
+ append_parameter(
821
+ result,
822
+ create_parameter_name_token(
823
+ keyword_rest->name_loc,
824
+ name,
825
+ prism_source_start,
826
+ source_base_offset,
827
+ source,
828
+ allocator
829
+ ),
830
+ NULL,
831
+ "keyword_rest",
832
+ false,
833
+ start,
834
+ end,
835
+ allocator
836
+ );
837
+
838
+ hb_allocator_dealloc(allocator, name);
839
+ } else {
840
+ // Anonymous **
841
+ append_parameter(result, NULL, NULL, "keyword_rest", false, start, end, allocator);
842
+ }
843
+ }
844
+
845
+ // Block: |&blk|
846
+ if (parameters->block) {
847
+ pm_block_parameter_node_t* block_param = parameters->block;
848
+
849
+ if (block_param->name) {
850
+ size_t name_length = (size_t) (block_param->name_loc.end - block_param->name_loc.start);
851
+ char* name = hb_allocator_strndup(allocator, (const char*) block_param->name_loc.start, name_length);
852
+
853
+ position_T start =
854
+ prism_to_source_position(block_param->base.location.start, prism_source_start, source_base_offset, source);
855
+ position_T end =
856
+ prism_to_source_position(block_param->base.location.end, prism_source_start, source_base_offset, source);
857
+
858
+ append_parameter(
859
+ result,
860
+ create_parameter_name_token(
861
+ block_param->name_loc,
862
+ name,
863
+ prism_source_start,
864
+ source_base_offset,
865
+ source,
866
+ allocator
867
+ ),
868
+ NULL,
869
+ "block",
870
+ false,
871
+ start,
872
+ end,
873
+ allocator
874
+ );
875
+
876
+ hb_allocator_dealloc(allocator, name);
877
+ }
878
+ }
879
+
880
+ return result;
881
+ }
882
+
883
+ hb_array_T* extract_block_arguments_from_erb_node(
884
+ const AST_ERB_CONTENT_NODE_T* erb_node,
885
+ const char* source,
886
+ hb_array_T* errors,
887
+ hb_allocator_T* allocator
888
+ ) {
889
+ if (!erb_node || !erb_node->analyzed_ruby || !erb_node->analyzed_ruby->parsed) { return hb_array_init(0, allocator); }
890
+
891
+ pm_parser_t* parser = &erb_node->analyzed_ruby->parser;
892
+ pm_block_node_t* block_node = find_first_block_node(erb_node->analyzed_ruby->root);
893
+
894
+ if (!block_node || !block_node->parameters) { return hb_array_init(0, allocator); }
895
+ if (block_node->parameters->type != PM_BLOCK_PARAMETERS_NODE) { return hb_array_init(0, allocator); }
896
+
897
+ pm_block_parameters_node_t* block_parameters = (pm_block_parameters_node_t*) block_node->parameters;
898
+ size_t erb_content_offset = 0;
899
+
900
+ if (source && erb_node->content) {
901
+ erb_content_offset = calculate_byte_offset_from_position(source, erb_node->content->location.start);
902
+ }
903
+
904
+ const uint8_t* prism_source_start = (const uint8_t*) parser->start;
905
+
906
+ if (errors) {
907
+ for (const pm_diagnostic_t* error = (const pm_diagnostic_t*) parser->error_list.head; error != NULL;
908
+ error = (const pm_diagnostic_t*) error->node.next) {
909
+ if (error->diag_id == PM_ERR_BLOCK_TERM_END) { continue; }
910
+ if (error->diag_id == PM_ERR_BLOCK_TERM_BRACE) { continue; }
911
+ if (error->diag_id == PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT) { continue; }
912
+
913
+ size_t error_start_offset = (size_t) (error->location.start - prism_source_start);
914
+ size_t error_end_offset = (size_t) (error->location.end - prism_source_start);
915
+
916
+ position_T error_start = byte_offset_to_position(source, erb_content_offset + error_start_offset);
917
+ position_T error_end = byte_offset_to_position(source, erb_content_offset + error_end_offset);
918
+
919
+ RUBY_PARSE_ERROR_T* parse_error =
920
+ ruby_parse_error_from_prism_error_with_positions(error, error_start, error_end, allocator);
921
+
922
+ hb_array_append(errors, parse_error);
923
+ }
924
+ }
925
+
926
+ return extract_parameters_from_prism(
927
+ block_parameters->parameters,
928
+ parser,
929
+ source,
930
+ erb_content_offset,
931
+ prism_source_start,
932
+ allocator
933
+ );
934
+ }
@@ -322,6 +322,7 @@ static AST_NODE_T* build_block_node(control_builder_context_T* context) {
322
322
  context->tag_closing,
323
323
  HERB_PRISM_NODE_EMPTY,
324
324
  context->children,
325
+ hb_array_init(0, context->allocator),
325
326
  NULL,
326
327
  NULL,
327
328
  NULL,
@@ -153,6 +153,22 @@ void check_erb_node_for_missing_end(const AST_NODE_T* node, hb_allocator_T* allo
153
153
  break;
154
154
  }
155
155
 
156
+ case AST_ERB_RENDER_NODE: {
157
+ const AST_ERB_RENDER_NODE_T* erb_render_node = (const AST_ERB_RENDER_NODE_T*) node;
158
+
159
+ if (erb_render_node->end_node == NULL) {
160
+ append_missing_erb_end_tag_error(
161
+ hb_string("`<" "%" " render " "%" ">`"),
162
+ erb_render_node->tag_opening->location.start,
163
+ erb_render_node->tag_closing->location.end,
164
+ allocator,
165
+ node->errors
166
+ );
167
+ }
168
+
169
+ break;
170
+ }
171
+
156
172
  default: break;
157
173
  }
158
174
  }