about summary refs log tree commit diff
diff options
context:
space:
mode:
authormejrs <>2022-09-26 00:55:35 +0200
committermejrs <>2022-09-27 21:42:09 +0200
commitc4c94151321b8018ceb06ccff359109b8fed6bfe (patch)
treee9572b4e96cee41a8934626b50b98e22305b19c8
parent57ee5cf5a93923dae9c98bffb11545fc3a31368d (diff)
downloadrust-c4c94151321b8018ceb06ccff359109b8fed6bfe.tar.gz
rust-c4c94151321b8018ceb06ccff359109b8fed6bfe.zip
Wrapper suggestions
-rw-r--r--compiler/rustc_hir_analysis/src/check/method/suggest.rs213
-rw-r--r--compiler/rustc_span/src/symbol.rs2
-rw-r--r--library/core/src/cell.rs1
-rw-r--r--library/std/src/thread/local.rs1
-rw-r--r--src/test/ui/suggestions/inner_type.fixed30
-rw-r--r--src/test/ui/suggestions/inner_type.rs30
-rw-r--r--src/test/ui/suggestions/inner_type.stderr51
-rw-r--r--src/test/ui/suggestions/inner_type2.rs26
-rw-r--r--src/test/ui/suggestions/inner_type2.stderr29
9 files changed, 316 insertions, 67 deletions
diff --git a/compiler/rustc_hir_analysis/src/check/method/suggest.rs b/compiler/rustc_hir_analysis/src/check/method/suggest.rs
index 0e77ed0a4fe..1ff2575bcc5 100644
--- a/compiler/rustc_hir_analysis/src/check/method/suggest.rs
+++ b/compiler/rustc_hir_analysis/src/check/method/suggest.rs
@@ -2,6 +2,7 @@
 //! found or is otherwise invalid.
 
 use crate::check::FnCtxt;
+use rustc_ast::ast::Mutability;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_errors::{
     pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
@@ -30,7 +31,7 @@ use rustc_trait_selection::traits::{
 use std::cmp::Ordering;
 use std::iter;
 
-use super::probe::{IsSuggestion, Mode, ProbeScope};
+use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope};
 use super::{CandidateSource, MethodError, NoMatchData};
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -983,7 +984,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     self.check_for_field_method(&mut err, source, span, actual, item_name);
                 }
 
-                self.check_for_unwrap_self(&mut err, source, span, actual, item_name);
+                self.check_for_inner_self(&mut err, source, span, actual, item_name);
 
                 bound_spans.sort();
                 bound_spans.dedup();
@@ -1395,7 +1396,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    fn check_for_unwrap_self(
+    fn check_for_inner_self(
         &self,
         err: &mut Diagnostic,
         source: SelfSource<'tcx>,
@@ -1408,81 +1409,159 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let call_expr = tcx.hir().expect_expr(tcx.hir().get_parent_node(expr.hir_id));
 
         let ty::Adt(kind, substs) = actual.kind() else { return; };
-        if !kind.is_enum() {
-            return;
-        }
+        match kind.adt_kind() {
+            ty::AdtKind::Enum => {
+                let matching_variants: Vec<_> = kind
+                    .variants()
+                    .iter()
+                    .flat_map(|variant| {
+                        let [field] = &variant.fields[..] else { return None; };
+                        let field_ty = field.ty(tcx, substs);
+
+                        // Skip `_`, since that'll just lead to ambiguity.
+                        if self.resolve_vars_if_possible(field_ty).is_ty_var() {
+                            return None;
+                        }
 
-        let matching_variants: Vec<_> = kind
-            .variants()
-            .iter()
-            .flat_map(|variant| {
-                let [field] = &variant.fields[..] else { return None; };
-                let field_ty = field.ty(tcx, substs);
+                        self.lookup_probe(
+                            span,
+                            item_name,
+                            field_ty,
+                            call_expr,
+                            ProbeScope::AllTraits,
+                        )
+                        .ok()
+                        .map(|pick| (variant, field, pick))
+                    })
+                    .collect();
+
+                let ret_ty_matches = |diagnostic_item| {
+                    if let Some(ret_ty) = self
+                        .ret_coercion
+                        .as_ref()
+                        .map(|c| self.resolve_vars_if_possible(c.borrow().expected_ty()))
+                        && let ty::Adt(kind, _) = ret_ty.kind()
+                        && tcx.get_diagnostic_item(diagnostic_item) == Some(kind.did())
+                    {
+                        true
+                    } else {
+                        false
+                    }
+                };
 
-                // Skip `_`, since that'll just lead to ambiguity.
-                if self.resolve_vars_if_possible(field_ty).is_ty_var() {
-                    return None;
+                match &matching_variants[..] {
+                    [(_, field, pick)] => {
+                        let self_ty = field.ty(tcx, substs);
+                        err.span_note(
+                            tcx.def_span(pick.item.def_id),
+                            &format!("the method `{item_name}` exists on the type `{self_ty}`"),
+                        );
+                        let (article, kind, variant, question) =
+                            if tcx.is_diagnostic_item(sym::Result, kind.did()) {
+                                ("a", "Result", "Err", ret_ty_matches(sym::Result))
+                            } else if tcx.is_diagnostic_item(sym::Option, kind.did()) {
+                                ("an", "Option", "None", ret_ty_matches(sym::Option))
+                            } else {
+                                return;
+                            };
+                        if question {
+                            err.span_suggestion_verbose(
+                                expr.span.shrink_to_hi(),
+                                format!(
+                                    "use the `?` operator to extract the `{self_ty}` value, propagating \
+                                    {article} `{kind}::{variant}` value to the caller"
+                                ),
+                                "?",
+                                Applicability::MachineApplicable,
+                            );
+                        } else {
+                            err.span_suggestion_verbose(
+                                expr.span.shrink_to_hi(),
+                                format!(
+                                    "consider using `{kind}::expect` to unwrap the `{self_ty}` value, \
+                                    panicking if the value is {article} `{kind}::{variant}`"
+                                ),
+                                ".expect(\"REASON\")",
+                                Applicability::HasPlaceholders,
+                            );
+                        }
+                    }
+                    // FIXME(compiler-errors): Support suggestions for other matching enum variants
+                    _ => {}
                 }
-
-                self.lookup_probe(span, item_name, field_ty, call_expr, ProbeScope::AllTraits)
-                    .ok()
-                    .map(|pick| (variant, field, pick))
-            })
-            .collect();
-
-        let ret_ty_matches = |diagnostic_item| {
-            if let Some(ret_ty) = self
-                .ret_coercion
-                .as_ref()
-                .map(|c| self.resolve_vars_if_possible(c.borrow().expected_ty()))
-                && let ty::Adt(kind, _) = ret_ty.kind()
-                && tcx.get_diagnostic_item(diagnostic_item) == Some(kind.did())
-            {
-                true
-            } else {
-                false
             }
-        };
-
-        match &matching_variants[..] {
-            [(_, field, pick)] => {
-                let self_ty = field.ty(tcx, substs);
-                err.span_note(
-                    tcx.def_span(pick.item.def_id),
-                    &format!("the method `{item_name}` exists on the type `{self_ty}`"),
-                );
-                let (article, kind, variant, question) =
-                    if Some(kind.did()) == tcx.get_diagnostic_item(sym::Result) {
-                        ("a", "Result", "Err", ret_ty_matches(sym::Result))
-                    } else if Some(kind.did()) == tcx.get_diagnostic_item(sym::Option) {
-                        ("an", "Option", "None", ret_ty_matches(sym::Option))
-                    } else {
-                        return;
-                    };
-                if question {
+            // Target wrapper types - types that wrap or pretend to wrap another type,
+            // perhaps this inner type is meant to be called?
+            ty::AdtKind::Struct | ty::AdtKind::Union => {
+                let [first] = ***substs else { return; };
+                let ty::GenericArgKind::Type(ty) = first.unpack() else { return; };
+                let Ok(pick) = self.lookup_probe(
+                            span,
+                            item_name,
+                            ty,
+                            call_expr,
+                            ProbeScope::AllTraits,
+                        )  else { return; };
+
+                let name = self.ty_to_value_string(actual);
+                let inner_id = kind.did();
+
+                if tcx.is_diagnostic_item(sym::LocalKey, inner_id) {
+                    err.help("use `with` or `try_with` to access the contents of threadlocals");
+                } else if Some(kind.did()) == tcx.lang_items().maybe_uninit() {
+                    err.help(format!(
+                        "if this `{name}` has been initialized, \
+                        use one of the `assume_init` methods to access the inner value"
+                    ));
+                } else if tcx.is_diagnostic_item(sym::RefCell, inner_id) {
+                    match pick.autoref_or_ptr_adjustment {
+                        Some(AutorefOrPtrAdjustment::Autoref {
+                            mutbl: Mutability::Not, ..
+                        }) => {
+                            err.span_suggestion_verbose(
+                                expr.span.shrink_to_hi(),
+                                format!(
+                                    "use `.borrow()` to borrow the {ty}, \
+                                panicking if any outstanding mutable borrows exist."
+                                ),
+                                ".borrow()",
+                                Applicability::MaybeIncorrect,
+                            );
+                        }
+                        Some(AutorefOrPtrAdjustment::Autoref {
+                            mutbl: Mutability::Mut, ..
+                        }) => {
+                            err.span_suggestion_verbose(
+                                expr.span.shrink_to_hi(),
+                                format!(
+                                    "use `.borrow_mut()` to mutably borrow the {ty}, \
+                                panicking if any outstanding borrows exist."
+                                ),
+                                ".borrow_mut()",
+                                Applicability::MaybeIncorrect,
+                            );
+                        }
+                        _ => return,
+                    }
+                } else if tcx.is_diagnostic_item(sym::Mutex, inner_id) {
                     err.span_suggestion_verbose(
                         expr.span.shrink_to_hi(),
                         format!(
-                            "use the `?` operator to extract the `{self_ty}` value, propagating \
-                            {article} `{kind}::{variant}` value to the caller"
+                            "use `.lock()` to borrow the {ty}, \
+                            blocking the current thread until it can be acquired"
                         ),
-                        "?",
-                        Applicability::MachineApplicable,
+                        ".lock().unwrap()",
+                        Applicability::MaybeIncorrect,
                     );
                 } else {
-                    err.span_suggestion_verbose(
-                        expr.span.shrink_to_hi(),
-                        format!(
-                            "consider using `{kind}::expect` to unwrap the `{self_ty}` value, \
-                             panicking if the value is {article} `{kind}::{variant}`"
-                        ),
-                        ".expect(\"REASON\")",
-                        Applicability::HasPlaceholders,
-                    );
-                }
+                    return;
+                };
+
+                err.span_note(
+                    tcx.def_span(pick.item.def_id),
+                    &format!("the method `{item_name}` exists on the type `{ty}`"),
+                );
             }
-            // FIXME(compiler-errors): Support suggestions for other matching enum variants
-            _ => {}
         }
     }
 
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 502ef67fc67..6101c1c47e3 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -224,6 +224,7 @@ symbols! {
         Left,
         LinkedList,
         LintPass,
+        LocalKey,
         Mutex,
         MutexGuard,
         N,
@@ -266,6 +267,7 @@ symbols! {
         Rc,
         Ready,
         Receiver,
+        RefCell,
         Relaxed,
         Release,
         Result,
diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs
index 1abbb39497a..cf7b1b358c9 100644
--- a/library/core/src/cell.rs
+++ b/library/core/src/cell.rs
@@ -614,6 +614,7 @@ impl<T, const N: usize> Cell<[T; N]> {
 /// A mutable memory location with dynamically checked borrow rules
 ///
 /// See the [module-level documentation](self) for more.
+#[rustc_diagnostic_item = "RefCell"]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct RefCell<T: ?Sized> {
     borrow: Cell<BorrowFlag>,
diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs
index 8aedfc4a6b8..1d728349951 100644
--- a/library/std/src/thread/local.rs
+++ b/library/std/src/thread/local.rs
@@ -95,6 +95,7 @@ use crate::fmt;
 /// [loader lock]: https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices
 /// [`JoinHandle::join`]: crate::thread::JoinHandle::join
 /// [`with`]: LocalKey::with
+#[rustc_diagnostic_item = "LocalKey"]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct LocalKey<T: 'static> {
     // This outer `LocalKey<T>` type is what's going to be stored in statics,
diff --git a/src/test/ui/suggestions/inner_type.fixed b/src/test/ui/suggestions/inner_type.fixed
new file mode 100644
index 00000000000..f6dc7c4ce17
--- /dev/null
+++ b/src/test/ui/suggestions/inner_type.fixed
@@ -0,0 +1,30 @@
+// compile-flags: --edition=2021
+// run-rustfix
+
+pub struct Struct<T> {
+    pub p: T,
+}
+
+impl<T> Struct<T> {
+    pub fn method(&self) {}
+
+    pub fn some_mutable_method(&mut self) {}
+}
+
+fn main() {
+    let other_item = std::cell::RefCell::new(Struct { p: 42_u32 });
+
+    other_item.borrow().method();
+    //~^ ERROR no method named `method` found for struct `RefCell` in the current scope [E0599]
+    //~| HELP use `.borrow()` to borrow the Struct<u32>, panicking if any outstanding mutable borrows exist.
+
+    other_item.borrow_mut().some_mutable_method();
+    //~^ ERROR no method named `some_mutable_method` found for struct `RefCell` in the current scope [E0599]
+    //~| HELP use `.borrow_mut()` to mutably borrow the Struct<u32>, panicking if any outstanding borrows exist.
+
+    let another_item = std::sync::Mutex::new(Struct { p: 42_u32 });
+
+    another_item.lock().unwrap().method();
+    //~^ ERROR no method named `method` found for struct `Mutex` in the current scope [E0599]
+    //~| HELP use `.lock()` to borrow the Struct<u32>, blocking the current thread until it can be acquired
+}
\ No newline at end of file
diff --git a/src/test/ui/suggestions/inner_type.rs b/src/test/ui/suggestions/inner_type.rs
new file mode 100644
index 00000000000..b42067c047c
--- /dev/null
+++ b/src/test/ui/suggestions/inner_type.rs
@@ -0,0 +1,30 @@
+// compile-flags: --edition=2021
+// run-rustfix
+
+pub struct Struct<T> {
+    pub p: T,
+}
+
+impl<T> Struct<T> {
+    pub fn method(&self) {}
+
+    pub fn some_mutable_method(&mut self) {}
+}
+
+fn main() {
+    let other_item = std::cell::RefCell::new(Struct { p: 42_u32 });
+
+    other_item.method();
+    //~^ ERROR no method named `method` found for struct `RefCell` in the current scope [E0599]
+    //~| HELP use `.borrow()` to borrow the Struct<u32>, panicking if any outstanding mutable borrows exist.
+
+    other_item.some_mutable_method();
+    //~^ ERROR no method named `some_mutable_method` found for struct `RefCell` in the current scope [E0599]
+    //~| HELP use `.borrow_mut()` to mutably borrow the Struct<u32>, panicking if any outstanding borrows exist.
+
+    let another_item = std::sync::Mutex::new(Struct { p: 42_u32 });
+
+    another_item.method();
+    //~^ ERROR no method named `method` found for struct `Mutex` in the current scope [E0599]
+    //~| HELP use `.lock()` to borrow the Struct<u32>, blocking the current thread until it can be acquired
+}
\ No newline at end of file
diff --git a/src/test/ui/suggestions/inner_type.stderr b/src/test/ui/suggestions/inner_type.stderr
new file mode 100644
index 00000000000..f2b25944c8b
--- /dev/null
+++ b/src/test/ui/suggestions/inner_type.stderr
@@ -0,0 +1,51 @@
+error[E0599]: no method named `method` found for struct `RefCell` in the current scope
+  --> $DIR/inner_type.rs:17:16
+   |
+LL |     other_item.method();
+   |                ^^^^^^ method not found in `RefCell<Struct<u32>>`
+   |
+note: the method `method` exists on the type `Struct<u32>`
+  --> $DIR/inner_type.rs:9:5
+   |
+LL |     pub fn method(&self) {}
+   |     ^^^^^^^^^^^^^^^^^^^^
+help: use `.borrow()` to borrow the Struct<u32>, panicking if any outstanding mutable borrows exist.
+   |
+LL |     other_item.borrow().method();
+   |               +++++++++
+
+error[E0599]: no method named `some_mutable_method` found for struct `RefCell` in the current scope
+  --> $DIR/inner_type.rs:21:16
+   |
+LL |     other_item.some_mutable_method();
+   |                ^^^^^^^^^^^^^^^^^^^ method not found in `RefCell<Struct<u32>>`
+   |
+note: the method `some_mutable_method` exists on the type `Struct<u32>`
+  --> $DIR/inner_type.rs:11:5
+   |
+LL |     pub fn some_mutable_method(&mut self) {}
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: use `.borrow_mut()` to mutably borrow the Struct<u32>, panicking if any outstanding borrows exist.
+   |
+LL |     other_item.borrow_mut().some_mutable_method();
+   |               +++++++++++++
+
+error[E0599]: no method named `method` found for struct `Mutex` in the current scope
+  --> $DIR/inner_type.rs:27:18
+   |
+LL |     another_item.method();
+   |                  ^^^^^^ method not found in `Mutex<Struct<u32>>`
+   |
+note: the method `method` exists on the type `Struct<u32>`
+  --> $DIR/inner_type.rs:9:5
+   |
+LL |     pub fn method(&self) {}
+   |     ^^^^^^^^^^^^^^^^^^^^
+help: use `.lock()` to borrow the Struct<u32>, blocking the current thread until it can be acquired
+   |
+LL |     another_item.lock().unwrap().method();
+   |                 ++++++++++++++++
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0599`.
diff --git a/src/test/ui/suggestions/inner_type2.rs b/src/test/ui/suggestions/inner_type2.rs
new file mode 100644
index 00000000000..694c0adfd06
--- /dev/null
+++ b/src/test/ui/suggestions/inner_type2.rs
@@ -0,0 +1,26 @@
+pub struct Struct<T> {
+    pub p: T,
+}
+
+impl<T> Struct<T> {
+    pub fn method(&self) {}
+
+    pub fn some_mutable_method(&mut self) {}
+}
+
+thread_local! {
+    static STRUCT: Struct<u32> = Struct {
+        p: 42_u32
+    };
+}
+
+fn main() {
+    STRUCT.method();
+    //~^ ERROR no method named `method` found for struct `LocalKey` in the current scope [E0599]
+    //~| HELP use `with` or `try_with` to access the contents of threadlocals
+
+    let item = std::mem::MaybeUninit::new(Struct { p: 42_u32 });
+    item.method();
+    //~^ ERROR no method named `method` found for union `MaybeUninit` in the current scope [E0599]
+    //~| HELP if this `MaybeUninit::<Struct<u32>>` has been initialized, use one of the `assume_init` methods to access the inner value
+}
\ No newline at end of file
diff --git a/src/test/ui/suggestions/inner_type2.stderr b/src/test/ui/suggestions/inner_type2.stderr
new file mode 100644
index 00000000000..40e7a7ab41e
--- /dev/null
+++ b/src/test/ui/suggestions/inner_type2.stderr
@@ -0,0 +1,29 @@
+error[E0599]: no method named `method` found for struct `LocalKey` in the current scope
+  --> $DIR/inner_type2.rs:18:12
+   |
+LL |     STRUCT.method();
+   |            ^^^^^^ method not found in `LocalKey<Struct<u32>>`
+   |
+   = help: use `with` or `try_with` to access the contents of threadlocals
+note: the method `method` exists on the type `Struct<u32>`
+  --> $DIR/inner_type2.rs:6:5
+   |
+LL |     pub fn method(&self) {}
+   |     ^^^^^^^^^^^^^^^^^^^^
+
+error[E0599]: no method named `method` found for union `MaybeUninit` in the current scope
+  --> $DIR/inner_type2.rs:23:10
+   |
+LL |     item.method();
+   |          ^^^^^^ method not found in `MaybeUninit<Struct<u32>>`
+   |
+   = help: if this `MaybeUninit::<Struct<u32>>` has been initialized, use one of the `assume_init` methods to access the inner value
+note: the method `method` exists on the type `Struct<u32>`
+  --> $DIR/inner_type2.rs:6:5
+   |
+LL |     pub fn method(&self) {}
+   |     ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0599`.