herb 0.9.2-arm-linux-gnu → 0.9.4-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 (171) 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/3.0/herb.so +0 -0
  11. data/lib/herb/3.1/herb.so +0 -0
  12. data/lib/herb/3.2/herb.so +0 -0
  13. data/lib/herb/3.3/herb.so +0 -0
  14. data/lib/herb/3.4/herb.so +0 -0
  15. data/lib/herb/4.0/herb.so +0 -0
  16. data/lib/herb/ast/erb_content_node.rb +32 -0
  17. data/lib/herb/ast/nodes.rb +244 -3
  18. data/lib/herb/cli.rb +12 -2
  19. data/lib/herb/engine/compiler.rb +166 -75
  20. data/lib/herb/engine/validators/security_validator.rb +40 -0
  21. data/lib/herb/engine.rb +3 -0
  22. data/lib/herb/errors.rb +268 -0
  23. data/lib/herb/parser_options.rb +7 -2
  24. data/lib/herb/project.rb +58 -17
  25. data/lib/herb/version.rb +1 -1
  26. data/lib/herb/visitor.rb +82 -0
  27. data/lib/herb.rb +1 -0
  28. data/sig/herb/ast/erb_content_node.rbs +13 -0
  29. data/sig/herb/ast/nodes.rbs +98 -2
  30. data/sig/herb/engine/compiler.rbs +31 -2
  31. data/sig/herb/engine/validators/security_validator.rbs +4 -0
  32. data/sig/herb/engine.rbs +3 -0
  33. data/sig/herb/errors.rbs +122 -0
  34. data/sig/herb/parser_options.rbs +6 -2
  35. data/sig/herb/visitor.rbs +12 -0
  36. data/sig/serialized_ast_errors.rbs +29 -0
  37. data/sig/serialized_ast_nodes.rbs +19 -0
  38. data/src/analyze/action_view/attribute_extraction_helpers.c +420 -91
  39. data/src/analyze/action_view/image_tag.c +87 -0
  40. data/src/analyze/action_view/javascript_include_tag.c +22 -12
  41. data/src/analyze/action_view/registry.c +6 -3
  42. data/src/analyze/action_view/tag.c +19 -2
  43. data/src/analyze/action_view/tag_helper_node_builders.c +105 -36
  44. data/src/analyze/action_view/tag_helpers.c +792 -44
  45. data/src/analyze/analyze.c +167 -13
  46. data/src/analyze/{helpers.c → analyze_helpers.c} +1 -1
  47. data/src/analyze/analyzed_ruby.c +1 -1
  48. data/src/analyze/builders.c +11 -8
  49. data/src/analyze/conditional_elements.c +6 -7
  50. data/src/analyze/conditional_open_tags.c +6 -7
  51. data/src/analyze/control_type.c +4 -2
  52. data/src/analyze/invalid_structures.c +5 -5
  53. data/src/analyze/missing_end.c +2 -2
  54. data/src/analyze/parse_errors.c +47 -6
  55. data/src/analyze/prism_annotate.c +7 -7
  56. data/src/analyze/render_nodes.c +6 -26
  57. data/src/analyze/strict_locals.c +651 -0
  58. data/src/analyze/transform.c +7 -0
  59. data/src/{ast_node.c → ast/ast_node.c} +8 -8
  60. data/src/{ast_nodes.c → ast/ast_nodes.c} +82 -11
  61. data/src/{ast_pretty_print.c → ast/ast_pretty_print.c} +113 -9
  62. data/src/{pretty_print.c → ast/pretty_print.c} +9 -9
  63. data/src/errors.c +398 -8
  64. data/src/extract.c +5 -5
  65. data/src/herb.c +15 -5
  66. data/src/include/analyze/action_view/attribute_extraction_helpers.h +3 -1
  67. data/src/include/analyze/action_view/tag_helper_handler.h +3 -3
  68. data/src/include/analyze/action_view/tag_helper_node_builders.h +34 -5
  69. data/src/include/analyze/action_view/tag_helpers.h +4 -3
  70. data/src/include/analyze/analyze.h +12 -5
  71. data/src/include/analyze/analyzed_ruby.h +2 -2
  72. data/src/include/analyze/builders.h +4 -4
  73. data/src/include/analyze/conditional_elements.h +2 -2
  74. data/src/include/analyze/conditional_open_tags.h +2 -2
  75. data/src/include/analyze/control_type.h +1 -1
  76. data/src/include/analyze/helpers.h +2 -2
  77. data/src/include/analyze/invalid_structures.h +1 -1
  78. data/src/include/analyze/prism_annotate.h +2 -2
  79. data/src/include/analyze/render_nodes.h +1 -1
  80. data/src/include/analyze/strict_locals.h +11 -0
  81. data/src/include/{ast_node.h → ast/ast_node.h} +4 -4
  82. data/src/include/{ast_nodes.h → ast/ast_nodes.h} +38 -14
  83. data/src/include/{ast_pretty_print.h → ast/ast_pretty_print.h} +3 -3
  84. data/src/include/{pretty_print.h → ast/pretty_print.h} +4 -4
  85. data/src/include/errors.h +65 -7
  86. data/src/include/extract.h +2 -2
  87. data/src/include/herb.h +5 -5
  88. data/src/include/{lex_helpers.h → lexer/lex_helpers.h} +5 -5
  89. data/src/include/{lexer.h → lexer/lexer.h} +1 -1
  90. data/src/include/{lexer_peek_helpers.h → lexer/lexer_peek_helpers.h} +2 -2
  91. data/src/include/{lexer_struct.h → lexer/lexer_struct.h} +2 -2
  92. data/src/include/{token.h → lexer/token.h} +3 -3
  93. data/src/include/{token_matchers.h → lexer/token_matchers.h} +1 -1
  94. data/src/include/{token_struct.h → lexer/token_struct.h} +3 -3
  95. data/src/include/{util → lib}/hb_foreach.h +1 -1
  96. data/src/include/{util → lib}/hb_string.h +5 -1
  97. data/src/include/{location.h → location/location.h} +1 -1
  98. data/src/include/parser/dot_notation.h +12 -0
  99. data/src/include/{parser.h → parser/parser.h} +11 -4
  100. data/src/include/{parser_helpers.h → parser/parser_helpers.h} +6 -6
  101. data/src/include/{prism_context.h → prism/prism_context.h} +2 -2
  102. data/src/include/{prism_helpers.h → prism/prism_helpers.h} +6 -6
  103. data/src/include/{html_util.h → util/html_util.h} +1 -1
  104. data/src/include/util/ruby_util.h +9 -0
  105. data/src/include/{utf8.h → util/utf8.h} +1 -1
  106. data/src/include/{util.h → util/util.h} +1 -1
  107. data/src/include/version.h +1 -1
  108. data/src/include/visitor.h +3 -3
  109. data/src/{lexer_peek_helpers.c → lexer/lexer_peek_helpers.c} +3 -3
  110. data/src/{token.c → lexer/token.c} +8 -8
  111. data/src/{token_matchers.c → lexer/token_matchers.c} +2 -2
  112. data/src/lexer.c +6 -6
  113. data/src/{util → lib}/hb_allocator.c +2 -2
  114. data/src/{util → lib}/hb_arena.c +1 -1
  115. data/src/{util → lib}/hb_arena_debug.c +2 -2
  116. data/src/{util → lib}/hb_array.c +2 -2
  117. data/src/{util → lib}/hb_buffer.c +2 -2
  118. data/src/{util → lib}/hb_narray.c +1 -1
  119. data/src/{util → lib}/hb_string.c +2 -2
  120. data/src/{location.c → location/location.c} +2 -2
  121. data/src/{position.c → location/position.c} +2 -2
  122. data/src/{range.c → location/range.c} +1 -1
  123. data/src/main.c +11 -11
  124. data/src/parser/dot_notation.c +100 -0
  125. data/src/{parser_match_tags.c → parser/match_tags.c} +34 -5
  126. data/src/{parser_helpers.c → parser/parser_helpers.c} +10 -10
  127. data/src/parser.c +68 -32
  128. data/src/{prism_helpers.c → prism/prism_helpers.c} +7 -7
  129. data/src/{ruby_parser.c → prism/ruby_parser.c} +1 -1
  130. data/src/{html_util.c → util/html_util.c} +4 -4
  131. data/src/{io.c → util/io.c} +3 -3
  132. data/src/util/ruby_util.c +42 -0
  133. data/src/{utf8.c → util/utf8.c} +2 -2
  134. data/src/{util.c → util/util.c} +4 -4
  135. data/src/visitor.c +35 -3
  136. data/templates/ext/herb/error_helpers.c.erb +2 -2
  137. data/templates/ext/herb/nodes.c.erb +1 -1
  138. data/templates/java/error_helpers.c.erb +1 -1
  139. data/templates/java/error_helpers.h.erb +2 -2
  140. data/templates/java/nodes.c.erb +4 -4
  141. data/templates/java/nodes.h.erb +1 -1
  142. data/templates/javascript/packages/node/extension/error_helpers.cpp.erb +4 -4
  143. data/templates/javascript/packages/node/extension/nodes.cpp.erb +4 -4
  144. data/templates/lib/herb/visitor.rb.erb +14 -0
  145. data/templates/src/analyze/missing_end.c.erb +2 -2
  146. data/templates/src/{ast_nodes.c.erb → ast/ast_nodes.c.erb} +9 -9
  147. data/templates/src/{ast_pretty_print.c.erb → ast/ast_pretty_print.c.erb} +8 -8
  148. data/templates/src/errors.c.erb +8 -8
  149. data/templates/src/include/{ast_nodes.h.erb → ast/ast_nodes.h.erb} +11 -12
  150. data/templates/src/include/{ast_pretty_print.h.erb → ast/ast_pretty_print.h.erb} +2 -2
  151. data/templates/src/include/errors.h.erb +7 -7
  152. data/templates/src/{parser_match_tags.c.erb → parser/match_tags.c.erb} +4 -4
  153. data/templates/src/visitor.c.erb +3 -3
  154. data/templates/wasm/error_helpers.cpp.erb +4 -4
  155. data/templates/wasm/nodes.cpp.erb +5 -5
  156. metadata +76 -68
  157. data/src/include/element_source.h +0 -10
  158. /data/src/include/{util → lib}/hb_allocator.h +0 -0
  159. /data/src/include/{util → lib}/hb_arena.h +0 -0
  160. /data/src/include/{util → lib}/hb_arena_debug.h +0 -0
  161. /data/src/include/{util → lib}/hb_array.h +0 -0
  162. /data/src/include/{util → lib}/hb_buffer.h +0 -0
  163. /data/src/include/{util → lib}/hb_narray.h +0 -0
  164. /data/src/include/{util → lib}/string.h +0 -0
  165. /data/src/include/{position.h → location/position.h} +0 -0
  166. /data/src/include/{range.h → location/range.h} +0 -0
  167. /data/src/include/{herb_prism_node.h → prism/herb_prism_node.h} +0 -0
  168. /data/src/include/{prism_serialized.h → prism/prism_serialized.h} +0 -0
  169. /data/src/include/{ruby_parser.h → prism/ruby_parser.h} +0 -0
  170. /data/src/include/{io.h → util/io.h} +0 -0
  171. /data/templates/src/include/{util → lib}/hb_foreach.h.erb +0 -0
@@ -140,4 +140,33 @@ module Herb
140
140
  layout: String,
141
141
  }
142
142
 
143
+ type serialized_strict_locals_positional_argument_error = serialized_error & {
144
+ name: String,
145
+ }
146
+
147
+ type serialized_strict_locals_block_argument_error = serialized_error & {
148
+ name: String,
149
+ }
150
+
151
+ type serialized_strict_locals_splat_argument_error = serialized_error & {
152
+ name: String,
153
+ }
154
+
155
+ type serialized_strict_locals_missing_parenthesis_error = serialized_error & {
156
+ rest: String,
157
+ }
158
+
159
+ type serialized_strict_locals_duplicate_declaration_error = serialized_error & {
160
+ }
161
+
162
+ type serialized_void_element_content_error = serialized_error & {
163
+ tag_name: Herb::Token,
164
+ helper_name: String,
165
+ content_type: String,
166
+ }
167
+
168
+ type serialized_dot_notation_casing_error = serialized_error & {
169
+ segment: Herb::Token,
170
+ }
171
+
143
172
  end
@@ -170,6 +170,9 @@ module Herb
170
170
  tag_closing: Herb::Token,
171
171
  prism_node: String,
172
172
  body: Array[Herb::AST::Node],
173
+ rescue_clause: Herb::AST::ERBRescueNode,
174
+ else_clause: Herb::AST::ERBElseNode,
175
+ ensure_clause: Herb::AST::ERBEnsureNode,
173
176
  end_node: Herb::AST::ERBEndNode,
174
177
  }
175
178
 
@@ -299,6 +302,22 @@ module Herb
299
302
  locals: Array[Herb::AST::RubyRenderLocalNode],
300
303
  }
301
304
 
305
+ type serialized_ruby_strict_local_node = serialized_node & {
306
+ name: Herb::Token,
307
+ default_value: Herb::AST::RubyLiteralNode,
308
+ required: bool,
309
+ double_splat: bool,
310
+ }
311
+
312
+ type serialized_erb_strict_locals_node = serialized_node & {
313
+ tag_opening: Herb::Token,
314
+ content: Herb::Token,
315
+ tag_closing: Herb::Token,
316
+ analyzed_ruby: nil,
317
+ prism_node: String,
318
+ locals: Array[Herb::AST::RubyStrictLocalNode],
319
+ }
320
+
302
321
  type serialized_erb_yield_node = serialized_node & {
303
322
  tag_opening: Herb::Token,
304
323
  content: Herb::Token,
@@ -1,10 +1,11 @@
1
1
  #include "../../include/analyze/action_view/attribute_extraction_helpers.h"
2
2
  #include "../../include/analyze/action_view/tag_helper_node_builders.h"
3
- #include "../../include/html_util.h"
4
- #include "../../include/util.h"
5
- #include "../../include/util/hb_allocator.h"
6
- #include "../../include/util/hb_array.h"
7
- #include "../../include/util/hb_string.h"
3
+ #include "../../include/lib/hb_allocator.h"
4
+ #include "../../include/lib/hb_array.h"
5
+ #include "../../include/lib/hb_buffer.h"
6
+ #include "../../include/lib/hb_string.h"
7
+ #include "../../include/util/html_util.h"
8
+ #include "../../include/util/util.h"
8
9
 
9
10
  #include <prism.h>
10
11
  #include <stdlib.h>
@@ -29,6 +30,171 @@ static char* extract_string_from_prism_node(pm_node_t* node, hb_allocator_T* all
29
30
  return hb_allocator_strndup(allocator, (const char*) source, length);
30
31
  }
31
32
 
33
+ static void compute_position_pair(
34
+ const pm_location_t* location,
35
+ const uint8_t* source,
36
+ const char* original_source,
37
+ size_t erb_content_offset,
38
+ position_T* out_start,
39
+ position_T* out_end
40
+ ) {
41
+ *out_start = prism_location_to_position_with_offset(location, original_source, erb_content_offset, source);
42
+ pm_location_t end_location = { .start = location->end, .end = location->end };
43
+ *out_end = prism_location_to_position_with_offset(&end_location, original_source, erb_content_offset, source);
44
+ }
45
+
46
+ static void extract_key_name_location(pm_node_t* key, pm_location_t* out_name_loc) {
47
+ if (key->type == PM_SYMBOL_NODE) {
48
+ pm_symbol_node_t* symbol = (pm_symbol_node_t*) key;
49
+ *out_name_loc = symbol->value_loc;
50
+ } else if (key->type == PM_STRING_NODE) {
51
+ pm_string_node_t* string_node = (pm_string_node_t*) key;
52
+ *out_name_loc = string_node->content_loc;
53
+ } else {
54
+ *out_name_loc = key->location;
55
+ }
56
+ }
57
+
58
+ static void compute_separator_info(
59
+ pm_assoc_node_t* assoc,
60
+ const uint8_t* source,
61
+ const char* original_source,
62
+ size_t erb_content_offset,
63
+ position_T key_end,
64
+ const char** out_separator_string,
65
+ token_type_T* out_separator_type,
66
+ position_T* out_separator_start,
67
+ position_T* out_separator_end
68
+ ) {
69
+ *out_separator_end =
70
+ prism_location_to_position_with_offset(&assoc->value->location, original_source, erb_content_offset, source);
71
+
72
+ if (assoc->operator_loc.start != NULL) {
73
+ *out_separator_string = " => ";
74
+ *out_separator_type = TOKEN_EQUALS;
75
+ *out_separator_start = key_end;
76
+ } else {
77
+ *out_separator_string = ": ";
78
+ *out_separator_type = TOKEN_COLON;
79
+
80
+ pm_location_t colon_loc = {
81
+ .start = assoc->key->location.end - 1,
82
+ .end = assoc->key->location.end - 1,
83
+ };
84
+
85
+ *out_separator_start =
86
+ prism_location_to_position_with_offset(&colon_loc, original_source, erb_content_offset, source);
87
+ }
88
+ }
89
+
90
+ static void extract_delimited_locations(
91
+ pm_node_t* node,
92
+ const pm_location_t** out_opening,
93
+ const pm_location_t** out_closing,
94
+ const pm_location_t** out_content
95
+ ) {
96
+ if (node->type == PM_STRING_NODE) {
97
+ pm_string_node_t* string_node = (pm_string_node_t*) node;
98
+ *out_opening = &string_node->opening_loc;
99
+ *out_closing = &string_node->closing_loc;
100
+ *out_content = &string_node->content_loc;
101
+ } else if (node->type == PM_SYMBOL_NODE) {
102
+ pm_symbol_node_t* symbol_node = (pm_symbol_node_t*) node;
103
+ *out_opening = &symbol_node->opening_loc;
104
+ *out_closing = &symbol_node->closing_loc;
105
+ *out_content = &symbol_node->value_loc;
106
+ } else {
107
+ *out_opening = NULL;
108
+ *out_closing = NULL;
109
+ *out_content = NULL;
110
+ }
111
+ }
112
+
113
+ static void compute_value_positions(
114
+ pm_node_t* value_node,
115
+ const uint8_t* source,
116
+ const char* original_source,
117
+ size_t erb_content_offset,
118
+ position_T* out_value_start,
119
+ position_T* out_value_end,
120
+ position_T* out_content_start,
121
+ position_T* out_content_end,
122
+ bool* out_quoted
123
+ ) {
124
+ const pm_location_t* opening_loc;
125
+ const pm_location_t* closing_loc;
126
+ const pm_location_t* content_loc;
127
+
128
+ extract_delimited_locations(value_node, &opening_loc, &closing_loc, &content_loc);
129
+
130
+ if (opening_loc && opening_loc->start != NULL && closing_loc && closing_loc->start != NULL) {
131
+ compute_position_pair(
132
+ &*opening_loc,
133
+ source,
134
+ original_source,
135
+ erb_content_offset,
136
+ out_value_start,
137
+ out_content_start
138
+ );
139
+
140
+ compute_position_pair(&*closing_loc, source, original_source, erb_content_offset, out_content_end, out_value_end);
141
+ *out_quoted = true;
142
+ } else {
143
+ const pm_location_t* fallback_loc = content_loc ? content_loc : &value_node->location;
144
+ compute_position_pair(fallback_loc, source, original_source, erb_content_offset, out_value_start, out_value_end);
145
+ *out_content_start = *out_value_start;
146
+ *out_content_end = *out_value_end;
147
+ *out_quoted = false;
148
+ }
149
+ }
150
+
151
+ static void fill_attribute_positions(
152
+ pm_assoc_node_t* assoc,
153
+ const uint8_t* source,
154
+ const char* original_source,
155
+ size_t erb_content_offset,
156
+ attribute_positions_T* positions
157
+ ) {
158
+ pm_location_t name_loc;
159
+ extract_key_name_location(assoc->key, &name_loc);
160
+ compute_position_pair(
161
+ &name_loc,
162
+ source,
163
+ original_source,
164
+ erb_content_offset,
165
+ &positions->name_start,
166
+ &positions->name_end
167
+ );
168
+
169
+ position_T key_end;
170
+ pm_location_t key_end_loc = { .start = assoc->key->location.end, .end = assoc->key->location.end };
171
+ key_end = prism_location_to_position_with_offset(&key_end_loc, original_source, erb_content_offset, source);
172
+
173
+ compute_separator_info(
174
+ assoc,
175
+ source,
176
+ original_source,
177
+ erb_content_offset,
178
+ key_end,
179
+ &positions->separator_string,
180
+ &positions->separator_type,
181
+ &positions->separator_start,
182
+ &positions->separator_end
183
+ );
184
+
185
+ compute_value_positions(
186
+ assoc->value,
187
+ source,
188
+ original_source,
189
+ erb_content_offset,
190
+ &positions->value_start,
191
+ &positions->value_end,
192
+ &positions->content_start,
193
+ &positions->content_end,
194
+ &positions->quoted
195
+ );
196
+ }
197
+
32
198
  static char* build_prefixed_key(const char* prefix, const char* raw_key, hb_allocator_T* allocator) {
33
199
  char* dashed_key = convert_underscores_to_dashes(raw_key);
34
200
  const char* key = dashed_key ? dashed_key : raw_key;
@@ -43,46 +209,125 @@ static char* build_prefixed_key(const char* prefix, const char* raw_key, hb_allo
43
209
  return result;
44
210
  }
45
211
 
212
+ static bool is_static_string_array(pm_array_node_t* array) {
213
+ if (!array || array->elements.size == 0) { return false; }
214
+
215
+ for (size_t i = 0; i < array->elements.size; i++) {
216
+ pm_node_t* element = array->elements.nodes[i];
217
+
218
+ if (element->type != PM_STRING_NODE && element->type != PM_SYMBOL_NODE) { return false; }
219
+ }
220
+
221
+ return true;
222
+ }
223
+
224
+ static char* join_static_string_array(pm_array_node_t* array, hb_allocator_T* allocator) {
225
+ hb_buffer_T buffer;
226
+ hb_buffer_init(&buffer, 64, allocator);
227
+
228
+ for (size_t i = 0; i < array->elements.size; i++) {
229
+ if (i > 0) { hb_buffer_append(&buffer, " "); }
230
+
231
+ char* value = extract_string_from_prism_node(array->elements.nodes[i], allocator);
232
+
233
+ if (value) {
234
+ hb_buffer_append(&buffer, value);
235
+ hb_allocator_dealloc(allocator, value);
236
+ }
237
+ }
238
+
239
+ char* result = hb_allocator_strdup(allocator, hb_buffer_value(&buffer));
240
+
241
+ return result;
242
+ }
243
+
46
244
  static AST_HTML_ATTRIBUTE_NODE_T* create_attribute_from_value(
47
245
  const char* name_string,
48
246
  pm_node_t* value_node,
49
- position_T start_position,
50
- position_T end_position,
51
- hb_allocator_T* allocator
247
+ attribute_positions_T* positions,
248
+ hb_allocator_T* allocator,
249
+ bool is_nested
52
250
  ) {
251
+ if (value_node->type == PM_ARRAY_NODE && !is_nested && is_static_string_array((pm_array_node_t*) value_node)) {
252
+ char* joined = join_static_string_array((pm_array_node_t*) value_node, allocator);
253
+ if (!joined) { return NULL; }
254
+
255
+ AST_HTML_ATTRIBUTE_NODE_T* attribute =
256
+ create_html_attribute_node_precise(name_string, joined, positions, allocator);
257
+ hb_allocator_dealloc(allocator, joined);
258
+
259
+ return attribute;
260
+ }
261
+
53
262
  if (value_node->type == PM_SYMBOL_NODE || value_node->type == PM_STRING_NODE) {
54
263
  char* value_string = extract_string_from_prism_node(value_node, allocator);
55
264
  if (!value_string) { return NULL; }
56
265
 
57
266
  AST_HTML_ATTRIBUTE_NODE_T* attribute =
58
- create_html_attribute_node(name_string, value_string, start_position, end_position, allocator);
267
+ create_html_attribute_node_precise(name_string, value_string, positions, allocator);
59
268
  hb_allocator_dealloc(allocator, value_string);
60
269
 
61
270
  return attribute;
62
271
  } else if (value_node->type == PM_TRUE_NODE) {
63
272
  if (is_boolean_attribute(hb_string((char*) name_string))) {
64
- return create_html_attribute_node(name_string, NULL, start_position, end_position, allocator);
273
+ return create_html_attribute_node_precise(name_string, NULL, positions, allocator);
65
274
  }
66
- return create_html_attribute_node(name_string, "true", start_position, end_position, allocator);
275
+
276
+ return create_html_attribute_node_precise(name_string, "true", positions, allocator);
67
277
  } else if (value_node->type == PM_FALSE_NODE) {
68
278
  if (is_boolean_attribute(hb_string((char*) name_string))) { return NULL; }
69
- return create_html_attribute_node(name_string, "false", start_position, end_position, allocator);
279
+
280
+ return create_html_attribute_node_precise(name_string, "false", positions, allocator);
281
+ } else if (value_node->type == PM_INTEGER_NODE) {
282
+ size_t value_length = value_node->location.end - value_node->location.start;
283
+ char* value_string = hb_allocator_strndup(allocator, (const char*) value_node->location.start, value_length);
284
+ if (!value_string) { return NULL; }
285
+
286
+ AST_HTML_ATTRIBUTE_NODE_T* attribute =
287
+ create_html_attribute_node_precise(name_string, value_string, positions, allocator);
288
+ hb_allocator_dealloc(allocator, value_string);
289
+
290
+ return attribute;
70
291
  } else if (value_node->type == PM_INTERPOLATED_STRING_NODE) {
71
292
  return create_html_attribute_with_interpolated_value(
72
293
  name_string,
73
294
  (pm_interpolated_string_node_t*) value_node,
74
- start_position,
75
- end_position,
295
+ positions->name_start,
296
+ positions->value_end,
76
297
  allocator
77
298
  );
78
299
  } else {
79
300
  size_t value_length = value_node->location.end - value_node->location.start;
80
- char* ruby_content = hb_allocator_strndup(allocator, (const char*) value_node->location.start, value_length);
301
+ char* raw_content = hb_allocator_strndup(allocator, (const char*) value_node->location.start, value_length);
302
+
303
+ if (raw_content && value_node->location.start) {
304
+ char* ruby_content = raw_content;
305
+
306
+ if (!is_nested && strcmp(name_string, "class") == 0
307
+ && (value_node->type == PM_HASH_NODE || value_node->type == PM_ARRAY_NODE)) {
308
+ hb_buffer_T class_buffer;
309
+ hb_buffer_init(&class_buffer, value_length + 16, allocator);
310
+ hb_buffer_append(&class_buffer, "token_list(");
311
+ hb_buffer_append(&class_buffer, raw_content);
312
+ hb_buffer_append(&class_buffer, ")");
313
+
314
+ ruby_content = hb_buffer_value(&class_buffer);
315
+ }
316
+
317
+ // Rails calls .to_json on non-string/symbol values inside data:/aria: hashes
318
+ if (is_nested) {
319
+ hb_buffer_T json_buffer;
320
+ hb_buffer_init(&json_buffer, value_length + 16, allocator);
321
+ hb_buffer_append(&json_buffer, raw_content);
322
+ hb_buffer_append(&json_buffer, ".to_json");
323
+
324
+ ruby_content = hb_buffer_value(&json_buffer);
325
+ }
81
326
 
82
- if (ruby_content && value_node->location.start) {
83
327
  AST_HTML_ATTRIBUTE_NODE_T* attribute =
84
- create_html_attribute_with_ruby_literal(name_string, ruby_content, start_position, end_position, allocator);
85
- hb_allocator_dealloc(allocator, ruby_content);
328
+ create_html_attribute_with_ruby_literal_precise(name_string, ruby_content, positions, allocator);
329
+ hb_allocator_dealloc(allocator, raw_content);
330
+
86
331
  return attribute;
87
332
  }
88
333
 
@@ -90,6 +335,58 @@ static AST_HTML_ATTRIBUTE_NODE_T* create_attribute_from_value(
90
335
  }
91
336
  }
92
337
 
338
+ static const char* get_attribute_name_string(AST_HTML_ATTRIBUTE_NODE_T* attribute) {
339
+ if (!attribute || !attribute->name || !attribute->name->children) { return NULL; }
340
+ if (hb_array_size(attribute->name->children) == 0) { return NULL; }
341
+
342
+ AST_NODE_T* first_child = (AST_NODE_T*) hb_array_get(attribute->name->children, 0);
343
+ if (!first_child || first_child->type != AST_LITERAL_NODE) { return NULL; }
344
+
345
+ AST_LITERAL_NODE_T* literal = (AST_LITERAL_NODE_T*) first_child;
346
+ return (const char*) literal->content.data;
347
+ }
348
+
349
+ void resolve_nonce_attribute(hb_array_T* attributes, hb_allocator_T* allocator) {
350
+ if (!attributes) { return; }
351
+
352
+ for (size_t index = 0; index < hb_array_size(attributes); index++) {
353
+ AST_NODE_T* node = hb_array_get(attributes, index);
354
+ if (!node || node->type != AST_HTML_ATTRIBUTE_NODE) { continue; }
355
+
356
+ AST_HTML_ATTRIBUTE_NODE_T* attribute = (AST_HTML_ATTRIBUTE_NODE_T*) node;
357
+ const char* name = get_attribute_name_string(attribute);
358
+ if (!name || strcmp(name, "nonce") != 0) { continue; }
359
+
360
+ if (!attribute->value || !attribute->value->children) { continue; }
361
+ if (hb_array_size(attribute->value->children) == 0) { continue; }
362
+
363
+ AST_NODE_T* value_child = (AST_NODE_T*) hb_array_get(attribute->value->children, 0);
364
+ if (!value_child || value_child->type != AST_LITERAL_NODE) { continue; }
365
+
366
+ AST_LITERAL_NODE_T* literal = (AST_LITERAL_NODE_T*) value_child;
367
+
368
+ if (hb_string_equals(literal->content, hb_string("true"))) {
369
+ AST_RUBY_LITERAL_NODE_T* ruby_node = ast_ruby_literal_node_init(
370
+ hb_string_from_c_string("content_security_policy_nonce"),
371
+ attribute->value->base.location.start,
372
+ attribute->value->base.location.end,
373
+ hb_array_init(0, allocator),
374
+ allocator
375
+ );
376
+
377
+ hb_array_T* new_children = hb_array_init(1, allocator);
378
+ hb_array_append(new_children, (AST_NODE_T*) ruby_node);
379
+ attribute->value->children = new_children;
380
+ return;
381
+ }
382
+
383
+ if (hb_string_equals(literal->content, hb_string("false"))) {
384
+ hb_array_remove(attributes, index);
385
+ return;
386
+ }
387
+ }
388
+ }
389
+
93
390
  AST_HTML_ATTRIBUTE_NODE_T* extract_html_attribute_from_assoc(
94
391
  pm_assoc_node_t* assoc,
95
392
  const uint8_t* source,
@@ -102,10 +399,37 @@ AST_HTML_ATTRIBUTE_NODE_T* extract_html_attribute_from_assoc(
102
399
  char* name_string = extract_string_from_prism_node(assoc->key, allocator);
103
400
  if (!name_string) { return NULL; }
104
401
 
105
- position_T start_position =
106
- prism_location_to_position_with_offset(&assoc->key->location, original_source, erb_content_offset, source);
107
- position_T end_position =
108
- prism_location_to_position_with_offset(&assoc->value->location, original_source, erb_content_offset, source);
402
+ if (strcmp(name_string, "escape") == 0) {
403
+ hb_allocator_dealloc(allocator, name_string);
404
+ return NULL;
405
+ }
406
+
407
+ if (!assoc->value) {
408
+ hb_allocator_dealloc(allocator, name_string);
409
+ return NULL;
410
+ }
411
+
412
+ if (assoc->value->type == PM_IMPLICIT_NODE) {
413
+ pm_location_t name_loc;
414
+ extract_key_name_location(assoc->key, &name_loc);
415
+ position_T name_start, name_end;
416
+ compute_position_pair(&name_loc, source, original_source, erb_content_offset, &name_start, &name_end);
417
+
418
+ char* dashed_name = convert_underscores_to_dashes(name_string);
419
+
420
+ AST_HTML_ATTRIBUTE_NODE_T* attribute = create_html_attribute_with_ruby_literal(
421
+ dashed_name ? dashed_name : name_string,
422
+ name_string,
423
+ name_start,
424
+ name_start,
425
+ allocator
426
+ );
427
+
428
+ if (dashed_name) { free(dashed_name); }
429
+ hb_allocator_dealloc(allocator, name_string);
430
+
431
+ return attribute;
432
+ }
109
433
 
110
434
  // Rails converts `method:` and `remote:` to `data-*` attributes
111
435
  if (strcmp(name_string, "method") == 0 || strcmp(name_string, "remote") == 0) {
@@ -122,14 +446,12 @@ AST_HTML_ATTRIBUTE_NODE_T* extract_html_attribute_from_assoc(
122
446
  return NULL;
123
447
  }
124
448
 
449
+ attribute_positions_T positions;
450
+ fill_attribute_positions(assoc, source, original_source, erb_content_offset, &positions);
451
+
125
452
  char* dashed_name = convert_underscores_to_dashes(name_string);
126
- AST_HTML_ATTRIBUTE_NODE_T* attribute_node = create_attribute_from_value(
127
- dashed_name ? dashed_name : name_string,
128
- assoc->value,
129
- start_position,
130
- end_position,
131
- allocator
132
- );
453
+ AST_HTML_ATTRIBUTE_NODE_T* attribute_node =
454
+ create_attribute_from_value(dashed_name ? dashed_name : name_string, assoc->value, &positions, allocator, false);
133
455
 
134
456
  if (dashed_name) { free(dashed_name); }
135
457
  hb_allocator_dealloc(allocator, name_string);
@@ -154,25 +476,35 @@ hb_array_T* extract_html_attributes_from_keyword_hash(
154
476
 
155
477
  if (element->type == PM_ASSOC_SPLAT_NODE) {
156
478
  pm_assoc_splat_node_t* splat = (pm_assoc_splat_node_t*) element;
157
- size_t splat_length = splat->base.location.end - splat->base.location.start;
158
- char* splat_content = hb_allocator_strndup(allocator, (const char*) splat->base.location.start, splat_length);
159
-
160
- if (splat_content) {
161
- position_T splat_start =
162
- prism_location_to_position_with_offset(&splat->base.location, original_source, erb_content_offset, source);
163
-
164
- AST_RUBY_HTML_ATTRIBUTES_SPLAT_NODE_T* splat_node = ast_ruby_html_attributes_splat_node_init(
165
- hb_string_from_c_string(splat_content),
166
- HB_STRING_EMPTY,
167
- splat_start,
168
- splat_start,
169
- hb_array_init(0, allocator),
170
- allocator
171
- );
172
479
 
173
- if (splat_node) { hb_array_append(attributes, (AST_NODE_T*) splat_node); }
174
-
175
- hb_allocator_dealloc(allocator, splat_content);
480
+ if (splat->value) {
481
+ size_t value_length = splat->value->location.end - splat->value->location.start;
482
+ char* value_source = hb_allocator_strndup(allocator, (const char*) splat->value->location.start, value_length);
483
+
484
+ if (value_source) {
485
+ hb_buffer_T wrapped;
486
+ hb_buffer_init(&wrapped, value_length + 32, allocator);
487
+ hb_buffer_append(&wrapped, "tag.attributes(**");
488
+ hb_buffer_append(&wrapped, value_source);
489
+ hb_buffer_append(&wrapped, ")");
490
+
491
+ position_T splat_start =
492
+ prism_location_to_position_with_offset(&splat->base.location, original_source, erb_content_offset, source);
493
+
494
+ AST_RUBY_HTML_ATTRIBUTES_SPLAT_NODE_T* splat_node = ast_ruby_html_attributes_splat_node_init(
495
+ hb_string_from_c_string(hb_buffer_value(&wrapped)),
496
+ HB_STRING_EMPTY,
497
+ splat_start,
498
+ splat_start,
499
+ hb_array_init(0, allocator),
500
+ allocator
501
+ );
502
+
503
+ if (splat_node) { hb_array_append(attributes, (AST_NODE_T*) splat_node); }
504
+
505
+ hb_buffer_free(&wrapped);
506
+ hb_allocator_dealloc(allocator, value_source);
507
+ }
176
508
  }
177
509
  } else if (element->type == PM_ASSOC_NODE) {
178
510
  pm_assoc_node_t* assoc = (pm_assoc_node_t*) element;
@@ -188,30 +520,42 @@ hb_array_T* extract_html_attributes_from_keyword_hash(
188
520
 
189
521
  if (hash_element->type == PM_ASSOC_SPLAT_NODE) {
190
522
  pm_assoc_splat_node_t* splat = (pm_assoc_splat_node_t*) hash_element;
191
- size_t splat_length = splat->base.location.end - splat->base.location.start;
192
- char* splat_content =
193
- hb_allocator_strndup(allocator, (const char*) splat->base.location.start, splat_length);
194
-
195
- if (splat_content) {
196
- position_T splat_start = prism_location_to_position_with_offset(
197
- &splat->base.location,
198
- original_source,
199
- erb_content_offset,
200
- source
201
- );
202
-
203
- AST_RUBY_HTML_ATTRIBUTES_SPLAT_NODE_T* splat_node = ast_ruby_html_attributes_splat_node_init(
204
- hb_string_from_c_string(splat_content),
205
- hb_string_from_c_string(key_string),
206
- splat_start,
207
- splat_start,
208
- hb_array_init(0, allocator),
209
- allocator
210
- );
211
-
212
- if (splat_node) { hb_array_append(attributes, (AST_NODE_T*) splat_node); }
213
-
214
- hb_allocator_dealloc(allocator, splat_content);
523
+
524
+ if (splat->value) {
525
+ size_t value_length = splat->value->location.end - splat->value->location.start;
526
+ char* value_source =
527
+ hb_allocator_strndup(allocator, (const char*) splat->value->location.start, value_length);
528
+
529
+ if (value_source) {
530
+ hb_buffer_T wrapped;
531
+ hb_buffer_init(&wrapped, value_length + 48, allocator);
532
+ hb_buffer_append(&wrapped, "tag.attributes(");
533
+ hb_buffer_append(&wrapped, key_string);
534
+ hb_buffer_append(&wrapped, ": ");
535
+ hb_buffer_append(&wrapped, value_source);
536
+ hb_buffer_append(&wrapped, ")");
537
+
538
+ position_T splat_start = prism_location_to_position_with_offset(
539
+ &splat->base.location,
540
+ original_source,
541
+ erb_content_offset,
542
+ source
543
+ );
544
+
545
+ AST_RUBY_HTML_ATTRIBUTES_SPLAT_NODE_T* splat_node = ast_ruby_html_attributes_splat_node_init(
546
+ hb_string_from_c_string(hb_buffer_value(&wrapped)),
547
+ hb_string_from_c_string(key_string),
548
+ splat_start,
549
+ splat_start,
550
+ hb_array_init(0, allocator),
551
+ allocator
552
+ );
553
+
554
+ if (splat_node) { hb_array_append(attributes, (AST_NODE_T*) splat_node); }
555
+
556
+ hb_buffer_free(&wrapped);
557
+ hb_allocator_dealloc(allocator, value_source);
558
+ }
215
559
  }
216
560
 
217
561
  continue;
@@ -227,26 +571,11 @@ hb_array_T* extract_html_attributes_from_keyword_hash(
227
571
  hb_allocator_dealloc(allocator, raw_key);
228
572
 
229
573
  if (attribute_key_string) {
230
- position_T attribute_start = prism_location_to_position_with_offset(
231
- &hash_assoc->key->location,
232
- original_source,
233
- erb_content_offset,
234
- source
235
- );
236
- position_T attribute_end = prism_location_to_position_with_offset(
237
- &hash_assoc->value->location,
238
- original_source,
239
- erb_content_offset,
240
- source
241
- );
242
-
243
- AST_HTML_ATTRIBUTE_NODE_T* attribute = create_attribute_from_value(
244
- attribute_key_string,
245
- hash_assoc->value,
246
- attribute_start,
247
- attribute_end,
248
- allocator
249
- );
574
+ attribute_positions_T hash_positions;
575
+ fill_attribute_positions(hash_assoc, source, original_source, erb_content_offset, &hash_positions);
576
+
577
+ AST_HTML_ATTRIBUTE_NODE_T* attribute =
578
+ create_attribute_from_value(attribute_key_string, hash_assoc->value, &hash_positions, allocator, true);
250
579
 
251
580
  if (attribute) { hb_array_append(attributes, attribute); }
252
581
  hb_allocator_dealloc(allocator, attribute_key_string);