Skip to content

Detect method chains that call methods that aren't meant to be chained #104204

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Closed
estebank opened this issue Nov 9, 2022 · 1 comment · Fixed by #109116
Closed

Detect method chains that call methods that aren't meant to be chained #104204

estebank opened this issue Nov 9, 2022 · 1 comment · Fixed by #109116
Assignees
Labels
A-diagnostics Area: Messages for errors, warnings, and lints A-suggestion-diagnostics Area: Suggestions generated by the compiler applied by `cargo fix` D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. D-papercut Diagnostics: An error or lint that needs small tweaks. D-terse Diagnostics: An error or lint that doesn't give enough information about the problem at hand. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@estebank
Copy link
Contributor

estebank commented Nov 9, 2022

In long method chains we might inadvertently call a method that is not meant to be chained. When this happens, the most likely error to occur is a type error involving (). We should instead look if the rhs expression is a method chain and look at all the chained call's type to find where () is introduced.

Given

let x: Vec<i32> = vec![1, 2, 3].into_iter().collect::<Vec<i32>>().sort_by_key(|i| i);
let x: Vec<i32> = vec![1, 2, 3].into_iter().collect::<Vec<i32>>().sort_by_key(|i| i).sort();

we emit

error[[E0308]](https://doc.rust-lang.org/nightly/error-index.html#E0308): mismatched types
 --> src/main.rs:2:23
  |
2 |     let x: Vec<i32> = vec![1, 2, 3].into_iter().collect::<Vec<i32>>().sort_by_key(|i| i);
  |            --------   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Vec`, found `()`
  |            |
  |            expected due to this
  |
  = note: expected struct `Vec<i32>`
          found unit type `()`

error[[E0599]](https://doc.rust-lang.org/nightly/error-index.html#E0599): no method named `sort` found for unit type `()` in the current scope
 --> src/main.rs:3:90
  |
3 |     let x: Vec<i32> = vec![1, 2, 3].into_iter().collect::<Vec<i32>>().sort_by_key(|i| i).sort();
  |                                                                                          ^^^^ method not found in `()`

Ideally the output would be closer to

error[E0308]: mismatched types
 --> src/main.rs:2:23
  |
2 |     let x: Vec<i32> = vec![1, 2, 3].into_iter().collect::<Vec<i32>>().sort_by_key(|i| i);
  |            --------   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Vec`, found `()`
  |            |
  |            expected due to this
  |
  = note: expected struct `Vec<i32>`
          found unit type `()`
help: `sort_by_key` takes `&mut self` and returns `()`, it isn't meant to be used in method chains
 --> src/main.rs:2:23
  |
2 |     let x: Vec<i32> = vec![1, 2, 3].into_iter().collect::<Vec<i32>>().sort_by_key(|i| i);
  |                                                                       ^^^^^^^^^^^^^^^^^^ takes `&mut self` and returns `()`, it is not meant to be chained 
  |
help: introduce a binding and call `sort_by_key` on it
  |
2 ~     let mut x: Vec<i32> = vec![1, 2, 3].into_iter().collect::<Vec<i32>>();
3 +     x.sort_by_key(|i| i);
  |

error[E0599]: no method named `sort` found for unit type `()` in the current scope
 --> src/main.rs:3:90
  |
3 |     let x: Vec<i32> = vec![1, 2, 3].into_iter().collect::<Vec<i32>>().sort_by_key(|i| i).sort();
  |                                                                                          ^^^^ method not found in `()`
help: an earlier method call is of type `Vec<i32>`, which has method `sort`
 --> src/main.rs:3:90
  |
3 |     let x: Vec<i32> = vec![1, 2, 3].into_iter().collect::<Vec<i32>>().sort_by_key(|i| i).sort();
  |                                                ^^^^^^^^^^^^^^^^^^^^^^ ------------------ takes `&mut self` and returns `()`, it is not meant to be chained
  |                                                |
  |                                                `sort` can be called after this method call
@estebank estebank added A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. A-suggestion-diagnostics Area: Suggestions generated by the compiler applied by `cargo fix` D-papercut Diagnostics: An error or lint that needs small tweaks. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. D-terse Diagnostics: An error or lint that doesn't give enough information about the problem at hand. labels Nov 9, 2022
@MaciejWas
Copy link
Contributor

Hi, I'd like to try working on this :)
@rustbot claim

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
A-diagnostics Area: Messages for errors, warnings, and lints A-suggestion-diagnostics Area: Suggestions generated by the compiler applied by `cargo fix` D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. D-papercut Diagnostics: An error or lint that needs small tweaks. D-terse Diagnostics: An error or lint that doesn't give enough information about the problem at hand. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants