Skip to content

Commit 22633fe

Browse files
committed
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.
1 parent cc705b8 commit 22633fe

File tree

8 files changed

+134
-0
lines changed

8 files changed

+134
-0
lines changed

compiler/rustc_hir_typeck/src/method/suggest.rs

+80
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,86 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
409409
err.downgrade_to_delayed_bug();
410410
}
411411

412+
if let (ty::Adt(adt_def, _), SelfSource::QPath(_)) = (rcvr_ty.kind(), source) {
413+
// Look at all the associated functions without receivers in the type's inherent impls
414+
// to look for builders that return `Self`, `Option<Self>` or `Result<Self, _>`.
415+
let mut items = self
416+
.tcx
417+
.inherent_impls(adt_def.did())
418+
.iter()
419+
.flat_map(|i| self.tcx.associated_items(i).in_definition_order())
420+
.filter(|item| {
421+
// Only assoc fn with no receivers.
422+
matches!(item.kind, ty::AssocKind::Fn) && !item.fn_has_self_parameter
423+
})
424+
.filter_map(|item| {
425+
// Only assoc fns that return `Self`, `Option<Self>` or `Result<Self, _>`.
426+
let ret_ty = self.tcx.fn_sig(item.def_id).skip_binder().output();
427+
let ret_ty = self.tcx.erase_late_bound_regions(ret_ty);
428+
let ty::Adt(def, args) = ret_ty.kind() else {
429+
return None;
430+
};
431+
// Check for `-> Self`
432+
if self.can_eq(self.param_env, ret_ty, rcvr_ty) {
433+
return Some((item.def_id, ret_ty));
434+
}
435+
// Check for `-> Option<Self>` or `-> Result<Self, _>`
436+
if ![
437+
self.tcx.lang_items().option_type(),
438+
self.tcx.get_diagnostic_item(sym::Result),
439+
]
440+
.contains(&Some(def.did()))
441+
{
442+
return None;
443+
}
444+
let arg = args.get(0)?.expect_ty();
445+
if self.can_eq(self.param_env, rcvr_ty, arg) {
446+
Some((item.def_id, ret_ty))
447+
} else {
448+
None
449+
}
450+
})
451+
.collect::<Vec<_>>();
452+
let post = if items.len() > 5 {
453+
let items_len = items.len();
454+
items.truncate(4);
455+
format!("\nand {} others", items_len - 4)
456+
} else {
457+
String::new()
458+
};
459+
match &items[..] {
460+
[] => {}
461+
[(def_id, ret_ty)] => {
462+
err.span_note(
463+
self.tcx.def_span(def_id),
464+
format!(
465+
"if you're trying to build a new `{rcvr_ty}`, consider using `{}` \
466+
which returns `{ret_ty}`",
467+
self.tcx.def_path_str(def_id),
468+
),
469+
);
470+
}
471+
_ => {
472+
let span: MultiSpan = items
473+
.iter()
474+
.map(|(def_id, _)| self.tcx.def_span(def_id))
475+
.collect::<Vec<Span>>()
476+
.into();
477+
err.span_note(
478+
span,
479+
format!(
480+
"if you're trying to build a new `{rcvr_ty}` consider using one of the \
481+
following associated functions:\n{}{post}",
482+
items
483+
.iter()
484+
.map(|(def_id, _ret_ty)| self.tcx.def_path_str(def_id))
485+
.collect::<Vec<String>>()
486+
.join("\n")
487+
),
488+
);
489+
}
490+
}
491+
};
412492
if tcx.ty_is_opaque_future(rcvr_ty) && item_name.name == sym::poll {
413493
err.help(format!(
414494
"method `poll` found on `Pin<&mut {ty_str}>`, \

tests/ui/issues/issue-42880.stderr

+8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ error[E0599]: no associated item named `String` found for struct `String` in the
33
|
44
LL | let f = |&Value::String(_)| ();
55
| ^^^^^^ associated item not found in `String`
6+
|
7+
note: if you're trying to build a new `String` consider using one of the following associated functions:
8+
String::new
9+
String::with_capacity
10+
String::from_utf8
11+
String::from_utf16
12+
and 7 others
13+
--> $SRC_DIR/alloc/src/string.rs:LL:COL
614

715
error: aborting due to previous error
816

tests/ui/parser/emoji-identifiers.stderr

+6
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ LL | 👀::full_of✨()
7575
| |
7676
| function or associated item not found in `👀`
7777
| help: there is an associated function with a similar name: `full_of_✨`
78+
|
79+
note: if you're trying to build a new `👀`, consider using `👀::full_of_✨` which returns `👀`
80+
--> $DIR/emoji-identifiers.rs:4:5
81+
|
82+
LL | fn full_of_✨() -> 👀 {
83+
| ^^^^^^^^^^^^^^^^^^^^^
7884

7985
error[E0425]: cannot find function `i_like_to_😄_a_lot` in this scope
8086
--> $DIR/emoji-identifiers.rs:13:13
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
use std::net::TcpStream;
2+
3+
fn main() {
4+
let stream = TcpStream::new(); //~ ERROR no function or associated item named `new` found
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0599]: no function or associated item named `new` found for struct `TcpStream` in the current scope
2+
--> $DIR/fn-new-doesnt-exist.rs:4:28
3+
|
4+
LL | let stream = TcpStream::new();
5+
| ^^^ function or associated item not found in `TcpStream`
6+
|
7+
note: if you're trying to build a new `TcpStream` consider using one of the following associated functions:
8+
TcpStream::connect
9+
TcpStream::connect_timeout
10+
--> $SRC_DIR/std/src/net/tcp.rs:LL:COL
11+
12+
error: aborting due to previous error
13+
14+
For more information about this error, try `rustc --explain E0599`.

tests/ui/resolve/issue-82865.stderr

+7
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ LL | Box::z
1515
LL | mac!();
1616
| ------ in this macro invocation
1717
|
18+
note: if you're trying to build a new `Box<_, _>` consider using one of the following associated functions:
19+
Box::<T>::new
20+
Box::<T>::new_uninit
21+
Box::<T>::new_zeroed
22+
Box::<T>::try_new
23+
and 18 others
24+
--> $SRC_DIR/alloc/src/boxed.rs:LL:COL
1825
= note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info)
1926

2027
error: aborting due to 2 previous errors

tests/ui/suggestions/deref-path-method.stderr

+7
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ error[E0599]: no function or associated item named `contains` found for struct `
44
LL | Vec::contains(&vec, &0);
55
| ^^^^^^^^ function or associated item not found in `Vec<_, _>`
66
|
7+
note: if you're trying to build a new `Vec<_, _>` consider using one of the following associated functions:
8+
Vec::<T>::new
9+
Vec::<T>::with_capacity
10+
Vec::<T>::from_raw_parts
11+
Vec::<T, A>::new_in
12+
and 2 others
13+
--> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
714
help: the function `contains` is implemented on `[_]`
815
|
916
LL | <[_]>::contains(&vec, &0);

tests/ui/suggestions/issue-109291.stderr

+7
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ LL | println!("Custom backtrace: {}", std::backtrace::Backtrace::forced_capt
66
| |
77
| function or associated item not found in `Backtrace`
88
| help: there is an associated function with a similar name: `force_capture`
9+
|
10+
note: if you're trying to build a new `Backtrace` consider using one of the following associated functions:
11+
Backtrace::capture
12+
Backtrace::force_capture
13+
Backtrace::disabled
14+
Backtrace::create
15+
--> $SRC_DIR/std/src/backtrace.rs:LL:COL
916

1017
error: aborting due to previous error
1118

0 commit comments

Comments
 (0)