Skip to content

Commit 0aec4db

Browse files
committed
auto merge of #20889 : Manishearth/rust/trait-error, r=nikomatsakis
fixes #20783 r? @nikomatsakis
2 parents 15a4138 + 02d0a8b commit 0aec4db

File tree

11 files changed

+234
-3
lines changed

11 files changed

+234
-3
lines changed

src/doc/reference.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2117,6 +2117,13 @@ macro scope.
21172117
destructors from being run twice. Destructors might be run multiple times on
21182118
the same object with this attribute.
21192119
- `doc` - Doc comments such as `/// foo` are equivalent to `#[doc = "foo"]`.
2120+
- `rustc_on_unimplemented` - Write a custom note to be shown along with the error
2121+
when the trait is found to be unimplemented on a type.
2122+
You may use format arguments like `{T}`, `{A}` to correspond to the
2123+
types at the point of use corresponding to the type parameters of the
2124+
trait of the same name. `{Self}` will be replaced with the type that is supposed
2125+
to implement the trait but doesn't. To use this, the `on_unimplemented` feature gate
2126+
must be enabled.
21202127

21212128
### Conditional compilation
21222129

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+
#[rustc_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/libcore/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
#![feature(simd, unsafe_destructor, slicing_syntax)]
6464
#![feature(unboxed_closures)]
6565
#![allow(unknown_features)] #![feature(int_uint)]
66+
#![feature(on_unimplemented)]
6667
#![deny(missing_docs)]
6768

6869
#[macro_use]

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+
"rustc_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: 92 additions & 2 deletions
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 syntax::codemap::Span;
23+
use middle::ty::{self, AsPredicate, ReferencesError, ToPolyTraitRef, TraitRef};
24+
use std::collections::HashMap;
25+
use syntax::codemap::{DUMMY_SP, 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,85 @@ 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>,
70+
span: Span) -> Option<String> {
71+
let def_id = trait_ref.def_id;
72+
let mut report = None;
73+
ty::each_attr(infcx.tcx, def_id, |item| {
74+
if item.check_name("rustc_on_unimplemented") {
75+
let err_sp = if item.meta().span == DUMMY_SP {
76+
span
77+
} else {
78+
item.meta().span
79+
};
80+
let def = ty::lookup_trait_def(infcx.tcx, def_id);
81+
let trait_str = def.trait_ref.user_string(infcx.tcx);
82+
if let Some(ref istring) = item.value_str() {
83+
let mut generic_map = def.generics.types.iter_enumerated()
84+
.map(|(param, i, gen)| {
85+
(gen.name.as_str().to_string(),
86+
trait_ref.substs.types.get(param, i)
87+
.user_string(infcx.tcx))
88+
}).collect::<HashMap<String, String>>();
89+
generic_map.insert("Self".to_string(),
90+
trait_ref.self_ty().user_string(infcx.tcx));
91+
let parser = Parser::new(istring.get());
92+
let mut errored = false;
93+
let err: String = parser.filter_map(|p| {
94+
match p {
95+
Piece::String(s) => Some(s),
96+
Piece::NextArgument(a) => match a.position {
97+
Position::ArgumentNamed(s) => match generic_map.get(s) {
98+
Some(val) => Some(val.as_slice()),
99+
None => {
100+
infcx.tcx.sess
101+
.span_err(err_sp,
102+
format!("the #[rustc_on_unimplemented] \
103+
attribute on \
104+
trait definition for {} refers to \
105+
non-existent type parameter {}",
106+
trait_str, s)
107+
.as_slice());
108+
errored = true;
109+
None
110+
}
111+
},
112+
_ => {
113+
infcx.tcx.sess
114+
.span_err(err_sp,
115+
format!("the #[rustc_on_unimplemented] \
116+
attribute on \
117+
trait definition for {} must have named \
118+
format arguments, \
119+
eg `#[rustc_on_unimplemented = \
120+
\"foo {{T}}\"]`",
121+
trait_str).as_slice());
122+
errored = true;
123+
None
124+
}
125+
}
126+
}
127+
}).collect();
128+
// Report only if the format string checks out
129+
if !errored {
130+
report = Some(err);
131+
}
132+
} else {
133+
infcx.tcx.sess.span_err(err_sp,
134+
format!("the #[rustc_on_unimplemented] attribute on \
135+
trait definition for {} must have a value, \
136+
eg `#[rustc_on_unimplemented = \"foo\"]`",
137+
trait_str).as_slice());
138+
}
139+
false
140+
} else {
141+
true
142+
}
143+
});
144+
report
145+
}
146+
65147
pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
66148
obligation: &PredicateObligation<'tcx>,
67149
error: &SelectionError<'tcx>)
@@ -94,6 +176,14 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
94176
"the trait `{}` is not implemented for the type `{}`",
95177
trait_ref.user_string(infcx.tcx),
96178
trait_ref.self_ty().user_string(infcx.tcx)).as_slice());
179+
// Check if it has a custom "#[rustc_on_unimplemented]" error message,
180+
// report with that message if it does
181+
let custom_note = report_on_unimplemented(infcx, &*trait_ref.0,
182+
obligation.cause.span);
183+
if let Some(s) = custom_note {
184+
infcx.tcx.sess.span_note(obligation.cause.span,
185+
s.as_slice());
186+
}
97187
}
98188
}
99189

src/librustc_typeck/check/mod.rs

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ use self::TupleArgumentsFlag::*;
8383

8484
use astconv::{self, ast_region_to_region, ast_ty_to_ty, AstConv};
8585
use check::_match::pat_ctxt;
86+
use fmt_macros::{Parser, Piece, Position};
8687
use middle::{const_eval, def};
8788
use middle::infer;
8889
use middle::lang_items::IteratorItem;
@@ -114,6 +115,7 @@ use std::rc::Rc;
114115
use std::iter::repeat;
115116
use std::slice;
116117
use syntax::{self, abi, attr};
118+
use syntax::attr::AttrMetaMethods;
117119
use syntax::ast::{self, ProvidedMethod, RequiredMethod, TypeTraitItem, DefId};
118120
use syntax::ast_util::{self, local_def, PostExpansionMethod};
119121
use syntax::codemap::{self, Span};
@@ -727,7 +729,8 @@ pub fn check_item(ccx: &CrateCtxt, it: &ast::Item) {
727729
}
728730

729731
}
730-
ast::ItemTrait(_, _, _, ref trait_methods) => {
732+
ast::ItemTrait(_, ref generics, _, ref trait_methods) => {
733+
check_trait_on_unimplemented(ccx, generics, it);
731734
let trait_def = ty::lookup_trait_def(ccx.tcx, local_def(it.id));
732735
for trait_method in trait_methods.iter() {
733736
match *trait_method {
@@ -777,6 +780,51 @@ pub fn check_item(ccx: &CrateCtxt, it: &ast::Item) {
777780
}
778781
}
779782

783+
fn check_trait_on_unimplemented<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
784+
generics: &ast::Generics,
785+
item: &ast::Item) {
786+
if let Some(ref attr) = item.attrs.iter().find(|&: a| {
787+
a.check_name("rustc_on_unimplemented")
788+
}) {
789+
if let Some(ref istring) = attr.value_str() {
790+
let mut parser = Parser::new(istring.get());
791+
let types = generics.ty_params.as_slice();
792+
for token in parser {
793+
match token {
794+
Piece::String(_) => (), // Normal string, no need to check it
795+
Piece::NextArgument(a) => match a.position {
796+
// `{Self}` is allowed
797+
Position::ArgumentNamed(s) if s == "Self" => (),
798+
// So is `{A}` if A is a type parameter
799+
Position::ArgumentNamed(s) => match types.iter().find(|t| {
800+
t.ident.as_str() == s
801+
}) {
802+
Some(_) => (),
803+
None => {
804+
ccx.tcx.sess.span_err(attr.span,
805+
format!("there is no type parameter \
806+
{} on trait {}",
807+
s, item.ident.as_str())
808+
.as_slice());
809+
}
810+
},
811+
// `{:1}` and `{}` are not to be used
812+
Position::ArgumentIs(_) | Position::ArgumentNext => {
813+
ccx.tcx.sess.span_err(attr.span,
814+
"only named substitution \
815+
parameters are allowed");
816+
}
817+
}
818+
}
819+
}
820+
} else {
821+
ccx.tcx.sess.span_err(attr.span,
822+
"this attribute must have a value, \
823+
eg `#[rustc_on_unimplemented = \"foo\"]`")
824+
}
825+
}
826+
}
827+
780828
/// Type checks a method body.
781829
///
782830
/// # Parameters

src/librustc_typeck/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ This API is completely unstable and subject to change.
8484
#[macro_use] extern crate syntax;
8585

8686
extern crate arena;
87+
extern crate fmt_macros;
8788
extern crate rustc;
8889

8990
pub use rustc::lint;

src/libsyntax/feature_gate.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
7171
("visible_private_types", Active),
7272
("slicing_syntax", Active),
7373
("box_syntax", Active),
74+
("on_unimplemented", Active),
7475

7576
("if_let", Accepted),
7677
("while_let", Accepted),
@@ -249,6 +250,10 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
249250
self.gate_feature("linkage", i.span,
250251
"the `linkage` attribute is experimental \
251252
and not portable across platforms")
253+
} else if attr.name() == "rustc_on_unimplemented" {
254+
self.gate_feature("on_unimplemented", i.span,
255+
"the `#[rustc_on_unimplemented]` attribute \
256+
is an experimental feature")
252257
}
253258
}
254259
match i.node {
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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+
#![feature(on_unimplemented)]
13+
14+
#![allow(unused)]
15+
16+
#[rustc_on_unimplemented = "test error `{Self}` with `{Bar}` `{Baz}` `{Quux}`"]
17+
trait Foo<Bar, Baz, Quux>{}
18+
19+
#[rustc_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+
#[rustc_on_unimplemented] //~ ERROR this attribute must have a value
26+
trait BadAnnotation1 {}
27+
28+
#[rustc_on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{C}>`"]
29+
//~^ ERROR there is no type parameter C on trait BadAnnotation2
30+
trait BadAnnotation2<A,B> {}
31+
32+
#[rustc_on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{}>`"]
33+
//~^ only named substitution parameters are allowed
34+
trait BadAnnotation3<A,B> {}
35+
36+
pub fn main() {
37+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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+
#![feature(on_unimplemented)]
13+
14+
#[rustc_on_unimplemented = "test error `{Self}` with `{Bar}` `{Baz}` `{Quux}`"]
15+
trait Foo<Bar, Baz, Quux>{}
16+
17+
fn foobar<U: Clone, T: Foo<u8, U, u32>>() -> T {
18+
19+
}
20+
21+
#[rustc_on_unimplemented="a collection of type `{Self}` cannot be built from an iterator over elements of type `{A}`"]
22+
trait MyFromIterator<A> {
23+
/// Build a container with elements from an external iterator.
24+
fn my_from_iter<T: Iterator<Item=A>>(iterator: T) -> Self;
25+
}
26+
27+
fn collect<A, I: Iterator<Item=A>, B: MyFromIterator<A>>(it: I) -> B {
28+
MyFromIterator::my_from_iter(it)
29+
}
30+
31+
pub fn main() {
32+
let x = vec!(1u8, 2, 3, 4);
33+
let y: Option<Vec<u8>> = collect(x.iter()); // this should give approximately the same error for x.iter().collect()
34+
//~^ ERROR
35+
//~^^ NOTE a collection of type `core::option::Option<collections::vec::Vec<u8>>` cannot be built from an iterator over elements of type `&u8`
36+
let x: String = foobar(); //~ ERROR
37+
//~^ NOTE test error `collections::string::String` with `u8` `_` `u32`
38+
}

0 commit comments

Comments
 (0)