diff --git a/src/libcore/ops/try.rs b/src/libcore/ops/try.rs index 78326c3e6391a..e788b66a1ec82 100644 --- a/src/libcore/ops/try.rs +++ b/src/libcore/ops/try.rs @@ -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")] diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index f027dd34f29b4..be7bb4d811413 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -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>(x: T){} + +#[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"] +trait Index { /* ... */ } + +foo(true); // `bool` does not implement `Index` +``` + +There will be an error about `bool` not implementing `Index`, 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>(x: T){} + +#[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"] +trait Index { /* ... */ } + +foo(true); // `bool` does not implement `Index` +``` + +there will be an error about `bool` not implementing `Index`, 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>(x: T){} + +#[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"] +trait Index { /* ... */ } + +foo(true); // `bool` does not implement `Index` +``` + +there will be an error about `bool` not implementing `Index`, 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>(x: T){} - -#[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"] -trait Index { /* ... */ } - -foo(true); // `bool` does not implement `Index` -``` - -There will be an error about `bool` not implementing `Index`, 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>(x: T){} - -#[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"] -trait Index { /* ... */ } - -foo(true); // `bool` does not implement `Index` -``` - -there will be an error about `bool` not implementing `Index`, 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>(x: T){} - -#[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"] -trait Index { /* ... */ } - -foo(true); // `bool` does not implement `Index` -``` - -there will be an error about `bool` not implementing `Index`, 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 diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 0e7bfc8cd0d86..ce6da55fec36b 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -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 { + 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 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)); + } - 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::>(); - 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); + 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); diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 11a84f3636c67..33dcf3c76e6cf 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -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; diff --git a/src/librustc/traits/on_unimplemented.rs b/src/librustc/traits/on_unimplemented.rs new file mode 100644 index 0000000000000..7dd3fc70b1e3f --- /dev/null +++ b/src/librustc/traits/on_unimplemented.rs @@ -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 or the MIT license +// , 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, + pub subcommands: Vec, + pub message: Option, + pub label: Option, +} + +pub struct OnUnimplementedNote { + pub message: Option, + pub label: Option, +} + +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 + { + 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, 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 + { + 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::>(); + + 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() + } +} diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 9d088c660451b..3f210ea1737b7 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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>, diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 3750993504442..6bbe2233ff1fa 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -2505,50 +2505,6 @@ fn baz(x: &::A) where I: Foo {} ``` "##, -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 -{} -``` - -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 // 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 diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 86feea13b1723..7a6ee73b9b9e9 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -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; diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index bf8e0c0996ae4..adbbc1b0ac588 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -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(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) } } } diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index 24842ff29d428..cba5c812b07ce 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -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 { + 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]`). diff --git a/src/test/compile-fail/E0232.rs b/src/test/compile-fail/E0232.rs index ce4f4638dac59..a33120bbebd23 100644 --- a/src/test/compile-fail/E0232.rs +++ b/src/test/compile-fail/E0232.rs @@ -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 {} diff --git a/src/test/ui/on-unimplemented/bad-annotation.rs b/src/test/ui/on-unimplemented/bad-annotation.rs index 8580749084d22..54d3b3e087653 100644 --- a/src/test/ui/on-unimplemented/bad-annotation.rs +++ b/src/test/ui/on-unimplemented/bad-annotation.rs @@ -37,5 +37,29 @@ trait BadAnnotation2 trait BadAnnotation3 {} +#[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() { } diff --git a/src/test/ui/on-unimplemented/bad-annotation.stderr b/src/test/ui/on-unimplemented/bad-annotation.stderr index 8599477e8ed7e..73834f4422d38 100644 --- a/src/test/ui/on-unimplemented/bad-annotation.stderr +++ b/src/test/ui/on-unimplemented/bad-annotation.stderr @@ -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 diff --git a/src/test/ui/suggestions/try-operator-on-main.rs b/src/test/ui/suggestions/try-operator-on-main.rs index 55154e3507e87..eadd12924df66 100644 --- a/src/test/ui/suggestions/try-operator-on-main.rs +++ b/src/test/ui/suggestions/try-operator-on-main.rs @@ -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 { + // and a non-`Try` object on a `Try` fn. + ()?; + + loop {} } diff --git a/src/test/ui/suggestions/try-operator-on-main.stderr b/src/test/ui/suggestions/try-operator-on-main.stderr index cf0481bdab772..e83bf2abc1504 100644 --- a/src/test/ui/suggestions/try-operator-on-main.stderr +++ b/src/test/ui/suggestions/try-operator-on-main.stderr @@ -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