Skip to content

Prism parser ignores attr_* calls with trailing keyword arguments #1759

Description

@thyresias

Description

RDoc 8.0.0 no longer documents attributes declared through an attr,
attr_reader, attr_writer, or attr_accessor call when symbolic attribute
names are followed by keyword arguments.

This is a regression from the previous Ruby parser.

Minimal reproduction

class Config
  ##
  # Timeout in seconds.
  attr_accessor :open_timeout, type: Integer, default: 30
end

Parse this file with RDoc and inspect the attributes registered for Config.

With RDoc 6.17.0:

Config.attributes.map { |attr| [attr.name, attr.rw] }
# => [["open_timeout", "RW"]]

With RDoc 8.0.0:

Config.attributes.map { |attr| [attr.name, attr.rw] }
# => []

Real-world example

net-imap 0.6.4.1 uses a custom attr_accessor macro with keyword options:

attr_accessor :open_timeout, type: Integer, default: 30

Source:

https://github.com/ruby/net-imap/blob/v0.6.4.1/lib/net/imap/config.rb#L215

Because the attribute is absent from RDoc's store, references such as:

rdoc-ref:Config#open_timeout

cannot be resolved.

Cause

RDoc::Parser::Ruby::RDocVisitor#_visit_call_attr_reader_writer_accessor
calls symbol_arguments, which returns nil unless every argument is a
Prism::SymbolNode.

A trailing Prism::KeywordHashNode therefore causes the entire attribute
declaration to be ignored.

Expected behavior

RDoc should register the symbolic arguments as attributes and ignore a
trailing keyword hash.

Suggested fix

Handle a trailing Prism::KeywordHashNode separately:

arguments = call_node.arguments&.arguments
return unless arguments

arguments = arguments[0...-1] if arguments.last.is_a?(Prism::KeywordHashNode)
return unless arguments.all? { |arg| arg.is_a?(Prism::SymbolNode) }

names = arguments.map { |arg| arg.value.to_s }
@scanner.add_attributes(names, rw, call_node.location.start_line)

A parser test should cover attr, attr_reader, attr_writer, and
attr_accessor with keyword options.

Environment

Ruby 4.0.5 +PRISM
RDoc 8.0.0
Prism 1.9.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions