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