Skip to content

Less strict parsing of visibility method arguments - #1803

Merged
tompng merged 4 commits into
ruby:masterfrom
nevans:less-strict-visibility-method-argument-parsing
Sep 12, 2026
Merged

tompng merged 4 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
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 Outdated
Comment thread lib/rdoc/parser/ruby.rb Outdated
Comment thread lib/rdoc/parser/ruby.rb Outdated
@nevans
nevans force-pushed the less-strict-visibility-method-argument-parsing branch from 242a1e9 to 9dc0aec Compare September 9, 2026 17:38
@github-actions

github-actions Bot commented Sep 9, 2026 •

Copy link
Copy Markdown

Documentation preview

View the preview

Commit: 2c3972c

@nevans

nevans commented Sep 9, 2026 •

Copy link
Copy Markdown
Contributor Author

@st0012 I only just got back around to addressing your nits. And I rebased it after #1795, which made every line a conflict. But the changes here were small and simple to replay.

Comment thread lib/rdoc/parser/ruby.rb
end || []
return unless arguments_node = call_node.arguments
names = arguments_node.arguments.filter_map { |arg| argument_name(arg) }
names unless names.empty?

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.

Return value was always an array.
It'll be changed to nil-able, so code around L415 (updated in #1793) needs change.

# Around L415
when 'attr', 'attr_reader', 'attr_writer', 'attr_accessor'
  # attributes ||= call_node_name_arguments(node).compact if is_call_node
  # ↓
  attributes ||= call_node_name_arguments(node) || [] if is_call_node
  ...
end

if attributes
  # handles attr, attr_reader, attr_writer, attr_accessor (so attributes must be non-nil)
elsif line_no || node
  # handles non-attr ghost method
end

@nevans nevans Sep 11, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

While it is true that it was previously always an array:

  • it was only used inside an if attributes clause
  • the elsif line_no || node clause is triggered if and only if there's also a method or singleton-method directive.

Although it's a weird edge case (which explains why there's no tests for it), allowing method or singleton-method to trigger when attributes failed to collect any names seems like maybe a good thing to me? Anyway, that was my reasoning when I made the change. Is there some way this could cause an issue?

Mostly I changed it for the consistency with the similar Visitor methods (constant_arguments_names, symbol_arguments, visibility_method_arguments, etc). While call_node_name_arguments lives on the scanner, like those visitor methods it processes an AST node and doesn't reference any ivars. It feels to me like it ought to be a module_function in a shared module together with those other methods, so I figured it made sense to be consistent with them.

But I'm happy to change it back to always returning an array: unless there's some important distinction between empty vs non-existent, I generally prefer to return an empty collection rather than nil. It just feels odd for it to behave differently from the other related methods.

@nevans nevans Sep 11, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Nope, I reasoned incorrectly. While line_no is only set when those other directives are visible, node could be there for any ## comments that are matched to a subsequent node.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@tompng Thanks for catching that. I've added some examples to test_invalid_meta_method that capture the bug I'd created.

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 force-pushed the less-strict-visibility-method-argument-parsing branch from 9dc0aec to e30f6d6 Compare September 11, 2026 17:40
This addresses @tompng's comment here, and uses his suggested fix:
ruby#1803 (comment)

I'd previously reasoned that the add method clause would only be entered
when a `method` or `singleton-method` directive is seen.  I missed that
`node` is sent in as a parameter, so it can _also_ also trigger the
other clause.

The issue this seems to cause is that unparseable attributes directives
will be interpreted as ghost methods, and ghost methods with no known
name behave differently from ghost attributes with no known name in that
they are still created with an "unknown" name.  I've added some examples
to `test_invalid_meta_method` which demonstrated this bug.

Co-authored-by: tomoya ishida <tomoyapenguin@gmail.com>

@tompng tompng 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.

👍

@tompng
tompng merged commit 268350e into ruby:master Sep 12, 2026
29 checks passed
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.

4 participants