syntax_tree 6.0.1 → 6.1.0
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 +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +1 -1
- data/.ruby-version +1 -0
- data/CHANGELOG.md +27 -1
- data/Gemfile.lock +7 -7
- data/README.md +29 -0
- data/lib/syntax_tree/cli.rb +91 -0
- data/lib/syntax_tree/field_visitor.rb +2 -0
- data/lib/syntax_tree/index.rb +233 -23
- data/lib/syntax_tree/node.rb +65 -32
- data/lib/syntax_tree/parser.rb +14 -1
- data/lib/syntax_tree/reflection.rb +22 -6
- data/lib/syntax_tree/version.rb +1 -1
- data/lib/syntax_tree/with_scope.rb +71 -1
- data/lib/syntax_tree/yarv/compiler.rb +1 -2
- data/lib/syntax_tree/yarv/instruction_sequence.rb +14 -3
- data/lib/syntax_tree/yarv/instructions.rb +58 -0
- data/lib/syntax_tree.rb +1 -0
- data/tasks/sorbet.rake +96 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7515a827509d352259e6cae476930093fe382fcc35c248fba3ab716019e69ca9
|
4
|
+
data.tar.gz: add2625387abe5616f90d28843f4fcebac5b68222f9a6258cbfb04fbd41b4772
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ea0a666bd65180facaab89acd7b7bd87d3f5208c69a0fe80b91d1d25d57cdc2c281ac301070da8d73c7d119574a7f7de30c0facb0d7131f895bbc8fa7fe582c5
|
7
|
+
data.tar.gz: 4c9df489dcb876280a219a8e213a800f0544a0aaa02a303d8fc3ba948e07e195dbfb21da8f8f04825abc4bf61fb9ec92141d5a00cc7b3d53b1d6103d58d10be3
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -7,7 +7,7 @@ AllCops:
|
|
7
7
|
SuggestExtensions: false
|
8
8
|
TargetRubyVersion: 2.7
|
9
9
|
Exclude:
|
10
|
-
- '{.git,.github,bin,coverage,pkg,spec,test/fixtures,vendor,tmp}/**/*'
|
10
|
+
- '{.git,.github,bin,coverage,pkg,sorbet,spec,test/fixtures,vendor,tmp}/**/*'
|
11
11
|
- test.rb
|
12
12
|
|
13
13
|
Gemspec/DevelopmentDependencies:
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.2.0
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,31 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [6.1.0] - 2023-03-20
|
10
|
+
|
11
|
+
### Added
|
12
|
+
|
13
|
+
- The `stree ctags` command for generating ctags like `universal-ctags` or `ripper-tags` would.
|
14
|
+
- The `definedivar` YARV instruction has been added to match CRuby's implementation.
|
15
|
+
- We now generate better Sorbet RBI files for the nodes in the tree and the visitors.
|
16
|
+
- `SyntaxTree::Reflection.nodes` now includes the visitor method.
|
17
|
+
|
18
|
+
### Changed
|
19
|
+
|
20
|
+
- We now explicitly require `pp` in environments that need it.
|
21
|
+
|
22
|
+
## [6.0.2] - 2023-03-03
|
23
|
+
|
24
|
+
### Added
|
25
|
+
|
26
|
+
- The `WithScope` visitor mixin will now additionally report local variables defined through regular expression named captures.
|
27
|
+
- The `WithScope` visitor mixin now properly handles destructured splat arguments in required positions.
|
28
|
+
|
29
|
+
### Changed
|
30
|
+
|
31
|
+
- Fixed the AST output by adding blocks to `Command` and `CommandCall` nodes in the `FieldVisitor`.
|
32
|
+
- Fixed the location of lambda local variables (e.g., `->(; a) {}`).
|
33
|
+
|
9
34
|
## [6.0.1] - 2023-02-26
|
10
35
|
|
11
36
|
### Added
|
@@ -572,7 +597,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
|
|
572
597
|
|
573
598
|
- 🎉 Initial release! 🎉
|
574
599
|
|
575
|
-
[unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v6.0.
|
600
|
+
[unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v6.0.2...HEAD
|
601
|
+
[6.0.2]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v6.0.1...v6.0.2
|
576
602
|
[6.0.1]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v6.0.0...v6.0.1
|
577
603
|
[6.0.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v5.3.0...v6.0.0
|
578
604
|
[5.3.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v5.2.0...v5.3.0
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
syntax_tree (6.0
|
4
|
+
syntax_tree (6.1.0)
|
5
5
|
prettier_print (>= 1.2.0)
|
6
6
|
|
7
7
|
GEM
|
@@ -10,16 +10,16 @@ GEM
|
|
10
10
|
ast (2.4.2)
|
11
11
|
docile (1.4.0)
|
12
12
|
json (2.6.3)
|
13
|
-
minitest (5.
|
13
|
+
minitest (5.18.0)
|
14
14
|
parallel (1.22.1)
|
15
|
-
parser (3.2.1.
|
15
|
+
parser (3.2.1.1)
|
16
16
|
ast (~> 2.4.1)
|
17
|
-
prettier_print (1.2.
|
17
|
+
prettier_print (1.2.1)
|
18
18
|
rainbow (3.1.1)
|
19
19
|
rake (13.0.6)
|
20
20
|
regexp_parser (2.7.0)
|
21
21
|
rexml (3.2.5)
|
22
|
-
rubocop (1.
|
22
|
+
rubocop (1.48.1)
|
23
23
|
json (~> 2.3)
|
24
24
|
parallel (~> 1.10)
|
25
25
|
parser (>= 3.2.0.0)
|
@@ -29,9 +29,9 @@ GEM
|
|
29
29
|
rubocop-ast (>= 1.26.0, < 2.0)
|
30
30
|
ruby-progressbar (~> 1.7)
|
31
31
|
unicode-display_width (>= 2.4.0, < 3.0)
|
32
|
-
rubocop-ast (1.
|
32
|
+
rubocop-ast (1.27.0)
|
33
33
|
parser (>= 3.2.1.0)
|
34
|
-
ruby-progressbar (1.
|
34
|
+
ruby-progressbar (1.13.0)
|
35
35
|
simplecov (0.22.0)
|
36
36
|
docile (~> 1.1)
|
37
37
|
simplecov-html (~> 0.11)
|
data/README.md
CHANGED
@@ -15,6 +15,7 @@ It is built with only standard library dependencies. It additionally ships with
|
|
15
15
|
- [CLI](#cli)
|
16
16
|
- [ast](#ast)
|
17
17
|
- [check](#check)
|
18
|
+
- [ctags](#ctags)
|
18
19
|
- [expr](#expr)
|
19
20
|
- [format](#format)
|
20
21
|
- [json](#json)
|
@@ -139,6 +140,33 @@ To change the print width that you are checking against, specify the `--print-wi
|
|
139
140
|
stree check --print-width=100 path/to/file.rb
|
140
141
|
```
|
141
142
|
|
143
|
+
### ctags
|
144
|
+
|
145
|
+
This command will output to stdout a set of tags suitable for usage with [ctags](https://github.com/universal-ctags/ctags).
|
146
|
+
|
147
|
+
```sh
|
148
|
+
stree ctags path/to/file.rb
|
149
|
+
```
|
150
|
+
|
151
|
+
For a file containing the following Ruby code:
|
152
|
+
|
153
|
+
```ruby
|
154
|
+
class Foo
|
155
|
+
end
|
156
|
+
|
157
|
+
class Bar < Foo
|
158
|
+
end
|
159
|
+
```
|
160
|
+
|
161
|
+
you will receive:
|
162
|
+
|
163
|
+
```
|
164
|
+
!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/
|
165
|
+
!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/
|
166
|
+
Bar test.rb /^class Bar < Foo$/;" c inherits:Foo
|
167
|
+
Foo test.rb /^class Foo$/;" c
|
168
|
+
```
|
169
|
+
|
142
170
|
### expr
|
143
171
|
|
144
172
|
This command will output a Ruby case-match expression that would match correctly against the first expression of the input.
|
@@ -788,6 +816,7 @@ inherit_gem:
|
|
788
816
|
* [Neovim](https://neovim.io/) - [neovim/nvim-lspconfig](https://github.com/neovim/nvim-lspconfig).
|
789
817
|
* [Vim](https://www.vim.org/) - [dense-analysis/ale](https://github.com/dense-analysis/ale).
|
790
818
|
* [VSCode](https://code.visualstudio.com/) - [ruby-syntax-tree/vscode-syntax-tree](https://github.com/ruby-syntax-tree/vscode-syntax-tree).
|
819
|
+
* [Emacs](https://www.gnu.org/software/emacs/) - [emacs-format-all-the-code](https://github.com/lassik/emacs-format-all-the-code).
|
791
820
|
|
792
821
|
## Contributing
|
793
822
|
|
data/lib/syntax_tree/cli.rb
CHANGED
@@ -154,6 +154,92 @@ module SyntaxTree
|
|
154
154
|
end
|
155
155
|
end
|
156
156
|
|
157
|
+
# An action of the CLI that generates ctags for the given source.
|
158
|
+
class CTags < Action
|
159
|
+
attr_reader :entries
|
160
|
+
|
161
|
+
def initialize(options)
|
162
|
+
super(options)
|
163
|
+
@entries = []
|
164
|
+
end
|
165
|
+
|
166
|
+
def run(item)
|
167
|
+
lines = item.source.lines(chomp: true)
|
168
|
+
|
169
|
+
SyntaxTree
|
170
|
+
.index(item.source)
|
171
|
+
.each do |entry|
|
172
|
+
line = lines[entry.location.line - 1]
|
173
|
+
pattern = "/^#{line.gsub("\\", "\\\\\\\\").gsub("/", "\\/")}$/;\""
|
174
|
+
|
175
|
+
entries << case entry
|
176
|
+
when SyntaxTree::Index::ModuleDefinition
|
177
|
+
parts = [entry.name, item.filepath, pattern, "m"]
|
178
|
+
|
179
|
+
if entry.nesting != [[entry.name]]
|
180
|
+
parts << "class:#{entry.nesting.flatten.tap(&:pop).join(".")}"
|
181
|
+
end
|
182
|
+
|
183
|
+
parts.join("\t")
|
184
|
+
when SyntaxTree::Index::ClassDefinition
|
185
|
+
parts = [entry.name, item.filepath, pattern, "c"]
|
186
|
+
|
187
|
+
if entry.nesting != [[entry.name]]
|
188
|
+
parts << "class:#{entry.nesting.flatten.tap(&:pop).join(".")}"
|
189
|
+
end
|
190
|
+
|
191
|
+
unless entry.superclass.empty?
|
192
|
+
inherits = entry.superclass.join(".").delete_prefix(".")
|
193
|
+
parts << "inherits:#{inherits}"
|
194
|
+
end
|
195
|
+
|
196
|
+
parts.join("\t")
|
197
|
+
when SyntaxTree::Index::MethodDefinition
|
198
|
+
parts = [entry.name, item.filepath, pattern, "f"]
|
199
|
+
|
200
|
+
unless entry.nesting.empty?
|
201
|
+
parts << "class:#{entry.nesting.flatten.join(".")}"
|
202
|
+
end
|
203
|
+
|
204
|
+
parts.join("\t")
|
205
|
+
when SyntaxTree::Index::SingletonMethodDefinition
|
206
|
+
parts = [entry.name, item.filepath, pattern, "F"]
|
207
|
+
|
208
|
+
unless entry.nesting.empty?
|
209
|
+
parts << "class:#{entry.nesting.flatten.join(".")}"
|
210
|
+
end
|
211
|
+
|
212
|
+
parts.join("\t")
|
213
|
+
when SyntaxTree::Index::AliasMethodDefinition
|
214
|
+
parts = [entry.name, item.filepath, pattern, "a"]
|
215
|
+
|
216
|
+
unless entry.nesting.empty?
|
217
|
+
parts << "class:#{entry.nesting.flatten.join(".")}"
|
218
|
+
end
|
219
|
+
|
220
|
+
parts.join("\t")
|
221
|
+
when SyntaxTree::Index::ConstantDefinition
|
222
|
+
parts = [entry.name, item.filepath, pattern, "C"]
|
223
|
+
|
224
|
+
unless entry.nesting.empty?
|
225
|
+
parts << "class:#{entry.nesting.flatten.join(".")}"
|
226
|
+
end
|
227
|
+
|
228
|
+
parts.join("\t")
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def success
|
234
|
+
puts(<<~HEADER)
|
235
|
+
!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/
|
236
|
+
!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/
|
237
|
+
HEADER
|
238
|
+
|
239
|
+
entries.sort.each { |entry| puts(entry) }
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
157
243
|
# An action of the CLI that formats the source twice to check if the first
|
158
244
|
# format is not idempotent.
|
159
245
|
class Debug < Action
|
@@ -327,6 +413,9 @@ module SyntaxTree
|
|
327
413
|
#{Color.bold("stree check [--plugins=...] [--print-width=NUMBER] [-e SCRIPT] FILE")}
|
328
414
|
Check that the given files are formatted as syntax tree would format them
|
329
415
|
|
416
|
+
#{Color.bold("stree ctags [-e SCRIPT] FILE")}
|
417
|
+
Print out a ctags-compatible index of the given files
|
418
|
+
|
330
419
|
#{Color.bold("stree debug [--plugins=...] [--print-width=NUMBER] [-e SCRIPT] FILE")}
|
331
420
|
Check that the given files can be formatted idempotently
|
332
421
|
|
@@ -488,6 +577,8 @@ module SyntaxTree
|
|
488
577
|
AST.new(options)
|
489
578
|
when "c", "check"
|
490
579
|
Check.new(options)
|
580
|
+
when "ctags"
|
581
|
+
CTags.new(options)
|
491
582
|
when "debug"
|
492
583
|
Debug.new(options)
|
493
584
|
when "doc"
|
@@ -263,6 +263,7 @@ module SyntaxTree
|
|
263
263
|
node(node, "command") do
|
264
264
|
field("message", node.message)
|
265
265
|
field("arguments", node.arguments)
|
266
|
+
field("block", node.block) if node.block
|
266
267
|
comments(node)
|
267
268
|
end
|
268
269
|
end
|
@@ -273,6 +274,7 @@ module SyntaxTree
|
|
273
274
|
field("operator", node.operator)
|
274
275
|
field("message", node.message)
|
275
276
|
field("arguments", node.arguments) if node.arguments
|
277
|
+
field("block", node.block) if node.block
|
276
278
|
comments(node)
|
277
279
|
end
|
278
280
|
end
|
data/lib/syntax_tree/index.rb
CHANGED
@@ -31,6 +31,18 @@ module SyntaxTree
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
+
# This entry represents a constant assignment.
|
35
|
+
class ConstantDefinition
|
36
|
+
attr_reader :nesting, :name, :location, :comments
|
37
|
+
|
38
|
+
def initialize(nesting, name, location, comments)
|
39
|
+
@nesting = nesting
|
40
|
+
@name = name
|
41
|
+
@location = location
|
42
|
+
@comments = comments
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
34
46
|
# This entry represents a module definition using the module keyword.
|
35
47
|
class ModuleDefinition
|
36
48
|
attr_reader :nesting, :name, :location, :comments
|
@@ -68,6 +80,19 @@ module SyntaxTree
|
|
68
80
|
end
|
69
81
|
end
|
70
82
|
|
83
|
+
# This entry represents a method definition that was created using the alias
|
84
|
+
# keyword.
|
85
|
+
class AliasMethodDefinition
|
86
|
+
attr_reader :nesting, :name, :location, :comments
|
87
|
+
|
88
|
+
def initialize(nesting, name, location, comments)
|
89
|
+
@nesting = nesting
|
90
|
+
@name = name
|
91
|
+
@location = location
|
92
|
+
@comments = comments
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
71
96
|
# When you're using the instruction sequence backend, this class is used to
|
72
97
|
# lazily parse comments out of the source code.
|
73
98
|
class FileComments
|
@@ -178,7 +203,14 @@ module SyntaxTree
|
|
178
203
|
end
|
179
204
|
|
180
205
|
def find_constant_path(insns, index)
|
181
|
-
index -= 1 while
|
206
|
+
index -= 1 while index >= 0 &&
|
207
|
+
(
|
208
|
+
insns[index].is_a?(Integer) ||
|
209
|
+
(
|
210
|
+
insns[index].is_a?(Array) &&
|
211
|
+
%i[swap topn].include?(insns[index][0])
|
212
|
+
)
|
213
|
+
)
|
182
214
|
insn = insns[index]
|
183
215
|
|
184
216
|
if insn.is_a?(Array) && insn[0] == :opt_getconstant_path
|
@@ -207,11 +239,43 @@ module SyntaxTree
|
|
207
239
|
end
|
208
240
|
end
|
209
241
|
|
242
|
+
def find_attr_arguments(insns, index)
|
243
|
+
orig_argc = insns[index][1][:orig_argc]
|
244
|
+
names = []
|
245
|
+
|
246
|
+
current = index - 1
|
247
|
+
while current >= 0 && names.length < orig_argc
|
248
|
+
if insns[current].is_a?(Array) && insns[current][0] == :putobject
|
249
|
+
names.unshift(insns[current][1])
|
250
|
+
end
|
251
|
+
|
252
|
+
current -= 1
|
253
|
+
end
|
254
|
+
|
255
|
+
names if insns[current] == [:putself] && names.length == orig_argc
|
256
|
+
end
|
257
|
+
|
258
|
+
def method_definition(nesting, name, location, file_comments)
|
259
|
+
comments = EntryComments.new(file_comments, location)
|
260
|
+
|
261
|
+
if nesting.last == [:singletonclass]
|
262
|
+
SingletonMethodDefinition.new(
|
263
|
+
nesting[0...-1],
|
264
|
+
name,
|
265
|
+
location,
|
266
|
+
comments
|
267
|
+
)
|
268
|
+
else
|
269
|
+
MethodDefinition.new(nesting, name, location, comments)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
210
273
|
def index_iseq(iseq, file_comments)
|
211
274
|
results = []
|
212
275
|
queue = [[iseq, []]]
|
213
276
|
|
214
277
|
while (current_iseq, current_nesting = queue.shift)
|
278
|
+
file = current_iseq[5]
|
215
279
|
line = current_iseq[8]
|
216
280
|
insns = current_iseq[13]
|
217
281
|
|
@@ -246,8 +310,8 @@ module SyntaxTree
|
|
246
310
|
find_constant_path(insns, index - 1)
|
247
311
|
|
248
312
|
if superclass.empty?
|
249
|
-
|
250
|
-
|
313
|
+
warn("#{file}:#{line}: superclass with non constant path")
|
314
|
+
next
|
251
315
|
end
|
252
316
|
end
|
253
317
|
|
@@ -265,8 +329,10 @@ module SyntaxTree
|
|
265
329
|
# defined on self. We could, but it would require more
|
266
330
|
# emulation.
|
267
331
|
if insns[index - 2] != [:putself]
|
268
|
-
|
269
|
-
|
332
|
+
warn(
|
333
|
+
"#{file}:#{line}: singleton class with non-self receiver"
|
334
|
+
)
|
335
|
+
next
|
270
336
|
end
|
271
337
|
elsif flags & VM_DEFINECLASS_TYPE_MODULE > 0
|
272
338
|
location = location_for(class_iseq)
|
@@ -290,16 +356,16 @@ module SyntaxTree
|
|
290
356
|
queue << [class_iseq, next_nesting]
|
291
357
|
when :definemethod
|
292
358
|
location = location_for(insn[2])
|
293
|
-
results <<
|
359
|
+
results << method_definition(
|
294
360
|
current_nesting,
|
295
361
|
insn[1],
|
296
362
|
location,
|
297
|
-
|
363
|
+
file_comments
|
298
364
|
)
|
299
365
|
when :definesmethod
|
300
|
-
if
|
301
|
-
|
302
|
-
|
366
|
+
if insns[index - 1] != [:putself]
|
367
|
+
warn("#{file}:#{line}: singleton method with non-self receiver")
|
368
|
+
next
|
303
369
|
end
|
304
370
|
|
305
371
|
location = location_for(insn[2])
|
@@ -309,6 +375,69 @@ module SyntaxTree
|
|
309
375
|
location,
|
310
376
|
EntryComments.new(file_comments, location)
|
311
377
|
)
|
378
|
+
when :setconstant
|
379
|
+
next_nesting = current_nesting.dup
|
380
|
+
name = insn[1]
|
381
|
+
|
382
|
+
_, nesting = find_constant_path(insns, index - 1)
|
383
|
+
next_nesting << nesting if nesting.any?
|
384
|
+
|
385
|
+
location = Location.new(line, :unknown)
|
386
|
+
results << ConstantDefinition.new(
|
387
|
+
next_nesting,
|
388
|
+
name,
|
389
|
+
location,
|
390
|
+
EntryComments.new(file_comments, location)
|
391
|
+
)
|
392
|
+
when :opt_send_without_block, :send
|
393
|
+
case insn[1][:mid]
|
394
|
+
when :attr_reader, :attr_writer, :attr_accessor
|
395
|
+
attr_names = find_attr_arguments(insns, index)
|
396
|
+
next unless attr_names
|
397
|
+
|
398
|
+
location = Location.new(line, :unknown)
|
399
|
+
attr_names.each do |attr_name|
|
400
|
+
if insn[1][:mid] != :attr_writer
|
401
|
+
results << method_definition(
|
402
|
+
current_nesting,
|
403
|
+
attr_name,
|
404
|
+
location,
|
405
|
+
file_comments
|
406
|
+
)
|
407
|
+
end
|
408
|
+
|
409
|
+
if insn[1][:mid] != :attr_reader
|
410
|
+
results << method_definition(
|
411
|
+
current_nesting,
|
412
|
+
:"#{attr_name}=",
|
413
|
+
location,
|
414
|
+
file_comments
|
415
|
+
)
|
416
|
+
end
|
417
|
+
end
|
418
|
+
when :"core#set_method_alias"
|
419
|
+
# Now we have to validate that the alias is happening with a
|
420
|
+
# non-interpolated value. To do this we'll match the specific
|
421
|
+
# pattern we're expecting.
|
422
|
+
values =
|
423
|
+
insns[(index - 4)...index].map do |previous|
|
424
|
+
previous.is_a?(Array) ? previous[0] : previous
|
425
|
+
end
|
426
|
+
if values !=
|
427
|
+
%i[putspecialobject putspecialobject putobject putobject]
|
428
|
+
next
|
429
|
+
end
|
430
|
+
|
431
|
+
# Now that we know it's in the structure we want it, we can use
|
432
|
+
# the values of the putobject to determine the alias.
|
433
|
+
location = Location.new(line, :unknown)
|
434
|
+
results << AliasMethodDefinition.new(
|
435
|
+
current_nesting,
|
436
|
+
insns[index - 2][1],
|
437
|
+
location,
|
438
|
+
EntryComments.new(file_comments, location)
|
439
|
+
)
|
440
|
+
end
|
312
441
|
end
|
313
442
|
end
|
314
443
|
end
|
@@ -321,6 +450,20 @@ module SyntaxTree
|
|
321
450
|
# It is not as fast as using the instruction sequences directly, but is
|
322
451
|
# supported on all runtimes.
|
323
452
|
class ParserBackend
|
453
|
+
class ConstantNameVisitor < Visitor
|
454
|
+
def visit_const_ref(node)
|
455
|
+
[node.constant.value.to_sym]
|
456
|
+
end
|
457
|
+
|
458
|
+
def visit_const_path_ref(node)
|
459
|
+
visit(node.parent) << node.constant.value.to_sym
|
460
|
+
end
|
461
|
+
|
462
|
+
def visit_var_ref(node)
|
463
|
+
[node.value.value.to_sym]
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
324
467
|
class IndexVisitor < Visitor
|
325
468
|
attr_reader :results, :nesting, :statements
|
326
469
|
|
@@ -331,8 +474,46 @@ module SyntaxTree
|
|
331
474
|
end
|
332
475
|
|
333
476
|
visit_methods do
|
477
|
+
def visit_alias(node)
|
478
|
+
if node.left.is_a?(SymbolLiteral) && node.right.is_a?(SymbolLiteral)
|
479
|
+
location =
|
480
|
+
Location.new(
|
481
|
+
node.location.start_line,
|
482
|
+
node.location.start_column
|
483
|
+
)
|
484
|
+
|
485
|
+
results << AliasMethodDefinition.new(
|
486
|
+
nesting.dup,
|
487
|
+
node.left.value.value.to_sym,
|
488
|
+
location,
|
489
|
+
comments_for(node)
|
490
|
+
)
|
491
|
+
end
|
492
|
+
|
493
|
+
super
|
494
|
+
end
|
495
|
+
|
496
|
+
def visit_assign(node)
|
497
|
+
if node.target.is_a?(VarField) && node.target.value.is_a?(Const)
|
498
|
+
location =
|
499
|
+
Location.new(
|
500
|
+
node.location.start_line,
|
501
|
+
node.location.start_column
|
502
|
+
)
|
503
|
+
|
504
|
+
results << ConstantDefinition.new(
|
505
|
+
nesting.dup,
|
506
|
+
node.target.value.value.to_sym,
|
507
|
+
location,
|
508
|
+
comments_for(node)
|
509
|
+
)
|
510
|
+
end
|
511
|
+
|
512
|
+
super
|
513
|
+
end
|
514
|
+
|
334
515
|
def visit_class(node)
|
335
|
-
names =
|
516
|
+
names = node.constant.accept(ConstantNameVisitor.new)
|
336
517
|
nesting << names
|
337
518
|
|
338
519
|
location =
|
@@ -340,7 +521,7 @@ module SyntaxTree
|
|
340
521
|
|
341
522
|
superclass =
|
342
523
|
if node.superclass
|
343
|
-
visited =
|
524
|
+
visited = node.superclass.accept(ConstantNameVisitor.new)
|
344
525
|
|
345
526
|
if visited == [[]]
|
346
527
|
raise NotImplementedError, "superclass with non constant path"
|
@@ -363,12 +544,41 @@ module SyntaxTree
|
|
363
544
|
nesting.pop
|
364
545
|
end
|
365
546
|
|
366
|
-
def
|
367
|
-
|
368
|
-
|
547
|
+
def visit_command(node)
|
548
|
+
case node.message.value
|
549
|
+
when "attr_reader", "attr_writer", "attr_accessor"
|
550
|
+
comments = comments_for(node)
|
551
|
+
location =
|
552
|
+
Location.new(
|
553
|
+
node.location.start_line,
|
554
|
+
node.location.start_column
|
555
|
+
)
|
556
|
+
|
557
|
+
node.arguments.parts.each do |argument|
|
558
|
+
next unless argument.is_a?(SymbolLiteral)
|
559
|
+
name = argument.value.value.to_sym
|
560
|
+
|
561
|
+
if node.message.value != "attr_writer"
|
562
|
+
results << MethodDefinition.new(
|
563
|
+
nesting.dup,
|
564
|
+
name,
|
565
|
+
location,
|
566
|
+
comments
|
567
|
+
)
|
568
|
+
end
|
569
|
+
|
570
|
+
if node.message.value != "attr_reader"
|
571
|
+
results << MethodDefinition.new(
|
572
|
+
nesting.dup,
|
573
|
+
:"#{name}=",
|
574
|
+
location,
|
575
|
+
comments
|
576
|
+
)
|
577
|
+
end
|
578
|
+
end
|
579
|
+
end
|
369
580
|
|
370
|
-
|
371
|
-
visit(node.parent) << node.constant.value.to_sym
|
581
|
+
super
|
372
582
|
end
|
373
583
|
|
374
584
|
def visit_def(node)
|
@@ -391,10 +601,12 @@ module SyntaxTree
|
|
391
601
|
comments_for(node)
|
392
602
|
)
|
393
603
|
end
|
604
|
+
|
605
|
+
super
|
394
606
|
end
|
395
607
|
|
396
608
|
def visit_module(node)
|
397
|
-
names =
|
609
|
+
names = node.constant.accept(ConstantNameVisitor.new)
|
398
610
|
nesting << names
|
399
611
|
|
400
612
|
location =
|
@@ -420,10 +632,6 @@ module SyntaxTree
|
|
420
632
|
@statements = node
|
421
633
|
super
|
422
634
|
end
|
423
|
-
|
424
|
-
def visit_var_ref(node)
|
425
|
-
[node.value.value.to_sym]
|
426
|
-
end
|
427
635
|
end
|
428
636
|
|
429
637
|
private
|
@@ -433,8 +641,10 @@ module SyntaxTree
|
|
433
641
|
|
434
642
|
body = statements.body
|
435
643
|
line = node.location.start_line - 1
|
436
|
-
index = body.index(node)
|
644
|
+
index = body.index(node)
|
645
|
+
return comments if index.nil?
|
437
646
|
|
647
|
+
index -= 1
|
438
648
|
while index >= 0 && body[index].is_a?(Comment) &&
|
439
649
|
(line - body[index].location.start_line < 2)
|
440
650
|
comments.unshift(body[index].value)
|