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

Add Hash#each_value and Hash#each_key cop #7677

Merged
merged 8 commits into from Feb 7, 2020

Conversation

jemmaissroff
Copy link
Contributor

@jemmaissroff jemmaissroff commented Jan 28, 2020

Added a cop to match the style guide's recommendations around hash-each.
This cop enforces correct usage of Hash#each_key and Hash#each_value
as opposed to Hash#keys.each and Hash#values.each.


Before submitting the PR make sure the following are checked:

  • 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.
  • Added an entry to the Changelog if the new code introduces user-observable changes. See changelog entry format.
  • The PR relates to only one subject with a clear title and description in grammatically correct, complete sentences.
  • Run bundle exec rake default. It executes all tests and RuboCop for itself, and generates the documentation.

Style/HashEachMethods:
Description: 'Use Hash#each_key and Hash#each_value.'
StyleGuide: '#hash-each'
Enabled: true
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please, change this to pending. We're no longer enabling any new cops by default.

@@ -55,7 +55,7 @@ def load_file(file)
end

def add_missing_namespaces(path, hash)
hash.keys.each do |key|
hash.keys.each do |key| # rubocop:disable Style/HashEachMethods
Copy link
Collaborator

Choose a reason for hiding this comment

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

I guess we can just update this, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I actually don't think we can in this case. Due to the hash[q] = hash.delete(key) line below, we'll get a RuntimeError: can't add a new key into hash during iteration if we try do a hash.each_key here.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ops. my bad. Should have read the code more carefully. :-)

Copy link
Contributor

Choose a reason for hiding this comment

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

Could this whole issue be sidestepped with a

hash.transform_keys { |key| Cop::Cop.qualified_cop_name(key, path) }

? Or do we need to support rubies < 2.5?

@bbatsov
Copy link
Collaborator

bbatsov commented Jan 28, 2020

The code looks solid to me. Thanks for tackling this!

I guess we should mark the cop as unsafe, though, as there's definitely some potential for false positives.

@jemmaissroff
Copy link
Contributor Author

The code looks solid to me. Thanks for tackling this!

I guess we should mark the cop as unsafe, though, as there's definitely some potential for false positives.

Thanks for looking, I've pushed another commit where I marked it as unsafe, and also pending!

CHANGELOG.md Outdated
@@ -99,6 +100,7 @@
* [#7446](https://github.com/rubocop-hq/rubocop/issues/7446): Add `merge` to list of non-mutating methods. ([@cstyles][])
* [#7077](https://github.com/rubocop-hq/rubocop/issues/7077): **(Breaking)** Rename `Unneeded*` cops to `Redundant*` (e.g., `Style/UnneededPercentQ` becomes `Style/RedundantPercentQ`). ([@scottmatthewman][])
* [#7396](https://github.com/rubocop-hq/rubocop/issues/7396): Display assignments, branches, and conditions values with the offense. ([@avmnu-sng][])
* [#7434](https://github.com/rubocop-hq/rubocop/issues/7434): Fix an incorrect autocorrect for `Style/MultilineWhenThen` when the body of `when` branch starts with `then`. ([@koic][])
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like this might be a rebase artifact? The same line appears above in the Bug Fixes section.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, this change is not the difference in this PR.

Description: 'Use Hash#each_key and Hash#each_value.'
StyleGuide: '#hash-each'
Enabled: pending
VersionAdded: '0.79'
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be updated to '0.80'? Looks like 0.79 was released already.

Copy link
Member

Choose a reason for hiding this comment

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

Sure.


MSG = 'Use `%<prefer>s` instead of `%<current>s`.'

def_node_matcher :kv_each, <<-PATTERN
Copy link
Member

Choose a reason for hiding this comment

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

Can you update this entire PR with squiggly heredoc? (<<~)

it 'auto-corrects foo#keys.each with foo#each_key' do
new_source = autocorrect_source('foo.keys.each { |k| p k }')
expect(new_source).to eq('foo.each_key { |k| p k }')
end
Copy link
Member

Choose a reason for hiding this comment

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

Can you use expect_correction for test an automatic correction? The following address will be helpful.
#7650

@rrosenblum
Copy link
Contributor

I knew this cop sounded familiar to me. We have Performance/InefficientHashSearch in the Performance project for changing keys.include? and values.include? to key? or has_key? and value? or has_value?. Does this cop make more sense as a Style Cop or a Performance Cop?

I also think we may have tried to implement this same cop in the past. I don't think this change is safe if you are mutating the keys or values while using each_key or each_value.

This code does not work if you change keys.each to each_key because we remove keys
lib/rubocop/config_loader.rb

      def add_missing_namespaces(path, hash)
        hash.keys.each do |key|
          q = Cop::Cop.qualified_cop_name(key, path)
          next if q == key

          hash[q] = hash.delete(key)
        end
      end

@Darhazer
Copy link
Member

Darhazer commented Jan 31, 2020

We used to have Performance/HashEach. Not sure what happened to it.

Update: Found the removal of the cop

@jemmaissroff
Copy link
Contributor Author

We used to have Performance/HashEach. Not sure what happened to it.

Update: Found the removal of the cop

Yup, and there was an issue filed where it was suggested to instead by a Style PR. I didn't link it as part of the commit message because it's in a different repo and so I think the link would break. Let me know if I should be doing something differently here!

@bbatsov
Copy link
Collaborator

bbatsov commented Feb 4, 2020

@jemmaissroff Just address the failing build and I think we're good here.

@bbatsov
Copy link
Collaborator

bbatsov commented Feb 4, 2020

Actually - it might be cool to also make the cop configurable, so that people could decide whether they prefer hash.foo.each or hash.each_foo. Generally we try to be inclusive of most people's preferences. :-) Anyways, that's just an idea - if you don't have time for this it's fine.

@rrosenblum
Copy link
Contributor

it might be cool to also make the cop configurable

If we are classifying this as a Style Cop, then I completely agree that it would be cool for this to be configurable. If we were to classify it as a Performance Cop then I don't think the configuration makes sense.

@jemmaissroff
Copy link
Contributor Author

@bbatsov build is fixed!

.rubocop.yml Outdated
@@ -28,6 +28,9 @@ Style/FormatStringToken:
Exclude:
- spec/**/*

Style/HashEachMethods:
Enabled: false
Copy link
Member

Choose a reason for hiding this comment

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

This setting looks unnecessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I had a build failing that prompted me to add it here. Can you tell me more about why this looks unnecessary?

Copy link
Member

Choose a reason for hiding this comment

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

By default, it is better to use the setting in config/default.yml.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think I'm getting confused around what pending will do. It appears that breaks builds also. Are you suggesting to leave it as pending and then remove it from here?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, config/default.yml leaves the specification of Enabled: pending. OTOH, I expect this setting to be removed from .rubocop.yml.

It appears that breaks builds

Can you show me what build error is occurring?

Style/HashEachMethods:
Description: 'Use Hash#each_key and Hash#each_value.'
StyleGuide: '#hash-each'
Enabled: false
Copy link
Member

Choose a reason for hiding this comment

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

Can you update to Enabled: pending status?
#7677 (comment)

CHANGELOG.md Outdated
@@ -8,6 +8,7 @@
* [#7663](https://github.com/rubocop-hq/rubocop/pull/7663): Add new `Style/HashTransformKeys` and `Style/HashTransformValues` cops. ([@djudd][], [@eugeneius][])
* [#7619](https://github.com/rubocop-hq/rubocop/issues/7619): Support autocorrect of legacy cop names for `Migration/DepartmentName`. ([@koic][])
* [#7659](https://github.com/rubocop-hq/rubocop/pull/7659): Layout/LineLength autocorrect now breaks up long lines with blocks. ([@maxh][])
* Add a cop for `Hash#each_key` and `Hash#each_value`. ([@jemmaissroff][])
Copy link
Member

Choose a reason for hiding this comment

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

Can you add the PR link to this changelog entry?

@bbatsov
Copy link
Collaborator

bbatsov commented Feb 6, 2020

It seems you need to rebase to master and run the rake command that regenerates the manual and the build will pass.

@jemmaissroff
Copy link
Contributor Author

Ah okay, that was it! Builds are now passing. Thanks for the help @bbatsov and @koic !

@bbatsov bbatsov merged commit cca6e8f into rubocop:master Feb 7, 2020
@bbatsov
Copy link
Collaborator

bbatsov commented Feb 7, 2020

🎉

Thanks for working on this! 🙇

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.

None yet

6 participants