syntax_tree 6.0.1 → 6.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|