.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / no-extra-label.js
1 /**
2  * @fileoverview Rule to disallow unnecessary labels
3  * @author Toru Nagashima
4  */
5
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Requirements
10 //------------------------------------------------------------------------------
11
12 const astUtils = require("./utils/ast-utils");
13
14 //------------------------------------------------------------------------------
15 // Rule Definition
16 //------------------------------------------------------------------------------
17
18 module.exports = {
19     meta: {
20         type: "suggestion",
21
22         docs: {
23             description: "disallow unnecessary labels",
24             category: "Best Practices",
25             recommended: false,
26             url: "https://eslint.org/docs/rules/no-extra-label"
27         },
28
29         schema: [],
30         fixable: "code",
31
32         messages: {
33             unexpected: "This label '{{name}}' is unnecessary."
34         }
35     },
36
37     create(context) {
38         const sourceCode = context.getSourceCode();
39         let scopeInfo = null;
40
41         /**
42          * Creates a new scope with a breakable statement.
43          * @param {ASTNode} node A node to create. This is a BreakableStatement.
44          * @returns {void}
45          */
46         function enterBreakableStatement(node) {
47             scopeInfo = {
48                 label: node.parent.type === "LabeledStatement" ? node.parent.label : null,
49                 breakable: true,
50                 upper: scopeInfo
51             };
52         }
53
54         /**
55          * Removes the top scope of the stack.
56          * @returns {void}
57          */
58         function exitBreakableStatement() {
59             scopeInfo = scopeInfo.upper;
60         }
61
62         /**
63          * Creates a new scope with a labeled statement.
64          *
65          * This ignores it if the body is a breakable statement.
66          * In this case it's handled in the `enterBreakableStatement` function.
67          * @param {ASTNode} node A node to create. This is a LabeledStatement.
68          * @returns {void}
69          */
70         function enterLabeledStatement(node) {
71             if (!astUtils.isBreakableStatement(node.body)) {
72                 scopeInfo = {
73                     label: node.label,
74                     breakable: false,
75                     upper: scopeInfo
76                 };
77             }
78         }
79
80         /**
81          * Removes the top scope of the stack.
82          *
83          * This ignores it if the body is a breakable statement.
84          * In this case it's handled in the `exitBreakableStatement` function.
85          * @param {ASTNode} node A node. This is a LabeledStatement.
86          * @returns {void}
87          */
88         function exitLabeledStatement(node) {
89             if (!astUtils.isBreakableStatement(node.body)) {
90                 scopeInfo = scopeInfo.upper;
91             }
92         }
93
94         /**
95          * Reports a given control node if it's unnecessary.
96          * @param {ASTNode} node A node. This is a BreakStatement or a
97          *      ContinueStatement.
98          * @returns {void}
99          */
100         function reportIfUnnecessary(node) {
101             if (!node.label) {
102                 return;
103             }
104
105             const labelNode = node.label;
106
107             for (let info = scopeInfo; info !== null; info = info.upper) {
108                 if (info.breakable || info.label && info.label.name === labelNode.name) {
109                     if (info.breakable && info.label && info.label.name === labelNode.name) {
110                         context.report({
111                             node: labelNode,
112                             messageId: "unexpected",
113                             data: labelNode,
114                             fix(fixer) {
115                                 const breakOrContinueToken = sourceCode.getFirstToken(node);
116
117                                 if (sourceCode.commentsExistBetween(breakOrContinueToken, labelNode)) {
118                                     return null;
119                                 }
120
121                                 return fixer.removeRange([breakOrContinueToken.range[1], labelNode.range[1]]);
122                             }
123                         });
124                     }
125                     return;
126                 }
127             }
128         }
129
130         return {
131             WhileStatement: enterBreakableStatement,
132             "WhileStatement:exit": exitBreakableStatement,
133             DoWhileStatement: enterBreakableStatement,
134             "DoWhileStatement:exit": exitBreakableStatement,
135             ForStatement: enterBreakableStatement,
136             "ForStatement:exit": exitBreakableStatement,
137             ForInStatement: enterBreakableStatement,
138             "ForInStatement:exit": exitBreakableStatement,
139             ForOfStatement: enterBreakableStatement,
140             "ForOfStatement:exit": exitBreakableStatement,
141             SwitchStatement: enterBreakableStatement,
142             "SwitchStatement:exit": exitBreakableStatement,
143             LabeledStatement: enterLabeledStatement,
144             "LabeledStatement:exit": exitLabeledStatement,
145             BreakStatement: reportIfUnnecessary,
146             ContinueStatement: reportIfUnnecessary
147         };
148     }
149 };