diff --git a/src/libcore/ops/try.rs b/src/libcore/ops/try.rs index 4f4652084a801..a748ee87ef99a 100644 --- a/src/libcore/ops/try.rs +++ b/src/libcore/ops/try.rs @@ -5,19 +5,20 @@ /// 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( +#[cfg_attr(not(bootstrap), rustc_on_unimplemented( on(all( any(from_method="from_error", from_method="from_ok"), from_desugaring="QuestionMark"), message="the `?` operator can only be used in {ItemContext} \ that returns `Result` or `Option` \ (or another type that implements `{Try}`)", -label="cannot use the `?` operator in {ItemContext} that returns `{Self}`"), +label="cannot use the `?` operator in {ItemContext} that returns `{Self}`", +enclosing_scope="this function should return `Result` or `Option` to accept `?`"), on(all(from_method="into_result", from_desugaring="QuestionMark"), message="the `?` operator can only be applied to values \ that implement `{Try}`", label="the `?` operator cannot be applied to type `{Self}`") -)] +))] #[doc(alias = "?")] pub trait Try { /// The type of this value when viewed as successful. diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 0144d51a9693f..40c7db71f52f2 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -519,7 +519,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ) { command.evaluate(self.tcx, trait_ref, &flags[..]) } else { - OnUnimplementedNote::empty() + OnUnimplementedNote::default() } } @@ -695,6 +695,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { fallback_has_occurred: bool, points_at_arg: bool, ) { + let tcx = self.tcx; let span = obligation.cause.span; let mut err = match *error { @@ -730,6 +731,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { message, label, note, + enclosing_scope, } = self.on_unimplemented_note(trait_ref, obligation); let have_alt_message = message.is_some() || label.is_some(); let is_try = self.tcx.sess.source_map().span_to_snippet(span) @@ -794,6 +796,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // If it has a custom `#[rustc_on_unimplemented]` note, let's display it err.note(s.as_str()); } + if let Some(ref s) = enclosing_scope { + let enclosing_scope_span = tcx.def_span( + tcx.hir() + .opt_local_def_id(obligation.cause.body_id) + .unwrap_or_else(|| { + tcx.hir().body_owner_def_id(hir::BodyId { + hir_id: obligation.cause.body_id, + }) + }), + ); + + err.span_label(enclosing_scope_span, s.as_str()); + } self.suggest_borrow_on_unsized_slice(&obligation.cause.code, &mut err); self.suggest_fn_call(&obligation, &mut err, &trait_ref, points_at_arg); diff --git a/src/librustc/traits/on_unimplemented.rs b/src/librustc/traits/on_unimplemented.rs index 59aa0810975e8..604f39dcf293b 100644 --- a/src/librustc/traits/on_unimplemented.rs +++ b/src/librustc/traits/on_unimplemented.rs @@ -22,18 +22,15 @@ pub struct OnUnimplementedDirective { pub message: Option, pub label: Option, pub note: Option, + pub enclosing_scope: Option, } +#[derive(Default)] pub struct OnUnimplementedNote { pub message: Option, pub label: Option, pub note: Option, -} - -impl OnUnimplementedNote { - pub fn empty() -> Self { - OnUnimplementedNote { message: None, label: None, note: None } - } + pub enclosing_scope: Option, } fn parse_error( @@ -85,24 +82,33 @@ impl<'tcx> OnUnimplementedDirective { let mut message = None; let mut label = None; let mut note = None; + let mut enclosing_scope = None; let mut subcommands = vec![]; + + let parse_value = |value_str| { + OnUnimplementedFormatString::try_parse(tcx, trait_def_id, value_str, span) + .map(Some) + }; + for item in item_iter { if item.check_name(sym::message) && message.is_none() { if let Some(message_) = item.value_str() { - message = Some(OnUnimplementedFormatString::try_parse( - tcx, trait_def_id, message_, span)?); + message = parse_value(message_)?; continue; } } else if item.check_name(sym::label) && label.is_none() { if let Some(label_) = item.value_str() { - label = Some(OnUnimplementedFormatString::try_parse( - tcx, trait_def_id, label_, span)?); + label = parse_value(label_)?; continue; } } else if item.check_name(sym::note) && note.is_none() { if let Some(note_) = item.value_str() { - note = Some(OnUnimplementedFormatString::try_parse( - tcx, trait_def_id, note_, span)?); + note = parse_value(note_)?; + continue; + } + } else if item.check_name(sym::enclosing_scope) && enclosing_scope.is_none() { + if let Some(enclosing_scope_) = item.value_str() { + enclosing_scope = parse_value(enclosing_scope_)?; continue; } } else if item.check_name(sym::on) && is_root && @@ -130,7 +136,14 @@ impl<'tcx> OnUnimplementedDirective { if errored { Err(ErrorReported) } else { - Ok(OnUnimplementedDirective { condition, message, label, subcommands, note }) + Ok(OnUnimplementedDirective { + condition, + subcommands, + message, + label, + note, + enclosing_scope + }) } } @@ -157,6 +170,7 @@ impl<'tcx> OnUnimplementedDirective { label: Some(OnUnimplementedFormatString::try_parse( tcx, trait_def_id, value, attr.span)?), note: None, + enclosing_scope: None, })) } else { return Err(ErrorReported); @@ -174,6 +188,7 @@ impl<'tcx> OnUnimplementedDirective { let mut message = None; let mut label = None; let mut note = None; + let mut enclosing_scope = None; info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options); for command in self.subcommands.iter().chain(Some(self)).rev() { @@ -202,6 +217,10 @@ impl<'tcx> OnUnimplementedDirective { if let Some(ref note_) = command.note { note = Some(note_.clone()); } + + if let Some(ref enclosing_scope_) = command.enclosing_scope { + enclosing_scope = Some(enclosing_scope_.clone()); + } } let options: FxHashMap = options.into_iter() @@ -211,6 +230,7 @@ impl<'tcx> OnUnimplementedDirective { label: label.map(|l| l.format(tcx, trait_ref, &options)), message: message.map(|m| m.format(tcx, trait_ref, &options)), note: note.map(|n| n.format(tcx, trait_ref, &options)), + enclosing_scope: enclosing_scope.map(|e_s| e_s.format(tcx, trait_ref, &options)), } } } diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 7d43c3c8d076e..3e122c0906e28 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -279,6 +279,7 @@ symbols! { Err, Eq, Equal, + enclosing_scope, except, exclusive_range_pattern, exhaustive_integer_patterns, diff --git a/src/test/ui/async-await/try-on-option-in-async.stderr b/src/test/ui/async-await/try-on-option-in-async.stderr index 7d31f60efdc6a..46f8f41076bf5 100644 --- a/src/test/ui/async-await/try-on-option-in-async.stderr +++ b/src/test/ui/async-await/try-on-option-in-async.stderr @@ -1,8 +1,14 @@ error[E0277]: the `?` operator can only be used in an async block that returns `Result` or `Option` (or another type that implements `std::ops::Try`) --> $DIR/try-on-option-in-async.rs:8:9 | -LL | x?; - | ^^ cannot use the `?` operator in an async block that returns `{integer}` +LL | async { + | ___________- +LL | | let x: Option = None; +LL | | x?; + | | ^^ cannot use the `?` operator in an async block that returns `{integer}` +LL | | 22 +LL | | }.await + | |_____- this function should return `Result` or `Option` to accept `?` | = help: the trait `std::ops::Try` is not implemented for `{integer}` = note: required by `std::ops::Try::from_error` @@ -10,8 +16,14 @@ LL | x?; error[E0277]: the `?` operator can only be used in an async closure that returns `Result` or `Option` (or another type that implements `std::ops::Try`) --> $DIR/try-on-option-in-async.rs:16:9 | -LL | x?; - | ^^ cannot use the `?` operator in an async closure that returns `u32` +LL | let async_closure = async || { + | __________________________________- +LL | | let x: Option = None; +LL | | x?; + | | ^^ cannot use the `?` operator in an async closure that returns `u32` +LL | | 22_u32 +LL | | }; + | |_____- this function should return `Result` or `Option` to accept `?` | = help: the trait `std::ops::Try` is not implemented for `u32` = note: required by `std::ops::Try::from_error` @@ -19,8 +31,14 @@ LL | x?; error[E0277]: the `?` operator can only be used in an async function that returns `Result` or `Option` (or another type that implements `std::ops::Try`) --> $DIR/try-on-option-in-async.rs:25:5 | -LL | x?; - | ^^ cannot use the `?` operator in an async function that returns `u32` +LL | async fn an_async_function() -> u32 { + | _____________________________________- +LL | | let x: Option = None; +LL | | x?; + | | ^^ cannot use the `?` operator in an async function that returns `u32` +LL | | 22 +LL | | } + | |_- this function should return `Result` or `Option` to accept `?` | = help: the trait `std::ops::Try` is not implemented for `u32` = note: required by `std::ops::Try::from_error` diff --git a/src/test/ui/on-unimplemented/enclosing-scope.rs b/src/test/ui/on-unimplemented/enclosing-scope.rs new file mode 100644 index 0000000000000..881bff63f5f63 --- /dev/null +++ b/src/test/ui/on-unimplemented/enclosing-scope.rs @@ -0,0 +1,27 @@ +// Test scope annotations from `enclosing_scope` parameter + +#![feature(rustc_attrs)] + +#[rustc_on_unimplemented(enclosing_scope="in this scope")] +trait Trait{} + +struct Foo; + +fn f(x: T) {} + +fn main() { + let x = || { + f(Foo{}); //~ ERROR the trait bound `Foo: Trait` is not satisfied + let y = || { + f(Foo{}); //~ ERROR the trait bound `Foo: Trait` is not satisfied + }; + }; + + { + { + f(Foo{}); //~ ERROR the trait bound `Foo: Trait` is not satisfied + } + } + + f(Foo{}); //~ ERROR the trait bound `Foo: Trait` is not satisfied +} diff --git a/src/test/ui/on-unimplemented/enclosing-scope.stderr b/src/test/ui/on-unimplemented/enclosing-scope.stderr new file mode 100644 index 0000000000000..092e560330b4c --- /dev/null +++ b/src/test/ui/on-unimplemented/enclosing-scope.stderr @@ -0,0 +1,66 @@ +error[E0277]: the trait bound `Foo: Trait` is not satisfied + --> $DIR/enclosing-scope.rs:14:11 + | +LL | fn f(x: T) {} + | - ----- required by this bound in `f` +... +LL | let x = || { + | _____________- +LL | | f(Foo{}); + | | ^^^^^ the trait `Trait` is not implemented for `Foo` +LL | | let y = || { +LL | | f(Foo{}); +LL | | }; +LL | | }; + | |_____- in this scope + +error[E0277]: the trait bound `Foo: Trait` is not satisfied + --> $DIR/enclosing-scope.rs:16:15 + | +LL | fn f(x: T) {} + | - ----- required by this bound in `f` +... +LL | let y = || { + | _________________- +LL | | f(Foo{}); + | | ^^^^^ the trait `Trait` is not implemented for `Foo` +LL | | }; + | |_________- in this scope + +error[E0277]: the trait bound `Foo: Trait` is not satisfied + --> $DIR/enclosing-scope.rs:22:15 + | +LL | fn f(x: T) {} + | - ----- required by this bound in `f` +LL | +LL | / fn main() { +LL | | let x = || { +LL | | f(Foo{}); +LL | | let y = || { +... | +LL | | f(Foo{}); + | | ^^^^^ the trait `Trait` is not implemented for `Foo` +... | +LL | | f(Foo{}); +LL | | } + | |_- in this scope + +error[E0277]: the trait bound `Foo: Trait` is not satisfied + --> $DIR/enclosing-scope.rs:26:7 + | +LL | fn f(x: T) {} + | - ----- required by this bound in `f` +LL | +LL | / fn main() { +LL | | let x = || { +LL | | f(Foo{}); +LL | | let y = || { +... | +LL | | f(Foo{}); + | | ^^^^^ the trait `Trait` is not implemented for `Foo` +LL | | } + | |_- in this scope + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr index f24ea0505e785..1143bddfe45a3 100644 --- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr +++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr @@ -575,8 +575,17 @@ LL | if (let 0 = 0)? {} error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`) --> $DIR/disallowed-positions.rs:46:8 | -LL | if (let 0 = 0)? {} - | ^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()` +LL | / fn nested_within_if_expr() { +LL | | if &let 0 = 0 {} +LL | | +LL | | +... | +LL | | if (let 0 = 0)? {} + | | ^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()` +... | +LL | | if let true = let true = true {} +LL | | } + | |_- this function should return `Result` or `Option` to accept `?` | = help: the trait `std::ops::Try` is not implemented for `()` = note: required by `std::ops::Try::from_error` @@ -754,8 +763,17 @@ LL | while (let 0 = 0)? {} error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`) --> $DIR/disallowed-positions.rs:110:11 | -LL | while (let 0 = 0)? {} - | ^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()` +LL | / fn nested_within_while_expr() { +LL | | while &let 0 = 0 {} +LL | | +LL | | +... | +LL | | while (let 0 = 0)? {} + | | ^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()` +... | +LL | | while let true = let true = true {} +LL | | } + | |_- this function should return `Result` or `Option` to accept `?` | = help: the trait `std::ops::Try` is not implemented for `()` = note: required by `std::ops::Try::from_error` @@ -924,8 +942,17 @@ LL | (let 0 = 0)?; error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`) --> $DIR/disallowed-positions.rs:183:5 | -LL | (let 0 = 0)?; - | ^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()` +LL | / fn outside_if_and_while_expr() { +LL | | &let 0 = 0; +LL | | +LL | | !let 0 = 0; +... | +LL | | (let 0 = 0)?; + | | ^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()` +... | +LL | | +LL | | } + | |_- this function should return `Result` or `Option` to accept `?` | = help: the trait `std::ops::Try` is not implemented for `()` = note: required by `std::ops::Try::from_error` diff --git a/src/test/ui/try-on-option-diagnostics.stderr b/src/test/ui/try-on-option-diagnostics.stderr index 4dd515e1b5a45..ce3aca39fb8fb 100644 --- a/src/test/ui/try-on-option-diagnostics.stderr +++ b/src/test/ui/try-on-option-diagnostics.stderr @@ -1,8 +1,13 @@ error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`) --> $DIR/try-on-option-diagnostics.rs:7:5 | -LL | x?; - | ^^ cannot use the `?` operator in a function that returns `u32` +LL | / fn a_function() -> u32 { +LL | | let x: Option = None; +LL | | x?; + | | ^^ cannot use the `?` operator in a function that returns `u32` +LL | | 22 +LL | | } + | |_- this function should return `Result` or `Option` to accept `?` | = help: the trait `std::ops::Try` is not implemented for `u32` = note: required by `std::ops::Try::from_error` @@ -10,8 +15,14 @@ LL | x?; error[E0277]: the `?` operator can only be used in a closure that returns `Result` or `Option` (or another type that implements `std::ops::Try`) --> $DIR/try-on-option-diagnostics.rs:14:9 | -LL | x?; - | ^^ cannot use the `?` operator in a closure that returns `{integer}` +LL | let a_closure = || { + | _____________________- +LL | | let x: Option = None; +LL | | x?; + | | ^^ cannot use the `?` operator in a closure that returns `{integer}` +LL | | 22 +LL | | }; + | |_____- this function should return `Result` or `Option` to accept `?` | = help: the trait `std::ops::Try` is not implemented for `{integer}` = note: required by `std::ops::Try::from_error` diff --git a/src/test/ui/try-on-option.stderr b/src/test/ui/try-on-option.stderr index db5046f8c151a..07615b52a48a5 100644 --- a/src/test/ui/try-on-option.stderr +++ b/src/test/ui/try-on-option.stderr @@ -10,8 +10,13 @@ LL | x?; error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`) --> $DIR/try-on-option.rs:13:5 | -LL | x?; - | ^^ cannot use the `?` operator in a function that returns `u32` +LL | / fn bar() -> u32 { +LL | | let x: Option = None; +LL | | x?; + | | ^^ cannot use the `?` operator in a function that returns `u32` +LL | | 22 +LL | | } + | |_- this function should return `Result` or `Option` to accept `?` | = help: the trait `std::ops::Try` is not implemented for `u32` = note: required by `std::ops::Try::from_error` diff --git a/src/test/ui/try-operator-on-main.stderr b/src/test/ui/try-operator-on-main.stderr index d2f1a04837b86..d8ba264583e45 100644 --- a/src/test/ui/try-operator-on-main.stderr +++ b/src/test/ui/try-operator-on-main.stderr @@ -1,8 +1,15 @@ error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`) --> $DIR/try-operator-on-main.rs:9:5 | -LL | std::fs::File::open("foo")?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()` +LL | / fn main() { +LL | | // error for a `Try` type on a non-`Try` fn +LL | | std::fs::File::open("foo")?; + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()` +LL | | +... | +LL | | try_trait_generic::<()>(); +LL | | } + | |_- this function should return `Result` or `Option` to accept `?` | = help: the trait `std::ops::Try` is not implemented for `()` = note: required by `std::ops::Try::from_error`