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 RSpec/SortMetadata cop to sort only trailing metadata #1948

Merged

Conversation

cbliard
Copy link
Contributor

@cbliard cbliard commented Aug 12, 2024

Fixes #1946.

Symbols in metadata are processed by RSpec only when they are positioned last, meaning the other parameter types must be positioned before the symbols. RSpec context/describe accepts second non-symbol argument as an additional description.

Previously this cop was sorting all arguments, regardless of their type, which could lead to having the second string argument (the additional description) moved to another place (see #1946 for details). This PR fixes it.

Questions for reviewers:

  • is it ok to sort strings before variables? I have the feeling that this cop's responsibility should be to just take care of sorting symbols at the end of the arguments list. As discussed during the review, only the last metadata arguments are considered (symbols and final hash argument).
  • the message "Sort metadata alphabetically." does not fully reflect what the cop is doing anymore, and can be confusing for code like describe 'Something', :a, b, :c because the metadata is sorted. I could not come up with a good message. Suggestions? As only the trailing metadata is sorted now, the message seems ok.
  • I noticed an autocorrect issue with describe 'Something', :b, :a, { foo: :bar }: it gets autocorrected to describe 'Something', :b, :a, foo: :bar }. Should it be fixed in the same PR?
  • I updated the RuboCop::Cop::RSpec::Metadata#on_metadata_arguments method because it was skipping the last argument if it was not a hash. I also renamed symbols to metadata_arguments (or args in RSpec/SortMetadata cop). Should symbols be renamed to metadata_arguments or args in other cops relying on on_metadata too?

Before submitting the PR make sure the following are checked:

  • Feature branch is up-to-date with master (if not - rebase it).
  • Squashed related commits together.
  • Added tests.
  • Updated documentation.
  • Added an entry to the CHANGELOG.md if the new code introduces user-observable changes.
  • The build (bundle exec rake) passes (be sure to run this locally, since it may produce updated documentation that you will need to commit).

@cbliard cbliard requested a review from a team as a code owner August 12, 2024 06:37
it 'registers an offense when a symbol metadata is before second docstring ' \
'argument' do
expect_offense(<<~RUBY)
RSpec.describe 'Something', :a, :z, 'second docstring' do
Copy link
Member

Choose a reason for hiding this comment

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

Invalid RSpec syntax:

ArgumentError:
  wrong number of arguments (given 4, expected 0..2)

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

Actually there was already an existing test on master which would lead to this ArgumentError, even after autocorrect:

it 'registers an offense when using mixed metadata ' \
'and both symbols metadata and hash keys are not in alphabetical order ' \
'and the hash values are complex objects' do
expect_offense(<<~RUBY)
it 'Something', variable, 'B', :a, key => {}, foo: ->(x) { bar(x) }, Identifier.sample => true, baz: Snafu.new do
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Sort metadata alphabetically.
end
RUBY
expect_correction(<<~RUBY)
it 'Something', :a, 'B', variable, baz: Snafu.new, foo: ->(x) { bar(x) }, Identifier.sample => true, key => {} do
end
RUBY
end

That's why I thought adding this test would be ok. Note that after autocorrect the last string argument is moved at the 2nd place, so the ArgumentError would not occur anymore.

How should I rewrite this one?

it 'registers an offense when a symbol metadata is before a variable ' \
'argument' do
expect_offense(<<~RUBY)
RSpec.describe 'Something', :a, :z, variable, foo: :bar do
Copy link
Member

Choose a reason for hiding this comment

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

If the variable holds something that is not a symbol, this will fail with a similar ArgumentError

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

It is a bit like this existing test of MetadataStyle cop:

context 'with symbol metadata with another existing non-literal metadata' do
it 'registers offense' do
expect_offense(<<~RUBY)
describe 'Something', :a, b do
^^ Use hash style for metadata.
end
RUBY
expect_no_corrections
end
end

This one too will fail if b is not a symbol nor a hash.

How should I rewrite it then? Adding a variable = :v on the first line?

@pirj
Copy link
Member

pirj commented Aug 17, 2024

I suggest extending the spec in accordance with our findings. I'm happy to help on this front. Please accept my apologies for the delay in review, it's a summer season over here.

Copy link
Contributor Author

@cbliard cbliard left a comment

Choose a reason for hiding this comment

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

I replied to your comments and I am open to amend this pull request with your suggestions.

Please accept my apologies for the delay as well. As you wrote, it's summer season.

it 'registers an offense when a symbol metadata is before second docstring ' \
'argument' do
expect_offense(<<~RUBY)
RSpec.describe 'Something', :a, :z, 'second docstring' do
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 indeed.

Actually there was already an existing test on master which would lead to this ArgumentError, even after autocorrect:

it 'registers an offense when using mixed metadata ' \
'and both symbols metadata and hash keys are not in alphabetical order ' \
'and the hash values are complex objects' do
expect_offense(<<~RUBY)
it 'Something', variable, 'B', :a, key => {}, foo: ->(x) { bar(x) }, Identifier.sample => true, baz: Snafu.new do
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Sort metadata alphabetically.
end
RUBY
expect_correction(<<~RUBY)
it 'Something', :a, 'B', variable, baz: Snafu.new, foo: ->(x) { bar(x) }, Identifier.sample => true, key => {} do
end
RUBY
end

That's why I thought adding this test would be ok. Note that after autocorrect the last string argument is moved at the 2nd place, so the ArgumentError would not occur anymore.

How should I rewrite this one?

it 'registers an offense when a symbol metadata is before a variable ' \
'argument' do
expect_offense(<<~RUBY)
RSpec.describe 'Something', :a, :z, variable, foo: :bar do
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 indeed.

It is a bit like this existing test of MetadataStyle cop:

context 'with symbol metadata with another existing non-literal metadata' do
it 'registers offense' do
expect_offense(<<~RUBY)
describe 'Something', :a, b do
^^ Use hash style for metadata.
end
RUBY
expect_no_corrections
end
end

This one too will fail if b is not a symbol nor a hash.

How should I rewrite it then? Adding a variable = :v on the first line?

spec/rubocop/cop/rspec/sort_metadata_spec.rb Outdated Show resolved Hide resolved
spec/rubocop/cop/rspec/sort_metadata_spec.rb Outdated Show resolved Hide resolved
@cbliard cbliard force-pushed the sort_metadata_symbols_at_the_end_of_args_list branch from 95de9d6 to 7d1c8b8 Compare September 11, 2024 06:17
@cbliard
Copy link
Contributor Author

cbliard commented Sep 25, 2024

Hi @pirj,

Is there anything I can do to help with this pull request?

@pirj
Copy link
Member

pirj commented Oct 8, 2024

Sorry for the delayed response.
We had a fruitful discussion in the ticket about what cop should and shouldn’t do. I recall it as:

  • it should not attempt to handle code that will make RSpec fail anyway
  • it should handle realistic code
  • it should not autocorrect to code that would make RSpec to fail (was it what sparked this discussion initially?)
  • it should consider those subtle differences between examples and example groups when it comes to metadata arguments
  • it should ignore non-literal metadata arguments, and avoid risks automatically ordering them

Does this make sense? I’m writing this from my memory, and those criteria may be incomplete or incorrect.

Would it be a stretch to ask you to go over existing specs and make sure those criteria are met?
Last time I checked, I was under impression that a few cases in the very beginning of the changes to specs were off, and this repelled me from code reviewing further, as I thought it would require a lot commenting.

If you prefer, I can go through those specs, too.

And in any case - thank you for the dedication!
This is doable, let’s push the cop through the line where it can shine! 🌟

@cbliard
Copy link
Contributor Author

cbliard commented Oct 9, 2024

We had a fruitful discussion in the ticket about what cop should and shouldn’t do. I recall it as:

  • it should not attempt to handle code that will make RSpec fail anyway
  • it should handle realistic code
  • it should not autocorrect to code that would make RSpec to fail (was it what sparked this discussion initially?)
  • it should consider those subtle differences between examples and example groups when it comes to metadata arguments
  • it should ignore non-literal metadata arguments, and avoid risks automatically ordering them

Does this make sense? I’m writing this from my memory, and those criteria may be incomplete or incorrect.

Yes this makes sense. Not sure if that's exactly what we discussed but this makes sense.

Indeed "it should not autocorrect to code that would make RSpec to fail" is what sparked this discussion initially as rubocop-rspec autocorrects this spec file and the resulting corrected file makes RSpec fail because the string parameter is moved from second to third place.

Would it be a stretch to ask you to go over existing specs and make sure those criteria are met? Last time I checked, I was under impression that a few cases in the very beginning of the changes to specs were off, and this repelled me from code reviewing further, as I thought it would require a lot commenting.

Sure, so I should adapt these specs to ensure that it does not trigger at all, right?

I'll do that.

Thanks for the feedback.

@cbliard cbliard force-pushed the sort_metadata_symbols_at_the_end_of_args_list branch 2 times, most recently from 621c304 to 9fbf40d Compare October 10, 2024 07:24
@cbliard
Copy link
Contributor Author

cbliard commented Oct 10, 2024

@pirj I have adapted the spec and code as discussed. It is up for review.

It does not completely ignore code that could make RSpec fail. Instead it limits sorting to the trailing args which are actual symbols. Any symbol arg before a string literal or a variable will be ignored.

@cbliard cbliard requested a review from pirj October 14, 2024 06:41
Copy link
Member

@pirj pirj left a comment

Choose a reason for hiding this comment

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

Two months later 🙈

spec/rubocop/cop/rspec/sort_metadata_spec.rb Outdated Show resolved Hide resolved
spec/rubocop/cop/rspec/sort_metadata_spec.rb Outdated Show resolved Hide resolved
spec/rubocop/cop/rspec/sort_metadata_spec.rb Outdated Show resolved Hide resolved
spec/rubocop/cop/rspec/sort_metadata_spec.rb Outdated Show resolved Hide resolved
spec/rubocop/cop/rspec/sort_metadata_spec.rb Outdated Show resolved Hide resolved
spec/rubocop/cop/rspec/sort_metadata_spec.rb Outdated Show resolved Hide resolved
spec/rubocop/cop/rspec/sort_metadata_spec.rb Outdated Show resolved Hide resolved
spec/rubocop/cop/rspec/sort_metadata_spec.rb Outdated Show resolved Hide resolved
spec/rubocop/cop/rspec/sort_metadata_spec.rb Show resolved Hide resolved
@cbliard cbliard force-pushed the sort_metadata_symbols_at_the_end_of_args_list branch from 9fbf40d to 7acdb7c Compare December 23, 2024 15:48
@cbliard
Copy link
Contributor Author

cbliard commented Dec 23, 2024

Thanks @pirj for the review. I updated it following your suggestions and rebased it on master branch.

Is there anything else I should do?

@cbliard cbliard changed the title Fix RSpec/SortMetadata cop to sort strings and variables first Fix RSpec/SortMetadata cop to sort only trailing metadata Dec 30, 2024
Copy link
Member

@pirj pirj left a comment

Choose a reason for hiding this comment

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

Thank you for the immense effort you've put into this!

Please accept my apologies for the daunting experience with those ages-long reviews. It is usually not like this in our repos 🙈

@pirj pirj requested a review from a team January 6, 2025 06:08
Comment on lines +48 to +50
it 'registers an offense when a symbol metadata not in alphabetical order ' \
'is before a variable argument being the last argument ' \
'as it could be a hash' do
Copy link
Collaborator

Choose a reason for hiding this comment

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

This seems a bit strange to me. If some_hash “could be a hash”, it could also be e.g. an array. And if we sort symbol metadata before an array variable, why don’t we always sort symbol metadata before a variable?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's a valid point. Indeed each group of symbols could be alphabetically sorted independently.

This PR was originally addressing #1946 and is now approved. If that makes sense, could your point be registered as a separate issue to be addressed later? Feel free to mention me and I can work on a PR for it.

Copy link
Collaborator

Choose a reason for hiding this comment

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

My comment was not meant to block this PR; I’m find with merging this as-is.

@cbliard
Copy link
Contributor Author

cbliard commented Jan 6, 2025

Thank you @pirj for the review.

No worries for the time it took. I was not very available as well.

@cbliard
Copy link
Contributor Author

cbliard commented Jan 9, 2025

Anything particular that can be done to make the "CI / Prism" check pass and get that PR merged?

@ydah
Copy link
Member

ydah commented Jan 9, 2025

Anything particular that can be done to make the "CI / Prism" check pass and get that PR merged?

@cbliard Could you try git rebase and import the latest master branch?

@cbliard cbliard force-pushed the sort_metadata_symbols_at_the_end_of_args_list branch from 7acdb7c to 0ba1569 Compare January 9, 2025 14:39
Metadata processed by RSpec is:
- the last argument when it's a hash
- the trailing arguments when they are symbols

Only this metadata is sorted by this cop.

If the second argument to a `context`/`describe` block is used as an
additional description, it is not sorted anymore. This fixes rubocop#1946.

Co-authored-by: Phil Pirozhkov <[email protected]>
@cbliard cbliard force-pushed the sort_metadata_symbols_at_the_end_of_args_list branch from 0ba1569 to c2e861f Compare January 9, 2025 15:03
@cbliard
Copy link
Contributor Author

cbliard commented Jan 9, 2025

Thanks @ydah, that fix the Prism issue thanks to the example producing a syntax error you removed 2 weeks ago.

Then there was this error poping on "Edge RuboCop: internal_investigation" workflow:

lib/rubocop/rspec/description_extractor.rb:65:11: C: [Correctable] Style/MultipleComparison: Avoid comparing a variable with multiple items in a conditional, use Array#include? instead.
          yardoc.superclass.path == RSPEC_COP_CLASS_NAME || ...
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

I fixed it too and force pushed again.

@pirj pirj merged commit e44a27b into rubocop:master Jan 9, 2025
27 checks passed
@pirj
Copy link
Member

pirj commented Jan 9, 2025

Thank you! 🙌🌟

@cbliard cbliard deleted the sort_metadata_symbols_at_the_end_of_args_list branch January 10, 2025 08:59
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.

RSpec/SortMetadata autocorrect can break context groups having additional description
4 participants