]> git.lizzy.rs Git - rust.git/commitdiff
Add ability to attach custom #[on_unimplemented] error messages for unimplemented...
authorManish Goregaokar <manishsmail@gmail.com>
Sat, 10 Jan 2015 18:21:27 +0000 (23:51 +0530)
committerManish Goregaokar <manishsmail@gmail.com>
Sun, 11 Jan 2015 04:19:02 +0000 (09:49 +0530)
src/libcore/iter.rs
src/librustc/lib.rs
src/librustc/lint/builtin.rs
src/librustc/middle/traits/error_reporting.rs
src/test/compile-fail/on-unimplemented.rs [new file with mode: 0644]

index d4aa4c99a76bd48f20c5d1cc8e97a1e2047f5781..e22de10a974f02a7df795881b62c5c48418099d9 100644 (file)
@@ -101,6 +101,8 @@ fn size_hint(&self) -> (uint, Option<uint>) { (0, None) }
 
 /// Conversion from an `Iterator`
 #[stable]
+#[on_unimplemented="a collection of type `{Self}` cannot be \
+                    built from an iterator over elements of type `{A}`"]
 pub trait FromIterator<A> {
     /// Build a container with elements from an external iterator.
     fn from_iter<T: Iterator<Item=A>>(iterator: T) -> Self;
index fb7c5296d020e38ac637f6e7cc9ffbd0f5d2a59e..e720a5df5980741b9b0d8046dce765017a6bbb1d 100644 (file)
@@ -32,6 +32,7 @@
 
 extern crate arena;
 extern crate flate;
+extern crate fmt_macros;
 extern crate getopts;
 extern crate graphviz;
 extern crate libc;
index 620b8f277dddcd95a5a7959a96b6c31699d91d24..300b9aaf10060cf02b68f92c3786cda435dd2ba1 100644 (file)
@@ -666,6 +666,7 @@ fn check_attribute(&mut self, cx: &Context, attr: &ast::Attribute) {
             "must_use",
             "stable",
             "unstable",
+            "on_unimplemented",
 
             // FIXME: #19470 this shouldn't be needed forever
             "old_orphan_check",
index 02c913a9e81ae34991cb6405824558085ee6a936..8903667505b328f77cb9122289b541a54f873a57 100644 (file)
     SelectionError,
 };
 
+use fmt_macros::{Parser, Piece, Position};
 use middle::infer::InferCtxt;
-use middle::ty::{self, AsPredicate, ReferencesError, ToPolyTraitRef};
+use middle::ty::{self, AsPredicate, ReferencesError, ToPolyTraitRef, TraitRef};
+use std::collections::HashMap;
 use syntax::codemap::Span;
+use syntax::attr::{AttributeMethods, AttrMetaMethods};
 use util::ppaux::{Repr, UserString};
 
 pub fn report_fulfillment_errors<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
@@ -62,6 +65,69 @@ pub fn report_projection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
     }
 }
 
+fn report_on_unimplemented<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
+                                     trait_ref: &TraitRef<'tcx>) -> Option<String> {
+    let def_id = trait_ref.def_id;
+    let mut report = None;
+    ty::each_attr(infcx.tcx, def_id, |item| {
+        if item.check_name("on_unimplemented") {
+            if let Some(ref istring) = item.value_str() {
+                let def = ty::lookup_trait_def(infcx.tcx, def_id);
+                let mut generic_map = def.generics.types.iter_enumerated()
+                                         .map(|(param, i, gen)| {
+                                               (gen.name.as_str().to_string(),
+                                                trait_ref.substs.types.get(param, i)
+                                                         .user_string(infcx.tcx))
+                                              }).collect::<HashMap<String, String>>();
+                generic_map.insert("Self".to_string(),
+                                   trait_ref.self_ty().user_string(infcx.tcx));
+                let parser = Parser::new(istring.get());
+                let mut errored = false;
+                let err: String = parser.filter_map(|p| {
+                    match p {
+                        Piece::String(s) => Some(s),
+                        Piece::NextArgument(a) => match a.position {
+                            Position::ArgumentNamed(s) => match generic_map.get(s) {
+                                Some(val) => Some(val.as_slice()),
+                                None => {
+                                    infcx.tcx.sess
+                                         .span_err(item.meta().span,
+                                                   format!("there is no type parameter \
+                                                            {} on trait {}",
+                                                           s, def.trait_ref
+                                                                 .user_string(infcx.tcx))
+                                                   .as_slice());
+                                    errored = true;
+                                    None
+                                }
+                            },
+                            _ => {
+                                infcx.tcx.sess.span_err(item.meta().span,
+                                                        "only named substitution \
+                                                        parameters are allowed");
+                                errored = true;
+                                None
+                            }
+                        }
+                    }
+                }).collect();
+                // Report only if the format string checks out
+                if !errored {
+                    report = Some(err);
+                }
+            } else {
+                infcx.tcx.sess.span_err(item.meta().span,
+                                        "this attribute must have a value, \
+                                        eg `#[on_unimplemented = \"foo\"]`")
+            }
+            false
+        } else {
+            true
+        }
+    });
+    report
+}
+
 pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
                                         obligation: &PredicateObligation<'tcx>,
                                         error: &SelectionError<'tcx>)
@@ -88,12 +154,20 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
                         infcx.resolve_type_vars_if_possible(trait_predicate);
                     if !trait_predicate.references_error() {
                         let trait_ref = trait_predicate.to_poly_trait_ref();
+                        // Check if it has a custom "#[on_unimplemented]" error message,
+                        // report with that message if it does
+                        let custom_note = report_on_unimplemented(infcx, &*trait_ref.0);
                         infcx.tcx.sess.span_err(
                             obligation.cause.span,
                             format!(
                                 "the trait `{}` is not implemented for the type `{}`",
                                 trait_ref.user_string(infcx.tcx),
                                 trait_ref.self_ty().user_string(infcx.tcx)).as_slice());
+                        if let Some(s) = custom_note {
+                           infcx.tcx.sess.span_note(
+                                obligation.cause.span,
+                                s.as_slice());
+                        }
                     }
                 }
 
diff --git a/src/test/compile-fail/on-unimplemented.rs b/src/test/compile-fail/on-unimplemented.rs
new file mode 100644 (file)
index 0000000..5a56e91
--- /dev/null
@@ -0,0 +1,51 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+// ignore-tidy-linelength
+
+#[on_unimplemented = "test error `{Self}` with `{Bar}` `{Baz}` `{Quux}`"]
+trait Foo<Bar, Baz, Quux>{}
+
+fn foobar<U: Clone, T: Foo<u8, U, u32>>() -> T {
+
+}
+
+#[on_unimplemented="a collection of type `{Self}` cannot be built from an iterator over elements of type `{A}`"]
+trait MyFromIterator<A> {
+    /// Build a container with elements from an external iterator.
+    fn my_from_iter<T: Iterator<Item=A>>(iterator: T) -> Self;
+}
+
+fn collect<A, I: Iterator<Item=A>, B: MyFromIterator<A>>(it: I) -> B {
+    MyFromIterator::my_from_iter(it)
+}
+
+#[on_unimplemented] //~ ERROR this attribute must have a value
+trait BadAnnotation1 {}
+
+#[on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{C}>`"]
+//~^ ERROR there is no type parameter C on trait BadAnnotation2<A, B>
+trait BadAnnotation2<A,B> {}
+
+fn trigger1<T: BadAnnotation1>(t: T)  {}
+fn trigger2<A, B, T: BadAnnotation2<A,B>>(t: T) {}
+
+pub fn main() {
+    let x = vec!(1u8, 2, 3, 4);
+    let y: Option<Vec<u8>> = collect(x.iter()); // this should give approximately the same error for x.iter().collect()
+    //~^ ERROR
+    //~^^ NOTE a collection of type `core::option::Option<collections::vec::Vec<u8>>` cannot be built from an iterator over elements of type `&u8`
+    let x: String = foobar(); //~ ERROR
+    //~^ NOTE test error `collections::string::String` with `u8` `_` `u32`
+
+    // The following two have errors in their annotations, so the regular error should be thrown
+    trigger1(1u8); //~ ERROR the trait `BadAnnotation1` is not implemented for the type `u8`
+    trigger2::<u8, u8, u8>(1u8); //~ ERROR the trait `BadAnnotation2<u8, u8>` is not implemented
+
+}