Skip to content

Commit d7869ec

Browse files
committed
Make ItemContext available for better diagnositcs.
1 parent 3fa9554 commit d7869ec

File tree

8 files changed

+166
-2
lines changed

8 files changed

+166
-2
lines changed

src/libcore/ops/try.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
/// extracting those success or failure values from an existing instance and
66
/// creating a new instance from a success or failure value.
77
#[unstable(feature = "try_trait", issue = "42327")]
8-
#[rustc_on_unimplemented(
8+
#[cfg_attr(bootstrap, rustc_on_unimplemented(
99
on(all(
1010
any(from_method="from_error", from_method="from_ok"),
1111
from_desugaring="QuestionMark"),
@@ -17,7 +17,20 @@
1717
message="the `?` operator can only be applied to values \
1818
that implement `{Try}`",
1919
label="the `?` operator cannot be applied to type `{Self}`")
20-
)]
20+
))]
21+
#[cfg_attr(not(bootstrap), rustc_on_unimplemented(
22+
on(all(
23+
any(from_method="from_error", from_method="from_ok"),
24+
from_desugaring="QuestionMark"),
25+
message="the `?` operator can only be used in {ItemContext} \
26+
that returns `Result` or `Option` \
27+
(or another type that implements `{Try}`)",
28+
label="cannot use the `?` operator in {ItemContext} that returns `{Self}`"),
29+
on(all(from_method="into_result", from_desugaring="QuestionMark"),
30+
message="the `?` operator can only be applied to values \
31+
that implement `{Try}`",
32+
label="the `?` operator cannot be applied to type `{Self}`")
33+
))]
2134
#[doc(alias = "?")]
2235
pub trait Try {
2336
/// The type of this value when viewed as successful.

src/librustc/traits/error_reporting.rs

+49
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,52 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
347347
}
348348
}
349349

350+
fn describe_generator(&self, body_id: hir::BodyId) -> Option<&'static str> {
351+
self.tcx.hir().body(body_id).generator_kind.map(|gen_kind| {
352+
match gen_kind {
353+
hir::GeneratorKind::Gen => "a generator",
354+
hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) => "an async block",
355+
hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn) => "an async function",
356+
hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure) => "an async closure",
357+
}
358+
})
359+
}
360+
361+
/// Used to set on_unimplemented's `ItemContext`
362+
/// to be the enclosing (async) block/function/closure
363+
fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str> {
364+
let hir = &self.tcx.hir();
365+
let node = hir.find(hir_id)?;
366+
if let hir::Node::Item(
367+
hir::Item{kind: hir::ItemKind::Fn(_ ,fn_header ,_ , body_id), .. }) = &node {
368+
self.describe_generator(*body_id).or_else(||
369+
Some(if let hir::FnHeader{ asyncness: hir::IsAsync::Async, .. } = fn_header {
370+
"an async function"
371+
} else {
372+
"a function"
373+
})
374+
)
375+
} else if let hir::Node::Expr(hir::Expr {
376+
kind: hir::ExprKind::Closure(_is_move, _, body_id, _, gen_movability), .. }) = &node {
377+
self.describe_generator(*body_id).or_else(||
378+
Some(if gen_movability.is_some() {
379+
"an async closure"
380+
} else {
381+
"a closure"
382+
})
383+
)
384+
} else if let hir::Node::Expr(hir::Expr { .. }) = &node {
385+
let parent_hid = hir.get_parent_node(hir_id);
386+
if parent_hid != hir_id {
387+
return self.describe_enclosure(parent_hid);
388+
} else {
389+
None
390+
}
391+
} else {
392+
None
393+
}
394+
}
395+
350396
fn on_unimplemented_note(
351397
&self,
352398
trait_ref: ty::PolyTraitRef<'tcx>,
@@ -357,6 +403,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
357403
let trait_ref = *trait_ref.skip_binder();
358404

359405
let mut flags = vec![];
406+
flags.push((sym::item_context,
407+
self.describe_enclosure(obligation.cause.body_id).map(|s|s.to_owned())));
408+
360409
match obligation.cause.code {
361410
ObligationCauseCode::BuiltinDerivedObligation(..) |
362411
ObligationCauseCode::ImplDerivedObligation(..) => {}

src/librustc/traits/on_unimplemented.rs

+5
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,8 @@ impl<'tcx> OnUnimplementedFormatString {
248248
Position::ArgumentNamed(s) if s == sym::from_method => (),
249249
// `{from_desugaring}` is allowed
250250
Position::ArgumentNamed(s) if s == sym::from_desugaring => (),
251+
// `{ItemContext}` is allowed
252+
Position::ArgumentNamed(s) if s == sym::item_context => (),
251253
// So is `{A}` if A is a type parameter
252254
Position::ArgumentNamed(s) => match generics.params.iter().find(|param| {
253255
param.name.as_symbol() == s
@@ -296,6 +298,7 @@ impl<'tcx> OnUnimplementedFormatString {
296298

297299
let s = self.0.as_str();
298300
let parser = Parser::new(&s, None, vec![], false);
301+
let item_context = (options.get(&sym::item_context)).unwrap_or(&empty_string);
299302
parser.map(|p|
300303
match p {
301304
Piece::String(s) => s,
@@ -311,6 +314,8 @@ impl<'tcx> OnUnimplementedFormatString {
311314
} else if s == sym::from_desugaring || s == sym::from_method {
312315
// don't break messages using these two arguments incorrectly
313316
&empty_string
317+
} else if s == sym::item_context {
318+
&item_context
314319
} else {
315320
bug!("broken on_unimplemented {:?} for {:?}: \
316321
no argument matching {:?}",

src/libsyntax_pos/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,7 @@ symbols! {
369369
issue_5723_bootstrap,
370370
issue_tracker_base_url,
371371
item,
372+
item_context: "ItemContext",
372373
item_like_imports,
373374
iter,
374375
Iterator,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#![feature(try_trait, async_closure)]
2+
// edition:2018
3+
fn main() {}
4+
5+
async fn an_async_block() -> u32 {
6+
async {
7+
let x: Option<u32> = None;
8+
x?; //~ ERROR the `?` operator
9+
22
10+
}.await
11+
}
12+
13+
async fn async_closure_containing_fn() -> u32 {
14+
let async_closure = async || {
15+
let x: Option<u32> = None;
16+
x?; //~ ERROR the `?` operator
17+
22_u32
18+
};
19+
20+
async_closure().await
21+
}
22+
23+
async fn an_async_function() -> u32 {
24+
let x: Option<u32> = None;
25+
x?; //~ ERROR the `?` operator
26+
22
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
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`)
2+
--> $DIR/try-on-option-in-async.rs:8:9
3+
|
4+
LL | x?;
5+
| ^^ cannot use the `?` operator in an async block that returns `{integer}`
6+
|
7+
= help: the trait `std::ops::Try` is not implemented for `{integer}`
8+
= note: required by `std::ops::Try::from_error`
9+
10+
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`)
11+
--> $DIR/try-on-option-in-async.rs:16:9
12+
|
13+
LL | x?;
14+
| ^^ cannot use the `?` operator in an async closure that returns `u32`
15+
|
16+
= help: the trait `std::ops::Try` is not implemented for `u32`
17+
= note: required by `std::ops::Try::from_error`
18+
19+
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`)
20+
--> $DIR/try-on-option-in-async.rs:25:5
21+
|
22+
LL | x?;
23+
| ^^ cannot use the `?` operator in an async function that returns `u32`
24+
|
25+
= help: the trait `std::ops::Try` is not implemented for `u32`
26+
= note: required by `std::ops::Try::from_error`
27+
28+
error: aborting due to 3 previous errors
29+
30+
For more information about this error, try `rustc --explain E0277`.
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#![feature(try_trait)]
2+
// edition:2018
3+
fn main() {}
4+
5+
fn a_function() -> u32 {
6+
let x: Option<u32> = None;
7+
x?; //~ ERROR the `?` operator
8+
22
9+
}
10+
11+
fn a_closure() -> u32 {
12+
let a_closure = || {
13+
let x: Option<u32> = None;
14+
x?; //~ ERROR the `?` operator
15+
22
16+
};
17+
a_closure()
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
2+
--> $DIR/try-on-option-diagnostics.rs:7:5
3+
|
4+
LL | x?;
5+
| ^^ cannot use the `?` operator in a function that returns `u32`
6+
|
7+
= help: the trait `std::ops::Try` is not implemented for `u32`
8+
= note: required by `std::ops::Try::from_error`
9+
10+
error[E0277]: the `?` operator can only be used in a closure that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
11+
--> $DIR/try-on-option-diagnostics.rs:14:9
12+
|
13+
LL | x?;
14+
| ^^ cannot use the `?` operator in a closure that returns `{integer}`
15+
|
16+
= help: the trait `std::ops::Try` is not implemented for `{integer}`
17+
= note: required by `std::ops::Try::from_error`
18+
19+
error: aborting due to 2 previous errors
20+
21+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)