Skip to content

Commit

Permalink
Make ItemContext available for better diagnositcs.
Browse files Browse the repository at this point in the history
  • Loading branch information
gilescope committed Oct 30, 2019
1 parent 3fa9554 commit d7869ec
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 2 deletions.
17 changes: 15 additions & 2 deletions src/libcore/ops/try.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand All @@ -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.
Expand Down
49 changes: 49 additions & 0 deletions src/librustc/traits/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand All @@ -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(..) => {}
Expand Down
5 changes: 5 additions & 0 deletions src/librustc/traits/on_unimplemented.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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 {:?}",
Expand Down
1 change: 1 addition & 0 deletions src/libsyntax_pos/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@ symbols! {
issue_5723_bootstrap,
issue_tracker_base_url,
item,
item_context: "ItemContext",
item_like_imports,
iter,
Iterator,
Expand Down
27 changes: 27 additions & 0 deletions src/test/ui/async-await/try-on-option-in-async.rs
Original file line number Diff line number Diff line change
@@ -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
}
30 changes: 30 additions & 0 deletions src/test/ui/async-await/try-on-option-in-async.stderr
Original file line number Diff line number Diff line change
@@ -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`.
18 changes: 18 additions & 0 deletions src/test/ui/try-on-option-diagnostics.rs
Original file line number Diff line number Diff line change
@@ -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()
}
21 changes: 21 additions & 0 deletions src/test/ui/try-on-option-diagnostics.stderr
Original file line number Diff line number Diff line change
@@ -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`.

0 comments on commit d7869ec

Please sign in to comment.