about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGurinder Singh <frederick.the.fool@gmail.com>2024-05-23 07:23:59 +0530
committerGurinder Singh <frederick.the.fool@gmail.com>2024-05-23 07:23:59 +0530
commit273a78b05be72a4609d68740708d27fdeacd29f6 (patch)
tree0e592bfe1d50455fcc8d73b324c707432ddacac5
parentb54dd08a84f3c07efbc2aaf63c3df219ae680a03 (diff)
downloadrust-273a78b05be72a4609d68740708d27fdeacd29f6.tar.gz
rust-273a78b05be72a4609d68740708d27fdeacd29f6.zip
Do not suggest unresolvable builder methods
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs1
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs1
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs26
-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/suggest-builder-fn.rs64
-rw-r--r--tests/ui/resolve/suggest-builder-fn.stderr51
-rw-r--r--tests/ui/suggestions/deref-path-method.stderr2
-rw-r--r--tests/ui/suggestions/issue-109291.stderr1
-rw-r--r--tests/ui/ufcs/bad-builder.stderr2
10 files changed, 141 insertions, 26 deletions
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index fade943c5ae..9c3f62e85f5 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 77f90c0c131..3f67f0f5f04 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 54af8354c4c..440dcbf0894 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<Self>` or `Result<Self, _>`.
-    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<Self>` or `Result<Self, _>`.
                 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 4f6290808fc..00000000000
--- 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 418dd9ea6b8..00000000000
--- 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 00000000000..0d9b35549a4
--- /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<T> {
+    v : T
+}
+
+impl<T: SomeTrait + Default> Foo<T> {
+    // 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 `<impl Foo<T>>` it should not be
+    // suggested because `u8` does not meet the `T: SomeTrait` constraint
+    let _foo = Foo::<u8>::new();
+   //~^ ERROR the function or associated item `new` exists for struct `Foo<u8>`, but its trait bounds were not satisfied
+
+   // Should suggest only `<impl Bar>::build()` and `SomeMod::<impl Bar>::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 00000000000..9c5eed35ccf
--- /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<u8>`, but its trait bounds were not satisfied
+  --> $DIR/suggest-builder-fn.rs:57:27
+   |
+LL | struct Foo<T> {
+   | ------------- function or associated item `new` not found for this struct
+...
+LL |     let _foo = Foo::<u8>::new();
+   |                           ^^^ function or associated item cannot be called on `Foo<u8>` due to unsatisfied trait bounds
+   |
+note: trait bound `u8: SomeTrait` was not satisfied
+  --> $DIR/suggest-builder-fn.rs:12:9
+   |
+LL | impl<T: SomeTrait + Default> Foo<T> {
+   |         ^^^^^^^^^            ------
+   |         |
+   |         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::<impl Bar>::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 bfcc2307fd7..b27d9aef066 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::<T>::with_capacity
       Vec::<T>::try_with_capacity
       Vec::<T>::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 a173bbbb490..d4a9351af3a 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 e466f94d0d8..9cfeb7a5d09 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<Q>` consider using one of the followi
       Vec::<T>::with_capacity
       Vec::<T>::try_with_capacity
       Vec::<T>::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
    |