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

Lines changed: 7 additions & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 4 additions & 0 deletions
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

Lines changed: 10 additions & 2 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 4 additions & 4 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 96 additions & 0 deletions
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

Lines changed: 63 additions & 0 deletions
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+

0 commit comments

Comments
 (0)