about summary refs log tree commit diff
path: root/src/librustc
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2015-01-12 04:45:18 +0000
committerbors <bors@rust-lang.org>2015-01-12 04:45:18 +0000
commit0aec4db1c09574da2f30e3844de6d252d79d4939 (patch)
tree64ef8983eb848612559f373ab7069e033672745f /src/librustc
parent15a41380c14e94052332360b104ec1651f97297b (diff)
parent02d0a8bbcf0a64339e4279c4ddb4841189ba5069 (diff)
auto merge of #20889 : Manishearth/rust/trait-error, r=nikomatsakis
fixes #20783

r? @nikomatsakis
Diffstat (limited to 'src/librustc')
-rw-r--r--src/librustc/lib.rs1
-rw-r--r--src/librustc/lint/builtin.rs1
-rw-r--r--src/librustc/middle/traits/error_reporting.rs94
3 files changed, 94 insertions, 2 deletions
diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs
index fb7c5296d02..e720a5df598 100644
--- a/src/librustc/lib.rs
+++ b/src/librustc/lib.rs
@@ -32,6 +32,7 @@
 
 extern crate arena;
 extern crate flate;
+extern crate fmt_macros;
 extern crate getopts;
 extern crate graphviz;
 extern crate libc;
diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs
index 620b8f277dd..8ed177c82a8 100644
--- a/src/librustc/lint/builtin.rs
+++ b/src/librustc/lint/builtin.rs
@@ -666,6 +666,7 @@ impl LintPass for UnusedAttributes {
             "must_use",
             "stable",
             "unstable",
+            "rustc_on_unimplemented",
 
             // FIXME: #19470 this shouldn't be needed forever
             "old_orphan_check",
diff --git a/src/librustc/middle/traits/error_reporting.rs b/src/librustc/middle/traits/error_reporting.rs
index 02c913a9e81..6b4dd101286 100644
--- a/src/librustc/middle/traits/error_reporting.rs
+++ b/src/librustc/middle/traits/error_reporting.rs
@@ -18,9 +18,12 @@ use super::{
     SelectionError,
 };
 
+use fmt_macros::{Parser, Piece, Position};
 use middle::infer::InferCtxt;
-use middle::ty::{self, AsPredicate, ReferencesError, ToPolyTraitRef};
-use syntax::codemap::Span;
+use middle::ty::{self, AsPredicate, ReferencesError, ToPolyTraitRef, TraitRef};
+use std::collections::HashMap;
+use syntax::codemap::{DUMMY_SP, 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,85 @@ 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>,
+                                     span: Span) -> 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("rustc_on_unimplemented") {
+            let err_sp = if item.meta().span == DUMMY_SP {
+                span
+            } else {
+                item.meta().span
+            };
+            let def = ty::lookup_trait_def(infcx.tcx, def_id);
+            let trait_str = def.trait_ref.user_string(infcx.tcx);
+            if let Some(ref istring) = item.value_str() {
+                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(err_sp,
+                                                   format!("the #[rustc_on_unimplemented] \
+                                                            attribute on \
+                                                            trait definition for {} refers to \
+                                                            non-existent type parameter {}",
+                                                           trait_str, s)
+                                                   .as_slice());
+                                    errored = true;
+                                    None
+                                }
+                            },
+                            _ => {
+                                infcx.tcx.sess
+                                     .span_err(err_sp,
+                                               format!("the #[rustc_on_unimplemented] \
+                                                        attribute on \
+                                                        trait definition for {} must have named \
+                                                        format arguments, \
+                                                        eg `#[rustc_on_unimplemented = \
+                                                        \"foo {{T}}\"]`",
+                                                       trait_str).as_slice());
+                                errored = true;
+                                None
+                            }
+                        }
+                    }
+                }).collect();
+                // Report only if the format string checks out
+                if !errored {
+                    report = Some(err);
+                }
+            } else {
+                infcx.tcx.sess.span_err(err_sp,
+                                        format!("the #[rustc_on_unimplemented] attribute on \
+                                                 trait definition for {} must have a value, \
+                                                 eg `#[rustc_on_unimplemented = \"foo\"]`",
+                                                 trait_str).as_slice());
+            }
+            false
+        } else {
+            true
+        }
+    });
+    report
+}
+
 pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
                                         obligation: &PredicateObligation<'tcx>,
                                         error: &SelectionError<'tcx>)
@@ -94,6 +176,14 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
                                 "the trait `{}` is not implemented for the type `{}`",
                                 trait_ref.user_string(infcx.tcx),
                                 trait_ref.self_ty().user_string(infcx.tcx)).as_slice());
+                        // Check if it has a custom "#[rustc_on_unimplemented]" error message,
+                        // report with that message if it does
+                        let custom_note = report_on_unimplemented(infcx, &*trait_ref.0,
+                                                                  obligation.cause.span);
+                        if let Some(s) = custom_note {
+                           infcx.tcx.sess.span_note(obligation.cause.span,
+                                                    s.as_slice());
+                        }
                     }
                 }