yard icon indicating copy to clipboard operation
yard copied to clipboard

Strange behavior from macro + reference tag

Open nevans opened this issue 3 years ago • 0 comments

What I was looking for was a way to make a macro for Forwardable use the attribute/method @return info from the delegated class. I started by trying to get a reference tag to work inside of the macro. Only later did I notice the docs say that reference tags syntax doesn't work inside directives. But I did get it to do something weird and unexpected:

Steps to reproduce (including actual and desired/expected output)

  1. With the following code:

# the class that is delegated to
class Bar
  # @return [String] some value or another
  attr_reader :foobar
end

# the class that delegates
class Foo
  extend Forwardable

  # @return [Bar] the object we are delegating to
  attr :bar

  # @!macro macro_with_return
  #   @!attribute [r] $2
  #      @see Bar#$2
  #      @return (see Bar#$2)

  # @!macro macro_with_return
  def_delegator :bar, :foobar
end
  1. Run the following command: rm .yardoc doc -rvf; yard server -r
  2. Open browser to http://localhost:8808/docs/Foo
  3. See the following Instance Attribute Summary:
    #bar ⇒ Bar [ readonly ]
       The object we are delegating to.

    #foobar ⇒ Object [ readonly ]

It looks like the macro's return reference tag is completely ignored.

  1. So I figured, if it won't work inside the macro, maybe it'll work without the macro? Annoying to duplicate the type information, but maybe it'll work... Update Foo to the following:
# the class that delegates
class Foo
  extend Forwardable

  # @return [Bar] the object we are delegating to
  attr :bar

  # @!macro macro_without_return
  #   @!attribute [r] $2
  #      @see Bar#$2

  # @!macro macro_with_return
  # @return (see Bar#foobar)
  def_delegator :bar, :foobar
end
  1. Reload and see the following Instance Attribute Summary:
    #bar ⇒ String [ readonly ]
       Some value or another.

    #foobar ⇒ Object [ readonly ]

Well, that was definitely not what I wanted. I guess it's getting applied to the previous doc string?

  1. But at some point, I tried it with the return on both the macro definition and the macro application. Update Foo to the following:
# the class that delegates
class Foo
  extend Forwardable

  # @return [Bar] the object we are delegating to
  attr :bar

  # @!macro macro_with_return
  #   @!attribute [r] $2
  #      @see Bar#$2
  #      @return (see Bar#$2)

  # @!macro macro_with_return
  # @return (see Bar#foobar)
  def_delegator :bar, :foobar
end
  1. Reload and see the following Instance Attribute Summary:
    #bar ⇒ String [ readonly ]
       Some value or another.

    #foobar ⇒ String [ readonly ]
       Some value or another.

And now it works! Sort of... it duplicates the return info and more importantly: it's still "infecting" the doc string before it.

  1. At one point, I noticed I'd mistyped the @return outside the macro and it still worked. So I tried simply deleting the contents of the @return entirely, but still leaving it there, empty. Update Foo to the following:
# the class that delegates
class Foo
  extend Forwardable

  # @return [Bar] the object we are delegating to
  attr :bar

  # @!macro macro_with_return
  #   @!attribute [r] $2
  #      @see Bar#$2
  #      @return (see Bar#$2)

  # @!macro macro_with_return
  # @return
  def_delegator :bar, :foobar
end
  1. Reload and see the following Instance Attribute Summary:
    #bar ⇒ Object [ readonly ]

    #foobar ⇒ String [ readonly ]
       Some value or another.

And it still worked! That's weird but great, now I don't need to duplicate the return info in both places. But it's still "infecting" the doc string before it. I still haven't figured out how to prevent that.

  1. So as an annoying workaround, I just put all of these macros with their empty @returns at the top of the file. This gave me the desired result:
    #bar ⇒ Bar [ readonly ]
       The object we are delegating to.

    #foobar ⇒ String [ readonly ]
       Some value or another.

Questions:

  1. Is there a better way to "end" the previous docstring so that later tags don't "infect" it?
  2. Is there a better way to accomplish my goal, which is placing a reference tag inside a macro. I'm glad this approach works, but it feels hacky, like it might eventually be "fixed" as a bug and stop working.

Environment details:

  • OS: Ubuntu 20.04 LTS (fossa-melisa X20)
  • Ruby version (ruby -v): ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-linux]
  • YARD version (yard -v): yard 0.9.25

I have read the Contributing Guide.

nevans avatar Aug 31 '20 14:08 nevans