prism 2.0.0.rc1-arm64-darwin
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.
- checksums.yaml +7 -0
- data/BSDmakefile +58 -0
- data/CHANGELOG.md +870 -0
- data/CODE_OF_CONDUCT.md +76 -0
- data/CONTRIBUTING.md +58 -0
- data/LICENSE.md +7 -0
- data/Makefile +145 -0
- data/README.md +81 -0
- data/config.yml +4752 -0
- data/docs/build_system.md +119 -0
- data/docs/configuration.md +69 -0
- data/docs/cruby_compilation.md +27 -0
- data/docs/design.md +53 -0
- data/docs/encoding.md +121 -0
- data/docs/fuzzing.md +81 -0
- data/docs/heredocs.md +36 -0
- data/docs/javascript.md +133 -0
- data/docs/local_variable_depth.md +229 -0
- data/docs/mapping.md +117 -0
- data/docs/parser_translation.md +24 -0
- data/docs/parsing_rules.md +22 -0
- data/docs/releasing.md +147 -0
- data/docs/relocation.md +32 -0
- data/docs/ripper_translation.md +63 -0
- data/docs/ruby_api.md +45 -0
- data/docs/ruby_parser_translation.md +19 -0
- data/docs/serialization.md +230 -0
- data/docs/testing.md +72 -0
- data/ext/prism/api_node.c +7005 -0
- data/ext/prism/extconf.rb +126 -0
- data/ext/prism/extension.c +1783 -0
- data/ext/prism/extension.h +19 -0
- data/include/prism/arena.h +37 -0
- data/include/prism/ast.h +10371 -0
- data/include/prism/buffer.h +52 -0
- data/include/prism/comments.h +55 -0
- data/include/prism/compiler/accel.h +24 -0
- data/include/prism/compiler/align.h +36 -0
- data/include/prism/compiler/assume.h +25 -0
- data/include/prism/compiler/exported.h +24 -0
- data/include/prism/compiler/fallthrough.h +22 -0
- data/include/prism/compiler/filesystem.h +32 -0
- data/include/prism/compiler/flex_array.h +19 -0
- data/include/prism/compiler/force_inline.h +21 -0
- data/include/prism/compiler/format.h +25 -0
- data/include/prism/compiler/inline.h +17 -0
- data/include/prism/compiler/nodiscard.h +22 -0
- data/include/prism/compiler/nonnull.h +18 -0
- data/include/prism/compiler/unused.h +18 -0
- data/include/prism/constant_pool.h +81 -0
- data/include/prism/diagnostic.h +93 -0
- data/include/prism/errors_format.h +53 -0
- data/include/prism/excludes.h +29 -0
- data/include/prism/integer.h +41 -0
- data/include/prism/internal/allocator.h +68 -0
- data/include/prism/internal/allocator_debug.h +88 -0
- data/include/prism/internal/arena.h +108 -0
- data/include/prism/internal/bit.h +42 -0
- data/include/prism/internal/buffer.h +91 -0
- data/include/prism/internal/char.h +145 -0
- data/include/prism/internal/comments.h +53 -0
- data/include/prism/internal/constant_pool.h +152 -0
- data/include/prism/internal/diagnostic.h +389 -0
- data/include/prism/internal/encoding.h +242 -0
- data/include/prism/internal/integer.h +68 -0
- data/include/prism/internal/isinf.h +16 -0
- data/include/prism/internal/line_offset_list.h +34 -0
- data/include/prism/internal/list.h +62 -0
- data/include/prism/internal/magic_comments.h +23 -0
- data/include/prism/internal/memchr.h +15 -0
- data/include/prism/internal/node.h +38 -0
- data/include/prism/internal/options.h +232 -0
- data/include/prism/internal/parser.h +984 -0
- data/include/prism/internal/regexp.h +26 -0
- data/include/prism/internal/serialize.h +39 -0
- data/include/prism/internal/source.h +72 -0
- data/include/prism/internal/static_literals.h +100 -0
- data/include/prism/internal/stringy.h +35 -0
- data/include/prism/internal/strncasecmp.h +18 -0
- data/include/prism/internal/strpbrk.h +33 -0
- data/include/prism/internal/tokens.h +11 -0
- data/include/prism/json.h +32 -0
- data/include/prism/line_offset_list.h +61 -0
- data/include/prism/magic_comments.h +35 -0
- data/include/prism/node.h +94 -0
- data/include/prism/options.h +354 -0
- data/include/prism/parser.h +404 -0
- data/include/prism/prettyprint.h +31 -0
- data/include/prism/serialize.h +115 -0
- data/include/prism/source.h +155 -0
- data/include/prism/stream.h +28 -0
- data/include/prism/string_query.h +63 -0
- data/include/prism/stringy.h +72 -0
- data/include/prism/version.h +38 -0
- data/include/prism.h +131 -0
- data/lib/prism/3.3/prism.bundle +0 -0
- data/lib/prism/3.4/prism.bundle +0 -0
- data/lib/prism/4.0/prism.bundle +0 -0
- data/lib/prism/compiler.rb +815 -0
- data/lib/prism/desugar_compiler.rb +463 -0
- data/lib/prism/dispatcher.rb +2383 -0
- data/lib/prism/dot_visitor.rb +4848 -0
- data/lib/prism/dsl.rb +1369 -0
- data/lib/prism/ffi.rb +740 -0
- data/lib/prism/inspect_visitor.rb +2425 -0
- data/lib/prism/lex_compat.rb +953 -0
- data/lib/prism/mutation_compiler.rb +780 -0
- data/lib/prism/node.rb +33204 -0
- data/lib/prism/node_ext.rb +504 -0
- data/lib/prism/node_find.rb +215 -0
- data/lib/prism/parse_result/errors.rb +72 -0
- data/lib/prism/parse_result/newlines.rb +423 -0
- data/lib/prism/parse_result.rb +1221 -0
- data/lib/prism/pattern.rb +314 -0
- data/lib/prism/polyfill/append_as_bytes.rb +15 -0
- data/lib/prism/polyfill/byteindex.rb +13 -0
- data/lib/prism/polyfill/scan_byte.rb +14 -0
- data/lib/prism/polyfill/unpack1.rb +14 -0
- data/lib/prism/polyfill/warn.rb +36 -0
- data/lib/prism/reflection.rb +427 -0
- data/lib/prism/relocation.rb +567 -0
- data/lib/prism/serialize.rb +5308 -0
- data/lib/prism/string_query.rb +46 -0
- data/lib/prism/translation/parser/builder.rb +70 -0
- data/lib/prism/translation/parser/compiler.rb +2226 -0
- data/lib/prism/translation/parser/lexer.rb +769 -0
- data/lib/prism/translation/parser.rb +376 -0
- data/lib/prism/translation/parser_current.rb +26 -0
- data/lib/prism/translation/parser_versions.rb +36 -0
- data/lib/prism/translation/ripper/filter.rb +53 -0
- data/lib/prism/translation/ripper/lexer.rb +133 -0
- data/lib/prism/translation/ripper/sexp.rb +118 -0
- data/lib/prism/translation/ripper/shim.rb +7 -0
- data/lib/prism/translation/ripper.rb +4278 -0
- data/lib/prism/translation/ruby_parser.rb +1721 -0
- data/lib/prism/translation.rb +20 -0
- data/lib/prism/visitor.rb +1440 -0
- data/lib/prism.rb +168 -0
- data/prism.gemspec +234 -0
- data/rbi/generated/prism/compiler.rbi +489 -0
- data/rbi/generated/prism/desugar_compiler.rbi +306 -0
- data/rbi/generated/prism/dispatcher.rbi +1138 -0
- data/rbi/generated/prism/dot_visitor.rbi +618 -0
- data/rbi/generated/prism/dsl.rbi +751 -0
- data/rbi/generated/prism/inspect_visitor.rbi +503 -0
- data/rbi/generated/prism/lex_compat.rbi +162 -0
- data/rbi/generated/prism/mutation_compiler.rbi +464 -0
- data/rbi/generated/prism/node.rbi +13681 -0
- data/rbi/generated/prism/node_ext.rbi +259 -0
- data/rbi/generated/prism/node_find.rbi +79 -0
- data/rbi/generated/prism/parse_result/errors.rbi +21 -0
- data/rbi/generated/prism/parse_result/newlines.rbi +208 -0
- data/rbi/generated/prism/parse_result.rbi +764 -0
- data/rbi/generated/prism/pattern.rbi +134 -0
- data/rbi/generated/prism/reflection.rbi +89 -0
- data/rbi/generated/prism/relocation.rbi +329 -0
- data/rbi/generated/prism/serialize.rbi +181 -0
- data/rbi/generated/prism/string_query.rbi +36 -0
- data/rbi/generated/prism/translation.rbi +7 -0
- data/rbi/generated/prism/visitor.rbi +650 -0
- data/rbi/generated/prism.rbi +86 -0
- data/rbi/prism/translation/parser.rbi +11 -0
- data/rbi/prism/translation/parser_versions.rbi +28 -0
- data/rbi/prism/translation/ripper.rbi +15 -0
- data/rbi/rubyvm/node_find.rbi +14 -0
- data/sig/generated/prism/compiler.rbs +492 -0
- data/sig/generated/prism/desugar_compiler.rbs +305 -0
- data/sig/generated/prism/dispatcher.rbs +1292 -0
- data/sig/generated/prism/dot_visitor.rbs +635 -0
- data/sig/generated/prism/dsl.rbs +928 -0
- data/sig/generated/prism/inspect_visitor.rbs +509 -0
- data/sig/generated/prism/lex_compat.rbs +166 -0
- data/sig/generated/prism/mutation_compiler.rbs +464 -0
- data/sig/generated/prism/node.rbs +20160 -0
- data/sig/generated/prism/node_ext.rbs +290 -0
- data/sig/generated/prism/node_find.rbs +90 -0
- data/sig/generated/prism/parse_result/errors.rbs +22 -0
- data/sig/generated/prism/parse_result/newlines.rbs +223 -0
- data/sig/generated/prism/parse_result.rbs +859 -0
- data/sig/generated/prism/pattern.rbs +156 -0
- data/sig/generated/prism/reflection.rbs +101 -0
- data/sig/generated/prism/relocation.rbs +428 -0
- data/sig/generated/prism/serialize.rbs +195 -0
- data/sig/generated/prism/string_query.rbs +36 -0
- data/sig/generated/prism/translation.rbs +8 -0
- data/sig/generated/prism/visitor.rbs +1111 -0
- data/sig/generated/prism.rbs +84 -0
- data/sig/manifest.yaml +2 -0
- data/src/arena.c +117 -0
- data/src/buffer.c +380 -0
- data/src/char.c +289 -0
- data/src/comments.c +454 -0
- data/src/constant_pool.c +406 -0
- data/src/diagnostic.c +886 -0
- data/src/encoding.c +5345 -0
- data/src/errors_format.c +519 -0
- data/src/integer.c +681 -0
- data/src/json.c +5733 -0
- data/src/line_offset_list.c +100 -0
- data/src/list.c +24 -0
- data/src/memchr.c +37 -0
- data/src/node.c +4515 -0
- data/src/options.c +451 -0
- data/src/parser.c +349 -0
- data/src/prettyprint.c +9024 -0
- data/src/prism.c +23739 -0
- data/src/regexp.c +1706 -0
- data/src/serialize.c +2309 -0
- data/src/source.c +518 -0
- data/src/static_literals.c +685 -0
- data/src/string_query.c +166 -0
- data/src/stringy.c +91 -0
- data/src/strncasecmp.c +37 -0
- data/src/strpbrk.c +447 -0
- data/src/tokens.c +724 -0
- 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
|