2 * @fileoverview Common utils for AST.
3 * @author Gyandeep Singh
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 const esutils
= require("esutils");
13 const espree
= require("espree");
14 const lodash
= require("lodash");
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|[1-9]\d*)$/u;
41 const OCTAL_ESCAPE_PATTERN
= /^(?:[^\\]|\\[^0-7]|\\0(?![0-9]))*\\(?:[1-7]|0[0-9])/u;
44 * Checks reference if is non initializer and writable.
45 * @param {Reference} reference A reference to check.
46 * @param {int} index The index of the reference in the references.
47 * @param {Reference[]} references The array that the reference belongs to.
48 * @returns {boolean} Success/Failure
51 function isModifyingReference(reference
, index
, references
) {
52 const identifier
= reference
.identifier
;
55 * Destructuring assignments can have multiple default value, so
56 * possibly there are multiple writeable references for the same
59 const modifyingDifferentIdentifier
= index
=== 0 ||
60 references
[index
- 1].identifier
!== identifier
;
63 reference
.init
=== false &&
64 reference
.isWrite() &&
65 modifyingDifferentIdentifier
70 * Checks whether the given string starts with uppercase or not.
71 * @param {string} s The string to check.
72 * @returns {boolean} `true` if the string starts with uppercase.
74 function startsWithUpperCase(s
) {
75 return s
[0] !== s
[0].toLocaleLowerCase();
79 * Checks whether or not a node is a constructor.
80 * @param {ASTNode} node A function node to check.
81 * @returns {boolean} Wehether or not a node is a constructor.
83 function isES5Constructor(node
) {
84 return (node
.id
&& startsWithUpperCase(node
.id
.name
));
88 * Finds a function node from ancestors of a node.
89 * @param {ASTNode} node A start node to find.
90 * @returns {Node|null} A found function node.
92 function getUpperFunction(node
) {
93 for (let currentNode
= node
; currentNode
; currentNode
= currentNode
.parent
) {
94 if (anyFunctionPattern
.test(currentNode
.type
)) {
102 * Checks whether a given node is a function node or not.
103 * The following types are function nodes:
105 * - ArrowFunctionExpression
106 * - FunctionDeclaration
107 * - FunctionExpression
108 * @param {ASTNode|null} node A node to check.
109 * @returns {boolean} `true` if the node is a function node.
111 function isFunction(node
) {
112 return Boolean(node
&& anyFunctionPattern
.test(node
.type
));
116 * Checks whether a given node is a loop node or not.
117 * The following types are loop nodes:
124 * @param {ASTNode|null} node A node to check.
125 * @returns {boolean} `true` if the node is a loop node.
127 function isLoop(node
) {
128 return Boolean(node
&& anyLoopPattern
.test(node
.type
));
132 * Checks whether the given node is in a loop or not.
133 * @param {ASTNode} node The node to check.
134 * @returns {boolean} `true` if the node is in a loop.
136 function isInLoop(node
) {
137 for (let currentNode
= node
; currentNode
&& !isFunction(currentNode
); currentNode
= currentNode
.parent
) {
138 if (isLoop(currentNode
)) {
147 * Checks whether or not a node is `null` or `undefined`.
148 * @param {ASTNode} node A node to check.
149 * @returns {boolean} Whether or not the node is a `null` or `undefined`.
152 function isNullOrUndefined(node
) {
154 module
.exports
.isNullLiteral(node
) ||
155 (node
.type
=== "Identifier" && node
.name
=== "undefined") ||
156 (node
.type
=== "UnaryExpression" && node
.operator
=== "void")
161 * Checks whether or not a node is callee.
162 * @param {ASTNode} node A node to check.
163 * @returns {boolean} Whether or not the node is callee.
165 function isCallee(node
) {
166 return node
.parent
.type
=== "CallExpression" && node
.parent
.callee
=== node
;
170 * Checks whether or not a node is `Reflect.apply`.
171 * @param {ASTNode} node A node to check.
172 * @returns {boolean} Whether or not the node is a `Reflect.apply`.
174 function isReflectApply(node
) {
176 node
.type
=== "MemberExpression" &&
177 node
.object
.type
=== "Identifier" &&
178 node
.object
.name
=== "Reflect" &&
179 node
.property
.type
=== "Identifier" &&
180 node
.property
.name
=== "apply" &&
181 node
.computed
=== false
186 * Checks whether or not a node is `Array.from`.
187 * @param {ASTNode} node A node to check.
188 * @returns {boolean} Whether or not the node is a `Array.from`.
190 function isArrayFromMethod(node
) {
192 node
.type
=== "MemberExpression" &&
193 node
.object
.type
=== "Identifier" &&
194 arrayOrTypedArrayPattern
.test(node
.object
.name
) &&
195 node
.property
.type
=== "Identifier" &&
196 node
.property
.name
=== "from" &&
197 node
.computed
=== false
202 * Checks whether or not a node is a method which has `thisArg`.
203 * @param {ASTNode} node A node to check.
204 * @returns {boolean} Whether or not the node is a method which has `thisArg`.
206 function isMethodWhichHasThisArg(node
) {
208 let currentNode
= node
;
209 currentNode
.type
=== "MemberExpression" && !currentNode
.computed
;
210 currentNode
= currentNode
.property
212 if (currentNode
.property
.type
=== "Identifier") {
213 return arrayMethodPattern
.test(currentNode
.property
.name
);
221 * Creates the negate function of the given function.
222 * @param {Function} f The function to negate.
223 * @returns {Function} Negated function.
226 return token
=> !f(token
);
230 * Checks whether or not a node has a `@this` tag in its comments.
231 * @param {ASTNode} node A node to check.
232 * @param {SourceCode} sourceCode A SourceCode instance to get comments.
233 * @returns {boolean} Whether or not the node has a `@this` tag in its comments.
235 function hasJSDocThisTag(node
, sourceCode
) {
236 const jsdocComment
= sourceCode
.getJSDocComment(node
);
238 if (jsdocComment
&& thisTagPattern
.test(jsdocComment
.value
)) {
242 // Checks `@this` in its leading comments for callbacks,
243 // because callbacks don't have its JSDoc comment.
245 // sinon.test(/* @this sinon.Sandbox */function() { this.spy(); });
246 return sourceCode
.getCommentsBefore(node
).some(comment
=> thisTagPattern
.test(comment
.value
));
250 * Determines if a node is surrounded by parentheses.
251 * @param {SourceCode} sourceCode The ESLint source code object
252 * @param {ASTNode} node The node to be checked.
253 * @returns {boolean} True if the node is parenthesised.
256 function isParenthesised(sourceCode
, node
) {
257 const previousToken
= sourceCode
.getTokenBefore(node
),
258 nextToken
= sourceCode
.getTokenAfter(node
);
260 return Boolean(previousToken
&& nextToken
) &&
261 previousToken
.value
=== "(" && previousToken
.range
[1] <= node
.range
[0] &&
262 nextToken
.value
=== ")" && nextToken
.range
[0] >= node
.range
[1];
266 * Checks if the given token is an arrow token or not.
267 * @param {Token} token The token to check.
268 * @returns {boolean} `true` if the token is an arrow token.
270 function isArrowToken(token
) {
271 return token
.value
=== "=>" && token
.type
=== "Punctuator";
275 * Checks if the given token is a comma token or not.
276 * @param {Token} token The token to check.
277 * @returns {boolean} `true` if the token is a comma token.
279 function isCommaToken(token
) {
280 return token
.value
=== "," && token
.type
=== "Punctuator";
284 * Checks if the given token is a dot token or not.
285 * @param {Token} token The token to check.
286 * @returns {boolean} `true` if the token is a dot token.
288 function isDotToken(token
) {
289 return token
.value
=== "." && token
.type
=== "Punctuator";
293 * Checks if the given token is a semicolon token or not.
294 * @param {Token} token The token to check.
295 * @returns {boolean} `true` if the token is a semicolon token.
297 function isSemicolonToken(token
) {
298 return token
.value
=== ";" && token
.type
=== "Punctuator";
302 * Checks if the given token is a colon token or not.
303 * @param {Token} token The token to check.
304 * @returns {boolean} `true` if the token is a colon token.
306 function isColonToken(token
) {
307 return token
.value
=== ":" && token
.type
=== "Punctuator";
311 * Checks if the given token is an opening parenthesis token or not.
312 * @param {Token} token The token to check.
313 * @returns {boolean} `true` if the token is an opening parenthesis token.
315 function isOpeningParenToken(token
) {
316 return token
.value
=== "(" && token
.type
=== "Punctuator";
320 * Checks if the given token is a closing parenthesis token or not.
321 * @param {Token} token The token to check.
322 * @returns {boolean} `true` if the token is a closing parenthesis token.
324 function isClosingParenToken(token
) {
325 return token
.value
=== ")" && token
.type
=== "Punctuator";
329 * Checks if the given token is an opening square bracket token or not.
330 * @param {Token} token The token to check.
331 * @returns {boolean} `true` if the token is an opening square bracket token.
333 function isOpeningBracketToken(token
) {
334 return token
.value
=== "[" && token
.type
=== "Punctuator";
338 * Checks if the given token is a closing square bracket token or not.
339 * @param {Token} token The token to check.
340 * @returns {boolean} `true` if the token is a closing square bracket token.
342 function isClosingBracketToken(token
) {
343 return token
.value
=== "]" && token
.type
=== "Punctuator";
347 * Checks if the given token is an opening brace token or not.
348 * @param {Token} token The token to check.
349 * @returns {boolean} `true` if the token is an opening brace token.
351 function isOpeningBraceToken(token
) {
352 return token
.value
=== "{" && token
.type
=== "Punctuator";
356 * Checks if the given token is a closing brace token or not.
357 * @param {Token} token The token to check.
358 * @returns {boolean} `true` if the token is a closing brace token.
360 function isClosingBraceToken(token
) {
361 return token
.value
=== "}" && token
.type
=== "Punctuator";
365 * Checks if the given token is a comment token or not.
366 * @param {Token} token The token to check.
367 * @returns {boolean} `true` if the token is a comment token.
369 function isCommentToken(token
) {
370 return token
.type
=== "Line" || token
.type
=== "Block" || token
.type
=== "Shebang";
374 * Checks if the given token is a keyword token or not.
375 * @param {Token} token The token to check.
376 * @returns {boolean} `true` if the token is a keyword token.
378 function isKeywordToken(token
) {
379 return token
.type
=== "Keyword";
383 * Gets the `(` token of the given function node.
384 * @param {ASTNode} node The function node to get.
385 * @param {SourceCode} sourceCode The source code object to get tokens.
386 * @returns {Token} `(` token.
388 function getOpeningParenOfParams(node
, sourceCode
) {
390 ? sourceCode
.getTokenAfter(node
.id
, isOpeningParenToken
)
391 : sourceCode
.getFirstToken(node
, isOpeningParenToken
);
395 * Checks whether or not the tokens of two given nodes are same.
396 * @param {ASTNode} left A node 1 to compare.
397 * @param {ASTNode} right A node 2 to compare.
398 * @param {SourceCode} sourceCode The ESLint source code object.
399 * @returns {boolean} the source code for the given node.
401 function equalTokens(left
, right
, sourceCode
) {
402 const tokensL
= sourceCode
.getTokens(left
);
403 const tokensR
= sourceCode
.getTokens(right
);
405 if (tokensL
.length
!== tokensR
.length
) {
408 for (let i
= 0; i
< tokensL
.length
; ++i
) {
409 if (tokensL
[i
].type
!== tokensR
[i
].type
||
410 tokensL
[i
].value
!== tokensR
[i
].value
420 * Check if the given node is a true logical expression or not.
422 * The three binary expressions logical-or (`||`), logical-and (`&&`), and
423 * coalesce (`??`) are known as `ShortCircuitExpression`.
424 * But ESTree represents those by `LogicalExpression` node.
426 * This function rejects coalesce expressions of `LogicalExpression` node.
427 * @param {ASTNode} node The node to check.
428 * @returns {boolean} `true` if the node is `&&` or `||`.
429 * @see https://tc39.es/ecma262/#prod-ShortCircuitExpression
431 function isLogicalExpression(node
) {
433 node
.type
=== "LogicalExpression" &&
434 (node
.operator
=== "&&" || node
.operator
=== "||")
439 * Check if the given node is a nullish coalescing expression or not.
441 * The three binary expressions logical-or (`||`), logical-and (`&&`), and
442 * coalesce (`??`) are known as `ShortCircuitExpression`.
443 * But ESTree represents those by `LogicalExpression` node.
445 * This function finds only coalesce expressions of `LogicalExpression` node.
446 * @param {ASTNode} node The node to check.
447 * @returns {boolean} `true` if the node is `??`.
449 function isCoalesceExpression(node
) {
450 return node
.type
=== "LogicalExpression" && node
.operator
=== "??";
454 * Check if given two nodes are the pair of a logical expression and a coalesce expression.
455 * @param {ASTNode} left A node to check.
456 * @param {ASTNode} right Another node to check.
457 * @returns {boolean} `true` if the two nodes are the pair of a logical expression and a coalesce expression.
459 function isMixedLogicalAndCoalesceExpressions(left
, right
) {
461 (isLogicalExpression(left
) && isCoalesceExpression(right
)) ||
462 (isCoalesceExpression(left
) && isLogicalExpression(right
))
466 //------------------------------------------------------------------------------
468 //------------------------------------------------------------------------------
471 COMMENTS_IGNORE_PATTERN
,
473 LINEBREAK_MATCHER
: lineBreakPattern
,
474 SHEBANG_MATCHER
: shebangPattern
,
475 STATEMENT_LIST_PARENTS
,
478 * Determines whether two adjacent tokens are on the same line.
479 * @param {Object} left The left token object.
480 * @param {Object} right The right token object.
481 * @returns {boolean} Whether or not the tokens are on the same line.
484 isTokenOnSameLine(left
, right
) {
485 return left
.loc
.end
.line
=== right
.loc
.start
.line
;
497 createGlobalLinebreakMatcher
,
502 isClosingBracketToken
,
509 isNotClosingBraceToken
: negate(isClosingBraceToken
),
510 isNotClosingBracketToken
: negate(isClosingBracketToken
),
511 isNotClosingParenToken
: negate(isClosingParenToken
),
512 isNotColonToken
: negate(isColonToken
),
513 isNotCommaToken
: negate(isCommaToken
),
514 isNotDotToken
: negate(isDotToken
),
515 isNotOpeningBraceToken
: negate(isOpeningBraceToken
),
516 isNotOpeningBracketToken
: negate(isOpeningBracketToken
),
517 isNotOpeningParenToken
: negate(isOpeningParenToken
),
518 isNotSemicolonToken
: negate(isSemicolonToken
),
520 isOpeningBracketToken
,
525 * Checks whether or not a given node is a string literal.
526 * @param {ASTNode} node A node to check.
527 * @returns {boolean} `true` if the node is a string literal.
529 isStringLiteral(node
) {
531 (node
.type
=== "Literal" && typeof node
.value
=== "string") ||
532 node
.type
=== "TemplateLiteral"
537 * Checks whether a given node is a breakable statement or not.
538 * The node is breakable if the node is one of the following type:
546 * @param {ASTNode} node A node to check.
547 * @returns {boolean} `true` if the node is breakable.
549 isBreakableStatement(node
) {
550 return breakableTypePattern
.test(node
.type
);
554 * Gets references which are non initializer and writable.
555 * @param {Reference[]} references An array of references.
556 * @returns {Reference[]} An array of only references which are non initializer and writable.
559 getModifyingReferences(references
) {
560 return references
.filter(isModifyingReference
);
564 * Validate that a string passed in is surrounded by the specified character
565 * @param {string} val The text to check.
566 * @param {string} character The character to see if it's surrounded by.
567 * @returns {boolean} True if the text is surrounded by the character, false if not.
570 isSurroundedBy(val
, character
) {
571 return val
[0] === character
&& val
[val
.length
- 1] === character
;
575 * Returns whether the provided node is an ESLint directive comment or not
576 * @param {Line|Block} node The comment token to be checked
577 * @returns {boolean} `true` if the node is an ESLint directive comment
579 isDirectiveComment(node
) {
580 const comment
= node
.value
.trim();
583 node
.type
=== "Line" && comment
.indexOf("eslint-") === 0 ||
584 node
.type
=== "Block" && (
585 comment
.indexOf("global ") === 0 ||
586 comment
.indexOf("eslint ") === 0 ||
587 comment
.indexOf("eslint-") === 0
593 * Gets the trailing statement of a given node.
598 * When taking this `IfStatement`, returns `consequent;` statement.
599 * @param {ASTNode} A node to get.
600 * @returns {ASTNode|null} The trailing statement's node.
602 getTrailingStatement
: esutils
.ast
.trailingStatement
,
605 * Finds the variable by a given name in a given scope and its upper scopes.
606 * @param {eslint-scope.Scope} initScope A scope to start find.
607 * @param {string} name A variable name to find.
608 * @returns {eslint-scope.Variable|null} A found variable or `null`.
610 getVariableByName(initScope
, name
) {
611 let scope
= initScope
;
614 const variable
= scope
.set.get(name
);
627 * Checks whether or not a given function node is the default `this` binding.
629 * First, this checks the node:
631 * - The function name does not start with uppercase. It's a convention to capitalize the names
632 * of constructor functions. This check is not performed if `capIsConstructor` is set to `false`.
633 * - The function does not have a JSDoc comment that has a @this tag.
635 * Next, this checks the location of the node.
636 * If the location is below, this judges `this` is valid.
638 * - The location is not on an object literal.
639 * - The location is not assigned to a variable which starts with an uppercase letter. Applies to anonymous
640 * functions only, as the name of the variable is considered to be the name of the function in this case.
641 * This check is not performed if `capIsConstructor` is set to `false`.
642 * - The location is not on an ES2015 class.
643 * - Its `bind`/`call`/`apply` method is not called directly.
644 * - The function is not a callback of array methods (such as `.forEach()`) if `thisArg` is given.
645 * @param {ASTNode} node A function node to check.
646 * @param {SourceCode} sourceCode A SourceCode instance to get comments.
647 * @param {boolean} [capIsConstructor = true] `false` disables the assumption that functions which name starts
648 * with an uppercase or are assigned to a variable which name starts with an uppercase are constructors.
649 * @returns {boolean} The function node is the default `this` binding.
651 isDefaultThisBinding(node
, sourceCode
, { capIsConstructor
= true } = {}) {
653 (capIsConstructor
&& isES5Constructor(node
)) ||
654 hasJSDocThisTag(node
, sourceCode
)
658 const isAnonymous
= node
.id
=== null;
659 let currentNode
= node
;
661 while (currentNode
) {
662 const parent
= currentNode
.parent
;
664 switch (parent
.type
) {
667 * Looks up the destination.
668 * e.g., obj.foo = nativeFoo || function foo() { ... };
670 case "LogicalExpression":
671 case "ConditionalExpression":
672 currentNode
= parent
;
676 * If the upper function is IIFE, checks the destination of the return value.
678 * obj.foo = (function() {
680 * return function foo() { ... };
683 * function foo() { ... }
686 case "ReturnStatement": {
687 const func
= getUpperFunction(parent
);
689 if (func
=== null || !isCallee(func
)) {
692 currentNode
= func
.parent
;
695 case "ArrowFunctionExpression":
696 if (currentNode
!== parent
.body
|| !isCallee(parent
)) {
699 currentNode
= parent
.parent
;
704 * var obj = { foo() { ... } };
705 * var obj = { foo: function() { ... } };
706 * class A { constructor() { ... } }
707 * class A { foo() { ... } }
708 * class A { get foo() { ... } }
709 * class A { set foo() { ... } }
710 * class A { static foo() { ... } }
713 case "MethodDefinition":
714 return parent
.value
!== currentNode
;
718 * obj.foo = function foo() { ... };
719 * Foo = function() { ... };
720 * [obj.foo = function foo() { ... }] = a;
721 * [Foo = function() { ... }] = a;
723 case "AssignmentExpression":
724 case "AssignmentPattern":
725 if (parent
.left
.type
=== "MemberExpression") {
731 parent
.left
.type
=== "Identifier" &&
732 startsWithUpperCase(parent
.left
.name
)
740 * var Foo = function() { ... };
742 case "VariableDeclarator":
746 parent
.init
=== currentNode
&&
747 parent
.id
.type
=== "Identifier" &&
748 startsWithUpperCase(parent
.id
.name
)
753 * var foo = function foo() { ... }.bind(obj);
754 * (function foo() { ... }).call(obj);
755 * (function foo() { ... }).apply(obj, []);
757 case "MemberExpression":
759 parent
.object
!== currentNode
||
760 parent
.property
.type
!== "Identifier" ||
761 !bindOrCallOrApplyPattern
.test(parent
.property
.name
) ||
763 parent
.parent
.arguments
.length
=== 0 ||
764 isNullOrUndefined(parent
.parent
.arguments
[0])
769 * Reflect.apply(function() {}, obj, []);
770 * Array.from([], function() {}, obj);
771 * list.forEach(function() {}, obj);
773 case "CallExpression":
774 if (isReflectApply(parent
.callee
)) {
776 parent
.arguments
.length
!== 3 ||
777 parent
.arguments
[0] !== currentNode
||
778 isNullOrUndefined(parent
.arguments
[1])
781 if (isArrayFromMethod(parent
.callee
)) {
783 parent
.arguments
.length
!== 3 ||
784 parent
.arguments
[1] !== currentNode
||
785 isNullOrUndefined(parent
.arguments
[2])
788 if (isMethodWhichHasThisArg(parent
.callee
)) {
790 parent
.arguments
.length
!== 2 ||
791 parent
.arguments
[0] !== currentNode
||
792 isNullOrUndefined(parent
.arguments
[1])
797 // Otherwise `this` is default.
803 /* istanbul ignore next */
808 * Get the precedence level based on the node type
809 * @param {ASTNode} node node to evaluate
810 * @returns {int} precedence level
813 getPrecedence(node
) {
815 case "SequenceExpression":
818 case "AssignmentExpression":
819 case "ArrowFunctionExpression":
820 case "YieldExpression":
823 case "ConditionalExpression":
826 case "LogicalExpression":
827 switch (node
.operator
) {
839 case "BinaryExpression":
841 switch (node
.operator
) {
879 case "UnaryExpression":
880 case "AwaitExpression":
883 case "UpdateExpression":
886 case "CallExpression":
887 case "ImportExpression":
890 case "NewExpression":
899 * Checks whether the given node is an empty block node or not.
900 * @param {ASTNode|null} node The node to check.
901 * @returns {boolean} `true` if the node is an empty block.
904 return Boolean(node
&& node
.type
=== "BlockStatement" && node
.body
.length
=== 0);
908 * Checks whether the given node is an empty function node or not.
909 * @param {ASTNode|null} node The node to check.
910 * @returns {boolean} `true` if the node is an empty function.
912 isEmptyFunction(node
) {
913 return isFunction(node
) && module
.exports
.isEmptyBlock(node
.body
);
917 * Returns the result of the string conversion applied to the evaluated value of the given expression node,
918 * if it can be determined statically.
920 * This function returns a `string` value for all `Literal` nodes and simple `TemplateLiteral` nodes only.
921 * In all other cases, this function returns `null`.
922 * @param {ASTNode} node Expression node.
923 * @returns {string|null} String value if it can be determined. Otherwise, `null`.
925 getStaticStringValue(node
) {
928 if (node
.value
=== null) {
929 if (module
.exports
.isNullLiteral(node
)) {
930 return String(node
.value
); // "null"
933 return `/${node.regex.pattern}/${node.regex.flags}`;
939 // Otherwise, this is an unknown literal. The function will return null.
942 return String(node
.value
);
945 case "TemplateLiteral":
946 if (node
.expressions
.length
=== 0 && node
.quasis
.length
=== 1) {
947 return node
.quasis
[0].value
.cooked
;
958 * Gets the property name of a given node.
959 * The node can be a MemberExpression, a Property, or a MethodDefinition.
961 * If the name is dynamic, this returns `null`.
971 * a["a" + "b"] // => null
972 * a[tag`b`] // => null
973 * a[`${b}`] // => null
975 * let a = {b: 1} // => "b"
976 * let a = {["b"]: 1} // => "b"
977 * let a = {['b']: 1} // => "b"
978 * let a = {[`b`]: 1} // => "b"
979 * let a = {[100]: 1} // => "100"
980 * let a = {[b]: 1} // => null
981 * let a = {["a" + "b"]: 1} // => null
982 * let a = {[tag`b`]: 1} // => null
983 * let a = {[`${b}`]: 1} // => null
984 * @param {ASTNode} node The node to get.
985 * @returns {string|null} The property name if static. Otherwise, null.
987 getStaticPropertyName(node
) {
990 switch (node
&& node
.type
) {
992 case "MethodDefinition":
996 case "MemberExpression":
997 prop
= node
.property
;
1004 if (prop
.type
=== "Identifier" && !node
.computed
) {
1008 return module
.exports
.getStaticStringValue(prop
);
1015 * Get directives from directive prologue of a Program or Function node.
1016 * @param {ASTNode} node The node to check.
1017 * @returns {ASTNode[]} The directives found in the directive prologue.
1019 getDirectivePrologue(node
) {
1020 const directives
= [];
1022 // Directive prologues only occur at the top of files or functions.
1024 node
.type
=== "Program" ||
1025 node
.type
=== "FunctionDeclaration" ||
1026 node
.type
=== "FunctionExpression" ||
1029 * Do not check arrow functions with implicit return.
1030 * `() => "use strict";` returns the string `"use strict"`.
1032 (node
.type
=== "ArrowFunctionExpression" && node
.body
.type
=== "BlockStatement")
1034 const statements
= node
.type
=== "Program" ? node
.body
: node
.body
.body
;
1036 for (const statement
of statements
) {
1038 statement
.type
=== "ExpressionStatement" &&
1039 statement
.expression
.type
=== "Literal"
1041 directives
.push(statement
);
1053 * Determines whether this node is a decimal integer literal. If a node is a decimal integer literal, a dot added
1054 * after the node will be parsed as a decimal point, rather than a property-access dot.
1055 * @param {ASTNode} node The node to check.
1056 * @returns {boolean} `true` if this node is a decimal integer.
1070 isDecimalInteger(node
) {
1071 return node
.type
=== "Literal" && typeof node
.value
=== "number" &&
1072 DECIMAL_INTEGER_PATTERN
.test(node
.raw
);
1076 * Determines whether this token is a decimal integer numeric token.
1077 * This is similar to isDecimalInteger(), but for tokens.
1078 * @param {Token} token The token to check.
1079 * @returns {boolean} `true` if this token is a decimal integer.
1081 isDecimalIntegerNumericToken(token
) {
1082 return token
.type
=== "Numeric" && DECIMAL_INTEGER_PATTERN
.test(token
.value
);
1086 * Gets the name and kind of the given function node.
1088 * - `function foo() {}` .................... `function 'foo'`
1089 * - `(function foo() {})` .................. `function 'foo'`
1090 * - `(function() {})` ...................... `function`
1091 * - `function* foo() {}` ................... `generator function 'foo'`
1092 * - `(function* foo() {})` ................. `generator function 'foo'`
1093 * - `(function*() {})` ..................... `generator function`
1094 * - `() => {}` ............................. `arrow function`
1095 * - `async () => {}` ....................... `async arrow function`
1096 * - `({ foo: function foo() {} })` ......... `method 'foo'`
1097 * - `({ foo: function() {} })` ............. `method 'foo'`
1098 * - `({ ['foo']: function() {} })` ......... `method 'foo'`
1099 * - `({ [foo]: function() {} })` ........... `method`
1100 * - `({ foo() {} })` ....................... `method 'foo'`
1101 * - `({ foo: function* foo() {} })` ........ `generator method 'foo'`
1102 * - `({ foo: function*() {} })` ............ `generator method 'foo'`
1103 * - `({ ['foo']: function*() {} })` ........ `generator method 'foo'`
1104 * - `({ [foo]: function*() {} })` .......... `generator method`
1105 * - `({ *foo() {} })` ...................... `generator method 'foo'`
1106 * - `({ foo: async function foo() {} })` ... `async method 'foo'`
1107 * - `({ foo: async function() {} })` ....... `async method 'foo'`
1108 * - `({ ['foo']: async function() {} })` ... `async method 'foo'`
1109 * - `({ [foo]: async function() {} })` ..... `async method`
1110 * - `({ async foo() {} })` ................. `async method 'foo'`
1111 * - `({ get foo() {} })` ................... `getter 'foo'`
1112 * - `({ set foo(a) {} })` .................. `setter 'foo'`
1113 * - `class A { constructor() {} }` ......... `constructor`
1114 * - `class A { foo() {} }` ................. `method 'foo'`
1115 * - `class A { *foo() {} }` ................ `generator method 'foo'`
1116 * - `class A { async foo() {} }` ........... `async method 'foo'`
1117 * - `class A { ['foo']() {} }` ............. `method 'foo'`
1118 * - `class A { *['foo']() {} }` ............ `generator method 'foo'`
1119 * - `class A { async ['foo']() {} }` ....... `async method 'foo'`
1120 * - `class A { [foo]() {} }` ............... `method`
1121 * - `class A { *[foo]() {} }` .............. `generator method`
1122 * - `class A { async [foo]() {} }` ......... `async method`
1123 * - `class A { get foo() {} }` ............. `getter 'foo'`
1124 * - `class A { set foo(a) {} }` ............ `setter 'foo'`
1125 * - `class A { static foo() {} }` .......... `static method 'foo'`
1126 * - `class A { static *foo() {} }` ......... `static generator method 'foo'`
1127 * - `class A { static async foo() {} }` .... `static async method 'foo'`
1128 * - `class A { static get foo() {} }` ...... `static getter 'foo'`
1129 * - `class A { static set foo(a) {} }` ..... `static setter 'foo'`
1130 * @param {ASTNode} node The function node to get.
1131 * @returns {string} The name and kind of the function node.
1133 getFunctionNameWithKind(node
) {
1134 const parent
= node
.parent
;
1137 if (parent
.type
=== "MethodDefinition" && parent
.static) {
1138 tokens
.push("static");
1141 tokens
.push("async");
1143 if (node
.generator
) {
1144 tokens
.push("generator");
1147 if (node
.type
=== "ArrowFunctionExpression") {
1148 tokens
.push("arrow", "function");
1149 } else if (parent
.type
=== "Property" || parent
.type
=== "MethodDefinition") {
1150 if (parent
.kind
=== "constructor") {
1151 return "constructor";
1153 if (parent
.kind
=== "get") {
1154 tokens
.push("getter");
1155 } else if (parent
.kind
=== "set") {
1156 tokens
.push("setter");
1158 tokens
.push("method");
1161 tokens
.push("function");
1165 tokens
.push(`'${node.id.name}'`);
1167 const name
= module
.exports
.getStaticPropertyName(parent
);
1169 if (name
!== null) {
1170 tokens
.push(`'${name}'`);
1174 return tokens
.join(" ");
1178 * Gets the location of the given function node for reporting.
1180 * - `function foo() {}`
1182 * - `(function foo() {})`
1184 * - `(function() {})`
1186 * - `function* foo() {}`
1188 * - `(function* foo() {})`
1190 * - `(function*() {})`
1194 * - `async () => {}`
1196 * - `({ foo: function foo() {} })`
1198 * - `({ foo: function() {} })`
1200 * - `({ ['foo']: function() {} })`
1202 * - `({ [foo]: function() {} })`
1204 * - `({ foo() {} })`
1206 * - `({ foo: function* foo() {} })`
1207 * ^^^^^^^^^^^^^^^^^^
1208 * - `({ foo: function*() {} })`
1210 * - `({ ['foo']: function*() {} })`
1211 * ^^^^^^^^^^^^^^^^^^
1212 * - `({ [foo]: function*() {} })`
1214 * - `({ *foo() {} })`
1216 * - `({ foo: async function foo() {} })`
1217 * ^^^^^^^^^^^^^^^^^^^^^^^
1218 * - `({ foo: async function() {} })`
1219 * ^^^^^^^^^^^^^^^^^^^
1220 * - `({ ['foo']: async function() {} })`
1221 * ^^^^^^^^^^^^^^^^^^^^^^^
1222 * - `({ [foo]: async function() {} })`
1223 * ^^^^^^^^^^^^^^^^^^^^^
1224 * - `({ async foo() {} })`
1226 * - `({ get foo() {} })`
1228 * - `({ set foo(a) {} })`
1230 * - `class A { constructor() {} }`
1232 * - `class A { foo() {} }`
1234 * - `class A { *foo() {} }`
1236 * - `class A { async foo() {} }`
1238 * - `class A { ['foo']() {} }`
1240 * - `class A { *['foo']() {} }`
1242 * - `class A { async ['foo']() {} }`
1244 * - `class A { [foo]() {} }`
1246 * - `class A { *[foo]() {} }`
1248 * - `class A { async [foo]() {} }`
1250 * - `class A { get foo() {} }`
1252 * - `class A { set foo(a) {} }`
1254 * - `class A { static foo() {} }`
1256 * - `class A { static *foo() {} }`
1258 * - `class A { static async foo() {} }`
1260 * - `class A { static get foo() {} }`
1262 * - `class A { static set foo(a) {} }`
1264 * @param {ASTNode} node The function node to get.
1265 * @param {SourceCode} sourceCode The source code object to get tokens.
1266 * @returns {string} The location of the function node for reporting.
1268 getFunctionHeadLoc(node
, sourceCode
) {
1269 const parent
= node
.parent
;
1273 if (node
.type
=== "ArrowFunctionExpression") {
1274 const arrowToken
= sourceCode
.getTokenBefore(node
.body
, isArrowToken
);
1276 start
= arrowToken
.loc
.start
;
1277 end
= arrowToken
.loc
.end
;
1278 } else if (parent
.type
=== "Property" || parent
.type
=== "MethodDefinition") {
1279 start
= parent
.loc
.start
;
1280 end
= getOpeningParenOfParams(node
, sourceCode
).loc
.start
;
1282 start
= node
.loc
.start
;
1283 end
= getOpeningParenOfParams(node
, sourceCode
).loc
.start
;
1287 start
: Object
.assign({}, start
),
1288 end
: Object
.assign({}, end
)
1293 * Gets next location when the result is not out of bound, otherwise returns null.
1297 * - The given location represents a valid location in the given source code.
1298 * - Columns are 0-based.
1299 * - Lines are 1-based.
1300 * - Column immediately after the last character in a line (not incl. linebreaks) is considered to be a valid location.
1301 * - If the source code ends with a linebreak, `sourceCode.lines` array will have an extra element (empty string) at the end.
1302 * The start (column 0) of that extra line is considered to be a valid location.
1304 * Examples of successive locations (line, column):
1307 * locations: (1, 0) -> (1, 1) -> (1, 2) -> (1, 3) -> null
1310 * locations: (1, 0) -> (1, 1) -> (1, 2) -> (1, 3) -> (2, 0) -> null
1313 * locations: (1, 0) -> (1, 1) -> (1, 2) -> (1, 3) -> (2, 0) -> null
1316 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (2, 1) -> null
1319 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (2, 1) -> (3, 0) -> null
1321 * code: a<CR><LF>b<CR><LF>
1322 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (2, 1) -> (3, 0) -> null
1325 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (3, 0) -> null
1328 * locations: (1, 0) -> (2, 0) -> null
1331 * locations: (1, 0) -> null
1332 * @param {SourceCode} sourceCode The sourceCode
1333 * @param {{line: number, column: number}} location The location
1334 * @returns {{line: number, column: number} | null} Next location
1336 getNextLocation(sourceCode
, { line
, column
}) {
1337 if (column
< sourceCode
.lines
[line
- 1].length
) {
1344 if (line
< sourceCode
.lines
.length
) {
1355 * Gets the parenthesized text of a node. This is similar to sourceCode.getText(node), but it also includes any parentheses
1356 * surrounding the node.
1357 * @param {SourceCode} sourceCode The source code object
1358 * @param {ASTNode} node An expression node
1359 * @returns {string} The text representing the node, with all surrounding parentheses included
1361 getParenthesisedText(sourceCode
, node
) {
1362 let leftToken
= sourceCode
.getFirstToken(node
);
1363 let rightToken
= sourceCode
.getLastToken(node
);
1366 sourceCode
.getTokenBefore(leftToken
) &&
1367 sourceCode
.getTokenBefore(leftToken
).type
=== "Punctuator" &&
1368 sourceCode
.getTokenBefore(leftToken
).value
=== "(" &&
1369 sourceCode
.getTokenAfter(rightToken
) &&
1370 sourceCode
.getTokenAfter(rightToken
).type
=== "Punctuator" &&
1371 sourceCode
.getTokenAfter(rightToken
).value
=== ")"
1373 leftToken
= sourceCode
.getTokenBefore(leftToken
);
1374 rightToken
= sourceCode
.getTokenAfter(rightToken
);
1377 return sourceCode
.getText().slice(leftToken
.range
[0], rightToken
.range
[1]);
1381 * Determine if a node has a possiblity to be an Error object
1382 * @param {ASTNode} node ASTNode to check
1383 * @returns {boolean} True if there is a chance it contains an Error obj
1385 couldBeError(node
) {
1386 switch (node
.type
) {
1388 case "CallExpression":
1389 case "NewExpression":
1390 case "MemberExpression":
1391 case "TaggedTemplateExpression":
1392 case "YieldExpression":
1393 case "AwaitExpression":
1394 return true; // possibly an error object.
1396 case "AssignmentExpression":
1397 return module
.exports
.couldBeError(node
.right
);
1399 case "SequenceExpression": {
1400 const exprs
= node
.expressions
;
1402 return exprs
.length
!== 0 && module
.exports
.couldBeError(exprs
[exprs
.length
- 1]);
1405 case "LogicalExpression":
1406 return module
.exports
.couldBeError(node
.left
) || module
.exports
.couldBeError(node
.right
);
1408 case "ConditionalExpression":
1409 return module
.exports
.couldBeError(node
.consequent
) || module
.exports
.couldBeError(node
.alternate
);
1417 * Determines whether the given node is a `null` literal.
1418 * @param {ASTNode} node The node to check
1419 * @returns {boolean} `true` if the node is a `null` literal
1421 isNullLiteral(node
) {
1424 * Checking `node.value === null` does not guarantee that a literal is a null literal.
1425 * When parsing values that cannot be represented in the current environment (e.g. unicode
1426 * regexes in Node 4), `node.value` is set to `null` because it wouldn't be possible to
1427 * set `node.value` to a unicode regex. To make sure a literal is actually `null`, check
1428 * `node.regex` instead. Also see: https://github.com/eslint/eslint/issues/8020
1430 return node
.type
=== "Literal" && node
.value
=== null && !node
.regex
&& !node
.bigint
;
1434 * Check if a given node is a numeric literal or not.
1435 * @param {ASTNode} node The node to check.
1436 * @returns {boolean} `true` if the node is a number or bigint literal.
1438 isNumericLiteral(node
) {
1440 node
.type
=== "Literal" &&
1441 (typeof node
.value
=== "number" || Boolean(node
.bigint
))
1446 * Determines whether two tokens can safely be placed next to each other without merging into a single token
1447 * @param {Token|string} leftValue The left token. If this is a string, it will be tokenized and the last token will be used.
1448 * @param {Token|string} rightValue The right token. If this is a string, it will be tokenized and the first token will be used.
1449 * @returns {boolean} If the tokens cannot be safely placed next to each other, returns `false`. If the tokens can be placed
1450 * next to each other, behavior is undefined (although it should return `true` in most cases).
1452 canTokensBeAdjacent(leftValue
, rightValue
) {
1453 const espreeOptions
= {
1454 ecmaVersion
: espree
.latestEcmaVersion
,
1461 if (typeof leftValue
=== "string") {
1465 tokens
= espree
.tokenize(leftValue
, espreeOptions
);
1470 const comments
= tokens
.comments
;
1472 leftToken
= tokens
[tokens
.length
- 1];
1473 if (comments
.length
) {
1474 const lastComment
= comments
[comments
.length
- 1];
1476 if (lastComment
.range
[0] > leftToken
.range
[0]) {
1477 leftToken
= lastComment
;
1481 leftToken
= leftValue
;
1484 if (leftToken
.type
=== "Shebang") {
1490 if (typeof rightValue
=== "string") {
1494 tokens
= espree
.tokenize(rightValue
, espreeOptions
);
1499 const comments
= tokens
.comments
;
1501 rightToken
= tokens
[0];
1502 if (comments
.length
) {
1503 const firstComment
= comments
[0];
1505 if (firstComment
.range
[0] < rightToken
.range
[0]) {
1506 rightToken
= firstComment
;
1510 rightToken
= rightValue
;
1513 if (leftToken
.type
=== "Punctuator" || rightToken
.type
=== "Punctuator") {
1514 if (leftToken
.type
=== "Punctuator" && rightToken
.type
=== "Punctuator") {
1515 const PLUS_TOKENS
= new Set(["+", "++"]);
1516 const MINUS_TOKENS
= new Set(["-", "--"]);
1519 PLUS_TOKENS
.has(leftToken
.value
) && PLUS_TOKENS
.has(rightToken
.value
) ||
1520 MINUS_TOKENS
.has(leftToken
.value
) && MINUS_TOKENS
.has(rightToken
.value
)
1523 if (leftToken
.type
=== "Punctuator" && leftToken
.value
=== "/") {
1524 return !["Block", "Line", "RegularExpression"].includes(rightToken
.type
);
1530 leftToken
.type
=== "String" || rightToken
.type
=== "String" ||
1531 leftToken
.type
=== "Template" || rightToken
.type
=== "Template"
1536 if (leftToken
.type
!== "Numeric" && rightToken
.type
=== "Numeric" && rightToken
.value
.startsWith(".")) {
1540 if (leftToken
.type
=== "Block" || rightToken
.type
=== "Block" || rightToken
.type
=== "Line") {
1548 * Get the `loc` object of a given name in a `/*globals` directive comment.
1549 * @param {SourceCode} sourceCode The source code to convert index to loc.
1550 * @param {Comment} comment The `/*globals` directive comment which include the name.
1551 * @param {string} name The name to find.
1552 * @returns {SourceLocation} The `loc` object.
1554 getNameLocationInGlobalDirectiveComment(sourceCode
, comment
, name
) {
1555 const namePattern
= new RegExp(`[\\s,]${lodash.escapeRegExp(name)}(?:$|[\\s,:])`, "gu");
1557 // To ignore the first text "global".
1558 namePattern
.lastIndex
= comment
.value
.indexOf("global") + 6;
1560 // Search a given variable name.
1561 const match
= namePattern
.exec(comment
.value
);
1563 // Convert the index to loc.
1564 const start
= sourceCode
.getLocFromIndex(
1567 (match
? match
.index
+ 1 : 0)
1571 column
: start
.column
+ (match
? name
.length
: 1)
1574 return { start
, end
};
1578 * Determines whether the given raw string contains an octal escape sequence.
1580 * "\1", "\2" ... "\7"
1581 * "\00", "\01" ... "\09"
1583 * "\0", when not followed by a digit, is not an octal escape sequence.
1584 * @param {string} rawString A string in its raw representation.
1585 * @returns {boolean} `true` if the string contains at least one octal escape sequence.
1587 hasOctalEscapeSequence(rawString
) {
1588 return OCTAL_ESCAPE_PATTERN
.test(rawString
);
1591 isLogicalExpression
,
1592 isCoalesceExpression
,
1593 isMixedLogicalAndCoalesceExpressions