KEMBAR78
Register offense for parentheses around method calls with blocks in `Style/RedundantParentheses` by lovro-bikic · Pull Request #14407 · rubocop/rubocop · GitHub
Skip to content

Conversation

@lovro-bikic
Copy link
Contributor

@lovro-bikic lovro-bikic commented Jul 31, 2025

Currently, Style/RedundantParentheses won't register code like:

(foo.select { _1 }).one?

as an offense. Method calls without blocks are registered as offenses under this cop.

This patch registers redundant parentheses around method calls with blocks.

Here's the real-world-rails report:

Report
# rubocop ../real-world-rails/apps/{mastodon,dev.to,discourse,gitlabhq,openproject,canvas-lms} --only Style/RedundantParentheses

# ~/real-world-rails/apps/mastodon/spec/models/form/import_spec.rb:289:82: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
it_behaves_like 'on successful import', 'following', 'merge', 'imports.txt', (%w(user@example.com user@test.com).map { |acct| { 'acct' => acct } })

# ~/real-world-rails/apps/mastodon/spec/models/form/import_spec.rb:290:86: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
it_behaves_like 'on successful import', 'following', 'overwrite', 'imports.txt', (%w(user@example.com user@test.com).map { |acct| { 'acct' => acct } })

# ~/real-world-rails/apps/mastodon/spec/models/form/import_spec.rb:291:81: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
it_behaves_like 'on successful import', 'blocking', 'merge', 'imports.txt', (%w(user@example.com user@test.com).map { |acct| { 'acct' => acct } })

# ~/real-world-rails/apps/mastodon/spec/models/form/import_spec.rb:292:85: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
it_behaves_like 'on successful import', 'blocking', 'overwrite', 'imports.txt', (%w(user@example.com user@test.com).map { |acct| { 'acct' => acct } })

# ~/real-world-rails/apps/mastodon/spec/models/form/import_spec.rb:293:79: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
it_behaves_like 'on successful import', 'muting', 'merge', 'imports.txt', (%w(user@example.com user@test.com).map { |acct| { 'acct' => acct } })

# ~/real-world-rails/apps/mastodon/spec/models/form/import_spec.rb:294:94: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
it_behaves_like 'on successful import', 'domain_blocking', 'merge', 'domain_blocks.csv', (%w(bad.domain worse.domain reject.media).map { |domain| { 'domain' => domain } })

# ~/real-world-rails/apps/mastodon/spec/models/form/import_spec.rb:295:91: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
it_behaves_like 'on successful import', 'bookmarks', 'merge', 'bookmark-imports.txt', (%w(https://example.com/statuses/1312 https://local.com/users/foo/statuses/42 https://unknown-remote.com/users/bar/statuses/1 https://example.com/statuses/direct).map { |uri| { 'uri' => uri } })

# ~/real-world-rails/apps/mastodon/spec/workers/scheduler/accounts_statuses_cleanup_scheduler_spec.rb:115:16: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
.and (not_change { account_bob.statuses.count }) # No cleanup policy for account

# ~/real-world-rails/apps/discourse/app/controllers/tags_controller.rb:170:86: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
canonical_url "#{Discourse.base_url_no_prefix}#{public_send(canonical_method, *(canonical_params.values.map { |t| t.force_encoding("UTF-8") }))}"

# ~/real-world-rails/apps/gitlabhq/keeps/delete_old_feature_flags.rb:129:10: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
*(GREP_IGNORE.map { |path| ":^#{path}" })

# ~/real-world-rails/apps/gitlabhq/qa/spec/support/run_spec.rb:4:26: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
let(:class_instance) { (Class.new { include QA::Support::Run }).new }

# ~/real-world-rails/apps/gitlabhq/spec/lib/gitlab/avatar_cache_spec.rb:111:42: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
described_class.delete_by_email(*(Array.new(1001) { |i| i }))

# ~/real-world-rails/apps/gitlabhq/spec/lib/gitlab/exclusive_lease_helpers_spec.rb:8:26: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
let(:class_instance) { (Class.new { include ::Gitlab::ExclusiveLeaseHelpers }).new }

# ~/real-world-rails/apps/gitlabhq/spec/lib/gitlab/loop_helpers_spec.rb:6:26: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
let(:class_instance) { (Class.new { include ::Gitlab::LoopHelpers }).new }

# ~/real-world-rails/apps/gitlabhq/spec/lib/gitlab/repository_set_cache_spec.rb:135:23: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
cache.expire(*(Array.new(1001) { |i| i }))

# ~/real-world-rails/apps/gitlabhq/spec/requests/api/graphql/gitlab_schema_spec.rb:240:23: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
field :bar, (Class.new(::Types::BaseEnum) do ...

# ~/real-world-rails/apps/gitlabhq/spec/workers/pages/deactivated_deployments_delete_cron_worker_spec.rb:16:19: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
.and change { (file_paths.any? { |path| File.exist?(path) }) }.from(true).to(false)

# ~/real-world-rails/apps/openproject/app/menus/submenu.rb:109:31: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
if query_params.empty? && (%i[filters query_props query_id name].any? { |k| params.key? k })

# ~/real-world-rails/apps/openproject/app/menus/work_packages/menu.rb:71:9: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
(%i[filters query_props query_id name].none? { |k| params.key? k }) &&

# ~/real-world-rails/apps/openproject/app/models/queries/operators.rb:66:21: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
OPERATORS = Hash[*(operators.map { |o| [o.symbol.to_s, o] }).flatten].freeze

# ~/real-world-rails/apps/openproject/db/migrate/10000000000000_to_v710_aggregated_migrations.rb:140:22: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
execute <<-SQL + (intersection.map { |version| <<-CONDITIONS }).join(" OR ")

# ~/real-world-rails/apps/openproject/db/migrate/migration_utils/migration_squasher.rb:52:58: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
ActiveRecord::Base.connection.execute <<-SQL + (intersection.map { |version| <<-CONDITIONS }).join(" OR ")

# ~/real-world-rails/apps/openproject/lib/open_project/changed_by_system.rb:98:7: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
(model_changes.reject { |key, change| changed_by_system[key] == change }).keys

# ~/real-world-rails/apps/openproject/lib/open_project/text_formatting/filters/table_of_contents_filter.rb:105:23: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
result << (content_tag(:ul, class: "op-uc-toc--list") do ...

# ~/real-world-rails/apps/openproject/lib/redmine/platform.rb:33:77: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
(RUBY_PLATFORM =~ /(:?mswin|mingw)/) || (RUBY_PLATFORM == "java" && (ENV.fetch("OS") do ...

# ~/real-world-rails/apps/openproject/lib/tasks/copyright.rake:144:16: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
excluded = (%w(acts_as_tree rfpdf verification).map { |dir| "lib_static/plugins/#{dir}" })

# ~/real-world-rails/apps/openproject/modules/reporting/lib/widget/table/entry_table.rb:133:17: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
rows << (content_tag(:tr) do ...

# ~/real-world-rails/apps/openproject/spec/support/identical_ext.rb:60:7: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
(o.any? { |other_each| other_each.identical?(ea) })

# ~/real-world-rails/apps/canvas-lms/app/controllers/accounts_controller.rb:591:16: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
includes = (supported_includes.any? { |i| params[:include]&.include?(i) }) ? supported_includes : []

# ~/real-world-rails/apps/canvas-lms/app/controllers/groups_controller.rb:243:22: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
render json: (@groups.map { |g| group_json(g, @current_user, session, includes) })

# ~/real-world-rails/apps/canvas-lms/app/controllers/security_controller.rb:47:5: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
( ...

# ~/real-world-rails/apps/canvas-lms/app/controllers/tabs_controller.rb:142:11: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
tab = (tabs.find { |t| t.with_indifferent_access[:css_class] == css_class }).with_indifferent_access

# ~/real-world-rails/apps/canvas-lms/app/models/attachment.rb:2098:19: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
error_level = (warnable_errors.any? { |kls| e.is_a?(kls) }) ? :warn : :error

# ~/real-world-rails/apps/canvas-lms/app/models/conversation.rb:707:24: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
guard_rail_env = (conversations.any? { |c| c.updated_at && c.updated_at > 10.seconds.ago }) ? :primary : :secondary

# ~/real-world-rails/apps/canvas-lms/app/models/notification_policy_override.rb:78:6: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
!(find_all_for(user, contexts, channel:).find { |npo| npo.notification_id.nil? && npo.workflow_state == "disabled" })

# ~/real-world-rails/apps/canvas-lms/app/presenters/grade_summary_presenter.rb:87:5: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
(observed_students.to_a.min_by { |e| e[0].sortable_name })[1].first

# ~/real-world-rails/apps/canvas-lms/lib/lti/substitutions_helper.rb:221:7: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
(enrollments.any? { |membership| membership.state_based_on_date == :active }) ? LtiOutbound::LTIUser::ACTIVE_STATE : LtiOutbound::LTIUser::INACTIVE_STATE

# ~/real-world-rails/apps/canvas-lms/lib/notification_message_creator.rb:253:38: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
default_email?(user, channel) && (user.notification_policies.find { |np| np.notification_id == @notification.id }).nil?

# ~/real-world-rails/apps/canvas-lms/lib/user_list.rb:314:10: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
((@addresses.find { |a| a[:user_id] == address[:user_id] && a[:shard] == address[:shard] }) ? @duplicate_addresses : @addresses) << address

# ~/real-world-rails/apps/canvas-lms/lib/user_list.rb:316:10: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
((@addresses.find { |a| a[:address].casecmp?(address[:address]) }) ? @duplicate_addresses : @addresses) << address

# ~/real-world-rails/apps/canvas-lms/lib/user_list.rb:319:10: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
((@addresses.find { |a| a[:address].casecmp?(address[:address]) }) ? @duplicate_addresses : @addresses) << address

# ~/real-world-rails/apps/canvas-lms/spec/apis/v1/calendar_events_api_spec.rb:4564:23: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
all_day_event = (cal.events.select { |e| e.summary.include? "i am all day" }).first

# ~/real-world-rails/apps/canvas-lms/spec/lib/api/v1/moderation_grader_spec.rb:24:9: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
api = (Class.new do ...

# ~/real-world-rails/apps/canvas-lms/spec/lib/cc/basic_lti_links_spec.rb:24:13: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
subject { (Class.new { include CC::BasicLTILinks }).new }

# ~/real-world-rails/apps/canvas-lms/spec/lib/cc/lti_resource_links_spec.rb:110:7: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
(document.xpath("//blti:extensions/lticm:property").map do |el| ...

# ~/real-world-rails/apps/canvas-lms/spec/models/lti/tool_proxy_service_spec.rb:201:14: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
!(resource_placements.select { |p| p.placement == placement }).empty?

# ~/real-world-rails/apps/canvas-lms/spec/selenium/grades/student_grades_page/students_grades_page_arrange_by_spec.rb:87:24: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
current_list = (ff("#grades_summary tr a").reject { |a| a.text.empty? }).collect(&:text)

# ~/real-world-rails/apps/canvas-lms/spec/selenium/grades/student_grades_page/students_grades_page_arrange_by_spec.rb:93:24: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
current_list = (ff("#grades_summary tr a").reject { |a| a.text.empty? }).collect(&:text)

# ~/real-world-rails/apps/canvas-lms/spec/selenium/grades/student_grades_page/students_grades_page_arrange_by_spec.rb:99:24: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
current_list = (ff("#grades_summary tr a").reject { |a| a.text.empty? }).collect(&:text)

# ~/real-world-rails/apps/canvas-lms/spec/selenium/grades/student_grades_page/students_grades_page_arrange_by_spec.rb:105:24: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
current_list = (ff("#grades_summary tr a").reject { |a| a.text.empty? }).collect(&:text)

# ~/real-world-rails/apps/canvas-lms/spec/selenium/grades/student_grades_page/students_grades_page_arrange_by_spec.rb:115:24: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
current_list = (ff("#grades_summary tr a").reject { |a| a.text.empty? }).collect(&:text)

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.

@lovro-bikic lovro-bikic force-pushed the redundant-parentheses-false-negative-method-block branch from e907e7a to 868c1a3 Compare July 31, 2025 18:05
@koic koic merged commit 177ea7e into rubocop:master Aug 1, 2025
23 checks passed
@lovro-bikic lovro-bikic deleted the redundant-parentheses-false-negative-method-block branch August 1, 2025 07:43
@franzliedke
Copy link
Contributor

@koic I believe this is too loose now.

This first example can be fixed as proposed:

lib/myfile.rb:14:20: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
  modifier :param, (obj.some_method(arg1) { block_value })

The fix would be:

  modifier :param, obj.some_method(arg1) { block_value }

However, when using do...end:

lib/myfile.rb:14:20: C: [Correctable] Style/RedundantParentheses: Don't use parentheses around a method call.
  modifier :param, (obj.some_method(arg1) do ...

When applying the "correction":

  modifier :param, obj.some_method(arg1) do
    block_value
  end

This is incorrect, as do...end has different precedence than {}. Here, the block will be passed to modifier.

@franzliedke
Copy link
Contributor

Interestingly, the first one ends up with a new offense by Lint/AmbiguousBlockAssociation, which can then be fixed by wrapping the arguments to modifier in parens.

The do...end form does not raise such an offense.

koic added a commit to koic/rubocop that referenced this pull request Aug 6, 2025
Follow-up to rubocop#14407 (comment).

This PR fixes false positives for `Style/RedundantParentheses`
when `do`...`end` block is wrapped in parentheses as a method argument.
@koic
Copy link
Member

koic commented Aug 6, 2025

@franzliedke I've opened #14427 to solve that issue.

floehopper added a commit to freerange/mocha that referenced this pull request Aug 10, 2025
Seems to relate to this change [1] in rubocop v1.79.2.

[1]: rubocop/rubocop#14407
floehopper added a commit to freerange/mocha that referenced this pull request Aug 10, 2025
Seems to relate to this change [1] in rubocop v1.79.2.

[1]: rubocop/rubocop#14407
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