]>
Commit | Line | Data |
---|---|---|
eb39fafa DC |
1 | /** |
2 | * @fileoverview Rule that warns when identifier names that are | |
6f036462 | 3 | * specified in the configuration are used. |
eb39fafa | 4 | * @author Keith Cirkel (http://keithcirkel.co.uk) |
609c276f | 5 | * @deprecated in ESLint v7.5.0 |
eb39fafa DC |
6 | */ |
7 | ||
8 | "use strict"; | |
9 | ||
10 | //------------------------------------------------------------------------------ | |
11 | // Helpers | |
12 | //------------------------------------------------------------------------------ | |
13 | ||
14 | /** | |
15 | * Checks whether the given node represents assignment target in a normal assignment or destructuring. | |
16 | * @param {ASTNode} node The node to check. | |
17 | * @returns {boolean} `true` if the node is assignment target. | |
18 | */ | |
19 | function isAssignmentTarget(node) { | |
20 | const parent = node.parent; | |
21 | ||
22 | return ( | |
23 | ||
24 | // normal assignment | |
25 | ( | |
26 | parent.type === "AssignmentExpression" && | |
27 | parent.left === node | |
28 | ) || | |
29 | ||
30 | // destructuring | |
31 | parent.type === "ArrayPattern" || | |
32 | parent.type === "RestElement" || | |
33 | ( | |
34 | parent.type === "Property" && | |
35 | parent.value === node && | |
36 | parent.parent.type === "ObjectPattern" | |
37 | ) || | |
38 | ( | |
39 | parent.type === "AssignmentPattern" && | |
40 | parent.left === node | |
41 | ) | |
42 | ); | |
43 | } | |
44 | ||
45 | /** | |
46 | * Checks whether the given node represents an imported name that is renamed in the same import/export specifier. | |
47 | * | |
48 | * Examples: | |
49 | * import { a as b } from 'mod'; // node `a` is renamed import | |
50 | * export { a as b } from 'mod'; // node `a` is renamed import | |
51 | * @param {ASTNode} node `Identifier` node to check. | |
52 | * @returns {boolean} `true` if the node is a renamed import. | |
53 | */ | |
54 | function isRenamedImport(node) { | |
55 | const parent = node.parent; | |
56 | ||
57 | return ( | |
58 | ( | |
59 | parent.type === "ImportSpecifier" && | |
60 | parent.imported !== parent.local && | |
61 | parent.imported === node | |
62 | ) || | |
63 | ( | |
64 | parent.type === "ExportSpecifier" && | |
65 | parent.parent.source && // re-export | |
66 | parent.local !== parent.exported && | |
67 | parent.local === node | |
68 | ) | |
69 | ); | |
70 | } | |
71 | ||
72 | /** | |
73 | * Checks whether the given node is a renamed identifier node in an ObjectPattern destructuring. | |
74 | * | |
75 | * Examples: | |
76 | * const { a : b } = foo; // node `a` is renamed node. | |
77 | * @param {ASTNode} node `Identifier` node to check. | |
78 | * @returns {boolean} `true` if the node is a renamed node in an ObjectPattern destructuring. | |
79 | */ | |
80 | function isRenamedInDestructuring(node) { | |
81 | const parent = node.parent; | |
82 | ||
83 | return ( | |
84 | ( | |
85 | !parent.computed && | |
86 | parent.type === "Property" && | |
87 | parent.parent.type === "ObjectPattern" && | |
88 | parent.value !== node && | |
89 | parent.key === node | |
90 | ) | |
91 | ); | |
92 | } | |
93 | ||
94 | /** | |
95 | * Checks whether the given node represents shorthand definition of a property in an object literal. | |
96 | * @param {ASTNode} node `Identifier` node to check. | |
97 | * @returns {boolean} `true` if the node is a shorthand property definition. | |
98 | */ | |
99 | function isShorthandPropertyDefinition(node) { | |
100 | const parent = node.parent; | |
101 | ||
102 | return ( | |
103 | parent.type === "Property" && | |
104 | parent.parent.type === "ObjectExpression" && | |
105 | parent.shorthand | |
106 | ); | |
107 | } | |
108 | ||
109 | //------------------------------------------------------------------------------ | |
110 | // Rule Definition | |
111 | //------------------------------------------------------------------------------ | |
112 | ||
34eeec05 | 113 | /** @type {import('../shared/types').Rule} */ |
eb39fafa DC |
114 | module.exports = { |
115 | meta: { | |
6f036462 TL |
116 | deprecated: true, |
117 | replacedBy: ["id-denylist"], | |
118 | ||
eb39fafa DC |
119 | type: "suggestion", |
120 | ||
121 | docs: { | |
8f9d1d4d | 122 | description: "Disallow specified identifiers", |
eb39fafa | 123 | recommended: false, |
f2a92ac6 | 124 | url: "https://eslint.org/docs/latest/rules/id-blacklist" |
eb39fafa DC |
125 | }, |
126 | ||
127 | schema: { | |
128 | type: "array", | |
129 | items: { | |
130 | type: "string" | |
131 | }, | |
132 | uniqueItems: true | |
133 | }, | |
134 | messages: { | |
6f036462 | 135 | restricted: "Identifier '{{name}}' is restricted." |
eb39fafa DC |
136 | } |
137 | }, | |
138 | ||
139 | create(context) { | |
140 | ||
6f036462 | 141 | const denyList = new Set(context.options); |
eb39fafa | 142 | const reportedNodes = new Set(); |
f2a92ac6 | 143 | const sourceCode = context.sourceCode; |
eb39fafa DC |
144 | |
145 | let globalScope; | |
146 | ||
147 | /** | |
6f036462 | 148 | * Checks whether the given name is restricted. |
eb39fafa | 149 | * @param {string} name The name to check. |
6f036462 | 150 | * @returns {boolean} `true` if the name is restricted. |
eb39fafa DC |
151 | * @private |
152 | */ | |
6f036462 TL |
153 | function isRestricted(name) { |
154 | return denyList.has(name); | |
eb39fafa DC |
155 | } |
156 | ||
157 | /** | |
158 | * Checks whether the given node represents a reference to a global variable that is not declared in the source code. | |
159 | * These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables. | |
160 | * @param {ASTNode} node `Identifier` node to check. | |
161 | * @returns {boolean} `true` if the node is a reference to a global variable. | |
162 | */ | |
163 | function isReferenceToGlobalVariable(node) { | |
164 | const variable = globalScope.set.get(node.name); | |
165 | ||
166 | return variable && variable.defs.length === 0 && | |
167 | variable.references.some(ref => ref.identifier === node); | |
168 | } | |
169 | ||
170 | /** | |
171 | * Determines whether the given node should be checked. | |
172 | * @param {ASTNode} node `Identifier` node. | |
173 | * @returns {boolean} `true` if the node should be checked. | |
174 | */ | |
175 | function shouldCheck(node) { | |
176 | const parent = node.parent; | |
177 | ||
178 | /* | |
179 | * Member access has special rules for checking property names. | |
6f036462 TL |
180 | * Read access to a property with a restricted name is allowed, because it can be on an object that user has no control over. |
181 | * Write access isn't allowed, because it potentially creates a new property with a restricted name. | |
eb39fafa DC |
182 | */ |
183 | if ( | |
184 | parent.type === "MemberExpression" && | |
185 | parent.property === node && | |
186 | !parent.computed | |
187 | ) { | |
188 | return isAssignmentTarget(parent); | |
189 | } | |
190 | ||
191 | return ( | |
192 | parent.type !== "CallExpression" && | |
193 | parent.type !== "NewExpression" && | |
194 | !isRenamedImport(node) && | |
195 | !isRenamedInDestructuring(node) && | |
196 | !( | |
197 | isReferenceToGlobalVariable(node) && | |
198 | !isShorthandPropertyDefinition(node) | |
199 | ) | |
200 | ); | |
201 | } | |
202 | ||
203 | /** | |
204 | * Reports an AST node as a rule violation. | |
205 | * @param {ASTNode} node The node to report. | |
206 | * @returns {void} | |
207 | * @private | |
208 | */ | |
209 | function report(node) { | |
609c276f TL |
210 | |
211 | /* | |
212 | * We used the range instead of the node because it's possible | |
213 | * for the same identifier to be represented by two different | |
214 | * nodes, with the most clear example being shorthand properties: | |
215 | * { foo } | |
216 | * In this case, "foo" is represented by one node for the name | |
217 | * and one for the value. The only way to know they are the same | |
218 | * is to look at the range. | |
219 | */ | |
220 | if (!reportedNodes.has(node.range.toString())) { | |
eb39fafa DC |
221 | context.report({ |
222 | node, | |
6f036462 | 223 | messageId: "restricted", |
eb39fafa DC |
224 | data: { |
225 | name: node.name | |
226 | } | |
227 | }); | |
609c276f | 228 | reportedNodes.add(node.range.toString()); |
eb39fafa | 229 | } |
609c276f | 230 | |
eb39fafa DC |
231 | } |
232 | ||
233 | return { | |
234 | ||
f2a92ac6 DC |
235 | Program(node) { |
236 | globalScope = sourceCode.getScope(node); | |
eb39fafa DC |
237 | }, |
238 | ||
239 | Identifier(node) { | |
6f036462 | 240 | if (isRestricted(node.name) && shouldCheck(node)) { |
eb39fafa DC |
241 | report(node); |
242 | } | |
243 | } | |
244 | }; | |
245 | } | |
246 | }; |