herb 0.9.2 → 0.9.3

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 (162) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -0
  3. data/config.yml +125 -0
  4. data/ext/herb/error_helpers.c +172 -2
  5. data/ext/herb/extconf.rb +6 -0
  6. data/ext/herb/extension.c +16 -2
  7. data/ext/herb/extension_helpers.c +6 -5
  8. data/ext/herb/extension_helpers.h +4 -4
  9. data/ext/herb/nodes.c +89 -3
  10. data/lib/herb/ast/erb_content_node.rb +32 -0
  11. data/lib/herb/ast/nodes.rb +244 -3
  12. data/lib/herb/cli.rb +12 -2
  13. data/lib/herb/engine/compiler.rb +122 -59
  14. data/lib/herb/engine/validators/security_validator.rb +40 -0
  15. data/lib/herb/errors.rb +268 -0
  16. data/lib/herb/parser_options.rb +7 -2
  17. data/lib/herb/version.rb +1 -1
  18. data/lib/herb/visitor.rb +82 -0
  19. data/lib/herb.rb +1 -0
  20. data/sig/herb/ast/erb_content_node.rbs +13 -0
  21. data/sig/herb/ast/nodes.rbs +98 -2
  22. data/sig/herb/engine/compiler.rbs +15 -2
  23. data/sig/herb/engine/validators/security_validator.rbs +4 -0
  24. data/sig/herb/errors.rbs +122 -0
  25. data/sig/herb/parser_options.rbs +6 -2
  26. data/sig/herb/visitor.rbs +12 -0
  27. data/sig/serialized_ast_errors.rbs +29 -0
  28. data/sig/serialized_ast_nodes.rbs +19 -0
  29. data/src/analyze/action_view/attribute_extraction_helpers.c +420 -91
  30. data/src/analyze/action_view/image_tag.c +87 -0
  31. data/src/analyze/action_view/javascript_include_tag.c +22 -12
  32. data/src/analyze/action_view/registry.c +6 -3
  33. data/src/analyze/action_view/tag.c +19 -2
  34. data/src/analyze/action_view/tag_helper_node_builders.c +105 -36
  35. data/src/analyze/action_view/tag_helpers.c +792 -44
  36. data/src/analyze/analyze.c +165 -10
  37. data/src/analyze/{helpers.c → analyze_helpers.c} +1 -1
  38. data/src/analyze/analyzed_ruby.c +1 -1
  39. data/src/analyze/builders.c +11 -8
  40. data/src/analyze/conditional_elements.c +6 -7
  41. data/src/analyze/conditional_open_tags.c +6 -7
  42. data/src/analyze/control_type.c +4 -2
  43. data/src/analyze/invalid_structures.c +5 -5
  44. data/src/analyze/missing_end.c +2 -2
  45. data/src/analyze/parse_errors.c +5 -5
  46. data/src/analyze/prism_annotate.c +7 -7
  47. data/src/analyze/render_nodes.c +6 -26
  48. data/src/analyze/strict_locals.c +637 -0
  49. data/src/analyze/transform.c +7 -0
  50. data/src/{ast_node.c → ast/ast_node.c} +8 -8
  51. data/src/{ast_nodes.c → ast/ast_nodes.c} +82 -11
  52. data/src/{ast_pretty_print.c → ast/ast_pretty_print.c} +113 -9
  53. data/src/{pretty_print.c → ast/pretty_print.c} +9 -9
  54. data/src/errors.c +398 -8
  55. data/src/extract.c +5 -5
  56. data/src/herb.c +15 -5
  57. data/src/include/analyze/action_view/attribute_extraction_helpers.h +3 -1
  58. data/src/include/analyze/action_view/tag_helper_handler.h +3 -3
  59. data/src/include/analyze/action_view/tag_helper_node_builders.h +34 -5
  60. data/src/include/analyze/action_view/tag_helpers.h +4 -3
  61. data/src/include/analyze/analyze.h +6 -4
  62. data/src/include/analyze/analyzed_ruby.h +2 -2
  63. data/src/include/analyze/builders.h +4 -4
  64. data/src/include/analyze/conditional_elements.h +2 -2
  65. data/src/include/analyze/conditional_open_tags.h +2 -2
  66. data/src/include/analyze/control_type.h +1 -1
  67. data/src/include/analyze/helpers.h +2 -2
  68. data/src/include/analyze/invalid_structures.h +1 -1
  69. data/src/include/analyze/prism_annotate.h +2 -2
  70. data/src/include/analyze/render_nodes.h +1 -1
  71. data/src/include/analyze/strict_locals.h +11 -0
  72. data/src/include/{ast_node.h → ast/ast_node.h} +4 -4
  73. data/src/include/{ast_nodes.h → ast/ast_nodes.h} +38 -14
  74. data/src/include/{ast_pretty_print.h → ast/ast_pretty_print.h} +3 -3
  75. data/src/include/{pretty_print.h → ast/pretty_print.h} +4 -4
  76. data/src/include/errors.h +65 -7
  77. data/src/include/extract.h +2 -2
  78. data/src/include/herb.h +5 -5
  79. data/src/include/{lex_helpers.h → lexer/lex_helpers.h} +5 -5
  80. data/src/include/{lexer.h → lexer/lexer.h} +1 -1
  81. data/src/include/{lexer_peek_helpers.h → lexer/lexer_peek_helpers.h} +2 -2
  82. data/src/include/{lexer_struct.h → lexer/lexer_struct.h} +2 -2
  83. data/src/include/{token.h → lexer/token.h} +3 -3
  84. data/src/include/{token_matchers.h → lexer/token_matchers.h} +1 -1
  85. data/src/include/{token_struct.h → lexer/token_struct.h} +3 -3
  86. data/src/include/{util → lib}/hb_foreach.h +1 -1
  87. data/src/include/{util → lib}/hb_string.h +5 -1
  88. data/src/include/{location.h → location/location.h} +1 -1
  89. data/src/include/parser/dot_notation.h +12 -0
  90. data/src/include/{parser.h → parser/parser.h} +11 -4
  91. data/src/include/{parser_helpers.h → parser/parser_helpers.h} +6 -6
  92. data/src/include/{prism_context.h → prism/prism_context.h} +2 -2
  93. data/src/include/{prism_helpers.h → prism/prism_helpers.h} +6 -6
  94. data/src/include/{html_util.h → util/html_util.h} +1 -1
  95. data/src/include/util/ruby_util.h +9 -0
  96. data/src/include/{utf8.h → util/utf8.h} +1 -1
  97. data/src/include/{util.h → util/util.h} +1 -1
  98. data/src/include/version.h +1 -1
  99. data/src/include/visitor.h +3 -3
  100. data/src/{lexer_peek_helpers.c → lexer/lexer_peek_helpers.c} +3 -3
  101. data/src/{token.c → lexer/token.c} +8 -8
  102. data/src/{token_matchers.c → lexer/token_matchers.c} +2 -2
  103. data/src/lexer.c +6 -6
  104. data/src/{util → lib}/hb_allocator.c +2 -2
  105. data/src/{util → lib}/hb_arena.c +1 -1
  106. data/src/{util → lib}/hb_arena_debug.c +2 -2
  107. data/src/{util → lib}/hb_array.c +2 -2
  108. data/src/{util → lib}/hb_buffer.c +2 -2
  109. data/src/{util → lib}/hb_narray.c +1 -1
  110. data/src/{util → lib}/hb_string.c +2 -2
  111. data/src/{location.c → location/location.c} +2 -2
  112. data/src/{position.c → location/position.c} +2 -2
  113. data/src/{range.c → location/range.c} +1 -1
  114. data/src/main.c +11 -11
  115. data/src/parser/dot_notation.c +100 -0
  116. data/src/{parser_match_tags.c → parser/match_tags.c} +34 -5
  117. data/src/{parser_helpers.c → parser/parser_helpers.c} +10 -10
  118. data/src/parser.c +68 -32
  119. data/src/{prism_helpers.c → prism/prism_helpers.c} +7 -7
  120. data/src/{ruby_parser.c → prism/ruby_parser.c} +1 -1
  121. data/src/{html_util.c → util/html_util.c} +4 -4
  122. data/src/{io.c → util/io.c} +3 -3
  123. data/src/util/ruby_util.c +42 -0
  124. data/src/{utf8.c → util/utf8.c} +2 -2
  125. data/src/{util.c → util/util.c} +4 -4
  126. data/src/visitor.c +35 -3
  127. data/templates/ext/herb/error_helpers.c.erb +2 -2
  128. data/templates/ext/herb/nodes.c.erb +1 -1
  129. data/templates/java/error_helpers.c.erb +1 -1
  130. data/templates/java/error_helpers.h.erb +2 -2
  131. data/templates/java/nodes.c.erb +4 -4
  132. data/templates/java/nodes.h.erb +1 -1
  133. data/templates/javascript/packages/node/extension/error_helpers.cpp.erb +4 -4
  134. data/templates/javascript/packages/node/extension/nodes.cpp.erb +4 -4
  135. data/templates/lib/herb/visitor.rb.erb +14 -0
  136. data/templates/src/analyze/missing_end.c.erb +2 -2
  137. data/templates/src/{ast_nodes.c.erb → ast/ast_nodes.c.erb} +9 -9
  138. data/templates/src/{ast_pretty_print.c.erb → ast/ast_pretty_print.c.erb} +8 -8
  139. data/templates/src/errors.c.erb +8 -8
  140. data/templates/src/include/{ast_nodes.h.erb → ast/ast_nodes.h.erb} +11 -12
  141. data/templates/src/include/{ast_pretty_print.h.erb → ast/ast_pretty_print.h.erb} +2 -2
  142. data/templates/src/include/errors.h.erb +7 -7
  143. data/templates/src/{parser_match_tags.c.erb → parser/match_tags.c.erb} +4 -4
  144. data/templates/src/visitor.c.erb +3 -3
  145. data/templates/wasm/error_helpers.cpp.erb +4 -4
  146. data/templates/wasm/nodes.cpp.erb +5 -5
  147. metadata +76 -68
  148. data/src/include/element_source.h +0 -10
  149. /data/src/include/{util → lib}/hb_allocator.h +0 -0
  150. /data/src/include/{util → lib}/hb_arena.h +0 -0
  151. /data/src/include/{util → lib}/hb_arena_debug.h +0 -0
  152. /data/src/include/{util → lib}/hb_array.h +0 -0
  153. /data/src/include/{util → lib}/hb_buffer.h +0 -0
  154. /data/src/include/{util → lib}/hb_narray.h +0 -0
  155. /data/src/include/{util → lib}/string.h +0 -0
  156. /data/src/include/{position.h → location/position.h} +0 -0
  157. /data/src/include/{range.h → location/range.h} +0 -0
  158. /data/src/include/{herb_prism_node.h → prism/herb_prism_node.h} +0 -0
  159. /data/src/include/{prism_serialized.h → prism/prism_serialized.h} +0 -0
  160. /data/src/include/{ruby_parser.h → prism/ruby_parser.h} +0 -0
  161. /data/src/include/{io.h → util/io.h} +0 -0
  162. /data/templates/src/include/{util → lib}/hb_foreach.h.erb +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3a1a61c49f642abaffb189b7ff947009cc5c2e808d4eaa5e8103f0086b6b6b78
4
- data.tar.gz: d26cdc1d962413d92cc6a2800f4c183777e690aa95c7ab918b5ef4f6591e9278
3
+ metadata.gz: a490ad868e9fa08be317c7343a0553a8bf07234decbb95046d235d64256ed743
4
+ data.tar.gz: 2f1312897b63fe33263bf35f023b6d2377bfb9e806d5f640837d9de57b2878bb
5
5
  SHA512:
6
- metadata.gz: df024e2b48dadab5af063c54e614948dc46efed4aed24f813476ca491164db7b863692a6617f580bf2e9523701a2f76a8c392935a6fb40cb4b269eece5fb1737
7
- data.tar.gz: cfb04158d09179dc2a5d08b96a3300b1748ba1ec66afb71c5004e1eaabf1c4cfcfc8b1296d0fb613152eafd0927c400df43622463d5850b341ddfcb6aa03fae4
6
+ metadata.gz: f820a5463221a38d26c92a3a388f137bb2295125641dd7de7102290c85036adfa5c0bdafd3331e049fff6fc218a8c53b094fd5037277276c67121a64e99b7535
7
+ data.tar.gz: 6bee2c356c1e18c5670ce9ffcd8193100f0be4dc8d2aba74faaadc6409fe0d534390c34796dcd5aa8c1bfa4c55209cf07b856898a6a66cf654e871d14a869003
data/README.md CHANGED
@@ -56,6 +56,7 @@ The Herb ecosystem offers multiple tools that integrate seamlessly into editors,
56
56
  | [Herb Parser](https://herb-tools.dev/projects/parser) | Fast, portable, HTML-aware ERB parser written in C. |
57
57
  | [Herb Linter](https://herb-tools.dev/projects/linter) | Static analysis to enforce best practices and identify common mistakes. |
58
58
  | [Herb Formatter](https://herb-tools.dev/projects/formatter) | Automatic, consistent formatting for HTML+ERB files. *(experimental)* |
59
+ | [Herb Language Service](https://herb-tools.dev/projects/language-service) | HTML+ERB language service with ActionView tag helper support. |
59
60
  | [Herb Language Server](https://herb-tools.dev/projects/language-server) | Rich editor integration for VS Code, Zed, Neovim, and more. |
60
61
  | [Herb Engine](https://herb-tools.dev/projects/engine) | HTML-aware ERB rendering engine, API-compatible with Erubi. |
61
62
  | [Herb Dev Tools](https://herb-tools.dev/projects/dev-tools) | In-browser dev tools for inspecting and debugging templates, shipped with ReActionView. |
@@ -157,6 +158,7 @@ While Herb brings a fresh approach to HTML+ERB tooling, it builds upon and learn
157
158
  - [**erb_lint**](https://github.com/Shopify/erb_lint)
158
159
  - [**erb-formatter**](https://github.com/nebulab/erb-formatter)
159
160
  - [**erb-formatter-vscode**](https://github.com/nebulab/erb-formatter-vscode)
161
+ - [**erblint-github**](https://github.com/github/erblint-github)
160
162
  - [**deface**](https://github.com/spree/deface)
161
163
  - [**html_press**](https://github.com/stereobooster/html_press)
162
164
  - [**htmlbeautifier**](https://github.com/threedaymonk/htmlbeautifier)
data/config.yml CHANGED
@@ -410,6 +410,82 @@ errors:
410
410
  - name: layout
411
411
  type: string
412
412
 
413
+ - name: StrictLocalsPositionalArgumentError
414
+ message:
415
+ template: "Strict locals only support keyword arguments. Positional argument `%s` is not allowed. Use keyword argument format: `%s:`."
416
+ arguments:
417
+ - name
418
+ - name
419
+
420
+ fields:
421
+ - name: name
422
+ type: string
423
+
424
+ - name: StrictLocalsBlockArgumentError
425
+ message:
426
+ template: "Strict locals only support keyword arguments. Block argument `&%s` is not allowed."
427
+ arguments:
428
+ - name
429
+
430
+ fields:
431
+ - name: name
432
+ type: string
433
+
434
+ - name: StrictLocalsSplatArgumentError
435
+ message:
436
+ template: "Strict locals only support keyword arguments. Splat argument `*%s` is not allowed."
437
+ arguments:
438
+ - name
439
+
440
+ fields:
441
+ - name: name
442
+ type: string
443
+
444
+ - name: StrictLocalsMissingParenthesisError
445
+ message:
446
+ template: "Strict locals declaration requires parentheses. Expected `locals: (...)` but got `locals: %s`."
447
+ arguments:
448
+ - rest
449
+
450
+ fields:
451
+ - name: rest
452
+ type: string
453
+
454
+ - name: StrictLocalsDuplicateDeclarationError
455
+ message:
456
+ template: "Duplicate strict locals declaration. Only the first `<%# locals: (...) %>` declaration is used by Rails."
457
+ arguments: []
458
+
459
+ fields: []
460
+
461
+ - name: VoidElementContentError
462
+ message:
463
+ template: "Void element `%s` cannot have content. `%s` does not accept %s for content."
464
+ arguments:
465
+ - tag_name->value
466
+ - helper_name
467
+ - content_type
468
+
469
+ fields:
470
+ - name: tag_name
471
+ type: token
472
+
473
+ - name: helper_name
474
+ type: string
475
+
476
+ - name: content_type
477
+ type: string
478
+
479
+ - name: DotNotationCasingError
480
+ message:
481
+ template: "Dot-notation component tags require the first segment to start with an uppercase letter. `%s` does not start with an uppercase letter."
482
+ arguments:
483
+ - segment->value
484
+
485
+ fields:
486
+ - name: segment
487
+ type: token
488
+
413
489
  warnings:
414
490
  fields: []
415
491
  types: []
@@ -810,6 +886,18 @@ nodes:
810
886
  type: array
811
887
  kind: Node
812
888
 
889
+ - name: rescue_clause
890
+ type: node
891
+ kind: ERBRescueNode
892
+
893
+ - name: else_clause
894
+ type: node
895
+ kind: ERBElseNode
896
+
897
+ - name: ensure_clause
898
+ type: node
899
+ kind: ERBEnsureNode
900
+
813
901
  - name: end_node
814
902
  type: node
815
903
  kind: ERBEndNode
@@ -1169,6 +1257,43 @@ nodes:
1169
1257
  kind:
1170
1258
  - RubyRenderLocalNode
1171
1259
 
1260
+ - name: RubyStrictLocalNode
1261
+ fields:
1262
+ - name: name
1263
+ type: token
1264
+
1265
+ - name: default_value
1266
+ type: node
1267
+ kind: RubyLiteralNode
1268
+
1269
+ - name: required
1270
+ type: boolean
1271
+
1272
+ - name: double_splat
1273
+ type: boolean
1274
+
1275
+ - name: ERBStrictLocalsNode
1276
+ fields:
1277
+ - name: tag_opening
1278
+ type: token
1279
+
1280
+ - name: content
1281
+ type: token
1282
+
1283
+ - name: tag_closing
1284
+ type: token
1285
+
1286
+ - name: analyzed_ruby
1287
+ type: analyzed_ruby
1288
+
1289
+ - name: prism_node
1290
+ type: prism_node
1291
+
1292
+ - name: locals
1293
+ type: array
1294
+ kind:
1295
+ - RubyStrictLocalNode
1296
+
1172
1297
  - name: ERBYieldNode
1173
1298
  fields:
1174
1299
  - name: tag_opening
@@ -9,8 +9,8 @@
9
9
 
10
10
  #include "../../src/include/errors.h"
11
11
  #include "../../src/include/herb.h"
12
- #include "../../src/include/token.h"
13
- #include "../../src/include/util/hb_string.h"
12
+ #include "../../src/include/lexer/token.h"
13
+ #include "../../src/include/lib/hb_string.h"
14
14
 
15
15
  VALUE rb_error_from_c_struct(ERROR_T* error);
16
16
 
@@ -46,6 +46,13 @@ static VALUE cRenderConflictingPartialError;
46
46
  static VALUE cRenderInvalidAsOptionError;
47
47
  static VALUE cRenderObjectAndCollectionError;
48
48
  static VALUE cRenderLayoutWithoutBlockError;
49
+ static VALUE cStrictLocalsPositionalArgumentError;
50
+ static VALUE cStrictLocalsBlockArgumentError;
51
+ static VALUE cStrictLocalsSplatArgumentError;
52
+ static VALUE cStrictLocalsMissingParenthesisError;
53
+ static VALUE cStrictLocalsDuplicateDeclarationError;
54
+ static VALUE cVoidElementContentError;
55
+ static VALUE cDotNotationCasingError;
49
56
 
50
57
  void rb_init_error_classes(void) {
51
58
  mErrors = rb_define_module_under(mHerb, "Errors");
@@ -80,6 +87,13 @@ void rb_init_error_classes(void) {
80
87
  cRenderInvalidAsOptionError = rb_define_class_under(mErrors, "RenderInvalidAsOptionError", cError);
81
88
  cRenderObjectAndCollectionError = rb_define_class_under(mErrors, "RenderObjectAndCollectionError", cError);
82
89
  cRenderLayoutWithoutBlockError = rb_define_class_under(mErrors, "RenderLayoutWithoutBlockError", cError);
90
+ cStrictLocalsPositionalArgumentError = rb_define_class_under(mErrors, "StrictLocalsPositionalArgumentError", cError);
91
+ cStrictLocalsBlockArgumentError = rb_define_class_under(mErrors, "StrictLocalsBlockArgumentError", cError);
92
+ cStrictLocalsSplatArgumentError = rb_define_class_under(mErrors, "StrictLocalsSplatArgumentError", cError);
93
+ cStrictLocalsMissingParenthesisError = rb_define_class_under(mErrors, "StrictLocalsMissingParenthesisError", cError);
94
+ cStrictLocalsDuplicateDeclarationError = rb_define_class_under(mErrors, "StrictLocalsDuplicateDeclarationError", cError);
95
+ cVoidElementContentError = rb_define_class_under(mErrors, "VoidElementContentError", cError);
96
+ cDotNotationCasingError = rb_define_class_under(mErrors, "DotNotationCasingError", cError);
83
97
  }
84
98
 
85
99
  static VALUE rb_unexpected_error_from_c_struct(UNEXPECTED_ERROR_T* unexpected_error) {
@@ -743,6 +757,155 @@ static VALUE rb_render_layout_without_block_error_from_c_struct(RENDER_LAYOUT_WI
743
757
  return rb_class_new_instance(4, args, cRenderLayoutWithoutBlockError);
744
758
  };
745
759
 
760
+ static VALUE rb_strict_locals_positional_argument_error_from_c_struct(STRICT_LOCALS_POSITIONAL_ARGUMENT_ERROR_T* strict_locals_positional_argument_error) {
761
+ if (strict_locals_positional_argument_error == NULL) { return Qnil; }
762
+
763
+ ERROR_T* error = &strict_locals_positional_argument_error->base;
764
+
765
+ VALUE type = rb_string_from_hb_string(error_type_to_string(error));
766
+ VALUE location = rb_location_from_c_struct(error->location);
767
+ VALUE message = rb_string_from_hb_string(error->message);
768
+
769
+ VALUE strict_locals_positional_argument_error_name = rb_utf8_str_new(strict_locals_positional_argument_error->name.data, strict_locals_positional_argument_error->name.length);
770
+
771
+ VALUE args[4] = {
772
+ type,
773
+ location,
774
+ message,
775
+ strict_locals_positional_argument_error_name
776
+ };
777
+
778
+ return rb_class_new_instance(4, args, cStrictLocalsPositionalArgumentError);
779
+ };
780
+
781
+ static VALUE rb_strict_locals_block_argument_error_from_c_struct(STRICT_LOCALS_BLOCK_ARGUMENT_ERROR_T* strict_locals_block_argument_error) {
782
+ if (strict_locals_block_argument_error == NULL) { return Qnil; }
783
+
784
+ ERROR_T* error = &strict_locals_block_argument_error->base;
785
+
786
+ VALUE type = rb_string_from_hb_string(error_type_to_string(error));
787
+ VALUE location = rb_location_from_c_struct(error->location);
788
+ VALUE message = rb_string_from_hb_string(error->message);
789
+
790
+ VALUE strict_locals_block_argument_error_name = rb_utf8_str_new(strict_locals_block_argument_error->name.data, strict_locals_block_argument_error->name.length);
791
+
792
+ VALUE args[4] = {
793
+ type,
794
+ location,
795
+ message,
796
+ strict_locals_block_argument_error_name
797
+ };
798
+
799
+ return rb_class_new_instance(4, args, cStrictLocalsBlockArgumentError);
800
+ };
801
+
802
+ static VALUE rb_strict_locals_splat_argument_error_from_c_struct(STRICT_LOCALS_SPLAT_ARGUMENT_ERROR_T* strict_locals_splat_argument_error) {
803
+ if (strict_locals_splat_argument_error == NULL) { return Qnil; }
804
+
805
+ ERROR_T* error = &strict_locals_splat_argument_error->base;
806
+
807
+ VALUE type = rb_string_from_hb_string(error_type_to_string(error));
808
+ VALUE location = rb_location_from_c_struct(error->location);
809
+ VALUE message = rb_string_from_hb_string(error->message);
810
+
811
+ VALUE strict_locals_splat_argument_error_name = rb_utf8_str_new(strict_locals_splat_argument_error->name.data, strict_locals_splat_argument_error->name.length);
812
+
813
+ VALUE args[4] = {
814
+ type,
815
+ location,
816
+ message,
817
+ strict_locals_splat_argument_error_name
818
+ };
819
+
820
+ return rb_class_new_instance(4, args, cStrictLocalsSplatArgumentError);
821
+ };
822
+
823
+ static VALUE rb_strict_locals_missing_parenthesis_error_from_c_struct(STRICT_LOCALS_MISSING_PARENTHESIS_ERROR_T* strict_locals_missing_parenthesis_error) {
824
+ if (strict_locals_missing_parenthesis_error == NULL) { return Qnil; }
825
+
826
+ ERROR_T* error = &strict_locals_missing_parenthesis_error->base;
827
+
828
+ VALUE type = rb_string_from_hb_string(error_type_to_string(error));
829
+ VALUE location = rb_location_from_c_struct(error->location);
830
+ VALUE message = rb_string_from_hb_string(error->message);
831
+
832
+ VALUE strict_locals_missing_parenthesis_error_rest = rb_utf8_str_new(strict_locals_missing_parenthesis_error->rest.data, strict_locals_missing_parenthesis_error->rest.length);
833
+
834
+ VALUE args[4] = {
835
+ type,
836
+ location,
837
+ message,
838
+ strict_locals_missing_parenthesis_error_rest
839
+ };
840
+
841
+ return rb_class_new_instance(4, args, cStrictLocalsMissingParenthesisError);
842
+ };
843
+
844
+ static VALUE rb_strict_locals_duplicate_declaration_error_from_c_struct(STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR_T* strict_locals_duplicate_declaration_error) {
845
+ if (strict_locals_duplicate_declaration_error == NULL) { return Qnil; }
846
+
847
+ ERROR_T* error = &strict_locals_duplicate_declaration_error->base;
848
+
849
+ VALUE type = rb_string_from_hb_string(error_type_to_string(error));
850
+ VALUE location = rb_location_from_c_struct(error->location);
851
+ VALUE message = rb_string_from_hb_string(error->message);
852
+
853
+
854
+ VALUE args[3] = {
855
+ type,
856
+ location,
857
+ message
858
+ };
859
+
860
+ return rb_class_new_instance(3, args, cStrictLocalsDuplicateDeclarationError);
861
+ };
862
+
863
+ static VALUE rb_void_element_content_error_from_c_struct(VOID_ELEMENT_CONTENT_ERROR_T* void_element_content_error) {
864
+ if (void_element_content_error == NULL) { return Qnil; }
865
+
866
+ ERROR_T* error = &void_element_content_error->base;
867
+
868
+ VALUE type = rb_string_from_hb_string(error_type_to_string(error));
869
+ VALUE location = rb_location_from_c_struct(error->location);
870
+ VALUE message = rb_string_from_hb_string(error->message);
871
+
872
+ VALUE void_element_content_error_tag_name = rb_token_from_c_struct(void_element_content_error->tag_name);
873
+ VALUE void_element_content_error_helper_name = rb_utf8_str_new(void_element_content_error->helper_name.data, void_element_content_error->helper_name.length);
874
+ VALUE void_element_content_error_content_type = rb_utf8_str_new(void_element_content_error->content_type.data, void_element_content_error->content_type.length);
875
+
876
+ VALUE args[6] = {
877
+ type,
878
+ location,
879
+ message,
880
+ void_element_content_error_tag_name,
881
+ void_element_content_error_helper_name,
882
+ void_element_content_error_content_type
883
+ };
884
+
885
+ return rb_class_new_instance(6, args, cVoidElementContentError);
886
+ };
887
+
888
+ static VALUE rb_dot_notation_casing_error_from_c_struct(DOT_NOTATION_CASING_ERROR_T* dot_notation_casing_error) {
889
+ if (dot_notation_casing_error == NULL) { return Qnil; }
890
+
891
+ ERROR_T* error = &dot_notation_casing_error->base;
892
+
893
+ VALUE type = rb_string_from_hb_string(error_type_to_string(error));
894
+ VALUE location = rb_location_from_c_struct(error->location);
895
+ VALUE message = rb_string_from_hb_string(error->message);
896
+
897
+ VALUE dot_notation_casing_error_segment = rb_token_from_c_struct(dot_notation_casing_error->segment);
898
+
899
+ VALUE args[4] = {
900
+ type,
901
+ location,
902
+ message,
903
+ dot_notation_casing_error_segment
904
+ };
905
+
906
+ return rb_class_new_instance(4, args, cDotNotationCasingError);
907
+ };
908
+
746
909
 
747
910
  VALUE rb_error_from_c_struct(ERROR_T* error) {
748
911
  if (!error) { return Qnil; }
@@ -778,6 +941,13 @@ VALUE rb_error_from_c_struct(ERROR_T* error) {
778
941
  case RENDER_INVALID_AS_OPTION_ERROR: return rb_render_invalid_as_option_error_from_c_struct((RENDER_INVALID_AS_OPTION_ERROR_T*) error); break;
779
942
  case RENDER_OBJECT_AND_COLLECTION_ERROR: return rb_render_object_and_collection_error_from_c_struct((RENDER_OBJECT_AND_COLLECTION_ERROR_T*) error); break;
780
943
  case RENDER_LAYOUT_WITHOUT_BLOCK_ERROR: return rb_render_layout_without_block_error_from_c_struct((RENDER_LAYOUT_WITHOUT_BLOCK_ERROR_T*) error); break;
944
+ case STRICT_LOCALS_POSITIONAL_ARGUMENT_ERROR: return rb_strict_locals_positional_argument_error_from_c_struct((STRICT_LOCALS_POSITIONAL_ARGUMENT_ERROR_T*) error); break;
945
+ case STRICT_LOCALS_BLOCK_ARGUMENT_ERROR: return rb_strict_locals_block_argument_error_from_c_struct((STRICT_LOCALS_BLOCK_ARGUMENT_ERROR_T*) error); break;
946
+ case STRICT_LOCALS_SPLAT_ARGUMENT_ERROR: return rb_strict_locals_splat_argument_error_from_c_struct((STRICT_LOCALS_SPLAT_ARGUMENT_ERROR_T*) error); break;
947
+ case STRICT_LOCALS_MISSING_PARENTHESIS_ERROR: return rb_strict_locals_missing_parenthesis_error_from_c_struct((STRICT_LOCALS_MISSING_PARENTHESIS_ERROR_T*) error); break;
948
+ case STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR: return rb_strict_locals_duplicate_declaration_error_from_c_struct((STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR_T*) error); break;
949
+ case VOID_ELEMENT_CONTENT_ERROR: return rb_void_element_content_error_from_c_struct((VOID_ELEMENT_CONTENT_ERROR_T*) error); break;
950
+ case DOT_NOTATION_CASING_ERROR: return rb_dot_notation_casing_error_from_c_struct((DOT_NOTATION_CASING_ERROR_T*) error); break;
781
951
  }
782
952
 
783
953
  return Qnil;
data/ext/herb/extconf.rb CHANGED
@@ -46,6 +46,12 @@ prism_include_path = "#{prism_path}/include"
46
46
  $VPATH << "$(srcdir)/../../src"
47
47
  $VPATH << "$(srcdir)/../../src/analyze"
48
48
  $VPATH << "$(srcdir)/../../src/analyze/action_view"
49
+ $VPATH << "$(srcdir)/../../src/ast"
50
+ $VPATH << "$(srcdir)/../../src/lexer"
51
+ $VPATH << "$(srcdir)/../../src/location"
52
+ $VPATH << "$(srcdir)/../../src/parser"
53
+ $VPATH << "$(srcdir)/../../src/prism"
54
+ $VPATH << "$(srcdir)/../../src/lib"
49
55
  $VPATH << "$(srcdir)/../../src/util"
50
56
  $VPATH << prism_src_path
51
57
  $VPATH << "#{prism_src_path}/util"
data/ext/herb/extension.c CHANGED
@@ -1,7 +1,7 @@
1
1
  #include <ruby.h>
2
2
 
3
- #include "../../src/include/util/hb_allocator.h"
4
- #include "../../src/include/util/hb_arena_debug.h"
3
+ #include "../../src/include/lib/hb_allocator.h"
4
+ #include "../../src/include/lib/hb_arena_debug.h"
5
5
 
6
6
  #include "error_helpers.h"
7
7
  #include "extension.h"
@@ -136,10 +136,20 @@ static VALUE Herb_parse(int argc, VALUE* argv, VALUE self) {
136
136
  }
137
137
  if (!NIL_P(action_view_helpers) && RTEST(action_view_helpers)) { parser_options.action_view_helpers = true; }
138
138
 
139
+ VALUE dot_notation_tags = rb_hash_lookup(options, rb_utf8_str_new_cstr("dot_notation_tags"));
140
+ if (NIL_P(dot_notation_tags)) {
141
+ dot_notation_tags = rb_hash_lookup(options, ID2SYM(rb_intern("dot_notation_tags")));
142
+ }
143
+ if (!NIL_P(dot_notation_tags) && RTEST(dot_notation_tags)) { parser_options.dot_notation_tags = true; }
144
+
139
145
  VALUE render_nodes = rb_hash_lookup(options, rb_utf8_str_new_cstr("render_nodes"));
140
146
  if (NIL_P(render_nodes)) { render_nodes = rb_hash_lookup(options, ID2SYM(rb_intern("render_nodes"))); }
141
147
  if (!NIL_P(render_nodes) && RTEST(render_nodes)) { parser_options.render_nodes = true; }
142
148
 
149
+ VALUE strict_locals = rb_hash_lookup(options, rb_utf8_str_new_cstr("strict_locals"));
150
+ if (NIL_P(strict_locals)) { strict_locals = rb_hash_lookup(options, ID2SYM(rb_intern("strict_locals"))); }
151
+ if (!NIL_P(strict_locals) && RTEST(strict_locals)) { parser_options.strict_locals = true; }
152
+
143
153
  VALUE prism_nodes = rb_hash_lookup(options, rb_utf8_str_new_cstr("prism_nodes"));
144
154
  if (NIL_P(prism_nodes)) { prism_nodes = rb_hash_lookup(options, ID2SYM(rb_intern("prism_nodes"))); }
145
155
  if (!NIL_P(prism_nodes) && RTEST(prism_nodes)) { parser_options.prism_nodes = true; }
@@ -152,6 +162,10 @@ static VALUE Herb_parse(int argc, VALUE* argv, VALUE self) {
152
162
  if (NIL_P(prism_program)) { prism_program = rb_hash_lookup(options, ID2SYM(rb_intern("prism_program"))); }
153
163
  if (!NIL_P(prism_program) && RTEST(prism_program)) { parser_options.prism_program = true; }
154
164
 
165
+ VALUE html = rb_hash_lookup(options, rb_utf8_str_new_cstr("html"));
166
+ if (NIL_P(html)) { html = rb_hash_lookup(options, ID2SYM(rb_intern("html"))); }
167
+ if (!NIL_P(html) && !RTEST(html)) { parser_options.html = false; }
168
+
155
169
  VALUE arena_stats = rb_hash_lookup(options, rb_utf8_str_new_cstr("arena_stats"));
156
170
  if (NIL_P(arena_stats)) { arena_stats = rb_hash_lookup(options, ID2SYM(rb_intern("arena_stats"))); }
157
171
  if (!NIL_P(arena_stats) && RTEST(arena_stats)) { print_arena_stats = true; }
@@ -5,11 +5,11 @@
5
5
  #include "nodes.h"
6
6
 
7
7
  #include "../../src/include/herb.h"
8
- #include "../../src/include/location.h"
9
- #include "../../src/include/position.h"
10
- #include "../../src/include/token.h"
11
- #include "../../src/include/util/hb_allocator.h"
12
- #include "../../src/include/util/hb_string.h"
8
+ #include "../../src/include/lexer/token.h"
9
+ #include "../../src/include/lib/hb_allocator.h"
10
+ #include "../../src/include/lib/hb_string.h"
11
+ #include "../../src/include/location/location.h"
12
+ #include "../../src/include/location/position.h"
13
13
 
14
14
  const char* check_string(VALUE value) {
15
15
  if (NIL_P(value)) { return NULL; }
@@ -90,6 +90,7 @@ VALUE create_parse_result(AST_DOCUMENT_NODE_T* root, VALUE source, const parser_
90
90
  rb_hash_aset(kwargs, ID2SYM(rb_intern("analyze")), options->analyze ? Qtrue : Qfalse);
91
91
  rb_hash_aset(kwargs, ID2SYM(rb_intern("action_view_helpers")), options->action_view_helpers ? Qtrue : Qfalse);
92
92
  rb_hash_aset(kwargs, ID2SYM(rb_intern("render_nodes")), options->render_nodes ? Qtrue : Qfalse);
93
+ rb_hash_aset(kwargs, ID2SYM(rb_intern("strict_locals")), options->strict_locals ? Qtrue : Qfalse);
93
94
  rb_hash_aset(kwargs, ID2SYM(rb_intern("prism_nodes")), options->prism_nodes ? Qtrue : Qfalse);
94
95
  rb_hash_aset(kwargs, ID2SYM(rb_intern("prism_nodes_deep")), options->prism_nodes_deep ? Qtrue : Qfalse);
95
96
  rb_hash_aset(kwargs, ID2SYM(rb_intern("prism_program")), options->prism_program ? Qtrue : Qfalse);
@@ -4,10 +4,10 @@
4
4
  #include <ruby.h>
5
5
 
6
6
  #include "../../src/include/herb.h"
7
- #include "../../src/include/location.h"
8
- #include "../../src/include/position.h"
9
- #include "../../src/include/range.h"
10
- #include "../../src/include/token.h"
7
+ #include "../../src/include/lexer/token.h"
8
+ #include "../../src/include/location/location.h"
9
+ #include "../../src/include/location/position.h"
10
+ #include "../../src/include/location/range.h"
11
11
 
12
12
  const char* check_string(VALUE value);
13
13
  VALUE rb_string_from_hb_string(hb_string_T string);
data/ext/herb/nodes.c CHANGED
@@ -10,7 +10,7 @@
10
10
  #include "nodes.h"
11
11
 
12
12
  #include "../../src/include/herb.h"
13
- #include "../../src/include/token.h"
13
+ #include "../../src/include/lexer/token.h"
14
14
 
15
15
  VALUE rb_node_from_c_struct(AST_NODE_T* node);
16
16
  static VALUE rb_nodes_array_from_c_array(hb_array_T* array);
@@ -55,6 +55,8 @@ static VALUE cERBBeginNode;
55
55
  static VALUE cERBUnlessNode;
56
56
  static VALUE cRubyRenderLocalNode;
57
57
  static VALUE cERBRenderNode;
58
+ static VALUE cRubyStrictLocalNode;
59
+ static VALUE cERBStrictLocalsNode;
58
60
  static VALUE cERBYieldNode;
59
61
  static VALUE cERBInNode;
60
62
 
@@ -99,6 +101,8 @@ void rb_init_node_classes(void) {
99
101
  cERBUnlessNode = rb_define_class_under(mAST, "ERBUnlessNode", cNode);
100
102
  cRubyRenderLocalNode = rb_define_class_under(mAST, "RubyRenderLocalNode", cNode);
101
103
  cERBRenderNode = rb_define_class_under(mAST, "ERBRenderNode", cNode);
104
+ cRubyStrictLocalNode = rb_define_class_under(mAST, "RubyStrictLocalNode", cNode);
105
+ cERBStrictLocalsNode = rb_define_class_under(mAST, "ERBStrictLocalsNode", cNode);
102
106
  cERBYieldNode = rb_define_class_under(mAST, "ERBYieldNode", cNode);
103
107
  cERBInNode = rb_define_class_under(mAST, "ERBInNode", cNode);
104
108
  }
@@ -822,9 +826,12 @@ static VALUE rb_erb_block_node_from_c_struct(AST_ERB_BLOCK_NODE_T* erb_block_nod
822
826
  erb_block_node_prism_node = Qnil;
823
827
  }
824
828
  VALUE erb_block_node_body = rb_nodes_array_from_c_array(erb_block_node->body);
829
+ VALUE erb_block_node_rescue_clause = rb_node_from_c_struct((AST_NODE_T*) erb_block_node->rescue_clause);
830
+ VALUE erb_block_node_else_clause = rb_node_from_c_struct((AST_NODE_T*) erb_block_node->else_clause);
831
+ VALUE erb_block_node_ensure_clause = rb_node_from_c_struct((AST_NODE_T*) erb_block_node->ensure_clause);
825
832
  VALUE erb_block_node_end_node = rb_node_from_c_struct((AST_NODE_T*) erb_block_node->end_node);
826
833
 
827
- VALUE args[9] = {
834
+ VALUE args[12] = {
828
835
  type,
829
836
  location,
830
837
  errors,
@@ -833,10 +840,13 @@ static VALUE rb_erb_block_node_from_c_struct(AST_ERB_BLOCK_NODE_T* erb_block_nod
833
840
  erb_block_node_tag_closing,
834
841
  erb_block_node_prism_node,
835
842
  erb_block_node_body,
843
+ erb_block_node_rescue_clause,
844
+ erb_block_node_else_clause,
845
+ erb_block_node_ensure_clause,
836
846
  erb_block_node_end_node
837
847
  };
838
848
 
839
- return rb_class_new_instance(9, args, cERBBlockNode);
849
+ return rb_class_new_instance(12, args, cERBBlockNode);
840
850
  };
841
851
 
842
852
  static VALUE rb_erb_when_node_from_c_struct(AST_ERB_WHEN_NODE_T* erb_when_node) {
@@ -1368,6 +1378,80 @@ static VALUE rb_erb_render_node_from_c_struct(AST_ERB_RENDER_NODE_T* erb_render_
1368
1378
  return rb_class_new_instance(26, args, cERBRenderNode);
1369
1379
  };
1370
1380
 
1381
+ static VALUE rb_ruby_strict_local_node_from_c_struct(AST_RUBY_STRICT_LOCAL_NODE_T* ruby_strict_local_node) {
1382
+ if (ruby_strict_local_node == NULL) { return Qnil; }
1383
+
1384
+ AST_NODE_T* node = &ruby_strict_local_node->base;
1385
+
1386
+ VALUE type = rb_string_from_hb_string(ast_node_type_to_string(node));
1387
+ VALUE location = rb_location_from_c_struct(node->location);
1388
+ VALUE errors = rb_errors_array_from_c_array(node->errors);
1389
+
1390
+ VALUE ruby_strict_local_node_name = rb_token_from_c_struct(ruby_strict_local_node->name);
1391
+ VALUE ruby_strict_local_node_default_value = rb_node_from_c_struct((AST_NODE_T*) ruby_strict_local_node->default_value);
1392
+ VALUE ruby_strict_local_node_required = (ruby_strict_local_node->required) ? Qtrue : Qfalse;
1393
+ VALUE ruby_strict_local_node_double_splat = (ruby_strict_local_node->double_splat) ? Qtrue : Qfalse;
1394
+
1395
+ VALUE args[7] = {
1396
+ type,
1397
+ location,
1398
+ errors,
1399
+ ruby_strict_local_node_name,
1400
+ ruby_strict_local_node_default_value,
1401
+ ruby_strict_local_node_required,
1402
+ ruby_strict_local_node_double_splat
1403
+ };
1404
+
1405
+ return rb_class_new_instance(7, args, cRubyStrictLocalNode);
1406
+ };
1407
+
1408
+ static VALUE rb_erb_strict_locals_node_from_c_struct(AST_ERB_STRICT_LOCALS_NODE_T* erb_strict_locals_node) {
1409
+ if (erb_strict_locals_node == NULL) { return Qnil; }
1410
+
1411
+ AST_NODE_T* node = &erb_strict_locals_node->base;
1412
+
1413
+ VALUE type = rb_string_from_hb_string(ast_node_type_to_string(node));
1414
+ VALUE location = rb_location_from_c_struct(node->location);
1415
+ VALUE errors = rb_errors_array_from_c_array(node->errors);
1416
+
1417
+ VALUE erb_strict_locals_node_tag_opening = rb_token_from_c_struct(erb_strict_locals_node->tag_opening);
1418
+ VALUE erb_strict_locals_node_content = rb_token_from_c_struct(erb_strict_locals_node->content);
1419
+ VALUE erb_strict_locals_node_tag_closing = rb_token_from_c_struct(erb_strict_locals_node->tag_closing);
1420
+ /* analyzed_ruby is internal parser state, not exposed to Ruby */
1421
+ VALUE erb_strict_locals_node_analyzed_ruby = Qnil;
1422
+ VALUE erb_strict_locals_node_prism_node;
1423
+ if (erb_strict_locals_node->prism_node.node != NULL && erb_strict_locals_node->prism_node.parser != NULL) {
1424
+ pm_buffer_t pm_buffer = { 0 };
1425
+ pm_serialize(erb_strict_locals_node->prism_node.parser, erb_strict_locals_node->prism_node.node, &pm_buffer);
1426
+
1427
+ if (pm_buffer.length > 0) {
1428
+ erb_strict_locals_node_prism_node = rb_str_new(pm_buffer.value, pm_buffer.length);
1429
+ rb_enc_associate(erb_strict_locals_node_prism_node, rb_ascii8bit_encoding());
1430
+ OBJ_FREEZE(erb_strict_locals_node_prism_node);
1431
+ } else {
1432
+ erb_strict_locals_node_prism_node = Qnil;
1433
+ }
1434
+ pm_buffer_free(&pm_buffer);
1435
+ } else {
1436
+ erb_strict_locals_node_prism_node = Qnil;
1437
+ }
1438
+ VALUE erb_strict_locals_node_locals = rb_nodes_array_from_c_array(erb_strict_locals_node->locals);
1439
+
1440
+ VALUE args[9] = {
1441
+ type,
1442
+ location,
1443
+ errors,
1444
+ erb_strict_locals_node_tag_opening,
1445
+ erb_strict_locals_node_content,
1446
+ erb_strict_locals_node_tag_closing,
1447
+ erb_strict_locals_node_analyzed_ruby,
1448
+ erb_strict_locals_node_prism_node,
1449
+ erb_strict_locals_node_locals
1450
+ };
1451
+
1452
+ return rb_class_new_instance(9, args, cERBStrictLocalsNode);
1453
+ };
1454
+
1371
1455
  static VALUE rb_erb_yield_node_from_c_struct(AST_ERB_YIELD_NODE_T* erb_yield_node) {
1372
1456
  if (erb_yield_node == NULL) { return Qnil; }
1373
1457
 
@@ -1465,6 +1549,8 @@ VALUE rb_node_from_c_struct(AST_NODE_T* node) {
1465
1549
  case AST_ERB_UNLESS_NODE: return rb_erb_unless_node_from_c_struct((AST_ERB_UNLESS_NODE_T*) node); break;
1466
1550
  case AST_RUBY_RENDER_LOCAL_NODE: return rb_ruby_render_local_node_from_c_struct((AST_RUBY_RENDER_LOCAL_NODE_T*) node); break;
1467
1551
  case AST_ERB_RENDER_NODE: return rb_erb_render_node_from_c_struct((AST_ERB_RENDER_NODE_T*) node); break;
1552
+ case AST_RUBY_STRICT_LOCAL_NODE: return rb_ruby_strict_local_node_from_c_struct((AST_RUBY_STRICT_LOCAL_NODE_T*) node); break;
1553
+ case AST_ERB_STRICT_LOCALS_NODE: return rb_erb_strict_locals_node_from_c_struct((AST_ERB_STRICT_LOCALS_NODE_T*) node); break;
1468
1554
  case AST_ERB_YIELD_NODE: return rb_erb_yield_node_from_c_struct((AST_ERB_YIELD_NODE_T*) node); break;
1469
1555
  case AST_ERB_IN_NODE: return rb_erb_in_node_from_c_struct((AST_ERB_IN_NODE_T*) node); break;
1470
1556
  }
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ module Herb
5
+ module AST
6
+ class ERBContentNode < Node
7
+ #: () -> Prism::node?
8
+ def parsed_prism_node
9
+ erb_content = @content&.value&.strip
10
+ return nil unless erb_content
11
+
12
+ begin
13
+ require "prism"
14
+ rescue LoadError
15
+ return nil
16
+ end
17
+
18
+ prism_result = Prism.parse(erb_content)
19
+ return nil unless prism_result.success?
20
+
21
+ prism_result.value.statements.body.first
22
+ end
23
+
24
+ #: () -> Prism::node?
25
+ def prism
26
+ return @prism if defined?(@prism)
27
+
28
+ @prism = deserialized_prism_node || parsed_prism_node
29
+ end
30
+ end
31
+ end
32
+ end