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

Style/LegacyHash cop, prohibiting Hash[] method #9478

Merged
merged 2 commits into from
Feb 4, 2021

Conversation

zverok
Copy link
Contributor

@zverok zverok commented Jan 30, 2021

# bad
Hash[ary]
^^^^^^^^^ Prefer ary.to_h to Hash[ary].

# good
ary.to_h

# bad
Hash[a, b, c, d]
^^^^^^^^^^^^^^^^ Prefer literal hash to Hash[arg1, arg2, ...].

# good
{a => b, c => d}

# bad
Hash[]
^^^^^^ Prefer literal hash to Hash[arg1, arg2, ...].

# good
{}

Correctly identifies un-autocorrecable cases (Hash[*foo], and an odd number of arguments).
I am open to suggestions regarding the name, it is my best guess :)


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.

@zverok zverok force-pushed the hash-to_h branch 2 times, most recently from e2e7a13 to b2022f7 Compare January 30, 2021 13:18
@zverok zverok mentioned this pull request Jan 30, 2021
CHANGELOG.md Outdated
### New features

* [#9478](https://github.com/rubocop-hq/rubocop/pull/9478): Add new `Style/LegacyHash` cop. ([@zverok][])

Copy link
Contributor

@tejasbubane tejasbubane Jan 30, 2021

Choose a reason for hiding this comment

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

Please refer this for creating changelog entries. It will create separate files under /changelog which will later be combined into this main file by rubocop maintainer during release.


MSG_TO_H = 'Prefer ary.to_h to Hash[ary].'
MSG_LITERAL = 'Prefer literal hash to Hash[arg1, arg2, ...].'
MSG_SPLAT = 'Prefer array_of_pairs.to_h to Hash[*array].'
Copy link
Contributor

Choose a reason for hiding this comment

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

These messages should be have dynamic variable names. Please refer to other cops like this one

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 contemplated this, but I prefer not to. The real code that caused offense might look like this (Rubocop's own codebase):

Hash[PERCENT_LITERAL_TYPES.map do |type|
  [type, preferred_delimiters_config[type] ||
     preferred_delimiters_config['default']]
end]

...and that's not the limit, block inside Hash[] can take many lines, so the suggestion would look really cumbersome. That's why I've decided to stick with abstract recommendations.

Copy link
Member

Choose a reason for hiding this comment

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

@zverok that's a fair point. I think maybe one case we can be more explicit about is Hash[] to {}.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@dvandersluis I actually believe it is the tiniest percent of actual Hash::[] usages in code, and didn't want to complicate the code for the rare case.

Copy link
Member

Choose a reason for hiding this comment

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

Fair enough, I was basing on your example cases.


def single_argument(node)
if node.arguments.first.splat_type?
add_offense(node, message: MSG_SPLAT)
Copy link
Contributor

Choose a reason for hiding this comment

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

We should auto-correct this too.

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 don't think so. With splat, there are basically two possibilities:

  1. Code inside Hash[] produces a flat list of keys and values by design (say, it reads a file which has [key, value, key, value] split line-by-line)
  2. Code actually produces an array of pairs, and then flatten(1) added to the end of it to match Hash[] signature.

In both cases, it should be reviewed and redecided by human.
The only working suggestion we can produce is

- Hash[*something]
+ something.each_slice(2).to_h

...and in most cases, it would not be a good suggestion.

RUBY
end

it 'does not tries to correct multi-argument Hash with odd number of arguments' do
Copy link
Contributor

Choose a reason for hiding this comment

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

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 don't understand this suggestion, sorry. What exactly should be fixed here (except for "tries"→"try" 😂)?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah after correcting "tries" I think it's fine. The internal affairs cop is just to make sure that we don't have descriptions that contradict the actual test.

config/default.yml Outdated Show resolved Hide resolved
@bbatsov
Copy link
Collaborator

bbatsov commented Jan 30, 2021

It seems to me that a name like ArrayToHashConversation or something along those lines would be better, as it's both more neutral (allows us to make this configurable) and more descriptive of what the cop actually checks for.

@zverok
Copy link
Contributor Author

zverok commented Jan 30, 2021

@bbatsov This was my first guess, but...

Hash[:a, 1, :b, 2]

...has no array to be seen :) (And we really have a few of those in the codebase, so they can happen in the wild!)

I thought about configurability and (maybe too bold for me) decided there should be no reason to prefer Hash[foo] to foo.to_h, the .to_h is clearly superior.

@bbatsov
Copy link
Collaborator

bbatsov commented Jan 30, 2021

Fair points. Maybe simply HashConversion then? I still think that LegacyHash is not the most informative name.

@zverok
Copy link
Contributor Author

zverok commented Jan 30, 2021

@bbatsov Yeah, that's ideal.
Updated cop name, edited commit history to make more sense, pweeze check!

Copy link
Collaborator

@bbatsov bbatsov left a comment

Choose a reason for hiding this comment

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

Looks great. I'll merge it after we cut 1.9.1.

Comment on lines +38 to +41
PERCENT_LITERAL_TYPES.map do |type|
[type, preferred_delimiters_config[type] ||
preferred_delimiters_config['default']]
end]
end.to_h
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe this could be further simplified to avoid the map

              PERCENT_LITERAL_TYPES.to_h do |type|
                [type, preferred_delimiters_config[type] ||
                  preferred_delimiters_config['default']]
              end

Copy link
Member

Choose a reason for hiding this comment

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

to_h with a block was only added in ruby 2.6, but RuboCop supports 2.4, so we can’t use that.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah I see. Thanks for explaining that.

@bbatsov
Copy link
Collaborator

bbatsov commented Feb 1, 2021

@zverok Can you squash the fixup commit?

@zverok zverok force-pushed the hash-to_h branch 2 times, most recently from a1a15a3 to a86c06b Compare February 1, 2021 11:08
@zverok
Copy link
Contributor Author

zverok commented Feb 1, 2021

@bbatsov Done

@dvandersluis dvandersluis linked an issue Feb 1, 2021 that may be closed by this pull request
@@ -0,0 +1 @@
* [#9478](https://github.com/rubocop-hq/rubocop/pull/9478): Add new `Style/LegacyHash` cop. ([@zverok][])
Copy link
Collaborator

Choose a reason for hiding this comment

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

Just noticed that we need to update the name here as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@bbatsov fixed

jmkoni pushed a commit to standardrb/standard that referenced this pull request May 3, 2021
* Update rubocop from 1.12.1 to [1.13.0](https://github.com/rubocop-hq/rubocop/releases/tag/v1.13.0)
* Update rubocop-performance from 1.9.2 to [1.11.1](https://github.com/rubocop-hq/rubocop-performance/releases/tag/v1.11.1)
* Enabled the following rules:
  * [`Performance/RedundantSplitRegexpArgument`](rubocop/rubocop-performance#190)
  * [`Style/IfWithBooleanLiteralBranches`](rubocop/rubocop#9396)
  * [`Lint/TripleQuotes`](rubocop/rubocop#9402)
  * [`Lint/SymbolConversion`](rubocop/rubocop#9362)
  * [`Lint/OrAssignmentToConstant`](rubocop/rubocop#9363)
  * [`Lint/NumberedParameterAssignment`](rubocop/rubocop#9326)
  * [`Style/HashConversion`](rubocop/rubocop#9478)
  * [`Gemspec/DateAssignment`](rubocop/rubocop#9496)
  * [`Style/StringChars`](rubocop/rubocop#9615)
@zverok zverok deleted the hash-to_h branch May 5, 2021 20:40
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.

Hash[] vs to_h
5 participants