Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When not finding assoc fn on type, look for builder fn #117006

Merged
merged 1 commit into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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}>`, \
Expand Down Expand Up @@ -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(
Expand Down
4 changes: 4 additions & 0 deletions tests/ui/atomic-from-mut-not-available.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
6 changes: 6 additions & 0 deletions tests/ui/parser/emoji-identifiers.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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 `👀`
compiler-errors marked this conversation as resolved.
Show resolved Hide resolved
--> $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
Expand Down
5 changes: 5 additions & 0 deletions tests/ui/resolve/fn-new-doesnt-exist.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use std::net::TcpStream;

fn main() {
let stream = TcpStream::new(); //~ ERROR no function or associated item named `new` found
}
14 changes: 14 additions & 0 deletions tests/ui/resolve/fn-new-doesnt-exist.stderr
Original file line number Diff line number Diff line change
@@ -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`.
7 changes: 7 additions & 0 deletions tests/ui/resolve/issue-82865.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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:
estebank marked this conversation as resolved.
Show resolved Hide resolved
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
Expand Down
7 changes: 7 additions & 0 deletions tests/ui/suggestions/deref-path-method.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
7 changes: 7 additions & 0 deletions tests/ui/suggestions/issue-109291.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Loading