Make ItemContext available for better diagnositcs.
This commit is contained in:
parent
3fa9554d77
commit
d7869ec022
8 changed files with 166 additions and 2 deletions
|
@ -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.
|
||||
|
|
|
@ -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(..) => {}
|
||||
|
|
|
@ -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 {:?}",
|
||||
|
|
|
@ -369,6 +369,7 @@ symbols! {
|
|||
issue_5723_bootstrap,
|
||||
issue_tracker_base_url,
|
||||
item,
|
||||
item_context: "ItemContext",
|
||||
item_like_imports,
|
||||
iter,
|
||||
Iterator,
|
||||
|
|
27
src/test/ui/async-await/try-on-option-in-async.rs
Normal file
27
src/test/ui/async-await/try-on-option-in-async.rs
Normal file
|
@ -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
src/test/ui/async-await/try-on-option-in-async.stderr
Normal file
30
src/test/ui/async-await/try-on-option-in-async.stderr
Normal file
|
@ -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
src/test/ui/try-on-option-diagnostics.rs
Normal file
18
src/test/ui/try-on-option-diagnostics.rs
Normal file
|
@ -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
src/test/ui/try-on-option-diagnostics.stderr
Normal file
21
src/test/ui/try-on-option-diagnostics.stderr
Normal file
|
@ -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`.
|
Loading…
Add table
Add a link
Reference in a new issue