diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index c55c71f85da..71eefec2210 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -279,6 +279,12 @@ declare_lint! { "detects name collision with an existing but unstable method" } +declare_lint! { + pub UNUSED_LABELS, + Warn, + "detects labels that are never used" +} + /// Does nothing as a lint pass, but registers some `Lint`s /// which are used by other parts of the compiler. #[derive(Copy, Clone)] @@ -325,6 +331,7 @@ impl LintPass for HardwiredLints { UNUSED_MUT, SINGLE_USE_LIFETIME, UNUSED_LIFETIME, + UNUSED_LABELS, TYVAR_BEHIND_RAW_POINTER, ELIDED_LIFETIME_IN_PATH, BARE_TRAIT_OBJECT, diff --git a/src/librustc_resolve/check_unused.rs b/src/librustc_resolve/check_unused.rs index 163f6a64010..590ce168d5d 100644 --- a/src/librustc_resolve/check_unused.rs +++ b/src/librustc_resolve/check_unused.rs @@ -142,6 +142,10 @@ pub fn check_crate(resolver: &mut Resolver, krate: &ast::Crate) { } } + for (id, span) in resolver.unused_labels.iter() { + resolver.session.buffer_lint(lint::builtin::UNUSED_LABELS, *id, *span, "unused label"); + } + let mut visitor = UnusedImportCheckVisitor { resolver, unused_imports: NodeMap(), diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index e13e6bc6b74..feb5af571e4 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -1473,6 +1473,10 @@ pub struct Resolver<'a> { pub maybe_unused_trait_imports: NodeSet, pub maybe_unused_extern_crates: Vec<(NodeId, Span)>, + /// A list of labels as of yet unused. Labels will be removed from this map when + /// they are used (in a `break` or `continue` statement) + pub unused_labels: FxHashMap, + /// privacy errors are delayed until the end in order to deduplicate them privacy_errors: Vec>, /// ambiguity errors are delayed for deduplication @@ -1752,6 +1756,8 @@ impl<'a> Resolver<'a> { maybe_unused_trait_imports: NodeSet(), maybe_unused_extern_crates: Vec::new(), + unused_labels: FxHashMap(), + privacy_errors: Vec::new(), ambiguity_errors: Vec::new(), use_injections: Vec::new(), @@ -3694,6 +3700,7 @@ impl<'a> Resolver<'a> { where F: FnOnce(&mut Resolver) { if let Some(label) = label { + self.unused_labels.insert(id, label.ident.span); let def = Def::Label(id); self.with_label_rib(|this| { this.label_ribs.last_mut().unwrap().bindings.insert(label.ident, def); @@ -3742,9 +3749,10 @@ impl<'a> Resolver<'a> { ResolutionError::UndeclaredLabel(&label.ident.name.as_str(), close_match)); } - Some(def @ Def::Label(_)) => { + Some(Def::Label(id)) => { // Since this def is a label, it is never read. - self.record_def(expr.id, PathResolution::new(def)); + self.record_def(expr.id, PathResolution::new(Def::Label(id))); + self.unused_labels.remove(&id); } Some(_) => { span_bug!(expr.span, "label wasn't mapped to a label def!"); diff --git a/src/test/ui/lint/unused_labels.rs b/src/test/ui/lint/unused_labels.rs new file mode 100644 index 00000000000..86abb34658a --- /dev/null +++ b/src/test/ui/lint/unused_labels.rs @@ -0,0 +1,75 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// The output should warn when a loop label is not used. However, it +// should also deal with the edge cases where a label is shadowed, +// within nested loops + +// compile-pass + +fn main() { + 'unused_while_label: while 0 == 0 { + //~^ WARN unused label + } + + let opt = Some(0); + 'unused_while_let_label: while let Some(_) = opt { + //~^ WARN unused label + } + + 'unused_for_label: for _ in 0..10 { + //~^ WARN unused label + } + + 'used_loop_label: loop { + break 'used_loop_label; + } + + 'used_loop_label_outer_1: for _ in 0..10 { + 'used_loop_label_inner_1: for _ in 0..10 { + break 'used_loop_label_inner_1; + } + break 'used_loop_label_outer_1; + } + + 'used_loop_label_outer_2: for _ in 0..10 { + 'unused_loop_label_inner_2: for _ in 0..10 { + //~^ WARN unused label + break 'used_loop_label_outer_2; + } + } + + 'unused_loop_label_outer_3: for _ in 0..10 { + //~^ WARN unused label + 'used_loop_label_inner_3: for _ in 0..10 { + break 'used_loop_label_inner_3; + } + } + + // Test breaking many times with the same inner label doesn't break the + // warning on the outer label + 'many_used_shadowed: for _ in 0..10 { + //~^ WARN unused label + 'many_used_shadowed: for _ in 0..10 { + //~^ WARN label name `'many_used_shadowed` shadows a label name that is already in scope + if 1 % 2 == 0 { + break 'many_used_shadowed; + } else { + break 'many_used_shadowed; + } + } + } + + // This is diverging, so put it at the end so we don't get + // unreachable_code errors everywhere else + 'unused_loop_label: loop { + //~^ WARN unused label + } +} diff --git a/src/test/ui/lint/unused_labels.stderr b/src/test/ui/lint/unused_labels.stderr new file mode 100644 index 00000000000..425b0b1769d --- /dev/null +++ b/src/test/ui/lint/unused_labels.stderr @@ -0,0 +1,53 @@ +warning: unused label + --> $DIR/unused_labels.rs:18:5 + | +LL | 'unused_while_label: while 0 == 0 { + | ^^^^^^^^^^^^^^^^^^^ + | + = note: #[warn(unused_labels)] on by default + +warning: unused label + --> $DIR/unused_labels.rs:23:5 + | +LL | 'unused_while_let_label: while let Some(_) = opt { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused label + --> $DIR/unused_labels.rs:27:5 + | +LL | 'unused_for_label: for _ in 0..10 { + | ^^^^^^^^^^^^^^^^^ + +warning: unused label + --> $DIR/unused_labels.rs:43:9 + | +LL | 'unused_loop_label_inner_2: for _ in 0..10 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused label + --> $DIR/unused_labels.rs:49:5 + | +LL | 'unused_loop_label_outer_3: for _ in 0..10 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused label + --> $DIR/unused_labels.rs:58:5 + | +LL | 'many_used_shadowed: for _ in 0..10 { + | ^^^^^^^^^^^^^^^^^^^ + +warning: unused label + --> $DIR/unused_labels.rs:72:5 + | +LL | 'unused_loop_label: loop { + | ^^^^^^^^^^^^^^^^^^ + +warning: label name `'many_used_shadowed` shadows a label name that is already in scope + --> $DIR/unused_labels.rs:60:9 + | +LL | 'many_used_shadowed: for _ in 0..10 { + | ------------------- first declared here +LL | //~^ WARN unused label +LL | 'many_used_shadowed: for _ in 0..10 { + | ^^^^^^^^^^^^^^^^^^^ lifetime 'many_used_shadowed already in scope + diff --git a/src/test/ui/loops-reject-duplicate-labels-2.rs b/src/test/ui/loops-reject-duplicate-labels-2.rs index 598c7370b89..b273e7a0c7c 100644 --- a/src/test/ui/loops-reject-duplicate-labels-2.rs +++ b/src/test/ui/loops-reject-duplicate-labels-2.rs @@ -18,6 +18,7 @@ // discussed here: // https://internals.rust-lang.org/t/psa-rejecting-duplicate-loop-labels/1833 +#[allow(unused_labels)] pub fn foo() { { 'fl: for _ in 0..10 { break; } } { 'fl: loop { break; } } //~ WARN label name `'fl` shadows a label name that is already in scope diff --git a/src/test/ui/loops-reject-duplicate-labels-2.stderr b/src/test/ui/loops-reject-duplicate-labels-2.stderr index 830270a99d1..41b4a850f1b 100644 --- a/src/test/ui/loops-reject-duplicate-labels-2.stderr +++ b/src/test/ui/loops-reject-duplicate-labels-2.stderr @@ -1,5 +1,5 @@ warning: label name `'fl` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels-2.rs:23:7 + --> $DIR/loops-reject-duplicate-labels-2.rs:24:7 | LL | { 'fl: for _ in 0..10 { break; } } | --- first declared here @@ -7,7 +7,7 @@ LL | { 'fl: loop { break; } } //~ WARN label name `'fl` shadows | ^^^ lifetime 'fl already in scope warning: label name `'lf` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels-2.rs:25:7 + --> $DIR/loops-reject-duplicate-labels-2.rs:26:7 | LL | { 'lf: loop { break; } } | --- first declared here @@ -15,7 +15,7 @@ LL | { 'lf: for _ in 0..10 { break; } } //~ WARN label name `'lf` shadows | ^^^ lifetime 'lf already in scope warning: label name `'wl` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels-2.rs:27:7 + --> $DIR/loops-reject-duplicate-labels-2.rs:28:7 | LL | { 'wl: while 2 > 1 { break; } } | --- first declared here @@ -23,7 +23,7 @@ LL | { 'wl: loop { break; } } //~ WARN label name `'wl` shadows | ^^^ lifetime 'wl already in scope warning: label name `'lw` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels-2.rs:29:7 + --> $DIR/loops-reject-duplicate-labels-2.rs:30:7 | LL | { 'lw: loop { break; } } | --- first declared here @@ -31,7 +31,7 @@ LL | { 'lw: while 2 > 1 { break; } } //~ WARN label name `'lw` shadows | ^^^ lifetime 'lw already in scope warning: label name `'fw` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels-2.rs:31:7 + --> $DIR/loops-reject-duplicate-labels-2.rs:32:7 | LL | { 'fw: for _ in 0..10 { break; } } | --- first declared here @@ -39,7 +39,7 @@ LL | { 'fw: while 2 > 1 { break; } } //~ WARN label name `'fw` shadows | ^^^ lifetime 'fw already in scope warning: label name `'wf` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels-2.rs:33:7 + --> $DIR/loops-reject-duplicate-labels-2.rs:34:7 | LL | { 'wf: while 2 > 1 { break; } } | --- first declared here @@ -47,7 +47,7 @@ LL | { 'wf: for _ in 0..10 { break; } } //~ WARN label name `'wf` shadows | ^^^ lifetime 'wf already in scope warning: label name `'tl` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels-2.rs:35:7 + --> $DIR/loops-reject-duplicate-labels-2.rs:36:7 | LL | { 'tl: while let Some(_) = None:: { break; } } | --- first declared here @@ -55,7 +55,7 @@ LL | { 'tl: loop { break; } } //~ WARN label name `'tl` shadows | ^^^ lifetime 'tl already in scope warning: label name `'lt` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels-2.rs:37:7 + --> $DIR/loops-reject-duplicate-labels-2.rs:38:7 | LL | { 'lt: loop { break; } } | --- first declared here @@ -63,7 +63,7 @@ LL | { 'lt: while let Some(_) = None:: { break; } } | ^^^ lifetime 'lt already in scope error: compilation successful - --> $DIR/loops-reject-duplicate-labels-2.rs:42:1 + --> $DIR/loops-reject-duplicate-labels-2.rs:43:1 | LL | / pub fn main() { //~ ERROR compilation successful LL | | foo(); diff --git a/src/test/ui/loops-reject-duplicate-labels.rs b/src/test/ui/loops-reject-duplicate-labels.rs index d768b002ab1..ad24f69871c 100644 --- a/src/test/ui/loops-reject-duplicate-labels.rs +++ b/src/test/ui/loops-reject-duplicate-labels.rs @@ -15,6 +15,7 @@ // Issue #21633: reject duplicate loop labels in function bodies. // This is testing the exact cases that are in the issue description. +#[allow(unused_labels)] fn foo() { 'fl: for _ in 0..10 { break; } 'fl: loop { break; } //~ WARN label name `'fl` shadows a label name that is already in scope diff --git a/src/test/ui/loops-reject-duplicate-labels.stderr b/src/test/ui/loops-reject-duplicate-labels.stderr index a71f98b812a..d0cb81544f8 100644 --- a/src/test/ui/loops-reject-duplicate-labels.stderr +++ b/src/test/ui/loops-reject-duplicate-labels.stderr @@ -1,5 +1,5 @@ warning: label name `'fl` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels.rs:20:5 + --> $DIR/loops-reject-duplicate-labels.rs:21:5 | LL | 'fl: for _ in 0..10 { break; } | --- first declared here @@ -7,7 +7,7 @@ LL | 'fl: loop { break; } //~ WARN label name `'fl` shadows a labe | ^^^ lifetime 'fl already in scope warning: label name `'lf` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels.rs:23:5 + --> $DIR/loops-reject-duplicate-labels.rs:24:5 | LL | 'lf: loop { break; } | --- first declared here @@ -15,7 +15,7 @@ LL | 'lf: for _ in 0..10 { break; } //~ WARN label name `'lf` shadows a labe | ^^^ lifetime 'lf already in scope warning: label name `'wl` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels.rs:25:5 + --> $DIR/loops-reject-duplicate-labels.rs:26:5 | LL | 'wl: while 2 > 1 { break; } | --- first declared here @@ -23,7 +23,7 @@ LL | 'wl: loop { break; } //~ WARN label name `'wl` shadows a labe | ^^^ lifetime 'wl already in scope warning: label name `'lw` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels.rs:27:5 + --> $DIR/loops-reject-duplicate-labels.rs:28:5 | LL | 'lw: loop { break; } | --- first declared here @@ -31,7 +31,7 @@ LL | 'lw: while 2 > 1 { break; } //~ WARN label name `'lw` shadows a labe | ^^^ lifetime 'lw already in scope warning: label name `'fw` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels.rs:29:5 + --> $DIR/loops-reject-duplicate-labels.rs:30:5 | LL | 'fw: for _ in 0..10 { break; } | --- first declared here @@ -39,7 +39,7 @@ LL | 'fw: while 2 > 1 { break; } //~ WARN label name `'fw` shadows a labe | ^^^ lifetime 'fw already in scope warning: label name `'wf` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels.rs:31:5 + --> $DIR/loops-reject-duplicate-labels.rs:32:5 | LL | 'wf: while 2 > 1 { break; } | --- first declared here @@ -47,7 +47,7 @@ LL | 'wf: for _ in 0..10 { break; } //~ WARN label name `'wf` shadows a labe | ^^^ lifetime 'wf already in scope warning: label name `'tl` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels.rs:33:5 + --> $DIR/loops-reject-duplicate-labels.rs:34:5 | LL | 'tl: while let Some(_) = None:: { break; } | --- first declared here @@ -55,7 +55,7 @@ LL | 'tl: loop { break; } //~ WARN label name `'tl` shadows a labe | ^^^ lifetime 'tl already in scope warning: label name `'lt` shadows a label name that is already in scope - --> $DIR/loops-reject-duplicate-labels.rs:35:5 + --> $DIR/loops-reject-duplicate-labels.rs:36:5 | LL | 'lt: loop { break; } | --- first declared here @@ -63,7 +63,7 @@ LL | 'lt: while let Some(_) = None:: { break; } | ^^^ lifetime 'lt already in scope error: compilation successful - --> $DIR/loops-reject-duplicate-labels.rs:49:1 + --> $DIR/loops-reject-duplicate-labels.rs:50:1 | LL | / pub fn main() { //~ ERROR compilation successful LL | | let s = S; diff --git a/src/test/ui/suggestions/suggest-labels.rs b/src/test/ui/suggestions/suggest-labels.rs index 8c97301f40b..9fb519c57ed 100644 --- a/src/test/ui/suggestions/suggest-labels.rs +++ b/src/test/ui/suggestions/suggest-labels.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#[allow(unreachable_code)] +#[allow(unreachable_code, unused_labels)] fn main() { 'foo: loop { break 'fo; //~ ERROR use of undeclared label