Skip to content

Commit 8bd8f3b

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 dfa7539 commit 8bd8f3b

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
@@ -2686,8 +2686,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26862686

26872687
// try to add a suggestion in case the field is a nested field of a field of the Adt
26882688
let mod_id = self.tcx.parent_module(id).to_def_id();
2689+
let (ty, unwrap) = if let ty::Adt(def, args) = expr_t.kind()
2690+
&& (self.tcx.is_diagnostic_item(sym::Result, def.did())
2691+
|| self.tcx.is_diagnostic_item(sym::Option, def.did())
2692+
)
2693+
&& let Some(arg) = args.get(0)
2694+
&& let Some(ty) = arg.as_type()
2695+
{
2696+
(ty, "unwrap().")
2697+
} else {
2698+
(expr_t, "")
2699+
};
26892700
for (found_fields, args) in
2690-
self.get_field_candidates_considering_privacy(span, expr_t, mod_id, id)
2701+
self.get_field_candidates_considering_privacy(span, ty, mod_id, id)
26912702
{
26922703
let field_names = found_fields.iter().map(|field| field.name).collect::<Vec<_>>();
26932704
let candidate_fields: Vec<_> = found_fields
@@ -2707,9 +2718,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
27072718
field_path.pop();
27082719
field_path
27092720
.iter()
2710-
.map(|id| id.name.to_ident_string())
2711-
.collect::<Vec<String>>()
2712-
.join(".")
2721+
.map(|id| format!("{}.", id.name.to_ident_string()))
2722+
.collect::<String>()
27132723
})
27142724
.collect::<Vec<_>>();
27152725

@@ -2722,15 +2732,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
27222732
if len > 1 { "some" } else { "one" },
27232733
if len > 1 { "have" } else { "has" },
27242734
),
2725-
candidate_fields.iter().map(|path| format!("{path}.")),
2735+
candidate_fields.iter().map(|path| format!("{unwrap}{path}")),
27262736
Applicability::MaybeIncorrect,
27272737
);
27282738
} else {
27292739
if let Some(field_name) = find_best_match_for_name(&field_names, field.name, None) {
2730-
err.span_suggestion(
2740+
err.span_suggestion_verbose(
27312741
field.span,
27322742
"a field with a similar name exists",
2733-
field_name,
2743+
format!("{unwrap}{}", field_name),
27342744
Applicability::MaybeIncorrect,
27352745
);
27362746
} 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)