Skip to content

Commit e463a22

Browse files
authored
Rollup merge of rust-lang#64581 - ztlpn:fix-ok-wrapping-unreachable-code, r=Centril
Fix unreachable_code warnings for try{} block ok-wrapped expressions Fixes rust-lang#54165 and fixes rust-lang#63324.
2 parents 702b45e + 58eacaa commit e463a22

File tree

6 files changed

+161
-13
lines changed

6 files changed

+161
-13
lines changed

src/librustc/hir/lowering/expr.rs

+27-10
Original file line numberDiff line numberDiff line change
@@ -392,32 +392,49 @@ impl LoweringContext<'_> {
392392
)
393393
}
394394

395+
/// Desugar `try { <stmts>; <expr> }` into `{ <stmts>; ::std::ops::Try::from_ok(<expr>) }`,
396+
/// `try { <stmts>; }` into `{ <stmts>; ::std::ops::Try::from_ok(()) }`
397+
/// and save the block id to use it as a break target for desugaring of the `?` operator.
395398
fn lower_expr_try_block(&mut self, body: &Block) -> hir::ExprKind {
396399
self.with_catch_scope(body.id, |this| {
397-
let unstable_span = this.mark_span_with_reason(
400+
let mut block = this.lower_block(body, true).into_inner();
401+
402+
let try_span = this.mark_span_with_reason(
398403
DesugaringKind::TryBlock,
399404
body.span,
400405
this.allow_try_trait.clone(),
401406
);
402-
let mut block = this.lower_block(body, true).into_inner();
403-
let tail = block.expr.take().map_or_else(
404-
|| this.expr_unit(this.sess.source_map().end_point(unstable_span)),
407+
408+
// Final expression of the block (if present) or `()` with span at the end of block
409+
let tail_expr = block.expr.take().map_or_else(
410+
|| this.expr_unit(this.sess.source_map().end_point(try_span)),
405411
|x: P<hir::Expr>| x.into_inner(),
406412
);
407-
block.expr = Some(this.wrap_in_try_constructor(sym::from_ok, tail, unstable_span));
413+
414+
let ok_wrapped_span = this.mark_span_with_reason(
415+
DesugaringKind::TryBlock,
416+
tail_expr.span,
417+
None
418+
);
419+
420+
// `::std::ops::Try::from_ok($tail_expr)`
421+
block.expr = Some(this.wrap_in_try_constructor(
422+
sym::from_ok, try_span, tail_expr, ok_wrapped_span));
423+
408424
hir::ExprKind::Block(P(block), None)
409425
})
410426
}
411427

412428
fn wrap_in_try_constructor(
413429
&mut self,
414430
method: Symbol,
415-
e: hir::Expr,
416-
unstable_span: Span,
431+
method_span: Span,
432+
expr: hir::Expr,
433+
overall_span: Span,
417434
) -> P<hir::Expr> {
418435
let path = &[sym::ops, sym::Try, method];
419-
let from_err = P(self.expr_std_path(unstable_span, path, None, ThinVec::new()));
420-
P(self.expr_call(e.span, from_err, hir_vec![e]))
436+
let constructor = P(self.expr_std_path(method_span, path, None, ThinVec::new()));
437+
P(self.expr_call(overall_span, constructor, hir_vec![expr]))
421438
}
422439

423440
fn lower_arm(&mut self, arm: &Arm) -> hir::Arm {
@@ -1244,7 +1261,7 @@ impl LoweringContext<'_> {
12441261
self.expr_call_std_path(try_span, from_path, hir_vec![err_expr])
12451262
};
12461263
let from_err_expr =
1247-
self.wrap_in_try_constructor(sym::from_error, from_expr, unstable_span);
1264+
self.wrap_in_try_constructor(sym::from_error, unstable_span, from_expr, try_span);
12481265
let thin_attrs = ThinVec::from(attrs);
12491266
let catch_scope = self.catch_scopes.last().map(|x| *x);
12501267
let ret_expr = if let Some(catch_node) = catch_scope {

src/librustc/hir/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -861,7 +861,7 @@ pub struct Block {
861861
pub span: Span,
862862
/// If true, then there may exist `break 'a` values that aim to
863863
/// break out of this block early.
864-
/// Used by `'label: {}` blocks and by `catch` statements.
864+
/// Used by `'label: {}` blocks and by `try {}` blocks.
865865
pub targeted_by_break: bool,
866866
}
867867

src/librustc_typeck/check/expr.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use crate::util::nodemap::FxHashMap;
1818
use crate::astconv::AstConv as _;
1919

2020
use errors::{Applicability, DiagnosticBuilder, pluralise};
21+
use syntax_pos::hygiene::DesugaringKind;
2122
use syntax::ast;
2223
use syntax::symbol::{Symbol, kw, sym};
2324
use syntax::source_map::Span;
@@ -150,8 +151,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
150151
debug!(">> type-checking: expr={:?} expected={:?}",
151152
expr, expected);
152153

154+
// True if `expr` is a `Try::from_ok(())` that is a result of desugaring a try block
155+
// without the final expr (e.g. `try { return; }`). We don't want to generate an
156+
// unreachable_code lint for it since warnings for autogenerated code are confusing.
157+
let is_try_block_generated_unit_expr = match expr.node {
158+
ExprKind::Call(_, ref args) if expr.span.is_desugaring(DesugaringKind::TryBlock) =>
159+
args.len() == 1 && args[0].span.is_desugaring(DesugaringKind::TryBlock),
160+
161+
_ => false,
162+
};
163+
153164
// Warn for expressions after diverging siblings.
154-
self.warn_if_unreachable(expr.hir_id, expr.span, "expression");
165+
if !is_try_block_generated_unit_expr {
166+
self.warn_if_unreachable(expr.hir_id, expr.span, "expression");
167+
}
155168

156169
// Hide the outer diverging and has_errors flags.
157170
let old_diverges = self.diverges.get();
@@ -164,6 +177,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
164177
// Warn for non-block expressions with diverging children.
165178
match expr.kind {
166179
ExprKind::Block(..) | ExprKind::Loop(..) | ExprKind::Match(..) => {},
180+
// If `expr` is a result of desugaring the try block and is an ok-wrapped
181+
// diverging expression (e.g. it arose from desugaring of `try { return }`),
182+
// we skip issuing a warning because it is autogenerated code.
183+
ExprKind::Call(..) if expr.span.is_desugaring(DesugaringKind::TryBlock) => {},
167184
ExprKind::Call(ref callee, _) =>
168185
self.warn_if_unreachable(expr.hir_id, callee.span, "call"),
169186
ExprKind::MethodCall(_, ref span, _) =>

src/librustc_typeck/check/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,7 @@ pub enum Diverges {
472472
WarnedAlways
473473
}
474474

475-
// Convenience impls for combinig `Diverges`.
475+
// Convenience impls for combining `Diverges`.
476476

477477
impl ops::BitAnd for Diverges {
478478
type Output = Self;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Test unreachable_code lint for `try {}` block ok-wrapping. See issues #54165, #63324.
2+
3+
// compile-flags: --edition 2018
4+
// check-pass
5+
#![feature(try_blocks)]
6+
#![warn(unreachable_code)]
7+
8+
fn err() -> Result<u32, ()> {
9+
Err(())
10+
}
11+
12+
// In the following cases unreachable code is autogenerated and should not be reported.
13+
14+
fn test_ok_wrapped_divergent_expr_1() {
15+
let res: Result<u32, ()> = try {
16+
loop {
17+
err()?;
18+
}
19+
};
20+
println!("res: {:?}", res);
21+
}
22+
23+
fn test_ok_wrapped_divergent_expr_2() {
24+
let _: Result<u32, ()> = try {
25+
return
26+
};
27+
}
28+
29+
fn test_autogenerated_unit_after_divergent_expr() {
30+
let _: Result<(), ()> = try {
31+
return;
32+
};
33+
}
34+
35+
// In the following cases unreachable code should be reported.
36+
37+
fn test_try_block_after_divergent_stmt() {
38+
let _: Result<u32, ()> = {
39+
return;
40+
41+
try {
42+
loop {
43+
err()?;
44+
}
45+
}
46+
// ~^^^^^ WARNING unreachable expression
47+
};
48+
}
49+
50+
fn test_wrapped_divergent_expr() {
51+
let _: Result<u32, ()> = {
52+
Err(return)
53+
// ~^ WARNING unreachable call
54+
};
55+
}
56+
57+
fn test_expr_after_divergent_stmt_in_try_block() {
58+
let res: Result<u32, ()> = try {
59+
loop {
60+
err()?;
61+
}
62+
63+
42
64+
// ~^ WARNING unreachable expression
65+
};
66+
println!("res: {:?}", res);
67+
}
68+
69+
fn main() {
70+
test_ok_wrapped_divergent_expr_1();
71+
test_ok_wrapped_divergent_expr_2();
72+
test_autogenerated_unit_after_divergent_expr();
73+
test_try_block_after_divergent_stmt();
74+
test_wrapped_divergent_expr();
75+
test_expr_after_divergent_stmt_in_try_block();
76+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
warning: unreachable expression
2+
--> $DIR/try-block-unreachable-code-lint.rs:41:9
3+
|
4+
LL | return;
5+
| ------ any code following this expression is unreachable
6+
LL |
7+
LL | / try {
8+
LL | | loop {
9+
LL | | err()?;
10+
LL | | }
11+
LL | | }
12+
| |_________^ unreachable expression
13+
|
14+
note: lint level defined here
15+
--> $DIR/try-block-unreachable-code-lint.rs:6:9
16+
|
17+
LL | #![warn(unreachable_code)]
18+
| ^^^^^^^^^^^^^^^^
19+
20+
warning: unreachable call
21+
--> $DIR/try-block-unreachable-code-lint.rs:52:9
22+
|
23+
LL | Err(return)
24+
| ^^^ ------ any code following this expression is unreachable
25+
| |
26+
| unreachable call
27+
28+
warning: unreachable expression
29+
--> $DIR/try-block-unreachable-code-lint.rs:63:9
30+
|
31+
LL | / loop {
32+
LL | | err()?;
33+
LL | | }
34+
| |_________- any code following this expression is unreachable
35+
LL |
36+
LL | 42
37+
| ^^ unreachable expression
38+

0 commit comments

Comments
 (0)