about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMazdak Farrokhzad <twingoow@gmail.com>2019-09-03 14:27:00 +0200
committerGitHub <noreply@github.com>2019-09-03 14:27:00 +0200
commit56d00e388b3e55352340b5c2235992fdaf1d26d7 (patch)
treeec0d7e234c2a9b9868014585c528d901a6065b1c
parent4245be7fb3d7a045fc1798df5cc64878a2b38aa6 (diff)
parent141f5a7558289acb6c7aaf9c6500eb9f6dbeec1a (diff)
Rollup merge of #64056 - estebank:arbitrary-self-types, r=Centril
Account for arbitrary self types in E0599

Fix https://github.com/rust-lang/rust/issues/62373
-rw-r--r--src/librustc/ty/context.rs6
-rw-r--r--src/librustc_typeck/check/expr.rs88
-rw-r--r--src/librustc_typeck/check/method/mod.rs2
-rw-r--r--src/librustc_typeck/check/method/suggest.rs9
-rw-r--r--src/librustc_typeck/check/mod.rs2
-rw-r--r--src/test/ui/impl-trait/no-method-suggested-traits.stderr8
-rw-r--r--src/test/ui/self/point-at-arbitrary-self-type-method.rs9
-rw-r--r--src/test/ui/self/point-at-arbitrary-self-type-method.stderr15
-rw-r--r--src/test/ui/self/point-at-arbitrary-self-type-trait-method.rs10
-rw-r--r--src/test/ui/self/point-at-arbitrary-self-type-trait-method.stderr18
-rw-r--r--src/test/ui/traits/trait-item-privacy.stderr7
11 files changed, 148 insertions, 26 deletions
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index e240e0df8b9..17c9e520bce 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -2403,6 +2403,12 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 
     #[inline]
+    pub fn mk_lang_item(self, ty: Ty<'tcx>, item: lang_items::LangItem)  -> Ty<'tcx> {
+        let def_id = self.require_lang_item(item, None);
+        self.mk_generic_adt(def_id, ty)
+    }
+
+    #[inline]
     pub fn mk_maybe_uninit(self, ty: Ty<'tcx>) -> Ty<'tcx> {
         let def_id = self.require_lang_item(lang_items::MaybeUninitLangItem, None);
         self.mk_generic_adt(def_id, ty)
diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs
index a53fb12367d..fbaa9904d83 100644
--- a/src/librustc_typeck/check/expr.rs
+++ b/src/librustc_typeck/check/expr.rs
@@ -12,7 +12,7 @@ use crate::check::fatally_break_rust;
 use crate::check::report_unexpected_variant_res;
 use crate::check::Needs;
 use crate::check::TupleArgumentsFlag::DontTupleArguments;
-use crate::check::method::SelfSource;
+use crate::check::method::{probe, SelfSource, MethodError};
 use crate::util::common::ErrorReported;
 use crate::util::nodemap::FxHashMap;
 use crate::astconv::AstConv as _;
@@ -29,6 +29,7 @@ use rustc::hir::def::{CtorKind, Res, DefKind};
 use rustc::hir::ptr::P;
 use rustc::infer;
 use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc::middle::lang_items;
 use rustc::mir::interpret::GlobalId;
 use rustc::ty;
 use rustc::ty::adjustment::{
@@ -775,35 +776,80 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // no need to check for bot/err -- callee does that
         let rcvr_t = self.structurally_resolved_type(args[0].span, rcvr_t);
 
-        let method = match self.lookup_method(rcvr_t,
-                                              segment,
-                                              span,
-                                              expr,
-                                              rcvr) {
+        let method = match self.lookup_method(rcvr_t, segment, span, expr, rcvr) {
             Ok(method) => {
                 self.write_method_call(expr.hir_id, method);
                 Ok(method)
             }
             Err(error) => {
                 if segment.ident.name != kw::Invalid {
-                    self.report_method_error(span,
-                                             rcvr_t,
-                                             segment.ident,
-                                             SelfSource::MethodCall(rcvr),
-                                             error,
-                                             Some(args));
+                    self.report_extended_method_error(segment, span, args, rcvr_t, error);
                 }
                 Err(())
             }
         };
 
         // Call the generic checker.
-        self.check_method_argument_types(span,
-                                         expr.span,
-                                         method,
-                                         &args[1..],
-                                         DontTupleArguments,
-                                         expected)
+        self.check_method_argument_types(
+            span,
+            expr.span,
+            method,
+            &args[1..],
+            DontTupleArguments,
+            expected,
+        )
+    }
+
+    fn report_extended_method_error(
+        &self,
+        segment: &hir::PathSegment,
+        span: Span,
+        args: &'tcx [hir::Expr],
+        rcvr_t: Ty<'tcx>,
+        error: MethodError<'tcx>
+    ) {
+        let rcvr = &args[0];
+        let try_alt_rcvr = |err: &mut DiagnosticBuilder<'_>, new_rcvr_t| {
+            if let Ok(pick) = self.lookup_probe(
+                span,
+                segment.ident,
+                new_rcvr_t,
+                rcvr,
+                probe::ProbeScope::AllTraits,
+            ) {
+                err.span_label(
+                    pick.item.ident.span,
+                    &format!("the method is available for `{}` here", new_rcvr_t),
+                );
+            }
+        };
+
+        if let Some(mut err) = self.report_method_error(
+            span,
+            rcvr_t,
+            segment.ident,
+            SelfSource::MethodCall(rcvr),
+            error,
+            Some(args),
+        ) {
+            if let ty::Adt(..) = rcvr_t.sty {
+                // Try alternative arbitrary self types that could fulfill this call.
+                // FIXME: probe for all types that *could* be arbitrary self-types, not
+                // just this whitelist.
+                let box_rcvr_t = self.tcx.mk_box(rcvr_t);
+                try_alt_rcvr(&mut err, box_rcvr_t);
+                let pin_rcvr_t = self.tcx.mk_lang_item(
+                    rcvr_t,
+                    lang_items::PinTypeLangItem,
+                );
+                try_alt_rcvr(&mut err, pin_rcvr_t);
+                let arc_rcvr_t = self.tcx.mk_lang_item(rcvr_t, lang_items::Arc);
+                try_alt_rcvr(&mut err, arc_rcvr_t);
+                let rc_rcvr_t = self.tcx.mk_lang_item(rcvr_t, lang_items::Rc);
+                try_alt_rcvr(&mut err, rc_rcvr_t);
+            }
+            err.emit();
+        }
     }
 
     fn check_expr_cast(
@@ -1466,8 +1512,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let struct_variant_def = def.non_enum_variant();
             let field_names = self.available_field_names(struct_variant_def);
             if !field_names.is_empty() {
-                err.note(&format!("available fields are: {}",
-                                    self.name_series_display(field_names)));
+                err.note(&format!(
+                    "available fields are: {}",
+                    self.name_series_display(field_names),
+                ));
             }
         }
     }
diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs
index a7e4f8e5c62..1509c0f8a21 100644
--- a/src/librustc_typeck/check/method/mod.rs
+++ b/src/librustc_typeck/check/method/mod.rs
@@ -245,7 +245,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         Ok(result.callee)
     }
 
-    fn lookup_probe(
+    pub fn lookup_probe(
         &self,
         span: Span,
         method_name: ast::Ident,
diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs
index 440e7e5d0e3..72e6f597159 100644
--- a/src/librustc_typeck/check/method/suggest.rs
+++ b/src/librustc_typeck/check/method/suggest.rs
@@ -69,12 +69,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         source: SelfSource<'b>,
         error: MethodError<'tcx>,
         args: Option<&'tcx [hir::Expr]>,
-    ) {
+    ) -> Option<DiagnosticBuilder<'_>> {
         let orig_span = span;
         let mut span = span;
         // Avoid suggestions when we don't know what's going on.
         if rcvr_ty.references_error() {
-            return;
+            return None;
         }
 
         let print_disambiguation_help = |
@@ -314,7 +314,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             _ => {}
                         }
                         err.emit();
-                        return;
+                        return None;
                     } else {
                         span = item_name.span;
                         let mut err = struct_span_err!(
@@ -529,7 +529,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     );
                 }
 
-                err.emit();
+                return Some(err);
             }
 
             MethodError::Ambiguity(sources) => {
@@ -573,6 +573,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 bug!("no return type expectations but got BadReturnType")
             }
         }
+        None
     }
 
     fn suggest_use_candidates(&self,
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index cd041e80e7c..16903304a14 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -3580,7 +3580,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     SelfSource::QPath(qself),
                     error,
                     None,
-                );
+                ).map(|mut e| e.emit());
             }
             result
         });
diff --git a/src/test/ui/impl-trait/no-method-suggested-traits.stderr b/src/test/ui/impl-trait/no-method-suggested-traits.stderr
index d980d7cccad..002b60f9f25 100644
--- a/src/test/ui/impl-trait/no-method-suggested-traits.stderr
+++ b/src/test/ui/impl-trait/no-method-suggested-traits.stderr
@@ -49,6 +49,14 @@ LL | use foo::Bar;
 error[E0599]: no method named `method` found for type `std::rc::Rc<&mut std::boxed::Box<&char>>` in the current scope
   --> $DIR/no-method-suggested-traits.rs:32:43
    |
+LL |         fn method(&self) {}
+   |            ------
+   |            |
+   |            the method is available for `std::boxed::Box<std::rc::Rc<&mut std::boxed::Box<&char>>>` here
+   |            the method is available for `std::pin::Pin<std::rc::Rc<&mut std::boxed::Box<&char>>>` here
+   |            the method is available for `std::sync::Arc<std::rc::Rc<&mut std::boxed::Box<&char>>>` here
+   |            the method is available for `std::rc::Rc<std::rc::Rc<&mut std::boxed::Box<&char>>>` here
+...
 LL |     std::rc::Rc::new(&mut Box::new(&'a')).method();
    |                                           ^^^^^^
    |
diff --git a/src/test/ui/self/point-at-arbitrary-self-type-method.rs b/src/test/ui/self/point-at-arbitrary-self-type-method.rs
new file mode 100644
index 00000000000..0f7deeacad2
--- /dev/null
+++ b/src/test/ui/self/point-at-arbitrary-self-type-method.rs
@@ -0,0 +1,9 @@
+struct A;
+
+impl A {
+    fn foo(self: Box<Self>) {}
+}
+
+fn main() {
+    A.foo(); //~ ERROR E0599
+}
diff --git a/src/test/ui/self/point-at-arbitrary-self-type-method.stderr b/src/test/ui/self/point-at-arbitrary-self-type-method.stderr
new file mode 100644
index 00000000000..06dad7caa67
--- /dev/null
+++ b/src/test/ui/self/point-at-arbitrary-self-type-method.stderr
@@ -0,0 +1,15 @@
+error[E0599]: no method named `foo` found for type `A` in the current scope
+  --> $DIR/point-at-arbitrary-self-type-method.rs:8:7
+   |
+LL | struct A;
+   | --------- method `foo` not found for this
+...
+LL |     fn foo(self: Box<Self>) {}
+   |        --- the method is available for `std::boxed::Box<A>` here
+...
+LL |     A.foo();
+   |       ^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.
diff --git a/src/test/ui/self/point-at-arbitrary-self-type-trait-method.rs b/src/test/ui/self/point-at-arbitrary-self-type-trait-method.rs
new file mode 100644
index 00000000000..53d99277118
--- /dev/null
+++ b/src/test/ui/self/point-at-arbitrary-self-type-trait-method.rs
@@ -0,0 +1,10 @@
+trait B { fn foo(self: Box<Self>); }
+struct A;
+
+impl B for A {
+    fn foo(self: Box<Self>) {}
+}
+
+fn main() {
+    A.foo() //~ ERROR E0599
+}
diff --git a/src/test/ui/self/point-at-arbitrary-self-type-trait-method.stderr b/src/test/ui/self/point-at-arbitrary-self-type-trait-method.stderr
new file mode 100644
index 00000000000..90cd3b80745
--- /dev/null
+++ b/src/test/ui/self/point-at-arbitrary-self-type-trait-method.stderr
@@ -0,0 +1,18 @@
+error[E0599]: no method named `foo` found for type `A` in the current scope
+  --> $DIR/point-at-arbitrary-self-type-trait-method.rs:9:7
+   |
+LL | trait B { fn foo(self: Box<Self>); }
+   |              --- the method is available for `std::boxed::Box<A>` here
+LL | struct A;
+   | --------- method `foo` not found for this
+...
+LL |     A.foo()
+   |       ^^^
+   |
+   = help: items from traits can only be used if the trait is implemented and in scope
+   = note: the following trait defines an item `foo`, perhaps you need to implement it:
+           candidate #1: `B`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.
diff --git a/src/test/ui/traits/trait-item-privacy.stderr b/src/test/ui/traits/trait-item-privacy.stderr
index de699a69fa8..ce2919c8e77 100644
--- a/src/test/ui/traits/trait-item-privacy.stderr
+++ b/src/test/ui/traits/trait-item-privacy.stderr
@@ -17,6 +17,13 @@ error[E0599]: no method named `b` found for type `S` in the current scope
 LL | struct S;
    | --------- method `b` not found for this
 ...
+LL |         fn b(&self) { }
+   |            -
+   |            |
+   |            the method is available for `std::boxed::Box<S>` here
+   |            the method is available for `std::sync::Arc<S>` here
+   |            the method is available for `std::rc::Rc<S>` here
+...
 LL |     S.b();
    |       ^
    |