Auto merge of #44191 - arielb1:on-unimplemented-label, r=nikomatsakis

More general `on_unimplemented`, with uses in `Try`

Allow `on_unimplemented` directives to specify both the label and the primary message of the trait error, and allow them to be controlled by flags - currently only to be desugaring-sensitive.

e.g.
```Rust
#[rustc_on_unimplemented(
    on(all(direct, from_desugaring="?"),
        message="the `?` operator can only be used in a \
        function that returns `Result` \
        (or another type that implements `{Try}`)",
        label="cannot use the `?` operator in a function that returns `{Self}`"),
)]
```

r? @nikomatsakis
This commit is contained in:
bors 2017-09-03 16:25:46 +00:00
commit c8642daf93
15 changed files with 652 additions and 275 deletions

View File

@ -15,8 +15,24 @@
/// extracting those success or failure values from an existing instance and
/// creating a new instance from a success or failure value.
#[unstable(feature = "try_trait", issue = "42327")]
#[rustc_on_unimplemented = "the `?` operator can only be used in a function that returns `Result` \
(or another type that implements `{Try}`)"]
#[cfg_attr(stage0,
rustc_on_unimplemented = "the `?` operator can only be used in a \
function that returns `Result` \
(or another type that implements `{Try}`)")]
#[cfg_attr(not(stage0),
rustc_on_unimplemented(
on(all(
any(from_method="from_error", from_method="from_ok"),
from_desugaring="?"),
message="the `?` operator can only be used in a \
function that returns `Result` \
(or another type that implements `{Try}`)",
label="cannot use the `?` operator in a function that returns `{Self}`"),
on(all(from_method="into_result", from_desugaring="?"),
message="the `?` operator can only be applied to values \
that implement `{Try}`",
label="the `?` operator cannot be applied to type `{Self}`")
))]
pub trait Try {
/// The type of this value when viewed as successful.
#[unstable(feature = "try_trait", issue = "42327")]

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,
// E0272, // on_unimplemented #0
// E0273, // on_unimplemented #1
// E0274, // on_unimplemented #2
E0278, // requirement is not satisfied
E0279, // requirement is not satisfied
E0280, // requirement is not satisfied

View File

@ -15,6 +15,8 @@ use super::{
Obligation,
ObligationCause,
ObligationCauseCode,
OnUnimplementedDirective,
OnUnimplementedNote,
OutputTypeParameterMismatch,
TraitNotObjectSafe,
PredicateObligation,
@ -25,7 +27,6 @@ use super::{
};
use errors::DiagnosticBuilder;
use fmt_macros::{Parser, Piece, Position};
use hir;
use hir::def_id::DefId;
use infer::{self, InferCtxt};
@ -316,77 +317,56 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
}
}
fn on_unimplemented_note(&self,
trait_ref: ty::PolyTraitRef<'tcx>,
obligation: &PredicateObligation<'tcx>) -> Option<String> {
fn on_unimplemented_note(
&self,
trait_ref: ty::PolyTraitRef<'tcx>,
obligation: &PredicateObligation<'tcx>) ->
OnUnimplementedNote
{
let def_id = self.impl_similar_to(trait_ref, obligation)
.unwrap_or(trait_ref.def_id());
let trait_ref = trait_ref.skip_binder();
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);
let desugaring;
let method;
let mut flags = vec![];
let direct = match obligation.cause.code {
ObligationCauseCode::BuiltinDerivedObligation(..) |
ObligationCauseCode::ImplDerivedObligation(..) => false,
_ => true
};
if direct {
// this is a "direct", user-specified, rather than derived,
// obligation.
flags.push(("direct", None));
}
if let ObligationCauseCode::ItemObligation(item) = obligation.cause.code {
// FIXME: maybe also have some way of handling methods
// from other traits? That would require name resolution,
// which we might want to be some sort of hygienic.
//
// Currently I'm leaving it for what I need for `try`.
if self.tcx.trait_of_item(item) == Some(trait_ref.def_id) {
method = self.tcx.item_name(item).as_str();
flags.push(("from_method", None));
flags.push(("from_method", Some(&*method)));
}
}
report
if let Some(k) = obligation.cause.span.compiler_desugaring_kind() {
desugaring = k.as_symbol().as_str();
flags.push(("from_desugaring", None));
flags.push(("from_desugaring", Some(&*desugaring)));
}
if let Ok(Some(command)) = OnUnimplementedDirective::of_item(
self.tcx, trait_ref.def_id, def_id
) {
command.evaluate(self.tcx, trait_ref, &flags)
} else {
OnUnimplementedNote::empty()
}
}
fn find_similar_impl_candidates(&self,
@ -577,17 +557,23 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
let (post_message, pre_message) =
self.get_parent_trait_ref(&obligation.cause.code)
.map(|t| (format!(" in `{}`", t), format!("within `{}`, ", t)))
.unwrap_or((String::new(), String::new()));
.unwrap_or((String::new(), String::new()));
let OnUnimplementedNote { message, label }
= self.on_unimplemented_note(trait_ref, obligation);
let have_alt_message = message.is_some() || label.is_some();
let mut err = struct_span_err!(
self.tcx.sess,
span,
E0277,
"the trait bound `{}` is not satisfied{}",
trait_ref.to_predicate(),
post_message);
"{}",
message.unwrap_or_else(|| {
format!("the trait bound `{}` is not satisfied{}",
trait_ref.to_predicate(), post_message)
}));
let unimplemented_note = self.on_unimplemented_note(trait_ref, obligation);
if let Some(ref s) = unimplemented_note {
if let Some(ref s) = label {
// If it has a custom "#[rustc_on_unimplemented]"
// error message, let's display it as the label!
err.span_label(span, s.as_str());
@ -615,7 +601,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
// which is somewhat confusing.
err.help(&format!("consider adding a `where {}` bound",
trait_ref.to_predicate()));
} else if unimplemented_note.is_none() {
} else if !have_alt_message {
// Can't show anything else useful, try to find similar impls.
let impl_candidates = self.find_similar_impl_candidates(trait_ref);
self.report_similar_impl_candidates(impl_candidates, &mut err);

View File

@ -37,6 +37,7 @@ pub use self::project::{normalize, normalize_projection_type, Normalized};
pub use self::project::{ProjectionCache, ProjectionCacheSnapshot, Reveal};
pub use self::object_safety::ObjectSafetyViolation;
pub use self::object_safety::MethodViolationCode;
pub use self::on_unimplemented::{OnUnimplementedDirective, OnUnimplementedNote};
pub use self::select::{EvaluationCache, SelectionContext, SelectionCache};
pub use self::specialize::{OverlapError, specialization_graph, translate_substs};
pub use self::specialize::{SpecializesCache, find_associated_item};
@ -52,6 +53,7 @@ mod error_reporting;
mod fulfill;
mod project;
mod object_safety;
mod on_unimplemented;
mod select;
mod specialize;
mod structural_impls;

View File

@ -0,0 +1,307 @@
// Copyright 2017 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.
use fmt_macros::{Parser, Piece, Position};
use hir::def_id::DefId;
use ty::{self, TyCtxt};
use util::common::ErrorReported;
use util::nodemap::FxHashMap;
use syntax::ast::{MetaItem, NestedMetaItem};
use syntax::attr;
use syntax_pos::Span;
use syntax_pos::symbol::InternedString;
#[derive(Clone, Debug)]
pub struct OnUnimplementedFormatString(InternedString);
#[derive(Debug)]
pub struct OnUnimplementedDirective {
pub condition: Option<MetaItem>,
pub subcommands: Vec<OnUnimplementedDirective>,
pub message: Option<OnUnimplementedFormatString>,
pub label: Option<OnUnimplementedFormatString>,
}
pub struct OnUnimplementedNote {
pub message: Option<String>,
pub label: Option<String>,
}
impl OnUnimplementedNote {
pub fn empty() -> Self {
OnUnimplementedNote { message: None, label: None }
}
}
fn parse_error(tcx: TyCtxt, span: Span,
message: &str,
label: &str,
note: Option<&str>)
-> ErrorReported
{
let mut diag = struct_span_err!(
tcx.sess, span, E0232, "{}", message);
diag.span_label(span, label);
if let Some(note) = note {
diag.note(note);
}
diag.emit();
ErrorReported
}
impl<'a, 'gcx, 'tcx> OnUnimplementedDirective {
pub fn parse(tcx: TyCtxt<'a, 'gcx, 'tcx>,
trait_def_id: DefId,
items: &[NestedMetaItem],
span: Span,
is_root: bool)
-> Result<Self, ErrorReported>
{
let mut errored = false;
let mut item_iter = items.iter();
let condition = if is_root {
None
} else {
let cond = item_iter.next().ok_or_else(|| {
parse_error(tcx, span,
"empty `on`-clause in `#[rustc_on_unimplemented]`",
"empty on-clause here",
None)
})?.meta_item().ok_or_else(|| {
parse_error(tcx, span,
"invalid `on`-clause in `#[rustc_on_unimplemented]`",
"invalid on-clause here",
None)
})?;
attr::eval_condition(cond, &tcx.sess.parse_sess, &mut |_| true);
Some(cond.clone())
};
let mut message = None;
let mut label = None;
let mut subcommands = vec![];
for item in item_iter {
if item.check_name("message") && message.is_none() {
if let Some(message_) = item.value_str() {
message = Some(OnUnimplementedFormatString::try_parse(
tcx, trait_def_id, message_.as_str(), span)?);
continue;
}
} else if item.check_name("label") && label.is_none() {
if let Some(label_) = item.value_str() {
label = Some(OnUnimplementedFormatString::try_parse(
tcx, trait_def_id, label_.as_str(), span)?);
continue;
}
} else if item.check_name("on") && is_root &&
message.is_none() && label.is_none()
{
if let Some(items) = item.meta_item_list() {
if let Ok(subcommand) =
Self::parse(tcx, trait_def_id, &items, item.span, false)
{
subcommands.push(subcommand);
} else {
errored = true;
}
continue
}
}
// nothing found
parse_error(tcx, item.span,
"this attribute must have a valid value",
"expected value here",
Some(r#"eg `#[rustc_on_unimplemented = "foo"]`"#));
}
if errored {
Err(ErrorReported)
} else {
Ok(OnUnimplementedDirective { condition, message, label, subcommands })
}
}
pub fn of_item(tcx: TyCtxt<'a, 'gcx, 'tcx>,
trait_def_id: DefId,
impl_def_id: DefId)
-> 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 result = if let Some(items) = attr.meta_item_list() {
Self::parse(tcx, trait_def_id, &items, attr.span, true).map(Some)
} else if let Some(value) = attr.value_str() {
Ok(Some(OnUnimplementedDirective {
condition: None,
message: None,
subcommands: vec![],
label: Some(OnUnimplementedFormatString::try_parse(
tcx, trait_def_id, value.as_str(), attr.span)?)
}))
} else {
return Err(parse_error(tcx, attr.span,
"`#[rustc_on_unimplemented]` requires a value",
"value required here",
Some(r#"eg `#[rustc_on_unimplemented = "foo"]`"#)));
};
debug!("of_item({:?}/{:?}) = {:?}", trait_def_id, impl_def_id, result);
result
}
pub fn evaluate(&self,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
trait_ref: ty::TraitRef<'tcx>,
options: &[(&str, Option<&str>)])
-> OnUnimplementedNote
{
let mut message = None;
let mut label = None;
info!("evaluate({:?}, trait_ref={:?}, options={:?})",
self, trait_ref, options);
for command in self.subcommands.iter().chain(Some(self)).rev() {
if let Some(ref condition) = command.condition {
if !attr::eval_condition(condition, &tcx.sess.parse_sess, &mut |c| {
options.contains(&(&c.name().as_str(),
match c.value_str().map(|s| s.as_str()) {
Some(ref s) => Some(s),
None => None
}))
}) {
debug!("evaluate: skipping {:?} due to condition", command);
continue
}
}
debug!("evaluate: {:?} succeeded", command);
if let Some(ref message_) = command.message {
message = Some(message_.clone());
}
if let Some(ref label_) = command.label {
label = Some(label_.clone());
}
}
OnUnimplementedNote {
label: label.map(|l| l.format(tcx, trait_ref)),
message: message.map(|m| m.format(tcx, trait_ref))
}
}
}
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
}
pub 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()
}
}

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,11 @@ 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::OnUnimplementedDirective::of_item(tcx, trait_def_id, item_def_id);
}
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;

View File

@ -585,6 +585,20 @@ pub fn requests_inline(attrs: &[Attribute]) -> bool {
/// Tests if a cfg-pattern matches the cfg set
pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>) -> bool {
eval_condition(cfg, sess, &mut |cfg| {
if let (Some(feats), Some(gated_cfg)) = (features, GatedCfg::gate(cfg)) {
gated_cfg.check_and_emit(sess, feats);
}
sess.config.contains(&(cfg.name(), cfg.value_str()))
})
}
/// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to
/// evaluate individual items.
pub fn eval_condition<F>(cfg: &ast::MetaItem, sess: &ParseSess, eval: &mut F)
-> bool
where F: FnMut(&ast::MetaItem) -> bool
{
match cfg.node {
ast::MetaItemKind::List(ref mis) => {
for mi in mis.iter() {
@ -598,10 +612,10 @@ pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Feat
// that they won't fail with the loop above.
match &*cfg.name.as_str() {
"any" => mis.iter().any(|mi| {
cfg_matches(mi.meta_item().unwrap(), sess, features)
eval_condition(mi.meta_item().unwrap(), sess, eval)
}),
"all" => mis.iter().all(|mi| {
cfg_matches(mi.meta_item().unwrap(), sess, features)
eval_condition(mi.meta_item().unwrap(), sess, eval)
}),
"not" => {
if mis.len() != 1 {
@ -609,7 +623,7 @@ pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Feat
return false;
}
!cfg_matches(mis[0].meta_item().unwrap(), sess, features)
!eval_condition(mis[0].meta_item().unwrap(), sess, eval)
},
p => {
span_err!(sess.span_diagnostic, cfg.span, E0537, "invalid predicate `{}`", p);
@ -618,10 +632,7 @@ pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Feat
}
},
ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => {
if let (Some(feats), Some(gated_cfg)) = (features, GatedCfg::gate(cfg)) {
gated_cfg.check_and_emit(sess, feats);
}
sess.config.contains(&(cfg.name(), cfg.value_str()))
eval(cfg)
}
}
}

View File

@ -205,6 +205,18 @@ impl Span {
}
}
/// Return the compiler desugaring that created this span, or None
/// if this span is not from a desugaring.
pub fn compiler_desugaring_kind(&self) -> Option<CompilerDesugaringKind> {
match self.ctxt().outer().expn_info() {
Some(info) => match info.callee.format {
ExpnFormat::CompilerDesugaring(k) => Some(k),
_ => None
},
None => None
}
}
/// Check if a span is "internal" to a macro in which `unsafe`
/// can be used without triggering the `unsafe_code` lint
// (that is, a macro marked with `#[allow_internal_unsafe]`).

View File

@ -12,7 +12,7 @@
#[rustc_on_unimplemented]
//~^ ERROR E0232
//~| NOTE attribute requires a value
//~| NOTE value required here
//~| NOTE eg `#[rustc_on_unimplemented = "foo"]`
trait Bar {}

View File

@ -37,5 +37,29 @@ trait BadAnnotation2<A,B>
trait BadAnnotation3<A,B>
{}
#[rustc_on_unimplemented(lorem="")]
trait BadAnnotation4 {}
#[rustc_on_unimplemented(lorem(ipsum(dolor)))]
trait BadAnnotation5 {}
#[rustc_on_unimplemented(message="x", message="y")]
trait BadAnnotation6 {}
#[rustc_on_unimplemented(message="x", on(desugared, message="y"))]
trait BadAnnotation7 {}
#[rustc_on_unimplemented(on(), message="y")]
trait BadAnnotation8 {}
#[rustc_on_unimplemented(on="x", message="y")]
trait BadAnnotation9 {}
#[rustc_on_unimplemented(on(x="y"), message="y")]
trait BadAnnotation10 {}
#[rustc_on_unimplemented(on(desugared, on(desugared, message="x")), message="y")]
trait BadAnnotation11 {}
pub fn main() {
}

View File

@ -1,8 +1,8 @@
error[E0232]: this attribute must have a value
error[E0232]: `#[rustc_on_unimplemented]` requires a value
--> $DIR/bad-annotation.rs:26:1
|
26 | #[rustc_on_unimplemented] //~ ERROR this attribute must have a value
| ^^^^^^^^^^^^^^^^^^^^^^^^^ attribute requires a value
| ^^^^^^^^^^^^^^^^^^^^^^^^^ value required here
|
= note: eg `#[rustc_on_unimplemented = "foo"]`
@ -18,5 +18,59 @@ error[E0231]: only named substitution parameters are allowed
35 | #[rustc_on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{}>`"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 3 previous errors
error[E0232]: this attribute must have a valid value
--> $DIR/bad-annotation.rs:40:26
|
40 | #[rustc_on_unimplemented(lorem="")]
| ^^^^^^^^ expected value here
|
= note: eg `#[rustc_on_unimplemented = "foo"]`
error[E0232]: this attribute must have a valid value
--> $DIR/bad-annotation.rs:43:26
|
43 | #[rustc_on_unimplemented(lorem(ipsum(dolor)))]
| ^^^^^^^^^^^^^^^^^^^ expected value here
|
= note: eg `#[rustc_on_unimplemented = "foo"]`
error[E0232]: this attribute must have a valid value
--> $DIR/bad-annotation.rs:46:39
|
46 | #[rustc_on_unimplemented(message="x", message="y")]
| ^^^^^^^^^^^ expected value here
|
= note: eg `#[rustc_on_unimplemented = "foo"]`
error[E0232]: this attribute must have a valid value
--> $DIR/bad-annotation.rs:49:39
|
49 | #[rustc_on_unimplemented(message="x", on(desugared, message="y"))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected value here
|
= note: eg `#[rustc_on_unimplemented = "foo"]`
error[E0232]: empty `on`-clause in `#[rustc_on_unimplemented]`
--> $DIR/bad-annotation.rs:52:26
|
52 | #[rustc_on_unimplemented(on(), message="y")]
| ^^^^ empty on-clause here
error[E0232]: this attribute must have a valid value
--> $DIR/bad-annotation.rs:55:26
|
55 | #[rustc_on_unimplemented(on="x", message="y")]
| ^^^^^^ expected value here
|
= note: eg `#[rustc_on_unimplemented = "foo"]`
error[E0232]: this attribute must have a valid value
--> $DIR/bad-annotation.rs:61:40
|
61 | #[rustc_on_unimplemented(on(desugared, on(desugared, message="x")), message="y")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected value here
|
= note: eg `#[rustc_on_unimplemented = "foo"]`
error: aborting due to 10 previous errors

View File

@ -8,6 +8,26 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(try_trait)]
use std::ops::Try;
fn main() {
// error for a `Try` type on a non-`Try` fn
std::fs::File::open("foo")?;
// a non-`Try` type on a non-`Try` fn
()?;
// an unrelated use of `Try`
try_trait_generic::<()>();
}
fn try_trait_generic<T: Try>() -> T {
// and a non-`Try` object on a `Try` fn.
()?;
loop {}
}

View File

@ -1,14 +1,46 @@
error[E0277]: the trait bound `(): std::ops::Try` is not satisfied
--> $DIR/try-operator-on-main.rs:12:5
error[E0277]: the `?` operator can only be used in a function that returns `Result` (or another type that implements `std::ops::Try`)
--> $DIR/try-operator-on-main.rs:17:5
|
12 | std::fs::File::open("foo")?;
17 | std::fs::File::open("foo")?;
| ---------------------------
| |
| the `?` operator can only be used in a function that returns `Result` (or another type that implements `std::ops::Try`)
| cannot use the `?` operator in a function that returns `()`
| in this macro invocation
|
= help: the trait `std::ops::Try` is not implemented for `()`
= note: required by `std::ops::Try::from_error`
error: aborting due to previous error
error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try`
--> $DIR/try-operator-on-main.rs:20:5
|
20 | ()?;
| ---
| |
| the `?` operator cannot be applied to type `()`
| in this macro invocation
|
= help: the trait `std::ops::Try` is not implemented for `()`
= note: required by `std::ops::Try::into_result`
error[E0277]: the trait bound `(): std::ops::Try` is not satisfied
--> $DIR/try-operator-on-main.rs:23:5
|
23 | try_trait_generic::<()>();
| ^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::ops::Try` is not implemented for `()`
|
= note: required by `try_trait_generic`
error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try`
--> $DIR/try-operator-on-main.rs:30:5
|
30 | ()?;
| ---
| |
| the `?` operator cannot be applied to type `()`
| in this macro invocation
|
= help: the trait `std::ops::Try` is not implemented for `()`
= note: required by `std::ops::Try::into_result`
error: aborting due to 4 previous errors