Skip to content

Less strict parsing of visibility method arguments - #1803

Open
nevans wants to merge 5 commits into
ruby:masterfrom
nevans:less-strict-visibility-method-argument-parsing
Open

Less strict parsing of visibility method arguments#1803
nevans wants to merge 5 commits into
ruby:masterfrom
nevans:less-strict-visibility-method-argument-parsing

Conversation

@nevans

@nevans nevans commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

NOTE: this PR stacks on #1793. Only the last three commits are specific to this PR.

This updates method name argument parsing for the visibility methods:

  • private, public, protected
  • private_class_method, public_class_method
  • private_constant, public_constant
  • module_function

Prior to this PR, the parser is stricter about visibility method name arguments than it should be: it only parses method names when all arguments are symbols.

This updates the prism parser to behave more like the rdoc 7.2, so every (non-interpolated) string and symbol in the argument list is used (call_node_name_arguments is used to parse the arguments list).

Additionally:

  • Fix Parser::Ruby docs for private_class_method and public_class_method.
    There aren't any private_class_function and public_class_function methods. 😉
  • Refactor Parser::Ruby#call_node_name_arguments into plural and singular versions.
    Since meta-programmed method names are only ever inferred from the first argument, the singular version only reads the first argument. The plural version is now free to use filter_map and convert [] to nil, enabling the short-circuiting pattern used in the visitor.
    Arguably, these methods belong more to the visitor than the "scanner". Since they simply process prism nodes without any ivar references, they should probably be converted into module functions on a utility module. But I'm considering that out of scope for this PR.

nevans added 5 commits August 31, 2026 08:57
This updates argument parsing for the `attr`, `attr_reader`,
`attr_writer`, and `attr_accessor` methods, so they behave more like
rdoc 7.2's parser.

The prism parser is strict about attribute arguments: it only parses as
an attribute when _all_ arguments are symbols.  rdoc 7.2's parser
allowed all symbol or string arguments and ignored the rest.

As an example, the rdoc for `Net::IMAP::Config` intentionally took
advantage of the looser parsing done by rdoc 7.2. That class redefines
`attr_reader`, `attr_writer` and `attr_accessor` to add keyword
arguments for type validation/coercion and defaults:

```ruby
      # Seconds to wait until a connection is opened.
      #
      # Applied separately for establishing TCP connection and starting a TLS
      # connection.
      #
      # If the IMAP object cannot open a connection within this time,
      # it raises a Net::OpenTimeout exception.
      #
      # See Net::IMAP.new and Net::IMAP#starttls.
      #
      # The default value is +30+ seconds.
      attr_accessor :open_timeout, type: Integer, default: 30
```

rdoc 7.2 simply ignored the unknown keyword args, and parses this no
differently from `attr_accessor :open_timeout.`

Fixes ruby#1790.
This updates inferred attribute name parsing for the `:attr:`,
`:attr_reader:`, `:attr_writer:`, and `:attr_accessor:` directives, to
avoid creating unnamed attributes from unparsable arguments.

Previously, an unnamed (nil) attribute would be created.
Now, any non-string/non-symbol arguments are simply ignored.
This updates method name argument parsing for the visibility methods:
* `private`, `public`, `protected`
* `private_class_method`, `public_class_method`
* `private_constant`, `public_constant`
* `module_function`

Prior to this commit, the parser is stricter about visibility method
name arguments than it should be: it only parses method names when _all_
arguments are symbols.

This updates the prism parser to behave more like the rdoc 7.2, so every
(non-interpolated) string and symbol in the argument list is used
(`call_node_name_arguments` is used to parse the arguments list).
There aren't any `private_class_function` and `public_class_function`
methods.  😉
Arguably, these methods belong more to the visitor than the "scanner".
Since they simply process prism nodes without any ivar references, they
should probably be converted into module functions on a utility module.
@nevans
nevans deployed to fork-preview-protection August 31, 2026 13:30 — with GitHub Actions Active
@nevans

nevans commented Aug 31, 2026

Copy link
Copy Markdown
Contributor Author

NOTE: This PR stacks on #1793. But if I make that the PR's branch the base branch, GitHub will convert this PR into a PR on my repo. Only the last three commits are specific to this PR.

@matzbot

matzbot commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

🚀 Preview deployment available at: https://1a6e72ae.rdoc-6cd.pages.dev (commit: 242a1e9)

@st0012 st0012 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just some nits
Thanks for fixing it 👍

Comment thread lib/rdoc/parser/ruby.rb
Comment on lines +1348 to +1349
return unless names = call_node_name_arguments(call_node)
@scanner.container.set_constant_visibility_for(names, :public)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think the original condition shape can be kept and is clearer

Suggested change
return unless names = call_node_name_arguments(call_node)
@scanner.container.set_constant_visibility_for(names, :public)
names = call_node_name_arguments(call_node)
@scanner.container.set_constant_visibility_for(names, :public) if names

Comment thread lib/rdoc/parser/ruby.rb
names = symbol_arguments(call_node)
@scanner.container.set_constant_visibility_for(names.map(&:to_s), :private) if names
return unless names = call_node_name_arguments(call_node)
@scanner.container.set_constant_visibility_for(names, :private)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

Comment thread lib/rdoc/parser/ruby.rb
names = symbol_arguments(call_node)
@scanner.add_attributes(names.map(&:to_s), rw, call_node.location.start_line) if names
return unless names = call_node_name_arguments(call_node)
@scanner.add_attributes(names, rw, call_node.location.start_line)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants