From 273a78b05be72a4609d68740708d27fdeacd29f6 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Thu, 23 May 2024 07:23:59 +0530 Subject: [PATCH] Do not suggest unresolvable builder methods --- compiler/rustc_hir_typeck/src/expr.rs | 1 + .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 1 + .../rustc_hir_typeck/src/method/suggest.rs | 26 ++++++-- tests/ui/resolve/fn-new-doesnt-exist.rs | 5 -- tests/ui/resolve/fn-new-doesnt-exist.stderr | 14 ---- tests/ui/resolve/suggest-builder-fn.rs | 64 +++++++++++++++++++ tests/ui/resolve/suggest-builder-fn.stderr | 51 +++++++++++++++ tests/ui/suggestions/deref-path-method.stderr | 2 +- tests/ui/suggestions/issue-109291.stderr | 1 - tests/ui/ufcs/bad-builder.stderr | 2 +- 10 files changed, 141 insertions(+), 26 deletions(-) delete mode 100644 tests/ui/resolve/fn-new-doesnt-exist.rs delete mode 100644 tests/ui/resolve/fn-new-doesnt-exist.stderr create mode 100644 tests/ui/resolve/suggest-builder-fn.rs create mode 100644 tests/ui/resolve/suggest-builder-fn.stderr diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index fade943c5ae3d..9c3f62e85f581 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1349,6 +1349,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(rcvr), rcvr_t, segment.ident, + expr.hir_id, SelfSource::MethodCall(rcvr), error, Some(args), diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 77f90c0c1310c..3f67f0f5f0453 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -837,6 +837,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None, ty.normalized, item_name, + hir_id, SelfSource::QPath(qself), error, args, diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 54af8354c4c72..440dcbf089486 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -191,6 +191,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rcvr_opt: Option<&'tcx hir::Expr<'tcx>>, rcvr_ty: Ty<'tcx>, item_name: Ident, + expr_id: hir::HirId, source: SelfSource<'tcx>, error: MethodError<'tcx>, args: Option<&'tcx [hir::Expr<'tcx>]>, @@ -216,6 +217,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rcvr_opt, rcvr_ty, item_name, + expr_id, source, args, sugg_span, @@ -551,6 +553,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rcvr_opt: Option<&'tcx hir::Expr<'tcx>>, rcvr_ty: Ty<'tcx>, item_name: Ident, + expr_id: hir::HirId, source: SelfSource<'tcx>, args: Option<&'tcx [hir::Expr<'tcx>]>, sugg_span: Span, @@ -683,7 +686,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if matches!(source, SelfSource::QPath(_)) && args.is_some() { - self.find_builder_fn(&mut err, rcvr_ty); + self.find_builder_fn(&mut err, rcvr_ty, expr_id); } if tcx.ty_is_opaque_future(rcvr_ty) && item_name.name == sym::poll { @@ -1944,7 +1947,7 @@ 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` or `Result`. - fn find_builder_fn(&self, err: &mut Diag<'_>, rcvr_ty: Ty<'tcx>) { + fn find_builder_fn(&self, err: &mut Diag<'_>, rcvr_ty: Ty<'tcx>, expr_id: hir::HirId) { let ty::Adt(adt_def, _) = rcvr_ty.kind() else { return; }; @@ -1953,8 +1956,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut items = impls .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) + // Only assoc fn with no receivers and only if + // they are resolvable + .filter(|item| { + matches!(item.kind, ty::AssocKind::Fn) + && !item.fn_has_self_parameter + && self + .probe_for_name( + Mode::Path, + item.ident(self.tcx), + None, + IsSuggestion(true), + rcvr_ty, + expr_id, + ProbeScope::TraitsInScope, + ) + .is_ok() + }) .filter_map(|item| { // Only assoc fns that return `Self`, `Option` or `Result`. let ret_ty = self diff --git a/tests/ui/resolve/fn-new-doesnt-exist.rs b/tests/ui/resolve/fn-new-doesnt-exist.rs deleted file mode 100644 index 4f6290808fc03..0000000000000 --- a/tests/ui/resolve/fn-new-doesnt-exist.rs +++ /dev/null @@ -1,5 +0,0 @@ -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 deleted file mode 100644 index 418dd9ea6b846..0000000000000 --- a/tests/ui/resolve/fn-new-doesnt-exist.stderr +++ /dev/null @@ -1,14 +0,0 @@ -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 1 previous error - -For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/resolve/suggest-builder-fn.rs b/tests/ui/resolve/suggest-builder-fn.rs new file mode 100644 index 0000000000000..0d9b35549a47d --- /dev/null +++ b/tests/ui/resolve/suggest-builder-fn.rs @@ -0,0 +1,64 @@ +// Tests that we suggest the right alternatives when +// a builder method cannot be resolved + +use std::net::TcpStream; + +trait SomeTrait {} + +struct Foo { + v : T +} + +impl Foo { + // Should not be suggested if constraint on the impl not met + fn new() -> Self { + Self { v: T::default() } + } +} + +struct Bar; + +impl Bar { + // Should be suggested + fn build() -> Self { + Self {} + } + + // Method with self can't be a builder. + // Should not be suggested + fn build_with_self(&self) -> Self { + Self {} + } +} + +mod SomeMod { + use Bar; + + impl Bar { + // Public method. Should be suggested + pub fn build_public() -> Self { + Self {} + } + + // Private method. Should not be suggested + fn build_private() -> Self { + Self {} + } + } +} + +fn main() { + // `new` not found on `TcpStream` and `connect` should be suggested + let _stream = TcpStream::new(); + //~^ ERROR no function or associated item named `new` found + + // Although `new` is found on `>` it should not be + // suggested because `u8` does not meet the `T: SomeTrait` constraint + let _foo = Foo::::new(); + //~^ ERROR the function or associated item `new` exists for struct `Foo`, but its trait bounds were not satisfied + + // Should suggest only `::build()` and `SomeMod::::build_public()`. + // Other methods should not suggested because they are private or are not a builder + let _bar = Bar::new(); + //~^ ERROR no function or associated item named `new` found +} diff --git a/tests/ui/resolve/suggest-builder-fn.stderr b/tests/ui/resolve/suggest-builder-fn.stderr new file mode 100644 index 0000000000000..9c5eed35ccff1 --- /dev/null +++ b/tests/ui/resolve/suggest-builder-fn.stderr @@ -0,0 +1,51 @@ +error[E0599]: no function or associated item named `new` found for struct `TcpStream` in the current scope + --> $DIR/suggest-builder-fn.rs:52:29 + | +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[E0599]: the function or associated item `new` exists for struct `Foo`, but its trait bounds were not satisfied + --> $DIR/suggest-builder-fn.rs:57:27 + | +LL | struct Foo { + | ------------- function or associated item `new` not found for this struct +... +LL | let _foo = Foo::::new(); + | ^^^ function or associated item cannot be called on `Foo` due to unsatisfied trait bounds + | +note: trait bound `u8: SomeTrait` was not satisfied + --> $DIR/suggest-builder-fn.rs:12:9 + | +LL | impl Foo { + | ^^^^^^^^^ ------ + | | + | unsatisfied trait bound introduced here + +error[E0599]: no function or associated item named `new` found for struct `Bar` in the current scope + --> $DIR/suggest-builder-fn.rs:62:21 + | +LL | struct Bar; + | ---------- function or associated item `new` not found for this struct +... +LL | let _bar = Bar::new(); + | ^^^ function or associated item not found in `Bar` + | +note: if you're trying to build a new `Bar` consider using one of the following associated functions: + Bar::build + SomeMod::::build_public + --> $DIR/suggest-builder-fn.rs:23:5 + | +LL | fn build() -> Self { + | ^^^^^^^^^^^^^^^^^^ +... +LL | pub fn build_public() -> Self { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/suggestions/deref-path-method.stderr b/tests/ui/suggestions/deref-path-method.stderr index bfcc2307fd7fb..b27d9aef06614 100644 --- a/tests/ui/suggestions/deref-path-method.stderr +++ b/tests/ui/suggestions/deref-path-method.stderr @@ -9,7 +9,7 @@ note: if you're trying to build a new `Vec<_, _>` consider using one of the foll Vec::::with_capacity Vec::::try_with_capacity Vec::::from_raw_parts - and 6 others + and 4 others --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL help: the function `contains` is implemented on `[_]` | diff --git a/tests/ui/suggestions/issue-109291.stderr b/tests/ui/suggestions/issue-109291.stderr index a173bbbb4900c..d4a9351af3a85 100644 --- a/tests/ui/suggestions/issue-109291.stderr +++ b/tests/ui/suggestions/issue-109291.stderr @@ -8,7 +8,6 @@ note: if you're trying to build a new `Backtrace` consider using one of the foll Backtrace::capture Backtrace::force_capture Backtrace::disabled - Backtrace::create --> $SRC_DIR/std/src/backtrace.rs:LL:COL help: there is an associated function `force_capture` with a similar name | diff --git a/tests/ui/ufcs/bad-builder.stderr b/tests/ui/ufcs/bad-builder.stderr index e466f94d0d842..9cfeb7a5d09d6 100644 --- a/tests/ui/ufcs/bad-builder.stderr +++ b/tests/ui/ufcs/bad-builder.stderr @@ -9,7 +9,7 @@ note: if you're trying to build a new `Vec` consider using one of the followi Vec::::with_capacity Vec::::try_with_capacity Vec::::from_raw_parts - and 6 others + and 4 others --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL help: there is an associated function `new` with a similar name |