prism 2.0.0.rc1-x86_64-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 (216) hide show
  1. checksums.yaml +7 -0
  2. data/BSDmakefile +58 -0
  3. data/CHANGELOG.md +870 -0
  4. data/CODE_OF_CONDUCT.md +76 -0
  5. data/CONTRIBUTING.md +58 -0
  6. data/LICENSE.md +7 -0
  7. data/Makefile +145 -0
  8. data/README.md +81 -0
  9. data/config.yml +4752 -0
  10. data/docs/build_system.md +119 -0
  11. data/docs/configuration.md +69 -0
  12. data/docs/cruby_compilation.md +27 -0
  13. data/docs/design.md +53 -0
  14. data/docs/encoding.md +121 -0
  15. data/docs/fuzzing.md +81 -0
  16. data/docs/heredocs.md +36 -0
  17. data/docs/javascript.md +133 -0
  18. data/docs/local_variable_depth.md +229 -0
  19. data/docs/mapping.md +117 -0
  20. data/docs/parser_translation.md +24 -0
  21. data/docs/parsing_rules.md +22 -0
  22. data/docs/releasing.md +147 -0
  23. data/docs/relocation.md +32 -0
  24. data/docs/ripper_translation.md +63 -0
  25. data/docs/ruby_api.md +45 -0
  26. data/docs/ruby_parser_translation.md +19 -0
  27. data/docs/serialization.md +230 -0
  28. data/docs/testing.md +72 -0
  29. data/ext/prism/api_node.c +7005 -0
  30. data/ext/prism/extconf.rb +126 -0
  31. data/ext/prism/extension.c +1783 -0
  32. data/ext/prism/extension.h +19 -0
  33. data/include/prism/arena.h +37 -0
  34. data/include/prism/ast.h +10371 -0
  35. data/include/prism/buffer.h +52 -0
  36. data/include/prism/comments.h +55 -0
  37. data/include/prism/compiler/accel.h +24 -0
  38. data/include/prism/compiler/align.h +36 -0
  39. data/include/prism/compiler/assume.h +25 -0
  40. data/include/prism/compiler/exported.h +24 -0
  41. data/include/prism/compiler/fallthrough.h +22 -0
  42. data/include/prism/compiler/filesystem.h +32 -0
  43. data/include/prism/compiler/flex_array.h +19 -0
  44. data/include/prism/compiler/force_inline.h +21 -0
  45. data/include/prism/compiler/format.h +25 -0
  46. data/include/prism/compiler/inline.h +17 -0
  47. data/include/prism/compiler/nodiscard.h +22 -0
  48. data/include/prism/compiler/nonnull.h +18 -0
  49. data/include/prism/compiler/unused.h +18 -0
  50. data/include/prism/constant_pool.h +81 -0
  51. data/include/prism/diagnostic.h +93 -0
  52. data/include/prism/errors_format.h +53 -0
  53. data/include/prism/excludes.h +29 -0
  54. data/include/prism/integer.h +41 -0
  55. data/include/prism/internal/allocator.h +68 -0
  56. data/include/prism/internal/allocator_debug.h +88 -0
  57. data/include/prism/internal/arena.h +108 -0
  58. data/include/prism/internal/bit.h +42 -0
  59. data/include/prism/internal/buffer.h +91 -0
  60. data/include/prism/internal/char.h +145 -0
  61. data/include/prism/internal/comments.h +53 -0
  62. data/include/prism/internal/constant_pool.h +152 -0
  63. data/include/prism/internal/diagnostic.h +389 -0
  64. data/include/prism/internal/encoding.h +242 -0
  65. data/include/prism/internal/integer.h +68 -0
  66. data/include/prism/internal/isinf.h +16 -0
  67. data/include/prism/internal/line_offset_list.h +34 -0
  68. data/include/prism/internal/list.h +62 -0
  69. data/include/prism/internal/magic_comments.h +23 -0
  70. data/include/prism/internal/memchr.h +15 -0
  71. data/include/prism/internal/node.h +38 -0
  72. data/include/prism/internal/options.h +232 -0
  73. data/include/prism/internal/parser.h +984 -0
  74. data/include/prism/internal/regexp.h +26 -0
  75. data/include/prism/internal/serialize.h +39 -0
  76. data/include/prism/internal/source.h +72 -0
  77. data/include/prism/internal/static_literals.h +100 -0
  78. data/include/prism/internal/stringy.h +35 -0
  79. data/include/prism/internal/strncasecmp.h +18 -0
  80. data/include/prism/internal/strpbrk.h +33 -0
  81. data/include/prism/internal/tokens.h +11 -0
  82. data/include/prism/json.h +32 -0
  83. data/include/prism/line_offset_list.h +61 -0
  84. data/include/prism/magic_comments.h +35 -0
  85. data/include/prism/node.h +94 -0
  86. data/include/prism/options.h +354 -0
  87. data/include/prism/parser.h +404 -0
  88. data/include/prism/prettyprint.h +31 -0
  89. data/include/prism/serialize.h +115 -0
  90. data/include/prism/source.h +155 -0
  91. data/include/prism/stream.h +28 -0
  92. data/include/prism/string_query.h +63 -0
  93. data/include/prism/stringy.h +72 -0
  94. data/include/prism/version.h +38 -0
  95. data/include/prism.h +131 -0
  96. data/lib/prism/3.3/prism.so +0 -0
  97. data/lib/prism/3.4/prism.so +0 -0
  98. data/lib/prism/4.0/prism.so +0 -0
  99. data/lib/prism/compiler.rb +815 -0
  100. data/lib/prism/desugar_compiler.rb +463 -0
  101. data/lib/prism/dispatcher.rb +2383 -0
  102. data/lib/prism/dot_visitor.rb +4848 -0
  103. data/lib/prism/dsl.rb +1369 -0
  104. data/lib/prism/ffi.rb +740 -0
  105. data/lib/prism/inspect_visitor.rb +2425 -0
  106. data/lib/prism/lex_compat.rb +953 -0
  107. data/lib/prism/mutation_compiler.rb +780 -0
  108. data/lib/prism/node.rb +33204 -0
  109. data/lib/prism/node_ext.rb +504 -0
  110. data/lib/prism/node_find.rb +215 -0
  111. data/lib/prism/parse_result/errors.rb +72 -0
  112. data/lib/prism/parse_result/newlines.rb +423 -0
  113. data/lib/prism/parse_result.rb +1221 -0
  114. data/lib/prism/pattern.rb +314 -0
  115. data/lib/prism/polyfill/append_as_bytes.rb +15 -0
  116. data/lib/prism/polyfill/byteindex.rb +13 -0
  117. data/lib/prism/polyfill/scan_byte.rb +14 -0
  118. data/lib/prism/polyfill/unpack1.rb +14 -0
  119. data/lib/prism/polyfill/warn.rb +36 -0
  120. data/lib/prism/reflection.rb +427 -0
  121. data/lib/prism/relocation.rb +567 -0
  122. data/lib/prism/serialize.rb +5308 -0
  123. data/lib/prism/string_query.rb +46 -0
  124. data/lib/prism/translation/parser/builder.rb +70 -0
  125. data/lib/prism/translation/parser/compiler.rb +2226 -0
  126. data/lib/prism/translation/parser/lexer.rb +769 -0
  127. data/lib/prism/translation/parser.rb +376 -0
  128. data/lib/prism/translation/parser_current.rb +26 -0
  129. data/lib/prism/translation/parser_versions.rb +36 -0
  130. data/lib/prism/translation/ripper/filter.rb +53 -0
  131. data/lib/prism/translation/ripper/lexer.rb +133 -0
  132. data/lib/prism/translation/ripper/sexp.rb +118 -0
  133. data/lib/prism/translation/ripper/shim.rb +7 -0
  134. data/lib/prism/translation/ripper.rb +4278 -0
  135. data/lib/prism/translation/ruby_parser.rb +1721 -0
  136. data/lib/prism/translation.rb +20 -0
  137. data/lib/prism/visitor.rb +1440 -0
  138. data/lib/prism.rb +168 -0
  139. data/prism.gemspec +234 -0
  140. data/rbi/generated/prism/compiler.rbi +489 -0
  141. data/rbi/generated/prism/desugar_compiler.rbi +306 -0
  142. data/rbi/generated/prism/dispatcher.rbi +1138 -0
  143. data/rbi/generated/prism/dot_visitor.rbi +618 -0
  144. data/rbi/generated/prism/dsl.rbi +751 -0
  145. data/rbi/generated/prism/inspect_visitor.rbi +503 -0
  146. data/rbi/generated/prism/lex_compat.rbi +162 -0
  147. data/rbi/generated/prism/mutation_compiler.rbi +464 -0
  148. data/rbi/generated/prism/node.rbi +13681 -0
  149. data/rbi/generated/prism/node_ext.rbi +259 -0
  150. data/rbi/generated/prism/node_find.rbi +79 -0
  151. data/rbi/generated/prism/parse_result/errors.rbi +21 -0
  152. data/rbi/generated/prism/parse_result/newlines.rbi +208 -0
  153. data/rbi/generated/prism/parse_result.rbi +764 -0
  154. data/rbi/generated/prism/pattern.rbi +134 -0
  155. data/rbi/generated/prism/reflection.rbi +89 -0
  156. data/rbi/generated/prism/relocation.rbi +329 -0
  157. data/rbi/generated/prism/serialize.rbi +181 -0
  158. data/rbi/generated/prism/string_query.rbi +36 -0
  159. data/rbi/generated/prism/translation.rbi +7 -0
  160. data/rbi/generated/prism/visitor.rbi +650 -0
  161. data/rbi/generated/prism.rbi +86 -0
  162. data/rbi/prism/translation/parser.rbi +11 -0
  163. data/rbi/prism/translation/parser_versions.rbi +28 -0
  164. data/rbi/prism/translation/ripper.rbi +15 -0
  165. data/rbi/rubyvm/node_find.rbi +14 -0
  166. data/sig/generated/prism/compiler.rbs +492 -0
  167. data/sig/generated/prism/desugar_compiler.rbs +305 -0
  168. data/sig/generated/prism/dispatcher.rbs +1292 -0
  169. data/sig/generated/prism/dot_visitor.rbs +635 -0
  170. data/sig/generated/prism/dsl.rbs +928 -0
  171. data/sig/generated/prism/inspect_visitor.rbs +509 -0
  172. data/sig/generated/prism/lex_compat.rbs +166 -0
  173. data/sig/generated/prism/mutation_compiler.rbs +464 -0
  174. data/sig/generated/prism/node.rbs +20160 -0
  175. data/sig/generated/prism/node_ext.rbs +290 -0
  176. data/sig/generated/prism/node_find.rbs +90 -0
  177. data/sig/generated/prism/parse_result/errors.rbs +22 -0
  178. data/sig/generated/prism/parse_result/newlines.rbs +223 -0
  179. data/sig/generated/prism/parse_result.rbs +859 -0
  180. data/sig/generated/prism/pattern.rbs +156 -0
  181. data/sig/generated/prism/reflection.rbs +101 -0
  182. data/sig/generated/prism/relocation.rbs +428 -0
  183. data/sig/generated/prism/serialize.rbs +195 -0
  184. data/sig/generated/prism/string_query.rbs +36 -0
  185. data/sig/generated/prism/translation.rbs +8 -0
  186. data/sig/generated/prism/visitor.rbs +1111 -0
  187. data/sig/generated/prism.rbs +84 -0
  188. data/sig/manifest.yaml +2 -0
  189. data/src/arena.c +117 -0
  190. data/src/buffer.c +380 -0
  191. data/src/char.c +289 -0
  192. data/src/comments.c +454 -0
  193. data/src/constant_pool.c +406 -0
  194. data/src/diagnostic.c +886 -0
  195. data/src/encoding.c +5345 -0
  196. data/src/errors_format.c +519 -0
  197. data/src/integer.c +681 -0
  198. data/src/json.c +5733 -0
  199. data/src/line_offset_list.c +100 -0
  200. data/src/list.c +24 -0
  201. data/src/memchr.c +37 -0
  202. data/src/node.c +4515 -0
  203. data/src/options.c +451 -0
  204. data/src/parser.c +349 -0
  205. data/src/prettyprint.c +9024 -0
  206. data/src/prism.c +23739 -0
  207. data/src/regexp.c +1706 -0
  208. data/src/serialize.c +2309 -0
  209. data/src/source.c +518 -0
  210. data/src/static_literals.c +685 -0
  211. data/src/string_query.c +166 -0
  212. data/src/stringy.c +91 -0
  213. data/src/strncasecmp.c +37 -0
  214. data/src/strpbrk.c +447 -0
  215. data/src/tokens.c +724 -0
  216. metadata +259 -0
@@ -0,0 +1,504 @@
1
+ # frozen_string_literal: true
2
+ # :markup: markdown
3
+ #--
4
+ # rbs_inline: enabled
5
+
6
+ #--
7
+ # Here we are reopening the prism module to provide methods on nodes that aren't
8
+ # templated and are meant as convenience methods.
9
+ #++
10
+ module Prism
11
+ class Node
12
+ #: (*String replacements) -> void
13
+ def deprecated(*replacements) # :nodoc:
14
+ location = caller_locations(1, 1)&.[](0)&.label
15
+ suggest = replacements.map { |replacement| "#{self.class}##{replacement}" }
16
+
17
+ warn(<<~MSG, uplevel: 1, category: :deprecated)
18
+ [deprecation]: #{self.class}##{location} is deprecated and will be \
19
+ removed in the next major version. Use #{suggest.join("/")} instead.
20
+ #{(caller(1, 3) || []).join("\n")}
21
+ MSG
22
+ end
23
+ end
24
+
25
+ module RegularExpressionOptions # :nodoc:
26
+ # Returns a numeric value that represents the flags that were used to create
27
+ # the regular expression.
28
+ #
29
+ #: (Integer flags) -> Integer
30
+ def self.options(flags)
31
+ o = 0
32
+ o |= Regexp::IGNORECASE if flags.anybits?(RegularExpressionFlags::IGNORE_CASE)
33
+ o |= Regexp::EXTENDED if flags.anybits?(RegularExpressionFlags::EXTENDED)
34
+ o |= Regexp::MULTILINE if flags.anybits?(RegularExpressionFlags::MULTI_LINE)
35
+ o |= Regexp::FIXEDENCODING if flags.anybits?(RegularExpressionFlags::EUC_JP | RegularExpressionFlags::WINDOWS_31J | RegularExpressionFlags::UTF_8)
36
+ o |= Regexp::NOENCODING if flags.anybits?(RegularExpressionFlags::ASCII_8BIT)
37
+ o
38
+ end
39
+ end
40
+
41
+ class InterpolatedMatchLastLineNode < Node
42
+ # Returns a numeric value that represents the flags that were used to create
43
+ # the regular expression.
44
+ #
45
+ #: () -> Integer
46
+ def options
47
+ RegularExpressionOptions.options(flags)
48
+ end
49
+ end
50
+
51
+ class InterpolatedRegularExpressionNode < Node
52
+ # Returns a numeric value that represents the flags that were used to create
53
+ # the regular expression.
54
+ #
55
+ #: () -> Integer
56
+ def options
57
+ RegularExpressionOptions.options(flags)
58
+ end
59
+ end
60
+
61
+ class MatchLastLineNode < Node
62
+ # Returns a numeric value that represents the flags that were used to create
63
+ # the regular expression.
64
+ #
65
+ #: () -> Integer
66
+ def options
67
+ RegularExpressionOptions.options(flags)
68
+ end
69
+ end
70
+
71
+ class RegularExpressionNode < Node
72
+ # Returns a numeric value that represents the flags that were used to create
73
+ # the regular expression.
74
+ #
75
+ #: () -> Integer
76
+ def options
77
+ RegularExpressionOptions.options(flags)
78
+ end
79
+ end
80
+
81
+ private_constant :RegularExpressionOptions
82
+
83
+ module HeredocQuery # :nodoc:
84
+ # Returns true if this node was represented as a heredoc in the source code.
85
+ #
86
+ #: (String? opening) -> bool?
87
+ def self.heredoc?(opening)
88
+ # @type self: InterpolatedStringNode | InterpolatedXStringNode | StringNode | XStringNode
89
+ opening&.start_with?("<<")
90
+ end
91
+ end
92
+
93
+ class InterpolatedStringNode < Node
94
+ # Returns true if this node was represented as a heredoc in the source code.
95
+ #
96
+ #: () -> bool?
97
+ def heredoc?
98
+ HeredocQuery.heredoc?(opening)
99
+ end
100
+ end
101
+
102
+ class InterpolatedXStringNode < Node
103
+ # Returns true if this node was represented as a heredoc in the source code.
104
+ #
105
+ #: () -> bool?
106
+ def heredoc?
107
+ HeredocQuery.heredoc?(opening)
108
+ end
109
+ end
110
+
111
+ class StringNode < Node
112
+ # Returns true if this node was represented as a heredoc in the source code.
113
+ #
114
+ #: () -> bool?
115
+ def heredoc?
116
+ HeredocQuery.heredoc?(opening)
117
+ end
118
+
119
+ # Occasionally it's helpful to treat a string as if it were interpolated so
120
+ # that there's a consistent interface for working with strings.
121
+ #
122
+ #: () -> InterpolatedStringNode
123
+ def to_interpolated
124
+ InterpolatedStringNode.new(
125
+ source,
126
+ -1,
127
+ location,
128
+ frozen? ? InterpolatedStringNodeFlags::FROZEN : 0,
129
+ opening_loc,
130
+ [copy(location: content_loc, opening_loc: nil, closing_loc: nil)],
131
+ closing_loc
132
+ )
133
+ end
134
+ end
135
+
136
+ class XStringNode < Node
137
+ # Returns true if this node was represented as a heredoc in the source code.
138
+ #
139
+ #: () -> bool?
140
+ def heredoc?
141
+ HeredocQuery.heredoc?(opening)
142
+ end
143
+
144
+ # Occasionally it's helpful to treat a string as if it were interpolated so
145
+ # that there's a consistent interface for working with strings.
146
+ #
147
+ #: () -> InterpolatedXStringNode
148
+ def to_interpolated
149
+ InterpolatedXStringNode.new(
150
+ source,
151
+ -1,
152
+ location,
153
+ flags,
154
+ opening_loc,
155
+ [StringNode.new(source, node_id, content_loc, 0, nil, content_loc, nil, unescaped)],
156
+ closing_loc
157
+ )
158
+ end
159
+ end
160
+
161
+ private_constant :HeredocQuery
162
+
163
+ class ImaginaryNode < Node
164
+ # Returns the value of the node as a Ruby Complex.
165
+ #
166
+ #: () -> Complex
167
+ def value
168
+ Complex(0, numeric.value)
169
+ end
170
+ end
171
+
172
+ class RationalNode < Node
173
+ # Returns the value of the node as a Ruby Rational.
174
+ #
175
+ #: () -> Rational
176
+ def value
177
+ Rational(numerator, denominator)
178
+ end
179
+ end
180
+
181
+ class ConstantReadNode < Node
182
+ # Returns the list of parts for the full name of this constant.
183
+ # For example: [:Foo]
184
+ #
185
+ #: () -> Array[Symbol]
186
+ def full_name_parts
187
+ [name]
188
+ end
189
+
190
+ # Returns the full name of this constant. For example: "Foo"
191
+ #
192
+ #: () -> String
193
+ def full_name
194
+ name.to_s
195
+ end
196
+ end
197
+
198
+ class ConstantWriteNode < Node
199
+ # Returns the list of parts for the full name of this constant.
200
+ # For example: [:Foo]
201
+ #
202
+ #: () -> Array[Symbol]
203
+ def full_name_parts
204
+ [name]
205
+ end
206
+
207
+ # Returns the full name of this constant. For example: "Foo"
208
+ #
209
+ #: () -> String
210
+ def full_name
211
+ name.to_s
212
+ end
213
+ end
214
+
215
+ class ConstantPathNode < Node
216
+ # An error class raised when dynamic parts are found while computing a
217
+ # constant path's full name. For example:
218
+ # Foo::Bar::Baz -> does not raise because all parts of the constant path are
219
+ # simple constants
220
+ # var::Bar::Baz -> raises because the first part of the constant path is a
221
+ # local variable
222
+ class DynamicPartsInConstantPathError < StandardError; end
223
+
224
+ # An error class raised when error recovery nodes are found while computing a
225
+ # constant path's full name. For example:
226
+ # Foo:: -> raises because the constant path is missing the last part
227
+ class ErrorRecoveryNodesInConstantPathError < StandardError; end
228
+
229
+ # Returns the list of parts for the full name of this constant path.
230
+ # For example: [:Foo, :Bar]
231
+ #
232
+ #: () -> Array[Symbol]
233
+ def full_name_parts
234
+ parts = [] #: Array[Symbol]
235
+ current = self #: node?
236
+
237
+ while current.is_a?(ConstantPathNode)
238
+ name = current.name
239
+ if name.nil?
240
+ raise ErrorRecoveryNodesInConstantPathError, "Constant path contains error recovery nodes. Cannot compute full name"
241
+ end
242
+
243
+ parts.unshift(name)
244
+ current = current.parent
245
+ end
246
+
247
+ if !current.is_a?(ConstantReadNode) && !current.nil?
248
+ raise DynamicPartsInConstantPathError, "Constant path contains dynamic parts. Cannot compute full name"
249
+ end
250
+
251
+ parts.unshift(current&.name || :"")
252
+ end
253
+
254
+ # Returns the full name of this constant path. For example: "Foo::Bar"
255
+ #
256
+ #: () -> String
257
+ def full_name
258
+ full_name_parts.join("::")
259
+ end
260
+ end
261
+
262
+ class ConstantPathTargetNode < Node
263
+ # Returns the list of parts for the full name of this constant path.
264
+ # For example: [:Foo, :Bar]
265
+ #
266
+ #: () -> Array[Symbol]
267
+ def full_name_parts
268
+ parts =
269
+ case (parent = self.parent)
270
+ when ConstantPathNode, ConstantReadNode
271
+ parent.full_name_parts
272
+ when nil
273
+ [:""]
274
+ else
275
+ # e.g. self::Foo, (var)::Bar = baz
276
+ raise ConstantPathNode::DynamicPartsInConstantPathError, "Constant target path contains dynamic parts. Cannot compute full name"
277
+ end
278
+
279
+ if (name = self.name).nil?
280
+ raise ConstantPathNode::ErrorRecoveryNodesInConstantPathError, "Constant target path contains error recovery nodes. Cannot compute full name"
281
+ end
282
+
283
+ parts.push(name)
284
+ end
285
+
286
+ # Returns the full name of this constant path. For example: "Foo::Bar"
287
+ #
288
+ #: () -> String
289
+ def full_name
290
+ full_name_parts.join("::")
291
+ end
292
+ end
293
+
294
+ class ConstantTargetNode < Node
295
+ # Returns the list of parts for the full name of this constant.
296
+ # For example: [:Foo]
297
+ #
298
+ #: () -> Array[Symbol]
299
+ def full_name_parts
300
+ [name]
301
+ end
302
+
303
+ # Returns the full name of this constant. For example: "Foo"
304
+ #
305
+ #: () -> String
306
+ def full_name
307
+ name.to_s
308
+ end
309
+ end
310
+
311
+ class ParametersNode < Node
312
+ # Mirrors the Method#parameters method.
313
+ #
314
+ #: () -> Array[[Symbol, Symbol] | [Symbol]]
315
+ def signature
316
+ names = [] #: Array[[Symbol, Symbol] | [Symbol]]
317
+
318
+ requireds.each do |param|
319
+ names << (param.is_a?(MultiTargetNode) ? [:req] : [:req, param.name])
320
+ end
321
+
322
+ optionals.each { |param| names << [:opt, param.name] }
323
+
324
+ if (rest = self.rest).is_a?(RestParameterNode)
325
+ names << [:rest, rest.name || :*]
326
+ end
327
+
328
+ posts.each do |param|
329
+ case param
330
+ when MultiTargetNode
331
+ names << [:req]
332
+ when ErrorRecoveryNode
333
+ raise "Invalid syntax"
334
+ else
335
+ names << [:req, param.name]
336
+ end
337
+ end
338
+
339
+ # Regardless of the order in which the keywords were defined, the required
340
+ # keywords always come first followed by the optional keywords.
341
+ keyopt = [] #: Array[OptionalKeywordParameterNode]
342
+ keywords.each do |param|
343
+ if param.is_a?(OptionalKeywordParameterNode)
344
+ keyopt << param
345
+ else
346
+ names << [:keyreq, param.name]
347
+ end
348
+ end
349
+
350
+ keyopt.each { |param| names << [:key, param.name] }
351
+
352
+ case (keyword_rest = self.keyword_rest)
353
+ when ForwardingParameterNode
354
+ names.concat([[:rest, :*], [:keyrest, :**], [:block, :&]])
355
+ when KeywordRestParameterNode
356
+ names << [:keyrest, keyword_rest.name || :**]
357
+ when NoKeywordsParameterNode
358
+ names << [:nokey]
359
+ end
360
+
361
+ case (block = self.block)
362
+ when BlockParameterNode
363
+ names << [:block, block.name || :&]
364
+ when NoBlockParameterNode
365
+ names << [:noblock]
366
+ end
367
+
368
+ names
369
+ end
370
+ end
371
+
372
+ class CallNode < Node
373
+ # When a call node has the attribute_write flag set, it means that the call
374
+ # is using the attribute write syntax. This is either a method call to []=
375
+ # or a method call to a method that ends with =. Either way, the = sign is
376
+ # present in the source.
377
+ #
378
+ # Prism returns the message_loc _without_ the = sign attached, because there
379
+ # can be any amount of space between the message and the = sign. However,
380
+ # sometimes you want the location of the full message including the inner
381
+ # space and the = sign. This method provides that.
382
+ #
383
+ #: () -> Location?
384
+ def full_message_loc
385
+ attribute_write? ? message_loc&.adjoin("=") : message_loc
386
+ end
387
+ end
388
+
389
+ # :stopdoc:
390
+ class InNode < Node
391
+ #: () -> String
392
+ def in # :nodoc
393
+ in_keyword
394
+ end
395
+
396
+ #: () -> Location
397
+ def in_loc # :nodoc
398
+ in_keyword_loc
399
+ end
400
+
401
+ #: () -> String?
402
+ def then # :nodoc
403
+ then_keyword
404
+ end
405
+
406
+ #: () -> Location?
407
+ def then_loc # :nodoc
408
+ then_keyword_loc
409
+ end
410
+ end
411
+
412
+ class MatchPredicateNode < Node
413
+ #: () -> String
414
+ def operator # :nodoc
415
+ keyword
416
+ end
417
+
418
+ #: () -> Location
419
+ def operator_loc # :nodoc
420
+ keyword_loc
421
+ end
422
+ end
423
+
424
+ class UnlessNode < Node
425
+ #: () -> String
426
+ def keyword # :nodoc
427
+ unless_keyword
428
+ end
429
+
430
+ #: () -> Location
431
+ def keyword_loc # :nodoc
432
+ unless_keyword_loc
433
+ end
434
+ end
435
+
436
+ class UntilNode < Node
437
+ #: () -> String
438
+ def keyword # :nodoc
439
+ until_keyword
440
+ end
441
+
442
+ #: () -> Location
443
+ def keyword_loc # :nodoc
444
+ until_keyword_loc
445
+ end
446
+
447
+ #: () -> String?
448
+ def closing # :nodoc
449
+ end_keyword
450
+ end
451
+
452
+ #: () -> Location?
453
+ def closing_loc # :nodoc
454
+ end_keyword_loc
455
+ end
456
+ end
457
+
458
+ class WhenNode < Node
459
+ #: () -> String
460
+ def keyword # :nodoc
461
+ when_keyword
462
+ end
463
+
464
+ #: () -> Location
465
+ def keyword_loc # :nodoc
466
+ when_keyword_loc
467
+ end
468
+ end
469
+
470
+ class WhileNode < Node
471
+ #: () -> String
472
+ def keyword # :nodoc
473
+ while_keyword
474
+ end
475
+
476
+ #: () -> Location
477
+ def keyword_loc # :nodoc
478
+ while_keyword_loc
479
+ end
480
+
481
+ #: () -> String?
482
+ def closing # :nodoc
483
+ end_keyword
484
+ end
485
+
486
+ #: () -> Location?
487
+ def closing_loc # :nodoc
488
+ end_keyword_loc
489
+ end
490
+ end
491
+
492
+ class SymbolNode < Node
493
+ #: () -> String
494
+ def value # :nodoc
495
+ content
496
+ end
497
+
498
+ #: () -> Location
499
+ def value_loc # :nodoc
500
+ content_loc
501
+ end
502
+ end
503
+ # :startdoc:
504
+ end
@@ -0,0 +1,215 @@
1
+ # frozen_string_literal: true
2
+ # :markup: markdown
3
+ #--
4
+ # rbs_inline: enabled
5
+
6
+ module Prism
7
+ # Finds the Prism AST node corresponding to a given Method, UnboundMethod,
8
+ # Proc, or Thread::Backtrace::Location. On CRuby, uses node_id from the
9
+ # instruction sequence for an exact match. On other implementations, falls
10
+ # back to best-effort matching by source location line number.
11
+ #
12
+ # This module is autoloaded so that programs that don't use Prism.find don't
13
+ # pay for its definition.
14
+ module NodeFind # :nodoc:
15
+ # Find the node for the given callable or backtrace location.
16
+ #
17
+ #: (Method | UnboundMethod | Proc | Thread::Backtrace::Location callable) -> Node?
18
+ def self.find(callable)
19
+ if callable.respond_to?(:source_range) && !defined?(::RubyVM)
20
+ return SourceRangeFind.new.find(callable)
21
+ end
22
+
23
+ case callable
24
+ when Proc
25
+ if defined?(::RubyVM)
26
+ RubyVMCallableFind.new.find(callable)
27
+ elsif callable.lambda?
28
+ LineLambdaFind.new.find(callable)
29
+ else
30
+ LineProcFind.new.find(callable)
31
+ end
32
+ when Method, UnboundMethod
33
+ if defined?(::RubyVM)
34
+ RubyVMCallableFind.new.find(callable)
35
+ else
36
+ LineMethodFind.new.find(callable)
37
+ end
38
+ when Thread::Backtrace::Location
39
+ if defined?(::RubyVM)
40
+ RubyVMBacktraceLocationFind.new.find(callable)
41
+ else
42
+ LineBacktraceLocationFind.new.find(callable)
43
+ end
44
+ else
45
+ raise ArgumentError, "Expected a Method, UnboundMethod, Proc, or Thread::Backtrace::Location, got #{callable.class}"
46
+ end
47
+ end
48
+
49
+ # Base class that handles parsing a file.
50
+ class Find
51
+ private
52
+
53
+ # Parse the given file path, returning a ParseResult or nil.
54
+ #
55
+ #: (String? file) -> ParseResult?
56
+ def parse_file(file)
57
+ return unless file && File.readable?(file)
58
+ result = Prism.parse_file(file)
59
+ result if result.success?
60
+ end
61
+ end
62
+
63
+ # Finds the AST node for a Method, UnboundMethod, Proc or Thread::Backtrace::Location
64
+ # using the #source_range
65
+ class SourceRangeFind < Find
66
+ # Find the node for the given callable using the #source_range.
67
+ #
68
+ #: (Method | UnboundMethod | Proc | Thread::Backtrace::Location callable) -> Node?
69
+ def find(callable)
70
+ begin
71
+ range = callable.source_range
72
+ rescue ArgumentError # eval
73
+ return
74
+ end
75
+ return unless range
76
+ return unless (result = parse_file(range.absolute_path))
77
+
78
+ start_offset = result.source.byte_offset(range.start_line, range.start_column)
79
+ end_offset = result.source.byte_offset(range.end_line, range.end_column)
80
+ result.value.tunnel(range.start_line, range.start_column).reverse_each do |node|
81
+ if node.start_offset == start_offset && node.end_offset == end_offset
82
+ return node
83
+ end
84
+ end
85
+ nil
86
+ end
87
+ end
88
+
89
+ # Finds the AST node for a Method, UnboundMethod, or Proc using the node_id
90
+ # from the instruction sequence.
91
+ class RubyVMCallableFind < Find
92
+ # Find the node for the given callable using the ISeq node_id.
93
+ #
94
+ #: (Method | UnboundMethod | Proc callable) -> Node?
95
+ def find(callable)
96
+ return unless (source_location = callable.source_location)
97
+ return unless (result = parse_file(source_location[0]))
98
+ return unless (iseq = RubyVM::InstructionSequence.of(callable))
99
+
100
+ header = iseq.to_a[4]
101
+ return unless header[:parser] == :prism
102
+
103
+ result.value.find { |node| node.node_id == header[:node_id] }
104
+ end
105
+ end
106
+
107
+ # Finds the AST node for a Thread::Backtrace::Location using the node_id
108
+ # from the backtrace location.
109
+ class RubyVMBacktraceLocationFind < Find
110
+ # Find the node for the given backtrace location using node_id.
111
+ #
112
+ #: (Thread::Backtrace::Location location) -> Node?
113
+ def find(location)
114
+ file = location.absolute_path || location.path
115
+ return unless (result = parse_file(file))
116
+ return unless RubyVM::AbstractSyntaxTree.respond_to?(:node_id_for_backtrace_location)
117
+
118
+ node_id = RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location(location)
119
+
120
+ result.value.find { |node| node.node_id == node_id }
121
+ end
122
+ end
123
+
124
+ # Finds the AST node for a Method or UnboundMethod using best-effort line
125
+ # matching. Used on non-CRuby implementations.
126
+ class LineMethodFind < Find
127
+ # Find the node for the given method by matching on name and line.
128
+ #
129
+ #: (Method | UnboundMethod callable) -> Node?
130
+ def find(callable)
131
+ return unless (source_location = callable.source_location)
132
+ return unless (result = parse_file(source_location[0]))
133
+
134
+ name = callable.name
135
+ start_line = source_location[1]
136
+
137
+ result.value.find do |node|
138
+ case node
139
+ when DefNode
140
+ node.name == name && node.location.start_line == start_line
141
+ when CallNode
142
+ node.block.is_a?(BlockNode) && node.location.start_line == start_line
143
+ else
144
+ false
145
+ end
146
+ end
147
+ end
148
+ end
149
+
150
+ # Finds the AST node for a lambda using best-effort line matching. Used
151
+ # on non-CRuby implementations.
152
+ class LineLambdaFind < Find
153
+ # Find the node for the given lambda by matching on line.
154
+ #
155
+ #: (Proc callable) -> Node?
156
+ def find(callable)
157
+ return unless (source_location = callable.source_location)
158
+ return unless (result = parse_file(source_location[0]))
159
+
160
+ start_line = source_location[1]
161
+
162
+ result.value.find do |node|
163
+ case node
164
+ when LambdaNode
165
+ node.location.start_line == start_line
166
+ when CallNode
167
+ node.block.is_a?(BlockNode) && node.location.start_line == start_line
168
+ else
169
+ false
170
+ end
171
+ end
172
+ end
173
+ end
174
+
175
+ # Finds the AST node for a non-lambda Proc using best-effort line
176
+ # matching. Used on non-CRuby implementations.
177
+ class LineProcFind < Find
178
+ # Find the node for the given proc by matching on line.
179
+ #
180
+ #: (Proc callable) -> Node?
181
+ def find(callable)
182
+ return unless (source_location = callable.source_location)
183
+ return unless (result = parse_file(source_location[0]))
184
+
185
+ start_line = source_location[1]
186
+
187
+ result.value.find do |node|
188
+ case node
189
+ when ForNode
190
+ node.location.start_line == start_line
191
+ when CallNode
192
+ node.block.is_a?(BlockNode) && node.location.start_line == start_line
193
+ else
194
+ false
195
+ end
196
+ end
197
+ end
198
+ end
199
+
200
+ # Finds the AST node for a Thread::Backtrace::Location using best-effort
201
+ # line matching. Used on non-CRuby implementations.
202
+ class LineBacktraceLocationFind < Find
203
+ # Find the node for the given backtrace location by matching on line.
204
+ #
205
+ #: (Thread::Backtrace::Location location) -> Node?
206
+ def find(location)
207
+ file = location.absolute_path || location.path
208
+ return unless (result = parse_file(file))
209
+
210
+ start_line = location.lineno
211
+ result.value.find { |node| node.location.start_line == start_line }
212
+ end
213
+ end
214
+ end
215
+ end