about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2020-01-19 17:27:37 -0800
committerEsteban Küber <esteban@kuber.com.ar>2020-02-02 11:52:34 -0800
commitd72bcdb42cc10a20c2eef49e5f8cd2782f44b922 (patch)
tree9342efd73bc0a1d7a35281d8e3258bfbceed6b8d
parent1c9242f83fdea3e4c7a452d1453370ee81a900af (diff)
downloadrust-d72bcdb42cc10a20c2eef49e5f8cd2782f44b922.tar.gz
rust-d72bcdb42cc10a20c2eef49e5f8cd2782f44b922.zip
When object unsafe trait uses itself in associated item suggest using `Self`
-rw-r--r--src/librustc_typeck/check/wfcheck.rs69
-rw-r--r--src/test/ui/suggestions/object-unsafe-trait-should-use-self.rs16
-rw-r--r--src/test/ui/suggestions/object-unsafe-trait-should-use-self.stderr47
3 files changed, 130 insertions, 2 deletions
diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs
index ef3dcf15873..faeaedce8d0 100644
--- a/src/librustc_typeck/check/wfcheck.rs
+++ b/src/librustc_typeck/check/wfcheck.rs
@@ -10,10 +10,10 @@ use rustc::ty::{
     self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness,
 };
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::{struct_span_err, DiagnosticBuilder};
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
 use rustc_hir::def_id::DefId;
 use rustc_hir::ItemKind;
-use rustc_span::symbol::sym;
+use rustc_span::symbol::{sym, Ident};
 use rustc_span::Span;
 use syntax::ast;
 
@@ -176,9 +176,74 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: DefId) {
         hir::TraitItemKind::Method(ref sig, _) => Some(sig),
         _ => None,
     };
+    check_bare_self_trait_by_name(tcx, &trait_item);
     check_associated_item(tcx, trait_item.hir_id, trait_item.span, method_sig);
 }
 
+fn could_be_self(trait_name: Ident, ty: &hir::Ty<'_>) -> bool {
+    match ty.kind {
+        hir::TyKind::TraitObject([trait_ref], ..) => {
+            let mut p = trait_ref.trait_ref.path.segments.iter().map(|s| s.ident);
+            match (p.next(), p.next()) {
+                (Some(ident), None) => ident == trait_name,
+                _ => false,
+            }
+        }
+        _ => false,
+    }
+}
+
+/// Detect when an object unsafe trait is referring to itself in one of its associated items.
+/// When this is done, suggest using `Self` instead.
+fn check_bare_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem<'_>) {
+    let (trait_name, trait_def_id) = match tcx.hir().get(tcx.hir().get_parent_item(item.hir_id)) {
+        hir::Node::Item(item) => match item.kind {
+            hir::ItemKind::Trait(..) => (item.ident, tcx.hir().local_def_id(item.hir_id)),
+            _ => return,
+        },
+        _ => return,
+    };
+    let mut trait_should_be_self = vec![];
+    match &item.kind {
+        hir::TraitItemKind::Const(ty, _) | hir::TraitItemKind::Type(_, Some(ty))
+            if could_be_self(trait_name, ty) =>
+        {
+            trait_should_be_self.push(ty.span)
+        }
+        hir::TraitItemKind::Method(sig, _) => {
+            for ty in sig.decl.inputs {
+                if could_be_self(trait_name, ty) {
+                    trait_should_be_self.push(ty.span);
+                }
+            }
+            match sig.decl.output {
+                hir::FunctionRetTy::Return(ty) if could_be_self(trait_name, ty) => {
+                    trait_should_be_self.push(ty.span);
+                }
+                _ => {}
+            }
+        }
+        _ => {}
+    }
+    if !trait_should_be_self.is_empty() {
+        if rustc::traits::object_safety_violations(tcx, trait_def_id).is_empty() {
+            return;
+        }
+        let sugg = trait_should_be_self.iter().map(|span| (*span, "Self".to_string())).collect();
+        let mut err = tcx.sess.struct_span_err(
+            trait_should_be_self,
+            "associated item referring to unboxed trait object for its own trait",
+        );
+        err.span_label(trait_name.span, "in this trait");
+        err.multipart_suggestion(
+            "you might have meant to use `Self` to refer to the materialized type",
+            sugg,
+            Applicability::MachineApplicable,
+        );
+        err.emit();
+    }
+}
+
 pub fn check_impl_item(tcx: TyCtxt<'_>, def_id: DefId) {
     let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
     let impl_item = tcx.hir().expect_impl_item(hir_id);
diff --git a/src/test/ui/suggestions/object-unsafe-trait-should-use-self.rs b/src/test/ui/suggestions/object-unsafe-trait-should-use-self.rs
new file mode 100644
index 00000000000..75f99075eb1
--- /dev/null
+++ b/src/test/ui/suggestions/object-unsafe-trait-should-use-self.rs
@@ -0,0 +1,16 @@
+#![allow(bare_trait_objects)]
+trait A: Sized {
+    fn f(a: A) -> A;
+    //~^ ERROR associated item referring to unboxed trait object for its own trait
+    //~| ERROR the trait `A` cannot be made into an object
+}
+trait B {
+    fn f(a: B) -> B;
+    //~^ ERROR associated item referring to unboxed trait object for its own trait
+    //~| ERROR the trait `B` cannot be made into an object
+}
+trait C {
+    fn f(&self, a: C) -> C;
+}
+
+fn main() {}
diff --git a/src/test/ui/suggestions/object-unsafe-trait-should-use-self.stderr b/src/test/ui/suggestions/object-unsafe-trait-should-use-self.stderr
new file mode 100644
index 00000000000..70d069d2aa2
--- /dev/null
+++ b/src/test/ui/suggestions/object-unsafe-trait-should-use-self.stderr
@@ -0,0 +1,47 @@
+error: associated item referring to unboxed trait object for its own trait
+  --> $DIR/object-unsafe-trait-should-use-self.rs:3:13
+   |
+LL | trait A: Sized {
+   |       - in this trait
+LL |     fn f(a: A) -> A;
+   |             ^     ^
+   |
+help: you might have meant to use `Self` to refer to the materialized type
+   |
+LL |     fn f(a: Self) -> Self;
+   |             ^^^^     ^^^^
+
+error[E0038]: the trait `A` cannot be made into an object
+  --> $DIR/object-unsafe-trait-should-use-self.rs:3:13
+   |
+LL | trait A: Sized {
+   |          ----- the trait cannot require that `Self : Sized`
+LL |     fn f(a: A) -> A;
+   |             ^ the trait `A` cannot be made into an object
+   |
+   = note: the trait cannot require that `Self : Sized`
+
+error: associated item referring to unboxed trait object for its own trait
+  --> $DIR/object-unsafe-trait-should-use-self.rs:8:13
+   |
+LL | trait B {
+   |       - in this trait
+LL |     fn f(a: B) -> B;
+   |             ^     ^
+   |
+help: you might have meant to use `Self` to refer to the materialized type
+   |
+LL |     fn f(a: Self) -> Self;
+   |             ^^^^     ^^^^
+
+error[E0038]: the trait `B` cannot be made into an object
+  --> $DIR/object-unsafe-trait-should-use-self.rs:8:13
+   |
+LL |     fn f(a: B) -> B;
+   |        -    ^ the trait `B` cannot be made into an object
+   |        |
+   |        associated function `f` has no `self` parameter
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0038`.