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.
This commit is contained in:
Esteban Küber 2023-10-24 23:31:47 +00:00
parent dfa75391f8
commit 8bd8f3b090
17 changed files with 166 additions and 23 deletions

View File

@ -2686,8 +2686,19 @@ fn no_such_field_err(
// try to add a suggestion in case the field is a nested field of a field of the Adt
let mod_id = self.tcx.parent_module(id).to_def_id();
let (ty, unwrap) = if let ty::Adt(def, args) = expr_t.kind()
&& (self.tcx.is_diagnostic_item(sym::Result, def.did())
|| self.tcx.is_diagnostic_item(sym::Option, def.did())
)
&& let Some(arg) = args.get(0)
&& let Some(ty) = arg.as_type()
{
(ty, "unwrap().")
} else {
(expr_t, "")
};
for (found_fields, args) in
self.get_field_candidates_considering_privacy(span, expr_t, mod_id, id)
self.get_field_candidates_considering_privacy(span, ty, mod_id, id)
{
let field_names = found_fields.iter().map(|field| field.name).collect::<Vec<_>>();
let candidate_fields: Vec<_> = found_fields
@ -2707,9 +2718,8 @@ fn no_such_field_err(
field_path.pop();
field_path
.iter()
.map(|id| id.name.to_ident_string())
.collect::<Vec<String>>()
.join(".")
.map(|id| format!("{}.", id.name.to_ident_string()))
.collect::<String>()
})
.collect::<Vec<_>>();
@ -2722,15 +2732,15 @@ fn no_such_field_err(
if len > 1 { "some" } else { "one" },
if len > 1 { "have" } else { "has" },
),
candidate_fields.iter().map(|path| format!("{path}.")),
candidate_fields.iter().map(|path| format!("{unwrap}{path}")),
Applicability::MaybeIncorrect,
);
} else {
if let Some(field_name) = find_best_match_for_name(&field_names, field.name, None) {
err.span_suggestion(
err.span_suggestion_verbose(
field.span,
"a field with a similar name exists",
field_name,
format!("{unwrap}{}", field_name),
Applicability::MaybeIncorrect,
);
} else if !field_names.is_empty() {

View File

@ -12,11 +12,15 @@ error[E0609]: no field `await` on type `await_on_struct_similar::S`
--> $DIR/suggest-switching-edition-on-await-cargo.rs:23:7
|
LL | x.await;
| ^^^^^ help: a field with a similar name exists: `awai`
| ^^^^^
|
= note: to `.await` a `Future`, switch to Rust 2018 or later
= help: set `edition = "2021"` in `Cargo.toml`
= note: for more on editions, read https://doc.rust-lang.org/edition-guide
help: a field with a similar name exists
|
LL | x.awai;
| ~~~~
error[E0609]: no field `await` on type `Pin<&mut dyn Future<Output = ()>>`
--> $DIR/suggest-switching-edition-on-await-cargo.rs:32:7

View File

@ -12,11 +12,15 @@ error[E0609]: no field `await` on type `await_on_struct_similar::S`
--> $DIR/suggest-switching-edition-on-await.rs:21:7
|
LL | x.await;
| ^^^^^ help: a field with a similar name exists: `awai`
| ^^^^^
|
= note: to `.await` a `Future`, switch to Rust 2018 or later
= help: pass `--edition 2021` to `rustc`
= note: for more on editions, read https://doc.rust-lang.org/edition-guide
help: a field with a similar name exists
|
LL | x.awai;
| ~~~~
error[E0609]: no field `await` on type `Pin<&mut dyn Future<Output = ()>>`
--> $DIR/suggest-switching-edition-on-await.rs:30:7

View File

@ -2,7 +2,12 @@ error[E0609]: no field `c` on type `&Foo`
--> $DIR/issue-30580.rs:12:11
|
LL | b.c;
| ^ help: a field with a similar name exists: `a`
| ^
|
help: a field with a similar name exists
|
LL | b.a;
| ~
error: aborting due to previous error

View File

@ -2,7 +2,12 @@ error[E0609]: no field `baz` on type `Foo`
--> $DIR/issue-36798.rs:7:7
|
LL | f.baz;
| ^^^ help: a field with a similar name exists: `bar`
| ^^^
|
help: a field with a similar name exists
|
LL | f.bar;
| ~~~
error: aborting due to previous error

View File

@ -16,7 +16,12 @@ error[E0609]: no field `inocently_mispellable` on type `Demo`
--> $DIR/issue-42599_available_fields_note.rs:32:41
|
LL | let innocent_field_misaccess = demo.inocently_mispellable;
| ^^^^^^^^^^^^^^^^^^^^^ help: a field with a similar name exists: `innocently_misspellable`
| ^^^^^^^^^^^^^^^^^^^^^
|
help: a field with a similar name exists
|
LL | let innocent_field_misaccess = demo.innocently_misspellable;
| ~~~~~~~~~~~~~~~~~~~~~~~
error[E0609]: no field `egregiously_nonexistent_field` on type `Demo`
--> $DIR/issue-42599_available_fields_note.rs:35:42

View File

@ -2,7 +2,12 @@ error[E0609]: no field `1` on type `Foo`
--> $DIR/ex-E0612.rs:5:6
|
LL | y.1;
| ^ help: a field with a similar name exists: `0`
| ^
|
help: a field with a similar name exists
|
LL | y.0;
| ~
error: aborting due to previous error

View File

@ -2,7 +2,12 @@ error[E0609]: no field `00` on type `Verdict`
--> $DIR/issue-47073-zero-padded-tuple-struct-indices.rs:8:30
|
LL | let _condemned = justice.00;
| ^^ help: a field with a similar name exists: `0`
| ^^
|
help: a field with a similar name exists
|
LL | let _condemned = justice.0;
| ~
error[E0609]: no field `001` on type `Verdict`
--> $DIR/issue-47073-zero-padded-tuple-struct-indices.rs:10:31

View File

@ -2,7 +2,12 @@ error[E0609]: no field `baa` on type `BuildData`
--> $DIR/struct-fields-typo.rs:11:17
|
LL | let x = foo.baa;
| ^^^ help: a field with a similar name exists: `bar`
| ^^^
|
help: a field with a similar name exists
|
LL | let x = foo.bar;
| ~~~
error: aborting due to previous error

View File

@ -2,7 +2,12 @@ error[E0609]: no field `d` on type `&A`
--> $DIR/struct-pat-derived-error.rs:8:31
|
LL | let A { x, y } = self.d;
| ^ help: a field with a similar name exists: `b`
| ^
|
help: a field with a similar name exists
|
LL | let A { x, y } = self.b;
| ~
error[E0026]: struct `A` does not have fields named `x`, `y`
--> $DIR/struct-pat-derived-error.rs:8:17

View File

@ -10,4 +10,12 @@ fn main() {
let _ = x.long_name; //~ ERROR no field `longname`
let y = S { long_name: (), foo: () };
let _ = y.long_name; //~ ERROR no field `longname`
let a = Some(Arc::new(S { long_name: (), foo: () }));
let _ = a.unwrap().long_name; //~ ERROR no field `longname`
let b = Some(S { long_name: (), foo: () });
let _ = b.unwrap().long_name; //~ ERROR no field `long_name`
let c = Ok::<_, ()>(Arc::new(S { long_name: (), foo: () }));
let _ = c.unwrap().long_name; //~ ERROR no field `longname`
let d = Ok::<_, ()>(S { long_name: (), foo: () });
let _ = d.unwrap().long_name; //~ ERROR no field `long_name`
}

View File

@ -10,4 +10,12 @@ fn main() {
let _ = x.longname; //~ ERROR no field `longname`
let y = S { long_name: (), foo: () };
let _ = y.longname; //~ ERROR no field `longname`
let a = Some(Arc::new(S { long_name: (), foo: () }));
let _ = a.longname; //~ ERROR no field `longname`
let b = Some(S { long_name: (), foo: () });
let _ = b.long_name; //~ ERROR no field `long_name`
let c = Ok::<_, ()>(Arc::new(S { long_name: (), foo: () }));
let _ = c.longname; //~ ERROR no field `longname`
let d = Ok::<_, ()>(S { long_name: (), foo: () });
let _ = d.long_name; //~ ERROR no field `long_name`
}

View File

@ -2,14 +2,68 @@ error[E0609]: no field `longname` on type `Arc<S>`
--> $DIR/suggest-field-through-deref.rs:10:15
|
LL | let _ = x.longname;
| ^^^^^^^^ help: a field with a similar name exists: `long_name`
| ^^^^^^^^
|
help: a field with a similar name exists
|
LL | let _ = x.long_name;
| ~~~~~~~~~
error[E0609]: no field `longname` on type `S`
--> $DIR/suggest-field-through-deref.rs:12:15
|
LL | let _ = y.longname;
| ^^^^^^^^ help: a field with a similar name exists: `long_name`
| ^^^^^^^^
|
help: a field with a similar name exists
|
LL | let _ = y.long_name;
| ~~~~~~~~~
error: aborting due to 2 previous errors
error[E0609]: no field `longname` on type `Option<Arc<S>>`
--> $DIR/suggest-field-through-deref.rs:14:15
|
LL | let _ = a.longname;
| ^^^^^^^^
|
help: a field with a similar name exists
|
LL | let _ = a.unwrap().long_name;
| ~~~~~~~~~~~~~~~~~~
error[E0609]: no field `long_name` on type `Option<S>`
--> $DIR/suggest-field-through-deref.rs:16:15
|
LL | let _ = b.long_name;
| ^^^^^^^^^
|
help: one of the expressions' fields has a field of the same name
|
LL | let _ = b.unwrap().long_name;
| +++++++++
error[E0609]: no field `longname` on type `Result<Arc<S>, ()>`
--> $DIR/suggest-field-through-deref.rs:18:15
|
LL | let _ = c.longname;
| ^^^^^^^^
|
help: a field with a similar name exists
|
LL | let _ = c.unwrap().long_name;
| ~~~~~~~~~~~~~~~~~~
error[E0609]: no field `long_name` on type `Result<S, ()>`
--> $DIR/suggest-field-through-deref.rs:20:15
|
LL | let _ = d.long_name;
| ^^^^^^^^^
|
help: one of the expressions' fields has a field of the same name
|
LL | let _ = d.unwrap().long_name;
| +++++++++
error: aborting due to 6 previous errors
For more information about this error, try `rustc --explain E0609`.

View File

@ -2,7 +2,12 @@ error[E0609]: no field `0` on type `Point`
--> $DIR/tuple-index-not-tuple.rs:6:12
|
LL | origin.0;
| ^ help: a field with a similar name exists: `x`
| ^
|
help: a field with a similar name exists
|
LL | origin.x;
| ~
error[E0609]: no field `0` on type `Empty`
--> $DIR/tuple-index-not-tuple.rs:8:11

View File

@ -2,7 +2,12 @@ error[E0609]: no field `2` on type `Point`
--> $DIR/tuple-index-out-of-bounds.rs:7:12
|
LL | origin.2;
| ^ help: a field with a similar name exists: `0`
| ^
|
help: a field with a similar name exists
|
LL | origin.0;
| ~
error[E0609]: no field `2` on type `({integer}, {integer})`
--> $DIR/tuple-index-out-of-bounds.rs:12:11

View File

@ -8,7 +8,12 @@ error[E0609]: no field `principial` on type `U`
--> $DIR/union-suggest-field.rs:17:15
|
LL | let w = u.principial;
| ^^^^^^^^^^ help: a field with a similar name exists: `principal`
| ^^^^^^^^^^
|
help: a field with a similar name exists
|
LL | let w = u.principal;
| ~~~~~~~~~
error[E0615]: attempted to take value of method `calculate` on type `U`
--> $DIR/union-suggest-field.rs:21:15

View File

@ -8,7 +8,12 @@ error[E0609]: no field `principial` on type `U`
--> $DIR/union-suggest-field.rs:17:15
|
LL | let w = u.principial;
| ^^^^^^^^^^ help: a field with a similar name exists: `principal`
| ^^^^^^^^^^
|
help: a field with a similar name exists
|
LL | let w = u.principal;
| ~~~~~~~~~
error[E0615]: attempted to take value of method `calculate` on type `U`
--> $DIR/union-suggest-field.rs:21:15