Add ability to attach custom #[on_unimplemented] error messages for unimplemented traits (fixes #20783)
This commit is contained in:
parent
14f9d1f256
commit
4d17fbaf37
5 changed files with 130 additions and 1 deletions
|
@ -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<A> {
|
||||
/// Build a container with elements from an external iterator.
|
||||
fn from_iter<T: Iterator<Item=A>>(iterator: T) -> Self;
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
extern crate arena;
|
||||
extern crate flate;
|
||||
extern crate fmt_macros;
|
||||
extern crate getopts;
|
||||
extern crate graphviz;
|
||||
extern crate libc;
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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<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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
51
src/test/compile-fail/on-unimplemented.rs
Normal file
51
src/test/compile-fail/on-unimplemented.rs
Normal file
|
@ -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
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue