tmm1 / ripper-tags

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

Chokes on `private attr_reader :x` #110

Closed wincent closed 2 years ago

wincent commented 2 years ago

Given a class like this in a project:

class A
  private attr_reader :x
end

Running ripper-tags -R will die in the Vim formatter with:

/var/lib/gems/2.7.0/gems/ripper-tags-0.9.0/lib/ripper-tags/vim_formatter.rb:104:in `fetch': key not found: :name (KeyError)

The tag object in question is missing the :name property:

{
  :kind=>"method",
  :line=>2,
  :language=>"Ruby",
  :path=>"a.rb",
  :pattern=>"  private attr_reader :x",
  :access=>"private",
  :full_name=>"A#",
  :class=>"A"
}

I thought this might be related to the version of Ruby that I'm using, but when I checked the AST returned by ripper with a couple of different Ruby versions, it looks like they're very close to being the same (albeit not identical; see below). Additionally, I actually tried running ripper-tags under both versions and got the same exception both times.

Here's Ruby 3.1.2 (ie. with pp Ripper.sexp('class A; private attr_reader :x; end')):

[:program,
 [[:class,
   [:const_ref, [:@const, "A", [1, 6]]],
   nil,
   [:bodystmt,
    [[:void_stmt],
     [:command,
      [:@ident, "private", [1, 9]],
      [[:command,
        [:@ident, "attr_reader", [1, 17]],
        [:args_add_block, [[:symbol_literal, [:symbol, [:@ident, "x", [1, 30]]]]], false]]]]],
    nil,
    nil,
    nil]]]]

vs Ruby 2.7.4 (ie. with pp Ripper.sexp('class A; private attr_reader :x; end')):

[:program,
 [[:class,
   [:const_ref, [:@const, "A", [1, 6]]],
   nil,
   [:bodystmt,
    [[:void_stmt],
     [:command,
      [:@ident, "private", [1, 9]],
      [[:command,
        [:@ident, "attr_reader", [1, 17]],
        [:args_add_block,
         [[:symbol_literal, [:symbol, [:@ident, "x", [1, 30]]]]],
         false]]]]],
    nil,
    nil,
    nil]]]]

The workaround for now is to rewrite the code, breaking it into distinct calls on separate lines:

class A
  attr_reader :x
  private :x
end

Somewhat tangentially, older Ruby only likes the rewritten form, because — at least on 2.7.4 — attr_reader returns nil and private complains about being given nil instead of a symbol or string, but it is still syntactically valid, at least insofar as ripper accepts it. In 3.1.4 attr_reader returns [:x] and private is happy with that.

mislav commented 2 years ago

Thank you for reporting! 🙇