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

Suggest single quotes when char expected, str provided #92507

Merged
merged 1 commit into from
Jan 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 38 additions & 3 deletions compiler/rustc_infer/src/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2041,11 +2041,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
if let ValuePairs::Types(ty::error::ExpectedFound { expected, found }) =
trace.values
{
// If a tuple of length one was expected and the found expression has
// parentheses around it, perhaps the user meant to write `(expr,)` to
// build a tuple (issue #86100)
match (expected.kind(), found.kind()) {
(ty::Tuple(_), ty::Tuple(_)) => {}
// If a tuple of length one was expected and the found expression has
// parentheses around it, perhaps the user meant to write `(expr,)` to
// build a tuple (issue #86100)
(ty::Tuple(_), _) if expected.tuple_fields().count() == 1 => {
if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) {
if let Some(code) =
Expand All @@ -2060,6 +2060,41 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
}
}
}
// If a character was expected and the found expression is a string literal
// containing a single character, perhaps the user meant to write `'c'` to
// specify a character literal (issue #92479)
(ty::Char, ty::Ref(_, r, _)) if r.is_str() => {
if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) {
if let Some(code) =
code.strip_prefix('"').and_then(|s| s.strip_suffix('"'))
{
if code.chars().nth(1).is_none() {
Copy link
Member

Choose a reason for hiding this comment

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

Oh no, I think my suggestion also fires for empty string literals "", sorry 😟! I think it's too late to change in this PR since it's already approved.

Suggested change
if code.chars().nth(1).is_none() {
if code.chars().nth(1).is_none() && !code.is_empty() {

Hmm that code is not that self-explanatory.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point- I'll open another PR to add the 0-character check

err.span_suggestion(
span,
"if you meant to write a `char` literal, use single quotes",
format!("'{}'", code),
Applicability::MachineApplicable,
);
}
}
}
}
// If a string was expected and the found expression is a character literal,
// perhaps the user meant to write `"s"` to specify a string literal.
(ty::Ref(_, r, _), ty::Char) if r.is_str() => {
if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) {
if let Some(code) =
code.strip_prefix('\'').and_then(|s| s.strip_suffix('\''))
{
err.span_suggestion(
span,
"if you meant to write a `str` literal, use double quotes",
format!("\"{}\"", code),
Applicability::MachineApplicable,
);
}
}
}
_ => {}
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/test/ui/inference/char-as-str-multi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// When a MULTI-character string literal is used where a char should be,
// DO NOT suggest changing to single quotes.

fn main() {
let _: char = "foo"; //~ ERROR mismatched types
}
11 changes: 11 additions & 0 deletions src/test/ui/inference/char-as-str-multi.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
error[E0308]: mismatched types
--> $DIR/char-as-str-multi.rs:5:19
|
LL | let _: char = "foo";
| ---- ^^^^^ expected `char`, found `&str`
| |
| expected due to this

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
11 changes: 11 additions & 0 deletions src/test/ui/inference/char-as-str-single.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// When a SINGLE-character string literal is used where a char should be,
// suggest changing to single quotes.

// Testing both single-byte and multi-byte characters, as we should handle both.

// run-rustfix

fn main() {
let _: char = 'a'; //~ ERROR mismatched types
let _: char = '人'; //~ ERROR mismatched types
}
11 changes: 11 additions & 0 deletions src/test/ui/inference/char-as-str-single.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// When a SINGLE-character string literal is used where a char should be,
// suggest changing to single quotes.

// Testing both single-byte and multi-byte characters, as we should handle both.

// run-rustfix

fn main() {
let _: char = "a"; //~ ERROR mismatched types
let _: char = "人"; //~ ERROR mismatched types
}
29 changes: 29 additions & 0 deletions src/test/ui/inference/char-as-str-single.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
error[E0308]: mismatched types
--> $DIR/char-as-str-single.rs:9:19
|
LL | let _: char = "a";
| ---- ^^^ expected `char`, found `&str`
| |
| expected due to this
|
help: if you meant to write a `char` literal, use single quotes
|
LL | let _: char = 'a';
| ~~~

error[E0308]: mismatched types
--> $DIR/char-as-str-single.rs:10:19
|
LL | let _: char = "人";
| ---- ^^^^ expected `char`, found `&str`
| |
| expected due to this
|
help: if you meant to write a `char` literal, use single quotes
|
LL | let _: char = '人';
| ~~~~

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0308`.
8 changes: 8 additions & 0 deletions src/test/ui/inference/str-as-char.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// When a char literal is used where a str should be,
// suggest changing to double quotes.

// run-rustfix

fn main() {
let _: &str = "a"; //~ ERROR mismatched types
}
8 changes: 8 additions & 0 deletions src/test/ui/inference/str-as-char.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// When a char literal is used where a str should be,
// suggest changing to double quotes.

// run-rustfix

fn main() {
let _: &str = 'a'; //~ ERROR mismatched types
}
16 changes: 16 additions & 0 deletions src/test/ui/inference/str-as-char.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
error[E0308]: mismatched types
--> $DIR/str-as-char.rs:7:19
|
LL | let _: &str = 'a';
| ---- ^^^ expected `&str`, found `char`
| |
| expected due to this
|
help: if you meant to write a `str` literal, use double quotes
|
LL | let _: &str = "a";
| ~~~

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
5 changes: 5 additions & 0 deletions src/test/ui/issues/issue-23589.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ error[E0308]: mismatched types
|
LL | let v: Vec(&str) = vec!['1', '2'];
| ^^^ expected `&str`, found `char`
|
help: if you meant to write a `str` literal, use double quotes
|
LL | let v: Vec(&str) = vec!["1", '2'];
| ~~~

error: aborting due to 2 previous errors

Expand Down