From 4d17fbaf37a0641894317f016244943af66ce87b Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sat, 10 Jan 2015 23:51:27 +0530 Subject: [PATCH] Add ability to attach custom #[on_unimplemented] error messages for unimplemented traits (fixes #20783) --- src/libcore/iter.rs | 2 + src/librustc/lib.rs | 1 + src/librustc/lint/builtin.rs | 1 + src/librustc/middle/traits/error_reporting.rs | 76 ++++++++++++++++++- src/test/compile-fail/on-unimplemented.rs | 51 +++++++++++++ 5 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 src/test/compile-fail/on-unimplemented.rs diff --git a/src/libcore/iter.rs b/src/libcore/iter.rs index d4aa4c99a76b..e22de10a974f 100644 --- a/src/libcore/iter.rs +++ b/src/libcore/iter.rs @@ -101,6 +101,8 @@ pub trait Iterator { /// 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 { /// Build a container with elements from an external iterator. fn from_iter>(iterator: T) -> Self; diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index fb7c5296d020..e720a5df5980 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 620b8f277ddd..300b9aaf1006 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", + "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 02c913a9e81a..8903667505b3 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 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 { + 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::>(); + 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 index 000000000000..5a56e91cdda8 --- /dev/null +++ b/src/test/compile-fail/on-unimplemented.rs @@ -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 or the MIT license +// , 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{} + +fn foobar>() -> T { + +} + +#[on_unimplemented="a collection of type `{Self}` cannot be built from an iterator over elements of type `{A}`"] +trait MyFromIterator { + /// Build a container with elements from an external iterator. + fn my_from_iter>(iterator: T) -> Self; +} + +fn collect, B: MyFromIterator>(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 +trait BadAnnotation2 {} + +fn trigger1(t: T) {} +fn trigger2>(t: T) {} + +pub fn main() { + let x = vec!(1u8, 2, 3, 4); + let y: Option> = collect(x.iter()); // this should give approximately the same error for x.iter().collect() + //~^ ERROR + //~^^ NOTE a collection of type `core::option::Option>` 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::(1u8); //~ ERROR the trait `BadAnnotation2` is not implemented + +}