UNPKG

62.6 kBJavaScriptView Raw
1/**
2 * @fileoverview Common utils for AST.
3 * @author Gyandeep Singh
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Requirements
10//------------------------------------------------------------------------------
11
12const esutils = require("esutils");
13const espree = require("espree");
14const lodash = require("lodash");
15const {
16 breakableTypePattern,
17 createGlobalLinebreakMatcher,
18 lineBreakPattern,
19 shebangPattern
20} = require("../../shared/ast-utils");
21
22//------------------------------------------------------------------------------
23// Helpers
24//------------------------------------------------------------------------------
25
26const anyFunctionPattern = /^(?:Function(?:Declaration|Expression)|ArrowFunctionExpression)$/u;
27const anyLoopPattern = /^(?:DoWhile|For|ForIn|ForOf|While)Statement$/u;
28const arrayOrTypedArrayPattern = /Array$/u;
29const arrayMethodPattern = /^(?:every|filter|find|findIndex|forEach|map|some)$/u;
30const bindOrCallOrApplyPattern = /^(?:bind|call|apply)$/u;
31const thisTagPattern = /^[\s*]*@this/mu;
32
33
34const COMMENTS_IGNORE_PATTERN = /^\s*(?:eslint|jshint\s+|jslint\s+|istanbul\s+|globals?\s+|exported\s+|jscs)/u;
35const LINEBREAKS = new Set(["\r\n", "\r", "\n", "\u2028", "\u2029"]);
36
37// A set of node types that can contain a list of statements
38const STATEMENT_LIST_PARENTS = new Set(["Program", "BlockStatement", "SwitchCase"]);
39
40const DECIMAL_INTEGER_PATTERN = /^(?:0|0[0-7]*[89]\d*|[1-9](?:_?\d)*)$/u;
41
42// Tests the presence of at least one LegacyOctalEscapeSequence or NonOctalDecimalEscapeSequence in a raw string
43const OCTAL_OR_NON_OCTAL_DECIMAL_ESCAPE_PATTERN = /^(?:[^\\]|\\.)*\\(?:[1-9]|0[0-9])/su;
44
45const LOGICAL_ASSIGNMENT_OPERATORS = new Set(["&&=", "||=", "??="]);
46
47/**
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
53 * @private
54 */
55function isModifyingReference(reference, index, references) {
56 const identifier = reference.identifier;
57
58 /*
59 * Destructuring assignments can have multiple default value, so
60 * possibly there are multiple writeable references for the same
61 * identifier.
62 */
63 const modifyingDifferentIdentifier = index === 0 ||
64 references[index - 1].identifier !== identifier;
65
66 return (identifier &&
67 reference.init === false &&
68 reference.isWrite() &&
69 modifyingDifferentIdentifier
70 );
71}
72
73/**
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.
77 */
78function startsWithUpperCase(s) {
79 return s[0] !== s[0].toLocaleLowerCase();
80}
81
82/**
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.
86 */
87function isES5Constructor(node) {
88 return (node.id && startsWithUpperCase(node.id.name));
89}
90
91/**
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.
95 */
96function getUpperFunction(node) {
97 for (let currentNode = node; currentNode; currentNode = currentNode.parent) {
98 if (anyFunctionPattern.test(currentNode.type)) {
99 return currentNode;
100 }
101 }
102 return null;
103}
104
105/**
106 * Checks whether a given node is a function node or not.
107 * The following types are function nodes:
108 *
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.
114 */
115function isFunction(node) {
116 return Boolean(node && anyFunctionPattern.test(node.type));
117}
118
119/**
120 * Checks whether a given node is a loop node or not.
121 * The following types are loop nodes:
122 *
123 * - DoWhileStatement
124 * - ForInStatement
125 * - ForOfStatement
126 * - ForStatement
127 * - WhileStatement
128 * @param {ASTNode|null} node A node to check.
129 * @returns {boolean} `true` if the node is a loop node.
130 */
131function isLoop(node) {
132 return Boolean(node && anyLoopPattern.test(node.type));
133}
134
135/**
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.
139 */
140function isInLoop(node) {
141 for (let currentNode = node; currentNode && !isFunction(currentNode); currentNode = currentNode.parent) {
142 if (isLoop(currentNode)) {
143 return true;
144 }
145 }
146
147 return false;
148}
149
150/**
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
154 */
155function isNullLiteral(node) {
156
157 /*
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
163 */
164 return node.type === "Literal" && node.value === null && !node.regex && !node.bigint;
165}
166
167/**
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`.
171 * @public
172 */
173function isNullOrUndefined(node) {
174 return (
175 isNullLiteral(node) ||
176 (node.type === "Identifier" && node.name === "undefined") ||
177 (node.type === "UnaryExpression" && node.operator === "void")
178 );
179}
180
181/**
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.
185 */
186function isCallee(node) {
187 return node.parent.type === "CallExpression" && node.parent.callee === node;
188}
189
190/**
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.
193 *
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`.
198 */
199function getStaticStringValue(node) {
200 switch (node.type) {
201 case "Literal":
202 if (node.value === null) {
203 if (isNullLiteral(node)) {
204 return String(node.value); // "null"
205 }
206 if (node.regex) {
207 return `/${node.regex.pattern}/${node.regex.flags}`;
208 }
209 if (node.bigint) {
210 return node.bigint;
211 }
212
213 // Otherwise, this is an unknown literal. The function will return null.
214
215 } else {
216 return String(node.value);
217 }
218 break;
219 case "TemplateLiteral":
220 if (node.expressions.length === 0 && node.quasis.length === 1) {
221 return node.quasis[0].value.cooked;
222 }
223 break;
224
225 // no default
226 }
227
228 return null;
229}
230
231/**
232 * Gets the property name of a given node.
233 * The node can be a MemberExpression, a Property, or a MethodDefinition.
234 *
235 * If the name is dynamic, this returns `null`.
236 *
237 * For examples:
238 *
239 * a.b // => "b"
240 * a["b"] // => "b"
241 * a['b'] // => "b"
242 * a[`b`] // => "b"
243 * a[100] // => "100"
244 * a[b] // => null
245 * a["a" + "b"] // => null
246 * a[tag`b`] // => null
247 * a[`${b}`] // => null
248 *
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.
260 */
261function getStaticPropertyName(node) {
262 let prop;
263
264 switch (node && node.type) {
265 case "ChainExpression":
266 return getStaticPropertyName(node.expression);
267
268 case "Property":
269 case "MethodDefinition":
270 prop = node.key;
271 break;
272
273 case "MemberExpression":
274 prop = node.property;
275 break;
276
277 // no default
278 }
279
280 if (prop) {
281 if (prop.type === "Identifier" && !node.computed) {
282 return prop.name;
283 }
284
285 return getStaticStringValue(prop);
286 }
287
288 return null;
289}
290
291/**
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.
295 */
296function skipChainExpression(node) {
297 return node && node.type === "ChainExpression" ? node.expression : node;
298}
299
300/**
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.
305 */
306function checkText(actual, expected) {
307 return typeof expected === "string"
308 ? actual === expected
309 : expected.test(actual);
310}
311
312/**
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.
317 */
318function isSpecificId(node, name) {
319 return node.type === "Identifier" && checkText(node.name, name);
320}
321
322/**
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`.
330 */
331function isSpecificMemberAccess(node, objectName, propertyName) {
332 const checkNode = skipChainExpression(node);
333
334 if (checkNode.type !== "MemberExpression") {
335 return false;
336 }
337
338 if (objectName && !isSpecificId(checkNode.object, objectName)) {
339 return false;
340 }
341
342 if (propertyName) {
343 const actualPropertyName = getStaticPropertyName(checkNode);
344
345 if (typeof actualPropertyName !== "string" || !checkText(actualPropertyName, propertyName)) {
346 return false;
347 }
348 }
349
350 return true;
351}
352
353/**
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.
358 */
359function equalLiteralValue(left, right) {
360
361 // RegExp literal.
362 if (left.regex || right.regex) {
363 return Boolean(
364 left.regex &&
365 right.regex &&
366 left.regex.pattern === right.regex.pattern &&
367 left.regex.flags === right.regex.flags
368 );
369 }
370
371 // BigInt literal.
372 if (left.bigint || right.bigint) {
373 return left.bigint === right.bigint;
374 }
375
376 return left.value === right.value;
377}
378
379/**
380 * Check if two expressions reference the same value. For example:
381 * a = a
382 * a.b = a.b
383 * a[0] = a[0]
384 * a['b'] = a['b']
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.
389 */
390function isSameReference(left, right, disableStaticComputedKey = false) {
391 if (left.type !== right.type) {
392
393 // Handle `a.b` and `a?.b` are samely.
394 if (left.type === "ChainExpression") {
395 return isSameReference(left.expression, right, disableStaticComputedKey);
396 }
397 if (right.type === "ChainExpression") {
398 return isSameReference(left, right.expression, disableStaticComputedKey);
399 }
400
401 return false;
402 }
403
404 switch (left.type) {
405 case "Super":
406 case "ThisExpression":
407 return true;
408
409 case "Identifier":
410 return left.name === right.name;
411 case "Literal":
412 return equalLiteralValue(left, right);
413
414 case "ChainExpression":
415 return isSameReference(left.expression, right.expression, disableStaticComputedKey);
416
417 case "MemberExpression": {
418 if (!disableStaticComputedKey) {
419 const nameA = getStaticPropertyName(left);
420
421 // x.y = x["y"]
422 if (nameA !== null) {
423 return (
424 isSameReference(left.object, right.object, disableStaticComputedKey) &&
425 nameA === getStaticPropertyName(right)
426 );
427 }
428 }
429
430 /*
431 * x[0] = x[0]
432 * x[y] = x[y]
433 * x.y = x.y
434 */
435 return (
436 left.computed === right.computed &&
437 isSameReference(left.object, right.object, disableStaticComputedKey) &&
438 isSameReference(left.property, right.property, disableStaticComputedKey)
439 );
440 }
441
442 default:
443 return false;
444 }
445}
446
447/**
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`.
451 */
452function isReflectApply(node) {
453 return isSpecificMemberAccess(node, "Reflect", "apply");
454}
455
456/**
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`.
460 */
461function isArrayFromMethod(node) {
462 return isSpecificMemberAccess(node, arrayOrTypedArrayPattern, "from");
463}
464
465/**
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`.
469 */
470function isMethodWhichHasThisArg(node) {
471 return isSpecificMemberAccess(node, null, arrayMethodPattern);
472}
473
474/**
475 * Creates the negate function of the given function.
476 * @param {Function} f The function to negate.
477 * @returns {Function} Negated function.
478 */
479function negate(f) {
480 return token => !f(token);
481}
482
483/**
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.
488 */
489function hasJSDocThisTag(node, sourceCode) {
490 const jsdocComment = sourceCode.getJSDocComment(node);
491
492 if (jsdocComment && thisTagPattern.test(jsdocComment.value)) {
493 return true;
494 }
495
496 // Checks `@this` in its leading comments for callbacks,
497 // because callbacks don't have its JSDoc comment.
498 // e.g.
499 // sinon.test(/* @this sinon.Sandbox */function() { this.spy(); });
500 return sourceCode.getCommentsBefore(node).some(comment => thisTagPattern.test(comment.value));
501}
502
503/**
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.
508 * @private
509 */
510function isParenthesised(sourceCode, node) {
511 const previousToken = sourceCode.getTokenBefore(node),
512 nextToken = sourceCode.getTokenAfter(node);
513
514 return Boolean(previousToken && nextToken) &&
515 previousToken.value === "(" && previousToken.range[1] <= node.range[0] &&
516 nextToken.value === ")" && nextToken.range[0] >= node.range[1];
517}
518
519/**
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.
523 */
524function isArrowToken(token) {
525 return token.value === "=>" && token.type === "Punctuator";
526}
527
528/**
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.
532 */
533function isCommaToken(token) {
534 return token.value === "," && token.type === "Punctuator";
535}
536
537/**
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.
541 */
542function isDotToken(token) {
543 return token.value === "." && token.type === "Punctuator";
544}
545
546/**
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.
550 */
551function isQuestionDotToken(token) {
552 return token.value === "?." && token.type === "Punctuator";
553}
554
555/**
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.
559 */
560function isSemicolonToken(token) {
561 return token.value === ";" && token.type === "Punctuator";
562}
563
564/**
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.
568 */
569function isColonToken(token) {
570 return token.value === ":" && token.type === "Punctuator";
571}
572
573/**
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.
577 */
578function isOpeningParenToken(token) {
579 return token.value === "(" && token.type === "Punctuator";
580}
581
582/**
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.
586 */
587function isClosingParenToken(token) {
588 return token.value === ")" && token.type === "Punctuator";
589}
590
591/**
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.
595 */
596function isOpeningBracketToken(token) {
597 return token.value === "[" && token.type === "Punctuator";
598}
599
600/**
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.
604 */
605function isClosingBracketToken(token) {
606 return token.value === "]" && token.type === "Punctuator";
607}
608
609/**
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.
613 */
614function isOpeningBraceToken(token) {
615 return token.value === "{" && token.type === "Punctuator";
616}
617
618/**
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.
622 */
623function isClosingBraceToken(token) {
624 return token.value === "}" && token.type === "Punctuator";
625}
626
627/**
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.
631 */
632function isCommentToken(token) {
633 return token.type === "Line" || token.type === "Block" || token.type === "Shebang";
634}
635
636/**
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.
640 */
641function isKeywordToken(token) {
642 return token.type === "Keyword";
643}
644
645/**
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.
650 */
651function getOpeningParenOfParams(node, sourceCode) {
652 return node.id
653 ? sourceCode.getTokenAfter(node.id, isOpeningParenToken)
654 : sourceCode.getFirstToken(node, isOpeningParenToken);
655}
656
657/**
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.
663 */
664function equalTokens(left, right, sourceCode) {
665 const tokensL = sourceCode.getTokens(left);
666 const tokensR = sourceCode.getTokens(right);
667
668 if (tokensL.length !== tokensR.length) {
669 return false;
670 }
671 for (let i = 0; i < tokensL.length; ++i) {
672 if (tokensL[i].type !== tokensR[i].type ||
673 tokensL[i].value !== tokensR[i].value
674 ) {
675 return false;
676 }
677 }
678
679 return true;
680}
681
682/**
683 * Check if the given node is a true logical expression or not.
684 *
685 * The three binary expressions logical-or (`||`), logical-and (`&&`), and
686 * coalesce (`??`) are known as `ShortCircuitExpression`.
687 * But ESTree represents those by `LogicalExpression` node.
688 *
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
693 */
694function isLogicalExpression(node) {
695 return (
696 node.type === "LogicalExpression" &&
697 (node.operator === "&&" || node.operator === "||")
698 );
699}
700
701/**
702 * Check if the given node is a nullish coalescing expression or not.
703 *
704 * The three binary expressions logical-or (`||`), logical-and (`&&`), and
705 * coalesce (`??`) are known as `ShortCircuitExpression`.
706 * But ESTree represents those by `LogicalExpression` node.
707 *
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 `??`.
711 */
712function isCoalesceExpression(node) {
713 return node.type === "LogicalExpression" && node.operator === "??";
714}
715
716/**
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.
721 */
722function isMixedLogicalAndCoalesceExpressions(left, right) {
723 return (
724 (isLogicalExpression(left) && isCoalesceExpression(right)) ||
725 (isCoalesceExpression(left) && isLogicalExpression(right))
726 );
727}
728
729/**
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.
733 */
734function isLogicalAssignmentOperator(operator) {
735 return LOGICAL_ASSIGNMENT_OPERATORS.has(operator);
736}
737
738//------------------------------------------------------------------------------
739// Public Interface
740//------------------------------------------------------------------------------
741
742module.exports = {
743 COMMENTS_IGNORE_PATTERN,
744 LINEBREAKS,
745 LINEBREAK_MATCHER: lineBreakPattern,
746 SHEBANG_MATCHER: shebangPattern,
747 STATEMENT_LIST_PARENTS,
748
749 /**
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.
754 * @public
755 */
756 isTokenOnSameLine(left, right) {
757 return left.loc.end.line === right.loc.start.line;
758 },
759
760 isNullOrUndefined,
761 isCallee,
762 isES5Constructor,
763 getUpperFunction,
764 isFunction,
765 isLoop,
766 isInLoop,
767 isArrayFromMethod,
768 isParenthesised,
769 createGlobalLinebreakMatcher,
770 equalTokens,
771
772 isArrowToken,
773 isClosingBraceToken,
774 isClosingBracketToken,
775 isClosingParenToken,
776 isColonToken,
777 isCommaToken,
778 isCommentToken,
779 isDotToken,
780 isQuestionDotToken,
781 isKeywordToken,
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),
793 isOpeningBraceToken,
794 isOpeningBracketToken,
795 isOpeningParenToken,
796 isSemicolonToken,
797
798 /**
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.
802 */
803 isStringLiteral(node) {
804 return (
805 (node.type === "Literal" && typeof node.value === "string") ||
806 node.type === "TemplateLiteral"
807 );
808 },
809
810 /**
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:
813 *
814 * - DoWhileStatement
815 * - ForInStatement
816 * - ForOfStatement
817 * - ForStatement
818 * - SwitchStatement
819 * - WhileStatement
820 * @param {ASTNode} node A node to check.
821 * @returns {boolean} `true` if the node is breakable.
822 */
823 isBreakableStatement(node) {
824 return breakableTypePattern.test(node.type);
825 },
826
827 /**
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.
831 * @public
832 */
833 getModifyingReferences(references) {
834 return references.filter(isModifyingReference);
835 },
836
837 /**
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.
842 * @private
843 */
844 isSurroundedBy(val, character) {
845 return val[0] === character && val[val.length - 1] === character;
846 },
847
848 /**
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
852 */
853 isDirectiveComment(node) {
854 const comment = node.value.trim();
855
856 return (
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
862 )
863 );
864 },
865
866 /**
867 * Gets the trailing statement of a given node.
868 *
869 * if (code)
870 * consequent;
871 *
872 * When taking this `IfStatement`, returns `consequent;` statement.
873 * @param {ASTNode} A node to get.
874 * @returns {ASTNode|null} The trailing statement's node.
875 */
876 getTrailingStatement: esutils.ast.trailingStatement,
877
878 /**
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`.
883 */
884 getVariableByName(initScope, name) {
885 let scope = initScope;
886
887 while (scope) {
888 const variable = scope.set.get(name);
889
890 if (variable) {
891 return variable;
892 }
893
894 scope = scope.upper;
895 }
896
897 return null;
898 },
899
900 /**
901 * Checks whether or not a given function node is the default `this` binding.
902 *
903 * First, this checks the node:
904 *
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.
908 *
909 * Next, this checks the location of the node.
910 * If the location is below, this judges `this` is valid.
911 *
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.
924 */
925 isDefaultThisBinding(node, sourceCode, { capIsConstructor = true } = {}) {
926 if (
927 (capIsConstructor && isES5Constructor(node)) ||
928 hasJSDocThisTag(node, sourceCode)
929 ) {
930 return false;
931 }
932 const isAnonymous = node.id === null;
933 let currentNode = node;
934
935 while (currentNode) {
936 const parent = currentNode.parent;
937
938 switch (parent.type) {
939
940 /*
941 * Looks up the destination.
942 * e.g., obj.foo = nativeFoo || function foo() { ... };
943 */
944 case "LogicalExpression":
945 case "ConditionalExpression":
946 case "ChainExpression":
947 currentNode = parent;
948 break;
949
950 /*
951 * If the upper function is IIFE, checks the destination of the return value.
952 * e.g.
953 * obj.foo = (function() {
954 * // setup...
955 * return function foo() { ... };
956 * })();
957 * obj.foo = (() =>
958 * function foo() { ... }
959 * )();
960 */
961 case "ReturnStatement": {
962 const func = getUpperFunction(parent);
963
964 if (func === null || !isCallee(func)) {
965 return true;
966 }
967 currentNode = func.parent;
968 break;
969 }
970 case "ArrowFunctionExpression":
971 if (currentNode !== parent.body || !isCallee(parent)) {
972 return true;
973 }
974 currentNode = parent.parent;
975 break;
976
977 /*
978 * e.g.
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() { ... } }
986 */
987 case "Property":
988 case "MethodDefinition":
989 return parent.value !== currentNode;
990
991 /*
992 * e.g.
993 * obj.foo = function foo() { ... };
994 * Foo = function() { ... };
995 * [obj.foo = function foo() { ... }] = a;
996 * [Foo = function() { ... }] = a;
997 */
998 case "AssignmentExpression":
999 case "AssignmentPattern":
1000 if (parent.left.type === "MemberExpression") {
1001 return false;
1002 }
1003 if (
1004 capIsConstructor &&
1005 isAnonymous &&
1006 parent.left.type === "Identifier" &&
1007 startsWithUpperCase(parent.left.name)
1008 ) {
1009 return false;
1010 }
1011 return true;
1012
1013 /*
1014 * e.g.
1015 * var Foo = function() { ... };
1016 */
1017 case "VariableDeclarator":
1018 return !(
1019 capIsConstructor &&
1020 isAnonymous &&
1021 parent.init === currentNode &&
1022 parent.id.type === "Identifier" &&
1023 startsWithUpperCase(parent.id.name)
1024 );
1025
1026 /*
1027 * e.g.
1028 * var foo = function foo() { ... }.bind(obj);
1029 * (function foo() { ... }).call(obj);
1030 * (function foo() { ... }).apply(obj, []);
1031 */
1032 case "MemberExpression":
1033 if (
1034 parent.object === currentNode &&
1035 isSpecificMemberAccess(parent, null, bindOrCallOrApplyPattern)
1036 ) {
1037 const maybeCalleeNode = parent.parent.type === "ChainExpression"
1038 ? parent.parent
1039 : parent;
1040
1041 return !(
1042 isCallee(maybeCalleeNode) &&
1043 maybeCalleeNode.parent.arguments.length >= 1 &&
1044 !isNullOrUndefined(maybeCalleeNode.parent.arguments[0])
1045 );
1046 }
1047 return true;
1048
1049 /*
1050 * e.g.
1051 * Reflect.apply(function() {}, obj, []);
1052 * Array.from([], function() {}, obj);
1053 * list.forEach(function() {}, obj);
1054 */
1055 case "CallExpression":
1056 if (isReflectApply(parent.callee)) {
1057 return (
1058 parent.arguments.length !== 3 ||
1059 parent.arguments[0] !== currentNode ||
1060 isNullOrUndefined(parent.arguments[1])
1061 );
1062 }
1063 if (isArrayFromMethod(parent.callee)) {
1064 return (
1065 parent.arguments.length !== 3 ||
1066 parent.arguments[1] !== currentNode ||
1067 isNullOrUndefined(parent.arguments[2])
1068 );
1069 }
1070 if (isMethodWhichHasThisArg(parent.callee)) {
1071 return (
1072 parent.arguments.length !== 2 ||
1073 parent.arguments[0] !== currentNode ||
1074 isNullOrUndefined(parent.arguments[1])
1075 );
1076 }
1077 return true;
1078
1079 // Otherwise `this` is default.
1080 default:
1081 return true;
1082 }
1083 }
1084
1085 /* istanbul ignore next */
1086 return true;
1087 },
1088
1089 /**
1090 * Get the precedence level based on the node type
1091 * @param {ASTNode} node node to evaluate
1092 * @returns {int} precedence level
1093 * @private
1094 */
1095 getPrecedence(node) {
1096 switch (node.type) {
1097 case "SequenceExpression":
1098 return 0;
1099
1100 case "AssignmentExpression":
1101 case "ArrowFunctionExpression":
1102 case "YieldExpression":
1103 return 1;
1104
1105 case "ConditionalExpression":
1106 return 3;
1107
1108 case "LogicalExpression":
1109 switch (node.operator) {
1110 case "||":
1111 case "??":
1112 return 4;
1113 case "&&":
1114 return 5;
1115
1116 // no default
1117 }
1118
1119 /* falls through */
1120
1121 case "BinaryExpression":
1122
1123 switch (node.operator) {
1124 case "|":
1125 return 6;
1126 case "^":
1127 return 7;
1128 case "&":
1129 return 8;
1130 case "==":
1131 case "!=":
1132 case "===":
1133 case "!==":
1134 return 9;
1135 case "<":
1136 case "<=":
1137 case ">":
1138 case ">=":
1139 case "in":
1140 case "instanceof":
1141 return 10;
1142 case "<<":
1143 case ">>":
1144 case ">>>":
1145 return 11;
1146 case "+":
1147 case "-":
1148 return 12;
1149 case "*":
1150 case "/":
1151 case "%":
1152 return 13;
1153 case "**":
1154 return 15;
1155
1156 // no default
1157 }
1158
1159 /* falls through */
1160
1161 case "UnaryExpression":
1162 case "AwaitExpression":
1163 return 16;
1164
1165 case "UpdateExpression":
1166 return 17;
1167
1168 case "CallExpression":
1169 case "ChainExpression":
1170 case "ImportExpression":
1171 return 18;
1172
1173 case "NewExpression":
1174 return 19;
1175
1176 default:
1177 return 20;
1178 }
1179 },
1180
1181 /**
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.
1185 */
1186 isEmptyBlock(node) {
1187 return Boolean(node && node.type === "BlockStatement" && node.body.length === 0);
1188 },
1189
1190 /**
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.
1194 */
1195 isEmptyFunction(node) {
1196 return isFunction(node) && module.exports.isEmptyBlock(node.body);
1197 },
1198
1199 /**
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.
1203 */
1204 getDirectivePrologue(node) {
1205 const directives = [];
1206
1207 // Directive prologues only occur at the top of files or functions.
1208 if (
1209 node.type === "Program" ||
1210 node.type === "FunctionDeclaration" ||
1211 node.type === "FunctionExpression" ||
1212
1213 /*
1214 * Do not check arrow functions with implicit return.
1215 * `() => "use strict";` returns the string `"use strict"`.
1216 */
1217 (node.type === "ArrowFunctionExpression" && node.body.type === "BlockStatement")
1218 ) {
1219 const statements = node.type === "Program" ? node.body : node.body.body;
1220
1221 for (const statement of statements) {
1222 if (
1223 statement.type === "ExpressionStatement" &&
1224 statement.expression.type === "Literal"
1225 ) {
1226 directives.push(statement);
1227 } else {
1228 break;
1229 }
1230 }
1231 }
1232
1233 return directives;
1234 },
1235
1236
1237 /**
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.
1242 * @example
1243 *
1244 * 0 // true
1245 * 5 // true
1246 * 50 // true
1247 * 5_000 // true
1248 * 1_234_56 // true
1249 * 08 // true
1250 * 0192 // true
1251 * 5. // false
1252 * .5 // false
1253 * 5.0 // false
1254 * 5.00_00 // false
1255 * 05 // false
1256 * 0x5 // false
1257 * 0b101 // false
1258 * 0b11_01 // false
1259 * 0o5 // false
1260 * 5e0 // false
1261 * 5e1_000 // false
1262 * 5n // false
1263 * 1_000n // false
1264 * '5' // false
1265 */
1266 isDecimalInteger(node) {
1267 return node.type === "Literal" && typeof node.value === "number" &&
1268 DECIMAL_INTEGER_PATTERN.test(node.raw);
1269 },
1270
1271 /**
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.
1276 */
1277 isDecimalIntegerNumericToken(token) {
1278 return token.type === "Numeric" && DECIMAL_INTEGER_PATTERN.test(token.value);
1279 },
1280
1281 /**
1282 * Gets the name and kind of the given function node.
1283 *
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.
1328 */
1329 getFunctionNameWithKind(node) {
1330 const parent = node.parent;
1331 const tokens = [];
1332
1333 if (parent.type === "MethodDefinition" && parent.static) {
1334 tokens.push("static");
1335 }
1336 if (node.async) {
1337 tokens.push("async");
1338 }
1339 if (node.generator) {
1340 tokens.push("generator");
1341 }
1342
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";
1348 }
1349 if (parent.kind === "get") {
1350 tokens.push("getter");
1351 } else if (parent.kind === "set") {
1352 tokens.push("setter");
1353 } else {
1354 tokens.push("method");
1355 }
1356 } else {
1357 tokens.push("function");
1358 }
1359
1360 if (node.id) {
1361 tokens.push(`'${node.id.name}'`);
1362 } else {
1363 const name = getStaticPropertyName(parent);
1364
1365 if (name !== null) {
1366 tokens.push(`'${name}'`);
1367 }
1368 }
1369
1370 return tokens.join(" ");
1371 },
1372
1373 /**
1374 * Gets the location of the given function node for reporting.
1375 *
1376 * - `function foo() {}`
1377 * ^^^^^^^^^^^^
1378 * - `(function foo() {})`
1379 * ^^^^^^^^^^^^
1380 * - `(function() {})`
1381 * ^^^^^^^^
1382 * - `function* foo() {}`
1383 * ^^^^^^^^^^^^^
1384 * - `(function* foo() {})`
1385 * ^^^^^^^^^^^^^
1386 * - `(function*() {})`
1387 * ^^^^^^^^^
1388 * - `() => {}`
1389 * ^^
1390 * - `async () => {}`
1391 * ^^
1392 * - `({ foo: function foo() {} })`
1393 * ^^^^^^^^^^^^^^^^^
1394 * - `({ foo: function() {} })`
1395 * ^^^^^^^^^^^^^
1396 * - `({ ['foo']: function() {} })`
1397 * ^^^^^^^^^^^^^^^^^
1398 * - `({ [foo]: function() {} })`
1399 * ^^^^^^^^^^^^^^^
1400 * - `({ foo() {} })`
1401 * ^^^
1402 * - `({ foo: function* foo() {} })`
1403 * ^^^^^^^^^^^^^^^^^^
1404 * - `({ foo: function*() {} })`
1405 * ^^^^^^^^^^^^^^
1406 * - `({ ['foo']: function*() {} })`
1407 * ^^^^^^^^^^^^^^^^^^
1408 * - `({ [foo]: function*() {} })`
1409 * ^^^^^^^^^^^^^^^^
1410 * - `({ *foo() {} })`
1411 * ^^^^
1412 * - `({ foo: async function foo() {} })`
1413 * ^^^^^^^^^^^^^^^^^^^^^^^
1414 * - `({ foo: async function() {} })`
1415 * ^^^^^^^^^^^^^^^^^^^
1416 * - `({ ['foo']: async function() {} })`
1417 * ^^^^^^^^^^^^^^^^^^^^^^^
1418 * - `({ [foo]: async function() {} })`
1419 * ^^^^^^^^^^^^^^^^^^^^^
1420 * - `({ async foo() {} })`
1421 * ^^^^^^^^^
1422 * - `({ get foo() {} })`
1423 * ^^^^^^^
1424 * - `({ set foo(a) {} })`
1425 * ^^^^^^^
1426 * - `class A { constructor() {} }`
1427 * ^^^^^^^^^^^
1428 * - `class A { foo() {} }`
1429 * ^^^
1430 * - `class A { *foo() {} }`
1431 * ^^^^
1432 * - `class A { async foo() {} }`
1433 * ^^^^^^^^^
1434 * - `class A { ['foo']() {} }`
1435 * ^^^^^^^
1436 * - `class A { *['foo']() {} }`
1437 * ^^^^^^^^
1438 * - `class A { async ['foo']() {} }`
1439 * ^^^^^^^^^^^^^
1440 * - `class A { [foo]() {} }`
1441 * ^^^^^
1442 * - `class A { *[foo]() {} }`
1443 * ^^^^^^
1444 * - `class A { async [foo]() {} }`
1445 * ^^^^^^^^^^^
1446 * - `class A { get foo() {} }`
1447 * ^^^^^^^
1448 * - `class A { set foo(a) {} }`
1449 * ^^^^^^^
1450 * - `class A { static foo() {} }`
1451 * ^^^^^^^^^^
1452 * - `class A { static *foo() {} }`
1453 * ^^^^^^^^^^^
1454 * - `class A { static async foo() {} }`
1455 * ^^^^^^^^^^^^^^^^
1456 * - `class A { static get foo() {} }`
1457 * ^^^^^^^^^^^^^^
1458 * - `class A { static set foo(a) {} }`
1459 * ^^^^^^^^^^^^^^
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.
1463 */
1464 getFunctionHeadLoc(node, sourceCode) {
1465 const parent = node.parent;
1466 let start = null;
1467 let end = null;
1468
1469 if (node.type === "ArrowFunctionExpression") {
1470 const arrowToken = sourceCode.getTokenBefore(node.body, isArrowToken);
1471
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;
1477 } else {
1478 start = node.loc.start;
1479 end = getOpeningParenOfParams(node, sourceCode).loc.start;
1480 }
1481
1482 return {
1483 start: Object.assign({}, start),
1484 end: Object.assign({}, end)
1485 };
1486 },
1487
1488 /**
1489 * Gets next location when the result is not out of bound, otherwise returns null.
1490 *
1491 * Assumptions:
1492 *
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.
1499 *
1500 * Examples of successive locations (line, column):
1501 *
1502 * code: foo
1503 * locations: (1, 0) -> (1, 1) -> (1, 2) -> (1, 3) -> null
1504 *
1505 * code: foo<LF>
1506 * locations: (1, 0) -> (1, 1) -> (1, 2) -> (1, 3) -> (2, 0) -> null
1507 *
1508 * code: foo<CR><LF>
1509 * locations: (1, 0) -> (1, 1) -> (1, 2) -> (1, 3) -> (2, 0) -> null
1510 *
1511 * code: a<LF>b
1512 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (2, 1) -> null
1513 *
1514 * code: a<LF>b<LF>
1515 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (2, 1) -> (3, 0) -> null
1516 *
1517 * code: a<CR><LF>b<CR><LF>
1518 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (2, 1) -> (3, 0) -> null
1519 *
1520 * code: a<LF><LF>
1521 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (3, 0) -> null
1522 *
1523 * code: <LF>
1524 * locations: (1, 0) -> (2, 0) -> null
1525 *
1526 * code:
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
1531 */
1532 getNextLocation(sourceCode, { line, column }) {
1533 if (column < sourceCode.lines[line - 1].length) {
1534 return {
1535 line,
1536 column: column + 1
1537 };
1538 }
1539
1540 if (line < sourceCode.lines.length) {
1541 return {
1542 line: line + 1,
1543 column: 0
1544 };
1545 }
1546
1547 return null;
1548 },
1549
1550 /**
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
1556 */
1557 getParenthesisedText(sourceCode, node) {
1558 let leftToken = sourceCode.getFirstToken(node);
1559 let rightToken = sourceCode.getLastToken(node);
1560
1561 while (
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 === ")"
1568 ) {
1569 leftToken = sourceCode.getTokenBefore(leftToken);
1570 rightToken = sourceCode.getTokenAfter(rightToken);
1571 }
1572
1573 return sourceCode.getText().slice(leftToken.range[0], rightToken.range[1]);
1574 },
1575
1576 /*
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
1580 */
1581 couldBeError(node) {
1582 switch (node.type) {
1583 case "Identifier":
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.
1592
1593 case "AssignmentExpression":
1594 if (["=", "&&="].includes(node.operator)) {
1595 return module.exports.couldBeError(node.right);
1596 }
1597
1598 if (["||=", "??="].includes(node.operator)) {
1599 return module.exports.couldBeError(node.left) || module.exports.couldBeError(node.right);
1600 }
1601
1602 /**
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.
1606 */
1607 return false;
1608
1609 case "SequenceExpression": {
1610 const exprs = node.expressions;
1611
1612 return exprs.length !== 0 && module.exports.couldBeError(exprs[exprs.length - 1]);
1613 }
1614
1615 case "LogicalExpression":
1616
1617 /*
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.
1622 */
1623 if (node.operator === "&&") {
1624 return module.exports.couldBeError(node.right);
1625 }
1626
1627 return module.exports.couldBeError(node.left) || module.exports.couldBeError(node.right);
1628
1629 case "ConditionalExpression":
1630 return module.exports.couldBeError(node.consequent) || module.exports.couldBeError(node.alternate);
1631
1632 default:
1633 return false;
1634 }
1635 },
1636
1637 /**
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.
1641 */
1642 isNumericLiteral(node) {
1643 return (
1644 node.type === "Literal" &&
1645 (typeof node.value === "number" || Boolean(node.bigint))
1646 );
1647 },
1648
1649 /**
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).
1655 */
1656 canTokensBeAdjacent(leftValue, rightValue) {
1657 const espreeOptions = {
1658 ecmaVersion: espree.latestEcmaVersion,
1659 comment: true,
1660 range: true
1661 };
1662
1663 let leftToken;
1664
1665 if (typeof leftValue === "string") {
1666 let tokens;
1667
1668 try {
1669 tokens = espree.tokenize(leftValue, espreeOptions);
1670 } catch {
1671 return false;
1672 }
1673
1674 const comments = tokens.comments;
1675
1676 leftToken = tokens[tokens.length - 1];
1677 if (comments.length) {
1678 const lastComment = comments[comments.length - 1];
1679
1680 if (lastComment.range[0] > leftToken.range[0]) {
1681 leftToken = lastComment;
1682 }
1683 }
1684 } else {
1685 leftToken = leftValue;
1686 }
1687
1688 if (leftToken.type === "Shebang") {
1689 return false;
1690 }
1691
1692 let rightToken;
1693
1694 if (typeof rightValue === "string") {
1695 let tokens;
1696
1697 try {
1698 tokens = espree.tokenize(rightValue, espreeOptions);
1699 } catch {
1700 return false;
1701 }
1702
1703 const comments = tokens.comments;
1704
1705 rightToken = tokens[0];
1706 if (comments.length) {
1707 const firstComment = comments[0];
1708
1709 if (firstComment.range[0] < rightToken.range[0]) {
1710 rightToken = firstComment;
1711 }
1712 }
1713 } else {
1714 rightToken = rightValue;
1715 }
1716
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(["-", "--"]);
1721
1722 return !(
1723 PLUS_TOKENS.has(leftToken.value) && PLUS_TOKENS.has(rightToken.value) ||
1724 MINUS_TOKENS.has(leftToken.value) && MINUS_TOKENS.has(rightToken.value)
1725 );
1726 }
1727 if (leftToken.type === "Punctuator" && leftToken.value === "/") {
1728 return !["Block", "Line", "RegularExpression"].includes(rightToken.type);
1729 }
1730 return true;
1731 }
1732
1733 if (
1734 leftToken.type === "String" || rightToken.type === "String" ||
1735 leftToken.type === "Template" || rightToken.type === "Template"
1736 ) {
1737 return true;
1738 }
1739
1740 if (leftToken.type !== "Numeric" && rightToken.type === "Numeric" && rightToken.value.startsWith(".")) {
1741 return true;
1742 }
1743
1744 if (leftToken.type === "Block" || rightToken.type === "Block" || rightToken.type === "Line") {
1745 return true;
1746 }
1747
1748 return false;
1749 },
1750
1751 /**
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.
1757 */
1758 getNameLocationInGlobalDirectiveComment(sourceCode, comment, name) {
1759 const namePattern = new RegExp(`[\\s,]${lodash.escapeRegExp(name)}(?:$|[\\s,:])`, "gu");
1760
1761 // To ignore the first text "global".
1762 namePattern.lastIndex = comment.value.indexOf("global") + 6;
1763
1764 // Search a given variable name.
1765 const match = namePattern.exec(comment.value);
1766
1767 // Convert the index to loc.
1768 const start = sourceCode.getLocFromIndex(
1769 comment.range[0] +
1770 "/*".length +
1771 (match ? match.index + 1 : 0)
1772 );
1773 const end = {
1774 line: start.line,
1775 column: start.column + (match ? name.length : 1)
1776 };
1777
1778 return { start, end };
1779 },
1780
1781 /**
1782 * Determines whether the given raw string contains an octal escape sequence
1783 * or a non-octal decimal escape sequence ("\8", "\9").
1784 *
1785 * "\1", "\2" ... "\7", "\8", "\9"
1786 * "\00", "\01" ... "\07", "\08", "\09"
1787 *
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.
1792 */
1793 hasOctalOrNonOctalDecimalEscapeSequence(rawString) {
1794 return OCTAL_OR_NON_OCTAL_DECIMAL_ESCAPE_PATTERN.test(rawString);
1795 },
1796
1797 isLogicalExpression,
1798 isCoalesceExpression,
1799 isMixedLogicalAndCoalesceExpressions,
1800 isNullLiteral,
1801 getStaticStringValue,
1802 getStaticPropertyName,
1803 skipChainExpression,
1804 isSpecificId,
1805 isSpecificMemberAccess,
1806 equalLiteralValue,
1807 isSameReference,
1808 isLogicalAssignmentOperator
1809};