Allow less strict attribute argument parsing - #1793
Conversation
| def _visit_call_attr_reader_writer_accessor(call_node, rw) | ||
| return if @scanner.in_proc_block | ||
| names = symbol_arguments(call_node) | ||
| names = initial_symbol_arguments(call_node) |
There was a problem hiding this comment.
I feel symbol_arguments should already work like initial_symbol_arguments.
@tompng why was it designed to return nil when there's any non-symbol arguments? What's the case we're guarding against?
There was a problem hiding this comment.
To handle alias_method.
We need to prevent alias_method :foo, bar, :baz, blah: true treated as alias_method :foo, :baz.
In all other case, we can just use the non-strict version.
There was a problem hiding this comment.
I feel like maybe expanding the scope to everything other than attributes either deserves separate PRs (automatically calling out in separate line items in generated release notes), or (at the very least) renaming this PR. :)
|
|
||
| def initial_symbol_arguments(call_node) | ||
| return unless arguments = call_node.arguments&.arguments | ||
| symbol_args = arguments.take_while {|arg| arg.is_a?(Prism::SymbolNode) } |
There was a problem hiding this comment.
How about using arguments.grep(Prism::SymbolNode) instead of take_while and renaming the method?
I think it's worth adding a test like this, which is possible in the original attr_accessor spec
# document :foo, :bar and :baz
attr_accessor :foo, *ignored1, :bar, (ignored2), :bazThere was a problem hiding this comment.
Yeah, I was trying to be a little bit conservative, since I wasn't sure if the change was intentional. But I'm even happier with that.
I feel like maybe both methods should be renamed, to avoid ambiguity confusion...
- this grep-based variation =>
any_symbol_argumentsorevery_symbol_argument
(orgrep_symbol_arguments? I prefer "any".) symbol_arguments=>exclusively_symbol_arguments?
I (originally) added "node" to the names to emphasize that this is only matching symbol literal nodes.
(edited to add) But I dropped it in the version I pushed, both to be consistent with the original naming, and also because the methods are returning symbols and only working with literal SymbolNode is an implementation detail that can be inferred from context.
There was a problem hiding this comment.
Do we want to preserve any form of test_undocumentable_attributes? That is, what if the arguments aren't variables or expressions that're too dynamic or too complex to extract names from, but are instead integers or other literals which suggest an entirely different API is being used?
I'm updating the PR to just repurpose that test for your grep suggestion. But that test case (added in fde99f1) makes me think that you maybe had a specific scenario you wanted to prohibit?
There was a problem hiding this comment.
In my mind, there were two possible design:
- Only accept
attr_accessor :a, :band rejectattr_accessor :a, opt: - Accept
attr_accessor :a, 1, expr, :b, trueand alsoattr_accessor :a, opt:
And I selected the first one. test_undocumentable_attributes is testing this design decision.
IMO, if were going to be conservative, reject a usage that might be a completely different API, attr_accessor :a, opt: is one of the case that would be rejected. It's not ruby core attr_accessor usage. It's not a bug.
I think it's OK to change the design from 1 to 2, and allow any dynamic expression mixed with symbols.
b176b3a to
4285ba7
Compare
4285ba7 to
c805f78
Compare
|
@st0012 @tompng As a separate question, do we want to also allow string literal arguments? Because I'm pretty sure that all of the Module methods in question ( BUT it's pretty weird to use string literals rather than symbol literals. Sure, for convenience, maybe we sometimes pass some variables or expressions into them which might evaluate to strings, to avoid the extra So if it is done, maybe there's a reason, and maybe (for some of these) that reason could be that it's a simple (if somewhat hacky) way to avoid rdoc from picking up on them? But I dunno... I can only really see that argument working for the attribute methods and Anyway, after talking myself through it (and writing it down above), I'm leaning towards allowing both |
|
Related to that, I feel like maybe the following should not be ignored, but should issue a warning instead? private def self.foo = bar
# => WARNING: "private def self.foo" makes "M#foo" private, not "M.foo"
private_class_method def foo = bar
# => WARNING: "private_class_method def foo" makes "M.foo" private, not "M#foo"
private def to_s.foo = bar
# => WARNING: "private def (expr).foo" makes "M#foo' private, not "(expr).foo"Because That's more scope than I want to push into this PR, but I'm curious about your thoughts? |
|
Accepting StringNode is the original behavior in the old Ripper based parser. The Prism based parser just ported that behavior. I think there is no strong reason to keep that behavior because string isn't used. I'm fine either way - keeping or dropping the behavior accepting StringNode.
I think it's OK to do that: Warn and make instance method |
@tompng Just to be clear: the Prism based parser does not currently accept string arguments for these methods. I think you're saying the old parser did support string arguments. So you're okay with me adding support for them to the Prism parser? |
|
It looks like the current prism parser supports either StringNode or SymbolNode as the first argument for meta method comments ( |
|
I think we should accept string arguments in those calls. |
c805f78 to
b4f2611
Compare
|
@st0012 @tompng I've updated the PR to use It might make sense to extract that method (and several others which process prism nodes and don't use any ivars) into a shared utilities module, but that's out of scope for this PR, IMO. I'll post a second (stacked) PR to update the all of the visibility methods. |
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.
b4f2611 to
f4607d2
Compare
This updates argument parsing for the
attr,attr_reader,attr_writer, andattr_accessormethods, so they behave more like rdoc 7.2's parser.The prism parser is (prior to this PR) 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::Configintentionally took advantage of the looser parsing done by rdoc 7.2. That class redefinesattr_reader,attr_writerandattr_accessorto add keyword arguments for type validation/coercion and defaults:rdoc 7.2 simply ignored the unknown keyword args, and parses this no differently from
attr_accessor :open_timeout.Fixes #1790.