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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b1e39c546fb96aea864e8190b8a8ff338543afd644d2334edad5b98663d418f2
4
- data.tar.gz: 7633b68e7b42fcd782fc1c89395d93679e72828fe1a9214e741ad00718543c6b
3
+ metadata.gz: 7515a827509d352259e6cae476930093fe382fcc35c248fba3ab716019e69ca9
4
+ data.tar.gz: add2625387abe5616f90d28843f4fcebac5b68222f9a6258cbfb04fbd41b4772
5
5
  SHA512:
6
- metadata.gz: a0990a769cbc448e549d14542610ccfbf09536ada139cc10701871a506a1b160a8ce751712a94a8f003d3b2c1828a6b5d4bd5384efe63f0f82a21c0762b689d5
7
- data.tar.gz: a08b4057370c230bfd713ff9a717ea4f2a02be4f8610af51b18143ff9b2b9e542541b77c049488c500033751b50adc279d5e3a8beb0568f5e2bb165793c612d4
6
+ metadata.gz: ea0a666bd65180facaab89acd7b7bd87d3f5208c69a0fe80b91d1d25d57cdc2c281ac301070da8d73c7d119574a7f7de30c0facb0d7131f895bbc8fa7fe582c5
7
+ data.tar.gz: 4c9df489dcb876280a219a8e213a800f0544a0aaa02a303d8fc3ba948e07e195dbfb21da8f8f04825abc4bf61fb9ec92141d5a00cc7b3d53b1d6103d58d10be3
data/.gitignore CHANGED
@@ -4,6 +4,7 @@
4
4
  /coverage/
5
5
  /pkg/
6
6
  /rdocs/
7
+ /sorbet/
7
8
  /spec/reports/
8
9
  /tmp/
9
10
  /vendor/
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.1...HEAD
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.1)
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.17.0)
13
+ minitest (5.18.0)
14
14
  parallel (1.22.1)
15
- parser (3.2.1.0)
15
+ parser (3.2.1.1)
16
16
  ast (~> 2.4.1)
17
- prettier_print (1.2.0)
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.46.0)
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.26.0)
32
+ rubocop-ast (1.27.0)
33
33
  parser (>= 3.2.1.0)
34
- ruby-progressbar (1.11.0)
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
 
@@ -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
@@ -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 insns[index].is_a?(Integer)
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
- raise NotImplementedError,
250
- "superclass with non constant path on line #{line}"
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
- raise NotImplementedError,
269
- "singleton class with non-self receiver"
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 << MethodDefinition.new(
359
+ results << method_definition(
294
360
  current_nesting,
295
361
  insn[1],
296
362
  location,
297
- EntryComments.new(file_comments, location)
363
+ file_comments
298
364
  )
299
365
  when :definesmethod
300
- if current_iseq[13][index - 1] != [:putself]
301
- raise NotImplementedError,
302
- "singleton method with non-self receiver"
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 = visit(node.constant)
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 = visit(node.superclass)
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 visit_const_ref(node)
367
- [node.constant.value.to_sym]
368
- end
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
- def visit_const_path_ref(node)
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 = visit(node.constant)
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) - 1
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)