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 Sorbet/FetchWhenMust cop #141

Closed
wants to merge 2 commits into from
Closed

Add Sorbet/FetchWhenMust cop #141

wants to merge 2 commits into from

Conversation

sambostock
Copy link
Contributor

Checks for T.must(object[key]) and recommends object.fetch(key) instead.

While usually safe, false positives are possible if object does not respond to fetch, or if Hash#default_proc (or similar) is being used.

@sambostock sambostock requested a review from a team as a code owner February 18, 2023 16:06
@sambostock sambostock force-pushed the fetch-when-must branch 2 times, most recently from 659b1a3 to 98ba1b4 Compare February 18, 2023 19:46
@andyw8
Copy link
Contributor

andyw8 commented Feb 19, 2023

I'd love to have this, but I worry there will be some pushback since fetch is not commonly used on arrays.

@github-actions
Copy link

This PR has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@github-actions
Copy link

This PR has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

This allows examples to include `# rubocop:disable`, while still
catching rogue comments.
Checks for `T.must(object[key])` and recommends `object.fetch(key)`
instead.

While usually safe, false positives are possible if `object` does not
respond to `fetch`, or if `Hash#default_proc` (or similar) is being
used.

To avoid correction clobbering, we only correct nodes that are not
nested in another offending node. Since investigation is repeated until
no corrections are made, this eventually corrects all offenses.
@sambostock
Copy link
Contributor Author

I discovered an issue with nested offense correction where correcting the inner node would clobber the outer node correction.

After investigating, I found rubocop/rubocop#10511 & rubocop/rubocop#10461 which demonstrate how to address this skipping correction for nodes within an already corrected node. Because autocorrection repeats investigation until the file no longer requires correction, all nodes are eventually corrected. I have taken this approach here.

@andyw8
Copy link
Contributor

andyw8 commented May 2, 2023

cc @vinistock for your opinion on this, since you commented about it recently.

Copy link
Contributor

@amomchilov amomchilov left a comment

Choose a reason for hiding this comment

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

This is a common thing I suggest on PRs. Love the idea!

#
# # good
# # If `object` does not `respond_to? :fetch`, or if using `Hash` `default_proc`
# T.must(object[key]) # rubocop:disable Sorbet/FetchWhenMust
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not completely convinced by this cop exactly because of this case.

From a user experience point of view:

  1. I write some code that is seemingly correct
  2. The cop complains that I should use fetch
  3. I try to use fetch but now the type checker complains
  4. What should I do? Adding a rubocop:disable always feels wrong...
  5. Confusion state

I agree that is this something we generally recommend in code reviews but the inability to only target hashes / arrays from the cop is a huge limitation in this case.

Do we have an idea of how many rubocop:disable we would need to add in core for example?

I'm also worried about the massive change needed to enable this cop. What if the Sorbet nor tests do not catch that one of the fetch call is actually invalid?

Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a way to leverage Sorbet's type information to narrow this cop to only Arrays/Hashes and other similar container types?

@andyw8
Copy link
Contributor

andyw8 commented Jun 14, 2023

Minor data point – a Stripe engineer suggests Array#fetch here:

https://sorbet-ruby.slack.com/archives/CHN2L03NH/p1686444818750959?thread_ts=1686410073.163849&cid=CHN2L03NH

Sorbet does not track the information you want here. if you want a non-nilable type, you should use Array#fetch.

@KaanOzkan
Copy link
Contributor

I think I'm going to close this PR due to the experience @Morriar brought up. However, I must say I like this feature and think we can get close to it inside Sorbet autocorrect.

@sambostock what do you think of working on an implementation inside Sorbet that checks to see if the type's methods contain fetch and autocorrects to fetch rather than T.must? It's not as "strong" as a rubocop rule but it should still help during development especially with the LSP.

@KaanOzkan KaanOzkan closed this Jul 6, 2023
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

5 participants