about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Kรผber <esteban@kuber.com.ar>2023-10-21 01:18:41 +0000
committerEsteban Kรผber <esteban@kuber.com.ar>2023-11-07 00:54:10 +0000
commitf926031ea50ef58c1c8bfccd98da73d86d5fc937 (patch)
treeecd76d25ae1eaa3df2418337f7d42f19a77cfe35
parentb049093560aa1c69face8c1893bd8acd99fff275 (diff)
downloadrust-f926031ea50ef58c1c8bfccd98da73d86d5fc937.tar.gz
rust-f926031ea50ef58c1c8bfccd98da73d86d5fc937.zip
When not finding assoc fn on type, look for builder fn
When we have a resolution error when looking at a fully qualified path
on a type, look for all associated functions on inherent impls that
return `Self` and mention them to the user.

Fix #69512.
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs88
-rw-r--r--tests/ui/atomic-from-mut-not-available.stderr4
-rw-r--r--tests/ui/parser/emoji-identifiers.stderr6
-rw-r--r--tests/ui/resolve/fn-new-doesnt-exist.rs5
-rw-r--r--tests/ui/resolve/fn-new-doesnt-exist.stderr14
-rw-r--r--tests/ui/resolve/issue-82865.stderr7
-rw-r--r--tests/ui/suggestions/deref-path-method.stderr7
-rw-r--r--tests/ui/suggestions/issue-109291.stderr7
8 files changed, 138 insertions, 0 deletions
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 12cc5ed2f1a..87dfcf09cdd 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -409,6 +409,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             err.downgrade_to_delayed_bug();
         }
 
+        self.find_builder_fn(&mut err, rcvr_ty, source);
         if tcx.ty_is_opaque_future(rcvr_ty) && item_name.name == sym::poll {
             err.help(format!(
                 "method `poll` found on `Pin<&mut {ty_str}>`, \
@@ -1407,6 +1408,93 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
+    /// Look at all the associated functions without receivers in the type's inherent impls
+    /// to look for builders that return `Self`, `Option<Self>` or `Result<Self, _>`.
+    fn find_builder_fn(&self, err: &mut Diagnostic, rcvr_ty: Ty<'tcx>, source: SelfSource<'tcx>) {
+        let ty::Adt(adt_def, _) = rcvr_ty.kind() else {
+            return;
+        };
+        let SelfSource::QPath(ty) = source else {
+            return;
+        };
+        let hir = self.tcx.hir();
+        if let Some(Node::Pat(_)) = hir.find(hir.parent_id(ty.hir_id)) {
+            // Do not suggest a fn call when a pattern is expected.
+            return;
+        }
+        let mut items = self
+            .tcx
+            .inherent_impls(adt_def.did())
+            .iter()
+            .flat_map(|i| self.tcx.associated_items(i).in_definition_order())
+            // Only assoc fn with no receivers.
+            .filter(|item| matches!(item.kind, ty::AssocKind::Fn) && !item.fn_has_self_parameter)
+            .filter_map(|item| {
+                // Only assoc fns that return `Self`, `Option<Self>` or `Result<Self, _>`.
+                let ret_ty = self.tcx.fn_sig(item.def_id).skip_binder().output();
+                let ret_ty = self.tcx.erase_late_bound_regions(ret_ty);
+                let ty::Adt(def, args) = ret_ty.kind() else {
+                    return None;
+                };
+                // Check for `-> Self`
+                if self.can_eq(self.param_env, ret_ty, rcvr_ty) {
+                    return Some((item.def_id, ret_ty));
+                }
+                // Check for `-> Option<Self>` or `-> Result<Self, _>`
+                if ![self.tcx.lang_items().option_type(), self.tcx.get_diagnostic_item(sym::Result)]
+                    .contains(&Some(def.did()))
+                {
+                    return None;
+                }
+                let arg = args.get(0)?.expect_ty();
+                if self.can_eq(self.param_env, rcvr_ty, arg) {
+                    Some((item.def_id, ret_ty))
+                } else {
+                    None
+                }
+            })
+            .collect::<Vec<_>>();
+        let post = if items.len() > 5 {
+            let items_len = items.len();
+            items.truncate(4);
+            format!("\nand {} others", items_len - 4)
+        } else {
+            String::new()
+        };
+        match &items[..] {
+            [] => {}
+            [(def_id, ret_ty)] => {
+                err.span_note(
+                    self.tcx.def_span(def_id),
+                    format!(
+                        "if you're trying to build a new `{rcvr_ty}`, consider using `{}` which \
+                         returns `{ret_ty}`",
+                        self.tcx.def_path_str(def_id),
+                    ),
+                );
+            }
+            _ => {
+                let span: MultiSpan = items
+                    .iter()
+                    .map(|(def_id, _)| self.tcx.def_span(def_id))
+                    .collect::<Vec<Span>>()
+                    .into();
+                err.span_note(
+                    span,
+                    format!(
+                        "if you're trying to build a new `{rcvr_ty}` consider using one of the \
+                         following associated functions:\n{}{post}",
+                        items
+                            .iter()
+                            .map(|(def_id, _ret_ty)| self.tcx.def_path_str(def_id))
+                            .collect::<Vec<String>>()
+                            .join("\n")
+                    ),
+                );
+            }
+        }
+    }
+
     /// Suggest calling `Ty::method` if `.method()` isn't found because the method
     /// doesn't take a `self` receiver.
     fn suggest_associated_call_syntax(
diff --git a/tests/ui/atomic-from-mut-not-available.stderr b/tests/ui/atomic-from-mut-not-available.stderr
index d1ebca8a29e..c15d19b1594 100644
--- a/tests/ui/atomic-from-mut-not-available.stderr
+++ b/tests/ui/atomic-from-mut-not-available.stderr
@@ -3,6 +3,10 @@ error[E0599]: no function or associated item named `from_mut` found for struct `
    |
 LL |     core::sync::atomic::AtomicU64::from_mut(&mut 0u64);
    |                                    ^^^^^^^^ function or associated item not found in `AtomicU64`
+   |
+note: if you're trying to build a new `AtomicU64`, consider using `AtomicU64::new` which returns `AtomicU64`
+  --> $SRC_DIR/core/src/sync/atomic.rs:LL:COL
+   = note: this error originates in the macro `atomic_int` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to previous error
 
diff --git a/tests/ui/parser/emoji-identifiers.stderr b/tests/ui/parser/emoji-identifiers.stderr
index e645b68ba87..8250dd1ea2e 100644
--- a/tests/ui/parser/emoji-identifiers.stderr
+++ b/tests/ui/parser/emoji-identifiers.stderr
@@ -75,6 +75,12 @@ LL |     ๐Ÿ‘€::full_ofโœจ()
    |         |
    |         function or associated item not found in `๐Ÿ‘€`
    |         help: there is an associated function with a similar name: `full_of_โœจ`
+   |
+note: if you're trying to build a new `๐Ÿ‘€`, consider using `๐Ÿ‘€::full_of_โœจ` which returns `๐Ÿ‘€`
+  --> $DIR/emoji-identifiers.rs:4:5
+   |
+LL |     fn full_of_โœจ() -> ๐Ÿ‘€ {
+   |     ^^^^^^^^^^^^^^^^^^^^^
 
 error[E0425]: cannot find function `i_like_to_๐Ÿ˜„_a_lot` in this scope
   --> $DIR/emoji-identifiers.rs:13:13
diff --git a/tests/ui/resolve/fn-new-doesnt-exist.rs b/tests/ui/resolve/fn-new-doesnt-exist.rs
new file mode 100644
index 00000000000..4f6290808fc
--- /dev/null
+++ b/tests/ui/resolve/fn-new-doesnt-exist.rs
@@ -0,0 +1,5 @@
+use std::net::TcpStream;
+
+fn main() {
+   let stream = TcpStream::new(); //~ ERROR no function or associated item named `new` found
+}
diff --git a/tests/ui/resolve/fn-new-doesnt-exist.stderr b/tests/ui/resolve/fn-new-doesnt-exist.stderr
new file mode 100644
index 00000000000..39adc0fde44
--- /dev/null
+++ b/tests/ui/resolve/fn-new-doesnt-exist.stderr
@@ -0,0 +1,14 @@
+error[E0599]: no function or associated item named `new` found for struct `TcpStream` in the current scope
+  --> $DIR/fn-new-doesnt-exist.rs:4:28
+   |
+LL |    let stream = TcpStream::new();
+   |                            ^^^ function or associated item not found in `TcpStream`
+   |
+note: if you're trying to build a new `TcpStream` consider using one of the following associated functions:
+      TcpStream::connect
+      TcpStream::connect_timeout
+  --> $SRC_DIR/std/src/net/tcp.rs:LL:COL
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.
diff --git a/tests/ui/resolve/issue-82865.stderr b/tests/ui/resolve/issue-82865.stderr
index 730fd6d6026..9d0439d9d87 100644
--- a/tests/ui/resolve/issue-82865.stderr
+++ b/tests/ui/resolve/issue-82865.stderr
@@ -15,6 +15,13 @@ LL |     Box::z
 LL |     mac!();
    |     ------ in this macro invocation
    |
+note: if you're trying to build a new `Box<_, _>` consider using one of the following associated functions:
+      Box::<T>::new
+      Box::<T>::new_uninit
+      Box::<T>::new_zeroed
+      Box::<T>::try_new
+      and 18 others
+  --> $SRC_DIR/alloc/src/boxed.rs:LL:COL
    = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 2 previous errors
diff --git a/tests/ui/suggestions/deref-path-method.stderr b/tests/ui/suggestions/deref-path-method.stderr
index 1cc37d61151..0372a7e6cc0 100644
--- a/tests/ui/suggestions/deref-path-method.stderr
+++ b/tests/ui/suggestions/deref-path-method.stderr
@@ -4,6 +4,13 @@ error[E0599]: no function or associated item named `contains` found for struct `
 LL |     Vec::contains(&vec, &0);
    |          ^^^^^^^^ function or associated item not found in `Vec<_, _>`
    |
+note: if you're trying to build a new `Vec<_, _>` consider using one of the following associated functions:
+      Vec::<T>::new
+      Vec::<T>::with_capacity
+      Vec::<T>::from_raw_parts
+      Vec::<T, A>::new_in
+      and 2 others
+  --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
 help: the function `contains` is implemented on `[_]`
    |
 LL |     <[_]>::contains(&vec, &0);
diff --git a/tests/ui/suggestions/issue-109291.stderr b/tests/ui/suggestions/issue-109291.stderr
index 4ef5948d9bf..644841fdf7e 100644
--- a/tests/ui/suggestions/issue-109291.stderr
+++ b/tests/ui/suggestions/issue-109291.stderr
@@ -6,6 +6,13 @@ LL |     println!("Custom backtrace: {}", std::backtrace::Backtrace::forced_capt
    |                                                                 |
    |                                                                 function or associated item not found in `Backtrace`
    |                                                                 help: there is an associated function with a similar name: `force_capture`
+   |
+note: if you're trying to build a new `Backtrace` consider using one of the following associated functions:
+      Backtrace::capture
+      Backtrace::force_capture
+      Backtrace::disabled
+      Backtrace::create
+  --> $SRC_DIR/std/src/backtrace.rs:LL:COL
 
 error: aborting due to previous error