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
|
/// extracting those success or failure values from an existing instance and
|
||||||
/// creating a new instance from a success or failure value.
|
/// creating a new instance from a success or failure value.
|
||||||
#[unstable(feature = "try_trait", issue = "42327")]
|
#[unstable(feature = "try_trait", issue = "42327")]
|
||||||
#[rustc_on_unimplemented(
|
#[cfg_attr(bootstrap, rustc_on_unimplemented(
|
||||||
on(all(
|
on(all(
|
||||||
any(from_method="from_error", from_method="from_ok"),
|
any(from_method="from_error", from_method="from_ok"),
|
||||||
from_desugaring="QuestionMark"),
|
from_desugaring="QuestionMark"),
|
||||||
|
@ -17,7 +17,20 @@
|
||||||
message="the `?` operator can only be applied to values \
|
message="the `?` operator can only be applied to values \
|
||||||
that implement `{Try}`",
|
that implement `{Try}`",
|
||||||
label="the `?` operator cannot be applied to type `{Self}`")
|
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 = "?")]
|
#[doc(alias = "?")]
|
||||||
pub trait Try {
|
pub trait Try {
|
||||||
/// The type of this value when viewed as successful.
|
/// 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(
|
fn on_unimplemented_note(
|
||||||
&self,
|
&self,
|
||||||
trait_ref: ty::PolyTraitRef<'tcx>,
|
trait_ref: ty::PolyTraitRef<'tcx>,
|
||||||
|
@ -357,6 +403,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||||
let trait_ref = *trait_ref.skip_binder();
|
let trait_ref = *trait_ref.skip_binder();
|
||||||
|
|
||||||
let mut flags = vec![];
|
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 {
|
match obligation.cause.code {
|
||||||
ObligationCauseCode::BuiltinDerivedObligation(..) |
|
ObligationCauseCode::BuiltinDerivedObligation(..) |
|
||||||
ObligationCauseCode::ImplDerivedObligation(..) => {}
|
ObligationCauseCode::ImplDerivedObligation(..) => {}
|
||||||
|
|
|
@ -248,6 +248,8 @@ impl<'tcx> OnUnimplementedFormatString {
|
||||||
Position::ArgumentNamed(s) if s == sym::from_method => (),
|
Position::ArgumentNamed(s) if s == sym::from_method => (),
|
||||||
// `{from_desugaring}` is allowed
|
// `{from_desugaring}` is allowed
|
||||||
Position::ArgumentNamed(s) if s == sym::from_desugaring => (),
|
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
|
// So is `{A}` if A is a type parameter
|
||||||
Position::ArgumentNamed(s) => match generics.params.iter().find(|param| {
|
Position::ArgumentNamed(s) => match generics.params.iter().find(|param| {
|
||||||
param.name.as_symbol() == s
|
param.name.as_symbol() == s
|
||||||
|
@ -296,6 +298,7 @@ impl<'tcx> OnUnimplementedFormatString {
|
||||||
|
|
||||||
let s = self.0.as_str();
|
let s = self.0.as_str();
|
||||||
let parser = Parser::new(&s, None, vec![], false);
|
let parser = Parser::new(&s, None, vec![], false);
|
||||||
|
let item_context = (options.get(&sym::item_context)).unwrap_or(&empty_string);
|
||||||
parser.map(|p|
|
parser.map(|p|
|
||||||
match p {
|
match p {
|
||||||
Piece::String(s) => s,
|
Piece::String(s) => s,
|
||||||
|
@ -311,6 +314,8 @@ impl<'tcx> OnUnimplementedFormatString {
|
||||||
} else if s == sym::from_desugaring || s == sym::from_method {
|
} else if s == sym::from_desugaring || s == sym::from_method {
|
||||||
// don't break messages using these two arguments incorrectly
|
// don't break messages using these two arguments incorrectly
|
||||||
&empty_string
|
&empty_string
|
||||||
|
} else if s == sym::item_context {
|
||||||
|
&item_context
|
||||||
} else {
|
} else {
|
||||||
bug!("broken on_unimplemented {:?} for {:?}: \
|
bug!("broken on_unimplemented {:?} for {:?}: \
|
||||||
no argument matching {:?}",
|
no argument matching {:?}",
|
||||||
|
|
|
@ -369,6 +369,7 @@ symbols! {
|
||||||
issue_5723_bootstrap,
|
issue_5723_bootstrap,
|
||||||
issue_tracker_base_url,
|
issue_tracker_base_url,
|
||||||
item,
|
item,
|
||||||
|
item_context: "ItemContext",
|
||||||
item_like_imports,
|
item_like_imports,
|
||||||
iter,
|
iter,
|
||||||
Iterator,
|
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