about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGiles Cope <gilescope@gmail.com>2019-10-28 19:04:15 +0000
committerGiles Cope <gilescope@gmail.com>2019-10-30 06:12:49 +0000
commitd7869ec02281be58ed3857b545f258b81a6adb09 (patch)
treeb6a5197feb9f41a311bb1e3935d49d4417811899
parent3fa9554d77960627cb2c983470bceacfeeb486b0 (diff)
downloadrust-d7869ec02281be58ed3857b545f258b81a6adb09.tar.gz
rust-d7869ec02281be58ed3857b545f258b81a6adb09.zip
Make ItemContext available for better diagnositcs.
-rw-r--r--src/libcore/ops/try.rs17
-rw-r--r--src/librustc/traits/error_reporting.rs49
-rw-r--r--src/librustc/traits/on_unimplemented.rs5
-rw-r--r--src/libsyntax_pos/symbol.rs1
-rw-r--r--src/test/ui/async-await/try-on-option-in-async.rs27
-rw-r--r--src/test/ui/async-await/try-on-option-in-async.stderr30
-rw-r--r--src/test/ui/try-on-option-diagnostics.rs18
-rw-r--r--src/test/ui/try-on-option-diagnostics.stderr21
8 files changed, 166 insertions, 2 deletions
diff --git a/src/libcore/ops/try.rs b/src/libcore/ops/try.rs
index 76fec1020f1..e8f35f8cf24 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 a1c97d6c687..ddbd8e806dd 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 5a988d9509e..476b878515c 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 c7230d5ca15..7b4b906e96c 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 00000000000..51ac522017c
--- /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<u32> = None;
+        x?; //~ ERROR the `?` operator
+        22
+    }.await
+}
+
+async fn async_closure_containing_fn() -> u32 {
+    let async_closure = async || {
+        let x: Option<u32> = None;
+        x?; //~ ERROR the `?` operator
+        22_u32
+    };
+
+    async_closure().await
+}
+
+async fn an_async_function() -> u32 {
+    let x: Option<u32> = 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 00000000000..7d31f60efdc
--- /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 00000000000..65d5e29ec2f
--- /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<u32> = None;
+    x?; //~ ERROR the `?` operator
+    22
+}
+
+fn a_closure() -> u32 {
+    let a_closure = || {
+        let x: Option<u32> = 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 00000000000..4dd515e1b5a
--- /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`.