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
@@ -0,0 +1,651 @@
1
+ #include "../include/analyze/strict_locals.h"
2
+ #include "../include/analyze/action_view/tag_helper_node_builders.h"
3
+ #include "../include/analyze/action_view/tag_helpers.h"
4
+ #include "../include/analyze/analyze.h"
5
+ #include "../include/ast/ast_nodes.h"
6
+ #include "../include/errors.h"
7
+ #include "../include/lib/hb_allocator.h"
8
+ #include "../include/lib/hb_array.h"
9
+ #include "../include/lib/hb_buffer.h"
10
+ #include "../include/lib/hb_string.h"
11
+ #include "../include/util/util.h"
12
+ #include "../include/visitor.h"
13
+
14
+ #include "../include/prism/prism_helpers.h"
15
+
16
+ #include <prism.h>
17
+ #include <stdbool.h>
18
+ #include <string.h>
19
+
20
+ #define STRICT_LOCALS_PREFIX "locals:"
21
+ #define SYNTHETIC_PREFIX "def _"
22
+ #define SYNTHETIC_SUFFIX "; end"
23
+
24
+ static bool is_strict_locals_node(const AST_ERB_CONTENT_NODE_T* node) {
25
+ if (!node->tag_opening || !node->content) { return false; }
26
+ if (hb_string_is_empty(node->tag_opening->value)) { return false; }
27
+
28
+ const char* opening = node->tag_opening->value.data;
29
+ if (!opening || !strstr(opening, "#")) { return false; }
30
+
31
+ const char* content = node->content->value.data;
32
+ if (!content) { return false; }
33
+
34
+ while (is_whitespace(*content)) {
35
+ content++;
36
+ }
37
+
38
+ return strncmp(content, STRICT_LOCALS_PREFIX, strlen(STRICT_LOCALS_PREFIX)) == 0;
39
+ }
40
+
41
+ static const char* find_params_open(const char* content) {
42
+ while (is_whitespace(*content)) {
43
+ content++;
44
+ }
45
+
46
+ if (strncmp(content, STRICT_LOCALS_PREFIX, strlen(STRICT_LOCALS_PREFIX)) != 0) { return NULL; }
47
+ content += strlen(STRICT_LOCALS_PREFIX);
48
+
49
+ while (is_whitespace(*content)) {
50
+ content++;
51
+ }
52
+
53
+ if (*content != '(') { return NULL; }
54
+ return content;
55
+ }
56
+
57
+ static const char* find_params_close(const char* opening_paren) {
58
+ int depth = 0;
59
+ const char* cursor = opening_paren;
60
+
61
+ while (*cursor) {
62
+ if (*cursor == '(') {
63
+ depth++;
64
+ } else if (*cursor == ')') {
65
+ depth--;
66
+ if (depth == 0) { return cursor; }
67
+ }
68
+
69
+ cursor++;
70
+ }
71
+
72
+ return NULL;
73
+ }
74
+
75
+ static bool build_synthetic_ruby(
76
+ const char* params_open,
77
+ size_t params_length,
78
+ hb_buffer_T* buffer,
79
+ hb_allocator_T* allocator
80
+ ) {
81
+ size_t capacity = strlen(SYNTHETIC_PREFIX) + params_length + strlen(SYNTHETIC_SUFFIX) + 1;
82
+ if (!hb_buffer_init(buffer, capacity, allocator)) { return false; }
83
+
84
+ hb_buffer_append(buffer, SYNTHETIC_PREFIX);
85
+ hb_buffer_append_with_length(buffer, params_open, params_length);
86
+ hb_buffer_append(buffer, SYNTHETIC_SUFFIX);
87
+
88
+ return true;
89
+ }
90
+
91
+ static pm_parameters_node_t* find_parameters_node(pm_node_t* root) {
92
+ if (!root || root->type != PM_PROGRAM_NODE) { return NULL; }
93
+
94
+ pm_program_node_t* program = (pm_program_node_t*) root;
95
+ if (!program->statements || program->statements->body.size == 0) { return NULL; }
96
+
97
+ pm_node_t* first = program->statements->body.nodes[0];
98
+ if (!first || first->type != PM_DEF_NODE) { return NULL; }
99
+
100
+ pm_def_node_t* def_node = (pm_def_node_t*) first;
101
+ return def_node->parameters;
102
+ }
103
+
104
+ static token_T* create_strict_local_token(
105
+ const uint8_t* prism_node_start,
106
+ const uint8_t* prism_node_end,
107
+ const char* name,
108
+ token_type_T type,
109
+ const char* source,
110
+ size_t erb_content_byte_offset,
111
+ const char* content_bytes,
112
+ const char* params_open,
113
+ const uint8_t* synthetic_start,
114
+ hb_allocator_T* allocator
115
+ ) {
116
+ size_t params_in_content = (size_t) (params_open - content_bytes);
117
+ size_t prism_start_in_synthetic = (size_t) (prism_node_start - synthetic_start);
118
+ size_t prism_end_in_synthetic = (size_t) (prism_node_end - synthetic_start);
119
+ size_t content_start = params_in_content + (prism_start_in_synthetic - strlen(SYNTHETIC_PREFIX));
120
+ size_t content_end = params_in_content + (prism_end_in_synthetic - strlen(SYNTHETIC_PREFIX));
121
+
122
+ position_T start = byte_offset_to_position(source, erb_content_byte_offset + content_start);
123
+ position_T end = byte_offset_to_position(source, erb_content_byte_offset + content_end);
124
+
125
+ return create_synthetic_token(allocator, name, type, start, end);
126
+ }
127
+
128
+ static char* extract_name_from_location(pm_location_t name_location, hb_allocator_T* allocator) {
129
+ size_t length = (size_t) (name_location.end - name_location.start);
130
+
131
+ if (length > 0 && name_location.start[length - 1] == ':') { length--; }
132
+
133
+ return hb_allocator_strndup(allocator, (const char*) name_location.start, length);
134
+ }
135
+
136
+ static hb_array_T* extract_strict_locals(
137
+ pm_parameters_node_t* params,
138
+ const char* source,
139
+ size_t erb_content_byte_offset,
140
+ const char* content_bytes,
141
+ const char* params_open,
142
+ const uint8_t* synthetic_start,
143
+ hb_array_T* errors,
144
+ hb_allocator_T* allocator
145
+ ) {
146
+ hb_array_T* locals = hb_array_init(4, allocator);
147
+
148
+ if (!params) { return locals; }
149
+
150
+ for (size_t index = 0; index < params->optionals.size; index++) {
151
+ pm_node_t* optional = params->optionals.nodes[index];
152
+ if (!optional || optional->type != PM_OPTIONAL_PARAMETER_NODE) { continue; }
153
+
154
+ pm_optional_parameter_node_t* optional_param = (pm_optional_parameter_node_t*) optional;
155
+ pm_location_t location = optional_param->name_loc;
156
+ size_t length = (size_t) (location.end - location.start);
157
+ char* name = hb_allocator_strndup(allocator, (const char*) location.start, length);
158
+
159
+ token_T* name_token = create_strict_local_token(
160
+ location.start,
161
+ location.end,
162
+ name,
163
+ TOKEN_IDENTIFIER,
164
+ source,
165
+ erb_content_byte_offset,
166
+ content_bytes,
167
+ params_open,
168
+ synthetic_start,
169
+ allocator
170
+ );
171
+
172
+ position_T start = name_token ? name_token->location.start : (position_T) { .line = 1, .column = 1 };
173
+ position_T end = name_token ? name_token->location.end : start;
174
+
175
+ size_t value_prism_start = (size_t) (optional_param->value->location.start - synthetic_start);
176
+ size_t value_prism_end = (size_t) (optional_param->value->location.end - synthetic_start);
177
+ size_t params_in_content = (size_t) (params_open - content_bytes);
178
+ size_t value_content_start = params_in_content + (value_prism_start - strlen(SYNTHETIC_PREFIX));
179
+ size_t value_content_end = params_in_content + (value_prism_end - strlen(SYNTHETIC_PREFIX));
180
+
181
+ size_t value_length = value_prism_end - value_prism_start;
182
+ char* value_string =
183
+ hb_allocator_strndup(allocator, (const char*) optional_param->value->location.start, value_length);
184
+
185
+ position_T value_start = byte_offset_to_position(source, erb_content_byte_offset + value_content_start);
186
+ position_T value_end = byte_offset_to_position(source, erb_content_byte_offset + value_content_end);
187
+
188
+ AST_RUBY_LITERAL_NODE_T* value_node = ast_ruby_literal_node_init(
189
+ hb_string_from_c_string(value_string),
190
+ value_start,
191
+ value_end,
192
+ hb_array_init(0, allocator),
193
+ allocator
194
+ );
195
+
196
+ hb_array_T* node_errors = hb_array_init(1, allocator);
197
+ append_strict_locals_positional_argument_error(hb_string_from_c_string(name), start, end, allocator, node_errors);
198
+
199
+ AST_RUBY_STRICT_LOCAL_NODE_T* local =
200
+ ast_ruby_strict_local_node_init(name_token, value_node, false, false, start, value_end, node_errors, allocator);
201
+
202
+ hb_array_append(locals, local);
203
+ hb_allocator_dealloc(allocator, name);
204
+ hb_allocator_dealloc(allocator, value_string);
205
+ }
206
+
207
+ for (size_t index = 0; index < params->requireds.size; index++) {
208
+ pm_node_t* required = params->requireds.nodes[index];
209
+ if (!required) { continue; }
210
+
211
+ pm_location_t location = required->location;
212
+ size_t length = (size_t) (location.end - location.start);
213
+ char* name = hb_allocator_strndup(allocator, (const char*) location.start, length);
214
+
215
+ token_T* name_token = create_strict_local_token(
216
+ location.start,
217
+ location.end,
218
+ name,
219
+ TOKEN_IDENTIFIER,
220
+ source,
221
+ erb_content_byte_offset,
222
+ content_bytes,
223
+ params_open,
224
+ synthetic_start,
225
+ allocator
226
+ );
227
+
228
+ position_T start = name_token ? name_token->location.start : (position_T) { .line = 1, .column = 1 };
229
+ position_T end = name_token ? name_token->location.end : start;
230
+
231
+ hb_array_T* node_errors = hb_array_init(1, allocator);
232
+ append_strict_locals_positional_argument_error(hb_string_from_c_string(name), start, end, allocator, node_errors);
233
+
234
+ AST_RUBY_STRICT_LOCAL_NODE_T* local =
235
+ ast_ruby_strict_local_node_init(name_token, NULL, true, false, start, end, node_errors, allocator);
236
+
237
+ hb_array_append(locals, local);
238
+ hb_allocator_dealloc(allocator, name);
239
+ }
240
+
241
+ if (params->block) {
242
+ pm_block_parameter_node_t* block_param = params->block;
243
+
244
+ if (block_param->name) {
245
+ size_t length = (size_t) (block_param->name_loc.end - block_param->name_loc.start);
246
+ char* name = hb_allocator_strndup(allocator, (const char*) block_param->name_loc.start, length);
247
+
248
+ token_T* name_token = create_strict_local_token(
249
+ block_param->name_loc.start,
250
+ block_param->name_loc.end,
251
+ name,
252
+ TOKEN_IDENTIFIER,
253
+ source,
254
+ erb_content_byte_offset,
255
+ content_bytes,
256
+ params_open,
257
+ synthetic_start,
258
+ allocator
259
+ );
260
+
261
+ position_T start = name_token ? name_token->location.start : (position_T) { .line = 1, .column = 1 };
262
+ position_T end = name_token ? name_token->location.end : start;
263
+
264
+ hb_array_T* node_errors = hb_array_init(1, allocator);
265
+ append_strict_locals_block_argument_error(hb_string_from_c_string(name), start, end, allocator, node_errors);
266
+
267
+ AST_RUBY_STRICT_LOCAL_NODE_T* local =
268
+ ast_ruby_strict_local_node_init(name_token, NULL, false, false, start, end, node_errors, allocator);
269
+
270
+ hb_array_append(locals, local);
271
+ hb_allocator_dealloc(allocator, name);
272
+ }
273
+ }
274
+
275
+ if (params->rest && params->rest->type == PM_REST_PARAMETER_NODE) {
276
+ pm_rest_parameter_node_t* splat_param = (pm_rest_parameter_node_t*) params->rest;
277
+
278
+ if (splat_param->name) {
279
+ size_t length = (size_t) (splat_param->name_loc.end - splat_param->name_loc.start);
280
+ char* name = hb_allocator_strndup(allocator, (const char*) splat_param->name_loc.start, length);
281
+
282
+ token_T* name_token = create_strict_local_token(
283
+ splat_param->name_loc.start,
284
+ splat_param->name_loc.end,
285
+ name,
286
+ TOKEN_IDENTIFIER,
287
+ source,
288
+ erb_content_byte_offset,
289
+ content_bytes,
290
+ params_open,
291
+ synthetic_start,
292
+ allocator
293
+ );
294
+
295
+ position_T start = name_token ? name_token->location.start : (position_T) { .line = 1, .column = 1 };
296
+ position_T end = name_token ? name_token->location.end : start;
297
+
298
+ hb_array_T* node_errors = hb_array_init(1, allocator);
299
+ append_strict_locals_splat_argument_error(hb_string_from_c_string(name), start, end, allocator, node_errors);
300
+
301
+ AST_RUBY_STRICT_LOCAL_NODE_T* local =
302
+ ast_ruby_strict_local_node_init(name_token, NULL, false, false, start, end, node_errors, allocator);
303
+
304
+ hb_array_append(locals, local);
305
+ hb_allocator_dealloc(allocator, name);
306
+ }
307
+ }
308
+
309
+ for (size_t index = 0; index < params->keywords.size; index++) {
310
+ pm_node_t* keyword_node = params->keywords.nodes[index];
311
+
312
+ if (keyword_node->type == PM_REQUIRED_KEYWORD_PARAMETER_NODE) {
313
+ pm_required_keyword_parameter_node_t* keyword_param = (pm_required_keyword_parameter_node_t*) keyword_node;
314
+
315
+ char* name = extract_name_from_location(keyword_param->name_loc, allocator);
316
+ if (!name) { continue; }
317
+
318
+ token_T* name_token = create_strict_local_token(
319
+ keyword_param->name_loc.start,
320
+ keyword_param->name_loc.end,
321
+ name,
322
+ TOKEN_IDENTIFIER,
323
+ source,
324
+ erb_content_byte_offset,
325
+ content_bytes,
326
+ params_open,
327
+ synthetic_start,
328
+ allocator
329
+ );
330
+
331
+ position_T start = name_token ? name_token->location.start : (position_T) { .line = 1, .column = 1 };
332
+ position_T end = name_token ? name_token->location.end : start;
333
+
334
+ AST_RUBY_STRICT_LOCAL_NODE_T* local = ast_ruby_strict_local_node_init(
335
+ name_token,
336
+ NULL,
337
+ true,
338
+ false,
339
+ start,
340
+ end,
341
+ hb_array_init(0, allocator),
342
+ allocator
343
+ );
344
+
345
+ hb_array_append(locals, local);
346
+ hb_allocator_dealloc(allocator, name);
347
+
348
+ } else if (keyword_node->type == PM_OPTIONAL_KEYWORD_PARAMETER_NODE) {
349
+ pm_optional_keyword_parameter_node_t* keyword_param = (pm_optional_keyword_parameter_node_t*) keyword_node;
350
+
351
+ char* name = extract_name_from_location(keyword_param->name_loc, allocator);
352
+ if (!name) { continue; }
353
+
354
+ size_t value_prism_start = (size_t) (keyword_param->value->location.start - synthetic_start);
355
+ size_t value_prism_end = (size_t) (keyword_param->value->location.end - synthetic_start);
356
+ size_t params_in_content = (size_t) (params_open - content_bytes);
357
+ size_t value_content_start = params_in_content + (value_prism_start - strlen(SYNTHETIC_PREFIX));
358
+ size_t value_content_end = params_in_content + (value_prism_end - strlen(SYNTHETIC_PREFIX));
359
+
360
+ size_t value_length = value_prism_end - value_prism_start;
361
+ char* value_string =
362
+ hb_allocator_strndup(allocator, (const char*) keyword_param->value->location.start, value_length);
363
+
364
+ position_T value_start = byte_offset_to_position(source, erb_content_byte_offset + value_content_start);
365
+ position_T value_end = byte_offset_to_position(source, erb_content_byte_offset + value_content_end);
366
+
367
+ AST_RUBY_LITERAL_NODE_T* value_node = ast_ruby_literal_node_init(
368
+ hb_string_from_c_string(value_string),
369
+ value_start,
370
+ value_end,
371
+ hb_array_init(0, allocator),
372
+ allocator
373
+ );
374
+
375
+ token_T* name_token = create_strict_local_token(
376
+ keyword_param->name_loc.start,
377
+ keyword_param->name_loc.end,
378
+ name,
379
+ TOKEN_IDENTIFIER,
380
+ source,
381
+ erb_content_byte_offset,
382
+ content_bytes,
383
+ params_open,
384
+ synthetic_start,
385
+ allocator
386
+ );
387
+
388
+ position_T start = name_token ? name_token->location.start : (position_T) { .line = 1, .column = 1 };
389
+
390
+ AST_RUBY_STRICT_LOCAL_NODE_T* local = ast_ruby_strict_local_node_init(
391
+ name_token,
392
+ value_node,
393
+ false,
394
+ false,
395
+ start,
396
+ value_end,
397
+ hb_array_init(0, allocator),
398
+ allocator
399
+ );
400
+
401
+ hb_array_append(locals, local);
402
+ hb_allocator_dealloc(allocator, name);
403
+ hb_allocator_dealloc(allocator, value_string);
404
+ }
405
+ }
406
+
407
+ if (params->keyword_rest && params->keyword_rest->type == PM_KEYWORD_REST_PARAMETER_NODE) {
408
+ pm_keyword_rest_parameter_node_t* keyword_rest_param = (pm_keyword_rest_parameter_node_t*) params->keyword_rest;
409
+
410
+ if (keyword_rest_param->name) {
411
+ char* name = extract_name_from_location(keyword_rest_param->name_loc, allocator);
412
+ if (!name) { return locals; }
413
+
414
+ token_T* name_token = create_strict_local_token(
415
+ keyword_rest_param->name_loc.start,
416
+ keyword_rest_param->name_loc.end,
417
+ name,
418
+ TOKEN_IDENTIFIER,
419
+ source,
420
+ erb_content_byte_offset,
421
+ content_bytes,
422
+ params_open,
423
+ synthetic_start,
424
+ allocator
425
+ );
426
+
427
+ position_T start = name_token ? name_token->location.start : (position_T) { .line = 1, .column = 1 };
428
+ position_T end = name_token ? name_token->location.end : start;
429
+
430
+ AST_RUBY_STRICT_LOCAL_NODE_T* local = ast_ruby_strict_local_node_init(
431
+ name_token,
432
+ NULL,
433
+ false,
434
+ true,
435
+ start,
436
+ end,
437
+ hb_array_init(0, allocator),
438
+ allocator
439
+ );
440
+
441
+ hb_array_append(locals, local);
442
+ hb_allocator_dealloc(allocator, name);
443
+ } else {
444
+ size_t params_in_content = (size_t) (params_open - content_bytes);
445
+ size_t operator_prism_start = (size_t) (keyword_rest_param->operator_loc.start - synthetic_start);
446
+ size_t operator_prism_end = (size_t) (keyword_rest_param->operator_loc.end - synthetic_start);
447
+ size_t content_start = params_in_content + (operator_prism_start - strlen(SYNTHETIC_PREFIX));
448
+ size_t content_end = params_in_content + (operator_prism_end - strlen(SYNTHETIC_PREFIX));
449
+
450
+ position_T start = byte_offset_to_position(source, erb_content_byte_offset + content_start);
451
+ position_T end = byte_offset_to_position(source, erb_content_byte_offset + content_end);
452
+
453
+ AST_RUBY_STRICT_LOCAL_NODE_T* local =
454
+ ast_ruby_strict_local_node_init(NULL, NULL, false, true, start, end, hb_array_init(0, allocator), allocator);
455
+
456
+ hb_array_append(locals, local);
457
+ }
458
+ }
459
+
460
+ return locals;
461
+ }
462
+
463
+ static AST_ERB_STRICT_LOCALS_NODE_T* create_strict_locals_node(
464
+ AST_ERB_CONTENT_NODE_T* erb_node,
465
+ const char* source,
466
+ hb_allocator_T* allocator
467
+ ) {
468
+ const char* content_bytes = erb_node->content->value.data;
469
+ const char* params_open = find_params_open(content_bytes);
470
+
471
+ if (!params_open) {
472
+ hb_array_T* locals = hb_array_init(0, allocator);
473
+ hb_array_T* errors = hb_array_init(0, allocator);
474
+
475
+ const char* after_prefix = content_bytes;
476
+ while (is_whitespace(*after_prefix)) {
477
+ after_prefix++;
478
+ }
479
+ after_prefix += strlen(STRICT_LOCALS_PREFIX);
480
+ while (is_whitespace(*after_prefix)) {
481
+ after_prefix++;
482
+ }
483
+
484
+ size_t content_length = (size_t) erb_node->content->value.length;
485
+ size_t after_prefix_offset = (size_t) (after_prefix - content_bytes);
486
+ size_t rest_length = after_prefix_offset < content_length ? content_length - after_prefix_offset : 0;
487
+
488
+ while (rest_length > 0 && is_whitespace(after_prefix[rest_length - 1])) {
489
+ rest_length--;
490
+ }
491
+
492
+ char* rest = hb_allocator_strndup(allocator, after_prefix, rest_length);
493
+ size_t erb_content_byte_offset = calculate_byte_offset_from_position(source, erb_node->content->location.start);
494
+
495
+ position_T error_start = byte_offset_to_position(source, erb_content_byte_offset + after_prefix_offset);
496
+ position_T error_end = byte_offset_to_position(source, erb_content_byte_offset + after_prefix_offset + rest_length);
497
+
498
+ append_strict_locals_missing_parenthesis_error(
499
+ hb_string_from_c_string(rest),
500
+ error_start,
501
+ error_end,
502
+ allocator,
503
+ errors
504
+ );
505
+ hb_allocator_dealloc(allocator, rest);
506
+
507
+ return ast_erb_strict_locals_node_init(
508
+ erb_node->tag_opening,
509
+ erb_node->content,
510
+ erb_node->tag_closing,
511
+ erb_node->analyzed_ruby,
512
+ erb_node->prism_node,
513
+ locals,
514
+ erb_node->base.location.start,
515
+ erb_node->base.location.end,
516
+ errors,
517
+ allocator
518
+ );
519
+ }
520
+
521
+ const char* params_close = find_params_close(params_open);
522
+
523
+ size_t content_length = (size_t) erb_node->content->value.length;
524
+ size_t params_open_offset = (size_t) (params_open - content_bytes);
525
+
526
+ size_t params_length = params_close ? (size_t) (params_close - params_open) + 1 : content_length - params_open_offset;
527
+
528
+ hb_buffer_T synthetic_buffer;
529
+ if (!build_synthetic_ruby(params_open, params_length, &synthetic_buffer, allocator)) { return NULL; }
530
+
531
+ pm_parser_t parser;
532
+ pm_options_t options = { 0 };
533
+ pm_parser_init(
534
+ &parser,
535
+ (const uint8_t*) hb_buffer_value(&synthetic_buffer),
536
+ hb_buffer_length(&synthetic_buffer),
537
+ &options
538
+ );
539
+ pm_node_t* root = pm_parse(&parser);
540
+
541
+ const uint8_t* synthetic_start = parser.start;
542
+
543
+ pm_parameters_node_t* params_node = find_parameters_node(root);
544
+ size_t erb_content_byte_offset = calculate_byte_offset_from_position(source, erb_node->content->location.start);
545
+ hb_array_T* errors = hb_array_init(0, allocator);
546
+
547
+ size_t params_in_content = (size_t) (params_open - content_bytes);
548
+ size_t prefix_length = strlen(SYNTHETIC_PREFIX);
549
+
550
+ for (const pm_diagnostic_t* error = (const pm_diagnostic_t*) parser.error_list.head; error != NULL;
551
+ error = (const pm_diagnostic_t*) error->node.next) {
552
+ if (error->diag_id == PM_ERR_DEF_TERM) { continue; }
553
+
554
+ size_t error_start_in_synthetic = (size_t) (error->location.start - synthetic_start);
555
+ size_t error_end_in_synthetic = (size_t) (error->location.end - synthetic_start);
556
+
557
+ size_t error_content_start =
558
+ params_in_content + (error_start_in_synthetic > prefix_length ? error_start_in_synthetic - prefix_length : 0);
559
+ size_t error_content_end =
560
+ params_in_content + (error_end_in_synthetic > prefix_length ? error_end_in_synthetic - prefix_length : 0);
561
+
562
+ position_T error_start = byte_offset_to_position(source, erb_content_byte_offset + error_content_start);
563
+ position_T error_end = byte_offset_to_position(source, erb_content_byte_offset + error_content_end);
564
+
565
+ RUBY_PARSE_ERROR_T* parse_error =
566
+ ruby_parse_error_from_prism_error_with_positions(error, error_start, error_end, allocator);
567
+
568
+ hb_array_append(errors, parse_error);
569
+ }
570
+
571
+ hb_array_T* locals;
572
+
573
+ if (hb_array_size(errors) > 0) {
574
+ locals = hb_array_init(0, allocator);
575
+ } else {
576
+ locals = extract_strict_locals(
577
+ params_node,
578
+ source,
579
+ erb_content_byte_offset,
580
+ content_bytes,
581
+ params_open,
582
+ synthetic_start,
583
+ errors,
584
+ allocator
585
+ );
586
+ }
587
+
588
+ pm_node_destroy(&parser, root);
589
+ pm_parser_free(&parser);
590
+ pm_options_free(&options);
591
+ hb_buffer_free(&synthetic_buffer);
592
+
593
+ return ast_erb_strict_locals_node_init(
594
+ erb_node->tag_opening,
595
+ erb_node->content,
596
+ erb_node->tag_closing,
597
+ erb_node->analyzed_ruby,
598
+ erb_node->prism_node,
599
+ locals,
600
+ erb_node->base.location.start,
601
+ erb_node->base.location.end,
602
+ errors,
603
+ allocator
604
+ );
605
+ }
606
+
607
+ static void transform_strict_locals_in_array(hb_array_T* array, analyze_ruby_context_T* context) {
608
+ if (!array) { return; }
609
+
610
+ for (size_t index = 0; index < hb_array_size(array); index++) {
611
+ AST_NODE_T* child = hb_array_get(array, index);
612
+ if (!child || child->type != AST_ERB_CONTENT_NODE) { continue; }
613
+
614
+ AST_ERB_CONTENT_NODE_T* erb_node = (AST_ERB_CONTENT_NODE_T*) child;
615
+
616
+ if (!is_strict_locals_node(erb_node)) { continue; }
617
+
618
+ AST_ERB_STRICT_LOCALS_NODE_T* strict_locals_node =
619
+ create_strict_locals_node(erb_node, context->source, context->allocator);
620
+
621
+ if (!strict_locals_node) { continue; }
622
+
623
+ if (context->found_strict_locals) {
624
+ append_strict_locals_duplicate_declaration_error(
625
+ strict_locals_node->base.location.start,
626
+ strict_locals_node->base.location.end,
627
+ context->allocator,
628
+ strict_locals_node->base.errors
629
+ );
630
+ }
631
+
632
+ context->found_strict_locals = true;
633
+ hb_array_set(array, index, strict_locals_node);
634
+ }
635
+ }
636
+
637
+ static void transform_strict_locals_in_node(const AST_NODE_T* node, analyze_ruby_context_T* context) {
638
+ if (!node || !context) { return; }
639
+
640
+ transform_strict_locals_in_array(get_node_children_array(node), context);
641
+ }
642
+
643
+ bool transform_strict_locals_nodes(const AST_NODE_T* node, void* data) {
644
+ analyze_ruby_context_T* context = (analyze_ruby_context_T*) data;
645
+
646
+ transform_strict_locals_in_node(node, context);
647
+
648
+ herb_visit_child_nodes(node, transform_strict_locals_nodes, data);
649
+
650
+ return false;
651
+ }
@@ -204,6 +204,13 @@ bool transform_erb_nodes(const AST_NODE_T* node, void* data) {
204
204
  hb_array_free(&old_array);
205
205
  }
206
206
 
207
+ if (node->type == AST_ERB_STRICT_LOCALS_NODE) {
208
+ AST_ERB_STRICT_LOCALS_NODE_T* erb_strict_locals_node = (AST_ERB_STRICT_LOCALS_NODE_T*) node;
209
+ hb_array_T* old_array = erb_strict_locals_node->locals;
210
+ erb_strict_locals_node->locals = rewrite_node_array((AST_NODE_T*) node, erb_strict_locals_node->locals, context);
211
+ hb_array_free(&old_array);
212
+ }
213
+
207
214
  if (node->type == AST_ERB_IN_NODE) {
208
215
  AST_ERB_IN_NODE_T* erb_in_node = (AST_ERB_IN_NODE_T*) node;
209
216
  hb_array_T* old_array = erb_in_node->statements;
@@ -1,11 +1,11 @@
1
- #include "include/ast_node.h"
2
- #include "include/ast_nodes.h"
3
- #include "include/errors.h"
4
- #include "include/position.h"
5
- #include "include/util.h"
6
- #include "include/util/hb_allocator.h"
7
- #include "include/util/hb_string.h"
8
- #include "include/visitor.h"
1
+ #include "../include/ast/ast_node.h"
2
+ #include "../include/ast/ast_nodes.h"
3
+ #include "../include/errors.h"
4
+ #include "../include/lib/hb_allocator.h"
5
+ #include "../include/lib/hb_string.h"
6
+ #include "../include/location/position.h"
7
+ #include "../include/util/util.h"
8
+ #include "../include/visitor.h"
9
9
 
10
10
  #include <prism.h>
11
11
  #include <stdio.h>