diff --git a/src/libcore/ops/try.rs b/src/libcore/ops/try.rs index 76fec1020f1ef..e8f35f8cf2450 100644 --- a/src/libcore/ops/try.rs +++ b/src/libcore/ops/try.rs @@ -5,7 +5,7 @@ /// 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(bootstrap, rustc_on_unimplemented( on(all( any(from_method="from_error", from_method="from_ok"), from_desugaring="QuestionMark"), @@ -17,7 +17,20 @@ message="the `?` operator can only be applied to values \ that implement `{Try}`", label="the `?` operator cannot be applied to type `{Self}`") -)] +))] +#[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}`"), +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 a1c97d6c68790..ddbd8e806dd27 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -347,6 +347,52 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } + fn describe_generator(&self, body_id: hir::BodyId) -> Option<&'static str> { + self.tcx.hir().body(body_id).generator_kind.map(|gen_kind| { + match gen_kind { + hir::GeneratorKind::Gen => "a generator", + hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) => "an async block", + hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn) => "an async function", + hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure) => "an async closure", + } + }) + } + + /// Used to set on_unimplemented's `ItemContext` + /// to be the enclosing (async) block/function/closure + fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str> { + let hir = &self.tcx.hir(); + let node = hir.find(hir_id)?; + if let hir::Node::Item( + hir::Item{kind: hir::ItemKind::Fn(_ ,fn_header ,_ , body_id), .. }) = &node { + self.describe_generator(*body_id).or_else(|| + Some(if let hir::FnHeader{ asyncness: hir::IsAsync::Async, .. } = fn_header { + "an async function" + } else { + "a function" + }) + ) + } else if let hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure(_is_move, _, body_id, _, gen_movability), .. }) = &node { + self.describe_generator(*body_id).or_else(|| + Some(if gen_movability.is_some() { + "an async closure" + } else { + "a closure" + }) + ) + } else if let hir::Node::Expr(hir::Expr { .. }) = &node { + let parent_hid = hir.get_parent_node(hir_id); + if parent_hid != hir_id { + return self.describe_enclosure(parent_hid); + } else { + None + } + } else { + None + } + } + fn on_unimplemented_note( &self, trait_ref: ty::PolyTraitRef<'tcx>, @@ -357,6 +403,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let trait_ref = *trait_ref.skip_binder(); let mut flags = vec![]; + flags.push((sym::item_context, + self.describe_enclosure(obligation.cause.body_id).map(|s|s.to_owned()))); + match obligation.cause.code { ObligationCauseCode::BuiltinDerivedObligation(..) | ObligationCauseCode::ImplDerivedObligation(..) => {} diff --git a/src/librustc/traits/on_unimplemented.rs b/src/librustc/traits/on_unimplemented.rs index 5a988d9509e80..476b878515c45 100644 --- a/src/librustc/traits/on_unimplemented.rs +++ b/src/librustc/traits/on_unimplemented.rs @@ -248,6 +248,8 @@ impl<'tcx> OnUnimplementedFormatString { Position::ArgumentNamed(s) if s == sym::from_method => (), // `{from_desugaring}` is allowed Position::ArgumentNamed(s) if s == sym::from_desugaring => (), + // `{ItemContext}` is allowed + Position::ArgumentNamed(s) if s == sym::item_context => (), // So is `{A}` if A is a type parameter Position::ArgumentNamed(s) => match generics.params.iter().find(|param| { param.name.as_symbol() == s @@ -296,6 +298,7 @@ impl<'tcx> OnUnimplementedFormatString { let s = self.0.as_str(); let parser = Parser::new(&s, None, vec![], false); + let item_context = (options.get(&sym::item_context)).unwrap_or(&empty_string); parser.map(|p| match p { Piece::String(s) => s, @@ -311,6 +314,8 @@ impl<'tcx> OnUnimplementedFormatString { } else if s == sym::from_desugaring || s == sym::from_method { // don't break messages using these two arguments incorrectly &empty_string + } else if s == sym::item_context { + &item_context } else { bug!("broken on_unimplemented {:?} for {:?}: \ no argument matching {:?}", diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index c7230d5ca1522..7b4b906e96c55 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -369,6 +369,7 @@ symbols! { issue_5723_bootstrap, issue_tracker_base_url, item, + item_context: "ItemContext", item_like_imports, iter, Iterator, diff --git a/src/test/ui/async-await/try-on-option-in-async.rs b/src/test/ui/async-await/try-on-option-in-async.rs new file mode 100644 index 0000000000000..51ac522017cb3 --- /dev/null +++ b/src/test/ui/async-await/try-on-option-in-async.rs @@ -0,0 +1,27 @@ +#![feature(try_trait, async_closure)] +// edition:2018 +fn main() {} + +async fn an_async_block() -> u32 { + async { + let x: Option = None; + x?; //~ ERROR the `?` operator + 22 + }.await +} + +async fn async_closure_containing_fn() -> u32 { + let async_closure = async || { + let x: Option = None; + x?; //~ ERROR the `?` operator + 22_u32 + }; + + async_closure().await +} + +async fn an_async_function() -> u32 { + let x: Option = None; + x?; //~ ERROR the `?` operator + 22 +} 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 new file mode 100644 index 0000000000000..7d31f60efdc6a --- /dev/null +++ b/src/test/ui/async-await/try-on-option-in-async.stderr @@ -0,0 +1,30 @@ +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}` + | + = help: the trait `std::ops::Try` is not implemented for `{integer}` + = note: required by `std::ops::Try::from_error` + +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` + | + = help: the trait `std::ops::Try` is not implemented for `u32` + = note: required by `std::ops::Try::from_error` + +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` + | + = help: the trait `std::ops::Try` is not implemented for `u32` + = note: required by `std::ops::Try::from_error` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/try-on-option-diagnostics.rs b/src/test/ui/try-on-option-diagnostics.rs new file mode 100644 index 0000000000000..65d5e29ec2f13 --- /dev/null +++ b/src/test/ui/try-on-option-diagnostics.rs @@ -0,0 +1,18 @@ +#![feature(try_trait)] +// edition:2018 +fn main() {} + +fn a_function() -> u32 { + let x: Option = None; + x?; //~ ERROR the `?` operator + 22 +} + +fn a_closure() -> u32 { + let a_closure = || { + let x: Option = None; + x?; //~ ERROR the `?` operator + 22 + }; + a_closure() +} diff --git a/src/test/ui/try-on-option-diagnostics.stderr b/src/test/ui/try-on-option-diagnostics.stderr new file mode 100644 index 0000000000000..4dd515e1b5a45 --- /dev/null +++ b/src/test/ui/try-on-option-diagnostics.stderr @@ -0,0 +1,21 @@ +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` + | + = help: the trait `std::ops::Try` is not implemented for `u32` + = note: required by `std::ops::Try::from_error` + +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}` + | + = help: the trait `std::ops::Try` is not implemented for `{integer}` + = note: required by `std::ops::Try::from_error` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`.