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