UNPKG

3.11 kBJavaScriptView Raw
1/**
2 * @fileoverview Rule to disallow unused labels.
3 * @author Toru Nagashima
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Rule Definition
10//------------------------------------------------------------------------------
11
12module.exports = {
13 meta: {
14 docs: {
15 description: "disallow unused labels",
16 category: "Best Practices",
17 recommended: true
18 },
19
20 schema: [],
21
22 fixable: "code"
23 },
24
25 create(context) {
26 const sourceCode = context.getSourceCode();
27 let scopeInfo = null;
28
29 /**
30 * Adds a scope info to the stack.
31 *
32 * @param {ASTNode} node - A node to add. This is a LabeledStatement.
33 * @returns {void}
34 */
35 function enterLabeledScope(node) {
36 scopeInfo = {
37 label: node.label.name,
38 used: false,
39 upper: scopeInfo
40 };
41 }
42
43 /**
44 * Removes the top of the stack.
45 * At the same time, this reports the label if it's never used.
46 *
47 * @param {ASTNode} node - A node to report. This is a LabeledStatement.
48 * @returns {void}
49 */
50 function exitLabeledScope(node) {
51 if (!scopeInfo.used) {
52 context.report({
53 node: node.label,
54 message: "'{{name}}:' is defined but never used.",
55 data: node.label,
56 fix(fixer) {
57
58 /*
59 * Only perform a fix if there are no comments between the label and the body. This will be the case
60 * when there is exactly one token/comment (the ":") between the label and the body.
61 */
62 if (sourceCode.getTokenAfter(node.label, { includeComments: true }) === sourceCode.getTokenBefore(node.body, { includeComments: true })) {
63 return fixer.removeRange([node.range[0], node.body.range[0]]);
64 }
65
66 return null;
67 }
68 });
69 }
70
71 scopeInfo = scopeInfo.upper;
72 }
73
74 /**
75 * Marks the label of a given node as used.
76 *
77 * @param {ASTNode} node - A node to mark. This is a BreakStatement or
78 * ContinueStatement.
79 * @returns {void}
80 */
81 function markAsUsed(node) {
82 if (!node.label) {
83 return;
84 }
85
86 const label = node.label.name;
87 let info = scopeInfo;
88
89 while (info) {
90 if (info.label === label) {
91 info.used = true;
92 break;
93 }
94 info = info.upper;
95 }
96 }
97
98 return {
99 LabeledStatement: enterLabeledScope,
100 "LabeledStatement:exit": exitLabeledScope,
101 BreakStatement: markAsUsed,
102 ContinueStatement: markAsUsed
103 };
104 }
105};