Skip to content

Commit a5ebba0

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 5e510de commit a5ebba0

17 files changed

+166
-23
lines changed

compiler/rustc_hir_typeck/src/expr.rs

+17-7
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

+5-1
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

+5-1
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

+6-1
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

+6-1
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

+6-1
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

+6-1
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

+6-1
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

+6-1
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

+6-1
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

tests/ui/suggestions/suggest-field-through-deref.fixed

+8
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,12 @@ fn main() {
1010
let _ = x.long_name; //~ ERROR no field `longname`
1111
let y = S { long_name: (), foo: () };
1212
let _ = y.long_name; //~ ERROR no field `longname`
13+
let a = Some(Arc::new(S { long_name: (), foo: () }));
14+
let _ = a.unwrap().long_name; //~ ERROR no field `longname`
15+
let b = Some(S { long_name: (), foo: () });
16+
let _ = b.unwrap().long_name; //~ ERROR no field `long_name`
17+
let c = Ok::<_, ()>(Arc::new(S { long_name: (), foo: () }));
18+
let _ = c.unwrap().long_name; //~ ERROR no field `longname`
19+
let d = Ok::<_, ()>(S { long_name: (), foo: () });
20+
let _ = d.unwrap().long_name; //~ ERROR no field `long_name`
1321
}

tests/ui/suggestions/suggest-field-through-deref.rs

+8
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,12 @@ fn main() {
1010
let _ = x.longname; //~ ERROR no field `longname`
1111
let y = S { long_name: (), foo: () };
1212
let _ = y.longname; //~ ERROR no field `longname`
13+
let a = Some(Arc::new(S { long_name: (), foo: () }));
14+
let _ = a.longname; //~ ERROR no field `longname`
15+
let b = Some(S { long_name: (), foo: () });
16+
let _ = b.long_name; //~ ERROR no field `long_name`
17+
let c = Ok::<_, ()>(Arc::new(S { long_name: (), foo: () }));
18+
let _ = c.longname; //~ ERROR no field `longname`
19+
let d = Ok::<_, ()>(S { long_name: (), foo: () });
20+
let _ = d.long_name; //~ ERROR no field `long_name`
1321
}

tests/ui/suggestions/suggest-field-through-deref.stderr

+57-3
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,68 @@ error[E0609]: no field `longname` on type `Arc<S>`
22
--> $DIR/suggest-field-through-deref.rs:10:15
33
|
44
LL | let _ = x.longname;
5-
| ^^^^^^^^ help: a field with a similar name exists: `long_name`
5+
| ^^^^^^^^
6+
|
7+
help: a field with a similar name exists
8+
|
9+
LL | let _ = x.long_name;
10+
| ~~~~~~~~~
611

712
error[E0609]: no field `longname` on type `S`
813
--> $DIR/suggest-field-through-deref.rs:12:15
914
|
1015
LL | let _ = y.longname;
11-
| ^^^^^^^^ help: a field with a similar name exists: `long_name`
16+
| ^^^^^^^^
17+
|
18+
help: a field with a similar name exists
19+
|
20+
LL | let _ = y.long_name;
21+
| ~~~~~~~~~
22+
23+
error[E0609]: no field `longname` on type `Option<Arc<S>>`
24+
--> $DIR/suggest-field-through-deref.rs:14:15
25+
|
26+
LL | let _ = a.longname;
27+
| ^^^^^^^^
28+
|
29+
help: a field with a similar name exists
30+
|
31+
LL | let _ = a.unwrap().long_name;
32+
| ~~~~~~~~~~~~~~~~~~
33+
34+
error[E0609]: no field `long_name` on type `Option<S>`
35+
--> $DIR/suggest-field-through-deref.rs:16:15
36+
|
37+
LL | let _ = b.long_name;
38+
| ^^^^^^^^^
39+
|
40+
help: one of the expressions' fields has a field of the same name
41+
|
42+
LL | let _ = b.unwrap().long_name;
43+
| +++++++++
44+
45+
error[E0609]: no field `longname` on type `Result<Arc<S>, ()>`
46+
--> $DIR/suggest-field-through-deref.rs:18:15
47+
|
48+
LL | let _ = c.longname;
49+
| ^^^^^^^^
50+
|
51+
help: a field with a similar name exists
52+
|
53+
LL | let _ = c.unwrap().long_name;
54+
| ~~~~~~~~~~~~~~~~~~
55+
56+
error[E0609]: no field `long_name` on type `Result<S, ()>`
57+
--> $DIR/suggest-field-through-deref.rs:20:15
58+
|
59+
LL | let _ = d.long_name;
60+
| ^^^^^^^^^
61+
|
62+
help: one of the expressions' fields has a field of the same name
63+
|
64+
LL | let _ = d.unwrap().long_name;
65+
| +++++++++
1266

13-
error: aborting due to 2 previous errors
67+
error: aborting due to 6 previous errors
1468

1569
For more information about this error, try `rustc --explain E0609`.

tests/ui/tuple/tuple-index-not-tuple.stderr

+6-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ error[E0609]: no field `0` on type `Point`
22
--> $DIR/tuple-index-not-tuple.rs:6:12
33
|
44
LL | origin.0;
5-
| ^ help: a field with a similar name exists: `x`
5+
| ^
6+
|
7+
help: a field with a similar name exists
8+
|
9+
LL | origin.x;
10+
| ~
611

712
error[E0609]: no field `0` on type `Empty`
813
--> $DIR/tuple-index-not-tuple.rs:8:11

tests/ui/tuple/tuple-index-out-of-bounds.stderr

+6-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ error[E0609]: no field `2` on type `Point`
22
--> $DIR/tuple-index-out-of-bounds.rs:7:12
33
|
44
LL | origin.2;
5-
| ^ help: a field with a similar name exists: `0`
5+
| ^
6+
|
7+
help: a field with a similar name exists
8+
|
9+
LL | origin.0;
10+
| ~
611

712
error[E0609]: no field `2` on type `({integer}, {integer})`
813
--> $DIR/tuple-index-out-of-bounds.rs:12:11

tests/ui/union/union-suggest-field.mirunsafeck.stderr

+6-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@ error[E0609]: no field `principial` on type `U`
88
--> $DIR/union-suggest-field.rs:17:15
99
|
1010
LL | let w = u.principial;
11-
| ^^^^^^^^^^ help: a field with a similar name exists: `principal`
11+
| ^^^^^^^^^^
12+
|
13+
help: a field with a similar name exists
14+
|
15+
LL | let w = u.principal;
16+
| ~~~~~~~~~
1217

1318
error[E0615]: attempted to take value of method `calculate` on type `U`
1419
--> $DIR/union-suggest-field.rs:21:15

tests/ui/union/union-suggest-field.thirunsafeck.stderr

+6-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@ error[E0609]: no field `principial` on type `U`
88
--> $DIR/union-suggest-field.rs:17:15
99
|
1010
LL | let w = u.principial;
11-
| ^^^^^^^^^^ help: a field with a similar name exists: `principal`
11+
| ^^^^^^^^^^
12+
|
13+
help: a field with a similar name exists
14+
|
15+
LL | let w = u.principal;
16+
| ~~~~~~~~~
1217

1318
error[E0615]: attempted to take value of method `calculate` on type `U`
1419
--> $DIR/union-suggest-field.rs:21:15

0 commit comments

Comments
 (0)