Skip to content

Commit 4d17fba

Browse files
committed
Add ability to attach custom #[on_unimplemented] error messages for unimplemented traits (fixes #20783)
1 parent 14f9d1f commit 4d17fba

File tree

5 files changed

+130
-1
lines changed

5 files changed

+130
-1
lines changed

src/libcore/iter.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ pub trait Iterator {
101101

102102
/// Conversion from an `Iterator`
103103
#[stable]
104+
#[on_unimplemented="a collection of type `{Self}` cannot be \
105+
built from an iterator over elements of type `{A}`"]
104106
pub trait FromIterator<A> {
105107
/// Build a container with elements from an external iterator.
106108
fn from_iter<T: Iterator<Item=A>>(iterator: T) -> Self;

src/librustc/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
extern crate arena;
3434
extern crate flate;
35+
extern crate fmt_macros;
3536
extern crate getopts;
3637
extern crate graphviz;
3738
extern crate libc;

src/librustc/lint/builtin.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,7 @@ impl LintPass for UnusedAttributes {
666666
"must_use",
667667
"stable",
668668
"unstable",
669+
"on_unimplemented",
669670

670671
// FIXME: #19470 this shouldn't be needed forever
671672
"old_orphan_check",

src/librustc/middle/traits/error_reporting.rs

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@ use super::{
1818
SelectionError,
1919
};
2020

21+
use fmt_macros::{Parser, Piece, Position};
2122
use middle::infer::InferCtxt;
22-
use middle::ty::{self, AsPredicate, ReferencesError, ToPolyTraitRef};
23+
use middle::ty::{self, AsPredicate, ReferencesError, ToPolyTraitRef, TraitRef};
24+
use std::collections::HashMap;
2325
use syntax::codemap::Span;
26+
use syntax::attr::{AttributeMethods, AttrMetaMethods};
2427
use util::ppaux::{Repr, UserString};
2528

2629
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>,
6265
}
6366
}
6467

68+
fn report_on_unimplemented<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
69+
trait_ref: &TraitRef<'tcx>) -> Option<String> {
70+
let def_id = trait_ref.def_id;
71+
let mut report = None;
72+
ty::each_attr(infcx.tcx, def_id, |item| {
73+
if item.check_name("on_unimplemented") {
74+
if let Some(ref istring) = item.value_str() {
75+
let def = ty::lookup_trait_def(infcx.tcx, def_id);
76+
let mut generic_map = def.generics.types.iter_enumerated()
77+
.map(|(param, i, gen)| {
78+
(gen.name.as_str().to_string(),
79+
trait_ref.substs.types.get(param, i)
80+
.user_string(infcx.tcx))
81+
}).collect::<HashMap<String, String>>();
82+
generic_map.insert("Self".to_string(),
83+
trait_ref.self_ty().user_string(infcx.tcx));
84+
let parser = Parser::new(istring.get());
85+
let mut errored = false;
86+
let err: String = parser.filter_map(|p| {
87+
match p {
88+
Piece::String(s) => Some(s),
89+
Piece::NextArgument(a) => match a.position {
90+
Position::ArgumentNamed(s) => match generic_map.get(s) {
91+
Some(val) => Some(val.as_slice()),
92+
None => {
93+
infcx.tcx.sess
94+
.span_err(item.meta().span,
95+
format!("there is no type parameter \
96+
{} on trait {}",
97+
s, def.trait_ref
98+
.user_string(infcx.tcx))
99+
.as_slice());
100+
errored = true;
101+
None
102+
}
103+
},
104+
_ => {
105+
infcx.tcx.sess.span_err(item.meta().span,
106+
"only named substitution \
107+
parameters are allowed");
108+
errored = true;
109+
None
110+
}
111+
}
112+
}
113+
}).collect();
114+
// Report only if the format string checks out
115+
if !errored {
116+
report = Some(err);
117+
}
118+
} else {
119+
infcx.tcx.sess.span_err(item.meta().span,
120+
"this attribute must have a value, \
121+
eg `#[on_unimplemented = \"foo\"]`")
122+
}
123+
false
124+
} else {
125+
true
126+
}
127+
});
128+
report
129+
}
130+
65131
pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
66132
obligation: &PredicateObligation<'tcx>,
67133
error: &SelectionError<'tcx>)
@@ -88,12 +154,20 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
88154
infcx.resolve_type_vars_if_possible(trait_predicate);
89155
if !trait_predicate.references_error() {
90156
let trait_ref = trait_predicate.to_poly_trait_ref();
157+
// Check if it has a custom "#[on_unimplemented]" error message,
158+
// report with that message if it does
159+
let custom_note = report_on_unimplemented(infcx, &*trait_ref.0);
91160
infcx.tcx.sess.span_err(
92161
obligation.cause.span,
93162
format!(
94163
"the trait `{}` is not implemented for the type `{}`",
95164
trait_ref.user_string(infcx.tcx),
96165
trait_ref.self_ty().user_string(infcx.tcx)).as_slice());
166+
if let Some(s) = custom_note {
167+
infcx.tcx.sess.span_note(
168+
obligation.cause.span,
169+
s.as_slice());
170+
}
97171
}
98172
}
99173

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
// ignore-tidy-linelength
11+
12+
#[on_unimplemented = "test error `{Self}` with `{Bar}` `{Baz}` `{Quux}`"]
13+
trait Foo<Bar, Baz, Quux>{}
14+
15+
fn foobar<U: Clone, T: Foo<u8, U, u32>>() -> T {
16+
17+
}
18+
19+
#[on_unimplemented="a collection of type `{Self}` cannot be built from an iterator over elements of type `{A}`"]
20+
trait MyFromIterator<A> {
21+
/// Build a container with elements from an external iterator.
22+
fn my_from_iter<T: Iterator<Item=A>>(iterator: T) -> Self;
23+
}
24+
25+
fn collect<A, I: Iterator<Item=A>, B: MyFromIterator<A>>(it: I) -> B {
26+
MyFromIterator::my_from_iter(it)
27+
}
28+
29+
#[on_unimplemented] //~ ERROR this attribute must have a value
30+
trait BadAnnotation1 {}
31+
32+
#[on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{C}>`"]
33+
//~^ ERROR there is no type parameter C on trait BadAnnotation2<A, B>
34+
trait BadAnnotation2<A,B> {}
35+
36+
fn trigger1<T: BadAnnotation1>(t: T) {}
37+
fn trigger2<A, B, T: BadAnnotation2<A,B>>(t: T) {}
38+
39+
pub fn main() {
40+
let x = vec!(1u8, 2, 3, 4);
41+
let y: Option<Vec<u8>> = collect(x.iter()); // this should give approximately the same error for x.iter().collect()
42+
//~^ ERROR
43+
//~^^ NOTE a collection of type `core::option::Option<collections::vec::Vec<u8>>` cannot be built from an iterator over elements of type `&u8`
44+
let x: String = foobar(); //~ ERROR
45+
//~^ NOTE test error `collections::string::String` with `u8` `_` `u32`
46+
47+
// The following two have errors in their annotations, so the regular error should be thrown
48+
trigger1(1u8); //~ ERROR the trait `BadAnnotation1` is not implemented for the type `u8`
49+
trigger2::<u8, u8, u8>(1u8); //~ ERROR the trait `BadAnnotation2<u8, u8>` is not implemented
50+
51+
}

0 commit comments

Comments
 (0)