Skip to content

Commit 3c28682

Browse files
authored
Rollup merge of rust-lang#64592 - Aaron1011:feature/unreachable-span, r=Centril
Point at original span when emitting unreachable lint Fixes rust-lang#64590 When we emit an 'unreachable' lint, we now add a note pointing at the expression that actually causes the code to be unreachable (e.g. `return`, `break`, `panic`). This is especially useful when macros are involved, since a diverging expression might be hidden inside of a macro invocation.
2 parents bda52e5 + d67528f commit 3c28682

37 files changed

+372
-14
lines changed

src/librustc_typeck/check/_match.rs

+18-3
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
4343

4444
// If there are no arms, that is a diverging match; a special case.
4545
if arms.is_empty() {
46-
self.diverges.set(self.diverges.get() | Diverges::Always);
46+
self.diverges.set(self.diverges.get() | Diverges::always(expr.span));
4747
return tcx.types.never;
4848
}
4949

@@ -69,7 +69,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
6969
// warnings).
7070
match all_pats_diverge {
7171
Diverges::Maybe => Diverges::Maybe,
72-
Diverges::Always | Diverges::WarnedAlways => Diverges::WarnedAlways,
72+
Diverges::Always { .. } | Diverges::WarnedAlways => Diverges::WarnedAlways,
7373
}
7474
}).collect();
7575

@@ -167,6 +167,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
167167
prior_arm_ty = Some(arm_ty);
168168
}
169169

170+
// If all of the arms in the `match` diverge,
171+
// and we're dealing with an actual `match` block
172+
// (as opposed to a `match` desugared from something else'),
173+
// we can emit a better note. Rather than pointing
174+
// at a diverging expression in an arbitrary arm,
175+
// we can point at the entire `match` expression
176+
if let (Diverges::Always { .. }, hir::MatchSource::Normal) = (all_arms_diverge, match_src) {
177+
all_arms_diverge = Diverges::Always {
178+
span: expr.span,
179+
custom_note: Some(
180+
"any code following this `match` expression is unreachable, as all arms diverge"
181+
)
182+
};
183+
}
184+
170185
// We won't diverge unless the discriminant or all arms diverge.
171186
self.diverges.set(discrim_diverges | all_arms_diverge);
172187

@@ -176,7 +191,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
176191
/// When the previously checked expression (the scrutinee) diverges,
177192
/// warn the user about the match arms being unreachable.
178193
fn warn_arms_when_scrutinee_diverges(&self, arms: &'tcx [hir::Arm], source: hir::MatchSource) {
179-
if self.diverges.get().always() {
194+
if self.diverges.get().is_always() {
180195
use hir::MatchSource::*;
181196
let msg = match source {
182197
IfDesugar { .. } | IfLetDesugar { .. } => "block in `if` expression",

src/librustc_typeck/check/expr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
170170

171171
// Any expression that produces a value of type `!` must have diverged
172172
if ty.is_never() {
173-
self.diverges.set(self.diverges.get() | Diverges::Always);
173+
self.diverges.set(self.diverges.get() | Diverges::always(expr.span));
174174
}
175175

176176
// Record the type, which applies it effects.

src/librustc_typeck/check/mod.rs

+45-10
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,20 @@ pub enum Diverges {
450450

451451
/// Definitely known to diverge and therefore
452452
/// not reach the next sibling or its parent.
453-
Always,
453+
Always {
454+
/// The `Span` points to the expression
455+
/// that caused us to diverge
456+
/// (e.g. `return`, `break`, etc).
457+
span: Span,
458+
/// In some cases (e.g. a `match` expression
459+
/// where all arms diverge), we may be
460+
/// able to provide a more informative
461+
/// message to the user.
462+
/// If this is `None`, a default messsage
463+
/// will be generated, which is suitable
464+
/// for most cases.
465+
custom_note: Option<&'static str>
466+
},
454467

455468
/// Same as `Always` but with a reachability
456469
/// warning already emitted.
@@ -486,8 +499,22 @@ impl ops::BitOrAssign for Diverges {
486499
}
487500

488501
impl Diverges {
489-
fn always(self) -> bool {
490-
self >= Diverges::Always
502+
/// Creates a `Diverges::Always` with the provided `span` and the default note message.
503+
fn always(span: Span) -> Diverges {
504+
Diverges::Always {
505+
span,
506+
custom_note: None
507+
}
508+
}
509+
510+
fn is_always(self) -> bool {
511+
// Enum comparison ignores the
512+
// contents of fields, so we just
513+
// fill them in with garbage here.
514+
self >= Diverges::Always {
515+
span: DUMMY_SP,
516+
custom_note: None
517+
}
491518
}
492519
}
493520

@@ -2307,17 +2334,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
23072334
/// Produces warning on the given node, if the current point in the
23082335
/// function is unreachable, and there hasn't been another warning.
23092336
fn warn_if_unreachable(&self, id: hir::HirId, span: Span, kind: &str) {
2310-
if self.diverges.get() == Diverges::Always &&
2337+
// FIXME: Combine these two 'if' expressions into one once
2338+
// let chains are implemented
2339+
if let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() {
23112340
// If span arose from a desugaring of `if` or `while`, then it is the condition itself,
23122341
// which diverges, that we are about to lint on. This gives suboptimal diagnostics.
23132342
// Instead, stop here so that the `if`- or `while`-expression's block is linted instead.
2314-
!span.is_desugaring(DesugaringKind::CondTemporary) {
2315-
self.diverges.set(Diverges::WarnedAlways);
2343+
if !span.is_desugaring(DesugaringKind::CondTemporary) {
2344+
self.diverges.set(Diverges::WarnedAlways);
23162345

2317-
debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
2346+
debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
23182347

2319-
let msg = format!("unreachable {}", kind);
2320-
self.tcx().lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, &msg);
2348+
let msg = format!("unreachable {}", kind);
2349+
self.tcx().struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, &msg)
2350+
.span_note(
2351+
orig_span,
2352+
custom_note.unwrap_or("any code following this expression is unreachable")
2353+
)
2354+
.emit();
2355+
}
23212356
}
23222357
}
23232358

@@ -3825,7 +3860,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
38253860
//
38263861
// #41425 -- label the implicit `()` as being the
38273862
// "found type" here, rather than the "expected type".
3828-
if !self.diverges.get().always() {
3863+
if !self.diverges.get().is_always() {
38293864
// #50009 -- Do not point at the entire fn block span, point at the return type
38303865
// span, as it is the cause of the requirement, and
38313866
// `consider_hint_about_removing_semicolon` will point at the last expression

src/test/ui/dead-code-ret.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ note: lint level defined here
99
|
1010
LL | #![deny(unreachable_code)]
1111
| ^^^^^^^^^^^^^^^^
12+
note: any code following this expression is unreachable
13+
--> $DIR/dead-code-ret.rs:6:5
14+
|
15+
LL | return;
16+
| ^^^^^^
1217
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
1318

1419
error: aborting due to previous error

src/test/ui/if-ret.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,9 @@ LL | fn foo() { if (return) { } }
55
| ^^^
66
|
77
= note: `#[warn(unreachable_code)]` on by default
8+
note: any code following this expression is unreachable
9+
--> $DIR/if-ret.rs:6:15
10+
|
11+
LL | fn foo() { if (return) { } }
12+
| ^^^^^^^^
813

src/test/ui/issues/issue-2150.stderr

+6
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ note: lint level defined here
99
|
1010
LL | #![deny(unreachable_code)]
1111
| ^^^^^^^^^^^^^^^^
12+
note: any code following this expression is unreachable
13+
--> $DIR/issue-2150.rs:7:5
14+
|
15+
LL | panic!();
16+
| ^^^^^^^^^
17+
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
1218

1319
error: aborting due to previous error
1420

src/test/ui/issues/issue-7246.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ note: lint level defined here
99
|
1010
LL | #![deny(unreachable_code)]
1111
| ^^^^^^^^^^^^^^^^
12+
note: any code following this expression is unreachable
13+
--> $DIR/issue-7246.rs:6:5
14+
|
15+
LL | return;
16+
| ^^^^^^
1217

1318
error: aborting due to previous error
1419

src/test/ui/lint/lint-attr-non-item-node.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ note: lint level defined here
99
|
1010
LL | #[deny(unreachable_code)]
1111
| ^^^^^^^^^^^^^^^^
12+
note: any code following this expression is unreachable
13+
--> $DIR/lint-attr-non-item-node.rs:6:9
14+
|
15+
LL | break;
16+
| ^^^^^
1217

1318
error: aborting due to previous error
1419

src/test/ui/liveness/liveness-unused.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ note: lint level defined here
1010
LL | #![warn(unused)]
1111
| ^^^^^^
1212
= note: `#[warn(unreachable_code)]` implied by `#[warn(unused)]`
13+
note: any code following this expression is unreachable
14+
--> $DIR/liveness-unused.rs:91:9
15+
|
16+
LL | continue;
17+
| ^^^^^^^^
1318

1419
error: unused variable: `x`
1520
--> $DIR/liveness-unused.rs:8:7

src/test/ui/match/match-no-arms-unreachable-after.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ note: lint level defined here
99
|
1010
LL | #![deny(unreachable_code)]
1111
| ^^^^^^^^^^^^^^^^
12+
note: any code following this expression is unreachable
13+
--> $DIR/match-no-arms-unreachable-after.rs:7:5
14+
|
15+
LL | match v { }
16+
| ^^^^^^^^^^^
1217

1318
error: aborting due to previous error
1419

src/test/ui/never-assign-dead-code.stderr

+12
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,24 @@ note: lint level defined here
1010
LL | #![warn(unused)]
1111
| ^^^^^^
1212
= note: `#[warn(unreachable_code)]` implied by `#[warn(unused)]`
13+
note: any code following this expression is unreachable
14+
--> $DIR/never-assign-dead-code.rs:9:16
15+
|
16+
LL | let x: ! = panic!("aah");
17+
| ^^^^^^^^^^^^^
18+
= note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
1319

1420
warning: unreachable call
1521
--> $DIR/never-assign-dead-code.rs:10:5
1622
|
1723
LL | drop(x);
1824
| ^^^^
25+
|
26+
note: any code following this expression is unreachable
27+
--> $DIR/never-assign-dead-code.rs:10:10
28+
|
29+
LL | drop(x);
30+
| ^
1931

2032
warning: unused variable: `x`
2133
--> $DIR/never-assign-dead-code.rs:9:9

src/test/ui/reachable/expr_add.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ note: lint level defined here
99
|
1010
LL | #![deny(unreachable_code)]
1111
| ^^^^^^^^^^^^^^^^
12+
note: any code following this expression is unreachable
13+
--> $DIR/expr_add.rs:17:19
14+
|
15+
LL | let x = Foo + return;
16+
| ^^^^^^
1217

1318
error: aborting due to previous error
1419

src/test/ui/reachable/expr_again.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ note: lint level defined here
99
|
1010
LL | #![deny(unreachable_code)]
1111
| ^^^^^^^^^^^^^^^^
12+
note: any code following this expression is unreachable
13+
--> $DIR/expr_again.rs:7:9
14+
|
15+
LL | continue;
16+
| ^^^^^^^^
1217
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
1318

1419
error: aborting due to previous error

src/test/ui/reachable/expr_array.stderr

+11
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,23 @@ note: lint level defined here
99
|
1010
LL | #![deny(unreachable_code)]
1111
| ^^^^^^^^^^^^^^^^
12+
note: any code following this expression is unreachable
13+
--> $DIR/expr_array.rs:9:26
14+
|
15+
LL | let x: [usize; 2] = [return, 22];
16+
| ^^^^^^
1217

1318
error: unreachable expression
1419
--> $DIR/expr_array.rs:14:25
1520
|
1621
LL | let x: [usize; 2] = [22, return];
1722
| ^^^^^^^^^^^^
23+
|
24+
note: any code following this expression is unreachable
25+
--> $DIR/expr_array.rs:14:30
26+
|
27+
LL | let x: [usize; 2] = [22, return];
28+
| ^^^^^^
1829

1930
error: aborting due to 2 previous errors
2031

src/test/ui/reachable/expr_assign.stderr

+17
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,35 @@ note: lint level defined here
99
|
1010
LL | #![deny(unreachable_code)]
1111
| ^^^^^^^^^^^^^^^^
12+
note: any code following this expression is unreachable
13+
--> $DIR/expr_assign.rs:10:9
14+
|
15+
LL | x = return;
16+
| ^^^^^^
1217

1318
error: unreachable expression
1419
--> $DIR/expr_assign.rs:20:14
1520
|
1621
LL | *p = return;
1722
| ^^^^^^
23+
|
24+
note: any code following this expression is unreachable
25+
--> $DIR/expr_assign.rs:20:9
26+
|
27+
LL | *p = return;
28+
| ^^
1829

1930
error: unreachable expression
2031
--> $DIR/expr_assign.rs:26:15
2132
|
2233
LL | *{return; &mut i} = 22;
2334
| ^^^^^^
35+
|
36+
note: any code following this expression is unreachable
37+
--> $DIR/expr_assign.rs:26:7
38+
|
39+
LL | *{return; &mut i} = 22;
40+
| ^^^^^^
2441

2542
error: aborting due to 3 previous errors
2643

src/test/ui/reachable/expr_block.stderr

+10
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,23 @@ note: lint level defined here
99
|
1010
LL | #![deny(unreachable_code)]
1111
| ^^^^^^^^^^^^^^^^
12+
note: any code following this expression is unreachable
13+
--> $DIR/expr_block.rs:9:9
14+
|
15+
LL | return;
16+
| ^^^^^^
1217

1318
error: unreachable statement
1419
--> $DIR/expr_block.rs:25:9
1520
|
1621
LL | println!("foo");
1722
| ^^^^^^^^^^^^^^^^
1823
|
24+
note: any code following this expression is unreachable
25+
--> $DIR/expr_block.rs:24:9
26+
|
27+
LL | return;
28+
| ^^^^^^
1929
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
2030

2131
error: aborting due to 2 previous errors

src/test/ui/reachable/expr_box.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ note: lint level defined here
99
|
1010
LL | #![deny(unreachable_code)]
1111
| ^^^^^^^^^^^^^^^^
12+
note: any code following this expression is unreachable
13+
--> $DIR/expr_box.rs:6:17
14+
|
15+
LL | let x = box return;
16+
| ^^^^^^
1217

1318
error: aborting due to previous error
1419

src/test/ui/reachable/expr_call.stderr

+11
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,23 @@ note: lint level defined here
99
|
1010
LL | #![deny(unreachable_code)]
1111
| ^^^^^^^^^^^^^^^^
12+
note: any code following this expression is unreachable
13+
--> $DIR/expr_call.rs:13:9
14+
|
15+
LL | foo(return, 22);
16+
| ^^^^^^
1217

1318
error: unreachable call
1419
--> $DIR/expr_call.rs:18:5
1520
|
1621
LL | bar(return);
1722
| ^^^
23+
|
24+
note: any code following this expression is unreachable
25+
--> $DIR/expr_call.rs:18:9
26+
|
27+
LL | bar(return);
28+
| ^^^^^^
1829

1930
error: aborting due to 2 previous errors
2031

0 commit comments

Comments
 (0)