1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
use rustc::lint::*;
use rustc::hir;
use rustc::hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, NestedVisitorMap};
use std::collections::HashMap;
use syntax::ast;
use syntax::codemap::Span;
use syntax::symbol::InternedString;
use utils::{in_macro, span_lint};

/// **What it does:** Checks for unused labels.
///
/// **Why is this bad?** Maybe the label should be used in which case there is
/// an error in the code or it should be removed.
///
/// **Known problems:** Hopefully none.
///
/// **Example:**
/// ```rust,ignore
/// fn unused_label() {
///     'label: for i in 1..2 {
///         if i > 4 { continue }
///     }
/// ```
declare_lint! {
    pub UNUSED_LABEL,
    Warn,
    "unused labels"
}

pub struct UnusedLabel;

struct UnusedLabelVisitor<'a, 'tcx: 'a> {
    labels: HashMap<InternedString, Span>,
    cx: &'a LateContext<'a, 'tcx>,
}

impl LintPass for UnusedLabel {
    fn get_lints(&self) -> LintArray {
        lint_array!(UNUSED_LABEL)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedLabel {
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        kind: FnKind<'tcx>,
        decl: &'tcx hir::FnDecl,
        body: &'tcx hir::Body,
        span: Span,
        fn_id: ast::NodeId
    ) {
        if in_macro(cx, span) {
            return;
        }

        let mut v = UnusedLabelVisitor {
            cx: cx,
            labels: HashMap::new(),
        };
        walk_fn(&mut v, kind, decl, body.id(), span, fn_id);

        for (label, span) in v.labels {
            span_lint(cx, UNUSED_LABEL, span, &format!("unused label `{}`", label));
        }
    }
}

impl<'a, 'tcx: 'a> Visitor<'tcx> for UnusedLabelVisitor<'a, 'tcx> {
    fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
        match expr.node {
            hir::ExprBreak(Some(label), _) |
            hir::ExprAgain(Some(label)) => {
                self.labels.remove(&label.name.as_str());
            },
            hir::ExprLoop(_, Some(label), _) |
            hir::ExprWhile(_, _, Some(label)) => {
                self.labels.insert(label.node.as_str(), expr.span);
            },
            _ => (),
        }

        walk_expr(self, expr);
    }
    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
        NestedVisitorMap::All(&self.cx.tcx.map)
    }
}