refactor and centralize on_unimplemented parsing

This commit is contained in:
Ariel Ben-Yehuda 2017-08-30 00:44:29 +03:00
parent fc54bf949d
commit 243aa12d6e
6 changed files with 242 additions and 246 deletions

View File

@ -688,8 +688,8 @@ See also https://doc.rust-lang.org/book/first-edition/no-stdlib.html
"##,
E0214: r##"
A generic type was described using parentheses rather than angle brackets. For
example:
A generic type was described using parentheses rather than angle brackets.
For example:
```compile_fail,E0214
fn main() {
@ -702,6 +702,93 @@ Parentheses are currently only used with generic types when defining parameters
for `Fn`-family traits.
"##,
E0230: r##"
The `#[rustc_on_unimplemented]` attribute lets you specify a custom error
message for when a particular trait isn't implemented on a type placed in a
position that needs that trait. For example, when the following code is
compiled:
```compile_fail
#![feature(on_unimplemented)]
fn foo<T: Index<u8>>(x: T){}
#[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"]
trait Index<Idx> { /* ... */ }
foo(true); // `bool` does not implement `Index<u8>`
```
There will be an error about `bool` not implementing `Index<u8>`, followed by a
note saying "the type `bool` cannot be indexed by `u8`".
As you can see, you can specify type parameters in curly braces for
substitution with the actual types (using the regular format string syntax) in
a given situation. Furthermore, `{Self}` will substitute to the type (in this
case, `bool`) that we tried to use.
This error appears when the curly braces contain an identifier which doesn't
match with any of the type parameters or the string `Self`. This might happen
if you misspelled a type parameter, or if you intended to use literal curly
braces. If it is the latter, escape the curly braces with a second curly brace
of the same type; e.g. a literal `{` is `{{`.
"##,
E0231: r##"
The `#[rustc_on_unimplemented]` attribute lets you specify a custom error
message for when a particular trait isn't implemented on a type placed in a
position that needs that trait. For example, when the following code is
compiled:
```compile_fail
#![feature(on_unimplemented)]
fn foo<T: Index<u8>>(x: T){}
#[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"]
trait Index<Idx> { /* ... */ }
foo(true); // `bool` does not implement `Index<u8>`
```
there will be an error about `bool` not implementing `Index<u8>`, followed by a
note saying "the type `bool` cannot be indexed by `u8`".
As you can see, you can specify type parameters in curly braces for
substitution with the actual types (using the regular format string syntax) in
a given situation. Furthermore, `{Self}` will substitute to the type (in this
case, `bool`) that we tried to use.
This error appears when the curly braces do not contain an identifier. Please
add one of the same name as a type parameter. If you intended to use literal
braces, use `{{` and `}}` to escape them.
"##,
E0232: r##"
The `#[rustc_on_unimplemented]` attribute lets you specify a custom error
message for when a particular trait isn't implemented on a type placed in a
position that needs that trait. For example, when the following code is
compiled:
```compile_fail
#![feature(on_unimplemented)]
fn foo<T: Index<u8>>(x: T){}
#[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"]
trait Index<Idx> { /* ... */ }
foo(true); // `bool` does not implement `Index<u8>`
```
there will be an error about `bool` not implementing `Index<u8>`, followed by a
note saying "the type `bool` cannot be indexed by `u8`".
For this to work, some note must be specified. An empty attribute will not do
anything, please remove the attribute or add some helpful note for users of the
trait.
"##,
E0261: r##"
When using a lifetime like `'a` in a type, it must be declared before being
used.
@ -917,92 +1004,6 @@ for v in &vs {
```
"##,
E0272: r##"
The `#[rustc_on_unimplemented]` attribute lets you specify a custom error
message for when a particular trait isn't implemented on a type placed in a
position that needs that trait. For example, when the following code is
compiled:
```compile_fail
#![feature(on_unimplemented)]
fn foo<T: Index<u8>>(x: T){}
#[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"]
trait Index<Idx> { /* ... */ }
foo(true); // `bool` does not implement `Index<u8>`
```
There will be an error about `bool` not implementing `Index<u8>`, followed by a
note saying "the type `bool` cannot be indexed by `u8`".
As you can see, you can specify type parameters in curly braces for
substitution with the actual types (using the regular format string syntax) in
a given situation. Furthermore, `{Self}` will substitute to the type (in this
case, `bool`) that we tried to use.
This error appears when the curly braces contain an identifier which doesn't
match with any of the type parameters or the string `Self`. This might happen
if you misspelled a type parameter, or if you intended to use literal curly
braces. If it is the latter, escape the curly braces with a second curly brace
of the same type; e.g. a literal `{` is `{{`.
"##,
E0273: r##"
The `#[rustc_on_unimplemented]` attribute lets you specify a custom error
message for when a particular trait isn't implemented on a type placed in a
position that needs that trait. For example, when the following code is
compiled:
```compile_fail
#![feature(on_unimplemented)]
fn foo<T: Index<u8>>(x: T){}
#[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"]
trait Index<Idx> { /* ... */ }
foo(true); // `bool` does not implement `Index<u8>`
```
there will be an error about `bool` not implementing `Index<u8>`, followed by a
note saying "the type `bool` cannot be indexed by `u8`".
As you can see, you can specify type parameters in curly braces for
substitution with the actual types (using the regular format string syntax) in
a given situation. Furthermore, `{Self}` will substitute to the type (in this
case, `bool`) that we tried to use.
This error appears when the curly braces do not contain an identifier. Please
add one of the same name as a type parameter. If you intended to use literal
braces, use `{{` and `}}` to escape them.
"##,
E0274: r##"
The `#[rustc_on_unimplemented]` attribute lets you specify a custom error
message for when a particular trait isn't implemented on a type placed in a
position that needs that trait. For example, when the following code is
compiled:
```compile_fail
#![feature(on_unimplemented)]
fn foo<T: Index<u8>>(x: T){}
#[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"]
trait Index<Idx> { /* ... */ }
foo(true); // `bool` does not implement `Index<u8>`
```
there will be an error about `bool` not implementing `Index<u8>`, followed by a
note saying "the type `bool` cannot be indexed by `u8`".
For this to work, some note must be specified. An empty attribute will not do
anything, please remove the attribute or add some helpful note for users of the
trait.
"##,
E0275: r##"
This error occurs when there was a recursive trait requirement that overflowed
@ -2011,6 +2012,9 @@ register_diagnostics! {
// E0102, // replaced with E0282
// E0134,
// E0135,
// E0271, // on_unimplemented #0
// E0272, // on_unimplemented #1
// E0273, // on_unimplemented #2
E0278, // requirement is not satisfied
E0279, // requirement is not satisfied
E0280, // requirement is not satisfied

View File

@ -39,9 +39,148 @@ use ty::fast_reject;
use ty::fold::TypeFolder;
use ty::subst::Subst;
use ty::SubtypePredicate;
use util::common::ErrorReported;
use util::nodemap::{FxHashMap, FxHashSet};
use syntax_pos::{DUMMY_SP, Span};
use syntax_pos::symbol::InternedString;
pub struct OnUnimplementedFormatString(InternedString);
pub struct OnUnimplementedInfo {
note: OnUnimplementedFormatString
}
impl<'a, 'gcx, 'tcx> OnUnimplementedInfo {
pub fn of_item(tcx: TyCtxt<'a, 'gcx, 'tcx>,
trait_def_id: DefId,
impl_def_id: DefId,
span: Span)
-> Result<Option<Self>, ErrorReported>
{
let attrs = tcx.get_attrs(impl_def_id);
let attr = if let Some(item) =
attrs.into_iter().find(|a| a.check_name("rustc_on_unimplemented"))
{
item
} else {
return Ok(None);
};
let span = attr.span.substitute_dummy(span);
if let Some(label) = attr.value_str() {
Ok(Some(OnUnimplementedInfo {
note: OnUnimplementedFormatString::try_parse(
tcx, trait_def_id, label.as_str(), span)?
}))
} else {
struct_span_err!(
tcx.sess, span, E0232,
"this attribute must have a value")
.span_label(attr.span, "attribute requires a value")
.note(&format!("eg `#[rustc_on_unimplemented = \"foo\"]`"))
.emit();
Err(ErrorReported)
}
}
}
impl<'a, 'gcx, 'tcx> OnUnimplementedFormatString {
pub fn try_parse(tcx: TyCtxt<'a, 'gcx, 'tcx>,
trait_def_id: DefId,
from: InternedString,
err_sp: Span)
-> Result<Self, ErrorReported>
{
let result = OnUnimplementedFormatString(from);
result.verify(tcx, trait_def_id, err_sp)?;
Ok(result)
}
fn verify(&self,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
trait_def_id: DefId,
span: Span)
-> Result<(), ErrorReported>
{
let name = tcx.item_name(trait_def_id).as_str();
let generics = tcx.generics_of(trait_def_id);
let parser = Parser::new(&self.0);
let types = &generics.types;
let mut result = Ok(());
for token in parser {
match token {
Piece::String(_) => (), // Normal string, no need to check it
Piece::NextArgument(a) => match a.position {
// `{Self}` is allowed
Position::ArgumentNamed(s) if s == "Self" => (),
// `{ThisTraitsName}` is allowed
Position::ArgumentNamed(s) if s == name => (),
// So is `{A}` if A is a type parameter
Position::ArgumentNamed(s) => match types.iter().find(|t| {
t.name == s
}) {
Some(_) => (),
None => {
span_err!(tcx.sess, span, E0230,
"there is no type parameter \
{} on trait {}",
s, name);
result = Err(ErrorReported);
}
},
// `{:1}` and `{}` are not to be used
Position::ArgumentIs(_) => {
span_err!(tcx.sess, span, E0231,
"only named substitution \
parameters are allowed");
result = Err(ErrorReported);
}
}
}
}
result
}
fn format(&self,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
trait_ref: ty::TraitRef<'tcx>)
-> String
{
let name = tcx.item_name(trait_ref.def_id).as_str();
let trait_str = tcx.item_path_str(trait_ref.def_id);
let generics = tcx.generics_of(trait_ref.def_id);
let generic_map = generics.types.iter().map(|param| {
(param.name.as_str().to_string(),
trait_ref.substs.type_for_def(param).to_string())
}).collect::<FxHashMap<String, String>>();
let parser = Parser::new(&self.0);
parser.map(|p| {
match p {
Piece::String(s) => s,
Piece::NextArgument(a) => match a.position {
Position::ArgumentNamed(s) => match generic_map.get(s) {
Some(val) => val,
None if s == name => {
&trait_str
}
None => {
bug!("broken on_unimplemented {:?} for {:?}: \
no argument matching {:?}",
self.0, trait_ref, s)
}
},
_ => {
bug!("broken on_unimplemented {:?} - bad \
format arg", self.0)
}
}
}
}).collect()
}
}
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
pub fn report_fulfillment_errors(&self,
@ -323,70 +462,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
.unwrap_or(trait_ref.def_id());
let trait_ref = trait_ref.skip_binder();
let span = obligation.cause.span;
let mut report = None;
if let Some(item) = self.tcx
.get_attrs(def_id)
.into_iter()
.filter(|a| a.check_name("rustc_on_unimplemented"))
.next()
{
let name = self.tcx.item_name(def_id).as_str();
let err_sp = item.span.substitute_dummy(span);
let trait_str = self.tcx.item_path_str(trait_ref.def_id);
if let Some(istring) = item.value_str() {
let istring = &*istring.as_str();
let generics = self.tcx.generics_of(trait_ref.def_id);
let generic_map = generics.types.iter().map(|param| {
(param.name.as_str().to_string(),
trait_ref.substs.type_for_def(param).to_string())
}).collect::<FxHashMap<String, String>>();
let parser = Parser::new(istring);
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),
None if s == name => {
Some(&trait_str)
}
None => {
span_err!(self.tcx.sess, err_sp, E0272,
"the #[rustc_on_unimplemented] attribute on trait \
definition for {} refers to non-existent type \
parameter {}",
trait_str, s);
errored = true;
None
}
},
_ => {
span_err!(self.tcx.sess, err_sp, E0273,
"the #[rustc_on_unimplemented] attribute on trait \
definition for {} must have named format arguments, eg \
`#[rustc_on_unimplemented = \"foo {{T}}\"]`",
trait_str);
errored = true;
None
}
}
}
}).collect();
// Report only if the format string checks out
if !errored {
report = Some(err);
}
} else {
span_err!(self.tcx.sess, err_sp, E0274,
"the #[rustc_on_unimplemented] attribute on \
trait definition for {} must have a value, \
eg `#[rustc_on_unimplemented = \"foo\"]`",
trait_str);
}
match OnUnimplementedInfo::of_item(
self.tcx, trait_ref.def_id, def_id, obligation.cause.span
) {
Ok(Some(info)) => Some(info.note.format(self.tcx, *trait_ref)),
_ => None
}
report
}
fn find_similar_impl_candidates(&self,

View File

@ -31,6 +31,7 @@ use syntax_pos::{Span, DUMMY_SP};
pub use self::coherence::orphan_check;
pub use self::coherence::overlapping_impls;
pub use self::coherence::OrphanCheckErr;
pub use self::error_reporting::OnUnimplementedInfo;
pub use self::fulfill::{FulfillmentContext, RegionObligation};
pub use self::project::MismatchedProjectionTypes;
pub use self::project::{normalize, normalize_projection_type, Normalized};

View File

@ -85,7 +85,6 @@ use self::method::MethodCallee;
use self::TupleArgumentsFlag::*;
use astconv::AstConv;
use fmt_macros::{Parser, Piece, Position};
use hir::def::{Def, CtorKind};
use hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
use rustc_back::slice::ref_slice;
@ -1215,55 +1214,12 @@ pub fn check_item_type<'a,'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, it: &'tcx hir::Item
}
fn check_on_unimplemented<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId,
trait_def_id: DefId,
item: &hir::Item) {
let generics = tcx.generics_of(def_id);
if let Some(ref attr) = item.attrs.iter().find(|a| {
a.check_name("rustc_on_unimplemented")
}) {
if let Some(istring) = attr.value_str() {
let istring = istring.as_str();
let name = tcx.item_name(def_id).as_str();
let parser = Parser::new(&istring);
let types = &generics.types;
for token in parser {
match token {
Piece::String(_) => (), // Normal string, no need to check it
Piece::NextArgument(a) => match a.position {
// `{Self}` is allowed
Position::ArgumentNamed(s) if s == "Self" => (),
// `{ThisTraitsName}` is allowed
Position::ArgumentNamed(s) if s == name => (),
// So is `{A}` if A is a type parameter
Position::ArgumentNamed(s) => match types.iter().find(|t| {
t.name == s
}) {
Some(_) => (),
None => {
span_err!(tcx.sess, attr.span, E0230,
"there is no type parameter \
{} on trait {}",
s, name);
}
},
// `{:1}` and `{}` are not to be used
Position::ArgumentIs(_) => {
span_err!(tcx.sess, attr.span, E0231,
"only named substitution \
parameters are allowed");
}
}
}
}
} else {
struct_span_err!(
tcx.sess, attr.span, E0232,
"this attribute must have a value")
.span_label(attr.span, "attribute requires a value")
.note(&format!("eg `#[rustc_on_unimplemented = \"foo\"]`"))
.emit();
}
}
let item_def_id = tcx.hir.local_def_id(item.id);
// an error would be reported if this fails.
let _ = traits::OnUnimplementedInfo::of_item(
tcx, trait_def_id, item_def_id, item.span);
}
fn report_forbidden_specialization<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,

View File

@ -2505,50 +2505,6 @@ fn baz<I>(x: &<I as Foo>::A) where I: Foo<A=Bar> {}
```
"##,
E0230: r##"
The trait has more type parameters specified than appear in its definition.
Erroneous example code:
```compile_fail,E0230
#![feature(on_unimplemented)]
#[rustc_on_unimplemented = "Trait error on `{Self}` with `<{A},{B},{C}>`"]
// error: there is no type parameter C on trait TraitWithThreeParams
trait TraitWithThreeParams<A,B>
{}
```
Include the correct number of type parameters and the compilation should
proceed:
```
#![feature(on_unimplemented)]
#[rustc_on_unimplemented = "Trait error on `{Self}` with `<{A},{B},{C}>`"]
trait TraitWithThreeParams<A,B,C> // ok!
{}
```
"##,
E0232: r##"
The attribute must have a value. Erroneous code example:
```compile_fail,E0232
#![feature(on_unimplemented)]
#[rustc_on_unimplemented] // error: this attribute must have a value
trait Bar {}
```
Please supply the missing value of the attribute. Example:
```
#![feature(on_unimplemented)]
#[rustc_on_unimplemented = "foo"] // ok!
trait Bar {}
```
"##,
E0243: r##"
This error indicates that not enough type parameters were found in a type or
trait.
@ -4690,7 +4646,6 @@ register_diagnostics! {
E0224, // at least one non-builtin train is required for an object type
E0227, // ambiguous lifetime bound, explicit lifetime bound required
E0228, // explicit lifetime bound required
E0231, // only named substitution parameters are allowed
// E0233,
// E0234,
// E0235, // structure constructor specifies a structure of type but

View File

@ -84,7 +84,6 @@ This API is completely unstable and subject to change.
extern crate syntax_pos;
extern crate arena;
extern crate fmt_macros;
#[macro_use] extern crate rustc;
extern crate rustc_platform_intrinsics as intrinsics;
extern crate rustc_back;