Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Performance/Squeeze cop error on frozen AST string node value #480

Conversation

viralpraxis
Copy link
Contributor

Since sometimes AST string nodes might have their values frozen, we should call dup explicitly to make sure to_string_literal (which does not work with frozen strings) does not raise. Maybe we should also apply a patch on RuboCop's core side to fix the to_string_literal itself (it now uses force_encoding on its argument).

See [1] and [2] for details.

[1] ruby/prism#3309
[2] rubocop/rubocop-ast#342


Before submitting the PR make sure the following are checked:

  • The PR relates to only one subject with a clear title and description in grammatically correct, complete sentences.
  • Wrote [good commit messages][1].
  • Commit message starts with [Fix #issue-number] (if the related issue exists).
  • Feature branch is up-to-date with master (if not - rebase it).
  • Squashed related commits together.
  • Added tests.
  • Ran bundle exec rake default. It executes all tests and runs RuboCop on its own code.
  • Added an entry (file) to the changelog folder named {change_type}_{change_description}.md if the new code introduces user-observable changes. See changelog entry format for details.

@Earlopain
Copy link
Contributor

Yes, I think this should instead be handled in rubocop itself instead of potentially having to handle this at every callsite. https://github.com/rubocop/rubocop/blob/9f2ffa2270bbc174b2a622350d120b05672a1ce5/lib/rubocop/cop/util.rb#L198-L201. Other force_encoding in rubocop are already preceeded with a dup (and there's special handling for jruby for some reason)

@viralpraxis
Copy link
Contributor Author

viralpraxis commented Dec 18, 2024

Yes, I think this should instead be handled in rubocop itself instead of potentially having to handle this at every callsite. https://github.com/rubocop/rubocop/blob/9f2ffa2270bbc174b2a622350d120b05672a1ce5/lib/rubocop/cop/util.rb#L198-L201. Other force_encoding in rubocop are already preceeded with a dup (and there's special handling for jruby for some reason)

Shouldn't it still be also fixed here since latest rubocop-performance might work with old rubocop'?

@Earlopain
Copy link
Contributor

I guess, but for this I would just be tempted to force those select few users that run into this to upgrade instead. It is so very specific and doesn't look like something that someone would actually write (?). Not my decision though.

@viralpraxis
Copy link
Contributor Author

doesn't look like something that someone would actually write

That's actually the code I wrote right after learning about "character literals" 😄

Anyway, I'll open a PR at rubocop's side to address this issue

viralpraxis added a commit to viralpraxis/rubocop that referenced this pull request Dec 18, 2024
viralpraxis added a commit to viralpraxis/rubocop that referenced this pull request Dec 19, 2024
Currently, there are exactly three instances where the receiver-mutating
`String#force_encoding`` method is used. In all cases except for
`to_string_literal`, a `String#dup` call is included to ensure compatibility
with frozen strings. I couldn't identify any counterexamples in rubocop core that would result
in a runtime error, but there is at least one instance in `rubocop-performance` [1]
where a frozen string might be passed. To ensure everything works correctly,
we need to add a `#dup` call in the implementation of `to_string_literal`.

For additional context, it's worth noting that the Prism parser might start
freezing AST leaf nodes in the future [2],
and whitequark's parser has some unpredictability in this regard [3].

[1] rubocop/rubocop-performance#480
[2] ruby/prism#3309
[3] rubocop/rubocop-ast#342
viralpraxis added a commit to viralpraxis/rubocop that referenced this pull request Dec 19, 2024
Currently, there are exactly three instances where the receiver-mutating
`String#force_encoding` method is used. In all cases except for
`to_string_literal`, a `String#dup` call is included to ensure compatibility
with frozen strings. I couldn't identify any counterexamples in rubocop core that would result
in a runtime error, but there is at least one instance in `rubocop-performance` [1]
where a frozen string might be passed. To ensure everything works correctly,
we need to add a `#dup` call in the implementation of `to_string_literal`.

For additional context, it's worth noting that the Prism parser might start
freezing AST leaf nodes in the future [2],
and whitequark's parser has some unpredictability in this regard [3].

[1] rubocop/rubocop-performance#480
[2] ruby/prism#3309
[3] rubocop/rubocop-ast#342
viralpraxis added a commit to viralpraxis/rubocop that referenced this pull request Dec 20, 2024
Currently, there are exactly three instances where the receiver-mutating
`String#force_encoding` method is used. In all cases except for
`to_string_literal`, a `String#dup` call is included to ensure compatibility
with frozen strings. I couldn't identify any counterexamples in rubocop core that would result
in a runtime error, but there is at least one instance in `rubocop-performance` [1]
where a frozen string might be passed. To ensure everything works correctly,
we need to add a `#dup` call in the implementation of `to_string_literal`.

For additional context, it's worth noting that the Prism parser might start
freezing AST leaf nodes in the future [2],
and whitequark's parser has some unpredictability in this regard [3].

[1] rubocop/rubocop-performance#480
[2] ruby/prism#3309
[3] rubocop/rubocop-ast#342
@@ -46,7 +46,7 @@ def on_send(node)
message = format(MSG, current: bad_method, prefer: good_method)

add_offense(node.loc.selector, message: message) do |corrector|
string_literal = to_string_literal(replace_str)
string_literal = to_string_literal(replace_str.dup)
Copy link
Member

@koic koic Dec 20, 2024

Choose a reason for hiding this comment

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

Can you add a FIXME comment? e.g.:

Suggested change
string_literal = to_string_literal(replace_str.dup)
# FIXME: When requiring only RuboCop 1.70.0 and above,
# `dup` in `replace_str.dup` becomes unnecessary, so please remove it.
string_literal = to_string_literal(replace_str.dup)

Since sometimes AST string nodes might have their values frozen,
we should call `dup` explicitly to make sure `to_string_literal`
(which does not work with frozen strings) does not raise. Maybe
we should also apply a patch on RuboCop's core side to fix the
`to_string_literal` itself (it now uses `force_encoding` on its argument).

See [1] and [2] for details.

[1] ruby/prism#3309
[2] rubocop/rubocop-ast#342
@viralpraxis viralpraxis force-pushed the fix-performance-squeeze-cop-error-on-frozen-ast-literal-node-value branch from b18dd80 to 9fdbe4e Compare December 21, 2024 08:56
@koic koic merged commit d058c88 into rubocop:master Dec 21, 2024
14 checks passed
@koic
Copy link
Member

koic commented Dec 21, 2024

Thanks!

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