herb 0.11.1 → 0.11.2

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 (247) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +4 -2
  3. data/config.yml +6 -0
  4. data/ext/herb/extconf.rb +8 -31
  5. data/ext/herb/nodes.c +75 -75
  6. data/ext/herb/parser_options_helpers.c +26 -0
  7. data/lib/herb/analysis/partial_index.rb +1 -1
  8. data/lib/herb/analysis/partial_resolution.rb +27 -0
  9. data/lib/herb/ast/helpers.rb +1 -1
  10. data/lib/herb/configuration.rb +11 -1
  11. data/lib/herb/dev/classifier.rb +1 -1
  12. data/lib/herb/engine/compiler.rb +5 -8
  13. data/lib/herb/engine/slots/dynamics_compiler.rb +53 -14
  14. data/lib/herb/engine/slots/subtree_compiler.rb +1 -1
  15. data/lib/herb/engine/slots/visitor.rb +32 -4
  16. data/lib/herb/engine/visitors/instrumentation_visitor.rb +18 -6
  17. data/lib/herb/parser_options.rb +6 -3
  18. data/lib/herb/project.rb +19 -8
  19. data/lib/herb/version.rb +1 -1
  20. data/lib/herb.rb +1 -1
  21. data/sig/herb/analysis/partial_resolution.rbs +8 -0
  22. data/sig/herb/configuration.rbs +3 -0
  23. data/sig/herb/engine/slots/dynamics_compiler.rbs +24 -7
  24. data/sig/herb/engine/slots/visitor.rbs +14 -0
  25. data/sig/herb/engine/visitors/instrumentation_visitor.rbs +5 -1
  26. data/sig/herb/parser_options.rbs +6 -4
  27. data/sig/herb.rbs +2 -2
  28. data/src/analyze/action_view/attribute_extraction_helpers.c +40 -21
  29. data/src/analyze/action_view/generated_handlers.c +36 -36
  30. data/src/analyze/action_view/image_tag.c +7 -3
  31. data/src/analyze/action_view/javascript_include_tag.c +6 -3
  32. data/src/analyze/action_view/link_to.c +23 -23
  33. data/src/analyze/action_view/registry.c +7 -3
  34. data/src/analyze/action_view/tag_helper_node_builders.c +6 -13
  35. data/src/analyze/action_view/tag_helpers.c +139 -90
  36. data/src/analyze/action_view/turbo_frame_tag.c +5 -4
  37. data/src/analyze/analyze.c +35 -19
  38. data/src/analyze/analyze_helpers.c +161 -188
  39. data/src/analyze/analyzed_ruby.c +8 -7
  40. data/src/analyze/control_type.c +29 -22
  41. data/src/analyze/herb_directives.c +16 -20
  42. data/src/analyze/iteration_nodes.c +69 -24
  43. data/src/analyze/parse_errors.c +32 -22
  44. data/src/analyze/postfix_conditionals.c +23 -13
  45. data/src/analyze/prism_annotate.c +29 -28
  46. data/src/analyze/render_nodes.c +51 -41
  47. data/src/analyze/strict_locals.c +14 -15
  48. data/src/analyze/ternary_conditionals.c +14 -10
  49. data/src/herb.c +10 -8
  50. data/src/include/analyze/action_view/tag_helper_handler.h +1 -1
  51. data/src/include/analyze/action_view/tag_helper_node_builders.h +1 -0
  52. data/src/include/analyze/action_view/tag_helpers.h +1 -1
  53. data/src/include/analyze/analyze.h +3 -2
  54. data/src/include/analyze/analyzed_ruby.h +4 -2
  55. data/src/include/analyze/helpers.h +6 -6
  56. data/src/include/herb.h +3 -2
  57. data/src/include/parser/parser_options.h +5 -0
  58. data/src/include/prism/prism_context.h +12 -12
  59. data/src/include/prism/prism_helpers.h +30 -0
  60. data/src/include/prism_version.h +11 -0
  61. data/src/include/version.h +1 -1
  62. data/src/parser/parser_helpers.c +1 -2
  63. data/src/parser.c +6 -7
  64. data/src/parser_options_defaults.c +1 -0
  65. data/src/prism/prism_helpers.c +84 -50
  66. data/src/prism/ruby_parser.c +20 -20
  67. data/templates/ext/herb/nodes.c.erb +5 -5
  68. data/templates/ext/herb/parser_options_helpers.c.erb +33 -0
  69. data/templates/java/nodes.c.erb +6 -6
  70. data/templates/java/parser_options_helpers.c.erb +41 -2
  71. data/templates/java/parser_options_helpers.h.erb +2 -1
  72. data/templates/javascript/packages/node/extension/nodes.cpp.erb +5 -5
  73. data/templates/javascript/packages/node/extension/parser_options_helpers.cpp.erb +34 -2
  74. data/templates/javascript/packages/node/extension/parser_options_helpers.h.erb +1 -1
  75. data/templates/lib/herb/parser_options.rb.erb +1 -1
  76. data/templates/rust/src/prism/generated_deserialize.rs.erb +90 -44
  77. data/templates/rust/src/prism/generated_prettyprint.rs.erb +18 -27
  78. data/templates/src/analyze/action_view/generated_handlers.c.erb +23 -22
  79. data/templates/src/include/parser/parser_options.h.erb +10 -0
  80. data/templates/src/include/prism_version.h.erb +17 -0
  81. data/templates/src/parser_options_defaults.c.erb +1 -1
  82. data/templates/template.rb +17 -2
  83. data/templates/wasm/nodes.cpp.erb +5 -5
  84. data/templates/wasm/parser_options_helpers.cpp.erb +24 -0
  85. data/vendor/prism/Rakefile +76 -7
  86. data/vendor/prism/config.yml +174 -161
  87. data/vendor/prism/include/prism/arena.h +45 -0
  88. data/vendor/prism/include/prism/arena.hpp +42 -0
  89. data/vendor/prism/include/prism/ast.h +2738 -611
  90. data/vendor/prism/include/prism/ast.hpp +641 -0
  91. data/vendor/prism/include/prism/buffer.h +60 -0
  92. data/vendor/prism/include/prism/buffer.hpp +55 -0
  93. data/vendor/prism/include/prism/comments.h +72 -0
  94. data/vendor/prism/include/prism/compiler/accel.h +26 -0
  95. data/vendor/prism/include/prism/compiler/align.h +36 -0
  96. data/vendor/prism/include/prism/compiler/assume.h +25 -0
  97. data/vendor/prism/include/prism/compiler/branch.h +26 -0
  98. data/vendor/prism/include/prism/compiler/bswap.h +46 -0
  99. data/vendor/prism/include/prism/compiler/cplusplus.hpp +16 -0
  100. data/vendor/prism/include/prism/compiler/endianness.h +34 -0
  101. data/vendor/prism/include/prism/compiler/exceptions.hpp +17 -0
  102. data/vendor/prism/include/prism/compiler/exported.h +24 -0
  103. data/vendor/prism/include/prism/compiler/fallthrough.h +22 -0
  104. data/vendor/prism/include/prism/compiler/filesystem.h +32 -0
  105. data/vendor/prism/include/prism/compiler/flex_array.h +19 -0
  106. data/vendor/prism/include/prism/compiler/force_inline.h +21 -0
  107. data/vendor/prism/include/prism/compiler/format.h +27 -0
  108. data/vendor/prism/include/prism/compiler/inline.h +17 -0
  109. data/vendor/prism/include/prism/compiler/lifetimebound.hpp +25 -0
  110. data/vendor/prism/include/prism/compiler/nodiscard.h +22 -0
  111. data/vendor/prism/include/prism/compiler/nonnull.h +18 -0
  112. data/vendor/prism/include/prism/compiler/unused.h +18 -0
  113. data/vendor/prism/include/prism/constant_pool.h +106 -0
  114. data/vendor/prism/include/prism/diagnostic.h +55 -402
  115. data/vendor/prism/include/prism/errors_format.h +61 -0
  116. data/vendor/prism/include/prism/excludes.h +29 -0
  117. data/vendor/prism/include/prism/integer.h +41 -0
  118. data/vendor/prism/include/prism/internal/allocator.h +68 -0
  119. data/vendor/prism/include/prism/internal/allocator_debug.h +88 -0
  120. data/vendor/prism/include/prism/internal/arena.h +108 -0
  121. data/vendor/prism/include/prism/internal/bit.h +42 -0
  122. data/vendor/prism/include/prism/internal/buffer.h +94 -0
  123. data/vendor/prism/include/prism/internal/char.h +145 -0
  124. data/vendor/prism/include/prism/internal/comments.h +54 -0
  125. data/vendor/prism/include/prism/internal/constant_pool.h +152 -0
  126. data/vendor/prism/include/prism/internal/diagnostic.h +389 -0
  127. data/vendor/prism/include/prism/{encoding.h → internal/encoding.h} +27 -68
  128. data/vendor/prism/include/prism/internal/integer.h +68 -0
  129. data/vendor/prism/include/prism/internal/isinf.h +16 -0
  130. data/vendor/prism/include/prism/internal/line_offset_list.h +34 -0
  131. data/vendor/prism/include/prism/{util/pm_list.h → internal/list.h} +11 -52
  132. data/vendor/prism/include/prism/internal/magic_comments.h +23 -0
  133. data/vendor/prism/include/prism/internal/memchr.h +15 -0
  134. data/vendor/prism/include/prism/internal/node.h +42 -0
  135. data/vendor/prism/include/prism/internal/options.h +232 -0
  136. data/vendor/prism/include/prism/internal/parser.h +1015 -0
  137. data/vendor/prism/include/prism/internal/regexp.h +26 -0
  138. data/vendor/prism/include/prism/internal/serialize.h +39 -0
  139. data/vendor/prism/include/prism/internal/source.h +72 -0
  140. data/vendor/prism/include/prism/internal/static_literals.h +60 -0
  141. data/vendor/prism/include/prism/internal/stringy.h +30 -0
  142. data/vendor/prism/include/prism/{util/pm_strncasecmp.h → internal/strncasecmp.h} +3 -17
  143. data/vendor/prism/include/prism/{util/pm_strpbrk.h → internal/strpbrk.h} +7 -20
  144. data/vendor/prism/include/prism/internal/tokens.h +11 -0
  145. data/vendor/prism/include/prism/internal/xxh3.h +16 -0
  146. data/vendor/prism/include/prism/json.h +40 -0
  147. data/vendor/prism/include/prism/line_offset_list.h +69 -0
  148. data/vendor/prism/include/prism/magic_comments.h +53 -0
  149. data/vendor/prism/include/prism/node.h +59 -49
  150. data/vendor/prism/include/prism/node.hpp +272 -0
  151. data/vendor/prism/include/prism/node_type.hpp +38 -0
  152. data/vendor/prism/include/prism/options.h +154 -279
  153. data/vendor/prism/include/prism/options.hpp +43 -0
  154. data/vendor/prism/include/prism/parser.h +367 -870
  155. data/vendor/prism/include/prism/parser.hpp +274 -0
  156. data/vendor/prism/include/prism/prettyprint.h +14 -9
  157. data/vendor/prism/include/prism/serialize.h +123 -0
  158. data/vendor/prism/include/prism/source.h +163 -0
  159. data/vendor/prism/include/prism/stream.h +36 -0
  160. data/vendor/prism/include/prism/string_query.h +82 -0
  161. data/vendor/prism/include/prism/stringy.h +61 -0
  162. data/vendor/prism/include/prism/stringy.hpp +31 -0
  163. data/vendor/prism/include/prism/version.h +20 -3
  164. data/vendor/prism/include/prism.h +35 -320
  165. data/vendor/prism/include/prism.hpp +24 -0
  166. data/vendor/prism/src/arena.c +122 -0
  167. data/vendor/prism/src/{util/pm_buffer.c → buffer.c} +53 -21
  168. data/vendor/prism/src/{util/pm_char.c → char.c} +28 -57
  169. data/vendor/prism/src/comments.c +455 -0
  170. data/vendor/prism/src/constant_pool.c +406 -0
  171. data/vendor/prism/src/diagnostic.c +120 -68
  172. data/vendor/prism/src/encoding.c +8 -3
  173. data/vendor/prism/src/errors_format.c +519 -0
  174. data/vendor/prism/src/{util/pm_integer.c → integer.c} +38 -22
  175. data/vendor/prism/src/json.c +5733 -0
  176. data/vendor/prism/src/line_offset_list.c +100 -0
  177. data/vendor/prism/src/list.c +24 -0
  178. data/vendor/prism/src/{util/pm_memchr.c → memchr.c} +8 -6
  179. data/vendor/prism/src/node.c +3465 -7787
  180. data/vendor/prism/src/options.c +161 -54
  181. data/vendor/prism/src/parser.c +371 -0
  182. data/vendor/prism/src/prettyprint.c +421 -354
  183. data/vendor/prism/src/prism.c +8522 -7280
  184. data/vendor/prism/src/regexp.c +960 -45
  185. data/vendor/prism/src/serialize.c +2434 -2048
  186. data/vendor/prism/src/source.c +524 -0
  187. data/vendor/prism/src/static_literals.c +303 -360
  188. data/vendor/prism/src/string_query.c +180 -0
  189. data/vendor/prism/src/stringy.c +61 -0
  190. data/vendor/prism/src/{util/pm_strncasecmp.c → strncasecmp.c} +5 -4
  191. data/vendor/prism/src/strpbrk.c +447 -0
  192. data/vendor/prism/src/{token_type.c → tokens.c} +39 -18
  193. data/vendor/prism/src/xxh3.c +292 -0
  194. data/vendor/prism/templates/ext/prism/api_node.c.erb +58 -44
  195. data/vendor/prism/templates/include/prism/ast.h.erb +67 -17
  196. data/vendor/prism/templates/include/prism/ast.hpp.erb +31 -0
  197. data/vendor/prism/templates/include/prism/internal/diagnostic.h.erb +60 -0
  198. data/vendor/prism/templates/java/{org → api/src/main/java-templates/org/ruby_lang}/prism/AbstractNodeVisitor.java.erb +1 -1
  199. data/vendor/prism/templates/java/{org → api/src/main/java-templates/org/ruby_lang}/prism/Loader.java.erb +232 -133
  200. data/vendor/prism/templates/java/{org → api/src/main/java-templates/org/ruby_lang}/prism/Nodes.java.erb +62 -23
  201. data/vendor/prism/templates/javascript/src/deserialize.js.erb +153 -146
  202. data/vendor/prism/templates/javascript/src/nodes.js.erb +4 -9
  203. data/vendor/prism/templates/lib/prism/compiler.rb.erb +14 -5
  204. data/vendor/prism/templates/lib/prism/dispatcher.rb.erb +20 -12
  205. data/vendor/prism/templates/lib/prism/dot_visitor.rb.erb +37 -11
  206. data/vendor/prism/templates/lib/prism/dsl.rb.erb +43 -4
  207. data/vendor/prism/templates/lib/prism/inspect_visitor.rb.erb +26 -10
  208. data/vendor/prism/templates/lib/prism/mutation_compiler.rb.erb +5 -2
  209. data/vendor/prism/templates/lib/prism/node.rb.erb +283 -105
  210. data/vendor/prism/templates/lib/prism/reflection.rb.erb +11 -2
  211. data/vendor/prism/templates/lib/prism/serialize.rb.erb +291 -134
  212. data/vendor/prism/templates/lib/prism/visitor.rb.erb +18 -0
  213. data/vendor/prism/templates/src/diagnostic.c.erb +114 -63
  214. data/vendor/prism/templates/src/json.c.erb +130 -0
  215. data/vendor/prism/templates/src/node.c.erb +79 -236
  216. data/vendor/prism/templates/src/prettyprint.c.erb +24 -13
  217. data/vendor/prism/templates/src/serialize.c.erb +369 -183
  218. data/vendor/prism/templates/src/{token_type.c.erb → tokens.c.erb} +22 -11
  219. data/vendor/prism/templates/template.rb +193 -91
  220. metadata +100 -42
  221. data/vendor/prism/include/prism/defines.h +0 -260
  222. data/vendor/prism/include/prism/pack.h +0 -163
  223. data/vendor/prism/include/prism/regexp.h +0 -43
  224. data/vendor/prism/include/prism/static_literals.h +0 -121
  225. data/vendor/prism/include/prism/util/pm_buffer.h +0 -236
  226. data/vendor/prism/include/prism/util/pm_char.h +0 -204
  227. data/vendor/prism/include/prism/util/pm_constant_pool.h +0 -218
  228. data/vendor/prism/include/prism/util/pm_integer.h +0 -130
  229. data/vendor/prism/include/prism/util/pm_memchr.h +0 -29
  230. data/vendor/prism/include/prism/util/pm_newline_list.h +0 -113
  231. data/vendor/prism/include/prism/util/pm_string.h +0 -200
  232. data/vendor/prism/src/pack.c +0 -509
  233. data/vendor/prism/src/util/pm_constant_pool.c +0 -342
  234. data/vendor/prism/src/util/pm_list.c +0 -49
  235. data/vendor/prism/src/util/pm_newline_list.c +0 -125
  236. data/vendor/prism/src/util/pm_string.c +0 -381
  237. data/vendor/prism/src/util/pm_strpbrk.c +0 -206
  238. data/vendor/prism/templates/include/prism/diagnostic.h.erb +0 -130
  239. data/vendor/prism/templates/rbi/prism/dsl.rbi.erb +0 -68
  240. data/vendor/prism/templates/rbi/prism/node.rbi.erb +0 -167
  241. data/vendor/prism/templates/rbi/prism/visitor.rbi.erb +0 -18
  242. data/vendor/prism/templates/sig/prism/_private/dot_visitor.rbs.erb +0 -45
  243. data/vendor/prism/templates/sig/prism/dsl.rbs.erb +0 -31
  244. data/vendor/prism/templates/sig/prism/mutation_compiler.rbs.erb +0 -7
  245. data/vendor/prism/templates/sig/prism/node.rbs.erb +0 -136
  246. data/vendor/prism/templates/sig/prism/visitor.rbs.erb +0 -17
  247. data/vendor/prism/templates/sig/prism.rbs.erb +0 -92
@@ -0,0 +1,455 @@
1
+ #include "prism/internal/comments.h"
2
+
3
+ #include "prism/internal/arena.h"
4
+ #include "prism/internal/char.h"
5
+ #include "prism/internal/node.h"
6
+ #include "prism/internal/parser.h"
7
+
8
+ #include <assert.h>
9
+ #include <stdlib.h>
10
+ #include <string.h>
11
+
12
+ /* The state used while walking the tree to find the owner of each comment. */
13
+ typedef struct {
14
+ /*
15
+ * The arena for memory that is only needed during the walk, which is the
16
+ * scratch arena of the parser.
17
+ */
18
+ pm_arena_t *arena;
19
+
20
+ /* The start of the source that was parsed. */
21
+ const uint8_t *source;
22
+
23
+ /* The comments found while parsing, sorted by start offset. */
24
+ const pm_comment_t **comments;
25
+
26
+ /* The number of comments. */
27
+ uint32_t size;
28
+
29
+ /* The start offset of each comment. */
30
+ uint32_t *starts;
31
+
32
+ /*
33
+ * The result of the most recent search for the first comment after the end
34
+ * of a node. This is a hint to the search to avoid fully binary searching
35
+ * the comments list if possible.
36
+ */
37
+ uint32_t cursor;
38
+
39
+ /* The node each comment is attached to. */
40
+ const pm_node_t **attached;
41
+
42
+ /*
43
+ * A disjoint set over comment indices that maps each index to the next
44
+ * index at or after it that has not yet been claimed. It has one extra
45
+ * entry at the end that is never claimed, so that every lookup ends. This
46
+ * allows quickly finding the next unclaimed comment without having to check
47
+ * the state of each comment individually.
48
+ */
49
+ uint32_t *unclaimed;
50
+
51
+ /*
52
+ * A stack of nodes whose children are being visited. Each level of the
53
+ * walk pushes its children, visits them, then pops back to where it
54
+ * started.
55
+ */
56
+ struct {
57
+ const pm_node_t **nodes;
58
+ size_t size;
59
+ size_t capacity;
60
+ } stack;
61
+ } pm_comments_walk_t;
62
+
63
+ /* Compare two comments by start offset for qsort. */
64
+ static int
65
+ pm_comments_compare(const void *left, const void *right) {
66
+ uint32_t left_start = (*(const pm_comment_t *const *) left)->location.start;
67
+ uint32_t right_start = (*(const pm_comment_t *const *) right)->location.start;
68
+ return (left_start > right_start) - (left_start < right_start);
69
+ }
70
+
71
+ /*
72
+ * Copy the linked list of comments from the parser sorted by start offset, and
73
+ * relink the list in that order. Usually comments arrive in source order, but
74
+ * unfortunately because of heredocs they can occasionally be out of order.
75
+ */
76
+ static const pm_comment_t **
77
+ pm_comments_sort(pm_parser_t *parser, uint32_t size) {
78
+ pm_comment_t **comments = (pm_comment_t **) pm_arena_alloc(&parser->metadata_arena, sizeof(pm_comment_t *) * size, PRISM_ALIGNOF(pm_comment_t *));
79
+
80
+ uint32_t index = 0;
81
+ for (pm_list_node_t *node = parser->comment_list.head; node != NULL; node = node->next) {
82
+ comments[index++] = (pm_comment_t *) node;
83
+ }
84
+
85
+ qsort((void *) comments, size, sizeof(pm_comment_t *), pm_comments_compare);
86
+
87
+ for (index = 0; index + 1 < size; index++) {
88
+ comments[index]->node.next = &comments[index + 1]->node;
89
+ }
90
+
91
+ comments[size - 1]->node.next = NULL;
92
+ parser->comment_list.head = &comments[0]->node;
93
+ parser->comment_list.tail = &comments[size - 1]->node;
94
+
95
+ return (const pm_comment_t **) comments;
96
+ }
97
+
98
+ /*
99
+ * Return the index of the first comment in [left, right) that starts at or
100
+ * after the given offset, or right if there is none.
101
+ */
102
+ static uint32_t
103
+ pm_comments_lower_bound(const pm_comments_walk_t *walk, uint32_t left, uint32_t right, uint32_t offset) {
104
+ while (left < right) {
105
+ uint32_t middle = left + (right - left) / 2;
106
+
107
+ if (walk->starts[middle] < offset) {
108
+ left = middle + 1;
109
+ } else {
110
+ right = middle;
111
+ }
112
+ }
113
+
114
+ return left;
115
+ }
116
+
117
+ /*
118
+ * Return the index of the first comment that starts at or after the given
119
+ * offset, or the number of comments if there is none. This gallops outward
120
+ * from the previous result to find a range to search, so that a run of nearby
121
+ * offsets costs a constant number of comparisons each.
122
+ */
123
+ static uint32_t
124
+ pm_comments_seek(pm_comments_walk_t *walk, uint32_t offset) {
125
+ const uint32_t *starts = walk->starts;
126
+
127
+ uint32_t size = walk->size;
128
+ uint32_t cursor = walk->cursor;
129
+ uint32_t left;
130
+ uint32_t right;
131
+ uint32_t step = 1;
132
+
133
+ if (cursor == size || starts[cursor] >= offset) {
134
+ /*
135
+ * The answer is at or before the cursor. Widen the range backward
136
+ * until the comment before it starts before the offset.
137
+ */
138
+ right = cursor;
139
+ left = cursor;
140
+
141
+ while (left > 0 && starts[left - 1] >= offset) {
142
+ right = left - 1;
143
+ left = (left - 1 > step) ? left - 1 - step : 0;
144
+ step *= 2;
145
+ }
146
+ } else {
147
+ /*
148
+ * The answer is after the cursor. Widen the range forward until it
149
+ * ends at a comment that starts at or after the offset.
150
+ */
151
+ left = cursor + 1;
152
+ right = left;
153
+
154
+ while (right < size && starts[right] < offset) {
155
+ left = right + 1;
156
+ right = (size - left > step) ? left + step : size;
157
+ step *= 2;
158
+ }
159
+ }
160
+
161
+ walk->cursor = pm_comments_lower_bound(walk, left, right, offset);
162
+ return walk->cursor;
163
+ }
164
+
165
+ /*
166
+ * The first comment index at or after the given index that has not yet been
167
+ * claimed, compressing the path along the way.
168
+ */
169
+ static uint32_t
170
+ pm_comments_unclaimed(pm_comments_walk_t *walk, uint32_t index) {
171
+ uint32_t *unclaimed = walk->unclaimed;
172
+
173
+ while (unclaimed[index] != index) {
174
+ unclaimed[index] = unclaimed[unclaimed[index]];
175
+ index = unclaimed[index];
176
+ }
177
+
178
+ return index;
179
+ }
180
+
181
+ /*
182
+ * Return the earliest offset before the given one from which the source up to
183
+ * it holds only whitespace and comments. Any comment that starts at or after
184
+ * the returned offset is separated from the given offset by only whitespace and
185
+ * other comments.
186
+ */
187
+ static uint32_t
188
+ pm_comments_blank_before(const pm_comments_walk_t *walk, uint32_t offset) {
189
+ uint32_t index = pm_comments_lower_bound(walk, 0, walk->size, offset);
190
+
191
+ while (true) {
192
+ offset -= (uint32_t) pm_strrspn_whitespace(walk->source, (ptrdiff_t) offset);
193
+ if (index == 0) return offset;
194
+
195
+ /* An embedded document's location includes its trailing newline, so
196
+ * the whitespace that was just skipped can end inside a comment. */
197
+ const pm_location_t *location = &walk->comments[index - 1]->location;
198
+ if (location->start >= offset || PM_LOCATION_END(location) < offset) return offset;
199
+
200
+ offset = location->start;
201
+ index--;
202
+ }
203
+ }
204
+
205
+ /*
206
+ * Return the latest offset after the given one, and no later than the given
207
+ * end, up to which the source from the given offset holds only whitespace and
208
+ * comments. Any comment that ends at or before the returned offset is
209
+ * separated from the given offset by only whitespace and other comments.
210
+ */
211
+ static uint32_t
212
+ pm_comments_blank_after(const pm_comments_walk_t *walk, uint32_t offset, uint32_t end) {
213
+ uint32_t index = pm_comments_lower_bound(walk, 0, walk->size, offset);
214
+
215
+ while (true) {
216
+ offset += (uint32_t) pm_strspn_whitespace(walk->source + offset, (ptrdiff_t) end - (ptrdiff_t) offset);
217
+ if (index == walk->size || walk->starts[index] != offset) return offset;
218
+
219
+ offset = PM_LOCATION_END(&walk->comments[index]->location);
220
+ index++;
221
+ }
222
+ }
223
+
224
+ /*
225
+ * Push a child node onto the walk stack without descending into it. The walk
226
+ * descends itself, so that it can visit children in source order and handle
227
+ * each node after all of its descendants.
228
+ */
229
+ static pm_visit_result_t
230
+ pm_comments_push(const pm_node_t *node, void *data) {
231
+ pm_comments_walk_t *walk = (pm_comments_walk_t *) data;
232
+
233
+ if (walk->stack.size == walk->stack.capacity) {
234
+ size_t capacity = walk->stack.capacity == 0 ? 64 : walk->stack.capacity * 2;
235
+ const pm_node_t **nodes = (const pm_node_t **) pm_arena_alloc(walk->arena, sizeof(pm_node_t *) * capacity, PRISM_ALIGNOF(pm_node_t *));
236
+ if (walk->stack.size > 0) memcpy((void *) nodes, (const void *) walk->stack.nodes, sizeof(pm_node_t *) * walk->stack.size);
237
+
238
+ walk->stack.nodes = nodes;
239
+ walk->stack.capacity = capacity;
240
+ }
241
+
242
+ walk->stack.nodes[walk->stack.size++] = node;
243
+ return PM_VISIT_SKIP;
244
+ }
245
+
246
+ /*
247
+ * Visit the given node and its descendants in post-order, attaching comments as
248
+ * the nodes are visited.
249
+ */
250
+ static void
251
+ pm_comments_visit(pm_comments_walk_t *walk, const pm_node_t *node) {
252
+ /* First, push all of the children of this node onto the stack. */
253
+ size_t base = walk->stack.size;
254
+ pm_visit_child_nodes(node, pm_comments_push, walk);
255
+ size_t top = walk->stack.size;
256
+
257
+ /* Next, in-place sort the children by their starting location. */
258
+ for (size_t index = base + 1; index < top; index++) {
259
+ const pm_node_t *child = walk->stack.nodes[index];
260
+ size_t insert = index;
261
+
262
+ while (insert > base && walk->stack.nodes[insert - 1]->location.start > child->location.start) {
263
+ walk->stack.nodes[insert] = walk->stack.nodes[insert - 1];
264
+ insert--;
265
+ }
266
+
267
+ walk->stack.nodes[insert] = child;
268
+ }
269
+
270
+ /* Next, visit all of the children so that they can claim comments. */
271
+ for (size_t index = base; index < top; index++) {
272
+ pm_comments_visit(walk, walk->stack.nodes[index]);
273
+ }
274
+
275
+ uint32_t start = node->location.start;
276
+ uint32_t end = PM_LOCATION_END(&node->location);
277
+ uint32_t after = pm_comments_seek(walk, end);
278
+
279
+ /* If this node is followed by a comment on the same line, attach it if
280
+ * appropriate. Appropriate in this case means if no other node has claimed
281
+ * it or if we are closer in distance or equal in distance to the current
282
+ * claimant. */
283
+ if (
284
+ after < walk->size &&
285
+ !PM_NODE_TYPE_P(node, PM_PROGRAM_NODE) &&
286
+ !PM_NODE_TYPE_P(node, PM_STATEMENTS_NODE) &&
287
+ (node->location.length == 0 || walk->source[end - 1] != '\n')
288
+ ) {
289
+ uint32_t comment_start = walk->starts[after];
290
+
291
+ if (memchr(walk->source + end, '\n', comment_start - end) == NULL) {
292
+ const pm_node_t *attached = walk->attached[after];
293
+ uint32_t attached_end = attached == NULL ? 0 : PM_LOCATION_END(&attached->location);
294
+
295
+ if (end >= attached_end || attached_end > comment_start) {
296
+ walk->attached[after] = node;
297
+ }
298
+ }
299
+ }
300
+
301
+ /* Grab up the remaining comments within the range of this node that were
302
+ * not claimed by any of its descendants. A comment that is separated by
303
+ * only whitespace and other comments from a statements node child, either
304
+ * the child after it or the child before it, is attached to the statements
305
+ * instead, so that the comments before the first statement and after the
306
+ * last statement in a body belong to the body. The claimed comments are in
307
+ * order, so the children around each one are found by moving forward
308
+ * through the sorted children. */
309
+ if (after > 0 && walk->starts[after - 1] >= start) {
310
+ uint32_t index = pm_comments_unclaimed(walk, pm_comments_lower_bound(walk, 0, after, start));
311
+ size_t child_index = base;
312
+
313
+ /* The boundaries around the statements children are each found once,
314
+ * when the first comment that needs them is reached, since the
315
+ * comments that follow reach the same children. */
316
+ const pm_node_t *leading = NULL;
317
+ uint32_t leading_boundary = 0;
318
+ const pm_node_t *trailing = NULL;
319
+ uint32_t trailing_boundary = 0;
320
+
321
+ while (index < after && PM_LOCATION_END(&walk->comments[index]->location) <= end) {
322
+ if (walk->attached[index] == NULL) {
323
+ uint32_t comment_end = PM_LOCATION_END(&walk->comments[index]->location);
324
+ const pm_node_t *attached = node;
325
+
326
+ while (child_index < top && walk->stack.nodes[child_index]->location.start < comment_end) child_index++;
327
+
328
+ if (child_index < top) {
329
+ const pm_node_t *child = walk->stack.nodes[child_index];
330
+
331
+ if (PM_NODE_TYPE_P(child, PM_STATEMENTS_NODE)) {
332
+ if (child != leading) {
333
+ leading = child;
334
+ leading_boundary = pm_comments_blank_before(walk, child->location.start);
335
+ }
336
+
337
+ if (walk->starts[index] >= leading_boundary) attached = child;
338
+ }
339
+ }
340
+
341
+ if (attached == node && child_index > base) {
342
+ const pm_node_t *child = walk->stack.nodes[child_index - 1];
343
+
344
+ /* A rescue clause ends at its last statement rather than at
345
+ * the keyword that follows it, and the first clause in a
346
+ * body contains the clauses after it. So a comment after
347
+ * the statements of the last clause follows the first
348
+ * clause, and belongs to the statements of the last. */
349
+ if (PM_NODE_TYPE_P(child, PM_RESCUE_NODE)) {
350
+ const pm_rescue_node_t *rescue = (const pm_rescue_node_t *) child;
351
+ while (rescue->subsequent != NULL) rescue = rescue->subsequent;
352
+ child = (const pm_node_t *) rescue->statements;
353
+ }
354
+
355
+ if (child != NULL && PM_NODE_TYPE_P(child, PM_STATEMENTS_NODE) && PM_LOCATION_END(&child->location) <= walk->starts[index]) {
356
+ if (child != trailing) {
357
+ trailing = child;
358
+ trailing_boundary = pm_comments_blank_after(walk, PM_LOCATION_END(&child->location), end);
359
+ }
360
+
361
+ if (comment_end <= trailing_boundary) attached = child;
362
+ }
363
+ }
364
+
365
+ walk->attached[index] = attached;
366
+ }
367
+
368
+ walk->unclaimed[index] = index + 1;
369
+ index = pm_comments_unclaimed(walk, index + 1);
370
+ }
371
+ }
372
+
373
+ /* Truncate the stack back to the base size. */
374
+ walk->stack.size = base;
375
+ }
376
+
377
+ /*
378
+ * Sort the parser's comment list in place by start offset, then assign every
379
+ * comment to the node that owns it. A comment is owned by:
380
+ *
381
+ * 1. the node that ends latest on the comment's line before the comment starts,
382
+ * with ties going to the node that is higher in the tree;
383
+ * 2. otherwise, the innermost node whose location contains the comment, or the
384
+ * statements node child of that node if only whitespace and other comments
385
+ * come between the statements and the comment, on either side;
386
+ * 3. otherwise, the top-level statements node of the program.
387
+ */
388
+ void
389
+ pm_comments_attach(pm_parser_t *parser, const pm_node_t *root) {
390
+ assert(PM_NODE_TYPE_P(root, PM_PROGRAM_NODE));
391
+
392
+ uint32_t size = (uint32_t) parser->comment_list.size;
393
+ if (size == 0) return;
394
+
395
+ pm_arena_t *arena = &parser->scratch_arena;
396
+ pm_comments_walk_t walk = {
397
+ .arena = arena,
398
+ .source = parser->start,
399
+ .comments = pm_comments_sort(parser, size),
400
+ .size = size,
401
+ .starts = (uint32_t *) pm_arena_alloc(arena, sizeof(uint32_t) * size, PRISM_ALIGNOF(uint32_t)),
402
+ .attached = (const pm_node_t **) pm_arena_zalloc(arena, sizeof(pm_node_t *) * size, PRISM_ALIGNOF(pm_node_t *)),
403
+ .unclaimed = (uint32_t *) pm_arena_alloc(arena, sizeof(uint32_t) * (size + 1), PRISM_ALIGNOF(uint32_t))
404
+ };
405
+
406
+ for (uint32_t index = 0; index < size; index++) walk.starts[index] = walk.comments[index]->location.start;
407
+ for (uint32_t index = 0; index <= size; index++) walk.unclaimed[index] = index;
408
+
409
+ pm_comments_visit(&walk, root);
410
+
411
+ /*
412
+ * Attach comments that no node attached to during the walk to the
413
+ * top-level statements, then group the comments by node with a counting
414
+ * sort. Comments are placed in sorted order, so each node's comments stay
415
+ * sorted by start offset.
416
+ */
417
+ const pm_node_t *statements = (const pm_node_t *) ((const pm_program_node_t *) root)->statements;
418
+ uint32_t ranges_size = 0;
419
+
420
+ for (uint32_t index = 0; index < size; index++) {
421
+ if (walk.attached[index] == NULL) walk.attached[index] = statements;
422
+
423
+ uint32_t node_id = walk.attached[index]->node_id;
424
+ assert(node_id <= parser->node_id);
425
+ if (node_id >= ranges_size) ranges_size = node_id + 1;
426
+ }
427
+
428
+ /* The ranges only extend to the largest node id that owns a comment, since
429
+ * none of the nodes after it own any. */
430
+ pm_comment_range_t *ranges = (pm_comment_range_t *) pm_arena_zalloc(&parser->metadata_arena, sizeof(pm_comment_range_t) * ranges_size, PRISM_ALIGNOF(pm_comment_range_t));
431
+ uint32_t *attachments = (uint32_t *) pm_arena_alloc(&parser->metadata_arena, sizeof(uint32_t) * size, PRISM_ALIGNOF(uint32_t));
432
+
433
+ for (uint32_t index = 0; index < size; index++) {
434
+ ranges[walk.attached[index]->node_id].length++;
435
+ }
436
+
437
+ uint32_t offset = 0;
438
+ for (uint32_t index = 0; index < ranges_size; index++) {
439
+ ranges[index].index = offset;
440
+ offset += ranges[index].length;
441
+ ranges[index].length = 0;
442
+ }
443
+
444
+ for (uint32_t index = 0; index < size; index++) {
445
+ pm_comment_range_t *range = &ranges[walk.attached[index]->node_id];
446
+ attachments[range->index + range->length++] = index;
447
+ }
448
+
449
+ parser->comment_attachments = (pm_comment_attachments_t) {
450
+ .comments = walk.comments,
451
+ .attachments = attachments,
452
+ .ranges = ranges,
453
+ .ranges_size = ranges_size
454
+ };
455
+ }