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

rustc recommends removing desired str method instead of adding .to_owned() #114329

Closed
GinoMan opened this issue Aug 1, 2023 · 8 comments · Fixed by #120473
Closed

rustc recommends removing desired str method instead of adding .to_owned() #114329

GinoMan opened this issue Aug 1, 2023 · 8 comments · Fixed by #120473
Labels
A-diagnostics Area: Messages for errors, warnings, and lints C-bug Category: This is a bug. D-invalid-suggestion Diagnostics: A structured suggestion resulting in incorrect code.

Comments

@GinoMan
Copy link

GinoMan commented Aug 1, 2023

Summary

When you have a function that returns a String but add .trim() to the return value to remove extraneous whitespace, you have a type error because .trim() returns a &str. But the suggestion is not to add .to_owned() or to_string() to fix the problem but to remove the trimming behavior entirely even if that is exactly what the programmer intended.

When using cargo run, it additionally fails to show the line containing .trim() in the suggested change.

Lint Name

error[E0308]: mismatched types

Reproducer

I tried this code:

use std::io::stdin;

fn get_name() -> String {
	let mut your_name = String::new();
	stdin()
		.read_line(&mut your_name)
		.expect("Failed to read the line for some reason");
	your_name
		.trim()
}

fn main() {
	println!("Hello, What is your name? ");
	let your_name = get_name();
	println!("Hello, {}", your_name)
}

I saw this happen:

    Checking treehouse v0.1.0 (C:\Users\OpenM\Code\Projects\Rust\treehouse)
error[E0308]: mismatched types
 --> src\main.rs:8:2
  |
3 |   fn get_name() -> String {
  |                    ------ expected `std::string::String` because of return type
...
8 | /     your_name
9 | |         .trim()
  | |_______________^ expected `String`, found `&str`
  |
help: try removing the method call
  |
8 -     your_name
9 -         .trim()
8 +     your_name
  |

For more information about this error, try `rustc --explain E0308`.
error: could not compile `treehouse` (bin "treehouse") due to previous error

I expected to see this happen:

    Checking treehouse v0.1.0 (C:\Users\OpenM\Code\Projects\Rust\treehouse)
error[E0308]: mismatched types
 --> src\main.rs:8:2
  |
3 |   fn get_name() -> String {
  |                    ------ expected `std::string::String` because of return type
...
8 | /     your_name
9 | |         .trim()
  | |_______________^ expected `String`, found `&str`
  |
help: add .to_owned() to return a String instead of a &str
  |
8 -     your_name
9 -         .trim()
8 +     your_name
9 +         .trim().to_owned()
  |

For more information about this error, try `rustc --explain E0308`.
error: could not compile `treehouse` (bin "treehouse") due to previous error

Version

rustc 1.68.0 (2c8cc3432 2023-03-06)
binary: rustc
commit-hash: 2c8cc343237b8f7d5a3c3703e3a87f2eb2c54a74
commit-date: 2023-03-06
host: x86_64-pc-windows-msvc
release: 1.68.0
LLVM version: 15.0.6

Additional Labels

No response

@GinoMan GinoMan added the C-bug Category: This is a bug. label Aug 1, 2023
@Centri3
Copy link
Member

Centri3 commented Aug 1, 2023

Not really a clippy issue, as it's a wrapper around cargo check with additional lints (and thus any bad diagnostics there will be an issue here as well). This should be changed in rustc

@Alexendoo Alexendoo transferred this issue from rust-lang/rust-clippy Aug 1, 2023
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Aug 1, 2023
@Alexendoo Alexendoo changed the title rustc and cargo clippy both recommend removing desired str method instead of adding .to_owned() rustc recommends removing desired str method instead of adding .to_owned() Aug 1, 2023
@compiler-errors
Copy link
Member

This suggestion should only be applied for methods that have no "side-effects" -- things like ast_str, as_ptr, etc.

@saethlin saethlin added A-diagnostics Area: Messages for errors, warnings, and lints D-invalid-suggestion Diagnostics: A structured suggestion resulting in incorrect code. and removed needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels Aug 2, 2023
@gurry
Copy link
Contributor

gurry commented Aug 3, 2023

This suggestion should only be applied for methods that have no "side-effects" -- things like ast_str, as_ptr, etc.

How do you determine whether the method has "side-effects" or not?

Sorry if this is a stupid question. I'm an absolute newbie to rustc development, but would be keen to start on this issue.

@compiler-errors
Copy link
Member

No, that's a fine question. The way I would do it is probably just hard-code a set of known methods. Like... [sym::as_str, sym::as_ptr, sym::to_string] etc.

@gurry
Copy link
Contributor

gurry commented Aug 4, 2023

Thanks :) That's what I was thinking as well.

Is it okay if I claim this issue?

I presume the error originates in type checking and hence the change would go into some HIR related code. Is that correct?

@gurry
Copy link
Contributor

gurry commented Aug 5, 2023

As per my analysis the fix can go into the following if condition in the suggest_remove_last_method_call() method:

if let hir::ExprKind::MethodCall(hir::PathSegment { ident: method, .. }, recv_expr, &[], _) = expr.kind &&
let Some(recv_ty) = self.typeck_results.borrow().expr_ty_opt(recv_expr) &&
self.can_coerce(recv_ty, expected) {

We can add an extra check here for methods with side effects as shown below:

 if let hir::ExprKind::MethodCall(hir::PathSegment { ident: method, .. }, recv_expr, &[], _) = expr.kind &&
            let Some(recv_ty) = self.typeck_results.borrow().expr_ty_opt(recv_expr) &&
            self.can_coerce(recv_ty, expected) &&
            self.has_no_side_effects(expr) {
           }

where the has_no_side_effects() method could be as follows:

fn has_no_side_effects(expr: &hir::Expr<'tcx>) -> bool {
    if (/* expr value is &str && 
        expected type is String &&
        the last called method is in [sym::as_str, sym::as_ptr, sym::to_string...] */) {
         true
    } else {
        false
    }
}

As a result of this change, suggest_remove_last_method_call() will return false in the situation reported in the bug and the control will eventually get to the suggest_deref_ref_or_into() method which should automatically suggest a conversion using to_string()

There's a method hir::Expr::can_have_side_effects()() which seems to be something we could use instead of has_no_side_effects(), but it considers all method call exprs to have side effects. Therefore it may not be a good fit.

With regard to tests, we could enhance this test to verify our case. We could also enhance this test. Or we could write completely new tests.

What are your thoughts @compiler-errors?

@gurry
Copy link
Contributor

gurry commented Aug 9, 2023

While filtering on a set of hard-coded method symbols may work for String, it's not a scalable solution. What we need is a general way to tell whether any given method on any given type could have side effects or not. Since achieving something like that would be non-trivial, I don't think we have a good solution here.

Perhaps a conservative, but likely unacceptable, option is to never suggest removal of methods. Then at least the suggestions in this context will always be correct although not necessarily most natural.

@GinoMan
Copy link
Author

GinoMan commented Aug 29, 2023

While filtering on a set of hard-coded method symbols may work for String, it's not a scalable solution. What we need is a general way to tell whether any given method on any given type could have side effects or not. Since achieving something like that would be non-trivial, I don't think we have a good solution here.

Perhaps a conservative, but likely unacceptable, option is to never suggest removal of methods. Then at least the suggestions in this context will always be correct although not necessarily most natural.

I personally think that would be the best solution. I don't really care that it's suggesting I fix the code, I care that it suggests I fix it by removing a method at all that the programmer likely intended to call. Though I get that a lot of people learn Rust by letting the compiler teach them. I'm in the same boat as a learner myself. Still, seems kinda silly for the compiler to be like "Oh, you want trimming? No! The type doesn't match. Remove the trim and just deal with the spaces." when "to_owned()" handily fixes the problem. Maybe the compiler can tell you how to convert when the solution is as simple as "to_owned()"?

matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Feb 1, 2024
Only suggest removal of `as_*` and `to_` conversion methods on E0308

Instead of

```
error[E0308]: mismatched types
 --> tests/ui/suggestions/only-suggest-removal-of-conversion-method-calls.rs:9:5
  |
4 | fn get_name() -> String {
  |                  ------ expected `String` because of return type
...
9 |     your_name.trim()
  |     ^^^^^^^^^^^^^^^^ expected `String`, found `&str`
  |
help: try removing the method call
  |
9 -     your_name.trim()
9 +     your_name
```

output

```
error[E0308]: mismatched types
  --> $DIR/only-suggest-removal-of-conversion-method-calls.rs:9:5
   |
LL | fn get_name() -> String {
   |                  ------ expected `String` because of return type
...
LL |     your_name.trim()
   |     ^^^^^^^^^^^^^^^^- help: try using a conversion method: `.to_string()`
   |     |
   |     expected `String`, found `&str`
```

Fix rust-lang#114329.
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Feb 1, 2024
Only suggest removal of `as_*` and `to_` conversion methods on E0308

Instead of

```
error[E0308]: mismatched types
 --> tests/ui/suggestions/only-suggest-removal-of-conversion-method-calls.rs:9:5
  |
4 | fn get_name() -> String {
  |                  ------ expected `String` because of return type
...
9 |     your_name.trim()
  |     ^^^^^^^^^^^^^^^^ expected `String`, found `&str`
  |
help: try removing the method call
  |
9 -     your_name.trim()
9 +     your_name
```

output

```
error[E0308]: mismatched types
  --> $DIR/only-suggest-removal-of-conversion-method-calls.rs:9:5
   |
LL | fn get_name() -> String {
   |                  ------ expected `String` because of return type
...
LL |     your_name.trim()
   |     ^^^^^^^^^^^^^^^^- help: try using a conversion method: `.to_string()`
   |     |
   |     expected `String`, found `&str`
```

Fix rust-lang#114329.
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Feb 4, 2024
Only suggest removal of `as_*` and `to_` conversion methods on E0308

Instead of

```
error[E0308]: mismatched types
 --> tests/ui/suggestions/only-suggest-removal-of-conversion-method-calls.rs:9:5
  |
4 | fn get_name() -> String {
  |                  ------ expected `String` because of return type
...
9 |     your_name.trim()
  |     ^^^^^^^^^^^^^^^^ expected `String`, found `&str`
  |
help: try removing the method call
  |
9 -     your_name.trim()
9 +     your_name
```

output

```
error[E0308]: mismatched types
  --> $DIR/only-suggest-removal-of-conversion-method-calls.rs:9:5
   |
LL | fn get_name() -> String {
   |                  ------ expected `String` because of return type
...
LL |     your_name.trim()
   |     ^^^^^^^^^^^^^^^^- help: try using a conversion method: `.to_string()`
   |     |
   |     expected `String`, found `&str`
```

Fix rust-lang#114329.
@bors bors closed this as completed in 44d8ecb Feb 5, 2024
rust-timer added a commit to rust-lang-ci/rust that referenced this issue Feb 5, 2024
Rollup merge of rust-lang#120473 - estebank:issue-114329, r=TaKO8Ki

Only suggest removal of `as_*` and `to_` conversion methods on E0308

Instead of

```
error[E0308]: mismatched types
 --> tests/ui/suggestions/only-suggest-removal-of-conversion-method-calls.rs:9:5
  |
4 | fn get_name() -> String {
  |                  ------ expected `String` because of return type
...
9 |     your_name.trim()
  |     ^^^^^^^^^^^^^^^^ expected `String`, found `&str`
  |
help: try removing the method call
  |
9 -     your_name.trim()
9 +     your_name
```

output

```
error[E0308]: mismatched types
  --> $DIR/only-suggest-removal-of-conversion-method-calls.rs:9:5
   |
LL | fn get_name() -> String {
   |                  ------ expected `String` because of return type
...
LL |     your_name.trim()
   |     ^^^^^^^^^^^^^^^^- help: try using a conversion method: `.to_string()`
   |     |
   |     expected `String`, found `&str`
```

Fix rust-lang#114329.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-diagnostics Area: Messages for errors, warnings, and lints C-bug Category: This is a bug. D-invalid-suggestion Diagnostics: A structured suggestion resulting in incorrect code.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants