2 * @fileoverview Common utils for AST.
3 * @author Gyandeep Singh
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 const esutils
= require("esutils");
13 const espree
= require("espree");
14 const escapeRegExp
= require("escape-string-regexp");
17 createGlobalLinebreakMatcher
,
20 } = require("../../shared/ast-utils");
22 //------------------------------------------------------------------------------
24 //------------------------------------------------------------------------------
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;
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"]);
38 // A set of node types that can contain a list of statements
39 const STATEMENT_LIST_PARENTS
= new Set(["Program", "BlockStatement", "StaticBlock", "SwitchCase"]);
41 const DECIMAL_INTEGER_PATTERN
= /^(?:0|0[0-7]*[89]\d*|[1-9](?:_?\d)*)$/u;
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;
46 const LOGICAL_ASSIGNMENT_OPERATORS
= new Set(["&&=", "||=", "??="]);
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
56 function isModifyingReference(reference
, index
, references
) {
57 const identifier
= reference
.identifier
;
60 * Destructuring assignments can have multiple default value, so
61 * possibly there are multiple writeable references for the same
64 const modifyingDifferentIdentifier
= index
=== 0 ||
65 references
[index
- 1].identifier
!== identifier
;
68 reference
.init
=== false &&
69 reference
.isWrite() &&
70 modifyingDifferentIdentifier
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.
79 function startsWithUpperCase(s
) {
80 return s
[0] !== s
[0].toLocaleLowerCase();
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.
88 function isES5Constructor(node
) {
89 return (node
.id
&& startsWithUpperCase(node
.id
.name
));
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.
97 function getUpperFunction(node
) {
98 for (let currentNode
= node
; currentNode
; currentNode
= currentNode
.parent
) {
99 if (anyFunctionPattern
.test(currentNode
.type
)) {
107 * Checks whether a given node is a function node or not.
108 * The following types are function nodes:
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.
116 function isFunction(node
) {
117 return Boolean(node
&& anyFunctionPattern
.test(node
.type
));
121 * Checks whether a given node is a loop node or not.
122 * The following types are loop nodes:
129 * @param {ASTNode|null} node A node to check.
130 * @returns {boolean} `true` if the node is a loop node.
132 function isLoop(node
) {
133 return Boolean(node
&& anyLoopPattern
.test(node
.type
));
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.
141 function isInLoop(node
) {
142 for (let currentNode
= node
; currentNode
&& !isFunction(currentNode
); currentNode
= currentNode
.parent
) {
143 if (isLoop(currentNode
)) {
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
156 function isNullLiteral(node
) {
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
165 return node
.type
=== "Literal" && node
.value
=== null && !node
.regex
&& !node
.bigint
;
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`.
174 function isNullOrUndefined(node
) {
176 isNullLiteral(node
) ||
177 (node
.type
=== "Identifier" && node
.name
=== "undefined") ||
178 (node
.type
=== "UnaryExpression" && node
.operator
=== "void")
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.
187 function isCallee(node
) {
188 return node
.parent
.type
=== "CallExpression" && node
.parent
.callee
=== node
;
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.
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`.
200 function getStaticStringValue(node
) {
203 if (node
.value
=== null) {
204 if (isNullLiteral(node
)) {
205 return String(node
.value
); // "null"
208 return `/${node.regex.pattern}/${node.regex.flags}`;
214 // Otherwise, this is an unknown literal. The function will return null.
217 return String(node
.value
);
220 case "TemplateLiteral":
221 if (node
.expressions
.length
=== 0 && node
.quasis
.length
=== 1) {
222 return node
.quasis
[0].value
.cooked
;
233 * Gets the property name of a given node.
234 * The node can be a MemberExpression, a Property, or a MethodDefinition.
236 * If the name is dynamic, this returns `null`.
246 * a["a" + "b"] // => null
247 * a[tag`b`] // => null
248 * a[`${b}`] // => null
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.
262 function getStaticPropertyName(node
) {
265 switch (node
&& node
.type
) {
266 case "ChainExpression":
267 return getStaticPropertyName(node
.expression
);
270 case "PropertyDefinition":
271 case "MethodDefinition":
275 case "MemberExpression":
276 prop
= node
.property
;
283 if (prop
.type
=== "Identifier" && !node
.computed
) {
287 return getStaticStringValue(prop
);
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.
298 function skipChainExpression(node
) {
299 return node
&& node
.type
=== "ChainExpression" ? node
.expression
: node
;
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.
308 function checkText(actual
, expected
) {
309 return typeof expected
=== "string"
310 ? actual
=== expected
311 : expected
.test(actual
);
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.
320 function isSpecificId(node
, name
) {
321 return node
.type
=== "Identifier" && checkText(node
.name
, name
);
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`.
333 function isSpecificMemberAccess(node
, objectName
, propertyName
) {
334 const checkNode
= skipChainExpression(node
);
336 if (checkNode
.type
!== "MemberExpression") {
340 if (objectName
&& !isSpecificId(checkNode
.object
, objectName
)) {
345 const actualPropertyName
= getStaticPropertyName(checkNode
);
347 if (typeof actualPropertyName
!== "string" || !checkText(actualPropertyName
, propertyName
)) {
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.
361 function equalLiteralValue(left
, right
) {
364 if (left
.regex
|| right
.regex
) {
368 left
.regex
.pattern
=== right
.regex
.pattern
&&
369 left
.regex
.flags
=== right
.regex
.flags
374 if (left
.bigint
|| right
.bigint
) {
375 return left
.bigint
=== right
.bigint
;
378 return left
.value
=== right
.value
;
382 * Check if two expressions reference the same value. For example:
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.
392 function isSameReference(left
, right
, disableStaticComputedKey
= false) {
393 if (left
.type
!== right
.type
) {
395 // Handle `a.b` and `a?.b` are samely.
396 if (left
.type
=== "ChainExpression") {
397 return isSameReference(left
.expression
, right
, disableStaticComputedKey
);
399 if (right
.type
=== "ChainExpression") {
400 return isSameReference(left
, right
.expression
, disableStaticComputedKey
);
408 case "ThisExpression":
412 case "PrivateIdentifier":
413 return left
.name
=== right
.name
;
415 return equalLiteralValue(left
, right
);
417 case "ChainExpression":
418 return isSameReference(left
.expression
, right
.expression
, disableStaticComputedKey
);
420 case "MemberExpression": {
421 if (!disableStaticComputedKey
) {
422 const nameA
= getStaticPropertyName(left
);
425 if (nameA
!== null) {
427 isSameReference(left
.object
, right
.object
, disableStaticComputedKey
) &&
428 nameA
=== getStaticPropertyName(right
)
439 left
.computed
=== right
.computed
&&
440 isSameReference(left
.object
, right
.object
, disableStaticComputedKey
) &&
441 isSameReference(left
.property
, right
.property
, disableStaticComputedKey
)
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`.
455 function isReflectApply(node
) {
456 return isSpecificMemberAccess(node
, "Reflect", "apply");
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`.
464 function isArrayFromMethod(node
) {
465 return isSpecificMemberAccess(node
, arrayOrTypedArrayPattern
, "from");
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`.
473 function isMethodWhichHasThisArg(node
) {
474 return isSpecificMemberAccess(node
, null, arrayMethodPattern
);
478 * Creates the negate function of the given function.
479 * @param {Function} f The function to negate.
480 * @returns {Function} Negated function.
483 return token
=> !f(token
);
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.
492 function hasJSDocThisTag(node
, sourceCode
) {
493 const jsdocComment
= sourceCode
.getJSDocComment(node
);
495 if (jsdocComment
&& thisTagPattern
.test(jsdocComment
.value
)) {
499 // Checks `@this` in its leading comments for callbacks,
500 // because callbacks don't have its JSDoc comment.
502 // sinon.test(/* @this sinon.Sandbox */function() { this.spy(); });
503 return sourceCode
.getCommentsBefore(node
).some(comment
=> thisTagPattern
.test(comment
.value
));
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.
513 function isParenthesised(sourceCode
, node
) {
514 const previousToken
= sourceCode
.getTokenBefore(node
),
515 nextToken
= sourceCode
.getTokenAfter(node
);
517 return Boolean(previousToken
&& nextToken
) &&
518 previousToken
.value
=== "(" && previousToken
.range
[1] <= node
.range
[0] &&
519 nextToken
.value
=== ")" && nextToken
.range
[0] >= node
.range
[1];
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.
527 function isEqToken(token
) {
528 return token
.value
=== "=" && token
.type
=== "Punctuator";
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.
536 function isArrowToken(token
) {
537 return token
.value
=== "=>" && token
.type
=== "Punctuator";
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.
545 function isCommaToken(token
) {
546 return token
.value
=== "," && token
.type
=== "Punctuator";
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.
554 function isDotToken(token
) {
555 return token
.value
=== "." && token
.type
=== "Punctuator";
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.
563 function isQuestionDotToken(token
) {
564 return token
.value
=== "?." && token
.type
=== "Punctuator";
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.
572 function isSemicolonToken(token
) {
573 return token
.value
=== ";" && token
.type
=== "Punctuator";
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.
581 function isColonToken(token
) {
582 return token
.value
=== ":" && token
.type
=== "Punctuator";
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.
590 function isOpeningParenToken(token
) {
591 return token
.value
=== "(" && token
.type
=== "Punctuator";
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.
599 function isClosingParenToken(token
) {
600 return token
.value
=== ")" && token
.type
=== "Punctuator";
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.
608 function isOpeningBracketToken(token
) {
609 return token
.value
=== "[" && token
.type
=== "Punctuator";
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.
617 function isClosingBracketToken(token
) {
618 return token
.value
=== "]" && token
.type
=== "Punctuator";
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.
626 function isOpeningBraceToken(token
) {
627 return token
.value
=== "{" && token
.type
=== "Punctuator";
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.
635 function isClosingBraceToken(token
) {
636 return token
.value
=== "}" && token
.type
=== "Punctuator";
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.
644 function isCommentToken(token
) {
645 return token
.type
=== "Line" || token
.type
=== "Block" || token
.type
=== "Shebang";
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.
653 function isKeywordToken(token
) {
654 return token
.type
=== "Keyword";
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.
663 function getOpeningParenOfParams(node
, sourceCode
) {
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
);
670 return isOpeningParenToken(maybeParenToken
) ? maybeParenToken
: argToken
;
673 // Otherwise, returns paren.
675 ? sourceCode
.getTokenAfter(node
.id
, isOpeningParenToken
)
676 : sourceCode
.getFirstToken(node
, isOpeningParenToken
);
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.
686 function equalTokens(left
, right
, sourceCode
) {
687 const tokensL
= sourceCode
.getTokens(left
);
688 const tokensR
= sourceCode
.getTokens(right
);
690 if (tokensL
.length
!== tokensR
.length
) {
693 for (let i
= 0; i
< tokensL
.length
; ++i
) {
694 if (tokensL
[i
].type
!== tokensR
[i
].type
||
695 tokensL
[i
].value
!== tokensR
[i
].value
705 * Check if the given node is a true logical expression or not.
707 * The three binary expressions logical-or (`||`), logical-and (`&&`), and
708 * coalesce (`??`) are known as `ShortCircuitExpression`.
709 * But ESTree represents those by `LogicalExpression` node.
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
716 function isLogicalExpression(node
) {
718 node
.type
=== "LogicalExpression" &&
719 (node
.operator
=== "&&" || node
.operator
=== "||")
724 * Check if the given node is a nullish coalescing expression or not.
726 * The three binary expressions logical-or (`||`), logical-and (`&&`), and
727 * coalesce (`??`) are known as `ShortCircuitExpression`.
728 * But ESTree represents those by `LogicalExpression` node.
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 `??`.
734 function isCoalesceExpression(node
) {
735 return node
.type
=== "LogicalExpression" && node
.operator
=== "??";
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.
744 function isMixedLogicalAndCoalesceExpressions(left
, right
) {
746 (isLogicalExpression(left
) && isCoalesceExpression(right
)) ||
747 (isCoalesceExpression(left
) && isLogicalExpression(right
))
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.
756 function isLogicalAssignmentOperator(operator
) {
757 return LOGICAL_ASSIGNMENT_OPERATORS
.has(operator
);
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.
766 function getSwitchCaseColonToken(node
, sourceCode
) {
768 return sourceCode
.getTokenAfter(node
.test
, isColonToken
);
770 return sourceCode
.getFirstToken(node
, 1);
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.
783 function getModuleExportName(node
) {
784 if (node
.type
=== "Identifier") {
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.
798 function getBooleanValue(node
) {
799 if (node
.value
=== null) {
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
807 if (node
.raw
=== "null") {
811 // regex is always truthy
812 if (typeof node
.regex
=== "object") {
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
828 function isLogicalIdentity(node
, operator
) {
831 return (operator
=== "||" && getBooleanValue(node
) === true) ||
832 (operator
=== "&&" && getBooleanValue(node
) === false);
834 case "UnaryExpression":
835 return (operator
=== "&&" && node
.operator
=== "void");
837 case "LogicalExpression":
840 * handles `a && false || b`
841 * `false` is an identity element of `&&` but not `||`
843 return operator
=== node
.operator
&&
845 isLogicalIdentity(node
.left
, operator
) ||
846 isLogicalIdentity(node
.right
, operator
)
849 case "AssignmentExpression":
850 return ["||=", "&&="].includes(node
.operator
) &&
851 operator
=== node
.operator
.slice(0, -1) &&
852 isLogicalIdentity(node
.right
, operator
);
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.
865 function isReferenceToGlobalVariable(scope
, node
) {
866 const reference
= scope
.references
.find(ref
=> ref
.identifier
=== node
);
870 reference
.resolved
&&
871 reference
.resolved
.scope
.type
=== "global" &&
872 reference
.resolved
.defs
.length
=== 0
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
885 * @returns {boolean} true when node's truthiness is constant
888 function isConstant(scope
, node
, inBooleanPosition
) {
890 // node.elements can return null values in the case of sparse arrays ex. [,]
896 case "ArrowFunctionExpression":
897 case "FunctionExpression":
899 case "ClassExpression":
900 case "ObjectExpression":
903 * In theory objects like:
905 * `{toString: () => a}`
906 * `{valueOf: () => a}`
910 * `class { static toString() { return a } }`
911 * `class { static valueOf() { return a } }`
913 * Are not constant verifiably when `inBooleanPosition` is
914 * false, but it's an edge case we've opted not to handle.
917 case "TemplateLiteral":
918 return (inBooleanPosition
&& node
.quasis
.some(quasi
=> quasi
.value
.cooked
.length
)) ||
919 node
.expressions
.every(exp
=> isConstant(scope
, exp
, false));
921 case "ArrayExpression": {
922 if (!inBooleanPosition
) {
923 return node
.elements
.every(element
=> isConstant(scope
, element
, false));
928 case "UnaryExpression":
930 node
.operator
=== "void" ||
931 node
.operator
=== "typeof" && inBooleanPosition
936 if (node
.operator
=== "!") {
937 return isConstant(scope
, node
.argument
, true);
940 return isConstant(scope
, node
.argument
, false);
942 case "BinaryExpression":
943 return isConstant(scope
, node
.left
, false) &&
944 isConstant(scope
, node
.right
, false) &&
945 node
.operator
!== "in";
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
));
953 return (isLeftConstant
&& isRightConstant
) ||
954 isLeftShortCircuit
||
957 case "NewExpression":
958 return inBooleanPosition
;
959 case "AssignmentExpression":
960 if (node
.operator
=== "=") {
961 return isConstant(scope
, node
.right
, inBooleanPosition
);
964 if (["||=", "&&="].includes(node
.operator
) && inBooleanPosition
) {
965 return isLogicalIdentity(node
.right
, node
.operator
.slice(0, -1));
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
);
982 return node
.name
=== "undefined" && isReferenceToGlobalVariable(scope
, node
);
989 //------------------------------------------------------------------------------
991 //------------------------------------------------------------------------------
994 COMMENTS_IGNORE_PATTERN
,
996 LINEBREAK_MATCHER
: lineBreakPattern
,
997 SHEBANG_MATCHER
: shebangPattern
,
998 STATEMENT_LIST_PARENTS
,
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.
1007 isTokenOnSameLine(left
, right
) {
1008 return left
.loc
.end
.line
=== right
.loc
.start
.line
;
1020 createGlobalLinebreakMatcher
,
1024 isClosingBraceToken
,
1025 isClosingBracketToken
,
1026 isClosingParenToken
,
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
,
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.
1055 isStringLiteral(node
) {
1057 (node
.type
=== "Literal" && typeof node
.value
=== "string") ||
1058 node
.type
=== "TemplateLiteral"
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:
1066 * - DoWhileStatement
1072 * @param {ASTNode} node A node to check.
1073 * @returns {boolean} `true` if the node is breakable.
1075 isBreakableStatement(node
) {
1076 return breakableTypePattern
.test(node
.type
);
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.
1085 getModifyingReferences(references
) {
1086 return references
.filter(isModifyingReference
);
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.
1096 isSurroundedBy(val
, character
) {
1097 return val
[0] === character
&& val
[val
.length
- 1] === character
;
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
1105 isDirectiveComment(node
) {
1106 const comment
= node
.value
.trim();
1109 node
.type
=== "Line" && comment
.startsWith("eslint-") ||
1110 node
.type
=== "Block" && ESLINT_DIRECTIVE_PATTERN
.test(comment
)
1115 * Gets the trailing statement of a given node.
1120 * When taking this `IfStatement`, returns `consequent;` statement.
1121 * @param {ASTNode} A node to get.
1122 * @returns {ASTNode|null} The trailing statement's node.
1124 getTrailingStatement
: esutils
.ast
.trailingStatement
,
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`.
1132 getVariableByName(initScope
, name
) {
1133 let scope
= initScope
;
1136 const variable
= scope
.set.get(name
);
1142 scope
= scope
.upper
;
1149 * Checks whether or not a given function node is the default `this` binding.
1151 * First, this checks the node:
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.
1159 * Next, this checks the location of the node.
1160 * If the location is below, this judges `this` is valid.
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.
1176 isDefaultThisBinding(node
, sourceCode
, { capIsConstructor
= true } = {}) {
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.
1183 if (node
.parent
.type
=== "PropertyDefinition" && node
.parent
.value
=== node
) {
1187 // Class static blocks are implicit functions. In this case, `this` is always not default binding.
1188 if (node
.type
=== "StaticBlock") {
1193 (capIsConstructor
&& isES5Constructor(node
)) ||
1194 hasJSDocThisTag(node
, sourceCode
)
1198 const isAnonymous
= node
.id
=== null;
1199 let currentNode
= node
;
1201 while (currentNode
) {
1202 const parent
= currentNode
.parent
;
1204 switch (parent
.type
) {
1207 * Looks up the destination.
1208 * e.g., obj.foo = nativeFoo || function foo() { ... };
1210 case "LogicalExpression":
1211 case "ConditionalExpression":
1212 case "ChainExpression":
1213 currentNode
= parent
;
1217 * If the upper function is IIFE, checks the destination of the return value.
1219 * obj.foo = (function() {
1221 * return function foo() { ... };
1224 * function foo() { ... }
1227 case "ReturnStatement": {
1228 const func
= getUpperFunction(parent
);
1230 if (func
=== null || !isCallee(func
)) {
1233 currentNode
= func
.parent
;
1236 case "ArrowFunctionExpression":
1237 if (currentNode
!== parent
.body
|| !isCallee(parent
)) {
1240 currentNode
= parent
.parent
;
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() { ... } }
1255 case "PropertyDefinition":
1256 case "MethodDefinition":
1257 return parent
.value
!== currentNode
;
1261 * obj.foo = function foo() { ... };
1262 * Foo = function() { ... };
1263 * [obj.foo = function foo() { ... }] = a;
1264 * [Foo = function() { ... }] = a;
1266 case "AssignmentExpression":
1267 case "AssignmentPattern":
1268 if (parent
.left
.type
=== "MemberExpression") {
1274 parent
.left
.type
=== "Identifier" &&
1275 startsWithUpperCase(parent
.left
.name
)
1283 * var Foo = function() { ... };
1285 case "VariableDeclarator":
1289 parent
.init
=== currentNode
&&
1290 parent
.id
.type
=== "Identifier" &&
1291 startsWithUpperCase(parent
.id
.name
)
1296 * var foo = function foo() { ... }.bind(obj);
1297 * (function foo() { ... }).call(obj);
1298 * (function foo() { ... }).apply(obj, []);
1300 case "MemberExpression":
1302 parent
.object
=== currentNode
&&
1303 isSpecificMemberAccess(parent
, null, bindOrCallOrApplyPattern
)
1305 const maybeCalleeNode
= parent
.parent
.type
=== "ChainExpression"
1310 isCallee(maybeCalleeNode
) &&
1311 maybeCalleeNode
.parent
.arguments
.length
>= 1 &&
1312 !isNullOrUndefined(maybeCalleeNode
.parent
.arguments
[0])
1319 * Reflect.apply(function() {}, obj, []);
1320 * Array.from([], function() {}, obj);
1321 * list.forEach(function() {}, obj);
1323 case "CallExpression":
1324 if (isReflectApply(parent
.callee
)) {
1326 parent
.arguments
.length
!== 3 ||
1327 parent
.arguments
[0] !== currentNode
||
1328 isNullOrUndefined(parent
.arguments
[1])
1331 if (isArrayFromMethod(parent
.callee
)) {
1333 parent
.arguments
.length
!== 3 ||
1334 parent
.arguments
[1] !== currentNode
||
1335 isNullOrUndefined(parent
.arguments
[2])
1338 if (isMethodWhichHasThisArg(parent
.callee
)) {
1340 parent
.arguments
.length
!== 2 ||
1341 parent
.arguments
[0] !== currentNode
||
1342 isNullOrUndefined(parent
.arguments
[1])
1347 // Otherwise `this` is default.
1353 /* c8 ignore next */
1358 * Get the precedence level based on the node type
1359 * @param {ASTNode} node node to evaluate
1360 * @returns {int} precedence level
1363 getPrecedence(node
) {
1364 switch (node
.type
) {
1365 case "SequenceExpression":
1368 case "AssignmentExpression":
1369 case "ArrowFunctionExpression":
1370 case "YieldExpression":
1373 case "ConditionalExpression":
1376 case "LogicalExpression":
1377 switch (node
.operator
) {
1389 case "BinaryExpression":
1391 switch (node
.operator
) {
1429 case "UnaryExpression":
1430 case "AwaitExpression":
1433 case "UpdateExpression":
1436 case "CallExpression":
1437 case "ChainExpression":
1438 case "ImportExpression":
1441 case "NewExpression":
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.
1454 isEmptyBlock(node
) {
1455 return Boolean(node
&& node
.type
=== "BlockStatement" && node
.body
.length
=== 0);
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.
1463 isEmptyFunction(node
) {
1464 return isFunction(node
) && module
.exports
.isEmptyBlock(node
.body
);
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.
1472 getDirectivePrologue(node
) {
1473 const directives
= [];
1475 // Directive prologues only occur at the top of files or functions.
1477 node
.type
=== "Program" ||
1478 node
.type
=== "FunctionDeclaration" ||
1479 node
.type
=== "FunctionExpression" ||
1482 * Do not check arrow functions with implicit return.
1483 * `() => "use strict";` returns the string `"use strict"`.
1485 (node
.type
=== "ArrowFunctionExpression" && node
.body
.type
=== "BlockStatement")
1487 const statements
= node
.type
=== "Program" ? node
.body
: node
.body
.body
;
1489 for (const statement
of statements
) {
1491 statement
.type
=== "ExpressionStatement" &&
1492 statement
.expression
.type
=== "Literal"
1494 directives
.push(statement
);
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.
1535 isDecimalInteger(node
) {
1536 return node
.type
=== "Literal" && typeof node
.value
=== "number" &&
1537 DECIMAL_INTEGER_PATTERN
.test(node
.raw
);
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.
1546 isDecimalIntegerNumericToken(token
) {
1547 return token
.type
=== "Numeric" && DECIMAL_INTEGER_PATTERN
.test(token
.value
);
1551 * Gets the name and kind of the given function node.
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.
1608 getFunctionNameWithKind(node
) {
1609 const parent
= node
.parent
;
1612 if (parent
.type
=== "MethodDefinition" || parent
.type
=== "PropertyDefinition") {
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");
1618 if (!parent
.computed
&& parent
.key
.type
=== "PrivateIdentifier") {
1619 tokens
.push("private");
1623 tokens
.push("async");
1625 if (node
.generator
) {
1626 tokens
.push("generator");
1629 if (parent
.type
=== "Property" || parent
.type
=== "MethodDefinition") {
1630 if (parent
.kind
=== "constructor") {
1631 return "constructor";
1633 if (parent
.kind
=== "get") {
1634 tokens
.push("getter");
1635 } else if (parent
.kind
=== "set") {
1636 tokens
.push("setter");
1638 tokens
.push("method");
1640 } else if (parent
.type
=== "PropertyDefinition") {
1641 tokens
.push("method");
1643 if (node
.type
=== "ArrowFunctionExpression") {
1644 tokens
.push("arrow");
1646 tokens
.push("function");
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}`);
1653 const name
= getStaticPropertyName(parent
);
1655 if (name
!== null) {
1656 tokens
.push(`'${name}'`);
1657 } else if (node
.id
) {
1658 tokens
.push(`'${node.id.name}'`);
1661 } else if (node
.id
) {
1662 tokens
.push(`'${node.id.name}'`);
1665 return tokens
.join(" ");
1669 * Gets the location of the given function node for reporting.
1671 * - `function foo() {}`
1673 * - `(function foo() {})`
1675 * - `(function() {})`
1677 * - `function* foo() {}`
1679 * - `(function* foo() {})`
1681 * - `(function*() {})`
1685 * - `async () => {}`
1687 * - `({ foo: function foo() {} })`
1689 * - `({ foo: function() {} })`
1691 * - `({ ['foo']: function() {} })`
1693 * - `({ [foo]: function() {} })`
1695 * - `({ foo() {} })`
1697 * - `({ foo: function* foo() {} })`
1698 * ^^^^^^^^^^^^^^^^^^
1699 * - `({ foo: function*() {} })`
1701 * - `({ ['foo']: function*() {} })`
1702 * ^^^^^^^^^^^^^^^^^^
1703 * - `({ [foo]: function*() {} })`
1705 * - `({ *foo() {} })`
1707 * - `({ foo: async function foo() {} })`
1708 * ^^^^^^^^^^^^^^^^^^^^^^^
1709 * - `({ foo: async function() {} })`
1710 * ^^^^^^^^^^^^^^^^^^^
1711 * - `({ ['foo']: async function() {} })`
1712 * ^^^^^^^^^^^^^^^^^^^^^^^
1713 * - `({ [foo]: async function() {} })`
1714 * ^^^^^^^^^^^^^^^^^^^^^
1715 * - `({ async foo() {} })`
1717 * - `({ get foo() {} })`
1719 * - `({ set foo(a) {} })`
1721 * - `class A { constructor() {} }`
1723 * - `class A { foo() {} }`
1725 * - `class A { *foo() {} }`
1727 * - `class A { async foo() {} }`
1729 * - `class A { ['foo']() {} }`
1731 * - `class A { *['foo']() {} }`
1733 * - `class A { async ['foo']() {} }`
1735 * - `class A { [foo]() {} }`
1737 * - `class A { *[foo]() {} }`
1739 * - `class A { async [foo]() {} }`
1741 * - `class A { get foo() {} }`
1743 * - `class A { set foo(a) {} }`
1745 * - `class A { static foo() {} }`
1747 * - `class A { static *foo() {} }`
1749 * - `class A { static async foo() {} }`
1751 * - `class A { static get foo() {} }`
1753 * - `class A { static set foo(a) {} }`
1755 * - `class A { foo = function() {} }`
1757 * - `class A { static foo = function() {} }`
1758 * ^^^^^^^^^^^^^^^^^^^^^
1759 * - `class A { foo = (a, b) => {} }`
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.
1765 getFunctionHeadLoc(node
, sourceCode
) {
1766 const parent
= node
.parent
;
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
);
1776 start
= arrowToken
.loc
.start
;
1777 end
= arrowToken
.loc
.end
;
1779 start
= node
.loc
.start
;
1780 end
= getOpeningParenOfParams(node
, sourceCode
).loc
.start
;
1784 start
: Object
.assign({}, start
),
1785 end
: Object
.assign({}, end
)
1790 * Gets next location when the result is not out of bound, otherwise returns null.
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.
1801 * Examples of successive locations (line, column):
1804 * locations: (1, 0) -> (1, 1) -> (1, 2) -> (1, 3) -> null
1807 * locations: (1, 0) -> (1, 1) -> (1, 2) -> (1, 3) -> (2, 0) -> null
1810 * locations: (1, 0) -> (1, 1) -> (1, 2) -> (1, 3) -> (2, 0) -> null
1813 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (2, 1) -> null
1816 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (2, 1) -> (3, 0) -> null
1818 * code: a<CR><LF>b<CR><LF>
1819 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (2, 1) -> (3, 0) -> null
1822 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (3, 0) -> null
1825 * locations: (1, 0) -> (2, 0) -> null
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
1833 getNextLocation(sourceCode
, { line
, column
}) {
1834 if (column
< sourceCode
.lines
[line
- 1].length
) {
1841 if (line
< sourceCode
.lines
.length
) {
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
1858 getParenthesisedText(sourceCode
, node
) {
1859 let leftToken
= sourceCode
.getFirstToken(node
);
1860 let rightToken
= sourceCode
.getLastToken(node
);
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
=== ")"
1870 leftToken
= sourceCode
.getTokenBefore(leftToken
);
1871 rightToken
= sourceCode
.getTokenAfter(rightToken
);
1874 return sourceCode
.getText().slice(leftToken
.range
[0], rightToken
.range
[1]);
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
1882 couldBeError(node
) {
1883 switch (node
.type
) {
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.
1894 case "AssignmentExpression":
1895 if (["=", "&&="].includes(node
.operator
)) {
1896 return module
.exports
.couldBeError(node
.right
);
1899 if (["||=", "??="].includes(node
.operator
)) {
1900 return module
.exports
.couldBeError(node
.left
) || module
.exports
.couldBeError(node
.right
);
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.
1910 case "SequenceExpression": {
1911 const exprs
= node
.expressions
;
1913 return exprs
.length
!== 0 && module
.exports
.couldBeError(exprs
[exprs
.length
- 1]);
1916 case "LogicalExpression":
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.
1924 if (node
.operator
=== "&&") {
1925 return module
.exports
.couldBeError(node
.right
);
1928 return module
.exports
.couldBeError(node
.left
) || module
.exports
.couldBeError(node
.right
);
1930 case "ConditionalExpression":
1931 return module
.exports
.couldBeError(node
.consequent
) || module
.exports
.couldBeError(node
.alternate
);
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.
1943 isNumericLiteral(node
) {
1945 node
.type
=== "Literal" &&
1946 (typeof node
.value
=== "number" || Boolean(node
.bigint
))
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).
1957 canTokensBeAdjacent(leftValue
, rightValue
) {
1958 const espreeOptions
= {
1959 ecmaVersion
: espree
.latestEcmaVersion
,
1966 if (typeof leftValue
=== "string") {
1970 tokens
= espree
.tokenize(leftValue
, espreeOptions
);
1975 const comments
= tokens
.comments
;
1977 leftToken
= tokens
[tokens
.length
- 1];
1978 if (comments
.length
) {
1979 const lastComment
= comments
[comments
.length
- 1];
1981 if (!leftToken
|| lastComment
.range
[0] > leftToken
.range
[0]) {
1982 leftToken
= lastComment
;
1986 leftToken
= leftValue
;
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.
1995 if (leftToken
.type
=== "Shebang" || leftToken
.type
=== "Hashbang") {
2001 if (typeof rightValue
=== "string") {
2005 tokens
= espree
.tokenize(rightValue
, espreeOptions
);
2010 const comments
= tokens
.comments
;
2012 rightToken
= tokens
[0];
2013 if (comments
.length
) {
2014 const firstComment
= comments
[0];
2016 if (!rightToken
|| firstComment
.range
[0] < rightToken
.range
[0]) {
2017 rightToken
= firstComment
;
2021 rightToken
= rightValue
;
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(["-", "--"]);
2030 PLUS_TOKENS
.has(leftToken
.value
) && PLUS_TOKENS
.has(rightToken
.value
) ||
2031 MINUS_TOKENS
.has(leftToken
.value
) && MINUS_TOKENS
.has(rightToken
.value
)
2034 if (leftToken
.type
=== "Punctuator" && leftToken
.value
=== "/") {
2035 return !["Block", "Line", "RegularExpression"].includes(rightToken
.type
);
2041 leftToken
.type
=== "String" || rightToken
.type
=== "String" ||
2042 leftToken
.type
=== "Template" || rightToken
.type
=== "Template"
2047 if (leftToken
.type
!== "Numeric" && rightToken
.type
=== "Numeric" && rightToken
.value
.startsWith(".")) {
2051 if (leftToken
.type
=== "Block" || rightToken
.type
=== "Block" || rightToken
.type
=== "Line") {
2055 if (rightToken
.type
=== "PrivateIdentifier") {
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.
2069 getNameLocationInGlobalDirectiveComment(sourceCode
, comment
, name
) {
2070 const namePattern
= new RegExp(`[\\s,]${escapeRegExp(name)}(?:$|[\\s,:])`, "gu");
2072 // To ignore the first text "global".
2073 namePattern
.lastIndex
= comment
.value
.indexOf("global") + 6;
2075 // Search a given variable name.
2076 const match
= namePattern
.exec(comment
.value
);
2078 // Convert the index to loc.
2079 const start
= sourceCode
.getLocFromIndex(
2082 (match
? match
.index
+ 1 : 0)
2086 column
: start
.column
+ (match
? name
.length
: 1)
2089 return { start
, end
};
2093 * Determines whether the given raw string contains an octal escape sequence
2094 * or a non-octal decimal escape sequence ("\8", "\9").
2096 * "\1", "\2" ... "\7", "\8", "\9"
2097 * "\00", "\01" ... "\07", "\08", "\09"
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.
2104 hasOctalOrNonOctalDecimalEscapeSequence(rawString
) {
2105 return OCTAL_OR_NON_OCTAL_DECIMAL_ESCAPE_PATTERN
.test(rawString
);
2108 isReferenceToGlobalVariable
,
2109 isLogicalExpression
,
2110 isCoalesceExpression
,
2111 isMixedLogicalAndCoalesceExpressions
,
2113 getStaticStringValue
,
2114 getStaticPropertyName
,
2115 skipChainExpression
,
2117 isSpecificMemberAccess
,
2120 isLogicalAssignmentOperator
,
2121 getSwitchCaseColonToken
,
2122 getModuleExportName
,