diff options
| author | Esteban Kรผber <esteban@kuber.com.ar> | 2023-10-21 01:18:41 +0000 |
|---|---|---|
| committer | Esteban Kรผber <esteban@kuber.com.ar> | 2023-11-07 00:54:10 +0000 |
| commit | f926031ea50ef58c1c8bfccd98da73d86d5fc937 (patch) | |
| tree | ecd76d25ae1eaa3df2418337f7d42f19a77cfe35 | |
| parent | b049093560aa1c69face8c1893bd8acd99fff275 (diff) | |
| download | rust-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.rs | 88 | ||||
| -rw-r--r-- | tests/ui/atomic-from-mut-not-available.stderr | 4 | ||||
| -rw-r--r-- | tests/ui/parser/emoji-identifiers.stderr | 6 | ||||
| -rw-r--r-- | tests/ui/resolve/fn-new-doesnt-exist.rs | 5 | ||||
| -rw-r--r-- | tests/ui/resolve/fn-new-doesnt-exist.stderr | 14 | ||||
| -rw-r--r-- | tests/ui/resolve/issue-82865.stderr | 7 | ||||
| -rw-r--r-- | tests/ui/suggestions/deref-path-method.stderr | 7 | ||||
| -rw-r--r-- | tests/ui/suggestions/issue-109291.stderr | 7 |
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 |
