Skip to content

Commit 1b240da

Browse files
committed
Auto merge of #50763 - KyleStach1678:unused-loop-label, r=petrochenkov
Add lint checks for unused loop labels Previously no warning was generated when a loop label was written out but never used (i.e. in a `break` or `continue` statement): ```rust fn main() { 'unused_label: loop {} } ``` would compile without complaint. This fix introduces `-W unused_loop_label`, which generates the following warning message for the above snippet: ``` warning: unused loop label --> main.rs:2:5 | 2 | 'unused_label: loop {} | ^^^^^^^^^^^^^ | = note: #[warn(unused_loop_label)] on by default ``` Fixes: #50751.
2 parents ef8ee64 + 6da64a7 commit 1b240da

15 files changed

+210
-27
lines changed

src/librustc/lint/builtin.rs

+7
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,12 @@ declare_lint! {
273273
"detects name collision with an existing but unstable method"
274274
}
275275

276+
declare_lint! {
277+
pub UNUSED_LABELS,
278+
Allow,
279+
"detects labels that are never used"
280+
}
281+
276282
/// Does nothing as a lint pass, but registers some `Lint`s
277283
/// which are used by other parts of the compiler.
278284
#[derive(Copy, Clone)]
@@ -318,6 +324,7 @@ impl LintPass for HardwiredLints {
318324
UNUSED_MUT,
319325
SINGLE_USE_LIFETIME,
320326
UNUSED_LIFETIME,
327+
UNUSED_LABELS,
321328
TYVAR_BEHIND_RAW_POINTER,
322329
ELIDED_LIFETIME_IN_PATH,
323330
BARE_TRAIT_OBJECT,

src/librustc_lint/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
177177
UNUSED_DOC_COMMENT,
178178
UNUSED_EXTERN_CRATES,
179179
UNUSED_FEATURES,
180+
UNUSED_LABELS,
180181
UNUSED_PARENS);
181182

182183
add_lint_group!(sess,

src/librustc_resolve/check_unused.rs

+4
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ pub fn check_crate(resolver: &mut Resolver, krate: &ast::Crate) {
142142
}
143143
}
144144

145+
for (id, span) in resolver.unused_labels.iter() {
146+
resolver.session.buffer_lint(lint::builtin::UNUSED_LABELS, *id, *span, "unused label");
147+
}
148+
145149
let mut visitor = UnusedImportCheckVisitor {
146150
resolver,
147151
unused_imports: NodeMap(),

src/librustc_resolve/lib.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -1468,6 +1468,10 @@ pub struct Resolver<'a> {
14681468
pub maybe_unused_trait_imports: NodeSet,
14691469
pub maybe_unused_extern_crates: Vec<(NodeId, Span)>,
14701470

1471+
/// A list of labels as of yet unused. Labels will be removed from this map when
1472+
/// they are used (in a `break` or `continue` statement)
1473+
pub unused_labels: FxHashMap<NodeId, Span>,
1474+
14711475
/// privacy errors are delayed until the end in order to deduplicate them
14721476
privacy_errors: Vec<PrivacyError<'a>>,
14731477
/// ambiguity errors are delayed for deduplication
@@ -1747,6 +1751,8 @@ impl<'a> Resolver<'a> {
17471751
maybe_unused_trait_imports: NodeSet(),
17481752
maybe_unused_extern_crates: Vec::new(),
17491753

1754+
unused_labels: FxHashMap(),
1755+
17501756
privacy_errors: Vec::new(),
17511757
ambiguity_errors: Vec::new(),
17521758
use_injections: Vec::new(),
@@ -3682,6 +3688,7 @@ impl<'a> Resolver<'a> {
36823688
where F: FnOnce(&mut Resolver)
36833689
{
36843690
if let Some(label) = label {
3691+
self.unused_labels.insert(id, label.ident.span);
36853692
let def = Def::Label(id);
36863693
self.with_label_rib(|this| {
36873694
this.label_ribs.last_mut().unwrap().bindings.insert(label.ident, def);
@@ -3730,9 +3737,10 @@ impl<'a> Resolver<'a> {
37303737
ResolutionError::UndeclaredLabel(&label.ident.name.as_str(),
37313738
close_match));
37323739
}
3733-
Some(def @ Def::Label(_)) => {
3740+
Some(Def::Label(id)) => {
37343741
// Since this def is a label, it is never read.
3735-
self.record_def(expr.id, PathResolution::new(def));
3742+
self.record_def(expr.id, PathResolution::new(Def::Label(id)));
3743+
self.unused_labels.remove(&id);
37363744
}
37373745
Some(_) => {
37383746
span_bug!(expr.span, "label wasn't mapped to a label def!");

src/test/ui/label_break_value_continue.rs

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
// except according to those terms.
1010

1111
#![feature(label_break_value)]
12+
#![allow(unused_labels)]
1213

1314
// Simple continue pointing to an unlabeled break should yield in an error
1415
fn continue_simple() {

src/test/ui/label_break_value_continue.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
error[E0695]: unlabeled `continue` inside of a labeled block
2-
--> $DIR/label_break_value_continue.rs:16:9
2+
--> $DIR/label_break_value_continue.rs:17:9
33
|
44
LL | continue; //~ ERROR unlabeled `continue` inside of a labeled block
55
| ^^^^^^^^ `continue` statements that would diverge to or through a labeled block need to bear a label
66

77
error[E0696]: `continue` pointing to a labeled block
8-
--> $DIR/label_break_value_continue.rs:23:9
8+
--> $DIR/label_break_value_continue.rs:24:9
99
|
1010
LL | continue 'b; //~ ERROR `continue` pointing to a labeled block
1111
| ^^^^^^^^^^^ labeled blocks cannot be `continue`'d
1212
|
1313
note: labeled block the continue points to
14-
--> $DIR/label_break_value_continue.rs:22:5
14+
--> $DIR/label_break_value_continue.rs:23:5
1515
|
1616
LL | / 'b: {
1717
LL | | continue 'b; //~ ERROR `continue` pointing to a labeled block
1818
LL | | }
1919
| |_____^
2020

2121
error[E0695]: unlabeled `continue` inside of a labeled block
22-
--> $DIR/label_break_value_continue.rs:31:13
22+
--> $DIR/label_break_value_continue.rs:32:13
2323
|
2424
LL | continue; //~ ERROR unlabeled `continue` inside of a labeled block
2525
| ^^^^^^^^ `continue` statements that would diverge to or through a labeled block need to bear a label

src/test/ui/label_break_value_unlabeled_break.rs

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
// except according to those terms.
1010

1111
#![feature(label_break_value)]
12+
#![allow(unused_labels)]
1213

1314
// Simple unlabeled break should yield in an error
1415
fn unlabeled_break_simple() {

src/test/ui/label_break_value_unlabeled_break.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
error[E0695]: unlabeled `break` inside of a labeled block
2-
--> $DIR/label_break_value_unlabeled_break.rs:16:9
2+
--> $DIR/label_break_value_unlabeled_break.rs:17:9
33
|
44
LL | break; //~ ERROR unlabeled `break` inside of a labeled block
55
| ^^^^^ `break` statements that would diverge to or through a labeled block need to bear a label
66

77
error[E0695]: unlabeled `break` inside of a labeled block
8-
--> $DIR/label_break_value_unlabeled_break.rs:24:13
8+
--> $DIR/label_break_value_unlabeled_break.rs:25:13
99
|
1010
LL | break; //~ ERROR unlabeled `break` inside of a labeled block
1111
| ^^^^^ `break` statements that would diverge to or through a labeled block need to bear a label

src/test/ui/lint/unused_labels.rs

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// The output should warn when a loop label is not used. However, it
12+
// should also deal with the edge cases where a label is shadowed,
13+
// within nested loops
14+
15+
// compile-pass
16+
17+
#![feature(label_break_value)]
18+
#![warn(unused_labels)]
19+
20+
fn main() {
21+
'unused_while_label: while 0 == 0 {
22+
//~^ WARN unused label
23+
}
24+
25+
let opt = Some(0);
26+
'unused_while_let_label: while let Some(_) = opt {
27+
//~^ WARN unused label
28+
}
29+
30+
'unused_for_label: for _ in 0..10 {
31+
//~^ WARN unused label
32+
}
33+
34+
'used_loop_label: loop {
35+
break 'used_loop_label;
36+
}
37+
38+
'used_loop_label_outer_1: for _ in 0..10 {
39+
'used_loop_label_inner_1: for _ in 0..10 {
40+
break 'used_loop_label_inner_1;
41+
}
42+
break 'used_loop_label_outer_1;
43+
}
44+
45+
'used_loop_label_outer_2: for _ in 0..10 {
46+
'unused_loop_label_inner_2: for _ in 0..10 {
47+
//~^ WARN unused label
48+
break 'used_loop_label_outer_2;
49+
}
50+
}
51+
52+
'unused_loop_label_outer_3: for _ in 0..10 {
53+
//~^ WARN unused label
54+
'used_loop_label_inner_3: for _ in 0..10 {
55+
break 'used_loop_label_inner_3;
56+
}
57+
}
58+
59+
// You should be able to break the same label many times
60+
'many_used: loop {
61+
if true {
62+
break 'many_used;
63+
} else {
64+
break 'many_used;
65+
}
66+
}
67+
68+
// Test breaking many times with the same inner label doesn't break the
69+
// warning on the outer label
70+
'many_used_shadowed: for _ in 0..10 {
71+
//~^ WARN unused label
72+
'many_used_shadowed: for _ in 0..10 {
73+
//~^ WARN label name `'many_used_shadowed` shadows a label name that is already in scope
74+
if 1 % 2 == 0 {
75+
break 'many_used_shadowed;
76+
} else {
77+
break 'many_used_shadowed;
78+
}
79+
}
80+
}
81+
82+
'unused_loop_label: loop {
83+
//~^ WARN unused label
84+
break;
85+
}
86+
87+
// Make sure unused block labels give warnings...
88+
'unused_block_label: {
89+
//~^ WARN unused label
90+
}
91+
92+
// ...and that used ones don't:
93+
'used_block_label: {
94+
break 'used_block_label;
95+
}
96+
}

src/test/ui/lint/unused_labels.stderr

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
warning: unused label
2+
--> $DIR/unused_labels.rs:21:5
3+
|
4+
LL | 'unused_while_label: while 0 == 0 {
5+
| ^^^^^^^^^^^^^^^^^^^
6+
|
7+
note: lint level defined here
8+
--> $DIR/unused_labels.rs:18:9
9+
|
10+
LL | #![warn(unused_labels)]
11+
| ^^^^^^^^^^^^^
12+
13+
warning: unused label
14+
--> $DIR/unused_labels.rs:26:5
15+
|
16+
LL | 'unused_while_let_label: while let Some(_) = opt {
17+
| ^^^^^^^^^^^^^^^^^^^^^^^
18+
19+
warning: unused label
20+
--> $DIR/unused_labels.rs:30:5
21+
|
22+
LL | 'unused_for_label: for _ in 0..10 {
23+
| ^^^^^^^^^^^^^^^^^
24+
25+
warning: unused label
26+
--> $DIR/unused_labels.rs:46:9
27+
|
28+
LL | 'unused_loop_label_inner_2: for _ in 0..10 {
29+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
30+
31+
warning: unused label
32+
--> $DIR/unused_labels.rs:52:5
33+
|
34+
LL | 'unused_loop_label_outer_3: for _ in 0..10 {
35+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
36+
37+
warning: unused label
38+
--> $DIR/unused_labels.rs:70:5
39+
|
40+
LL | 'many_used_shadowed: for _ in 0..10 {
41+
| ^^^^^^^^^^^^^^^^^^^
42+
43+
warning: unused label
44+
--> $DIR/unused_labels.rs:82:5
45+
|
46+
LL | 'unused_loop_label: loop {
47+
| ^^^^^^^^^^^^^^^^^^
48+
49+
warning: unused label
50+
--> $DIR/unused_labels.rs:88:5
51+
|
52+
LL | 'unused_block_label: {
53+
| ^^^^^^^^^^^^^^^^^^^
54+
55+
warning: label name `'many_used_shadowed` shadows a label name that is already in scope
56+
--> $DIR/unused_labels.rs:72:9
57+
|
58+
LL | 'many_used_shadowed: for _ in 0..10 {
59+
| ------------------- first declared here
60+
LL | //~^ WARN unused label
61+
LL | 'many_used_shadowed: for _ in 0..10 {
62+
| ^^^^^^^^^^^^^^^^^^^ lifetime 'many_used_shadowed already in scope
63+

src/test/ui/loops-reject-duplicate-labels-2.rs

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
// discussed here:
1919
// https://internals.rust-lang.org/t/psa-rejecting-duplicate-loop-labels/1833
2020

21+
#[allow(unused_labels)]
2122
pub fn foo() {
2223
{ 'fl: for _ in 0..10 { break; } }
2324
{ 'fl: loop { break; } } //~ WARN label name `'fl` shadows a label name that is already in scope

src/test/ui/loops-reject-duplicate-labels-2.stderr

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,69 @@
11
warning: label name `'fl` shadows a label name that is already in scope
2-
--> $DIR/loops-reject-duplicate-labels-2.rs:23:7
2+
--> $DIR/loops-reject-duplicate-labels-2.rs:24:7
33
|
44
LL | { 'fl: for _ in 0..10 { break; } }
55
| --- first declared here
66
LL | { 'fl: loop { break; } } //~ WARN label name `'fl` shadows a label name that is already in scope
77
| ^^^ lifetime 'fl already in scope
88

99
warning: label name `'lf` shadows a label name that is already in scope
10-
--> $DIR/loops-reject-duplicate-labels-2.rs:25:7
10+
--> $DIR/loops-reject-duplicate-labels-2.rs:26:7
1111
|
1212
LL | { 'lf: loop { break; } }
1313
| --- first declared here
1414
LL | { 'lf: for _ in 0..10 { break; } } //~ WARN label name `'lf` shadows a label name that is already in scope
1515
| ^^^ lifetime 'lf already in scope
1616

1717
warning: label name `'wl` shadows a label name that is already in scope
18-
--> $DIR/loops-reject-duplicate-labels-2.rs:27:7
18+
--> $DIR/loops-reject-duplicate-labels-2.rs:28:7
1919
|
2020
LL | { 'wl: while 2 > 1 { break; } }
2121
| --- first declared here
2222
LL | { 'wl: loop { break; } } //~ WARN label name `'wl` shadows a label name that is already in scope
2323
| ^^^ lifetime 'wl already in scope
2424

2525
warning: label name `'lw` shadows a label name that is already in scope
26-
--> $DIR/loops-reject-duplicate-labels-2.rs:29:7
26+
--> $DIR/loops-reject-duplicate-labels-2.rs:30:7
2727
|
2828
LL | { 'lw: loop { break; } }
2929
| --- first declared here
3030
LL | { 'lw: while 2 > 1 { break; } } //~ WARN label name `'lw` shadows a label name that is already in scope
3131
| ^^^ lifetime 'lw already in scope
3232

3333
warning: label name `'fw` shadows a label name that is already in scope
34-
--> $DIR/loops-reject-duplicate-labels-2.rs:31:7
34+
--> $DIR/loops-reject-duplicate-labels-2.rs:32:7
3535
|
3636
LL | { 'fw: for _ in 0..10 { break; } }
3737
| --- first declared here
3838
LL | { 'fw: while 2 > 1 { break; } } //~ WARN label name `'fw` shadows a label name that is already in scope
3939
| ^^^ lifetime 'fw already in scope
4040

4141
warning: label name `'wf` shadows a label name that is already in scope
42-
--> $DIR/loops-reject-duplicate-labels-2.rs:33:7
42+
--> $DIR/loops-reject-duplicate-labels-2.rs:34:7
4343
|
4444
LL | { 'wf: while 2 > 1 { break; } }
4545
| --- first declared here
4646
LL | { 'wf: for _ in 0..10 { break; } } //~ WARN label name `'wf` shadows a label name that is already in scope
4747
| ^^^ lifetime 'wf already in scope
4848

4949
warning: label name `'tl` shadows a label name that is already in scope
50-
--> $DIR/loops-reject-duplicate-labels-2.rs:35:7
50+
--> $DIR/loops-reject-duplicate-labels-2.rs:36:7
5151
|
5252
LL | { 'tl: while let Some(_) = None::<i32> { break; } }
5353
| --- first declared here
5454
LL | { 'tl: loop { break; } } //~ WARN label name `'tl` shadows a label name that is already in scope
5555
| ^^^ lifetime 'tl already in scope
5656

5757
warning: label name `'lt` shadows a label name that is already in scope
58-
--> $DIR/loops-reject-duplicate-labels-2.rs:37:7
58+
--> $DIR/loops-reject-duplicate-labels-2.rs:38:7
5959
|
6060
LL | { 'lt: loop { break; } }
6161
| --- first declared here
6262
LL | { 'lt: while let Some(_) = None::<i32> { break; } }
6363
| ^^^ lifetime 'lt already in scope
6464

6565
error: compilation successful
66-
--> $DIR/loops-reject-duplicate-labels-2.rs:42:1
66+
--> $DIR/loops-reject-duplicate-labels-2.rs:43:1
6767
|
6868
LL | / pub fn main() { //~ ERROR compilation successful
6969
LL | | foo();

src/test/ui/loops-reject-duplicate-labels.rs

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
// Issue #21633: reject duplicate loop labels in function bodies.
1616
// This is testing the exact cases that are in the issue description.
1717

18+
#[allow(unused_labels)]
1819
fn foo() {
1920
'fl: for _ in 0..10 { break; }
2021
'fl: loop { break; } //~ WARN label name `'fl` shadows a label name that is already in scope

0 commit comments

Comments
 (0)