about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2023-10-24 23:31:47 +0000
committerEsteban Küber <esteban@kuber.com.ar>2023-11-16 17:00:23 +0000
commit8bd8f3b090507f87c6458ed507ee36ddd4a00767 (patch)
treef4c829096aefdd539a625f93bae2b4baa2371580
parentdfa75391f8c401d0f1b40b0bf9799a07b53c58ac (diff)
downloadrust-8bd8f3b090507f87c6458ed507ee36ddd4a00767.tar.gz
rust-8bd8f3b090507f87c6458ed507ee36ddd4a00767.zip
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.
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs24
-rw-r--r--tests/ui/async-await/suggest-switching-edition-on-await-cargo.stderr6
-rw-r--r--tests/ui/async-await/suggest-switching-edition-on-await.stderr6
-rw-r--r--tests/ui/derived-errors/issue-30580.stderr7
-rw-r--r--tests/ui/did_you_mean/issue-36798.stderr7
-rw-r--r--tests/ui/did_you_mean/issue-42599_available_fields_note.stderr7
-rw-r--r--tests/ui/error-codes/ex-E0612.stderr7
-rw-r--r--tests/ui/issues/issue-47073-zero-padded-tuple-struct-indices.stderr7
-rw-r--r--tests/ui/structs/struct-fields-typo.stderr7
-rw-r--r--tests/ui/structs/struct-pat-derived-error.stderr7
-rw-r--r--tests/ui/suggestions/suggest-field-through-deref.fixed8
-rw-r--r--tests/ui/suggestions/suggest-field-through-deref.rs8
-rw-r--r--tests/ui/suggestions/suggest-field-through-deref.stderr60
-rw-r--r--tests/ui/tuple/tuple-index-not-tuple.stderr7
-rw-r--r--tests/ui/tuple/tuple-index-out-of-bounds.stderr7
-rw-r--r--tests/ui/union/union-suggest-field.mirunsafeck.stderr7
-rw-r--r--tests/ui/union/union-suggest-field.thirunsafeck.stderr7
17 files changed, 166 insertions, 23 deletions
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index b95c673f3ba..264b96af6da 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -2686,8 +2686,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // 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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         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() {
diff --git a/tests/ui/async-await/suggest-switching-edition-on-await-cargo.stderr b/tests/ui/async-await/suggest-switching-edition-on-await-cargo.stderr
index 1b0912310e8..ebd117f506c 100644
--- a/tests/ui/async-await/suggest-switching-edition-on-await-cargo.stderr
+++ b/tests/ui/async-await/suggest-switching-edition-on-await-cargo.stderr
@@ -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
diff --git a/tests/ui/async-await/suggest-switching-edition-on-await.stderr b/tests/ui/async-await/suggest-switching-edition-on-await.stderr
index ddc44562d2c..82c5b107b05 100644
--- a/tests/ui/async-await/suggest-switching-edition-on-await.stderr
+++ b/tests/ui/async-await/suggest-switching-edition-on-await.stderr
@@ -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
diff --git a/tests/ui/derived-errors/issue-30580.stderr b/tests/ui/derived-errors/issue-30580.stderr
index 7bd0eaf77a9..146ffd7dd65 100644
--- a/tests/ui/derived-errors/issue-30580.stderr
+++ b/tests/ui/derived-errors/issue-30580.stderr
@@ -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
 
diff --git a/tests/ui/did_you_mean/issue-36798.stderr b/tests/ui/did_you_mean/issue-36798.stderr
index 98876e305ca..354aef4047b 100644
--- a/tests/ui/did_you_mean/issue-36798.stderr
+++ b/tests/ui/did_you_mean/issue-36798.stderr
@@ -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
 
diff --git a/tests/ui/did_you_mean/issue-42599_available_fields_note.stderr b/tests/ui/did_you_mean/issue-42599_available_fields_note.stderr
index a4c3512eb41..d830564bc37 100644
--- a/tests/ui/did_you_mean/issue-42599_available_fields_note.stderr
+++ b/tests/ui/did_you_mean/issue-42599_available_fields_note.stderr
@@ -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
diff --git a/tests/ui/error-codes/ex-E0612.stderr b/tests/ui/error-codes/ex-E0612.stderr
index b21b6fdfcf1..ee15147ba3e 100644
--- a/tests/ui/error-codes/ex-E0612.stderr
+++ b/tests/ui/error-codes/ex-E0612.stderr
@@ -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
 
diff --git a/tests/ui/issues/issue-47073-zero-padded-tuple-struct-indices.stderr b/tests/ui/issues/issue-47073-zero-padded-tuple-struct-indices.stderr
index 193c5aaf72d..0edaeca8a88 100644
--- a/tests/ui/issues/issue-47073-zero-padded-tuple-struct-indices.stderr
+++ b/tests/ui/issues/issue-47073-zero-padded-tuple-struct-indices.stderr
@@ -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
diff --git a/tests/ui/structs/struct-fields-typo.stderr b/tests/ui/structs/struct-fields-typo.stderr
index 6949a0a4a68..8423e7a97a0 100644
--- a/tests/ui/structs/struct-fields-typo.stderr
+++ b/tests/ui/structs/struct-fields-typo.stderr
@@ -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
 
diff --git a/tests/ui/structs/struct-pat-derived-error.stderr b/tests/ui/structs/struct-pat-derived-error.stderr
index a91e47657ab..f80c2383ef8 100644
--- a/tests/ui/structs/struct-pat-derived-error.stderr
+++ b/tests/ui/structs/struct-pat-derived-error.stderr
@@ -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
diff --git a/tests/ui/suggestions/suggest-field-through-deref.fixed b/tests/ui/suggestions/suggest-field-through-deref.fixed
index a0f20e563f3..07ba3aa911f 100644
--- a/tests/ui/suggestions/suggest-field-through-deref.fixed
+++ b/tests/ui/suggestions/suggest-field-through-deref.fixed
@@ -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`
 }
diff --git a/tests/ui/suggestions/suggest-field-through-deref.rs b/tests/ui/suggestions/suggest-field-through-deref.rs
index 729e5db0552..6e24b425e95 100644
--- a/tests/ui/suggestions/suggest-field-through-deref.rs
+++ b/tests/ui/suggestions/suggest-field-through-deref.rs
@@ -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`
 }
diff --git a/tests/ui/suggestions/suggest-field-through-deref.stderr b/tests/ui/suggestions/suggest-field-through-deref.stderr
index 649e28be51d..f12d53448d3 100644
--- a/tests/ui/suggestions/suggest-field-through-deref.stderr
+++ b/tests/ui/suggestions/suggest-field-through-deref.stderr
@@ -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[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 2 previous errors
+error: aborting due to 6 previous errors
 
 For more information about this error, try `rustc --explain E0609`.
diff --git a/tests/ui/tuple/tuple-index-not-tuple.stderr b/tests/ui/tuple/tuple-index-not-tuple.stderr
index cc94a61ae7f..0f81f2a1bb8 100644
--- a/tests/ui/tuple/tuple-index-not-tuple.stderr
+++ b/tests/ui/tuple/tuple-index-not-tuple.stderr
@@ -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
diff --git a/tests/ui/tuple/tuple-index-out-of-bounds.stderr b/tests/ui/tuple/tuple-index-out-of-bounds.stderr
index 7d7c5cd7892..7fc8289aaa0 100644
--- a/tests/ui/tuple/tuple-index-out-of-bounds.stderr
+++ b/tests/ui/tuple/tuple-index-out-of-bounds.stderr
@@ -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
diff --git a/tests/ui/union/union-suggest-field.mirunsafeck.stderr b/tests/ui/union/union-suggest-field.mirunsafeck.stderr
index 58b1f5cb078..805f84da388 100644
--- a/tests/ui/union/union-suggest-field.mirunsafeck.stderr
+++ b/tests/ui/union/union-suggest-field.mirunsafeck.stderr
@@ -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
diff --git a/tests/ui/union/union-suggest-field.thirunsafeck.stderr b/tests/ui/union/union-suggest-field.thirunsafeck.stderr
index 58b1f5cb078..805f84da388 100644
--- a/tests/ui/union/union-suggest-field.thirunsafeck.stderr
+++ b/tests/ui/union/union-suggest-field.thirunsafeck.stderr
@@ -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