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