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:
commit
c8642daf93
@ -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")]
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
307
src/librustc/traits/on_unimplemented.rs
Normal file
307
src/librustc/traits/on_unimplemented.rs
Normal 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()
|
||||
}
|
||||
}
|
@ -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>,
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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]`).
|
||||
|
@ -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 {}
|
||||
|
||||
|
@ -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() {
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 {}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user