tmm1 / ripper-tags

fast, accurate ctags generator for ruby source code using Ripper
MIT License
550 stars 43 forks source link

Bugs in access modifiers when used with symbols, not defs #78

Closed k3rni closed 6 years ago

k3rni commented 6 years ago

Consider this failing test:

  def test_extract_access_by_symbol
    tags = extract(<<-EOC)
      module Test
        def foo(); end
        private_class_method :foo
      end
    EOC

    pp tags
    foo = tags.find { |t| t[:name] == 'foo' }
    assert_equal 'private', foo[:access]
  end

and its console output (from pp tags, compacted slightly):

[{:kind=>"module",  :line=>1,  :language=>"Ruby",  :path=>"(eval)",
  :pattern=>"      module Test",  :full_name=>"Test",  :name=>"Test"},
 {:kind=>"method",  :line=>2,  :language=>"Ruby",  :path=>"(eval)",
  :pattern=>"        def foo(); end",  :name=>"foo",  :full_name=>"Test#foo",
  :class=>"Test"},
 {:kind=>"method",  :line=>3,  :language=>"Ruby",  :path=>"(eval)",
  :pattern=>"        private_class_method :foo",
  :access=>"private",
  :name=>3,
  :full_name=>"Test#3",
  :class=>"Test"}]

Problems:

  1. method foo is defined twice: on lines 2 and 3
  2. the second definition has a wrong name: its line number
  3. only the second one has proper access (and is not found by the test)

The second problem is relatively easy to fix: when handling private_class_method etc, handle the case where args[1] doesn't start with a :def symbol. In that case, args contains a list of method names to make private, so we can emit tags accordingly.

This won't fix the other problems, though. What should happen for line 3 is: update access on existing tag for method foo to private, in effect merging the two existing definitions.

k3rni commented 6 years ago

Discovered this because my tagfile contained a number of entries that were just numbers. Each of them matched the location of an access modifier line that listed the symbol.

mislav commented 6 years ago

Good catch; thank you!