KEMBAR78
[Fix #14486] Fix false positives for `Style/RedundantParentheses` with unary operators and `yield` by Earlopain · Pull Request #14489 · rubocop/rubocop · GitHub
Skip to content

Conversation

Earlopain
Copy link
Contributor

… and also , super/ defined?

Fixes #14486.

Removing parens for defined? does not result in a syntax error but because of it's low precedence it's better to keep them anyways. For example, in (defined? foo) && baz, removing the parens makes it into a single defined? invocation


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.
  • 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.

it_behaves_like 'plausible', '(!x.m arg)'
it_behaves_like 'plausible', '(!x).y'
it_behaves_like 'plausible', '(!super arg)'
it_behaves_like 'plausible', '(!yield arg)'
Copy link
Member

Choose a reason for hiding this comment

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

A standalone (!yield x) should be detected. The false positive occurs with logical conditions such as x && (!yield y).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, that is true. But (!x.m arg) is currently also not an offense, so this is a different issue.

These only get picked up when the argument is also parenthesised like (!x.m(arg))

Copy link
Member

@koic koic Aug 28, 2025

Choose a reason for hiding this comment

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

Hmm, since (!yield arg) is currently being detected, it should be kept that way.

$ echo '(!yield arg)' | bundle exec rubocop --stdin example.rb -a --only Style/RedundantParentheses
Inspecting 1 file
C

Offenses:

example.rb:1:1: C: [Corrected] Style/RedundantParentheses: Don't use parentheses around a unary operation.
(!yield arg)
^^^^^^^^^^^^

1 file inspected, 1 offense detected, 1 offense corrected
====================
!yield arg

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, I'll change it. I have something that seems to work, give me a while to check that this doesn't introduce other unwanted changes.

Copy link
Contributor Author

@Earlopain Earlopain Aug 28, 2025

Choose a reason for hiding this comment

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

I preserved this behaviour. Since it also applies to method calls, I added an additional changelog entry for that and a few tests

@koic
Copy link
Member

koic commented Aug 28, 2025

The Style/RedundantParens in the commit message and PR title is a typo for Style/RedundantParentheses.

@Earlopain Earlopain force-pushed the redundant-parens-yield-etc branch from 92ed884 to 5e1319e Compare August 28, 2025 13:18
@Earlopain
Copy link
Contributor Author

My bad, fix'd

@Earlopain Earlopain changed the title [Fix #14486] Fix false positives for Style/RedundantParens with unary operators and yield [Fix #14486] Fix false positives for Style/RedundantParentheses with unary operators and yield Aug 28, 2025
@Earlopain Earlopain force-pushed the redundant-parens-yield-etc branch from 5e1319e to 369f21a Compare August 28, 2025 18:35
it_behaves_like 'plausible', '+(1.foo)'
it_behaves_like 'plausible', '-(1.foo.bar)'
it_behaves_like 'plausible', '+(1.foo.bar)'
it_behaves_like 'plausible', 'foo(*(bar & baz))'
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These were not covered by tests and would have suggested removing the parens (which is wrong, it's a splat

Copy link
Contributor Author

Choose a reason for hiding this comment

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

BTW, I ran this change over real-world-rails and real-world-ruby and there are only a few new offenses (~60) and they all seem to make sense (apart from this one here, which I found through that).

Just mentioning since I found this to be a bit of a risky change overall with the change you requested but after checking I can say its rather safe (probably)

Comment on lines 212 to 231
# No problem removing the parens when it is the only expression
it_behaves_like 'redundant', '(!x arg)', '!x arg', 'a unary operation'
it_behaves_like 'redundant', '(!x.m arg)', '!x.m arg', 'a unary operation'
it_behaves_like 'redundant', '(!super arg)', '!super arg', 'a unary operation'
it_behaves_like 'redundant', '(!yield arg)', '!yield arg', 'a unary operation'
it_behaves_like 'redundant', '(!defined? arg)', '!defined? arg', 'a unary operation'
# Removing the parens leads to semantic differences
it_behaves_like 'plausible', '(!x arg) && foo'
it_behaves_like 'plausible', '(!x.m arg) && foo'
it_behaves_like 'plausible', '(!super arg) && foo'
it_behaves_like 'plausible', '(!yield arg) && foo'
it_behaves_like 'plausible', '(!defined? arg) && foo'
# Removing the parens leads to a syntax error
it_behaves_like 'plausible', 'foo && (!x arg)'
it_behaves_like 'plausible', 'foo && (!x.m arg)'
it_behaves_like 'plausible', 'foo && (!super arg)'
it_behaves_like 'plausible', 'foo && (!yield arg)'
it_behaves_like 'plausible', 'foo && (!defined? arg)'
Copy link
Member

Choose a reason for hiding this comment

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

That's just my two cents.

Suggested change
# No problem removing the parens when it is the only expression
it_behaves_like 'redundant', '(!x arg)', '!x arg', 'a unary operation'
it_behaves_like 'redundant', '(!x.m arg)', '!x.m arg', 'a unary operation'
it_behaves_like 'redundant', '(!super arg)', '!super arg', 'a unary operation'
it_behaves_like 'redundant', '(!yield arg)', '!yield arg', 'a unary operation'
it_behaves_like 'redundant', '(!defined? arg)', '!defined? arg', 'a unary operation'
# Removing the parens leads to semantic differences
it_behaves_like 'plausible', '(!x arg) && foo'
it_behaves_like 'plausible', '(!x.m arg) && foo'
it_behaves_like 'plausible', '(!super arg) && foo'
it_behaves_like 'plausible', '(!yield arg) && foo'
it_behaves_like 'plausible', '(!defined? arg) && foo'
# Removing the parens leads to a syntax error
it_behaves_like 'plausible', 'foo && (!x arg)'
it_behaves_like 'plausible', 'foo && (!x.m arg)'
it_behaves_like 'plausible', 'foo && (!super arg)'
it_behaves_like 'plausible', 'foo && (!yield arg)'
it_behaves_like 'plausible', 'foo && (!defined? arg)'
# No problem removing the parens when it is the only expression.
it_behaves_like 'redundant', '(!x arg)', '!x arg', 'a unary operation'
it_behaves_like 'redundant', '(!x.m arg)', '!x.m arg', 'a unary operation'
it_behaves_like 'redundant', '(!super arg)', '!super arg', 'a unary operation'
it_behaves_like 'redundant', '(!yield arg)', '!yield arg', 'a unary operation'
it_behaves_like 'redundant', '(!defined? arg)', '!defined? arg', 'a unary operation'
# Removing the parens leads to semantic differences.
it_behaves_like 'plausible', '(!x arg) && foo'
it_behaves_like 'plausible', '(!x.m arg) && foo'
it_behaves_like 'plausible', '(!super arg) && foo'
it_behaves_like 'plausible', '(!yield arg) && foo'
it_behaves_like 'plausible', '(!defined? arg) && foo'
# Removing the parens leads to a syntax error.
it_behaves_like 'plausible', 'foo && (!x arg)'
it_behaves_like 'plausible', 'foo && (!x.m arg)'
it_behaves_like 'plausible', 'foo && (!super arg)'
it_behaves_like 'plausible', 'foo && (!yield arg)'
it_behaves_like 'plausible', 'foo && (!defined? arg)'

…es` with unary operators and `yield`, `super`, or `defined?`

Removing parens for `defined?` does not result in a syntax error
but because of it's low precedence it's better to keep them anyways.
For example, in `(defined? foo) && baz`, removing the parens makes it into a single `defined?` invocation.
Since code like `(!yield arg)` should continue to register an offense, this also updates normal method calls
to do the same. As such, I added a few more testcases for that (+ a changelog entry)
@Earlopain Earlopain force-pushed the redundant-parens-yield-etc branch from 369f21a to 381374b Compare August 31, 2025 14:54
@koic koic merged commit 2271a78 into rubocop:master Aug 31, 2025
22 checks passed
@Earlopain Earlopain deleted the redundant-parens-yield-etc branch September 3, 2025 12:10
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.

Style/RedundantParentheses false positive when yield is used

2 participants