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
@@ -5,12 +5,44 @@
5
5
  #include "../include/extract.h"
6
6
  #include "../include/lib/hb_allocator.h"
7
7
  #include "../include/lib/hb_string.h"
8
+ #include "../include/lib/string.h"
8
9
  #include "../include/prism/prism_helpers.h"
9
10
 
10
11
  #include <prism.h>
11
12
  #include <stdlib.h>
12
13
  #include <string.h>
13
14
 
15
+ static bool document_has_anonymous_keyword_rest(AST_DOCUMENT_NODE_T* document) {
16
+ if (!document || !document->children) { return false; }
17
+
18
+ for (size_t index = 0; index < hb_array_size(document->children); index++) {
19
+ AST_NODE_T* child = hb_array_get(document->children, index);
20
+ if (!child || child->type != AST_ERB_STRICT_LOCALS_NODE) { continue; }
21
+
22
+ AST_ERB_STRICT_LOCALS_NODE_T* strict_locals_node = (AST_ERB_STRICT_LOCALS_NODE_T*) child;
23
+ if (!strict_locals_node->locals) { continue; }
24
+
25
+ for (size_t local_index = 0; local_index < hb_array_size(strict_locals_node->locals); local_index++) {
26
+ AST_RUBY_PARAMETER_NODE_T* local = hb_array_get(strict_locals_node->locals, local_index);
27
+ if (local && string_equals(local->kind.data, "keyword_rest") && local->name == NULL) { return true; }
28
+ }
29
+ }
30
+
31
+ return false;
32
+ }
33
+
34
+ static bool should_skip_forwarding_error(
35
+ const pm_diagnostic_t* error,
36
+ bool strict_locals_enabled,
37
+ bool has_anonymous_keyword_rest
38
+ ) {
39
+ if (error->diag_id != PM_ERR_ARGUMENT_NO_FORWARDING_STAR_STAR) { return false; }
40
+
41
+ if (!strict_locals_enabled) { return true; }
42
+
43
+ return has_anonymous_keyword_rest;
44
+ }
45
+
14
46
  static void parse_erb_content_errors(AST_NODE_T* erb_node, const char* source, hb_allocator_T* allocator) {
15
47
  if (!erb_node || erb_node->type != AST_ERB_CONTENT_NODE) { return; }
16
48
  AST_ERB_CONTENT_NODE_T* content_node = (AST_ERB_CONTENT_NODE_T*) erb_node;
@@ -45,11 +77,19 @@ static void parse_erb_content_errors(AST_NODE_T* erb_node, const char* source, h
45
77
  free(content);
46
78
  }
47
79
 
48
- void herb_analyze_parse_errors(AST_DOCUMENT_NODE_T* document, const char* source, hb_allocator_T* allocator) {
80
+ void herb_analyze_parse_errors(
81
+ AST_DOCUMENT_NODE_T* document,
82
+ const char* source,
83
+ const parser_options_T* parser_options,
84
+ hb_allocator_T* allocator
85
+ ) {
49
86
  char* extracted_ruby = herb_extract_ruby_with_semicolons(source, allocator);
50
87
 
51
88
  if (!extracted_ruby) { return; }
52
89
 
90
+ bool strict_locals_enabled = parser_options && parser_options->strict_locals;
91
+ bool has_anonymous_keyword_rest = strict_locals_enabled && document_has_anonymous_keyword_rest(document);
92
+
53
93
  pm_parser_t parser;
54
94
  pm_options_t options = { 0, .partial_script = true };
55
95
  pm_parser_init(&parser, (const uint8_t*) extracted_ruby, strlen(extracted_ruby), &options);
@@ -58,6 +98,8 @@ void herb_analyze_parse_errors(AST_DOCUMENT_NODE_T* document, const char* source
58
98
 
59
99
  for (const pm_diagnostic_t* error = (const pm_diagnostic_t*) parser.error_list.head; error != NULL;
60
100
  error = (const pm_diagnostic_t*) error->node.next) {
101
+ if (should_skip_forwarding_error(error, strict_locals_enabled, has_anonymous_keyword_rest)) { continue; }
102
+
61
103
  size_t error_offset = (size_t) (error->location.start - parser.start);
62
104
 
63
105
  if (strstr(error->message, "unexpected ';'") != NULL) {
@@ -2,6 +2,7 @@
2
2
  #include "../include/analyze/action_view/tag_helper_node_builders.h"
3
3
  #include "../include/analyze/action_view/tag_helpers.h"
4
4
  #include "../include/analyze/analyze.h"
5
+ #include "../include/analyze/helpers.h"
5
6
  #include "../include/ast/ast_nodes.h"
6
7
  #include "../include/errors.h"
7
8
  #include "../include/lib/hb_allocator.h"
@@ -14,6 +15,16 @@
14
15
  #include <stdbool.h>
15
16
  #include <string.h>
16
17
 
18
+ static token_T* create_token_from_prism_node(
19
+ pm_node_t* node,
20
+ const char* value,
21
+ token_type_T type,
22
+ const char* source,
23
+ size_t erb_content_offset,
24
+ const uint8_t* erb_content_source,
25
+ hb_allocator_T* allocator
26
+ );
27
+
17
28
  typedef struct {
18
29
  char* value;
19
30
  pm_node_t* value_node;
@@ -62,6 +73,27 @@ static pm_call_node_t* find_render_call(pm_node_t* node, pm_parser_t* parser) {
62
73
  return NULL;
63
74
  }
64
75
 
76
+ static pm_block_node_t* find_block_on_render_call(pm_call_node_t* render_call) {
77
+ if (!render_call) { return NULL; }
78
+
79
+ if (render_call->block && render_call->block->type == PM_BLOCK_NODE) { return (pm_block_node_t*) render_call->block; }
80
+
81
+ pm_arguments_node_t* arguments = render_call->arguments;
82
+ if (!arguments || arguments->arguments.size == 0) { return NULL; }
83
+
84
+ pm_node_t* first_argument = arguments->arguments.nodes[0];
85
+
86
+ if (first_argument->type == PM_CALL_NODE) {
87
+ pm_call_node_t* argument_call = (pm_call_node_t*) first_argument;
88
+
89
+ if (argument_call->block && argument_call->block->type == PM_BLOCK_NODE) {
90
+ return (pm_block_node_t*) argument_call->block;
91
+ }
92
+ }
93
+
94
+ return NULL;
95
+ }
96
+
65
97
  static char* extract_string_value(pm_node_t* node, hb_allocator_T* allocator) {
66
98
  if (!node) { return NULL; }
67
99
 
@@ -274,6 +306,15 @@ static token_T* extract_keyword_token(
274
306
  return token;
275
307
  }
276
308
 
309
+ typedef struct {
310
+ hb_array_T* block_body;
311
+ hb_array_T* block_arguments;
312
+ AST_ERB_RESCUE_NODE_T* rescue_clause;
313
+ AST_ERB_ELSE_NODE_T* else_clause;
314
+ AST_ERB_ENSURE_NODE_T* ensure_clause;
315
+ AST_ERB_END_NODE_T* end_node;
316
+ } render_block_fields_T;
317
+
277
318
  static AST_ERB_RENDER_NODE_T* create_render_node_from_call(
278
319
  AST_ERB_CONTENT_NODE_T* erb_node,
279
320
  pm_call_node_t* call_node,
@@ -281,9 +322,9 @@ static AST_ERB_RENDER_NODE_T* create_render_node_from_call(
281
322
  const char* source,
282
323
  size_t erb_content_offset,
283
324
  const uint8_t* erb_content_source,
325
+ render_block_fields_T* block_fields,
284
326
  hb_allocator_T* allocator
285
327
  ) {
286
- (void) parser;
287
328
 
288
329
  pm_arguments_node_t* arguments = call_node->arguments;
289
330
  pm_keyword_hash_node_t* keyword_hash = arguments ? find_keyword_hash(arguments) : NULL;
@@ -626,12 +667,68 @@ static AST_ERB_RENDER_NODE_T* create_render_node_from_call(
626
667
 
627
668
  herb_prism_node_T prism_node = erb_node->prism_node;
628
669
 
629
- AST_ERB_RENDER_NODE_T* render_node = ast_erb_render_node_init(
630
- erb_node->tag_opening,
631
- erb_node->content,
632
- erb_node->tag_closing,
633
- erb_node->analyzed_ruby,
634
- prism_node,
670
+ hb_array_T* render_block_body = block_fields ? block_fields->block_body : NULL;
671
+ hb_array_T* render_block_arguments = block_fields ? block_fields->block_arguments : NULL;
672
+
673
+ AST_ERB_RESCUE_NODE_T* render_rescue_clause = block_fields ? block_fields->rescue_clause : NULL;
674
+ AST_ERB_ELSE_NODE_T* render_else_clause = block_fields ? block_fields->else_clause : NULL;
675
+ AST_ERB_ENSURE_NODE_T* render_ensure_clause = block_fields ? block_fields->ensure_clause : NULL;
676
+ AST_ERB_END_NODE_T* render_end_node = block_fields ? block_fields->end_node : NULL;
677
+
678
+ if (!render_block_body) { render_block_body = hb_array_init(0, allocator); }
679
+ if (!render_block_arguments) { render_block_arguments = hb_array_init(0, allocator); }
680
+
681
+ if (!block_fields) {
682
+ pm_block_node_t* inline_block = find_block_on_render_call(call_node);
683
+
684
+ if (inline_block) {
685
+ const uint8_t* content_source = erb_content_source ? erb_content_source : (const uint8_t*) parser->start;
686
+
687
+ if (inline_block->parameters && inline_block->parameters->type == PM_BLOCK_PARAMETERS_NODE) {
688
+ pm_block_parameters_node_t* block_parameters = (pm_block_parameters_node_t*) inline_block->parameters;
689
+
690
+ render_block_arguments = extract_parameters_from_prism(
691
+ block_parameters->parameters,
692
+ parser,
693
+ source,
694
+ erb_content_offset,
695
+ content_source,
696
+ allocator
697
+ );
698
+ }
699
+
700
+ if (inline_block->body) {
701
+ size_t body_length = (size_t) (inline_block->body->location.end - inline_block->body->location.start);
702
+ char* body_source =
703
+ hb_allocator_strndup(allocator, (const char*) inline_block->body->location.start, body_length);
704
+
705
+ position_T body_start = { .line = 1, .column = 1 };
706
+ position_T body_end = { .line = 1, .column = 1 };
707
+
708
+ if (source && content_source) {
709
+ size_t start_offset = (size_t) (inline_block->body->location.start - content_source);
710
+ size_t end_offset = (size_t) (inline_block->body->location.end - content_source);
711
+ body_start = byte_offset_to_position(source, erb_content_offset + start_offset);
712
+ body_end = byte_offset_to_position(source, erb_content_offset + end_offset);
713
+ }
714
+
715
+ AST_RUBY_LITERAL_NODE_T* body_node = ast_ruby_literal_node_init(
716
+ hb_string_from_c_string(body_source),
717
+ body_start,
718
+ body_end,
719
+ hb_array_init(0, allocator),
720
+ allocator
721
+ );
722
+
723
+ render_block_body = hb_array_init(1, allocator);
724
+ hb_array_append(render_block_body, body_node);
725
+
726
+ hb_allocator_dealloc(allocator, body_source);
727
+ }
728
+ }
729
+ }
730
+
731
+ AST_RUBY_RENDER_KEYWORDS_NODE_T* keywords_node = ast_ruby_render_keywords_node_init(
635
732
  partial,
636
733
  template_path,
637
734
  layout,
@@ -652,6 +749,25 @@ static AST_ERB_RENDER_NODE_T* create_render_node_from_call(
652
749
  locals,
653
750
  erb_node->base.location.start,
654
751
  erb_node->base.location.end,
752
+ hb_array_init(0, allocator),
753
+ allocator
754
+ );
755
+
756
+ AST_ERB_RENDER_NODE_T* render_node = ast_erb_render_node_init(
757
+ erb_node->tag_opening,
758
+ erb_node->content,
759
+ erb_node->tag_closing,
760
+ erb_node->analyzed_ruby,
761
+ prism_node,
762
+ keywords_node,
763
+ render_block_body,
764
+ render_block_arguments,
765
+ render_rescue_clause,
766
+ render_else_clause,
767
+ render_ensure_clause,
768
+ render_end_node,
769
+ erb_node->base.location.start,
770
+ erb_node->base.location.end,
655
771
  errors,
656
772
  allocator
657
773
  );
@@ -682,43 +798,123 @@ static size_t calculate_byte_offset_from_pos(const char* source, position_T posi
682
798
  return offset;
683
799
  }
684
800
 
685
- static void transform_render_nodes_in_array(hb_array_T* array, analyze_ruby_context_T* context) {
686
- if (!array) { return; }
801
+ static bool is_erb_comment_tag(token_T* tag_opening) {
802
+ if (!tag_opening || hb_string_is_empty(tag_opening->value)) { return false; }
687
803
 
688
- for (size_t index = 0; index < hb_array_size(array); index++) {
689
- AST_NODE_T* child = hb_array_get(array, index);
690
- if (!child || child->type != AST_ERB_CONTENT_NODE) { continue; }
804
+ const char* opening_string = tag_opening->value.data;
805
+ return opening_string && strstr(opening_string, "#") != NULL;
806
+ }
691
807
 
692
- AST_ERB_CONTENT_NODE_T* erb_node = (AST_ERB_CONTENT_NODE_T*) child;
808
+ static AST_ERB_RENDER_NODE_T* try_transform_content_node(
809
+ AST_ERB_CONTENT_NODE_T* erb_node,
810
+ analyze_ruby_context_T* context
811
+ ) {
812
+ if (!erb_node->analyzed_ruby || !erb_node->analyzed_ruby->valid || !erb_node->analyzed_ruby->parsed) { return NULL; }
813
+ if (is_erb_comment_tag(erb_node->tag_opening)) { return NULL; }
693
814
 
694
- if (!erb_node->analyzed_ruby || !erb_node->analyzed_ruby->valid || !erb_node->analyzed_ruby->parsed) { continue; }
815
+ pm_call_node_t* render_call = find_render_call(erb_node->analyzed_ruby->root, &erb_node->analyzed_ruby->parser);
816
+ if (!render_call) { return NULL; }
695
817
 
696
- token_T* tag_opening = erb_node->tag_opening;
818
+ size_t erb_content_offset = 0;
697
819
 
698
- if (tag_opening && !hb_string_is_empty(tag_opening->value)) {
699
- const char* opening_string = tag_opening->value.data;
700
- if (opening_string && strstr(opening_string, "#") != NULL) { continue; }
701
- }
820
+ if (context->source && erb_node->content) {
821
+ erb_content_offset = calculate_byte_offset_from_pos(context->source, erb_node->content->location.start);
822
+ }
702
823
 
703
- pm_call_node_t* render_call = find_render_call(erb_node->analyzed_ruby->root, &erb_node->analyzed_ruby->parser);
704
- if (!render_call) { continue; }
824
+ const uint8_t* erb_content_source = (const uint8_t*) erb_node->analyzed_ruby->parser.start;
705
825
 
706
- size_t erb_content_offset = 0;
707
- if (context->source && erb_node->content) {
708
- erb_content_offset = calculate_byte_offset_from_pos(context->source, erb_node->content->location.start);
709
- }
826
+ return create_render_node_from_call(
827
+ erb_node,
828
+ render_call,
829
+ &erb_node->analyzed_ruby->parser,
830
+ context->source,
831
+ erb_content_offset,
832
+ erb_content_source,
833
+ NULL,
834
+ context->allocator
835
+ );
836
+ }
837
+
838
+ static AST_ERB_RENDER_NODE_T* try_transform_block_node(
839
+ AST_ERB_BLOCK_NODE_T* block_node,
840
+ analyze_ruby_context_T* context
841
+ ) {
842
+ if (!block_node->content || hb_string_is_empty(block_node->content->value)) { return NULL; }
710
843
 
711
- const uint8_t* erb_content_source = (const uint8_t*) erb_node->analyzed_ruby->parser.start;
844
+ const char* ruby_source = block_node->content->value.data;
845
+ size_t ruby_length = block_node->content->value.length;
712
846
 
713
- AST_ERB_RENDER_NODE_T* render_node = create_render_node_from_call(
714
- erb_node,
715
- render_call,
716
- &erb_node->analyzed_ruby->parser,
717
- context->source,
718
- erb_content_offset,
719
- erb_content_source,
720
- context->allocator
721
- );
847
+ pm_parser_t parser;
848
+ pm_parser_init(&parser, (const uint8_t*) ruby_source, ruby_length, NULL);
849
+ pm_node_t* root = pm_parse(&parser);
850
+
851
+ pm_call_node_t* render_call = find_render_call(root, &parser);
852
+
853
+ if (!render_call) {
854
+ pm_node_destroy(&parser, root);
855
+ pm_parser_free(&parser);
856
+ return NULL;
857
+ }
858
+
859
+ AST_ERB_CONTENT_NODE_T content_view = {
860
+ .base = block_node->base,
861
+ .tag_opening = block_node->tag_opening,
862
+ .content = block_node->content,
863
+ .tag_closing = block_node->tag_closing,
864
+ .analyzed_ruby = NULL,
865
+ .parsed = true,
866
+ .valid = true,
867
+ .prism_node = block_node->prism_node,
868
+ };
869
+
870
+ size_t erb_content_offset = 0;
871
+
872
+ if (context->source && block_node->content) {
873
+ erb_content_offset = calculate_byte_offset_from_pos(context->source, block_node->content->location.start);
874
+ }
875
+
876
+ const uint8_t* erb_content_source = (const uint8_t*) parser.start;
877
+
878
+ render_block_fields_T block_fields = {
879
+ .block_body = block_node->body,
880
+ .block_arguments = block_node->block_arguments,
881
+ .rescue_clause = block_node->rescue_clause,
882
+ .else_clause = block_node->else_clause,
883
+ .ensure_clause = block_node->ensure_clause,
884
+ .end_node = block_node->end_node,
885
+ };
886
+
887
+ AST_ERB_RENDER_NODE_T* render_node = create_render_node_from_call(
888
+ &content_view,
889
+ render_call,
890
+ &parser,
891
+ context->source,
892
+ erb_content_offset,
893
+ erb_content_source,
894
+ &block_fields,
895
+ context->allocator
896
+ );
897
+
898
+ pm_node_destroy(&parser, root);
899
+ pm_parser_free(&parser);
900
+
901
+ return render_node;
902
+ }
903
+
904
+ static void transform_render_nodes_in_array(hb_array_T* array, analyze_ruby_context_T* context) {
905
+ if (!array) { return; }
906
+
907
+ for (size_t index = 0; index < hb_array_size(array); index++) {
908
+ AST_NODE_T* child = hb_array_get(array, index);
909
+ if (!child) { continue; }
910
+
911
+ AST_ERB_RENDER_NODE_T* render_node = NULL;
912
+
913
+ if (child->type == AST_ERB_CONTENT_NODE) {
914
+ render_node = try_transform_content_node((AST_ERB_CONTENT_NODE_T*) child, context);
915
+ } else if (child->type == AST_ERB_BLOCK_NODE) {
916
+ render_node = try_transform_block_node((AST_ERB_BLOCK_NODE_T*) child, context);
917
+ }
722
918
 
723
919
  if (render_node) { hb_array_set(array, index, render_node); }
724
920
  }