]> git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/utils/ast-utils.js
import 8.23.1 source
[pve-eslint.git] / eslint / lib / rules / utils / ast-utils.js
1 /**
2 * @fileoverview Common utils for AST.
3 * @author Gyandeep Singh
4 */
5
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Requirements
10 //------------------------------------------------------------------------------
11
12 const esutils = require("esutils");
13 const espree = require("espree");
14 const escapeRegExp = require("escape-string-regexp");
15 const {
16 breakableTypePattern,
17 createGlobalLinebreakMatcher,
18 lineBreakPattern,
19 shebangPattern
20 } = require("../../shared/ast-utils");
21
22 //------------------------------------------------------------------------------
23 // Helpers
24 //------------------------------------------------------------------------------
25
26 const anyFunctionPattern = /^(?:Function(?:Declaration|Expression)|ArrowFunctionExpression)$/u;
27 const anyLoopPattern = /^(?:DoWhile|For|ForIn|ForOf|While)Statement$/u;
28 const arrayOrTypedArrayPattern = /Array$/u;
29 const arrayMethodPattern = /^(?:every|filter|find|findIndex|forEach|map|some)$/u;
30 const bindOrCallOrApplyPattern = /^(?:bind|call|apply)$/u;
31 const thisTagPattern = /^[\s*]*@this/mu;
32
33
34 const COMMENTS_IGNORE_PATTERN = /^\s*(?:eslint|jshint\s+|jslint\s+|istanbul\s+|globals?\s+|exported\s+|jscs)/u;
35 const ESLINT_DIRECTIVE_PATTERN = /^(?:eslint[- ]|(?:globals?|exported) )/u;
36 const LINEBREAKS = new Set(["\r\n", "\r", "\n", "\u2028", "\u2029"]);
37
38 // A set of node types that can contain a list of statements
39 const STATEMENT_LIST_PARENTS = new Set(["Program", "BlockStatement", "StaticBlock", "SwitchCase"]);
40
41 const DECIMAL_INTEGER_PATTERN = /^(?:0|0[0-7]*[89]\d*|[1-9](?:_?\d)*)$/u;
42
43 // Tests the presence of at least one LegacyOctalEscapeSequence or NonOctalDecimalEscapeSequence in a raw string
44 const OCTAL_OR_NON_OCTAL_DECIMAL_ESCAPE_PATTERN = /^(?:[^\\]|\\.)*\\(?:[1-9]|0[0-9])/su;
45
46 const LOGICAL_ASSIGNMENT_OPERATORS = new Set(["&&=", "||=", "??="]);
47
48 /**
49 * Checks reference if is non initializer and writable.
50 * @param {Reference} reference A reference to check.
51 * @param {int} index The index of the reference in the references.
52 * @param {Reference[]} references The array that the reference belongs to.
53 * @returns {boolean} Success/Failure
54 * @private
55 */
56 function isModifyingReference(reference, index, references) {
57 const identifier = reference.identifier;
58
59 /*
60 * Destructuring assignments can have multiple default value, so
61 * possibly there are multiple writeable references for the same
62 * identifier.
63 */
64 const modifyingDifferentIdentifier = index === 0 ||
65 references[index - 1].identifier !== identifier;
66
67 return (identifier &&
68 reference.init === false &&
69 reference.isWrite() &&
70 modifyingDifferentIdentifier
71 );
72 }
73
74 /**
75 * Checks whether the given string starts with uppercase or not.
76 * @param {string} s The string to check.
77 * @returns {boolean} `true` if the string starts with uppercase.
78 */
79 function startsWithUpperCase(s) {
80 return s[0] !== s[0].toLocaleLowerCase();
81 }
82
83 /**
84 * Checks whether or not a node is a constructor.
85 * @param {ASTNode} node A function node to check.
86 * @returns {boolean} Whether or not a node is a constructor.
87 */
88 function isES5Constructor(node) {
89 return (node.id && startsWithUpperCase(node.id.name));
90 }
91
92 /**
93 * Finds a function node from ancestors of a node.
94 * @param {ASTNode} node A start node to find.
95 * @returns {Node|null} A found function node.
96 */
97 function getUpperFunction(node) {
98 for (let currentNode = node; currentNode; currentNode = currentNode.parent) {
99 if (anyFunctionPattern.test(currentNode.type)) {
100 return currentNode;
101 }
102 }
103 return null;
104 }
105
106 /**
107 * Checks whether a given node is a function node or not.
108 * The following types are function nodes:
109 *
110 * - ArrowFunctionExpression
111 * - FunctionDeclaration
112 * - FunctionExpression
113 * @param {ASTNode|null} node A node to check.
114 * @returns {boolean} `true` if the node is a function node.
115 */
116 function isFunction(node) {
117 return Boolean(node && anyFunctionPattern.test(node.type));
118 }
119
120 /**
121 * Checks whether a given node is a loop node or not.
122 * The following types are loop nodes:
123 *
124 * - DoWhileStatement
125 * - ForInStatement
126 * - ForOfStatement
127 * - ForStatement
128 * - WhileStatement
129 * @param {ASTNode|null} node A node to check.
130 * @returns {boolean} `true` if the node is a loop node.
131 */
132 function isLoop(node) {
133 return Boolean(node && anyLoopPattern.test(node.type));
134 }
135
136 /**
137 * Checks whether the given node is in a loop or not.
138 * @param {ASTNode} node The node to check.
139 * @returns {boolean} `true` if the node is in a loop.
140 */
141 function isInLoop(node) {
142 for (let currentNode = node; currentNode && !isFunction(currentNode); currentNode = currentNode.parent) {
143 if (isLoop(currentNode)) {
144 return true;
145 }
146 }
147
148 return false;
149 }
150
151 /**
152 * Determines whether the given node is a `null` literal.
153 * @param {ASTNode} node The node to check
154 * @returns {boolean} `true` if the node is a `null` literal
155 */
156 function isNullLiteral(node) {
157
158 /*
159 * Checking `node.value === null` does not guarantee that a literal is a null literal.
160 * When parsing values that cannot be represented in the current environment (e.g. unicode
161 * regexes in Node 4), `node.value` is set to `null` because it wouldn't be possible to
162 * set `node.value` to a unicode regex. To make sure a literal is actually `null`, check
163 * `node.regex` instead. Also see: https://github.com/eslint/eslint/issues/8020
164 */
165 return node.type === "Literal" && node.value === null && !node.regex && !node.bigint;
166 }
167
168 /**
169 * Checks whether or not a node is `null` or `undefined`.
170 * @param {ASTNode} node A node to check.
171 * @returns {boolean} Whether or not the node is a `null` or `undefined`.
172 * @public
173 */
174 function isNullOrUndefined(node) {
175 return (
176 isNullLiteral(node) ||
177 (node.type === "Identifier" && node.name === "undefined") ||
178 (node.type === "UnaryExpression" && node.operator === "void")
179 );
180 }
181
182 /**
183 * Checks whether or not a node is callee.
184 * @param {ASTNode} node A node to check.
185 * @returns {boolean} Whether or not the node is callee.
186 */
187 function isCallee(node) {
188 return node.parent.type === "CallExpression" && node.parent.callee === node;
189 }
190
191 /**
192 * Returns the result of the string conversion applied to the evaluated value of the given expression node,
193 * if it can be determined statically.
194 *
195 * This function returns a `string` value for all `Literal` nodes and simple `TemplateLiteral` nodes only.
196 * In all other cases, this function returns `null`.
197 * @param {ASTNode} node Expression node.
198 * @returns {string|null} String value if it can be determined. Otherwise, `null`.
199 */
200 function getStaticStringValue(node) {
201 switch (node.type) {
202 case "Literal":
203 if (node.value === null) {
204 if (isNullLiteral(node)) {
205 return String(node.value); // "null"
206 }
207 if (node.regex) {
208 return `/${node.regex.pattern}/${node.regex.flags}`;
209 }
210 if (node.bigint) {
211 return node.bigint;
212 }
213
214 // Otherwise, this is an unknown literal. The function will return null.
215
216 } else {
217 return String(node.value);
218 }
219 break;
220 case "TemplateLiteral":
221 if (node.expressions.length === 0 && node.quasis.length === 1) {
222 return node.quasis[0].value.cooked;
223 }
224 break;
225
226 // no default
227 }
228
229 return null;
230 }
231
232 /**
233 * Gets the property name of a given node.
234 * The node can be a MemberExpression, a Property, or a MethodDefinition.
235 *
236 * If the name is dynamic, this returns `null`.
237 *
238 * For examples:
239 *
240 * a.b // => "b"
241 * a["b"] // => "b"
242 * a['b'] // => "b"
243 * a[`b`] // => "b"
244 * a[100] // => "100"
245 * a[b] // => null
246 * a["a" + "b"] // => null
247 * a[tag`b`] // => null
248 * a[`${b}`] // => null
249 *
250 * let a = {b: 1} // => "b"
251 * let a = {["b"]: 1} // => "b"
252 * let a = {['b']: 1} // => "b"
253 * let a = {[`b`]: 1} // => "b"
254 * let a = {[100]: 1} // => "100"
255 * let a = {[b]: 1} // => null
256 * let a = {["a" + "b"]: 1} // => null
257 * let a = {[tag`b`]: 1} // => null
258 * let a = {[`${b}`]: 1} // => null
259 * @param {ASTNode} node The node to get.
260 * @returns {string|null} The property name if static. Otherwise, null.
261 */
262 function getStaticPropertyName(node) {
263 let prop;
264
265 switch (node && node.type) {
266 case "ChainExpression":
267 return getStaticPropertyName(node.expression);
268
269 case "Property":
270 case "PropertyDefinition":
271 case "MethodDefinition":
272 prop = node.key;
273 break;
274
275 case "MemberExpression":
276 prop = node.property;
277 break;
278
279 // no default
280 }
281
282 if (prop) {
283 if (prop.type === "Identifier" && !node.computed) {
284 return prop.name;
285 }
286
287 return getStaticStringValue(prop);
288 }
289
290 return null;
291 }
292
293 /**
294 * Retrieve `ChainExpression#expression` value if the given node a `ChainExpression` node. Otherwise, pass through it.
295 * @param {ASTNode} node The node to address.
296 * @returns {ASTNode} The `ChainExpression#expression` value if the node is a `ChainExpression` node. Otherwise, the node.
297 */
298 function skipChainExpression(node) {
299 return node && node.type === "ChainExpression" ? node.expression : node;
300 }
301
302 /**
303 * Check if the `actual` is an expected value.
304 * @param {string} actual The string value to check.
305 * @param {string | RegExp} expected The expected string value or pattern.
306 * @returns {boolean} `true` if the `actual` is an expected value.
307 */
308 function checkText(actual, expected) {
309 return typeof expected === "string"
310 ? actual === expected
311 : expected.test(actual);
312 }
313
314 /**
315 * Check if a given node is an Identifier node with a given name.
316 * @param {ASTNode} node The node to check.
317 * @param {string | RegExp} name The expected name or the expected pattern of the object name.
318 * @returns {boolean} `true` if the node is an Identifier node with the name.
319 */
320 function isSpecificId(node, name) {
321 return node.type === "Identifier" && checkText(node.name, name);
322 }
323
324 /**
325 * Check if a given node is member access with a given object name and property name pair.
326 * This is regardless of optional or not.
327 * @param {ASTNode} node The node to check.
328 * @param {string | RegExp | null} objectName The expected name or the expected pattern of the object name. If this is nullish, this method doesn't check object.
329 * @param {string | RegExp | null} propertyName The expected name or the expected pattern of the property name. If this is nullish, this method doesn't check property.
330 * @returns {boolean} `true` if the node is member access with the object name and property name pair.
331 * The node is a `MemberExpression` or `ChainExpression`.
332 */
333 function isSpecificMemberAccess(node, objectName, propertyName) {
334 const checkNode = skipChainExpression(node);
335
336 if (checkNode.type !== "MemberExpression") {
337 return false;
338 }
339
340 if (objectName && !isSpecificId(checkNode.object, objectName)) {
341 return false;
342 }
343
344 if (propertyName) {
345 const actualPropertyName = getStaticPropertyName(checkNode);
346
347 if (typeof actualPropertyName !== "string" || !checkText(actualPropertyName, propertyName)) {
348 return false;
349 }
350 }
351
352 return true;
353 }
354
355 /**
356 * Check if two literal nodes are the same value.
357 * @param {ASTNode} left The Literal node to compare.
358 * @param {ASTNode} right The other Literal node to compare.
359 * @returns {boolean} `true` if the two literal nodes are the same value.
360 */
361 function equalLiteralValue(left, right) {
362
363 // RegExp literal.
364 if (left.regex || right.regex) {
365 return Boolean(
366 left.regex &&
367 right.regex &&
368 left.regex.pattern === right.regex.pattern &&
369 left.regex.flags === right.regex.flags
370 );
371 }
372
373 // BigInt literal.
374 if (left.bigint || right.bigint) {
375 return left.bigint === right.bigint;
376 }
377
378 return left.value === right.value;
379 }
380
381 /**
382 * Check if two expressions reference the same value. For example:
383 * a = a
384 * a.b = a.b
385 * a[0] = a[0]
386 * a['b'] = a['b']
387 * @param {ASTNode} left The left side of the comparison.
388 * @param {ASTNode} right The right side of the comparison.
389 * @param {boolean} [disableStaticComputedKey] Don't address `a.b` and `a["b"]` are the same if `true`. For backward compatibility.
390 * @returns {boolean} `true` if both sides match and reference the same value.
391 */
392 function isSameReference(left, right, disableStaticComputedKey = false) {
393 if (left.type !== right.type) {
394
395 // Handle `a.b` and `a?.b` are samely.
396 if (left.type === "ChainExpression") {
397 return isSameReference(left.expression, right, disableStaticComputedKey);
398 }
399 if (right.type === "ChainExpression") {
400 return isSameReference(left, right.expression, disableStaticComputedKey);
401 }
402
403 return false;
404 }
405
406 switch (left.type) {
407 case "Super":
408 case "ThisExpression":
409 return true;
410
411 case "Identifier":
412 case "PrivateIdentifier":
413 return left.name === right.name;
414 case "Literal":
415 return equalLiteralValue(left, right);
416
417 case "ChainExpression":
418 return isSameReference(left.expression, right.expression, disableStaticComputedKey);
419
420 case "MemberExpression": {
421 if (!disableStaticComputedKey) {
422 const nameA = getStaticPropertyName(left);
423
424 // x.y = x["y"]
425 if (nameA !== null) {
426 return (
427 isSameReference(left.object, right.object, disableStaticComputedKey) &&
428 nameA === getStaticPropertyName(right)
429 );
430 }
431 }
432
433 /*
434 * x[0] = x[0]
435 * x[y] = x[y]
436 * x.y = x.y
437 */
438 return (
439 left.computed === right.computed &&
440 isSameReference(left.object, right.object, disableStaticComputedKey) &&
441 isSameReference(left.property, right.property, disableStaticComputedKey)
442 );
443 }
444
445 default:
446 return false;
447 }
448 }
449
450 /**
451 * Checks whether or not a node is `Reflect.apply`.
452 * @param {ASTNode} node A node to check.
453 * @returns {boolean} Whether or not the node is a `Reflect.apply`.
454 */
455 function isReflectApply(node) {
456 return isSpecificMemberAccess(node, "Reflect", "apply");
457 }
458
459 /**
460 * Checks whether or not a node is `Array.from`.
461 * @param {ASTNode} node A node to check.
462 * @returns {boolean} Whether or not the node is a `Array.from`.
463 */
464 function isArrayFromMethod(node) {
465 return isSpecificMemberAccess(node, arrayOrTypedArrayPattern, "from");
466 }
467
468 /**
469 * Checks whether or not a node is a method which has `thisArg`.
470 * @param {ASTNode} node A node to check.
471 * @returns {boolean} Whether or not the node is a method which has `thisArg`.
472 */
473 function isMethodWhichHasThisArg(node) {
474 return isSpecificMemberAccess(node, null, arrayMethodPattern);
475 }
476
477 /**
478 * Creates the negate function of the given function.
479 * @param {Function} f The function to negate.
480 * @returns {Function} Negated function.
481 */
482 function negate(f) {
483 return token => !f(token);
484 }
485
486 /**
487 * Checks whether or not a node has a `@this` tag in its comments.
488 * @param {ASTNode} node A node to check.
489 * @param {SourceCode} sourceCode A SourceCode instance to get comments.
490 * @returns {boolean} Whether or not the node has a `@this` tag in its comments.
491 */
492 function hasJSDocThisTag(node, sourceCode) {
493 const jsdocComment = sourceCode.getJSDocComment(node);
494
495 if (jsdocComment && thisTagPattern.test(jsdocComment.value)) {
496 return true;
497 }
498
499 // Checks `@this` in its leading comments for callbacks,
500 // because callbacks don't have its JSDoc comment.
501 // e.g.
502 // sinon.test(/* @this sinon.Sandbox */function() { this.spy(); });
503 return sourceCode.getCommentsBefore(node).some(comment => thisTagPattern.test(comment.value));
504 }
505
506 /**
507 * Determines if a node is surrounded by parentheses.
508 * @param {SourceCode} sourceCode The ESLint source code object
509 * @param {ASTNode} node The node to be checked.
510 * @returns {boolean} True if the node is parenthesised.
511 * @private
512 */
513 function isParenthesised(sourceCode, node) {
514 const previousToken = sourceCode.getTokenBefore(node),
515 nextToken = sourceCode.getTokenAfter(node);
516
517 return Boolean(previousToken && nextToken) &&
518 previousToken.value === "(" && previousToken.range[1] <= node.range[0] &&
519 nextToken.value === ")" && nextToken.range[0] >= node.range[1];
520 }
521
522 /**
523 * Checks if the given token is a `=` token or not.
524 * @param {Token} token The token to check.
525 * @returns {boolean} `true` if the token is a `=` token.
526 */
527 function isEqToken(token) {
528 return token.value === "=" && token.type === "Punctuator";
529 }
530
531 /**
532 * Checks if the given token is an arrow token or not.
533 * @param {Token} token The token to check.
534 * @returns {boolean} `true` if the token is an arrow token.
535 */
536 function isArrowToken(token) {
537 return token.value === "=>" && token.type === "Punctuator";
538 }
539
540 /**
541 * Checks if the given token is a comma token or not.
542 * @param {Token} token The token to check.
543 * @returns {boolean} `true` if the token is a comma token.
544 */
545 function isCommaToken(token) {
546 return token.value === "," && token.type === "Punctuator";
547 }
548
549 /**
550 * Checks if the given token is a dot token or not.
551 * @param {Token} token The token to check.
552 * @returns {boolean} `true` if the token is a dot token.
553 */
554 function isDotToken(token) {
555 return token.value === "." && token.type === "Punctuator";
556 }
557
558 /**
559 * Checks if the given token is a `?.` token or not.
560 * @param {Token} token The token to check.
561 * @returns {boolean} `true` if the token is a `?.` token.
562 */
563 function isQuestionDotToken(token) {
564 return token.value === "?." && token.type === "Punctuator";
565 }
566
567 /**
568 * Checks if the given token is a semicolon token or not.
569 * @param {Token} token The token to check.
570 * @returns {boolean} `true` if the token is a semicolon token.
571 */
572 function isSemicolonToken(token) {
573 return token.value === ";" && token.type === "Punctuator";
574 }
575
576 /**
577 * Checks if the given token is a colon token or not.
578 * @param {Token} token The token to check.
579 * @returns {boolean} `true` if the token is a colon token.
580 */
581 function isColonToken(token) {
582 return token.value === ":" && token.type === "Punctuator";
583 }
584
585 /**
586 * Checks if the given token is an opening parenthesis token or not.
587 * @param {Token} token The token to check.
588 * @returns {boolean} `true` if the token is an opening parenthesis token.
589 */
590 function isOpeningParenToken(token) {
591 return token.value === "(" && token.type === "Punctuator";
592 }
593
594 /**
595 * Checks if the given token is a closing parenthesis token or not.
596 * @param {Token} token The token to check.
597 * @returns {boolean} `true` if the token is a closing parenthesis token.
598 */
599 function isClosingParenToken(token) {
600 return token.value === ")" && token.type === "Punctuator";
601 }
602
603 /**
604 * Checks if the given token is an opening square bracket token or not.
605 * @param {Token} token The token to check.
606 * @returns {boolean} `true` if the token is an opening square bracket token.
607 */
608 function isOpeningBracketToken(token) {
609 return token.value === "[" && token.type === "Punctuator";
610 }
611
612 /**
613 * Checks if the given token is a closing square bracket token or not.
614 * @param {Token} token The token to check.
615 * @returns {boolean} `true` if the token is a closing square bracket token.
616 */
617 function isClosingBracketToken(token) {
618 return token.value === "]" && token.type === "Punctuator";
619 }
620
621 /**
622 * Checks if the given token is an opening brace token or not.
623 * @param {Token} token The token to check.
624 * @returns {boolean} `true` if the token is an opening brace token.
625 */
626 function isOpeningBraceToken(token) {
627 return token.value === "{" && token.type === "Punctuator";
628 }
629
630 /**
631 * Checks if the given token is a closing brace token or not.
632 * @param {Token} token The token to check.
633 * @returns {boolean} `true` if the token is a closing brace token.
634 */
635 function isClosingBraceToken(token) {
636 return token.value === "}" && token.type === "Punctuator";
637 }
638
639 /**
640 * Checks if the given token is a comment token or not.
641 * @param {Token} token The token to check.
642 * @returns {boolean} `true` if the token is a comment token.
643 */
644 function isCommentToken(token) {
645 return token.type === "Line" || token.type === "Block" || token.type === "Shebang";
646 }
647
648 /**
649 * Checks if the given token is a keyword token or not.
650 * @param {Token} token The token to check.
651 * @returns {boolean} `true` if the token is a keyword token.
652 */
653 function isKeywordToken(token) {
654 return token.type === "Keyword";
655 }
656
657 /**
658 * Gets the `(` token of the given function node.
659 * @param {ASTNode} node The function node to get.
660 * @param {SourceCode} sourceCode The source code object to get tokens.
661 * @returns {Token} `(` token.
662 */
663 function getOpeningParenOfParams(node, sourceCode) {
664
665 // If the node is an arrow function and doesn't have parens, this returns the identifier of the first param.
666 if (node.type === "ArrowFunctionExpression" && node.params.length === 1) {
667 const argToken = sourceCode.getFirstToken(node.params[0]);
668 const maybeParenToken = sourceCode.getTokenBefore(argToken);
669
670 return isOpeningParenToken(maybeParenToken) ? maybeParenToken : argToken;
671 }
672
673 // Otherwise, returns paren.
674 return node.id
675 ? sourceCode.getTokenAfter(node.id, isOpeningParenToken)
676 : sourceCode.getFirstToken(node, isOpeningParenToken);
677 }
678
679 /**
680 * Checks whether or not the tokens of two given nodes are same.
681 * @param {ASTNode} left A node 1 to compare.
682 * @param {ASTNode} right A node 2 to compare.
683 * @param {SourceCode} sourceCode The ESLint source code object.
684 * @returns {boolean} the source code for the given node.
685 */
686 function equalTokens(left, right, sourceCode) {
687 const tokensL = sourceCode.getTokens(left);
688 const tokensR = sourceCode.getTokens(right);
689
690 if (tokensL.length !== tokensR.length) {
691 return false;
692 }
693 for (let i = 0; i < tokensL.length; ++i) {
694 if (tokensL[i].type !== tokensR[i].type ||
695 tokensL[i].value !== tokensR[i].value
696 ) {
697 return false;
698 }
699 }
700
701 return true;
702 }
703
704 /**
705 * Check if the given node is a true logical expression or not.
706 *
707 * The three binary expressions logical-or (`||`), logical-and (`&&`), and
708 * coalesce (`??`) are known as `ShortCircuitExpression`.
709 * But ESTree represents those by `LogicalExpression` node.
710 *
711 * This function rejects coalesce expressions of `LogicalExpression` node.
712 * @param {ASTNode} node The node to check.
713 * @returns {boolean} `true` if the node is `&&` or `||`.
714 * @see https://tc39.es/ecma262/#prod-ShortCircuitExpression
715 */
716 function isLogicalExpression(node) {
717 return (
718 node.type === "LogicalExpression" &&
719 (node.operator === "&&" || node.operator === "||")
720 );
721 }
722
723 /**
724 * Check if the given node is a nullish coalescing expression or not.
725 *
726 * The three binary expressions logical-or (`||`), logical-and (`&&`), and
727 * coalesce (`??`) are known as `ShortCircuitExpression`.
728 * But ESTree represents those by `LogicalExpression` node.
729 *
730 * This function finds only coalesce expressions of `LogicalExpression` node.
731 * @param {ASTNode} node The node to check.
732 * @returns {boolean} `true` if the node is `??`.
733 */
734 function isCoalesceExpression(node) {
735 return node.type === "LogicalExpression" && node.operator === "??";
736 }
737
738 /**
739 * Check if given two nodes are the pair of a logical expression and a coalesce expression.
740 * @param {ASTNode} left A node to check.
741 * @param {ASTNode} right Another node to check.
742 * @returns {boolean} `true` if the two nodes are the pair of a logical expression and a coalesce expression.
743 */
744 function isMixedLogicalAndCoalesceExpressions(left, right) {
745 return (
746 (isLogicalExpression(left) && isCoalesceExpression(right)) ||
747 (isCoalesceExpression(left) && isLogicalExpression(right))
748 );
749 }
750
751 /**
752 * Checks if the given operator is a logical assignment operator.
753 * @param {string} operator The operator to check.
754 * @returns {boolean} `true` if the operator is a logical assignment operator.
755 */
756 function isLogicalAssignmentOperator(operator) {
757 return LOGICAL_ASSIGNMENT_OPERATORS.has(operator);
758 }
759
760 /**
761 * Get the colon token of the given SwitchCase node.
762 * @param {ASTNode} node The SwitchCase node to get.
763 * @param {SourceCode} sourceCode The source code object to get tokens.
764 * @returns {Token} The colon token of the node.
765 */
766 function getSwitchCaseColonToken(node, sourceCode) {
767 if (node.test) {
768 return sourceCode.getTokenAfter(node.test, isColonToken);
769 }
770 return sourceCode.getFirstToken(node, 1);
771 }
772
773 /**
774 * Gets ESM module export name represented by the given node.
775 * @param {ASTNode} node `Identifier` or string `Literal` node in a position
776 * that represents a module export name:
777 * - `ImportSpecifier#imported`
778 * - `ExportSpecifier#local` (if it is a re-export from another module)
779 * - `ExportSpecifier#exported`
780 * - `ExportAllDeclaration#exported`
781 * @returns {string} The module export name.
782 */
783 function getModuleExportName(node) {
784 if (node.type === "Identifier") {
785 return node.name;
786 }
787
788 // string literal
789 return node.value;
790 }
791
792 /**
793 * Returns literal's value converted to the Boolean type
794 * @param {ASTNode} node any `Literal` node
795 * @returns {boolean | null} `true` when node is truthy, `false` when node is falsy,
796 * `null` when it cannot be determined.
797 */
798 function getBooleanValue(node) {
799 if (node.value === null) {
800
801 /*
802 * it might be a null literal or bigint/regex literal in unsupported environments .
803 * https://github.com/estree/estree/blob/14df8a024956ea289bd55b9c2226a1d5b8a473ee/es5.md#regexpliteral
804 * https://github.com/estree/estree/blob/14df8a024956ea289bd55b9c2226a1d5b8a473ee/es2020.md#bigintliteral
805 */
806
807 if (node.raw === "null") {
808 return false;
809 }
810
811 // regex is always truthy
812 if (typeof node.regex === "object") {
813 return true;
814 }
815
816 return null;
817 }
818
819 return !!node.value;
820 }
821
822 /**
823 * Checks if a branch node of LogicalExpression short circuits the whole condition
824 * @param {ASTNode} node The branch of main condition which needs to be checked
825 * @param {string} operator The operator of the main LogicalExpression.
826 * @returns {boolean} true when condition short circuits whole condition
827 */
828 function isLogicalIdentity(node, operator) {
829 switch (node.type) {
830 case "Literal":
831 return (operator === "||" && getBooleanValue(node) === true) ||
832 (operator === "&&" && getBooleanValue(node) === false);
833
834 case "UnaryExpression":
835 return (operator === "&&" && node.operator === "void");
836
837 case "LogicalExpression":
838
839 /*
840 * handles `a && false || b`
841 * `false` is an identity element of `&&` but not `||`
842 */
843 return operator === node.operator &&
844 (
845 isLogicalIdentity(node.left, operator) ||
846 isLogicalIdentity(node.right, operator)
847 );
848
849 case "AssignmentExpression":
850 return ["||=", "&&="].includes(node.operator) &&
851 operator === node.operator.slice(0, -1) &&
852 isLogicalIdentity(node.right, operator);
853
854 // no default
855 }
856 return false;
857 }
858
859 /**
860 * Checks if an identifier is a reference to a global variable.
861 * @param {Scope} scope The scope in which the identifier is referenced.
862 * @param {ASTNode} node An identifier node to check.
863 * @returns {boolean} `true` if the identifier is a reference to a global variable.
864 */
865 function isReferenceToGlobalVariable(scope, node) {
866 const reference = scope.references.find(ref => ref.identifier === node);
867
868 return Boolean(
869 reference &&
870 reference.resolved &&
871 reference.resolved.scope.type === "global" &&
872 reference.resolved.defs.length === 0
873 );
874 }
875
876
877 /**
878 * Checks if a node has a constant truthiness value.
879 * @param {Scope} scope Scope in which the node appears.
880 * @param {ASTNode} node The AST node to check.
881 * @param {boolean} inBooleanPosition `true` if checking the test of a
882 * condition. `false` in all other cases. When `false`, checks if -- for
883 * both string and number -- if coerced to that type, the value will
884 * be constant.
885 * @returns {boolean} true when node's truthiness is constant
886 * @private
887 */
888 function isConstant(scope, node, inBooleanPosition) {
889
890 // node.elements can return null values in the case of sparse arrays ex. [,]
891 if (!node) {
892 return true;
893 }
894 switch (node.type) {
895 case "Literal":
896 case "ArrowFunctionExpression":
897 case "FunctionExpression":
898 return true;
899 case "ClassExpression":
900 case "ObjectExpression":
901
902 /**
903 * In theory objects like:
904 *
905 * `{toString: () => a}`
906 * `{valueOf: () => a}`
907 *
908 * Or a classes like:
909 *
910 * `class { static toString() { return a } }`
911 * `class { static valueOf() { return a } }`
912 *
913 * Are not constant verifiably when `inBooleanPosition` is
914 * false, but it's an edge case we've opted not to handle.
915 */
916 return true;
917 case "TemplateLiteral":
918 return (inBooleanPosition && node.quasis.some(quasi => quasi.value.cooked.length)) ||
919 node.expressions.every(exp => isConstant(scope, exp, false));
920
921 case "ArrayExpression": {
922 if (!inBooleanPosition) {
923 return node.elements.every(element => isConstant(scope, element, false));
924 }
925 return true;
926 }
927
928 case "UnaryExpression":
929 if (
930 node.operator === "void" ||
931 node.operator === "typeof" && inBooleanPosition
932 ) {
933 return true;
934 }
935
936 if (node.operator === "!") {
937 return isConstant(scope, node.argument, true);
938 }
939
940 return isConstant(scope, node.argument, false);
941
942 case "BinaryExpression":
943 return isConstant(scope, node.left, false) &&
944 isConstant(scope, node.right, false) &&
945 node.operator !== "in";
946
947 case "LogicalExpression": {
948 const isLeftConstant = isConstant(scope, node.left, inBooleanPosition);
949 const isRightConstant = isConstant(scope, node.right, inBooleanPosition);
950 const isLeftShortCircuit = (isLeftConstant && isLogicalIdentity(node.left, node.operator));
951 const isRightShortCircuit = (inBooleanPosition && isRightConstant && isLogicalIdentity(node.right, node.operator));
952
953 return (isLeftConstant && isRightConstant) ||
954 isLeftShortCircuit ||
955 isRightShortCircuit;
956 }
957 case "NewExpression":
958 return inBooleanPosition;
959 case "AssignmentExpression":
960 if (node.operator === "=") {
961 return isConstant(scope, node.right, inBooleanPosition);
962 }
963
964 if (["||=", "&&="].includes(node.operator) && inBooleanPosition) {
965 return isLogicalIdentity(node.right, node.operator.slice(0, -1));
966 }
967
968 return false;
969
970 case "SequenceExpression":
971 return isConstant(scope, node.expressions[node.expressions.length - 1], inBooleanPosition);
972 case "SpreadElement":
973 return isConstant(scope, node.argument, inBooleanPosition);
974 case "CallExpression":
975 if (node.callee.type === "Identifier" && node.callee.name === "Boolean") {
976 if (node.arguments.length === 0 || isConstant(scope, node.arguments[0], true)) {
977 return isReferenceToGlobalVariable(scope, node.callee);
978 }
979 }
980 return false;
981 case "Identifier":
982 return node.name === "undefined" && isReferenceToGlobalVariable(scope, node);
983
984 // no default
985 }
986 return false;
987 }
988
989 //------------------------------------------------------------------------------
990 // Public Interface
991 //------------------------------------------------------------------------------
992
993 module.exports = {
994 COMMENTS_IGNORE_PATTERN,
995 LINEBREAKS,
996 LINEBREAK_MATCHER: lineBreakPattern,
997 SHEBANG_MATCHER: shebangPattern,
998 STATEMENT_LIST_PARENTS,
999
1000 /**
1001 * Determines whether two adjacent tokens are on the same line.
1002 * @param {Object} left The left token object.
1003 * @param {Object} right The right token object.
1004 * @returns {boolean} Whether or not the tokens are on the same line.
1005 * @public
1006 */
1007 isTokenOnSameLine(left, right) {
1008 return left.loc.end.line === right.loc.start.line;
1009 },
1010
1011 isNullOrUndefined,
1012 isCallee,
1013 isES5Constructor,
1014 getUpperFunction,
1015 isFunction,
1016 isLoop,
1017 isInLoop,
1018 isArrayFromMethod,
1019 isParenthesised,
1020 createGlobalLinebreakMatcher,
1021 equalTokens,
1022
1023 isArrowToken,
1024 isClosingBraceToken,
1025 isClosingBracketToken,
1026 isClosingParenToken,
1027 isColonToken,
1028 isCommaToken,
1029 isCommentToken,
1030 isDotToken,
1031 isQuestionDotToken,
1032 isKeywordToken,
1033 isNotClosingBraceToken: negate(isClosingBraceToken),
1034 isNotClosingBracketToken: negate(isClosingBracketToken),
1035 isNotClosingParenToken: negate(isClosingParenToken),
1036 isNotColonToken: negate(isColonToken),
1037 isNotCommaToken: negate(isCommaToken),
1038 isNotDotToken: negate(isDotToken),
1039 isNotQuestionDotToken: negate(isQuestionDotToken),
1040 isNotOpeningBraceToken: negate(isOpeningBraceToken),
1041 isNotOpeningBracketToken: negate(isOpeningBracketToken),
1042 isNotOpeningParenToken: negate(isOpeningParenToken),
1043 isNotSemicolonToken: negate(isSemicolonToken),
1044 isOpeningBraceToken,
1045 isOpeningBracketToken,
1046 isOpeningParenToken,
1047 isSemicolonToken,
1048 isEqToken,
1049
1050 /**
1051 * Checks whether or not a given node is a string literal.
1052 * @param {ASTNode} node A node to check.
1053 * @returns {boolean} `true` if the node is a string literal.
1054 */
1055 isStringLiteral(node) {
1056 return (
1057 (node.type === "Literal" && typeof node.value === "string") ||
1058 node.type === "TemplateLiteral"
1059 );
1060 },
1061
1062 /**
1063 * Checks whether a given node is a breakable statement or not.
1064 * The node is breakable if the node is one of the following type:
1065 *
1066 * - DoWhileStatement
1067 * - ForInStatement
1068 * - ForOfStatement
1069 * - ForStatement
1070 * - SwitchStatement
1071 * - WhileStatement
1072 * @param {ASTNode} node A node to check.
1073 * @returns {boolean} `true` if the node is breakable.
1074 */
1075 isBreakableStatement(node) {
1076 return breakableTypePattern.test(node.type);
1077 },
1078
1079 /**
1080 * Gets references which are non initializer and writable.
1081 * @param {Reference[]} references An array of references.
1082 * @returns {Reference[]} An array of only references which are non initializer and writable.
1083 * @public
1084 */
1085 getModifyingReferences(references) {
1086 return references.filter(isModifyingReference);
1087 },
1088
1089 /**
1090 * Validate that a string passed in is surrounded by the specified character
1091 * @param {string} val The text to check.
1092 * @param {string} character The character to see if it's surrounded by.
1093 * @returns {boolean} True if the text is surrounded by the character, false if not.
1094 * @private
1095 */
1096 isSurroundedBy(val, character) {
1097 return val[0] === character && val[val.length - 1] === character;
1098 },
1099
1100 /**
1101 * Returns whether the provided node is an ESLint directive comment or not
1102 * @param {Line|Block} node The comment token to be checked
1103 * @returns {boolean} `true` if the node is an ESLint directive comment
1104 */
1105 isDirectiveComment(node) {
1106 const comment = node.value.trim();
1107
1108 return (
1109 node.type === "Line" && comment.startsWith("eslint-") ||
1110 node.type === "Block" && ESLINT_DIRECTIVE_PATTERN.test(comment)
1111 );
1112 },
1113
1114 /**
1115 * Gets the trailing statement of a given node.
1116 *
1117 * if (code)
1118 * consequent;
1119 *
1120 * When taking this `IfStatement`, returns `consequent;` statement.
1121 * @param {ASTNode} A node to get.
1122 * @returns {ASTNode|null} The trailing statement's node.
1123 */
1124 getTrailingStatement: esutils.ast.trailingStatement,
1125
1126 /**
1127 * Finds the variable by a given name in a given scope and its upper scopes.
1128 * @param {eslint-scope.Scope} initScope A scope to start find.
1129 * @param {string} name A variable name to find.
1130 * @returns {eslint-scope.Variable|null} A found variable or `null`.
1131 */
1132 getVariableByName(initScope, name) {
1133 let scope = initScope;
1134
1135 while (scope) {
1136 const variable = scope.set.get(name);
1137
1138 if (variable) {
1139 return variable;
1140 }
1141
1142 scope = scope.upper;
1143 }
1144
1145 return null;
1146 },
1147
1148 /**
1149 * Checks whether or not a given function node is the default `this` binding.
1150 *
1151 * First, this checks the node:
1152 *
1153 * - The given node is not in `PropertyDefinition#value` position.
1154 * - The given node is not `StaticBlock`.
1155 * - The function name does not start with uppercase. It's a convention to capitalize the names
1156 * of constructor functions. This check is not performed if `capIsConstructor` is set to `false`.
1157 * - The function does not have a JSDoc comment that has a @this tag.
1158 *
1159 * Next, this checks the location of the node.
1160 * If the location is below, this judges `this` is valid.
1161 *
1162 * - The location is not on an object literal.
1163 * - The location is not assigned to a variable which starts with an uppercase letter. Applies to anonymous
1164 * functions only, as the name of the variable is considered to be the name of the function in this case.
1165 * This check is not performed if `capIsConstructor` is set to `false`.
1166 * - The location is not on an ES2015 class.
1167 * - Its `bind`/`call`/`apply` method is not called directly.
1168 * - The function is not a callback of array methods (such as `.forEach()`) if `thisArg` is given.
1169 * @param {ASTNode} node A function node to check. It also can be an implicit function, like `StaticBlock`
1170 * or any expression that is `PropertyDefinition#value` node.
1171 * @param {SourceCode} sourceCode A SourceCode instance to get comments.
1172 * @param {boolean} [capIsConstructor = true] `false` disables the assumption that functions which name starts
1173 * with an uppercase or are assigned to a variable which name starts with an uppercase are constructors.
1174 * @returns {boolean} The function node is the default `this` binding.
1175 */
1176 isDefaultThisBinding(node, sourceCode, { capIsConstructor = true } = {}) {
1177
1178 /*
1179 * Class field initializers are implicit functions, but ESTree doesn't have the AST node of field initializers.
1180 * Therefore, A expression node at `PropertyDefinition#value` is a function.
1181 * In this case, `this` is always not default binding.
1182 */
1183 if (node.parent.type === "PropertyDefinition" && node.parent.value === node) {
1184 return false;
1185 }
1186
1187 // Class static blocks are implicit functions. In this case, `this` is always not default binding.
1188 if (node.type === "StaticBlock") {
1189 return false;
1190 }
1191
1192 if (
1193 (capIsConstructor && isES5Constructor(node)) ||
1194 hasJSDocThisTag(node, sourceCode)
1195 ) {
1196 return false;
1197 }
1198 const isAnonymous = node.id === null;
1199 let currentNode = node;
1200
1201 while (currentNode) {
1202 const parent = currentNode.parent;
1203
1204 switch (parent.type) {
1205
1206 /*
1207 * Looks up the destination.
1208 * e.g., obj.foo = nativeFoo || function foo() { ... };
1209 */
1210 case "LogicalExpression":
1211 case "ConditionalExpression":
1212 case "ChainExpression":
1213 currentNode = parent;
1214 break;
1215
1216 /*
1217 * If the upper function is IIFE, checks the destination of the return value.
1218 * e.g.
1219 * obj.foo = (function() {
1220 * // setup...
1221 * return function foo() { ... };
1222 * })();
1223 * obj.foo = (() =>
1224 * function foo() { ... }
1225 * )();
1226 */
1227 case "ReturnStatement": {
1228 const func = getUpperFunction(parent);
1229
1230 if (func === null || !isCallee(func)) {
1231 return true;
1232 }
1233 currentNode = func.parent;
1234 break;
1235 }
1236 case "ArrowFunctionExpression":
1237 if (currentNode !== parent.body || !isCallee(parent)) {
1238 return true;
1239 }
1240 currentNode = parent.parent;
1241 break;
1242
1243 /*
1244 * e.g.
1245 * var obj = { foo() { ... } };
1246 * var obj = { foo: function() { ... } };
1247 * class A { constructor() { ... } }
1248 * class A { foo() { ... } }
1249 * class A { get foo() { ... } }
1250 * class A { set foo() { ... } }
1251 * class A { static foo() { ... } }
1252 * class A { foo = function() { ... } }
1253 */
1254 case "Property":
1255 case "PropertyDefinition":
1256 case "MethodDefinition":
1257 return parent.value !== currentNode;
1258
1259 /*
1260 * e.g.
1261 * obj.foo = function foo() { ... };
1262 * Foo = function() { ... };
1263 * [obj.foo = function foo() { ... }] = a;
1264 * [Foo = function() { ... }] = a;
1265 */
1266 case "AssignmentExpression":
1267 case "AssignmentPattern":
1268 if (parent.left.type === "MemberExpression") {
1269 return false;
1270 }
1271 if (
1272 capIsConstructor &&
1273 isAnonymous &&
1274 parent.left.type === "Identifier" &&
1275 startsWithUpperCase(parent.left.name)
1276 ) {
1277 return false;
1278 }
1279 return true;
1280
1281 /*
1282 * e.g.
1283 * var Foo = function() { ... };
1284 */
1285 case "VariableDeclarator":
1286 return !(
1287 capIsConstructor &&
1288 isAnonymous &&
1289 parent.init === currentNode &&
1290 parent.id.type === "Identifier" &&
1291 startsWithUpperCase(parent.id.name)
1292 );
1293
1294 /*
1295 * e.g.
1296 * var foo = function foo() { ... }.bind(obj);
1297 * (function foo() { ... }).call(obj);
1298 * (function foo() { ... }).apply(obj, []);
1299 */
1300 case "MemberExpression":
1301 if (
1302 parent.object === currentNode &&
1303 isSpecificMemberAccess(parent, null, bindOrCallOrApplyPattern)
1304 ) {
1305 const maybeCalleeNode = parent.parent.type === "ChainExpression"
1306 ? parent.parent
1307 : parent;
1308
1309 return !(
1310 isCallee(maybeCalleeNode) &&
1311 maybeCalleeNode.parent.arguments.length >= 1 &&
1312 !isNullOrUndefined(maybeCalleeNode.parent.arguments[0])
1313 );
1314 }
1315 return true;
1316
1317 /*
1318 * e.g.
1319 * Reflect.apply(function() {}, obj, []);
1320 * Array.from([], function() {}, obj);
1321 * list.forEach(function() {}, obj);
1322 */
1323 case "CallExpression":
1324 if (isReflectApply(parent.callee)) {
1325 return (
1326 parent.arguments.length !== 3 ||
1327 parent.arguments[0] !== currentNode ||
1328 isNullOrUndefined(parent.arguments[1])
1329 );
1330 }
1331 if (isArrayFromMethod(parent.callee)) {
1332 return (
1333 parent.arguments.length !== 3 ||
1334 parent.arguments[1] !== currentNode ||
1335 isNullOrUndefined(parent.arguments[2])
1336 );
1337 }
1338 if (isMethodWhichHasThisArg(parent.callee)) {
1339 return (
1340 parent.arguments.length !== 2 ||
1341 parent.arguments[0] !== currentNode ||
1342 isNullOrUndefined(parent.arguments[1])
1343 );
1344 }
1345 return true;
1346
1347 // Otherwise `this` is default.
1348 default:
1349 return true;
1350 }
1351 }
1352
1353 /* c8 ignore next */
1354 return true;
1355 },
1356
1357 /**
1358 * Get the precedence level based on the node type
1359 * @param {ASTNode} node node to evaluate
1360 * @returns {int} precedence level
1361 * @private
1362 */
1363 getPrecedence(node) {
1364 switch (node.type) {
1365 case "SequenceExpression":
1366 return 0;
1367
1368 case "AssignmentExpression":
1369 case "ArrowFunctionExpression":
1370 case "YieldExpression":
1371 return 1;
1372
1373 case "ConditionalExpression":
1374 return 3;
1375
1376 case "LogicalExpression":
1377 switch (node.operator) {
1378 case "||":
1379 case "??":
1380 return 4;
1381 case "&&":
1382 return 5;
1383
1384 // no default
1385 }
1386
1387 /* falls through */
1388
1389 case "BinaryExpression":
1390
1391 switch (node.operator) {
1392 case "|":
1393 return 6;
1394 case "^":
1395 return 7;
1396 case "&":
1397 return 8;
1398 case "==":
1399 case "!=":
1400 case "===":
1401 case "!==":
1402 return 9;
1403 case "<":
1404 case "<=":
1405 case ">":
1406 case ">=":
1407 case "in":
1408 case "instanceof":
1409 return 10;
1410 case "<<":
1411 case ">>":
1412 case ">>>":
1413 return 11;
1414 case "+":
1415 case "-":
1416 return 12;
1417 case "*":
1418 case "/":
1419 case "%":
1420 return 13;
1421 case "**":
1422 return 15;
1423
1424 // no default
1425 }
1426
1427 /* falls through */
1428
1429 case "UnaryExpression":
1430 case "AwaitExpression":
1431 return 16;
1432
1433 case "UpdateExpression":
1434 return 17;
1435
1436 case "CallExpression":
1437 case "ChainExpression":
1438 case "ImportExpression":
1439 return 18;
1440
1441 case "NewExpression":
1442 return 19;
1443
1444 default:
1445 return 20;
1446 }
1447 },
1448
1449 /**
1450 * Checks whether the given node is an empty block node or not.
1451 * @param {ASTNode|null} node The node to check.
1452 * @returns {boolean} `true` if the node is an empty block.
1453 */
1454 isEmptyBlock(node) {
1455 return Boolean(node && node.type === "BlockStatement" && node.body.length === 0);
1456 },
1457
1458 /**
1459 * Checks whether the given node is an empty function node or not.
1460 * @param {ASTNode|null} node The node to check.
1461 * @returns {boolean} `true` if the node is an empty function.
1462 */
1463 isEmptyFunction(node) {
1464 return isFunction(node) && module.exports.isEmptyBlock(node.body);
1465 },
1466
1467 /**
1468 * Get directives from directive prologue of a Program or Function node.
1469 * @param {ASTNode} node The node to check.
1470 * @returns {ASTNode[]} The directives found in the directive prologue.
1471 */
1472 getDirectivePrologue(node) {
1473 const directives = [];
1474
1475 // Directive prologues only occur at the top of files or functions.
1476 if (
1477 node.type === "Program" ||
1478 node.type === "FunctionDeclaration" ||
1479 node.type === "FunctionExpression" ||
1480
1481 /*
1482 * Do not check arrow functions with implicit return.
1483 * `() => "use strict";` returns the string `"use strict"`.
1484 */
1485 (node.type === "ArrowFunctionExpression" && node.body.type === "BlockStatement")
1486 ) {
1487 const statements = node.type === "Program" ? node.body : node.body.body;
1488
1489 for (const statement of statements) {
1490 if (
1491 statement.type === "ExpressionStatement" &&
1492 statement.expression.type === "Literal"
1493 ) {
1494 directives.push(statement);
1495 } else {
1496 break;
1497 }
1498 }
1499 }
1500
1501 return directives;
1502 },
1503
1504
1505 /**
1506 * Determines whether this node is a decimal integer literal. If a node is a decimal integer literal, a dot added
1507 * after the node will be parsed as a decimal point, rather than a property-access dot.
1508 * @param {ASTNode} node The node to check.
1509 * @returns {boolean} `true` if this node is a decimal integer.
1510 * @example
1511 *
1512 * 0 // true
1513 * 5 // true
1514 * 50 // true
1515 * 5_000 // true
1516 * 1_234_56 // true
1517 * 08 // true
1518 * 0192 // true
1519 * 5. // false
1520 * .5 // false
1521 * 5.0 // false
1522 * 5.00_00 // false
1523 * 05 // false
1524 * 0x5 // false
1525 * 0b101 // false
1526 * 0b11_01 // false
1527 * 0o5 // false
1528 * 5e0 // false
1529 * 5e1_000 // false
1530 * 5n // false
1531 * 1_000n // false
1532 * "5" // false
1533 *
1534 */
1535 isDecimalInteger(node) {
1536 return node.type === "Literal" && typeof node.value === "number" &&
1537 DECIMAL_INTEGER_PATTERN.test(node.raw);
1538 },
1539
1540 /**
1541 * Determines whether this token is a decimal integer numeric token.
1542 * This is similar to isDecimalInteger(), but for tokens.
1543 * @param {Token} token The token to check.
1544 * @returns {boolean} `true` if this token is a decimal integer.
1545 */
1546 isDecimalIntegerNumericToken(token) {
1547 return token.type === "Numeric" && DECIMAL_INTEGER_PATTERN.test(token.value);
1548 },
1549
1550 /**
1551 * Gets the name and kind of the given function node.
1552 *
1553 * - `function foo() {}` .................... `function 'foo'`
1554 * - `(function foo() {})` .................. `function 'foo'`
1555 * - `(function() {})` ...................... `function`
1556 * - `function* foo() {}` ................... `generator function 'foo'`
1557 * - `(function* foo() {})` ................. `generator function 'foo'`
1558 * - `(function*() {})` ..................... `generator function`
1559 * - `() => {}` ............................. `arrow function`
1560 * - `async () => {}` ....................... `async arrow function`
1561 * - `({ foo: function foo() {} })` ......... `method 'foo'`
1562 * - `({ foo: function() {} })` ............. `method 'foo'`
1563 * - `({ ['foo']: function() {} })` ......... `method 'foo'`
1564 * - `({ [foo]: function() {} })` ........... `method`
1565 * - `({ foo() {} })` ....................... `method 'foo'`
1566 * - `({ foo: function* foo() {} })` ........ `generator method 'foo'`
1567 * - `({ foo: function*() {} })` ............ `generator method 'foo'`
1568 * - `({ ['foo']: function*() {} })` ........ `generator method 'foo'`
1569 * - `({ [foo]: function*() {} })` .......... `generator method`
1570 * - `({ *foo() {} })` ...................... `generator method 'foo'`
1571 * - `({ foo: async function foo() {} })` ... `async method 'foo'`
1572 * - `({ foo: async function() {} })` ....... `async method 'foo'`
1573 * - `({ ['foo']: async function() {} })` ... `async method 'foo'`
1574 * - `({ [foo]: async function() {} })` ..... `async method`
1575 * - `({ async foo() {} })` ................. `async method 'foo'`
1576 * - `({ get foo() {} })` ................... `getter 'foo'`
1577 * - `({ set foo(a) {} })` .................. `setter 'foo'`
1578 * - `class A { constructor() {} }` ......... `constructor`
1579 * - `class A { foo() {} }` ................. `method 'foo'`
1580 * - `class A { *foo() {} }` ................ `generator method 'foo'`
1581 * - `class A { async foo() {} }` ........... `async method 'foo'`
1582 * - `class A { ['foo']() {} }` ............. `method 'foo'`
1583 * - `class A { *['foo']() {} }` ............ `generator method 'foo'`
1584 * - `class A { async ['foo']() {} }` ....... `async method 'foo'`
1585 * - `class A { [foo]() {} }` ............... `method`
1586 * - `class A { *[foo]() {} }` .............. `generator method`
1587 * - `class A { async [foo]() {} }` ......... `async method`
1588 * - `class A { get foo() {} }` ............. `getter 'foo'`
1589 * - `class A { set foo(a) {} }` ............ `setter 'foo'`
1590 * - `class A { static foo() {} }` .......... `static method 'foo'`
1591 * - `class A { static *foo() {} }` ......... `static generator method 'foo'`
1592 * - `class A { static async foo() {} }` .... `static async method 'foo'`
1593 * - `class A { static get foo() {} }` ...... `static getter 'foo'`
1594 * - `class A { static set foo(a) {} }` ..... `static setter 'foo'`
1595 * - `class A { foo = () => {}; }` .......... `method 'foo'`
1596 * - `class A { foo = function() {}; }` ..... `method 'foo'`
1597 * - `class A { foo = function bar() {}; }` . `method 'foo'`
1598 * - `class A { static foo = () => {}; }` ... `static method 'foo'`
1599 * - `class A { '#foo' = () => {}; }` ....... `method '#foo'`
1600 * - `class A { #foo = () => {}; }` ......... `private method #foo`
1601 * - `class A { static #foo = () => {}; }` .. `static private method #foo`
1602 * - `class A { '#foo'() {} }` .............. `method '#foo'`
1603 * - `class A { #foo() {} }` ................ `private method #foo`
1604 * - `class A { static #foo() {} }` ......... `static private method #foo`
1605 * @param {ASTNode} node The function node to get.
1606 * @returns {string} The name and kind of the function node.
1607 */
1608 getFunctionNameWithKind(node) {
1609 const parent = node.parent;
1610 const tokens = [];
1611
1612 if (parent.type === "MethodDefinition" || parent.type === "PropertyDefinition") {
1613
1614 // The proposal uses `static` word consistently before visibility words: https://github.com/tc39/proposal-static-class-features
1615 if (parent.static) {
1616 tokens.push("static");
1617 }
1618 if (!parent.computed && parent.key.type === "PrivateIdentifier") {
1619 tokens.push("private");
1620 }
1621 }
1622 if (node.async) {
1623 tokens.push("async");
1624 }
1625 if (node.generator) {
1626 tokens.push("generator");
1627 }
1628
1629 if (parent.type === "Property" || parent.type === "MethodDefinition") {
1630 if (parent.kind === "constructor") {
1631 return "constructor";
1632 }
1633 if (parent.kind === "get") {
1634 tokens.push("getter");
1635 } else if (parent.kind === "set") {
1636 tokens.push("setter");
1637 } else {
1638 tokens.push("method");
1639 }
1640 } else if (parent.type === "PropertyDefinition") {
1641 tokens.push("method");
1642 } else {
1643 if (node.type === "ArrowFunctionExpression") {
1644 tokens.push("arrow");
1645 }
1646 tokens.push("function");
1647 }
1648
1649 if (parent.type === "Property" || parent.type === "MethodDefinition" || parent.type === "PropertyDefinition") {
1650 if (!parent.computed && parent.key.type === "PrivateIdentifier") {
1651 tokens.push(`#${parent.key.name}`);
1652 } else {
1653 const name = getStaticPropertyName(parent);
1654
1655 if (name !== null) {
1656 tokens.push(`'${name}'`);
1657 } else if (node.id) {
1658 tokens.push(`'${node.id.name}'`);
1659 }
1660 }
1661 } else if (node.id) {
1662 tokens.push(`'${node.id.name}'`);
1663 }
1664
1665 return tokens.join(" ");
1666 },
1667
1668 /**
1669 * Gets the location of the given function node for reporting.
1670 *
1671 * - `function foo() {}`
1672 * ^^^^^^^^^^^^
1673 * - `(function foo() {})`
1674 * ^^^^^^^^^^^^
1675 * - `(function() {})`
1676 * ^^^^^^^^
1677 * - `function* foo() {}`
1678 * ^^^^^^^^^^^^^
1679 * - `(function* foo() {})`
1680 * ^^^^^^^^^^^^^
1681 * - `(function*() {})`
1682 * ^^^^^^^^^
1683 * - `() => {}`
1684 * ^^
1685 * - `async () => {}`
1686 * ^^
1687 * - `({ foo: function foo() {} })`
1688 * ^^^^^^^^^^^^^^^^^
1689 * - `({ foo: function() {} })`
1690 * ^^^^^^^^^^^^^
1691 * - `({ ['foo']: function() {} })`
1692 * ^^^^^^^^^^^^^^^^^
1693 * - `({ [foo]: function() {} })`
1694 * ^^^^^^^^^^^^^^^
1695 * - `({ foo() {} })`
1696 * ^^^
1697 * - `({ foo: function* foo() {} })`
1698 * ^^^^^^^^^^^^^^^^^^
1699 * - `({ foo: function*() {} })`
1700 * ^^^^^^^^^^^^^^
1701 * - `({ ['foo']: function*() {} })`
1702 * ^^^^^^^^^^^^^^^^^^
1703 * - `({ [foo]: function*() {} })`
1704 * ^^^^^^^^^^^^^^^^
1705 * - `({ *foo() {} })`
1706 * ^^^^
1707 * - `({ foo: async function foo() {} })`
1708 * ^^^^^^^^^^^^^^^^^^^^^^^
1709 * - `({ foo: async function() {} })`
1710 * ^^^^^^^^^^^^^^^^^^^
1711 * - `({ ['foo']: async function() {} })`
1712 * ^^^^^^^^^^^^^^^^^^^^^^^
1713 * - `({ [foo]: async function() {} })`
1714 * ^^^^^^^^^^^^^^^^^^^^^
1715 * - `({ async foo() {} })`
1716 * ^^^^^^^^^
1717 * - `({ get foo() {} })`
1718 * ^^^^^^^
1719 * - `({ set foo(a) {} })`
1720 * ^^^^^^^
1721 * - `class A { constructor() {} }`
1722 * ^^^^^^^^^^^
1723 * - `class A { foo() {} }`
1724 * ^^^
1725 * - `class A { *foo() {} }`
1726 * ^^^^
1727 * - `class A { async foo() {} }`
1728 * ^^^^^^^^^
1729 * - `class A { ['foo']() {} }`
1730 * ^^^^^^^
1731 * - `class A { *['foo']() {} }`
1732 * ^^^^^^^^
1733 * - `class A { async ['foo']() {} }`
1734 * ^^^^^^^^^^^^^
1735 * - `class A { [foo]() {} }`
1736 * ^^^^^
1737 * - `class A { *[foo]() {} }`
1738 * ^^^^^^
1739 * - `class A { async [foo]() {} }`
1740 * ^^^^^^^^^^^
1741 * - `class A { get foo() {} }`
1742 * ^^^^^^^
1743 * - `class A { set foo(a) {} }`
1744 * ^^^^^^^
1745 * - `class A { static foo() {} }`
1746 * ^^^^^^^^^^
1747 * - `class A { static *foo() {} }`
1748 * ^^^^^^^^^^^
1749 * - `class A { static async foo() {} }`
1750 * ^^^^^^^^^^^^^^^^
1751 * - `class A { static get foo() {} }`
1752 * ^^^^^^^^^^^^^^
1753 * - `class A { static set foo(a) {} }`
1754 * ^^^^^^^^^^^^^^
1755 * - `class A { foo = function() {} }`
1756 * ^^^^^^^^^^^^^^
1757 * - `class A { static foo = function() {} }`
1758 * ^^^^^^^^^^^^^^^^^^^^^
1759 * - `class A { foo = (a, b) => {} }`
1760 * ^^^^^^
1761 * @param {ASTNode} node The function node to get.
1762 * @param {SourceCode} sourceCode The source code object to get tokens.
1763 * @returns {string} The location of the function node for reporting.
1764 */
1765 getFunctionHeadLoc(node, sourceCode) {
1766 const parent = node.parent;
1767 let start = null;
1768 let end = null;
1769
1770 if (parent.type === "Property" || parent.type === "MethodDefinition" || parent.type === "PropertyDefinition") {
1771 start = parent.loc.start;
1772 end = getOpeningParenOfParams(node, sourceCode).loc.start;
1773 } else if (node.type === "ArrowFunctionExpression") {
1774 const arrowToken = sourceCode.getTokenBefore(node.body, isArrowToken);
1775
1776 start = arrowToken.loc.start;
1777 end = arrowToken.loc.end;
1778 } else {
1779 start = node.loc.start;
1780 end = getOpeningParenOfParams(node, sourceCode).loc.start;
1781 }
1782
1783 return {
1784 start: Object.assign({}, start),
1785 end: Object.assign({}, end)
1786 };
1787 },
1788
1789 /**
1790 * Gets next location when the result is not out of bound, otherwise returns null.
1791 *
1792 * Assumptions:
1793 *
1794 * - The given location represents a valid location in the given source code.
1795 * - Columns are 0-based.
1796 * - Lines are 1-based.
1797 * - Column immediately after the last character in a line (not incl. linebreaks) is considered to be a valid location.
1798 * - If the source code ends with a linebreak, `sourceCode.lines` array will have an extra element (empty string) at the end.
1799 * The start (column 0) of that extra line is considered to be a valid location.
1800 *
1801 * Examples of successive locations (line, column):
1802 *
1803 * code: foo
1804 * locations: (1, 0) -> (1, 1) -> (1, 2) -> (1, 3) -> null
1805 *
1806 * code: foo<LF>
1807 * locations: (1, 0) -> (1, 1) -> (1, 2) -> (1, 3) -> (2, 0) -> null
1808 *
1809 * code: foo<CR><LF>
1810 * locations: (1, 0) -> (1, 1) -> (1, 2) -> (1, 3) -> (2, 0) -> null
1811 *
1812 * code: a<LF>b
1813 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (2, 1) -> null
1814 *
1815 * code: a<LF>b<LF>
1816 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (2, 1) -> (3, 0) -> null
1817 *
1818 * code: a<CR><LF>b<CR><LF>
1819 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (2, 1) -> (3, 0) -> null
1820 *
1821 * code: a<LF><LF>
1822 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (3, 0) -> null
1823 *
1824 * code: <LF>
1825 * locations: (1, 0) -> (2, 0) -> null
1826 *
1827 * code:
1828 * locations: (1, 0) -> null
1829 * @param {SourceCode} sourceCode The sourceCode
1830 * @param {{line: number, column: number}} location The location
1831 * @returns {{line: number, column: number} | null} Next location
1832 */
1833 getNextLocation(sourceCode, { line, column }) {
1834 if (column < sourceCode.lines[line - 1].length) {
1835 return {
1836 line,
1837 column: column + 1
1838 };
1839 }
1840
1841 if (line < sourceCode.lines.length) {
1842 return {
1843 line: line + 1,
1844 column: 0
1845 };
1846 }
1847
1848 return null;
1849 },
1850
1851 /**
1852 * Gets the parenthesized text of a node. This is similar to sourceCode.getText(node), but it also includes any parentheses
1853 * surrounding the node.
1854 * @param {SourceCode} sourceCode The source code object
1855 * @param {ASTNode} node An expression node
1856 * @returns {string} The text representing the node, with all surrounding parentheses included
1857 */
1858 getParenthesisedText(sourceCode, node) {
1859 let leftToken = sourceCode.getFirstToken(node);
1860 let rightToken = sourceCode.getLastToken(node);
1861
1862 while (
1863 sourceCode.getTokenBefore(leftToken) &&
1864 sourceCode.getTokenBefore(leftToken).type === "Punctuator" &&
1865 sourceCode.getTokenBefore(leftToken).value === "(" &&
1866 sourceCode.getTokenAfter(rightToken) &&
1867 sourceCode.getTokenAfter(rightToken).type === "Punctuator" &&
1868 sourceCode.getTokenAfter(rightToken).value === ")"
1869 ) {
1870 leftToken = sourceCode.getTokenBefore(leftToken);
1871 rightToken = sourceCode.getTokenAfter(rightToken);
1872 }
1873
1874 return sourceCode.getText().slice(leftToken.range[0], rightToken.range[1]);
1875 },
1876
1877 /**
1878 * Determine if a node has a possibility to be an Error object
1879 * @param {ASTNode} node ASTNode to check
1880 * @returns {boolean} True if there is a chance it contains an Error obj
1881 */
1882 couldBeError(node) {
1883 switch (node.type) {
1884 case "Identifier":
1885 case "CallExpression":
1886 case "NewExpression":
1887 case "MemberExpression":
1888 case "TaggedTemplateExpression":
1889 case "YieldExpression":
1890 case "AwaitExpression":
1891 case "ChainExpression":
1892 return true; // possibly an error object.
1893
1894 case "AssignmentExpression":
1895 if (["=", "&&="].includes(node.operator)) {
1896 return module.exports.couldBeError(node.right);
1897 }
1898
1899 if (["||=", "??="].includes(node.operator)) {
1900 return module.exports.couldBeError(node.left) || module.exports.couldBeError(node.right);
1901 }
1902
1903 /**
1904 * All other assignment operators are mathematical assignment operators (arithmetic or bitwise).
1905 * An assignment expression with a mathematical operator can either evaluate to a primitive value,
1906 * or throw, depending on the operands. Thus, it cannot evaluate to an `Error` object.
1907 */
1908 return false;
1909
1910 case "SequenceExpression": {
1911 const exprs = node.expressions;
1912
1913 return exprs.length !== 0 && module.exports.couldBeError(exprs[exprs.length - 1]);
1914 }
1915
1916 case "LogicalExpression":
1917
1918 /*
1919 * If the && operator short-circuits, the left side was falsy and therefore not an error, and if it
1920 * doesn't short-circuit, it takes the value from the right side, so the right side must always be
1921 * a plausible error. A future improvement could verify that the left side could be truthy by
1922 * excluding falsy literals.
1923 */
1924 if (node.operator === "&&") {
1925 return module.exports.couldBeError(node.right);
1926 }
1927
1928 return module.exports.couldBeError(node.left) || module.exports.couldBeError(node.right);
1929
1930 case "ConditionalExpression":
1931 return module.exports.couldBeError(node.consequent) || module.exports.couldBeError(node.alternate);
1932
1933 default:
1934 return false;
1935 }
1936 },
1937
1938 /**
1939 * Check if a given node is a numeric literal or not.
1940 * @param {ASTNode} node The node to check.
1941 * @returns {boolean} `true` if the node is a number or bigint literal.
1942 */
1943 isNumericLiteral(node) {
1944 return (
1945 node.type === "Literal" &&
1946 (typeof node.value === "number" || Boolean(node.bigint))
1947 );
1948 },
1949
1950 /**
1951 * Determines whether two tokens can safely be placed next to each other without merging into a single token
1952 * @param {Token|string} leftValue The left token. If this is a string, it will be tokenized and the last token will be used.
1953 * @param {Token|string} rightValue The right token. If this is a string, it will be tokenized and the first token will be used.
1954 * @returns {boolean} If the tokens cannot be safely placed next to each other, returns `false`. If the tokens can be placed
1955 * next to each other, behavior is undefined (although it should return `true` in most cases).
1956 */
1957 canTokensBeAdjacent(leftValue, rightValue) {
1958 const espreeOptions = {
1959 ecmaVersion: espree.latestEcmaVersion,
1960 comment: true,
1961 range: true
1962 };
1963
1964 let leftToken;
1965
1966 if (typeof leftValue === "string") {
1967 let tokens;
1968
1969 try {
1970 tokens = espree.tokenize(leftValue, espreeOptions);
1971 } catch {
1972 return false;
1973 }
1974
1975 const comments = tokens.comments;
1976
1977 leftToken = tokens[tokens.length - 1];
1978 if (comments.length) {
1979 const lastComment = comments[comments.length - 1];
1980
1981 if (!leftToken || lastComment.range[0] > leftToken.range[0]) {
1982 leftToken = lastComment;
1983 }
1984 }
1985 } else {
1986 leftToken = leftValue;
1987 }
1988
1989 /*
1990 * If a hashbang comment was passed as a token object from SourceCode,
1991 * its type will be "Shebang" because of the way ESLint itself handles hashbangs.
1992 * If a hashbang comment was passed in a string and then tokenized in this function,
1993 * its type will be "Hashbang" because of the way Espree tokenizes hashbangs.
1994 */
1995 if (leftToken.type === "Shebang" || leftToken.type === "Hashbang") {
1996 return false;
1997 }
1998
1999 let rightToken;
2000
2001 if (typeof rightValue === "string") {
2002 let tokens;
2003
2004 try {
2005 tokens = espree.tokenize(rightValue, espreeOptions);
2006 } catch {
2007 return false;
2008 }
2009
2010 const comments = tokens.comments;
2011
2012 rightToken = tokens[0];
2013 if (comments.length) {
2014 const firstComment = comments[0];
2015
2016 if (!rightToken || firstComment.range[0] < rightToken.range[0]) {
2017 rightToken = firstComment;
2018 }
2019 }
2020 } else {
2021 rightToken = rightValue;
2022 }
2023
2024 if (leftToken.type === "Punctuator" || rightToken.type === "Punctuator") {
2025 if (leftToken.type === "Punctuator" && rightToken.type === "Punctuator") {
2026 const PLUS_TOKENS = new Set(["+", "++"]);
2027 const MINUS_TOKENS = new Set(["-", "--"]);
2028
2029 return !(
2030 PLUS_TOKENS.has(leftToken.value) && PLUS_TOKENS.has(rightToken.value) ||
2031 MINUS_TOKENS.has(leftToken.value) && MINUS_TOKENS.has(rightToken.value)
2032 );
2033 }
2034 if (leftToken.type === "Punctuator" && leftToken.value === "/") {
2035 return !["Block", "Line", "RegularExpression"].includes(rightToken.type);
2036 }
2037 return true;
2038 }
2039
2040 if (
2041 leftToken.type === "String" || rightToken.type === "String" ||
2042 leftToken.type === "Template" || rightToken.type === "Template"
2043 ) {
2044 return true;
2045 }
2046
2047 if (leftToken.type !== "Numeric" && rightToken.type === "Numeric" && rightToken.value.startsWith(".")) {
2048 return true;
2049 }
2050
2051 if (leftToken.type === "Block" || rightToken.type === "Block" || rightToken.type === "Line") {
2052 return true;
2053 }
2054
2055 if (rightToken.type === "PrivateIdentifier") {
2056 return true;
2057 }
2058
2059 return false;
2060 },
2061
2062 /**
2063 * Get the `loc` object of a given name in a `/*globals` directive comment.
2064 * @param {SourceCode} sourceCode The source code to convert index to loc.
2065 * @param {Comment} comment The `/*globals` directive comment which include the name.
2066 * @param {string} name The name to find.
2067 * @returns {SourceLocation} The `loc` object.
2068 */
2069 getNameLocationInGlobalDirectiveComment(sourceCode, comment, name) {
2070 const namePattern = new RegExp(`[\\s,]${escapeRegExp(name)}(?:$|[\\s,:])`, "gu");
2071
2072 // To ignore the first text "global".
2073 namePattern.lastIndex = comment.value.indexOf("global") + 6;
2074
2075 // Search a given variable name.
2076 const match = namePattern.exec(comment.value);
2077
2078 // Convert the index to loc.
2079 const start = sourceCode.getLocFromIndex(
2080 comment.range[0] +
2081 "/*".length +
2082 (match ? match.index + 1 : 0)
2083 );
2084 const end = {
2085 line: start.line,
2086 column: start.column + (match ? name.length : 1)
2087 };
2088
2089 return { start, end };
2090 },
2091
2092 /**
2093 * Determines whether the given raw string contains an octal escape sequence
2094 * or a non-octal decimal escape sequence ("\8", "\9").
2095 *
2096 * "\1", "\2" ... "\7", "\8", "\9"
2097 * "\00", "\01" ... "\07", "\08", "\09"
2098 *
2099 * "\0", when not followed by a digit, is not an octal escape sequence.
2100 * @param {string} rawString A string in its raw representation.
2101 * @returns {boolean} `true` if the string contains at least one octal escape sequence
2102 * or at least one non-octal decimal escape sequence.
2103 */
2104 hasOctalOrNonOctalDecimalEscapeSequence(rawString) {
2105 return OCTAL_OR_NON_OCTAL_DECIMAL_ESCAPE_PATTERN.test(rawString);
2106 },
2107
2108 isReferenceToGlobalVariable,
2109 isLogicalExpression,
2110 isCoalesceExpression,
2111 isMixedLogicalAndCoalesceExpressions,
2112 isNullLiteral,
2113 getStaticStringValue,
2114 getStaticPropertyName,
2115 skipChainExpression,
2116 isSpecificId,
2117 isSpecificMemberAccess,
2118 equalLiteralValue,
2119 isSameReference,
2120 isLogicalAssignmentOperator,
2121 getSwitchCaseColonToken,
2122 getModuleExportName,
2123 isConstant
2124 };