Skip to content

Commit d3ceb6c

Browse files
committed
Suggest unwrap() on field not found for Result/Option
When encountering a `Result<T, _>` or `Option<T>` where `T` has a field that's being accessed, suggest calling `.unwrap()` to get to the field.
1 parent 933341a commit d3ceb6c

17 files changed

+166
-23
lines changed

compiler/rustc_hir_typeck/src/expr.rs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2632,8 +2632,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26322632

26332633
// try to add a suggestion in case the field is a nested field of a field of the Adt
26342634
let mod_id = self.tcx.parent_module(id).to_def_id();
2635+
let (ty, unwrap) = if let ty::Adt(def, args) = expr_t.kind()
2636+
&& (self.tcx.is_diagnostic_item(sym::Result, def.did())
2637+
|| self.tcx.is_diagnostic_item(sym::Option, def.did())
2638+
)
2639+
&& let Some(arg) = args.get(0)
2640+
&& let Some(ty) = arg.as_type()
2641+
{
2642+
(ty, "unwrap().")
2643+
} else {
2644+
(expr_t, "")
2645+
};
26352646
for (found_fields, args) in
2636-
self.get_field_candidates_considering_privacy(span, expr_t, mod_id, id)
2647+
self.get_field_candidates_considering_privacy(span, ty, mod_id, id)
26372648
{
26382649
let field_names = found_fields.iter().map(|field| field.name).collect::<Vec<_>>();
26392650
let candidate_fields: Vec<_> = found_fields
@@ -2653,9 +2664,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26532664
field_path.pop();
26542665
field_path
26552666
.iter()
2656-
.map(|id| id.name.to_ident_string())
2657-
.collect::<Vec<String>>()
2658-
.join(".")
2667+
.map(|id| format!("{}.", id.name.to_ident_string()))
2668+
.collect::<String>()
26592669
})
26602670
.collect::<Vec<_>>();
26612671

@@ -2668,15 +2678,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26682678
if len > 1 { "some" } else { "one" },
26692679
if len > 1 { "have" } else { "has" },
26702680
),
2671-
candidate_fields.iter().map(|path| format!("{path}.")),
2681+
candidate_fields.iter().map(|path| format!("{unwrap}{path}")),
26722682
Applicability::MaybeIncorrect,
26732683
);
26742684
} else {
26752685
if let Some(field_name) = find_best_match_for_name(&field_names, field.name, None) {
2676-
err.span_suggestion(
2686+
err.span_suggestion_verbose(
26772687
field.span,
26782688
"a field with a similar name exists",
2679-
field_name,
2689+
format!("{unwrap}{}", field_name),
26802690
Applicability::MaybeIncorrect,
26812691
);
26822692
} else if !field_names.is_empty() {

tests/ui/async-await/suggest-switching-edition-on-await-cargo.stderr

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,15 @@ error[E0609]: no field `await` on type `await_on_struct_similar::S`
1212
--> $DIR/suggest-switching-edition-on-await-cargo.rs:23:7
1313
|
1414
LL | x.await;
15-
| ^^^^^ help: a field with a similar name exists: `awai`
15+
| ^^^^^
1616
|
1717
= note: to `.await` a `Future`, switch to Rust 2018 or later
1818
= help: set `edition = "2021"` in `Cargo.toml`
1919
= note: for more on editions, read https://doc.rust-lang.org/edition-guide
20+
help: a field with a similar name exists
21+
|
22+
LL | x.awai;
23+
| ~~~~
2024

2125
error[E0609]: no field `await` on type `Pin<&mut dyn Future<Output = ()>>`
2226
--> $DIR/suggest-switching-edition-on-await-cargo.rs:32:7

tests/ui/async-await/suggest-switching-edition-on-await.stderr

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,15 @@ error[E0609]: no field `await` on type `await_on_struct_similar::S`
1212
--> $DIR/suggest-switching-edition-on-await.rs:21:7
1313
|
1414
LL | x.await;
15-
| ^^^^^ help: a field with a similar name exists: `awai`
15+
| ^^^^^
1616
|
1717
= note: to `.await` a `Future`, switch to Rust 2018 or later
1818
= help: pass `--edition 2021` to `rustc`
1919
= note: for more on editions, read https://doc.rust-lang.org/edition-guide
20+
help: a field with a similar name exists
21+
|
22+
LL | x.awai;
23+
| ~~~~
2024

2125
error[E0609]: no field `await` on type `Pin<&mut dyn Future<Output = ()>>`
2226
--> $DIR/suggest-switching-edition-on-await.rs:30:7

tests/ui/derived-errors/issue-30580.stderr

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ error[E0609]: no field `c` on type `&Foo`
22
--> $DIR/issue-30580.rs:12:11
33
|
44
LL | b.c;
5-
| ^ help: a field with a similar name exists: `a`
5+
| ^
6+
|
7+
help: a field with a similar name exists
8+
|
9+
LL | b.a;
10+
| ~
611

712
error: aborting due to previous error
813

tests/ui/did_you_mean/issue-36798.stderr

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ error[E0609]: no field `baz` on type `Foo`
22
--> $DIR/issue-36798.rs:7:7
33
|
44
LL | f.baz;
5-
| ^^^ help: a field with a similar name exists: `bar`
5+
| ^^^
6+
|
7+
help: a field with a similar name exists
8+
|
9+
LL | f.bar;
10+
| ~~~
611

712
error: aborting due to previous error
813

tests/ui/did_you_mean/issue-42599_available_fields_note.stderr

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ error[E0609]: no field `inocently_mispellable` on type `Demo`
1616
--> $DIR/issue-42599_available_fields_note.rs:32:41
1717
|
1818
LL | let innocent_field_misaccess = demo.inocently_mispellable;
19-
| ^^^^^^^^^^^^^^^^^^^^^ help: a field with a similar name exists: `innocently_misspellable`
19+
| ^^^^^^^^^^^^^^^^^^^^^
20+
|
21+
help: a field with a similar name exists
22+
|
23+
LL | let innocent_field_misaccess = demo.innocently_misspellable;
24+
| ~~~~~~~~~~~~~~~~~~~~~~~
2025

2126
error[E0609]: no field `egregiously_nonexistent_field` on type `Demo`
2227
--> $DIR/issue-42599_available_fields_note.rs:35:42

tests/ui/error-codes/ex-E0612.stderr

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ error[E0609]: no field `1` on type `Foo`
22
--> $DIR/ex-E0612.rs:5:6
33
|
44
LL | y.1;
5-
| ^ help: a field with a similar name exists: `0`
5+
| ^
6+
|
7+
help: a field with a similar name exists
8+
|
9+
LL | y.0;
10+
| ~
611

712
error: aborting due to previous error
813

tests/ui/issues/issue-47073-zero-padded-tuple-struct-indices.stderr

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ error[E0609]: no field `00` on type `Verdict`
22
--> $DIR/issue-47073-zero-padded-tuple-struct-indices.rs:8:30
33
|
44
LL | let _condemned = justice.00;
5-
| ^^ help: a field with a similar name exists: `0`
5+
| ^^
6+
|
7+
help: a field with a similar name exists
8+
|
9+
LL | let _condemned = justice.0;
10+
| ~
611

712
error[E0609]: no field `001` on type `Verdict`
813
--> $DIR/issue-47073-zero-padded-tuple-struct-indices.rs:10:31

tests/ui/structs/struct-fields-typo.stderr

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ error[E0609]: no field `baa` on type `BuildData`
22
--> $DIR/struct-fields-typo.rs:11:17
33
|
44
LL | let x = foo.baa;
5-
| ^^^ help: a field with a similar name exists: `bar`
5+
| ^^^
6+
|
7+
help: a field with a similar name exists
8+
|
9+
LL | let x = foo.bar;
10+
| ~~~
611

712
error: aborting due to previous error
813

tests/ui/structs/struct-pat-derived-error.stderr

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ error[E0609]: no field `d` on type `&A`
22
--> $DIR/struct-pat-derived-error.rs:8:31
33
|
44
LL | let A { x, y } = self.d;
5-
| ^ help: a field with a similar name exists: `b`
5+
| ^
6+
|
7+
help: a field with a similar name exists
8+
|
9+
LL | let A { x, y } = self.b;
10+
| ~
611

712
error[E0026]: struct `A` does not have fields named `x`, `y`
813
--> $DIR/struct-pat-derived-error.rs:8:17

0 commit comments

Comments
 (0)