Skip to content

Commit 8dacfc2

Browse files
authored
Rollup merge of #66651 - Areredify:on-unimplemented-scope, r=davidtwco
Add `enclosing scope` parameter to `rustc_on_unimplemented` Adds a new parameter to `#[rustc_on_unimplemented]`, `enclosing scope`, which highlights the function or closure scope with a message. The wip part refers to adding this annotation to `Try` trait to improve ergonomics (which I don't know how to do since I change both std and librustc) Closes #61709.
2 parents 3045d22 + 1d0c015 commit 8dacfc2

11 files changed

+235
-37
lines changed

src/libcore/ops/try.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,20 @@
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(not(bootstrap), rustc_on_unimplemented(
99
on(all(
1010
any(from_method="from_error", from_method="from_ok"),
1111
from_desugaring="QuestionMark"),
1212
message="the `?` operator can only be used in {ItemContext} \
1313
that returns `Result` or `Option` \
1414
(or another type that implements `{Try}`)",
15-
label="cannot use the `?` operator in {ItemContext} that returns `{Self}`"),
15+
label="cannot use the `?` operator in {ItemContext} that returns `{Self}`",
16+
enclosing_scope="this function should return `Result` or `Option` to accept `?`"),
1617
on(all(from_method="into_result", from_desugaring="QuestionMark"),
1718
message="the `?` operator can only be applied to values \
1819
that implement `{Try}`",
1920
label="the `?` operator cannot be applied to type `{Self}`")
20-
)]
21+
))]
2122
#[doc(alias = "?")]
2223
pub trait Try {
2324
/// The type of this value when viewed as successful.

src/librustc/traits/error_reporting.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
521521
) {
522522
command.evaluate(self.tcx, trait_ref, &flags[..])
523523
} else {
524-
OnUnimplementedNote::empty()
524+
OnUnimplementedNote::default()
525525
}
526526
}
527527

@@ -697,6 +697,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
697697
fallback_has_occurred: bool,
698698
points_at_arg: bool,
699699
) {
700+
let tcx = self.tcx;
700701
let span = obligation.cause.span;
701702

702703
let mut err = match *error {
@@ -732,6 +733,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
732733
message,
733734
label,
734735
note,
736+
enclosing_scope,
735737
} = self.on_unimplemented_note(trait_ref, obligation);
736738
let have_alt_message = message.is_some() || label.is_some();
737739
let is_try = self.tcx.sess.source_map().span_to_snippet(span)
@@ -798,6 +800,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
798800
// If it has a custom `#[rustc_on_unimplemented]` note, let's display it
799801
err.note(s.as_str());
800802
}
803+
if let Some(ref s) = enclosing_scope {
804+
let enclosing_scope_span = tcx.def_span(
805+
tcx.hir()
806+
.opt_local_def_id(obligation.cause.body_id)
807+
.unwrap_or_else(|| {
808+
tcx.hir().body_owner_def_id(hir::BodyId {
809+
hir_id: obligation.cause.body_id,
810+
})
811+
}),
812+
);
813+
814+
err.span_label(enclosing_scope_span, s.as_str());
815+
}
801816

802817
self.suggest_borrow_on_unsized_slice(&obligation.cause.code, &mut err);
803818
self.suggest_fn_call(&obligation, &mut err, &trait_ref, points_at_arg);

src/librustc/traits/on_unimplemented.rs

+33-13
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,15 @@ pub struct OnUnimplementedDirective {
2222
pub message: Option<OnUnimplementedFormatString>,
2323
pub label: Option<OnUnimplementedFormatString>,
2424
pub note: Option<OnUnimplementedFormatString>,
25+
pub enclosing_scope: Option<OnUnimplementedFormatString>,
2526
}
2627

28+
#[derive(Default)]
2729
pub struct OnUnimplementedNote {
2830
pub message: Option<String>,
2931
pub label: Option<String>,
3032
pub note: Option<String>,
31-
}
32-
33-
impl OnUnimplementedNote {
34-
pub fn empty() -> Self {
35-
OnUnimplementedNote { message: None, label: None, note: None }
36-
}
33+
pub enclosing_scope: Option<String>,
3734
}
3835

3936
fn parse_error(
@@ -85,24 +82,33 @@ impl<'tcx> OnUnimplementedDirective {
8582
let mut message = None;
8683
let mut label = None;
8784
let mut note = None;
85+
let mut enclosing_scope = None;
8886
let mut subcommands = vec![];
87+
88+
let parse_value = |value_str| {
89+
OnUnimplementedFormatString::try_parse(tcx, trait_def_id, value_str, span)
90+
.map(Some)
91+
};
92+
8993
for item in item_iter {
9094
if item.check_name(sym::message) && message.is_none() {
9195
if let Some(message_) = item.value_str() {
92-
message = Some(OnUnimplementedFormatString::try_parse(
93-
tcx, trait_def_id, message_, span)?);
96+
message = parse_value(message_)?;
9497
continue;
9598
}
9699
} else if item.check_name(sym::label) && label.is_none() {
97100
if let Some(label_) = item.value_str() {
98-
label = Some(OnUnimplementedFormatString::try_parse(
99-
tcx, trait_def_id, label_, span)?);
101+
label = parse_value(label_)?;
100102
continue;
101103
}
102104
} else if item.check_name(sym::note) && note.is_none() {
103105
if let Some(note_) = item.value_str() {
104-
note = Some(OnUnimplementedFormatString::try_parse(
105-
tcx, trait_def_id, note_, span)?);
106+
note = parse_value(note_)?;
107+
continue;
108+
}
109+
} else if item.check_name(sym::enclosing_scope) && enclosing_scope.is_none() {
110+
if let Some(enclosing_scope_) = item.value_str() {
111+
enclosing_scope = parse_value(enclosing_scope_)?;
106112
continue;
107113
}
108114
} else if item.check_name(sym::on) && is_root &&
@@ -130,7 +136,14 @@ impl<'tcx> OnUnimplementedDirective {
130136
if errored {
131137
Err(ErrorReported)
132138
} else {
133-
Ok(OnUnimplementedDirective { condition, message, label, subcommands, note })
139+
Ok(OnUnimplementedDirective {
140+
condition,
141+
subcommands,
142+
message,
143+
label,
144+
note,
145+
enclosing_scope
146+
})
134147
}
135148
}
136149

@@ -157,6 +170,7 @@ impl<'tcx> OnUnimplementedDirective {
157170
label: Some(OnUnimplementedFormatString::try_parse(
158171
tcx, trait_def_id, value, attr.span)?),
159172
note: None,
173+
enclosing_scope: None,
160174
}))
161175
} else {
162176
return Err(ErrorReported);
@@ -174,6 +188,7 @@ impl<'tcx> OnUnimplementedDirective {
174188
let mut message = None;
175189
let mut label = None;
176190
let mut note = None;
191+
let mut enclosing_scope = None;
177192
info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options);
178193

179194
for command in self.subcommands.iter().chain(Some(self)).rev() {
@@ -202,6 +217,10 @@ impl<'tcx> OnUnimplementedDirective {
202217
if let Some(ref note_) = command.note {
203218
note = Some(note_.clone());
204219
}
220+
221+
if let Some(ref enclosing_scope_) = command.enclosing_scope {
222+
enclosing_scope = Some(enclosing_scope_.clone());
223+
}
205224
}
206225

207226
let options: FxHashMap<Symbol, String> = options.into_iter()
@@ -211,6 +230,7 @@ impl<'tcx> OnUnimplementedDirective {
211230
label: label.map(|l| l.format(tcx, trait_ref, &options)),
212231
message: message.map(|m| m.format(tcx, trait_ref, &options)),
213232
note: note.map(|n| n.format(tcx, trait_ref, &options)),
233+
enclosing_scope: enclosing_scope.map(|e_s| e_s.format(tcx, trait_ref, &options)),
214234
}
215235
}
216236
}

src/libsyntax_pos/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ symbols! {
280280
Err,
281281
Eq,
282282
Equal,
283+
enclosing_scope,
283284
except,
284285
exclusive_range_pattern,
285286
exhaustive_integer_patterns,

src/test/ui/async-await/try-on-option-in-async.stderr

+24-6
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,44 @@
11
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`)
22
--> $DIR/try-on-option-in-async.rs:8:9
33
|
4-
LL | x?;
5-
| ^^ cannot use the `?` operator in an async block that returns `{integer}`
4+
LL | async {
5+
| ___________-
6+
LL | | let x: Option<u32> = None;
7+
LL | | x?;
8+
| | ^^ cannot use the `?` operator in an async block that returns `{integer}`
9+
LL | | 22
10+
LL | | }.await
11+
| |_____- this function should return `Result` or `Option` to accept `?`
612
|
713
= help: the trait `std::ops::Try` is not implemented for `{integer}`
814
= note: required by `std::ops::Try::from_error`
915

1016
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`)
1117
--> $DIR/try-on-option-in-async.rs:16:9
1218
|
13-
LL | x?;
14-
| ^^ cannot use the `?` operator in an async closure that returns `u32`
19+
LL | let async_closure = async || {
20+
| __________________________________-
21+
LL | | let x: Option<u32> = None;
22+
LL | | x?;
23+
| | ^^ cannot use the `?` operator in an async closure that returns `u32`
24+
LL | | 22_u32
25+
LL | | };
26+
| |_____- this function should return `Result` or `Option` to accept `?`
1527
|
1628
= help: the trait `std::ops::Try` is not implemented for `u32`
1729
= note: required by `std::ops::Try::from_error`
1830

1931
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`)
2032
--> $DIR/try-on-option-in-async.rs:25:5
2133
|
22-
LL | x?;
23-
| ^^ cannot use the `?` operator in an async function that returns `u32`
34+
LL | async fn an_async_function() -> u32 {
35+
| _____________________________________-
36+
LL | | let x: Option<u32> = None;
37+
LL | | x?;
38+
| | ^^ cannot use the `?` operator in an async function that returns `u32`
39+
LL | | 22
40+
LL | | }
41+
| |_- this function should return `Result` or `Option` to accept `?`
2442
|
2543
= help: the trait `std::ops::Try` is not implemented for `u32`
2644
= note: required by `std::ops::Try::from_error`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Test scope annotations from `enclosing_scope` parameter
2+
3+
#![feature(rustc_attrs)]
4+
5+
#[rustc_on_unimplemented(enclosing_scope="in this scope")]
6+
trait Trait{}
7+
8+
struct Foo;
9+
10+
fn f<T: Trait>(x: T) {}
11+
12+
fn main() {
13+
let x = || {
14+
f(Foo{}); //~ ERROR the trait bound `Foo: Trait` is not satisfied
15+
let y = || {
16+
f(Foo{}); //~ ERROR the trait bound `Foo: Trait` is not satisfied
17+
};
18+
};
19+
20+
{
21+
{
22+
f(Foo{}); //~ ERROR the trait bound `Foo: Trait` is not satisfied
23+
}
24+
}
25+
26+
f(Foo{}); //~ ERROR the trait bound `Foo: Trait` is not satisfied
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
error[E0277]: the trait bound `Foo: Trait` is not satisfied
2+
--> $DIR/enclosing-scope.rs:14:11
3+
|
4+
LL | fn f<T: Trait>(x: T) {}
5+
| - ----- required by this bound in `f`
6+
...
7+
LL | let x = || {
8+
| _____________-
9+
LL | | f(Foo{});
10+
| | ^^^^^ the trait `Trait` is not implemented for `Foo`
11+
LL | | let y = || {
12+
LL | | f(Foo{});
13+
LL | | };
14+
LL | | };
15+
| |_____- in this scope
16+
17+
error[E0277]: the trait bound `Foo: Trait` is not satisfied
18+
--> $DIR/enclosing-scope.rs:16:15
19+
|
20+
LL | fn f<T: Trait>(x: T) {}
21+
| - ----- required by this bound in `f`
22+
...
23+
LL | let y = || {
24+
| _________________-
25+
LL | | f(Foo{});
26+
| | ^^^^^ the trait `Trait` is not implemented for `Foo`
27+
LL | | };
28+
| |_________- in this scope
29+
30+
error[E0277]: the trait bound `Foo: Trait` is not satisfied
31+
--> $DIR/enclosing-scope.rs:22:15
32+
|
33+
LL | fn f<T: Trait>(x: T) {}
34+
| - ----- required by this bound in `f`
35+
LL |
36+
LL | / fn main() {
37+
LL | | let x = || {
38+
LL | | f(Foo{});
39+
LL | | let y = || {
40+
... |
41+
LL | | f(Foo{});
42+
| | ^^^^^ the trait `Trait` is not implemented for `Foo`
43+
... |
44+
LL | | f(Foo{});
45+
LL | | }
46+
| |_- in this scope
47+
48+
error[E0277]: the trait bound `Foo: Trait` is not satisfied
49+
--> $DIR/enclosing-scope.rs:26:7
50+
|
51+
LL | fn f<T: Trait>(x: T) {}
52+
| - ----- required by this bound in `f`
53+
LL |
54+
LL | / fn main() {
55+
LL | | let x = || {
56+
LL | | f(Foo{});
57+
LL | | let y = || {
58+
... |
59+
LL | | f(Foo{});
60+
| | ^^^^^ the trait `Trait` is not implemented for `Foo`
61+
LL | | }
62+
| |_- in this scope
63+
64+
error: aborting due to 4 previous errors
65+
66+
For more information about this error, try `rustc --explain E0277`.

src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr

+33-6
Original file line numberDiff line numberDiff line change
@@ -575,8 +575,17 @@ LL | if (let 0 = 0)? {}
575575
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
576576
--> $DIR/disallowed-positions.rs:46:8
577577
|
578-
LL | if (let 0 = 0)? {}
579-
| ^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
578+
LL | / fn nested_within_if_expr() {
579+
LL | | if &let 0 = 0 {}
580+
LL | |
581+
LL | |
582+
... |
583+
LL | | if (let 0 = 0)? {}
584+
| | ^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
585+
... |
586+
LL | | if let true = let true = true {}
587+
LL | | }
588+
| |_- this function should return `Result` or `Option` to accept `?`
580589
|
581590
= help: the trait `std::ops::Try` is not implemented for `()`
582591
= note: required by `std::ops::Try::from_error`
@@ -754,8 +763,17 @@ LL | while (let 0 = 0)? {}
754763
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
755764
--> $DIR/disallowed-positions.rs:110:11
756765
|
757-
LL | while (let 0 = 0)? {}
758-
| ^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
766+
LL | / fn nested_within_while_expr() {
767+
LL | | while &let 0 = 0 {}
768+
LL | |
769+
LL | |
770+
... |
771+
LL | | while (let 0 = 0)? {}
772+
| | ^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
773+
... |
774+
LL | | while let true = let true = true {}
775+
LL | | }
776+
| |_- this function should return `Result` or `Option` to accept `?`
759777
|
760778
= help: the trait `std::ops::Try` is not implemented for `()`
761779
= note: required by `std::ops::Try::from_error`
@@ -924,8 +942,17 @@ LL | (let 0 = 0)?;
924942
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
925943
--> $DIR/disallowed-positions.rs:183:5
926944
|
927-
LL | (let 0 = 0)?;
928-
| ^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
945+
LL | / fn outside_if_and_while_expr() {
946+
LL | | &let 0 = 0;
947+
LL | |
948+
LL | | !let 0 = 0;
949+
... |
950+
LL | | (let 0 = 0)?;
951+
| | ^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
952+
... |
953+
LL | |
954+
LL | | }
955+
| |_- this function should return `Result` or `Option` to accept `?`
929956
|
930957
= help: the trait `std::ops::Try` is not implemented for `()`
931958
= note: required by `std::ops::Try::from_error`

0 commit comments

Comments
 (0)