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 LINEBREAKS = new Set(["\r\n", "\r", "\n", "\u2028", "\u2029"]);
37 // A set of node types that can contain a list of statements
38 const STATEMENT_LIST_PARENTS = new Set(["Program", "BlockStatement", "SwitchCase"]);
40 const DECIMAL_INTEGER_PATTERN = /^(?:0|0[0-7]*[89]\d*|[1-9](?:_?\d)*)$/u;
42 // Tests the presence of at least one LegacyOctalEscapeSequence or NonOctalDecimalEscapeSequence in a raw string
43 const OCTAL_OR_NON_OCTAL_DECIMAL_ESCAPE_PATTERN = /^(?:[^\\]|\\.)*\\(?:[1-9]|0[0-9])/su;
45 const LOGICAL_ASSIGNMENT_OPERATORS = new Set(["&&=", "||=", "??="]);
48 * Checks reference if is non initializer and writable.
49 * @param {Reference} reference A reference to check.
50 * @param {int} index The index of the reference in the references.
51 * @param {Reference[]} references The array that the reference belongs to.
52 * @returns {boolean} Success/Failure
55 function isModifyingReference(reference, index, references) {
56 const identifier = reference.identifier;
59 * Destructuring assignments can have multiple default value, so
60 * possibly there are multiple writeable references for the same
63 const modifyingDifferentIdentifier = index === 0 ||
64 references[index - 1].identifier !== identifier;
67 reference.init === false &&
68 reference.isWrite() &&
69 modifyingDifferentIdentifier
74 * Checks whether the given string starts with uppercase or not.
75 * @param {string} s The string to check.
76 * @returns {boolean} `true` if the string starts with uppercase.
78 function startsWithUpperCase(s) {
79 return s[0] !== s[0].toLocaleLowerCase();
83 * Checks whether or not a node is a constructor.
84 * @param {ASTNode} node A function node to check.
85 * @returns {boolean} Whether or not a node is a constructor.
87 function isES5Constructor(node) {
88 return (node.id && startsWithUpperCase(node.id.name));
92 * Finds a function node from ancestors of a node.
93 * @param {ASTNode} node A start node to find.
94 * @returns {Node|null} A found function node.
96 function getUpperFunction(node) {
97 for (let currentNode = node; currentNode; currentNode = currentNode.parent) {
98 if (anyFunctionPattern.test(currentNode.type)) {
106 * Checks whether a given node is a function node or not.
107 * The following types are function nodes:
109 * - ArrowFunctionExpression
110 * - FunctionDeclaration
111 * - FunctionExpression
112 * @param {ASTNode|null} node A node to check.
113 * @returns {boolean} `true` if the node is a function node.
115 function isFunction(node) {
116 return Boolean(node && anyFunctionPattern.test(node.type));
120 * Checks whether a given node is a loop node or not.
121 * The following types are loop nodes:
128 * @param {ASTNode|null} node A node to check.
129 * @returns {boolean} `true` if the node is a loop node.
131 function isLoop(node) {
132 return Boolean(node && anyLoopPattern.test(node.type));
136 * Checks whether the given node is in a loop or not.
137 * @param {ASTNode} node The node to check.
138 * @returns {boolean} `true` if the node is in a loop.
140 function isInLoop(node) {
141 for (let currentNode = node; currentNode && !isFunction(currentNode); currentNode = currentNode.parent) {
142 if (isLoop(currentNode)) {
151 * Determines whether the given node is a `null` literal.
152 * @param {ASTNode} node The node to check
153 * @returns {boolean} `true` if the node is a `null` literal
155 function isNullLiteral(node) {
158 * Checking `node.value === null` does not guarantee that a literal is a null literal.
159 * When parsing values that cannot be represented in the current environment (e.g. unicode
160 * regexes in Node 4), `node.value` is set to `null` because it wouldn't be possible to
161 * set `node.value` to a unicode regex. To make sure a literal is actually `null`, check
162 * `node.regex` instead. Also see: https://github.com/eslint/eslint/issues/8020
164 return node.type === "Literal" && node.value === null && !node.regex && !node.bigint;
168 * Checks whether or not a node is `null` or `undefined`.
169 * @param {ASTNode} node A node to check.
170 * @returns {boolean} Whether or not the node is a `null` or `undefined`.
173 function isNullOrUndefined(node) {
175 isNullLiteral(node) ||
176 (node.type === "Identifier" && node.name === "undefined") ||
177 (node.type === "UnaryExpression" && node.operator === "void")
182 * Checks whether or not a node is callee.
183 * @param {ASTNode} node A node to check.
184 * @returns {boolean} Whether or not the node is callee.
186 function isCallee(node) {
187 return node.parent.type === "CallExpression" && node.parent.callee === node;
191 * Returns the result of the string conversion applied to the evaluated value of the given expression node,
192 * if it can be determined statically.
194 * This function returns a `string` value for all `Literal` nodes and simple `TemplateLiteral` nodes only.
195 * In all other cases, this function returns `null`.
196 * @param {ASTNode} node Expression node.
197 * @returns {string|null} String value if it can be determined. Otherwise, `null`.
199 function getStaticStringValue(node) {
202 if (node.value === null) {
203 if (isNullLiteral(node)) {
204 return String(node.value); // "null"
207 return `/${node.regex.pattern}/${node.regex.flags}`;
213 // Otherwise, this is an unknown literal. The function will return null.
216 return String(node.value);
219 case "TemplateLiteral":
220 if (node.expressions.length === 0 && node.quasis.length === 1) {
221 return node.quasis[0].value.cooked;
232 * Gets the property name of a given node.
233 * The node can be a MemberExpression, a Property, or a MethodDefinition.
235 * If the name is dynamic, this returns `null`.
245 * a["a" + "b"] // => null
246 * a[tag`b`] // => null
247 * a[`${b}`] // => null
249 * let a = {b: 1} // => "b"
250 * let a = {["b"]: 1} // => "b"
251 * let a = {['b']: 1} // => "b"
252 * let a = {[`b`]: 1} // => "b"
253 * let a = {[100]: 1} // => "100"
254 * let a = {[b]: 1} // => null
255 * let a = {["a" + "b"]: 1} // => null
256 * let a = {[tag`b`]: 1} // => null
257 * let a = {[`${b}`]: 1} // => null
258 * @param {ASTNode} node The node to get.
259 * @returns {string|null} The property name if static. Otherwise, null.
261 function getStaticPropertyName(node) {
264 switch (node && node.type) {
265 case "ChainExpression":
266 return getStaticPropertyName(node.expression);
269 case "MethodDefinition":
273 case "MemberExpression":
274 prop = node.property;
281 if (prop.type === "Identifier" && !node.computed) {
285 return getStaticStringValue(prop);
292 * Retrieve `ChainExpression#expression` value if the given node a `ChainExpression` node. Otherwise, pass through it.
293 * @param {ASTNode} node The node to address.
294 * @returns {ASTNode} The `ChainExpression#expression` value if the node is a `ChainExpression` node. Otherwise, the node.
296 function skipChainExpression(node) {
297 return node && node.type === "ChainExpression" ? node.expression : node;
301 * Check if the `actual` is an expected value.
302 * @param {string} actual The string value to check.
303 * @param {string | RegExp} expected The expected string value or pattern.
304 * @returns {boolean} `true` if the `actual` is an expected value.
306 function checkText(actual, expected) {
307 return typeof expected === "string"
308 ? actual === expected
309 : expected.test(actual);
313 * Check if a given node is an Identifier node with a given name.
314 * @param {ASTNode} node The node to check.
315 * @param {string | RegExp} name The expected name or the expected pattern of the object name.
316 * @returns {boolean} `true` if the node is an Identifier node with the name.
318 function isSpecificId(node, name) {
319 return node.type === "Identifier" && checkText(node.name, name);
323 * Check if a given node is member access with a given object name and property name pair.
324 * This is regardless of optional or not.
325 * @param {ASTNode} node The node to check.
326 * @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.
327 * @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.
328 * @returns {boolean} `true` if the node is member access with the object name and property name pair.
329 * The node is a `MemberExpression` or `ChainExpression`.
331 function isSpecificMemberAccess(node, objectName, propertyName) {
332 const checkNode = skipChainExpression(node);
334 if (checkNode.type !== "MemberExpression") {
338 if (objectName && !isSpecificId(checkNode.object, objectName)) {
343 const actualPropertyName = getStaticPropertyName(checkNode);
345 if (typeof actualPropertyName !== "string" || !checkText(actualPropertyName, propertyName)) {
354 * Check if two literal nodes are the same value.
355 * @param {ASTNode} left The Literal node to compare.
356 * @param {ASTNode} right The other Literal node to compare.
357 * @returns {boolean} `true` if the two literal nodes are the same value.
359 function equalLiteralValue(left, right) {
362 if (left.regex || right.regex) {
366 left.regex.pattern === right.regex.pattern &&
367 left.regex.flags === right.regex.flags
372 if (left.bigint || right.bigint) {
373 return left.bigint === right.bigint;
376 return left.value === right.value;
380 * Check if two expressions reference the same value. For example:
385 * @param {ASTNode} left The left side of the comparison.
386 * @param {ASTNode} right The right side of the comparison.
387 * @param {boolean} [disableStaticComputedKey] Don't address `a.b` and `a["b"]` are the same if `true`. For backward compatibility.
388 * @returns {boolean} `true` if both sides match and reference the same value.
390 function isSameReference(left, right, disableStaticComputedKey = false) {
391 if (left.type !== right.type) {
393 // Handle `a.b` and `a?.b` are samely.
394 if (left.type === "ChainExpression") {
395 return isSameReference(left.expression, right, disableStaticComputedKey);
397 if (right.type === "ChainExpression") {
398 return isSameReference(left, right.expression, disableStaticComputedKey);
406 case "ThisExpression":
410 return left.name === right.name;
412 return equalLiteralValue(left, right);
414 case "ChainExpression":
415 return isSameReference(left.expression, right.expression, disableStaticComputedKey);
417 case "MemberExpression": {
418 if (!disableStaticComputedKey) {
419 const nameA = getStaticPropertyName(left);
422 if (nameA !== null) {
424 isSameReference(left.object, right.object, disableStaticComputedKey) &&
425 nameA === getStaticPropertyName(right)
436 left.computed === right.computed &&
437 isSameReference(left.object, right.object, disableStaticComputedKey) &&
438 isSameReference(left.property, right.property, disableStaticComputedKey)
448 * Checks whether or not a node is `Reflect.apply`.
449 * @param {ASTNode} node A node to check.
450 * @returns {boolean} Whether or not the node is a `Reflect.apply`.
452 function isReflectApply(node) {
453 return isSpecificMemberAccess(node, "Reflect", "apply");
457 * Checks whether or not a node is `Array.from`.
458 * @param {ASTNode} node A node to check.
459 * @returns {boolean} Whether or not the node is a `Array.from`.
461 function isArrayFromMethod(node) {
462 return isSpecificMemberAccess(node, arrayOrTypedArrayPattern, "from");
466 * Checks whether or not a node is a method which has `thisArg`.
467 * @param {ASTNode} node A node to check.
468 * @returns {boolean} Whether or not the node is a method which has `thisArg`.
470 function isMethodWhichHasThisArg(node) {
471 return isSpecificMemberAccess(node, null, arrayMethodPattern);
475 * Creates the negate function of the given function.
476 * @param {Function} f The function to negate.
477 * @returns {Function} Negated function.
480 return token => !f(token);
484 * Checks whether or not a node has a `@this` tag in its comments.
485 * @param {ASTNode} node A node to check.
486 * @param {SourceCode} sourceCode A SourceCode instance to get comments.
487 * @returns {boolean} Whether or not the node has a `@this` tag in its comments.
489 function hasJSDocThisTag(node, sourceCode) {
490 const jsdocComment = sourceCode.getJSDocComment(node);
492 if (jsdocComment && thisTagPattern.test(jsdocComment.value)) {
496 // Checks `@this` in its leading comments for callbacks,
497 // because callbacks don't have its JSDoc comment.
499 // sinon.test(/* @this sinon.Sandbox */function() { this.spy(); });
500 return sourceCode.getCommentsBefore(node).some(comment => thisTagPattern.test(comment.value));
504 * Determines if a node is surrounded by parentheses.
505 * @param {SourceCode} sourceCode The ESLint source code object
506 * @param {ASTNode} node The node to be checked.
507 * @returns {boolean} True if the node is parenthesised.
510 function isParenthesised(sourceCode, node) {
511 const previousToken = sourceCode.getTokenBefore(node),
512 nextToken = sourceCode.getTokenAfter(node);
514 return Boolean(previousToken && nextToken) &&
515 previousToken.value === "(" && previousToken.range[1] <= node.range[0] &&
516 nextToken.value === ")" && nextToken.range[0] >= node.range[1];
520 * Checks if the given token is an arrow token or not.
521 * @param {Token} token The token to check.
522 * @returns {boolean} `true` if the token is an arrow token.
524 function isArrowToken(token) {
525 return token.value === "=>" && token.type === "Punctuator";
529 * Checks if the given token is a comma token or not.
530 * @param {Token} token The token to check.
531 * @returns {boolean} `true` if the token is a comma token.
533 function isCommaToken(token) {
534 return token.value === "," && token.type === "Punctuator";
538 * Checks if the given token is a dot token or not.
539 * @param {Token} token The token to check.
540 * @returns {boolean} `true` if the token is a dot token.
542 function isDotToken(token) {
543 return token.value === "." && token.type === "Punctuator";
547 * Checks if the given token is a `?.` token or not.
548 * @param {Token} token The token to check.
549 * @returns {boolean} `true` if the token is a `?.` token.
551 function isQuestionDotToken(token) {
552 return token.value === "?." && token.type === "Punctuator";
556 * Checks if the given token is a semicolon token or not.
557 * @param {Token} token The token to check.
558 * @returns {boolean} `true` if the token is a semicolon token.
560 function isSemicolonToken(token) {
561 return token.value === ";" && token.type === "Punctuator";
565 * Checks if the given token is a colon token or not.
566 * @param {Token} token The token to check.
567 * @returns {boolean} `true` if the token is a colon token.
569 function isColonToken(token) {
570 return token.value === ":" && token.type === "Punctuator";
574 * Checks if the given token is an opening parenthesis token or not.
575 * @param {Token} token The token to check.
576 * @returns {boolean} `true` if the token is an opening parenthesis token.
578 function isOpeningParenToken(token) {
579 return token.value === "(" && token.type === "Punctuator";
583 * Checks if the given token is a closing parenthesis token or not.
584 * @param {Token} token The token to check.
585 * @returns {boolean} `true` if the token is a closing parenthesis token.
587 function isClosingParenToken(token) {
588 return token.value === ")" && token.type === "Punctuator";
592 * Checks if the given token is an opening square bracket token or not.
593 * @param {Token} token The token to check.
594 * @returns {boolean} `true` if the token is an opening square bracket token.
596 function isOpeningBracketToken(token) {
597 return token.value === "[" && token.type === "Punctuator";
601 * Checks if the given token is a closing square bracket token or not.
602 * @param {Token} token The token to check.
603 * @returns {boolean} `true` if the token is a closing square bracket token.
605 function isClosingBracketToken(token) {
606 return token.value === "]" && token.type === "Punctuator";
610 * Checks if the given token is an opening brace token or not.
611 * @param {Token} token The token to check.
612 * @returns {boolean} `true` if the token is an opening brace token.
614 function isOpeningBraceToken(token) {
615 return token.value === "{" && token.type === "Punctuator";
619 * Checks if the given token is a closing brace token or not.
620 * @param {Token} token The token to check.
621 * @returns {boolean} `true` if the token is a closing brace token.
623 function isClosingBraceToken(token) {
624 return token.value === "}" && token.type === "Punctuator";
628 * Checks if the given token is a comment token or not.
629 * @param {Token} token The token to check.
630 * @returns {boolean} `true` if the token is a comment token.
632 function isCommentToken(token) {
633 return token.type === "Line" || token.type === "Block" || token.type === "Shebang";
637 * Checks if the given token is a keyword token or not.
638 * @param {Token} token The token to check.
639 * @returns {boolean} `true` if the token is a keyword token.
641 function isKeywordToken(token) {
642 return token.type === "Keyword";
646 * Gets the `(` token of the given function node.
647 * @param {ASTNode} node The function node to get.
648 * @param {SourceCode} sourceCode The source code object to get tokens.
649 * @returns {Token} `(` token.
651 function getOpeningParenOfParams(node, sourceCode) {
653 ? sourceCode.getTokenAfter(node.id, isOpeningParenToken)
654 : sourceCode.getFirstToken(node, isOpeningParenToken);
658 * Checks whether or not the tokens of two given nodes are same.
659 * @param {ASTNode} left A node 1 to compare.
660 * @param {ASTNode} right A node 2 to compare.
661 * @param {SourceCode} sourceCode The ESLint source code object.
662 * @returns {boolean} the source code for the given node.
664 function equalTokens(left, right, sourceCode) {
665 const tokensL = sourceCode.getTokens(left);
666 const tokensR = sourceCode.getTokens(right);
668 if (tokensL.length !== tokensR.length) {
671 for (let i = 0; i < tokensL.length; ++i) {
672 if (tokensL[i].type !== tokensR[i].type ||
673 tokensL[i].value !== tokensR[i].value
683 * Check if the given node is a true logical expression or not.
685 * The three binary expressions logical-or (`||`), logical-and (`&&`), and
686 * coalesce (`??`) are known as `ShortCircuitExpression`.
687 * But ESTree represents those by `LogicalExpression` node.
689 * This function rejects coalesce expressions of `LogicalExpression` node.
690 * @param {ASTNode} node The node to check.
691 * @returns {boolean} `true` if the node is `&&` or `||`.
692 * @see https://tc39.es/ecma262/#prod-ShortCircuitExpression
694 function isLogicalExpression(node) {
696 node.type === "LogicalExpression" &&
697 (node.operator === "&&" || node.operator === "||")
702 * Check if the given node is a nullish coalescing expression or not.
704 * The three binary expressions logical-or (`||`), logical-and (`&&`), and
705 * coalesce (`??`) are known as `ShortCircuitExpression`.
706 * But ESTree represents those by `LogicalExpression` node.
708 * This function finds only coalesce expressions of `LogicalExpression` node.
709 * @param {ASTNode} node The node to check.
710 * @returns {boolean} `true` if the node is `??`.
712 function isCoalesceExpression(node) {
713 return node.type === "LogicalExpression" && node.operator === "??";
717 * Check if given two nodes are the pair of a logical expression and a coalesce expression.
718 * @param {ASTNode} left A node to check.
719 * @param {ASTNode} right Another node to check.
720 * @returns {boolean} `true` if the two nodes are the pair of a logical expression and a coalesce expression.
722 function isMixedLogicalAndCoalesceExpressions(left, right) {
724 (isLogicalExpression(left) && isCoalesceExpression(right)) ||
725 (isCoalesceExpression(left) && isLogicalExpression(right))
730 * Checks if the given operator is a logical assignment operator.
731 * @param {string} operator The operator to check.
732 * @returns {boolean} `true` if the operator is a logical assignment operator.
734 function isLogicalAssignmentOperator(operator) {
735 return LOGICAL_ASSIGNMENT_OPERATORS.has(operator);
738 //------------------------------------------------------------------------------
740 //------------------------------------------------------------------------------
743 COMMENTS_IGNORE_PATTERN,
745 LINEBREAK_MATCHER: lineBreakPattern,
746 SHEBANG_MATCHER: shebangPattern,
747 STATEMENT_LIST_PARENTS,
750 * Determines whether two adjacent tokens are on the same line.
751 * @param {Object} left The left token object.
752 * @param {Object} right The right token object.
753 * @returns {boolean} Whether or not the tokens are on the same line.
756 isTokenOnSameLine(left, right) {
757 return left.loc.end.line === right.loc.start.line;
769 createGlobalLinebreakMatcher,
774 isClosingBracketToken,
782 isNotClosingBraceToken: negate(isClosingBraceToken),
783 isNotClosingBracketToken: negate(isClosingBracketToken),
784 isNotClosingParenToken: negate(isClosingParenToken),
785 isNotColonToken: negate(isColonToken),
786 isNotCommaToken: negate(isCommaToken),
787 isNotDotToken: negate(isDotToken),
788 isNotQuestionDotToken: negate(isQuestionDotToken),
789 isNotOpeningBraceToken: negate(isOpeningBraceToken),
790 isNotOpeningBracketToken: negate(isOpeningBracketToken),
791 isNotOpeningParenToken: negate(isOpeningParenToken),
792 isNotSemicolonToken: negate(isSemicolonToken),
794 isOpeningBracketToken,
799 * Checks whether or not a given node is a string literal.
800 * @param {ASTNode} node A node to check.
801 * @returns {boolean} `true` if the node is a string literal.
803 isStringLiteral(node) {
805 (node.type === "Literal" && typeof node.value === "string") ||
806 node.type === "TemplateLiteral"
811 * Checks whether a given node is a breakable statement or not.
812 * The node is breakable if the node is one of the following type:
820 * @param {ASTNode} node A node to check.
821 * @returns {boolean} `true` if the node is breakable.
823 isBreakableStatement(node) {
824 return breakableTypePattern.test(node.type);
828 * Gets references which are non initializer and writable.
829 * @param {Reference[]} references An array of references.
830 * @returns {Reference[]} An array of only references which are non initializer and writable.
833 getModifyingReferences(references) {
834 return references.filter(isModifyingReference);
838 * Validate that a string passed in is surrounded by the specified character
839 * @param {string} val The text to check.
840 * @param {string} character The character to see if it's surrounded by.
841 * @returns {boolean} True if the text is surrounded by the character, false if not.
844 isSurroundedBy(val, character) {
845 return val[0] === character && val[val.length - 1] === character;
849 * Returns whether the provided node is an ESLint directive comment or not
850 * @param {Line|Block} node The comment token to be checked
851 * @returns {boolean} `true` if the node is an ESLint directive comment
853 isDirectiveComment(node) {
854 const comment = node.value.trim();
857 node.type === "Line" && comment.indexOf("eslint-") === 0 ||
858 node.type === "Block" && (
859 comment.indexOf("global ") === 0 ||
860 comment.indexOf("eslint ") === 0 ||
861 comment.indexOf("eslint-") === 0
867 * Gets the trailing statement of a given node.
872 * When taking this `IfStatement`, returns `consequent;` statement.
873 * @param {ASTNode} A node to get.
874 * @returns {ASTNode|null} The trailing statement's node.
876 getTrailingStatement: esutils.ast.trailingStatement,
879 * Finds the variable by a given name in a given scope and its upper scopes.
880 * @param {eslint-scope.Scope} initScope A scope to start find.
881 * @param {string} name A variable name to find.
882 * @returns {eslint-scope.Variable|null} A found variable or `null`.
884 getVariableByName(initScope, name) {
885 let scope = initScope;
888 const variable = scope.set.get(name);
901 * Checks whether or not a given function node is the default `this` binding.
903 * First, this checks the node:
905 * - The function name does not start with uppercase. It's a convention to capitalize the names
906 * of constructor functions. This check is not performed if `capIsConstructor` is set to `false`.
907 * - The function does not have a JSDoc comment that has a @this tag.
909 * Next, this checks the location of the node.
910 * If the location is below, this judges `this` is valid.
912 * - The location is not on an object literal.
913 * - The location is not assigned to a variable which starts with an uppercase letter. Applies to anonymous
914 * functions only, as the name of the variable is considered to be the name of the function in this case.
915 * This check is not performed if `capIsConstructor` is set to `false`.
916 * - The location is not on an ES2015 class.
917 * - Its `bind`/`call`/`apply` method is not called directly.
918 * - The function is not a callback of array methods (such as `.forEach()`) if `thisArg` is given.
919 * @param {ASTNode} node A function node to check.
920 * @param {SourceCode} sourceCode A SourceCode instance to get comments.
921 * @param {boolean} [capIsConstructor = true] `false` disables the assumption that functions which name starts
922 * with an uppercase or are assigned to a variable which name starts with an uppercase are constructors.
923 * @returns {boolean} The function node is the default `this` binding.
925 isDefaultThisBinding(node, sourceCode, { capIsConstructor = true } = {}) {
927 (capIsConstructor && isES5Constructor(node)) ||
928 hasJSDocThisTag(node, sourceCode)
932 const isAnonymous = node.id === null;
933 let currentNode = node;
935 while (currentNode) {
936 const parent = currentNode.parent;
938 switch (parent.type) {
941 * Looks up the destination.
942 * e.g., obj.foo = nativeFoo || function foo() { ... };
944 case "LogicalExpression":
945 case "ConditionalExpression":
946 case "ChainExpression":
947 currentNode = parent;
951 * If the upper function is IIFE, checks the destination of the return value.
953 * obj.foo = (function() {
955 * return function foo() { ... };
958 * function foo() { ... }
961 case "ReturnStatement": {
962 const func = getUpperFunction(parent);
964 if (func === null || !isCallee(func)) {
967 currentNode = func.parent;
970 case "ArrowFunctionExpression":
971 if (currentNode !== parent.body || !isCallee(parent)) {
974 currentNode = parent.parent;
979 * var obj = { foo() { ... } };
980 * var obj = { foo: function() { ... } };
981 * class A { constructor() { ... } }
982 * class A { foo() { ... } }
983 * class A { get foo() { ... } }
984 * class A { set foo() { ... } }
985 * class A { static foo() { ... } }
988 case "MethodDefinition":
989 return parent.value !== currentNode;
993 * obj.foo = function foo() { ... };
994 * Foo = function() { ... };
995 * [obj.foo = function foo() { ... }] = a;
996 * [Foo = function() { ... }] = a;
998 case "AssignmentExpression":
999 case "AssignmentPattern":
1000 if (parent.left.type === "MemberExpression") {
1006 parent.left.type === "Identifier" &&
1007 startsWithUpperCase(parent.left.name)
1015 * var Foo = function() { ... };
1017 case "VariableDeclarator":
1021 parent.init === currentNode &&
1022 parent.id.type === "Identifier" &&
1023 startsWithUpperCase(parent.id.name)
1028 * var foo = function foo() { ... }.bind(obj);
1029 * (function foo() { ... }).call(obj);
1030 * (function foo() { ... }).apply(obj, []);
1032 case "MemberExpression":
1034 parent.object === currentNode &&
1035 isSpecificMemberAccess(parent, null, bindOrCallOrApplyPattern)
1037 const maybeCalleeNode = parent.parent.type === "ChainExpression"
1042 isCallee(maybeCalleeNode) &&
1043 maybeCalleeNode.parent.arguments.length >= 1 &&
1044 !isNullOrUndefined(maybeCalleeNode.parent.arguments[0])
1051 * Reflect.apply(function() {}, obj, []);
1052 * Array.from([], function() {}, obj);
1053 * list.forEach(function() {}, obj);
1055 case "CallExpression":
1056 if (isReflectApply(parent.callee)) {
1058 parent.arguments.length !== 3 ||
1059 parent.arguments[0] !== currentNode ||
1060 isNullOrUndefined(parent.arguments[1])
1063 if (isArrayFromMethod(parent.callee)) {
1065 parent.arguments.length !== 3 ||
1066 parent.arguments[1] !== currentNode ||
1067 isNullOrUndefined(parent.arguments[2])
1070 if (isMethodWhichHasThisArg(parent.callee)) {
1072 parent.arguments.length !== 2 ||
1073 parent.arguments[0] !== currentNode ||
1074 isNullOrUndefined(parent.arguments[1])
1079 // Otherwise `this` is default.
1085 /* istanbul ignore next */
1090 * Get the precedence level based on the node type
1091 * @param {ASTNode} node node to evaluate
1092 * @returns {int} precedence level
1095 getPrecedence(node) {
1096 switch (node.type) {
1097 case "SequenceExpression":
1100 case "AssignmentExpression":
1101 case "ArrowFunctionExpression":
1102 case "YieldExpression":
1105 case "ConditionalExpression":
1108 case "LogicalExpression":
1109 switch (node.operator) {
1121 case "BinaryExpression":
1123 switch (node.operator) {
1161 case "UnaryExpression":
1162 case "AwaitExpression":
1165 case "UpdateExpression":
1168 case "CallExpression":
1169 case "ChainExpression":
1170 case "ImportExpression":
1173 case "NewExpression":
1182 * Checks whether the given node is an empty block node or not.
1183 * @param {ASTNode|null} node The node to check.
1184 * @returns {boolean} `true` if the node is an empty block.
1186 isEmptyBlock(node) {
1187 return Boolean(node && node.type === "BlockStatement" && node.body.length === 0);
1191 * Checks whether the given node is an empty function node or not.
1192 * @param {ASTNode|null} node The node to check.
1193 * @returns {boolean} `true` if the node is an empty function.
1195 isEmptyFunction(node) {
1196 return isFunction(node) && module.exports.isEmptyBlock(node.body);
1200 * Get directives from directive prologue of a Program or Function node.
1201 * @param {ASTNode} node The node to check.
1202 * @returns {ASTNode[]} The directives found in the directive prologue.
1204 getDirectivePrologue(node) {
1205 const directives = [];
1207 // Directive prologues only occur at the top of files or functions.
1209 node.type === "Program" ||
1210 node.type === "FunctionDeclaration" ||
1211 node.type === "FunctionExpression" ||
1214 * Do not check arrow functions with implicit return.
1215 * `() => "use strict";` returns the string `"use strict"`.
1217 (node.type === "ArrowFunctionExpression" && node.body.type === "BlockStatement")
1219 const statements = node.type === "Program" ? node.body : node.body.body;
1221 for (const statement of statements) {
1223 statement.type === "ExpressionStatement" &&
1224 statement.expression.type === "Literal"
1226 directives.push(statement);
1238 * Determines whether this node is a decimal integer literal. If a node is a decimal integer literal, a dot added
1239 * after the node will be parsed as a decimal point, rather than a property-access dot.
1240 * @param {ASTNode} node The node to check.
1241 * @returns {boolean} `true` if this node is a decimal integer.
1266 isDecimalInteger(node) {
1267 return node.type === "Literal" && typeof node.value === "number" &&
1268 DECIMAL_INTEGER_PATTERN.test(node.raw);
1272 * Determines whether this token is a decimal integer numeric token.
1273 * This is similar to isDecimalInteger(), but for tokens.
1274 * @param {Token} token The token to check.
1275 * @returns {boolean} `true` if this token is a decimal integer.
1277 isDecimalIntegerNumericToken(token) {
1278 return token.type === "Numeric" && DECIMAL_INTEGER_PATTERN.test(token.value);
1282 * Gets the name and kind of the given function node.
1284 * - `function foo() {}` .................... `function 'foo'`
1285 * - `(function foo() {})` .................. `function 'foo'`
1286 * - `(function() {})` ...................... `function`
1287 * - `function* foo() {}` ................... `generator function 'foo'`
1288 * - `(function* foo() {})` ................. `generator function 'foo'`
1289 * - `(function*() {})` ..................... `generator function`
1290 * - `() => {}` ............................. `arrow function`
1291 * - `async () => {}` ....................... `async arrow function`
1292 * - `({ foo: function foo() {} })` ......... `method 'foo'`
1293 * - `({ foo: function() {} })` ............. `method 'foo'`
1294 * - `({ ['foo']: function() {} })` ......... `method 'foo'`
1295 * - `({ [foo]: function() {} })` ........... `method`
1296 * - `({ foo() {} })` ....................... `method 'foo'`
1297 * - `({ foo: function* foo() {} })` ........ `generator method 'foo'`
1298 * - `({ foo: function*() {} })` ............ `generator method 'foo'`
1299 * - `({ ['foo']: function*() {} })` ........ `generator method 'foo'`
1300 * - `({ [foo]: function*() {} })` .......... `generator method`
1301 * - `({ *foo() {} })` ...................... `generator method 'foo'`
1302 * - `({ foo: async function foo() {} })` ... `async method 'foo'`
1303 * - `({ foo: async function() {} })` ....... `async method 'foo'`
1304 * - `({ ['foo']: async function() {} })` ... `async method 'foo'`
1305 * - `({ [foo]: async function() {} })` ..... `async method`
1306 * - `({ async foo() {} })` ................. `async method 'foo'`
1307 * - `({ get foo() {} })` ................... `getter 'foo'`
1308 * - `({ set foo(a) {} })` .................. `setter 'foo'`
1309 * - `class A { constructor() {} }` ......... `constructor`
1310 * - `class A { foo() {} }` ................. `method 'foo'`
1311 * - `class A { *foo() {} }` ................ `generator method 'foo'`
1312 * - `class A { async foo() {} }` ........... `async method 'foo'`
1313 * - `class A { ['foo']() {} }` ............. `method 'foo'`
1314 * - `class A { *['foo']() {} }` ............ `generator method 'foo'`
1315 * - `class A { async ['foo']() {} }` ....... `async method 'foo'`
1316 * - `class A { [foo]() {} }` ............... `method`
1317 * - `class A { *[foo]() {} }` .............. `generator method`
1318 * - `class A { async [foo]() {} }` ......... `async method`
1319 * - `class A { get foo() {} }` ............. `getter 'foo'`
1320 * - `class A { set foo(a) {} }` ............ `setter 'foo'`
1321 * - `class A { static foo() {} }` .......... `static method 'foo'`
1322 * - `class A { static *foo() {} }` ......... `static generator method 'foo'`
1323 * - `class A { static async foo() {} }` .... `static async method 'foo'`
1324 * - `class A { static get foo() {} }` ...... `static getter 'foo'`
1325 * - `class A { static set foo(a) {} }` ..... `static setter 'foo'`
1326 * @param {ASTNode} node The function node to get.
1327 * @returns {string} The name and kind of the function node.
1329 getFunctionNameWithKind(node) {
1330 const parent = node.parent;
1333 if (parent.type === "MethodDefinition" && parent.static) {
1334 tokens.push("static");
1337 tokens.push("async");
1339 if (node.generator) {
1340 tokens.push("generator");
1343 if (node.type === "ArrowFunctionExpression") {
1344 tokens.push("arrow", "function");
1345 } else if (parent.type === "Property" || parent.type === "MethodDefinition") {
1346 if (parent.kind === "constructor") {
1347 return "constructor";
1349 if (parent.kind === "get") {
1350 tokens.push("getter");
1351 } else if (parent.kind === "set") {
1352 tokens.push("setter");
1354 tokens.push("method");
1357 tokens.push("function");
1361 tokens.push(`'${node.id.name}'`);
1363 const name = getStaticPropertyName(parent);
1365 if (name !== null) {
1366 tokens.push(`'${name}'`);
1370 return tokens.join(" ");
1374 * Gets the location of the given function node for reporting.
1376 * - `function foo() {}`
1378 * - `(function foo() {})`
1380 * - `(function() {})`
1382 * - `function* foo() {}`
1384 * - `(function* foo() {})`
1386 * - `(function*() {})`
1390 * - `async () => {}`
1392 * - `({ foo: function foo() {} })`
1394 * - `({ foo: function() {} })`
1396 * - `({ ['foo']: function() {} })`
1398 * - `({ [foo]: function() {} })`
1400 * - `({ foo() {} })`
1402 * - `({ foo: function* foo() {} })`
1403 * ^^^^^^^^^^^^^^^^^^
1404 * - `({ foo: function*() {} })`
1406 * - `({ ['foo']: function*() {} })`
1407 * ^^^^^^^^^^^^^^^^^^
1408 * - `({ [foo]: function*() {} })`
1410 * - `({ *foo() {} })`
1412 * - `({ foo: async function foo() {} })`
1413 * ^^^^^^^^^^^^^^^^^^^^^^^
1414 * - `({ foo: async function() {} })`
1415 * ^^^^^^^^^^^^^^^^^^^
1416 * - `({ ['foo']: async function() {} })`
1417 * ^^^^^^^^^^^^^^^^^^^^^^^
1418 * - `({ [foo]: async function() {} })`
1419 * ^^^^^^^^^^^^^^^^^^^^^
1420 * - `({ async foo() {} })`
1422 * - `({ get foo() {} })`
1424 * - `({ set foo(a) {} })`
1426 * - `class A { constructor() {} }`
1428 * - `class A { foo() {} }`
1430 * - `class A { *foo() {} }`
1432 * - `class A { async foo() {} }`
1434 * - `class A { ['foo']() {} }`
1436 * - `class A { *['foo']() {} }`
1438 * - `class A { async ['foo']() {} }`
1440 * - `class A { [foo]() {} }`
1442 * - `class A { *[foo]() {} }`
1444 * - `class A { async [foo]() {} }`
1446 * - `class A { get foo() {} }`
1448 * - `class A { set foo(a) {} }`
1450 * - `class A { static foo() {} }`
1452 * - `class A { static *foo() {} }`
1454 * - `class A { static async foo() {} }`
1456 * - `class A { static get foo() {} }`
1458 * - `class A { static set foo(a) {} }`
1460 * @param {ASTNode} node The function node to get.
1461 * @param {SourceCode} sourceCode The source code object to get tokens.
1462 * @returns {string} The location of the function node for reporting.
1464 getFunctionHeadLoc(node, sourceCode) {
1465 const parent = node.parent;
1469 if (node.type === "ArrowFunctionExpression") {
1470 const arrowToken = sourceCode.getTokenBefore(node.body, isArrowToken);
1472 start = arrowToken.loc.start;
1473 end = arrowToken.loc.end;
1474 } else if (parent.type === "Property" || parent.type === "MethodDefinition") {
1475 start = parent.loc.start;
1476 end = getOpeningParenOfParams(node, sourceCode).loc.start;
1478 start = node.loc.start;
1479 end = getOpeningParenOfParams(node, sourceCode).loc.start;
1483 start: Object.assign({}, start),
1484 end: Object.assign({}, end)
1489 * Gets next location when the result is not out of bound, otherwise returns null.
1493 * - The given location represents a valid location in the given source code.
1494 * - Columns are 0-based.
1495 * - Lines are 1-based.
1496 * - Column immediately after the last character in a line (not incl. linebreaks) is considered to be a valid location.
1497 * - If the source code ends with a linebreak, `sourceCode.lines` array will have an extra element (empty string) at the end.
1498 * The start (column 0) of that extra line is considered to be a valid location.
1500 * Examples of successive locations (line, column):
1503 * locations: (1, 0) -> (1, 1) -> (1, 2) -> (1, 3) -> null
1506 * locations: (1, 0) -> (1, 1) -> (1, 2) -> (1, 3) -> (2, 0) -> null
1509 * locations: (1, 0) -> (1, 1) -> (1, 2) -> (1, 3) -> (2, 0) -> null
1512 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (2, 1) -> null
1515 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (2, 1) -> (3, 0) -> null
1517 * code: a<CR><LF>b<CR><LF>
1518 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (2, 1) -> (3, 0) -> null
1521 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (3, 0) -> null
1524 * locations: (1, 0) -> (2, 0) -> null
1527 * locations: (1, 0) -> null
1528 * @param {SourceCode} sourceCode The sourceCode
1529 * @param {{line: number, column: number}} location The location
1530 * @returns {{line: number, column: number} | null} Next location
1532 getNextLocation(sourceCode, { line, column }) {
1533 if (column < sourceCode.lines[line - 1].length) {
1540 if (line < sourceCode.lines.length) {
1551 * Gets the parenthesized text of a node. This is similar to sourceCode.getText(node), but it also includes any parentheses
1552 * surrounding the node.
1553 * @param {SourceCode} sourceCode The source code object
1554 * @param {ASTNode} node An expression node
1555 * @returns {string} The text representing the node, with all surrounding parentheses included
1557 getParenthesisedText(sourceCode, node) {
1558 let leftToken = sourceCode.getFirstToken(node);
1559 let rightToken = sourceCode.getLastToken(node);
1562 sourceCode.getTokenBefore(leftToken) &&
1563 sourceCode.getTokenBefore(leftToken).type === "Punctuator" &&
1564 sourceCode.getTokenBefore(leftToken).value === "(" &&
1565 sourceCode.getTokenAfter(rightToken) &&
1566 sourceCode.getTokenAfter(rightToken).type === "Punctuator" &&
1567 sourceCode.getTokenAfter(rightToken).value === ")"
1569 leftToken = sourceCode.getTokenBefore(leftToken);
1570 rightToken = sourceCode.getTokenAfter(rightToken);
1573 return sourceCode.getText().slice(leftToken.range[0], rightToken.range[1]);
1577 * Determine if a node has a possibility to be an Error object
1578 * @param {ASTNode} node ASTNode to check
1579 * @returns {boolean} True if there is a chance it contains an Error obj
1581 couldBeError(node) {
1582 switch (node.type) {
1584 case "CallExpression":
1585 case "NewExpression":
1586 case "MemberExpression":
1587 case "TaggedTemplateExpression":
1588 case "YieldExpression":
1589 case "AwaitExpression":
1590 case "ChainExpression":
1591 return true; // possibly an error object.
1593 case "AssignmentExpression":
1594 if (["=", "&&="].includes(node.operator)) {
1595 return module.exports.couldBeError(node.right);
1598 if (["||=", "??="].includes(node.operator)) {
1599 return module.exports.couldBeError(node.left) || module.exports.couldBeError(node.right);
1603 * All other assignment operators are mathematical assignment operators (arithmetic or bitwise).
1604 * An assignment expression with a mathematical operator can either evaluate to a primitive value,
1605 * or throw, depending on the operands. Thus, it cannot evaluate to an `Error` object.
1609 case "SequenceExpression": {
1610 const exprs = node.expressions;
1612 return exprs.length !== 0 && module.exports.couldBeError(exprs[exprs.length - 1]);
1615 case "LogicalExpression":
1618 * If the && operator short-circuits, the left side was falsy and therefore not an error, and if it
1619 * doesn't short-circuit, it takes the value from the right side, so the right side must always be
1620 * a plausible error. A future improvement could verify that the left side could be truthy by
1621 * excluding falsy literals.
1623 if (node.operator === "&&") {
1624 return module.exports.couldBeError(node.right);
1627 return module.exports.couldBeError(node.left) || module.exports.couldBeError(node.right);
1629 case "ConditionalExpression":
1630 return module.exports.couldBeError(node.consequent) || module.exports.couldBeError(node.alternate);
1638 * Check if a given node is a numeric literal or not.
1639 * @param {ASTNode} node The node to check.
1640 * @returns {boolean} `true` if the node is a number or bigint literal.
1642 isNumericLiteral(node) {
1644 node.type === "Literal" &&
1645 (typeof node.value === "number" || Boolean(node.bigint))
1650 * Determines whether two tokens can safely be placed next to each other without merging into a single token
1651 * @param {Token|string} leftValue The left token. If this is a string, it will be tokenized and the last token will be used.
1652 * @param {Token|string} rightValue The right token. If this is a string, it will be tokenized and the first token will be used.
1653 * @returns {boolean} If the tokens cannot be safely placed next to each other, returns `false`. If the tokens can be placed
1654 * next to each other, behavior is undefined (although it should return `true` in most cases).
1656 canTokensBeAdjacent(leftValue, rightValue) {
1657 const espreeOptions = {
1658 ecmaVersion: espree.latestEcmaVersion,
1665 if (typeof leftValue === "string") {
1669 tokens = espree.tokenize(leftValue, espreeOptions);
1674 const comments = tokens.comments;
1676 leftToken = tokens[tokens.length - 1];
1677 if (comments.length) {
1678 const lastComment = comments[comments.length - 1];
1680 if (lastComment.range[0] > leftToken.range[0]) {
1681 leftToken = lastComment;
1685 leftToken = leftValue;
1688 if (leftToken.type === "Shebang") {
1694 if (typeof rightValue === "string") {
1698 tokens = espree.tokenize(rightValue, espreeOptions);
1703 const comments = tokens.comments;
1705 rightToken = tokens[0];
1706 if (comments.length) {
1707 const firstComment = comments[0];
1709 if (firstComment.range[0] < rightToken.range[0]) {
1710 rightToken = firstComment;
1714 rightToken = rightValue;
1717 if (leftToken.type === "Punctuator" || rightToken.type === "Punctuator") {
1718 if (leftToken.type === "Punctuator" && rightToken.type === "Punctuator") {
1719 const PLUS_TOKENS = new Set(["+", "++"]);
1720 const MINUS_TOKENS = new Set(["-", "--"]);
1723 PLUS_TOKENS.has(leftToken.value) && PLUS_TOKENS.has(rightToken.value) ||
1724 MINUS_TOKENS.has(leftToken.value) && MINUS_TOKENS.has(rightToken.value)
1727 if (leftToken.type === "Punctuator" && leftToken.value === "/") {
1728 return !["Block", "Line", "RegularExpression"].includes(rightToken.type);
1734 leftToken.type === "String" || rightToken.type === "String" ||
1735 leftToken.type === "Template" || rightToken.type === "Template"
1740 if (leftToken.type !== "Numeric" && rightToken.type === "Numeric" && rightToken.value.startsWith(".")) {
1744 if (leftToken.type === "Block" || rightToken.type === "Block" || rightToken.type === "Line") {
1752 * Get the `loc` object of a given name in a `/*globals` directive comment.
1753 * @param {SourceCode} sourceCode The source code to convert index to loc.
1754 * @param {Comment} comment The `/*globals` directive comment which include the name.
1755 * @param {string} name The name to find.
1756 * @returns {SourceLocation} The `loc` object.
1758 getNameLocationInGlobalDirectiveComment(sourceCode, comment, name) {
1759 const namePattern = new RegExp(`[\\s,]${escapeRegExp(name)}(?:$|[\\s,:])`, "gu");
1761 // To ignore the first text "global".
1762 namePattern.lastIndex = comment.value.indexOf("global") + 6;
1764 // Search a given variable name.
1765 const match = namePattern.exec(comment.value);
1767 // Convert the index to loc.
1768 const start = sourceCode.getLocFromIndex(
1771 (match ? match.index + 1 : 0)
1775 column: start.column + (match ? name.length : 1)
1778 return { start, end };
1782 * Determines whether the given raw string contains an octal escape sequence
1783 * or a non-octal decimal escape sequence ("\8", "\9").
1785 * "\1", "\2" ... "\7", "\8", "\9"
1786 * "\00", "\01" ... "\07", "\08", "\09"
1788 * "\0", when not followed by a digit, is not an octal escape sequence.
1789 * @param {string} rawString A string in its raw representation.
1790 * @returns {boolean} `true` if the string contains at least one octal escape sequence
1791 * or at least one non-octal decimal escape sequence.
1793 hasOctalOrNonOctalDecimalEscapeSequence(rawString) {
1794 return OCTAL_OR_NON_OCTAL_DECIMAL_ESCAPE_PATTERN.test(rawString);
1797 isLogicalExpression,
1798 isCoalesceExpression,
1799 isMixedLogicalAndCoalesceExpressions,
1801 getStaticStringValue,
1802 getStaticPropertyName,
1803 skipChainExpression,
1805 isSpecificMemberAccess,
1808 isLogicalAssignmentOperator