herb 0.9.0-arm-linux-gnu → 0.9.1-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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/config.yml +156 -0
  3. data/ext/herb/error_helpers.c +168 -0
  4. data/ext/herb/extension.c +4 -0
  5. data/ext/herb/extension_helpers.c +1 -0
  6. data/ext/herb/nodes.c +110 -0
  7. data/lib/herb/3.0/herb.so +0 -0
  8. data/lib/herb/3.1/herb.so +0 -0
  9. data/lib/herb/3.2/herb.so +0 -0
  10. data/lib/herb/3.3/herb.so +0 -0
  11. data/lib/herb/3.4/herb.so +0 -0
  12. data/lib/herb/4.0/herb.so +0 -0
  13. data/lib/herb/ast/nodes.rb +393 -17
  14. data/lib/herb/engine.rb +55 -26
  15. data/lib/herb/errors.rb +245 -0
  16. data/lib/herb/parser_options.rb +6 -1
  17. data/lib/herb/prism_inspect.rb +5 -1
  18. data/lib/herb/version.rb +1 -1
  19. data/lib/herb/visitor.rb +10 -0
  20. data/sig/herb/ast/nodes.rbs +132 -0
  21. data/sig/herb/engine.rbs +4 -0
  22. data/sig/herb/errors.rbs +114 -0
  23. data/sig/herb/parser_options.rbs +4 -0
  24. data/sig/herb/visitor.rbs +6 -0
  25. data/sig/rubyvm.rbs +5 -0
  26. data/sig/serialized_ast_errors.rbs +28 -0
  27. data/sig/serialized_ast_nodes.rbs +31 -0
  28. data/src/analyze/action_view/attribute_extraction_helpers.c +14 -1
  29. data/src/analyze/action_view/content_tag.c +19 -11
  30. data/src/analyze/action_view/link_to.c +25 -1
  31. data/src/analyze/action_view/registry.c +23 -0
  32. data/src/analyze/action_view/tag.c +14 -8
  33. data/src/analyze/action_view/tag_helpers.c +78 -11
  34. data/src/analyze/analyze.c +3 -0
  35. data/src/analyze/prism_annotate.c +4 -2
  36. data/src/analyze/render_nodes.c +761 -0
  37. data/src/analyze/transform.c +7 -0
  38. data/src/ast_nodes.c +97 -0
  39. data/src/ast_pretty_print.c +74 -0
  40. data/src/errors.c +379 -0
  41. data/src/include/analyze/action_view/tag_helper_handler.h +2 -0
  42. data/src/include/analyze/render_nodes.h +11 -0
  43. data/src/include/ast_nodes.h +37 -0
  44. data/src/include/errors.h +58 -0
  45. data/src/include/parser.h +1 -0
  46. data/src/include/version.h +1 -1
  47. data/src/parser.c +1 -0
  48. data/src/parser_match_tags.c +20 -0
  49. data/src/visitor.c +20 -0
  50. data/templates/lib/herb/ast/nodes.rb.erb +8 -2
  51. data/templates/rust/src/ast/nodes.rs.erb +1 -1
  52. data/templates/rust/src/nodes.rs.erb +1 -1
  53. metadata +4 -1
@@ -268,6 +268,37 @@ module Herb
268
268
  end_node: Herb::AST::ERBEndNode,
269
269
  }
270
270
 
271
+ type serialized_ruby_render_local_node = serialized_node & {
272
+ name: Herb::Token,
273
+ value: Herb::AST::RubyLiteralNode,
274
+ }
275
+
276
+ type serialized_erb_render_node = serialized_node & {
277
+ tag_opening: Herb::Token,
278
+ content: Herb::Token,
279
+ tag_closing: Herb::Token,
280
+ analyzed_ruby: nil,
281
+ prism_node: String,
282
+ partial: Herb::Token,
283
+ template_path: Herb::Token,
284
+ layout: Herb::Token,
285
+ file: Herb::Token,
286
+ inline_template: Herb::Token,
287
+ body: Herb::Token,
288
+ plain: Herb::Token,
289
+ html: Herb::Token,
290
+ renderable: Herb::Token,
291
+ collection: Herb::Token,
292
+ object: Herb::Token,
293
+ as_name: Herb::Token,
294
+ spacer_template: Herb::Token,
295
+ formats: Herb::Token,
296
+ variants: Herb::Token,
297
+ handlers: Herb::Token,
298
+ content_type: Herb::Token,
299
+ locals: Array[Herb::AST::RubyRenderLocalNode],
300
+ }
301
+
271
302
  type serialized_erb_yield_node = serialized_node & {
272
303
  tag_opening: Herb::Token,
273
304
  content: Herb::Token,
@@ -265,7 +265,7 @@ bool has_html_attributes_in_call(pm_call_node_t* call_node) {
265
265
 
266
266
  pm_node_t* last_argument = arguments->arguments.nodes[arguments->arguments.size - 1];
267
267
 
268
- return last_argument && last_argument->type == PM_KEYWORD_HASH_NODE;
268
+ return last_argument && (last_argument->type == PM_KEYWORD_HASH_NODE || last_argument->type == PM_HASH_NODE);
269
269
  }
270
270
 
271
271
  hb_array_T* extract_html_attributes_from_call_node(
@@ -280,6 +280,19 @@ hb_array_T* extract_html_attributes_from_call_node(
280
280
  pm_arguments_node_t* arguments = call_node->arguments;
281
281
  pm_node_t* last_argument = arguments->arguments.nodes[arguments->arguments.size - 1];
282
282
 
283
+ if (last_argument->type == PM_HASH_NODE) {
284
+ pm_hash_node_t* hash_node = (pm_hash_node_t*) last_argument;
285
+ pm_keyword_hash_node_t synthetic = { .base = hash_node->base, .elements = hash_node->elements };
286
+
287
+ return extract_html_attributes_from_keyword_hash(
288
+ &synthetic,
289
+ source,
290
+ original_source,
291
+ erb_content_offset,
292
+ allocator
293
+ );
294
+ }
295
+
283
296
  return extract_html_attributes_from_keyword_hash(
284
297
  (pm_keyword_hash_node_t*) last_argument,
285
298
  source,
@@ -38,23 +38,31 @@ char* extract_content_tag_name(pm_call_node_t* call_node, pm_parser_t* parser, h
38
38
  char* extract_content_tag_content(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
39
39
  (void) parser;
40
40
 
41
- if (!call_node || !call_node->arguments) { return NULL; }
41
+ if (!call_node) { return NULL; }
42
42
 
43
- pm_arguments_node_t* arguments = call_node->arguments;
44
- if (arguments->arguments.size < 2) { return NULL; }
43
+ char* block_content = extract_inline_block_content(call_node, allocator);
44
+ if (block_content) { return block_content; }
45
45
 
46
- pm_node_t* second_argument = arguments->arguments.nodes[1];
46
+ if (call_node->arguments) {
47
+ pm_arguments_node_t* arguments = call_node->arguments;
47
48
 
48
- if (second_argument->type == PM_KEYWORD_HASH_NODE) { return NULL; }
49
+ if (arguments->arguments.size >= 2) {
50
+ pm_node_t* second_argument = arguments->arguments.nodes[1];
49
51
 
50
- if (second_argument->type == PM_STRING_NODE) {
51
- pm_string_node_t* string_node = (pm_string_node_t*) second_argument;
52
- size_t length = pm_string_length(&string_node->unescaped);
53
- return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
52
+ if (second_argument->type != PM_KEYWORD_HASH_NODE) {
53
+ if (second_argument->type == PM_STRING_NODE) {
54
+ pm_string_node_t* string_node = (pm_string_node_t*) second_argument;
55
+ size_t length = pm_string_length(&string_node->unescaped);
56
+ return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
57
+ }
58
+
59
+ size_t source_length = second_argument->location.end - second_argument->location.start;
60
+ return hb_allocator_strndup(allocator, (const char*) second_argument->location.start, source_length);
61
+ }
62
+ }
54
63
  }
55
64
 
56
- size_t source_length = second_argument->location.end - second_argument->location.start;
57
- return hb_allocator_strndup(allocator, (const char*) second_argument->location.start, source_length);
65
+ return NULL;
58
66
  }
59
67
 
60
68
  bool content_tag_supports_block(void) {
@@ -56,7 +56,12 @@ char* extract_link_to_tag_name(pm_call_node_t* call_node, pm_parser_t* parser, h
56
56
  char* extract_link_to_content(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
57
57
  (void) parser;
58
58
 
59
- if (!call_node || !call_node->arguments) { return NULL; }
59
+ if (!call_node) { return NULL; }
60
+
61
+ char* block_content = extract_inline_block_content(call_node, allocator);
62
+ if (block_content) { return block_content; }
63
+
64
+ if (!call_node->arguments) { return NULL; }
60
65
 
61
66
  pm_arguments_node_t* arguments = call_node->arguments;
62
67
  if (!arguments->arguments.size) { return NULL; }
@@ -96,6 +101,25 @@ char* extract_link_to_href(pm_call_node_t* call_node, pm_parser_t* parser, hb_al
96
101
  if (!call_node || !call_node->arguments) { return NULL; }
97
102
 
98
103
  pm_arguments_node_t* arguments = call_node->arguments;
104
+ bool has_inline_block = call_node->block && call_node->block->type == PM_BLOCK_NODE;
105
+
106
+ if (has_inline_block && arguments->arguments.size >= 1) {
107
+ pm_node_t* first_argument = arguments->arguments.nodes[0];
108
+
109
+ if (first_argument->type == PM_STRING_NODE) {
110
+ pm_string_node_t* string_node = (pm_string_node_t*) first_argument;
111
+ size_t length = pm_string_length(&string_node->unescaped);
112
+ return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
113
+ }
114
+
115
+ size_t source_length = first_argument->location.end - first_argument->location.start;
116
+
117
+ if (is_route_helper_node(first_argument, parser)) {
118
+ return hb_allocator_strndup(allocator, (const char*) first_argument->location.start, source_length);
119
+ }
120
+
121
+ return wrap_in_url_for((const char*) first_argument->location.start, source_length, allocator);
122
+ }
99
123
 
100
124
  // Format: "url_for(<expression>)"
101
125
  if (arguments->arguments.size == 1) {
@@ -58,3 +58,26 @@ tag_helper_handler_T* get_tag_helper_handlers(void) {
58
58
  size_t get_tag_helper_handlers_count(void) {
59
59
  return handlers_count;
60
60
  }
61
+
62
+ char* extract_inline_block_content(pm_call_node_t* call_node, hb_allocator_T* allocator) {
63
+ if (!call_node || !call_node->block || call_node->block->type != PM_BLOCK_NODE) { return NULL; }
64
+
65
+ pm_block_node_t* block_node = (pm_block_node_t*) call_node->block;
66
+
67
+ if (!block_node->body || block_node->body->type != PM_STATEMENTS_NODE) { return NULL; }
68
+
69
+ pm_statements_node_t* statements = (pm_statements_node_t*) block_node->body;
70
+
71
+ if (statements->body.size != 1) { return NULL; }
72
+
73
+ pm_node_t* statement = statements->body.nodes[0];
74
+
75
+ if (statement->type == PM_STRING_NODE) {
76
+ pm_string_node_t* string_node = (pm_string_node_t*) statement;
77
+ size_t length = pm_string_length(&string_node->unescaped);
78
+ return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
79
+ }
80
+
81
+ size_t source_length = statement->location.end - statement->location.start;
82
+ return hb_allocator_strndup(allocator, (const char*) statement->location.start, source_length);
83
+ }
@@ -36,17 +36,23 @@ char* extract_tag_dot_name(pm_call_node_t* call_node, pm_parser_t* parser, hb_al
36
36
  char* extract_tag_dot_content(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
37
37
  (void) parser;
38
38
 
39
- if (!call_node || !call_node->arguments) { return NULL; }
39
+ if (!call_node) { return NULL; }
40
40
 
41
- pm_arguments_node_t* arguments = call_node->arguments;
42
- if (!arguments->arguments.size) { return NULL; }
41
+ char* block_content = extract_inline_block_content(call_node, allocator);
42
+ if (block_content) { return block_content; }
43
43
 
44
- pm_node_t* first_argument = arguments->arguments.nodes[0];
44
+ if (call_node->arguments) {
45
+ pm_arguments_node_t* arguments = call_node->arguments;
45
46
 
46
- if (first_argument->type == PM_STRING_NODE) {
47
- pm_string_node_t* string_node = (pm_string_node_t*) first_argument;
48
- size_t length = pm_string_length(&string_node->unescaped);
49
- return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
47
+ if (arguments->arguments.size) {
48
+ pm_node_t* first_argument = arguments->arguments.nodes[0];
49
+
50
+ if (first_argument->type == PM_STRING_NODE) {
51
+ pm_string_node_t* string_node = (pm_string_node_t*) first_argument;
52
+ size_t length = pm_string_length(&string_node->unescaped);
53
+ return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
54
+ }
55
+ }
50
56
  }
51
57
 
52
58
  return NULL;
@@ -262,13 +262,29 @@ static AST_NODE_T* transform_tag_helper_with_attributes(
262
262
  if (parse_context->info->call_node && handler->extract_content) {
263
263
  helper_content = handler->extract_content(parse_context->info->call_node, &parse_context->parser, allocator);
264
264
 
265
- if (helper_content && parse_context->info->call_node->arguments) {
266
- if (strcmp(handler->name, "content_tag") == 0 && parse_context->info->call_node->arguments->arguments.size >= 2) {
267
- content_is_ruby_expression =
268
- (parse_context->info->call_node->arguments->arguments.nodes[1]->type != PM_STRING_NODE);
269
- } else if (parse_context->info->call_node->arguments->arguments.size >= 1) {
270
- content_is_ruby_expression =
271
- (parse_context->info->call_node->arguments->arguments.nodes[0]->type != PM_STRING_NODE);
265
+ if (helper_content) {
266
+ pm_call_node_t* call = parse_context->info->call_node;
267
+
268
+ if (call->arguments) {
269
+ if (strcmp(handler->name, "content_tag") == 0 && call->arguments->arguments.size >= 2
270
+ && call->arguments->arguments.nodes[1]->type != PM_KEYWORD_HASH_NODE) {
271
+ content_is_ruby_expression = (call->arguments->arguments.nodes[1]->type != PM_STRING_NODE);
272
+ } else if (strcmp(handler->name, "content_tag") != 0 && call->arguments->arguments.size >= 1
273
+ && call->arguments->arguments.nodes[0]->type != PM_KEYWORD_HASH_NODE) {
274
+ content_is_ruby_expression = (call->arguments->arguments.nodes[0]->type != PM_STRING_NODE);
275
+ }
276
+ }
277
+
278
+ if (!content_is_ruby_expression && call->block && call->block->type == PM_BLOCK_NODE) {
279
+ pm_block_node_t* block_node = (pm_block_node_t*) call->block;
280
+
281
+ if (block_node->body && block_node->body->type == PM_STATEMENTS_NODE) {
282
+ pm_statements_node_t* statements = (pm_statements_node_t*) block_node->body;
283
+
284
+ if (statements->body.size == 1) {
285
+ content_is_ruby_expression = (statements->body.nodes[0]->type != PM_STRING_NODE);
286
+ }
287
+ }
272
288
  }
273
289
  }
274
290
  }
@@ -492,8 +508,12 @@ static AST_NODE_T* transform_link_to_helper(
492
508
 
493
509
  hb_array_T* attributes = NULL;
494
510
  pm_arguments_node_t* link_arguments = info->call_node->arguments;
495
- bool keyword_hash_is_url = link_arguments && link_arguments->arguments.size == 2
496
- && link_arguments->arguments.nodes[1]->type == PM_KEYWORD_HASH_NODE;
511
+ bool has_inline_block = info->call_node->block && info->call_node->block->type == PM_BLOCK_NODE;
512
+
513
+ bool second_arg_is_hash = link_arguments && link_arguments->arguments.size == 2
514
+ && (link_arguments->arguments.nodes[1]->type == PM_KEYWORD_HASH_NODE
515
+ || link_arguments->arguments.nodes[1]->type == PM_HASH_NODE);
516
+ bool keyword_hash_is_url = !has_inline_block && second_arg_is_hash;
497
517
 
498
518
  if (!keyword_hash_is_url) {
499
519
  attributes = extract_html_attributes_from_call_node(
@@ -507,6 +527,38 @@ static AST_NODE_T* transform_link_to_helper(
507
527
 
508
528
  if (!attributes) { attributes = hb_array_init(4, allocator); }
509
529
 
530
+ if (has_inline_block && link_arguments && link_arguments->arguments.size >= 2) {
531
+ pm_node_t* second_arg = link_arguments->arguments.nodes[1];
532
+
533
+ if (second_arg->type != PM_KEYWORD_HASH_NODE && second_arg->type != PM_HASH_NODE
534
+ && second_arg->type != PM_STRING_NODE && second_arg->type != PM_SYMBOL_NODE) {
535
+ size_t source_length = second_arg->location.end - second_arg->location.start;
536
+ char* content = hb_allocator_strndup(allocator, (const char*) second_arg->location.start, source_length);
537
+
538
+ if (content) {
539
+ position_T position = prism_location_to_position_with_offset(
540
+ &second_arg->location,
541
+ parse_context->original_source,
542
+ parse_context->erb_content_offset,
543
+ parse_context->prism_source
544
+ );
545
+
546
+ AST_RUBY_HTML_ATTRIBUTES_SPLAT_NODE_T* splat_node = ast_ruby_html_attributes_splat_node_init(
547
+ hb_string_from_c_string(content),
548
+ HB_STRING_EMPTY,
549
+ position,
550
+ position,
551
+ hb_array_init(0, allocator),
552
+ allocator
553
+ );
554
+
555
+ if (splat_node) { hb_array_append(attributes, (AST_NODE_T*) splat_node); }
556
+
557
+ hb_allocator_dealloc(allocator, content);
558
+ }
559
+ }
560
+ }
561
+
510
562
  // `method:` implies `rel="nofollow"`
511
563
  bool has_data_method = false;
512
564
  hb_string_T data_method_string = hb_string("data-method");
@@ -549,7 +601,12 @@ static AST_NODE_T* transform_link_to_helper(
549
601
  pm_arguments_node_t* arguments = info->call_node->arguments;
550
602
  pm_node_t* href_argument = NULL;
551
603
 
552
- if (arguments->arguments.size >= 2) {
604
+ if (has_inline_block) {
605
+ if (arguments->arguments.size >= 1) {
606
+ href_argument = arguments->arguments.nodes[0];
607
+ href_is_ruby_expression = (href_argument->type != PM_STRING_NODE);
608
+ }
609
+ } else if (arguments->arguments.size >= 2) {
553
610
  href_argument = arguments->arguments.nodes[1];
554
611
  href_is_ruby_expression = (href_argument->type != PM_STRING_NODE);
555
612
  } else if (arguments->arguments.size == 1) {
@@ -602,7 +659,17 @@ static AST_NODE_T* transform_link_to_helper(
602
659
  if (info->content) {
603
660
  bool content_is_ruby_expression = false;
604
661
 
605
- if (info->call_node && info->call_node->arguments && info->call_node->arguments->arguments.size >= 1) {
662
+ if (has_inline_block && info->call_node->block && info->call_node->block->type == PM_BLOCK_NODE) {
663
+ pm_block_node_t* block_node = (pm_block_node_t*) info->call_node->block;
664
+
665
+ if (block_node->body && block_node->body->type == PM_STATEMENTS_NODE) {
666
+ pm_statements_node_t* statements = (pm_statements_node_t*) block_node->body;
667
+
668
+ if (statements->body.size == 1) {
669
+ content_is_ruby_expression = (statements->body.nodes[0]->type != PM_STRING_NODE);
670
+ }
671
+ }
672
+ } else if (info->call_node && info->call_node->arguments && info->call_node->arguments->arguments.size >= 1) {
606
673
  pm_node_t* first_argument = info->call_node->arguments->arguments.nodes[0];
607
674
  content_is_ruby_expression = (first_argument->type != PM_STRING_NODE);
608
675
  }
@@ -8,6 +8,7 @@
8
8
  #include "../include/analyze/control_type.h"
9
9
  #include "../include/analyze/helpers.h"
10
10
  #include "../include/analyze/invalid_structures.h"
11
+ #include "../include/analyze/render_nodes.h"
11
12
  #include "../include/ast_node.h"
12
13
  #include "../include/ast_nodes.h"
13
14
  #include "../include/errors.h"
@@ -859,6 +860,8 @@ void herb_analyze_parse_tree(
859
860
 
860
861
  herb_visit_node((AST_NODE_T*) document, transform_erb_nodes, &context);
861
862
 
863
+ if (options && options->render_nodes) { herb_visit_node((AST_NODE_T*) document, transform_render_nodes, &context); }
864
+
862
865
  if (options && options->action_view_helpers) {
863
866
  herb_visit_node((AST_NODE_T*) document, transform_tag_helper_nodes, &context);
864
867
  }
@@ -168,6 +168,7 @@ static void collect_prism_nodes(pm_node_t* node, hb_narray_T* list) {
168
168
  static token_T* get_content_token(const AST_NODE_T* node) {
169
169
  switch (node->type) {
170
170
  case AST_ERB_CONTENT_NODE: return ((AST_ERB_CONTENT_NODE_T*) node)->content;
171
+ case AST_ERB_RENDER_NODE: return ((AST_ERB_RENDER_NODE_T*) node)->content;
171
172
  case AST_ERB_IF_NODE: return ((AST_ERB_IF_NODE_T*) node)->content;
172
173
  case AST_ERB_BLOCK_NODE: return ((AST_ERB_BLOCK_NODE_T*) node)->content;
173
174
  case AST_ERB_CASE_NODE: return ((AST_ERB_CASE_NODE_T*) node)->content;
@@ -184,6 +185,7 @@ static token_T* get_content_token(const AST_NODE_T* node) {
184
185
  static void set_prism_node(AST_NODE_T* node, herb_prism_node_T prism_ref) {
185
186
  switch (node->type) {
186
187
  case AST_ERB_CONTENT_NODE: ((AST_ERB_CONTENT_NODE_T*) node)->prism_node = prism_ref; break;
188
+ case AST_ERB_RENDER_NODE: ((AST_ERB_RENDER_NODE_T*) node)->prism_node = prism_ref; break;
187
189
  case AST_ERB_IF_NODE: ((AST_ERB_IF_NODE_T*) node)->prism_node = prism_ref; break;
188
190
  case AST_ERB_BLOCK_NODE: ((AST_ERB_BLOCK_NODE_T*) node)->prism_node = prism_ref; break;
189
191
  case AST_ERB_CASE_NODE: ((AST_ERB_CASE_NODE_T*) node)->prism_node = prism_ref; break;
@@ -232,7 +234,7 @@ static bool annotate_visitor(const AST_NODE_T* node, void* data) {
232
234
  pm_parser_t* parser;
233
235
  hb_narray_T* node_list;
234
236
 
235
- if (node->type == AST_ERB_CONTENT_NODE || context->prism_nodes_deep) {
237
+ if (node->type == AST_ERB_CONTENT_NODE || node->type == AST_ERB_RENDER_NODE || context->prism_nodes_deep) {
236
238
  parser = context->parser;
237
239
  node_list = context->node_list;
238
240
  } else {
@@ -248,7 +250,7 @@ static bool annotate_visitor(const AST_NODE_T* node, void* data) {
248
250
  }
249
251
 
250
252
  static bool collect_content_ranges_visitor(const AST_NODE_T* node, void* data) {
251
- if (node->type != AST_ERB_CONTENT_NODE) { return true; }
253
+ if (node->type != AST_ERB_CONTENT_NODE && node->type != AST_ERB_RENDER_NODE) { return true; }
252
254
 
253
255
  hb_narray_T* ranges = (hb_narray_T*) data;
254
256
  token_T* content = get_content_token(node);