]> git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/utils/ast-utils.js
ecea6948da236040a023745bd16eee77f9153f76
[pve-eslint.git] / eslint / lib / rules / utils / ast-utils.js
1 /**
2 * @fileoverview Common utils for AST.
3 * @author Gyandeep Singh
4 */
5
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Requirements
10 //------------------------------------------------------------------------------
11
12 const esutils = require("esutils");
13 const espree = require("espree");
14 const lodash = require("lodash");
15 const {
16 breakableTypePattern,
17 createGlobalLinebreakMatcher,
18 lineBreakPattern,
19 shebangPattern
20 } = require("../../shared/ast-utils");
21
22 //------------------------------------------------------------------------------
23 // Helpers
24 //------------------------------------------------------------------------------
25
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;
32
33
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"]);
36
37 // A set of node types that can contain a list of statements
38 const STATEMENT_LIST_PARENTS = new Set(["Program", "BlockStatement", "SwitchCase"]);
39
40 const DECIMAL_INTEGER_PATTERN = /^(0|[1-9]\d*)$/u;
41 const OCTAL_ESCAPE_PATTERN = /^(?:[^\\]|\\[^0-7]|\\0(?![0-9]))*\\(?:[1-7]|0[0-9])/u;
42
43 /**
44 * Checks reference if is non initializer and writable.
45 * @param {Reference} reference A reference to check.
46 * @param {int} index The index of the reference in the references.
47 * @param {Reference[]} references The array that the reference belongs to.
48 * @returns {boolean} Success/Failure
49 * @private
50 */
51 function isModifyingReference(reference, index, references) {
52 const identifier = reference.identifier;
53
54 /*
55 * Destructuring assignments can have multiple default value, so
56 * possibly there are multiple writeable references for the same
57 * identifier.
58 */
59 const modifyingDifferentIdentifier = index === 0 ||
60 references[index - 1].identifier !== identifier;
61
62 return (identifier &&
63 reference.init === false &&
64 reference.isWrite() &&
65 modifyingDifferentIdentifier
66 );
67 }
68
69 /**
70 * Checks whether the given string starts with uppercase or not.
71 * @param {string} s The string to check.
72 * @returns {boolean} `true` if the string starts with uppercase.
73 */
74 function startsWithUpperCase(s) {
75 return s[0] !== s[0].toLocaleLowerCase();
76 }
77
78 /**
79 * Checks whether or not a node is a constructor.
80 * @param {ASTNode} node A function node to check.
81 * @returns {boolean} Wehether or not a node is a constructor.
82 */
83 function isES5Constructor(node) {
84 return (node.id && startsWithUpperCase(node.id.name));
85 }
86
87 /**
88 * Finds a function node from ancestors of a node.
89 * @param {ASTNode} node A start node to find.
90 * @returns {Node|null} A found function node.
91 */
92 function getUpperFunction(node) {
93 for (let currentNode = node; currentNode; currentNode = currentNode.parent) {
94 if (anyFunctionPattern.test(currentNode.type)) {
95 return currentNode;
96 }
97 }
98 return null;
99 }
100
101 /**
102 * Checks whether a given node is a function node or not.
103 * The following types are function nodes:
104 *
105 * - ArrowFunctionExpression
106 * - FunctionDeclaration
107 * - FunctionExpression
108 * @param {ASTNode|null} node A node to check.
109 * @returns {boolean} `true` if the node is a function node.
110 */
111 function isFunction(node) {
112 return Boolean(node && anyFunctionPattern.test(node.type));
113 }
114
115 /**
116 * Checks whether a given node is a loop node or not.
117 * The following types are loop nodes:
118 *
119 * - DoWhileStatement
120 * - ForInStatement
121 * - ForOfStatement
122 * - ForStatement
123 * - WhileStatement
124 * @param {ASTNode|null} node A node to check.
125 * @returns {boolean} `true` if the node is a loop node.
126 */
127 function isLoop(node) {
128 return Boolean(node && anyLoopPattern.test(node.type));
129 }
130
131 /**
132 * Checks whether the given node is in a loop or not.
133 * @param {ASTNode} node The node to check.
134 * @returns {boolean} `true` if the node is in a loop.
135 */
136 function isInLoop(node) {
137 for (let currentNode = node; currentNode && !isFunction(currentNode); currentNode = currentNode.parent) {
138 if (isLoop(currentNode)) {
139 return true;
140 }
141 }
142
143 return false;
144 }
145
146 /**
147 * Checks whether or not a node is `null` or `undefined`.
148 * @param {ASTNode} node A node to check.
149 * @returns {boolean} Whether or not the node is a `null` or `undefined`.
150 * @public
151 */
152 function isNullOrUndefined(node) {
153 return (
154 module.exports.isNullLiteral(node) ||
155 (node.type === "Identifier" && node.name === "undefined") ||
156 (node.type === "UnaryExpression" && node.operator === "void")
157 );
158 }
159
160 /**
161 * Checks whether or not a node is callee.
162 * @param {ASTNode} node A node to check.
163 * @returns {boolean} Whether or not the node is callee.
164 */
165 function isCallee(node) {
166 return node.parent.type === "CallExpression" && node.parent.callee === node;
167 }
168
169 /**
170 * Checks whether or not a node is `Reflect.apply`.
171 * @param {ASTNode} node A node to check.
172 * @returns {boolean} Whether or not the node is a `Reflect.apply`.
173 */
174 function isReflectApply(node) {
175 return (
176 node.type === "MemberExpression" &&
177 node.object.type === "Identifier" &&
178 node.object.name === "Reflect" &&
179 node.property.type === "Identifier" &&
180 node.property.name === "apply" &&
181 node.computed === false
182 );
183 }
184
185 /**
186 * Checks whether or not a node is `Array.from`.
187 * @param {ASTNode} node A node to check.
188 * @returns {boolean} Whether or not the node is a `Array.from`.
189 */
190 function isArrayFromMethod(node) {
191 return (
192 node.type === "MemberExpression" &&
193 node.object.type === "Identifier" &&
194 arrayOrTypedArrayPattern.test(node.object.name) &&
195 node.property.type === "Identifier" &&
196 node.property.name === "from" &&
197 node.computed === false
198 );
199 }
200
201 /**
202 * Checks whether or not a node is a method which has `thisArg`.
203 * @param {ASTNode} node A node to check.
204 * @returns {boolean} Whether or not the node is a method which has `thisArg`.
205 */
206 function isMethodWhichHasThisArg(node) {
207 for (
208 let currentNode = node;
209 currentNode.type === "MemberExpression" && !currentNode.computed;
210 currentNode = currentNode.property
211 ) {
212 if (currentNode.property.type === "Identifier") {
213 return arrayMethodPattern.test(currentNode.property.name);
214 }
215 }
216
217 return false;
218 }
219
220 /**
221 * Creates the negate function of the given function.
222 * @param {Function} f The function to negate.
223 * @returns {Function} Negated function.
224 */
225 function negate(f) {
226 return token => !f(token);
227 }
228
229 /**
230 * Checks whether or not a node has a `@this` tag in its comments.
231 * @param {ASTNode} node A node to check.
232 * @param {SourceCode} sourceCode A SourceCode instance to get comments.
233 * @returns {boolean} Whether or not the node has a `@this` tag in its comments.
234 */
235 function hasJSDocThisTag(node, sourceCode) {
236 const jsdocComment = sourceCode.getJSDocComment(node);
237
238 if (jsdocComment && thisTagPattern.test(jsdocComment.value)) {
239 return true;
240 }
241
242 // Checks `@this` in its leading comments for callbacks,
243 // because callbacks don't have its JSDoc comment.
244 // e.g.
245 // sinon.test(/* @this sinon.Sandbox */function() { this.spy(); });
246 return sourceCode.getCommentsBefore(node).some(comment => thisTagPattern.test(comment.value));
247 }
248
249 /**
250 * Determines if a node is surrounded by parentheses.
251 * @param {SourceCode} sourceCode The ESLint source code object
252 * @param {ASTNode} node The node to be checked.
253 * @returns {boolean} True if the node is parenthesised.
254 * @private
255 */
256 function isParenthesised(sourceCode, node) {
257 const previousToken = sourceCode.getTokenBefore(node),
258 nextToken = sourceCode.getTokenAfter(node);
259
260 return Boolean(previousToken && nextToken) &&
261 previousToken.value === "(" && previousToken.range[1] <= node.range[0] &&
262 nextToken.value === ")" && nextToken.range[0] >= node.range[1];
263 }
264
265 /**
266 * Checks if the given token is an arrow token or not.
267 * @param {Token} token The token to check.
268 * @returns {boolean} `true` if the token is an arrow token.
269 */
270 function isArrowToken(token) {
271 return token.value === "=>" && token.type === "Punctuator";
272 }
273
274 /**
275 * Checks if the given token is a comma token or not.
276 * @param {Token} token The token to check.
277 * @returns {boolean} `true` if the token is a comma token.
278 */
279 function isCommaToken(token) {
280 return token.value === "," && token.type === "Punctuator";
281 }
282
283 /**
284 * Checks if the given token is a dot token or not.
285 * @param {Token} token The token to check.
286 * @returns {boolean} `true` if the token is a dot token.
287 */
288 function isDotToken(token) {
289 return token.value === "." && token.type === "Punctuator";
290 }
291
292 /**
293 * Checks if the given token is a semicolon token or not.
294 * @param {Token} token The token to check.
295 * @returns {boolean} `true` if the token is a semicolon token.
296 */
297 function isSemicolonToken(token) {
298 return token.value === ";" && token.type === "Punctuator";
299 }
300
301 /**
302 * Checks if the given token is a colon token or not.
303 * @param {Token} token The token to check.
304 * @returns {boolean} `true` if the token is a colon token.
305 */
306 function isColonToken(token) {
307 return token.value === ":" && token.type === "Punctuator";
308 }
309
310 /**
311 * Checks if the given token is an opening parenthesis token or not.
312 * @param {Token} token The token to check.
313 * @returns {boolean} `true` if the token is an opening parenthesis token.
314 */
315 function isOpeningParenToken(token) {
316 return token.value === "(" && token.type === "Punctuator";
317 }
318
319 /**
320 * Checks if the given token is a closing parenthesis token or not.
321 * @param {Token} token The token to check.
322 * @returns {boolean} `true` if the token is a closing parenthesis token.
323 */
324 function isClosingParenToken(token) {
325 return token.value === ")" && token.type === "Punctuator";
326 }
327
328 /**
329 * Checks if the given token is an opening square bracket token or not.
330 * @param {Token} token The token to check.
331 * @returns {boolean} `true` if the token is an opening square bracket token.
332 */
333 function isOpeningBracketToken(token) {
334 return token.value === "[" && token.type === "Punctuator";
335 }
336
337 /**
338 * Checks if the given token is a closing square bracket token or not.
339 * @param {Token} token The token to check.
340 * @returns {boolean} `true` if the token is a closing square bracket token.
341 */
342 function isClosingBracketToken(token) {
343 return token.value === "]" && token.type === "Punctuator";
344 }
345
346 /**
347 * Checks if the given token is an opening brace token or not.
348 * @param {Token} token The token to check.
349 * @returns {boolean} `true` if the token is an opening brace token.
350 */
351 function isOpeningBraceToken(token) {
352 return token.value === "{" && token.type === "Punctuator";
353 }
354
355 /**
356 * Checks if the given token is a closing brace token or not.
357 * @param {Token} token The token to check.
358 * @returns {boolean} `true` if the token is a closing brace token.
359 */
360 function isClosingBraceToken(token) {
361 return token.value === "}" && token.type === "Punctuator";
362 }
363
364 /**
365 * Checks if the given token is a comment token or not.
366 * @param {Token} token The token to check.
367 * @returns {boolean} `true` if the token is a comment token.
368 */
369 function isCommentToken(token) {
370 return token.type === "Line" || token.type === "Block" || token.type === "Shebang";
371 }
372
373 /**
374 * Checks if the given token is a keyword token or not.
375 * @param {Token} token The token to check.
376 * @returns {boolean} `true` if the token is a keyword token.
377 */
378 function isKeywordToken(token) {
379 return token.type === "Keyword";
380 }
381
382 /**
383 * Gets the `(` token of the given function node.
384 * @param {ASTNode} node The function node to get.
385 * @param {SourceCode} sourceCode The source code object to get tokens.
386 * @returns {Token} `(` token.
387 */
388 function getOpeningParenOfParams(node, sourceCode) {
389 return node.id
390 ? sourceCode.getTokenAfter(node.id, isOpeningParenToken)
391 : sourceCode.getFirstToken(node, isOpeningParenToken);
392 }
393
394 /**
395 * Checks whether or not the tokens of two given nodes are same.
396 * @param {ASTNode} left A node 1 to compare.
397 * @param {ASTNode} right A node 2 to compare.
398 * @param {SourceCode} sourceCode The ESLint source code object.
399 * @returns {boolean} the source code for the given node.
400 */
401 function equalTokens(left, right, sourceCode) {
402 const tokensL = sourceCode.getTokens(left);
403 const tokensR = sourceCode.getTokens(right);
404
405 if (tokensL.length !== tokensR.length) {
406 return false;
407 }
408 for (let i = 0; i < tokensL.length; ++i) {
409 if (tokensL[i].type !== tokensR[i].type ||
410 tokensL[i].value !== tokensR[i].value
411 ) {
412 return false;
413 }
414 }
415
416 return true;
417 }
418
419 /**
420 * Check if the given node is a true logical expression or not.
421 *
422 * The three binary expressions logical-or (`||`), logical-and (`&&`), and
423 * coalesce (`??`) are known as `ShortCircuitExpression`.
424 * But ESTree represents those by `LogicalExpression` node.
425 *
426 * This function rejects coalesce expressions of `LogicalExpression` node.
427 * @param {ASTNode} node The node to check.
428 * @returns {boolean} `true` if the node is `&&` or `||`.
429 * @see https://tc39.es/ecma262/#prod-ShortCircuitExpression
430 */
431 function isLogicalExpression(node) {
432 return (
433 node.type === "LogicalExpression" &&
434 (node.operator === "&&" || node.operator === "||")
435 );
436 }
437
438 /**
439 * Check if the given node is a nullish coalescing expression or not.
440 *
441 * The three binary expressions logical-or (`||`), logical-and (`&&`), and
442 * coalesce (`??`) are known as `ShortCircuitExpression`.
443 * But ESTree represents those by `LogicalExpression` node.
444 *
445 * This function finds only coalesce expressions of `LogicalExpression` node.
446 * @param {ASTNode} node The node to check.
447 * @returns {boolean} `true` if the node is `??`.
448 */
449 function isCoalesceExpression(node) {
450 return node.type === "LogicalExpression" && node.operator === "??";
451 }
452
453 /**
454 * Check if given two nodes are the pair of a logical expression and a coalesce expression.
455 * @param {ASTNode} left A node to check.
456 * @param {ASTNode} right Another node to check.
457 * @returns {boolean} `true` if the two nodes are the pair of a logical expression and a coalesce expression.
458 */
459 function isMixedLogicalAndCoalesceExpressions(left, right) {
460 return (
461 (isLogicalExpression(left) && isCoalesceExpression(right)) ||
462 (isCoalesceExpression(left) && isLogicalExpression(right))
463 );
464 }
465
466 //------------------------------------------------------------------------------
467 // Public Interface
468 //------------------------------------------------------------------------------
469
470 module.exports = {
471 COMMENTS_IGNORE_PATTERN,
472 LINEBREAKS,
473 LINEBREAK_MATCHER: lineBreakPattern,
474 SHEBANG_MATCHER: shebangPattern,
475 STATEMENT_LIST_PARENTS,
476
477 /**
478 * Determines whether two adjacent tokens are on the same line.
479 * @param {Object} left The left token object.
480 * @param {Object} right The right token object.
481 * @returns {boolean} Whether or not the tokens are on the same line.
482 * @public
483 */
484 isTokenOnSameLine(left, right) {
485 return left.loc.end.line === right.loc.start.line;
486 },
487
488 isNullOrUndefined,
489 isCallee,
490 isES5Constructor,
491 getUpperFunction,
492 isFunction,
493 isLoop,
494 isInLoop,
495 isArrayFromMethod,
496 isParenthesised,
497 createGlobalLinebreakMatcher,
498 equalTokens,
499
500 isArrowToken,
501 isClosingBraceToken,
502 isClosingBracketToken,
503 isClosingParenToken,
504 isColonToken,
505 isCommaToken,
506 isCommentToken,
507 isDotToken,
508 isKeywordToken,
509 isNotClosingBraceToken: negate(isClosingBraceToken),
510 isNotClosingBracketToken: negate(isClosingBracketToken),
511 isNotClosingParenToken: negate(isClosingParenToken),
512 isNotColonToken: negate(isColonToken),
513 isNotCommaToken: negate(isCommaToken),
514 isNotDotToken: negate(isDotToken),
515 isNotOpeningBraceToken: negate(isOpeningBraceToken),
516 isNotOpeningBracketToken: negate(isOpeningBracketToken),
517 isNotOpeningParenToken: negate(isOpeningParenToken),
518 isNotSemicolonToken: negate(isSemicolonToken),
519 isOpeningBraceToken,
520 isOpeningBracketToken,
521 isOpeningParenToken,
522 isSemicolonToken,
523
524 /**
525 * Checks whether or not a given node is a string literal.
526 * @param {ASTNode} node A node to check.
527 * @returns {boolean} `true` if the node is a string literal.
528 */
529 isStringLiteral(node) {
530 return (
531 (node.type === "Literal" && typeof node.value === "string") ||
532 node.type === "TemplateLiteral"
533 );
534 },
535
536 /**
537 * Checks whether a given node is a breakable statement or not.
538 * The node is breakable if the node is one of the following type:
539 *
540 * - DoWhileStatement
541 * - ForInStatement
542 * - ForOfStatement
543 * - ForStatement
544 * - SwitchStatement
545 * - WhileStatement
546 * @param {ASTNode} node A node to check.
547 * @returns {boolean} `true` if the node is breakable.
548 */
549 isBreakableStatement(node) {
550 return breakableTypePattern.test(node.type);
551 },
552
553 /**
554 * Gets references which are non initializer and writable.
555 * @param {Reference[]} references An array of references.
556 * @returns {Reference[]} An array of only references which are non initializer and writable.
557 * @public
558 */
559 getModifyingReferences(references) {
560 return references.filter(isModifyingReference);
561 },
562
563 /**
564 * Validate that a string passed in is surrounded by the specified character
565 * @param {string} val The text to check.
566 * @param {string} character The character to see if it's surrounded by.
567 * @returns {boolean} True if the text is surrounded by the character, false if not.
568 * @private
569 */
570 isSurroundedBy(val, character) {
571 return val[0] === character && val[val.length - 1] === character;
572 },
573
574 /**
575 * Returns whether the provided node is an ESLint directive comment or not
576 * @param {Line|Block} node The comment token to be checked
577 * @returns {boolean} `true` if the node is an ESLint directive comment
578 */
579 isDirectiveComment(node) {
580 const comment = node.value.trim();
581
582 return (
583 node.type === "Line" && comment.indexOf("eslint-") === 0 ||
584 node.type === "Block" && (
585 comment.indexOf("global ") === 0 ||
586 comment.indexOf("eslint ") === 0 ||
587 comment.indexOf("eslint-") === 0
588 )
589 );
590 },
591
592 /**
593 * Gets the trailing statement of a given node.
594 *
595 * if (code)
596 * consequent;
597 *
598 * When taking this `IfStatement`, returns `consequent;` statement.
599 * @param {ASTNode} A node to get.
600 * @returns {ASTNode|null} The trailing statement's node.
601 */
602 getTrailingStatement: esutils.ast.trailingStatement,
603
604 /**
605 * Finds the variable by a given name in a given scope and its upper scopes.
606 * @param {eslint-scope.Scope} initScope A scope to start find.
607 * @param {string} name A variable name to find.
608 * @returns {eslint-scope.Variable|null} A found variable or `null`.
609 */
610 getVariableByName(initScope, name) {
611 let scope = initScope;
612
613 while (scope) {
614 const variable = scope.set.get(name);
615
616 if (variable) {
617 return variable;
618 }
619
620 scope = scope.upper;
621 }
622
623 return null;
624 },
625
626 /**
627 * Checks whether or not a given function node is the default `this` binding.
628 *
629 * First, this checks the node:
630 *
631 * - The function name does not start with uppercase. It's a convention to capitalize the names
632 * of constructor functions. This check is not performed if `capIsConstructor` is set to `false`.
633 * - The function does not have a JSDoc comment that has a @this tag.
634 *
635 * Next, this checks the location of the node.
636 * If the location is below, this judges `this` is valid.
637 *
638 * - The location is not on an object literal.
639 * - The location is not assigned to a variable which starts with an uppercase letter. Applies to anonymous
640 * functions only, as the name of the variable is considered to be the name of the function in this case.
641 * This check is not performed if `capIsConstructor` is set to `false`.
642 * - The location is not on an ES2015 class.
643 * - Its `bind`/`call`/`apply` method is not called directly.
644 * - The function is not a callback of array methods (such as `.forEach()`) if `thisArg` is given.
645 * @param {ASTNode} node A function node to check.
646 * @param {SourceCode} sourceCode A SourceCode instance to get comments.
647 * @param {boolean} [capIsConstructor = true] `false` disables the assumption that functions which name starts
648 * with an uppercase or are assigned to a variable which name starts with an uppercase are constructors.
649 * @returns {boolean} The function node is the default `this` binding.
650 */
651 isDefaultThisBinding(node, sourceCode, { capIsConstructor = true } = {}) {
652 if (
653 (capIsConstructor && isES5Constructor(node)) ||
654 hasJSDocThisTag(node, sourceCode)
655 ) {
656 return false;
657 }
658 const isAnonymous = node.id === null;
659 let currentNode = node;
660
661 while (currentNode) {
662 const parent = currentNode.parent;
663
664 switch (parent.type) {
665
666 /*
667 * Looks up the destination.
668 * e.g., obj.foo = nativeFoo || function foo() { ... };
669 */
670 case "LogicalExpression":
671 case "ConditionalExpression":
672 currentNode = parent;
673 break;
674
675 /*
676 * If the upper function is IIFE, checks the destination of the return value.
677 * e.g.
678 * obj.foo = (function() {
679 * // setup...
680 * return function foo() { ... };
681 * })();
682 * obj.foo = (() =>
683 * function foo() { ... }
684 * )();
685 */
686 case "ReturnStatement": {
687 const func = getUpperFunction(parent);
688
689 if (func === null || !isCallee(func)) {
690 return true;
691 }
692 currentNode = func.parent;
693 break;
694 }
695 case "ArrowFunctionExpression":
696 if (currentNode !== parent.body || !isCallee(parent)) {
697 return true;
698 }
699 currentNode = parent.parent;
700 break;
701
702 /*
703 * e.g.
704 * var obj = { foo() { ... } };
705 * var obj = { foo: function() { ... } };
706 * class A { constructor() { ... } }
707 * class A { foo() { ... } }
708 * class A { get foo() { ... } }
709 * class A { set foo() { ... } }
710 * class A { static foo() { ... } }
711 */
712 case "Property":
713 case "MethodDefinition":
714 return parent.value !== currentNode;
715
716 /*
717 * e.g.
718 * obj.foo = function foo() { ... };
719 * Foo = function() { ... };
720 * [obj.foo = function foo() { ... }] = a;
721 * [Foo = function() { ... }] = a;
722 */
723 case "AssignmentExpression":
724 case "AssignmentPattern":
725 if (parent.left.type === "MemberExpression") {
726 return false;
727 }
728 if (
729 capIsConstructor &&
730 isAnonymous &&
731 parent.left.type === "Identifier" &&
732 startsWithUpperCase(parent.left.name)
733 ) {
734 return false;
735 }
736 return true;
737
738 /*
739 * e.g.
740 * var Foo = function() { ... };
741 */
742 case "VariableDeclarator":
743 return !(
744 capIsConstructor &&
745 isAnonymous &&
746 parent.init === currentNode &&
747 parent.id.type === "Identifier" &&
748 startsWithUpperCase(parent.id.name)
749 );
750
751 /*
752 * e.g.
753 * var foo = function foo() { ... }.bind(obj);
754 * (function foo() { ... }).call(obj);
755 * (function foo() { ... }).apply(obj, []);
756 */
757 case "MemberExpression":
758 return (
759 parent.object !== currentNode ||
760 parent.property.type !== "Identifier" ||
761 !bindOrCallOrApplyPattern.test(parent.property.name) ||
762 !isCallee(parent) ||
763 parent.parent.arguments.length === 0 ||
764 isNullOrUndefined(parent.parent.arguments[0])
765 );
766
767 /*
768 * e.g.
769 * Reflect.apply(function() {}, obj, []);
770 * Array.from([], function() {}, obj);
771 * list.forEach(function() {}, obj);
772 */
773 case "CallExpression":
774 if (isReflectApply(parent.callee)) {
775 return (
776 parent.arguments.length !== 3 ||
777 parent.arguments[0] !== currentNode ||
778 isNullOrUndefined(parent.arguments[1])
779 );
780 }
781 if (isArrayFromMethod(parent.callee)) {
782 return (
783 parent.arguments.length !== 3 ||
784 parent.arguments[1] !== currentNode ||
785 isNullOrUndefined(parent.arguments[2])
786 );
787 }
788 if (isMethodWhichHasThisArg(parent.callee)) {
789 return (
790 parent.arguments.length !== 2 ||
791 parent.arguments[0] !== currentNode ||
792 isNullOrUndefined(parent.arguments[1])
793 );
794 }
795 return true;
796
797 // Otherwise `this` is default.
798 default:
799 return true;
800 }
801 }
802
803 /* istanbul ignore next */
804 return true;
805 },
806
807 /**
808 * Get the precedence level based on the node type
809 * @param {ASTNode} node node to evaluate
810 * @returns {int} precedence level
811 * @private
812 */
813 getPrecedence(node) {
814 switch (node.type) {
815 case "SequenceExpression":
816 return 0;
817
818 case "AssignmentExpression":
819 case "ArrowFunctionExpression":
820 case "YieldExpression":
821 return 1;
822
823 case "ConditionalExpression":
824 return 3;
825
826 case "LogicalExpression":
827 switch (node.operator) {
828 case "||":
829 case "??":
830 return 4;
831 case "&&":
832 return 5;
833
834 // no default
835 }
836
837 /* falls through */
838
839 case "BinaryExpression":
840
841 switch (node.operator) {
842 case "|":
843 return 6;
844 case "^":
845 return 7;
846 case "&":
847 return 8;
848 case "==":
849 case "!=":
850 case "===":
851 case "!==":
852 return 9;
853 case "<":
854 case "<=":
855 case ">":
856 case ">=":
857 case "in":
858 case "instanceof":
859 return 10;
860 case "<<":
861 case ">>":
862 case ">>>":
863 return 11;
864 case "+":
865 case "-":
866 return 12;
867 case "*":
868 case "/":
869 case "%":
870 return 13;
871 case "**":
872 return 15;
873
874 // no default
875 }
876
877 /* falls through */
878
879 case "UnaryExpression":
880 case "AwaitExpression":
881 return 16;
882
883 case "UpdateExpression":
884 return 17;
885
886 case "CallExpression":
887 case "ImportExpression":
888 return 18;
889
890 case "NewExpression":
891 return 19;
892
893 default:
894 return 20;
895 }
896 },
897
898 /**
899 * Checks whether the given node is an empty block node or not.
900 * @param {ASTNode|null} node The node to check.
901 * @returns {boolean} `true` if the node is an empty block.
902 */
903 isEmptyBlock(node) {
904 return Boolean(node && node.type === "BlockStatement" && node.body.length === 0);
905 },
906
907 /**
908 * Checks whether the given node is an empty function node or not.
909 * @param {ASTNode|null} node The node to check.
910 * @returns {boolean} `true` if the node is an empty function.
911 */
912 isEmptyFunction(node) {
913 return isFunction(node) && module.exports.isEmptyBlock(node.body);
914 },
915
916 /**
917 * Returns the result of the string conversion applied to the evaluated value of the given expression node,
918 * if it can be determined statically.
919 *
920 * This function returns a `string` value for all `Literal` nodes and simple `TemplateLiteral` nodes only.
921 * In all other cases, this function returns `null`.
922 * @param {ASTNode} node Expression node.
923 * @returns {string|null} String value if it can be determined. Otherwise, `null`.
924 */
925 getStaticStringValue(node) {
926 switch (node.type) {
927 case "Literal":
928 if (node.value === null) {
929 if (module.exports.isNullLiteral(node)) {
930 return String(node.value); // "null"
931 }
932 if (node.regex) {
933 return `/${node.regex.pattern}/${node.regex.flags}`;
934 }
935 if (node.bigint) {
936 return node.bigint;
937 }
938
939 // Otherwise, this is an unknown literal. The function will return null.
940
941 } else {
942 return String(node.value);
943 }
944 break;
945 case "TemplateLiteral":
946 if (node.expressions.length === 0 && node.quasis.length === 1) {
947 return node.quasis[0].value.cooked;
948 }
949 break;
950
951 // no default
952 }
953
954 return null;
955 },
956
957 /**
958 * Gets the property name of a given node.
959 * The node can be a MemberExpression, a Property, or a MethodDefinition.
960 *
961 * If the name is dynamic, this returns `null`.
962 *
963 * For examples:
964 *
965 * a.b // => "b"
966 * a["b"] // => "b"
967 * a['b'] // => "b"
968 * a[`b`] // => "b"
969 * a[100] // => "100"
970 * a[b] // => null
971 * a["a" + "b"] // => null
972 * a[tag`b`] // => null
973 * a[`${b}`] // => null
974 *
975 * let a = {b: 1} // => "b"
976 * let a = {["b"]: 1} // => "b"
977 * let a = {['b']: 1} // => "b"
978 * let a = {[`b`]: 1} // => "b"
979 * let a = {[100]: 1} // => "100"
980 * let a = {[b]: 1} // => null
981 * let a = {["a" + "b"]: 1} // => null
982 * let a = {[tag`b`]: 1} // => null
983 * let a = {[`${b}`]: 1} // => null
984 * @param {ASTNode} node The node to get.
985 * @returns {string|null} The property name if static. Otherwise, null.
986 */
987 getStaticPropertyName(node) {
988 let prop;
989
990 switch (node && node.type) {
991 case "Property":
992 case "MethodDefinition":
993 prop = node.key;
994 break;
995
996 case "MemberExpression":
997 prop = node.property;
998 break;
999
1000 // no default
1001 }
1002
1003 if (prop) {
1004 if (prop.type === "Identifier" && !node.computed) {
1005 return prop.name;
1006 }
1007
1008 return module.exports.getStaticStringValue(prop);
1009 }
1010
1011 return null;
1012 },
1013
1014 /**
1015 * Get directives from directive prologue of a Program or Function node.
1016 * @param {ASTNode} node The node to check.
1017 * @returns {ASTNode[]} The directives found in the directive prologue.
1018 */
1019 getDirectivePrologue(node) {
1020 const directives = [];
1021
1022 // Directive prologues only occur at the top of files or functions.
1023 if (
1024 node.type === "Program" ||
1025 node.type === "FunctionDeclaration" ||
1026 node.type === "FunctionExpression" ||
1027
1028 /*
1029 * Do not check arrow functions with implicit return.
1030 * `() => "use strict";` returns the string `"use strict"`.
1031 */
1032 (node.type === "ArrowFunctionExpression" && node.body.type === "BlockStatement")
1033 ) {
1034 const statements = node.type === "Program" ? node.body : node.body.body;
1035
1036 for (const statement of statements) {
1037 if (
1038 statement.type === "ExpressionStatement" &&
1039 statement.expression.type === "Literal"
1040 ) {
1041 directives.push(statement);
1042 } else {
1043 break;
1044 }
1045 }
1046 }
1047
1048 return directives;
1049 },
1050
1051
1052 /**
1053 * Determines whether this node is a decimal integer literal. If a node is a decimal integer literal, a dot added
1054 * after the node will be parsed as a decimal point, rather than a property-access dot.
1055 * @param {ASTNode} node The node to check.
1056 * @returns {boolean} `true` if this node is a decimal integer.
1057 * @example
1058 *
1059 * 5 // true
1060 * 5. // false
1061 * 5.0 // false
1062 * 05 // false
1063 * 0x5 // false
1064 * 0b101 // false
1065 * 0o5 // false
1066 * 5e0 // false
1067 * '5' // false
1068 * 5n // false
1069 */
1070 isDecimalInteger(node) {
1071 return node.type === "Literal" && typeof node.value === "number" &&
1072 DECIMAL_INTEGER_PATTERN.test(node.raw);
1073 },
1074
1075 /**
1076 * Determines whether this token is a decimal integer numeric token.
1077 * This is similar to isDecimalInteger(), but for tokens.
1078 * @param {Token} token The token to check.
1079 * @returns {boolean} `true` if this token is a decimal integer.
1080 */
1081 isDecimalIntegerNumericToken(token) {
1082 return token.type === "Numeric" && DECIMAL_INTEGER_PATTERN.test(token.value);
1083 },
1084
1085 /**
1086 * Gets the name and kind of the given function node.
1087 *
1088 * - `function foo() {}` .................... `function 'foo'`
1089 * - `(function foo() {})` .................. `function 'foo'`
1090 * - `(function() {})` ...................... `function`
1091 * - `function* foo() {}` ................... `generator function 'foo'`
1092 * - `(function* foo() {})` ................. `generator function 'foo'`
1093 * - `(function*() {})` ..................... `generator function`
1094 * - `() => {}` ............................. `arrow function`
1095 * - `async () => {}` ....................... `async arrow function`
1096 * - `({ foo: function foo() {} })` ......... `method 'foo'`
1097 * - `({ foo: function() {} })` ............. `method 'foo'`
1098 * - `({ ['foo']: function() {} })` ......... `method 'foo'`
1099 * - `({ [foo]: function() {} })` ........... `method`
1100 * - `({ foo() {} })` ....................... `method 'foo'`
1101 * - `({ foo: function* foo() {} })` ........ `generator method 'foo'`
1102 * - `({ foo: function*() {} })` ............ `generator method 'foo'`
1103 * - `({ ['foo']: function*() {} })` ........ `generator method 'foo'`
1104 * - `({ [foo]: function*() {} })` .......... `generator method`
1105 * - `({ *foo() {} })` ...................... `generator method 'foo'`
1106 * - `({ foo: async function foo() {} })` ... `async method 'foo'`
1107 * - `({ foo: async function() {} })` ....... `async method 'foo'`
1108 * - `({ ['foo']: async function() {} })` ... `async method 'foo'`
1109 * - `({ [foo]: async function() {} })` ..... `async method`
1110 * - `({ async foo() {} })` ................. `async method 'foo'`
1111 * - `({ get foo() {} })` ................... `getter 'foo'`
1112 * - `({ set foo(a) {} })` .................. `setter 'foo'`
1113 * - `class A { constructor() {} }` ......... `constructor`
1114 * - `class A { foo() {} }` ................. `method 'foo'`
1115 * - `class A { *foo() {} }` ................ `generator method 'foo'`
1116 * - `class A { async foo() {} }` ........... `async method 'foo'`
1117 * - `class A { ['foo']() {} }` ............. `method 'foo'`
1118 * - `class A { *['foo']() {} }` ............ `generator method 'foo'`
1119 * - `class A { async ['foo']() {} }` ....... `async method 'foo'`
1120 * - `class A { [foo]() {} }` ............... `method`
1121 * - `class A { *[foo]() {} }` .............. `generator method`
1122 * - `class A { async [foo]() {} }` ......... `async method`
1123 * - `class A { get foo() {} }` ............. `getter 'foo'`
1124 * - `class A { set foo(a) {} }` ............ `setter 'foo'`
1125 * - `class A { static foo() {} }` .......... `static method 'foo'`
1126 * - `class A { static *foo() {} }` ......... `static generator method 'foo'`
1127 * - `class A { static async foo() {} }` .... `static async method 'foo'`
1128 * - `class A { static get foo() {} }` ...... `static getter 'foo'`
1129 * - `class A { static set foo(a) {} }` ..... `static setter 'foo'`
1130 * @param {ASTNode} node The function node to get.
1131 * @returns {string} The name and kind of the function node.
1132 */
1133 getFunctionNameWithKind(node) {
1134 const parent = node.parent;
1135 const tokens = [];
1136
1137 if (parent.type === "MethodDefinition" && parent.static) {
1138 tokens.push("static");
1139 }
1140 if (node.async) {
1141 tokens.push("async");
1142 }
1143 if (node.generator) {
1144 tokens.push("generator");
1145 }
1146
1147 if (node.type === "ArrowFunctionExpression") {
1148 tokens.push("arrow", "function");
1149 } else if (parent.type === "Property" || parent.type === "MethodDefinition") {
1150 if (parent.kind === "constructor") {
1151 return "constructor";
1152 }
1153 if (parent.kind === "get") {
1154 tokens.push("getter");
1155 } else if (parent.kind === "set") {
1156 tokens.push("setter");
1157 } else {
1158 tokens.push("method");
1159 }
1160 } else {
1161 tokens.push("function");
1162 }
1163
1164 if (node.id) {
1165 tokens.push(`'${node.id.name}'`);
1166 } else {
1167 const name = module.exports.getStaticPropertyName(parent);
1168
1169 if (name !== null) {
1170 tokens.push(`'${name}'`);
1171 }
1172 }
1173
1174 return tokens.join(" ");
1175 },
1176
1177 /**
1178 * Gets the location of the given function node for reporting.
1179 *
1180 * - `function foo() {}`
1181 * ^^^^^^^^^^^^
1182 * - `(function foo() {})`
1183 * ^^^^^^^^^^^^
1184 * - `(function() {})`
1185 * ^^^^^^^^
1186 * - `function* foo() {}`
1187 * ^^^^^^^^^^^^^
1188 * - `(function* foo() {})`
1189 * ^^^^^^^^^^^^^
1190 * - `(function*() {})`
1191 * ^^^^^^^^^
1192 * - `() => {}`
1193 * ^^
1194 * - `async () => {}`
1195 * ^^
1196 * - `({ foo: function foo() {} })`
1197 * ^^^^^^^^^^^^^^^^^
1198 * - `({ foo: function() {} })`
1199 * ^^^^^^^^^^^^^
1200 * - `({ ['foo']: function() {} })`
1201 * ^^^^^^^^^^^^^^^^^
1202 * - `({ [foo]: function() {} })`
1203 * ^^^^^^^^^^^^^^^
1204 * - `({ foo() {} })`
1205 * ^^^
1206 * - `({ foo: function* foo() {} })`
1207 * ^^^^^^^^^^^^^^^^^^
1208 * - `({ foo: function*() {} })`
1209 * ^^^^^^^^^^^^^^
1210 * - `({ ['foo']: function*() {} })`
1211 * ^^^^^^^^^^^^^^^^^^
1212 * - `({ [foo]: function*() {} })`
1213 * ^^^^^^^^^^^^^^^^
1214 * - `({ *foo() {} })`
1215 * ^^^^
1216 * - `({ foo: async function foo() {} })`
1217 * ^^^^^^^^^^^^^^^^^^^^^^^
1218 * - `({ foo: async function() {} })`
1219 * ^^^^^^^^^^^^^^^^^^^
1220 * - `({ ['foo']: async function() {} })`
1221 * ^^^^^^^^^^^^^^^^^^^^^^^
1222 * - `({ [foo]: async function() {} })`
1223 * ^^^^^^^^^^^^^^^^^^^^^
1224 * - `({ async foo() {} })`
1225 * ^^^^^^^^^
1226 * - `({ get foo() {} })`
1227 * ^^^^^^^
1228 * - `({ set foo(a) {} })`
1229 * ^^^^^^^
1230 * - `class A { constructor() {} }`
1231 * ^^^^^^^^^^^
1232 * - `class A { foo() {} }`
1233 * ^^^
1234 * - `class A { *foo() {} }`
1235 * ^^^^
1236 * - `class A { async foo() {} }`
1237 * ^^^^^^^^^
1238 * - `class A { ['foo']() {} }`
1239 * ^^^^^^^
1240 * - `class A { *['foo']() {} }`
1241 * ^^^^^^^^
1242 * - `class A { async ['foo']() {} }`
1243 * ^^^^^^^^^^^^^
1244 * - `class A { [foo]() {} }`
1245 * ^^^^^
1246 * - `class A { *[foo]() {} }`
1247 * ^^^^^^
1248 * - `class A { async [foo]() {} }`
1249 * ^^^^^^^^^^^
1250 * - `class A { get foo() {} }`
1251 * ^^^^^^^
1252 * - `class A { set foo(a) {} }`
1253 * ^^^^^^^
1254 * - `class A { static foo() {} }`
1255 * ^^^^^^^^^^
1256 * - `class A { static *foo() {} }`
1257 * ^^^^^^^^^^^
1258 * - `class A { static async foo() {} }`
1259 * ^^^^^^^^^^^^^^^^
1260 * - `class A { static get foo() {} }`
1261 * ^^^^^^^^^^^^^^
1262 * - `class A { static set foo(a) {} }`
1263 * ^^^^^^^^^^^^^^
1264 * @param {ASTNode} node The function node to get.
1265 * @param {SourceCode} sourceCode The source code object to get tokens.
1266 * @returns {string} The location of the function node for reporting.
1267 */
1268 getFunctionHeadLoc(node, sourceCode) {
1269 const parent = node.parent;
1270 let start = null;
1271 let end = null;
1272
1273 if (node.type === "ArrowFunctionExpression") {
1274 const arrowToken = sourceCode.getTokenBefore(node.body, isArrowToken);
1275
1276 start = arrowToken.loc.start;
1277 end = arrowToken.loc.end;
1278 } else if (parent.type === "Property" || parent.type === "MethodDefinition") {
1279 start = parent.loc.start;
1280 end = getOpeningParenOfParams(node, sourceCode).loc.start;
1281 } else {
1282 start = node.loc.start;
1283 end = getOpeningParenOfParams(node, sourceCode).loc.start;
1284 }
1285
1286 return {
1287 start: Object.assign({}, start),
1288 end: Object.assign({}, end)
1289 };
1290 },
1291
1292 /**
1293 * Gets next location when the result is not out of bound, otherwise returns null.
1294 *
1295 * Assumptions:
1296 *
1297 * - The given location represents a valid location in the given source code.
1298 * - Columns are 0-based.
1299 * - Lines are 1-based.
1300 * - Column immediately after the last character in a line (not incl. linebreaks) is considered to be a valid location.
1301 * - If the source code ends with a linebreak, `sourceCode.lines` array will have an extra element (empty string) at the end.
1302 * The start (column 0) of that extra line is considered to be a valid location.
1303 *
1304 * Examples of successive locations (line, column):
1305 *
1306 * code: foo
1307 * locations: (1, 0) -> (1, 1) -> (1, 2) -> (1, 3) -> null
1308 *
1309 * code: foo<LF>
1310 * locations: (1, 0) -> (1, 1) -> (1, 2) -> (1, 3) -> (2, 0) -> null
1311 *
1312 * code: foo<CR><LF>
1313 * locations: (1, 0) -> (1, 1) -> (1, 2) -> (1, 3) -> (2, 0) -> null
1314 *
1315 * code: a<LF>b
1316 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (2, 1) -> null
1317 *
1318 * code: a<LF>b<LF>
1319 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (2, 1) -> (3, 0) -> null
1320 *
1321 * code: a<CR><LF>b<CR><LF>
1322 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (2, 1) -> (3, 0) -> null
1323 *
1324 * code: a<LF><LF>
1325 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (3, 0) -> null
1326 *
1327 * code: <LF>
1328 * locations: (1, 0) -> (2, 0) -> null
1329 *
1330 * code:
1331 * locations: (1, 0) -> null
1332 * @param {SourceCode} sourceCode The sourceCode
1333 * @param {{line: number, column: number}} location The location
1334 * @returns {{line: number, column: number} | null} Next location
1335 */
1336 getNextLocation(sourceCode, { line, column }) {
1337 if (column < sourceCode.lines[line - 1].length) {
1338 return {
1339 line,
1340 column: column + 1
1341 };
1342 }
1343
1344 if (line < sourceCode.lines.length) {
1345 return {
1346 line: line + 1,
1347 column: 0
1348 };
1349 }
1350
1351 return null;
1352 },
1353
1354 /**
1355 * Gets the parenthesized text of a node. This is similar to sourceCode.getText(node), but it also includes any parentheses
1356 * surrounding the node.
1357 * @param {SourceCode} sourceCode The source code object
1358 * @param {ASTNode} node An expression node
1359 * @returns {string} The text representing the node, with all surrounding parentheses included
1360 */
1361 getParenthesisedText(sourceCode, node) {
1362 let leftToken = sourceCode.getFirstToken(node);
1363 let rightToken = sourceCode.getLastToken(node);
1364
1365 while (
1366 sourceCode.getTokenBefore(leftToken) &&
1367 sourceCode.getTokenBefore(leftToken).type === "Punctuator" &&
1368 sourceCode.getTokenBefore(leftToken).value === "(" &&
1369 sourceCode.getTokenAfter(rightToken) &&
1370 sourceCode.getTokenAfter(rightToken).type === "Punctuator" &&
1371 sourceCode.getTokenAfter(rightToken).value === ")"
1372 ) {
1373 leftToken = sourceCode.getTokenBefore(leftToken);
1374 rightToken = sourceCode.getTokenAfter(rightToken);
1375 }
1376
1377 return sourceCode.getText().slice(leftToken.range[0], rightToken.range[1]);
1378 },
1379
1380 /*
1381 * Determine if a node has a possiblity to be an Error object
1382 * @param {ASTNode} node ASTNode to check
1383 * @returns {boolean} True if there is a chance it contains an Error obj
1384 */
1385 couldBeError(node) {
1386 switch (node.type) {
1387 case "Identifier":
1388 case "CallExpression":
1389 case "NewExpression":
1390 case "MemberExpression":
1391 case "TaggedTemplateExpression":
1392 case "YieldExpression":
1393 case "AwaitExpression":
1394 return true; // possibly an error object.
1395
1396 case "AssignmentExpression":
1397 return module.exports.couldBeError(node.right);
1398
1399 case "SequenceExpression": {
1400 const exprs = node.expressions;
1401
1402 return exprs.length !== 0 && module.exports.couldBeError(exprs[exprs.length - 1]);
1403 }
1404
1405 case "LogicalExpression":
1406 return module.exports.couldBeError(node.left) || module.exports.couldBeError(node.right);
1407
1408 case "ConditionalExpression":
1409 return module.exports.couldBeError(node.consequent) || module.exports.couldBeError(node.alternate);
1410
1411 default:
1412 return false;
1413 }
1414 },
1415
1416 /**
1417 * Determines whether the given node is a `null` literal.
1418 * @param {ASTNode} node The node to check
1419 * @returns {boolean} `true` if the node is a `null` literal
1420 */
1421 isNullLiteral(node) {
1422
1423 /*
1424 * Checking `node.value === null` does not guarantee that a literal is a null literal.
1425 * When parsing values that cannot be represented in the current environment (e.g. unicode
1426 * regexes in Node 4), `node.value` is set to `null` because it wouldn't be possible to
1427 * set `node.value` to a unicode regex. To make sure a literal is actually `null`, check
1428 * `node.regex` instead. Also see: https://github.com/eslint/eslint/issues/8020
1429 */
1430 return node.type === "Literal" && node.value === null && !node.regex && !node.bigint;
1431 },
1432
1433 /**
1434 * Check if a given node is a numeric literal or not.
1435 * @param {ASTNode} node The node to check.
1436 * @returns {boolean} `true` if the node is a number or bigint literal.
1437 */
1438 isNumericLiteral(node) {
1439 return (
1440 node.type === "Literal" &&
1441 (typeof node.value === "number" || Boolean(node.bigint))
1442 );
1443 },
1444
1445 /**
1446 * Determines whether two tokens can safely be placed next to each other without merging into a single token
1447 * @param {Token|string} leftValue The left token. If this is a string, it will be tokenized and the last token will be used.
1448 * @param {Token|string} rightValue The right token. If this is a string, it will be tokenized and the first token will be used.
1449 * @returns {boolean} If the tokens cannot be safely placed next to each other, returns `false`. If the tokens can be placed
1450 * next to each other, behavior is undefined (although it should return `true` in most cases).
1451 */
1452 canTokensBeAdjacent(leftValue, rightValue) {
1453 const espreeOptions = {
1454 ecmaVersion: espree.latestEcmaVersion,
1455 comment: true,
1456 range: true
1457 };
1458
1459 let leftToken;
1460
1461 if (typeof leftValue === "string") {
1462 let tokens;
1463
1464 try {
1465 tokens = espree.tokenize(leftValue, espreeOptions);
1466 } catch {
1467 return false;
1468 }
1469
1470 const comments = tokens.comments;
1471
1472 leftToken = tokens[tokens.length - 1];
1473 if (comments.length) {
1474 const lastComment = comments[comments.length - 1];
1475
1476 if (lastComment.range[0] > leftToken.range[0]) {
1477 leftToken = lastComment;
1478 }
1479 }
1480 } else {
1481 leftToken = leftValue;
1482 }
1483
1484 if (leftToken.type === "Shebang") {
1485 return false;
1486 }
1487
1488 let rightToken;
1489
1490 if (typeof rightValue === "string") {
1491 let tokens;
1492
1493 try {
1494 tokens = espree.tokenize(rightValue, espreeOptions);
1495 } catch {
1496 return false;
1497 }
1498
1499 const comments = tokens.comments;
1500
1501 rightToken = tokens[0];
1502 if (comments.length) {
1503 const firstComment = comments[0];
1504
1505 if (firstComment.range[0] < rightToken.range[0]) {
1506 rightToken = firstComment;
1507 }
1508 }
1509 } else {
1510 rightToken = rightValue;
1511 }
1512
1513 if (leftToken.type === "Punctuator" || rightToken.type === "Punctuator") {
1514 if (leftToken.type === "Punctuator" && rightToken.type === "Punctuator") {
1515 const PLUS_TOKENS = new Set(["+", "++"]);
1516 const MINUS_TOKENS = new Set(["-", "--"]);
1517
1518 return !(
1519 PLUS_TOKENS.has(leftToken.value) && PLUS_TOKENS.has(rightToken.value) ||
1520 MINUS_TOKENS.has(leftToken.value) && MINUS_TOKENS.has(rightToken.value)
1521 );
1522 }
1523 if (leftToken.type === "Punctuator" && leftToken.value === "/") {
1524 return !["Block", "Line", "RegularExpression"].includes(rightToken.type);
1525 }
1526 return true;
1527 }
1528
1529 if (
1530 leftToken.type === "String" || rightToken.type === "String" ||
1531 leftToken.type === "Template" || rightToken.type === "Template"
1532 ) {
1533 return true;
1534 }
1535
1536 if (leftToken.type !== "Numeric" && rightToken.type === "Numeric" && rightToken.value.startsWith(".")) {
1537 return true;
1538 }
1539
1540 if (leftToken.type === "Block" || rightToken.type === "Block" || rightToken.type === "Line") {
1541 return true;
1542 }
1543
1544 return false;
1545 },
1546
1547 /**
1548 * Get the `loc` object of a given name in a `/*globals` directive comment.
1549 * @param {SourceCode} sourceCode The source code to convert index to loc.
1550 * @param {Comment} comment The `/*globals` directive comment which include the name.
1551 * @param {string} name The name to find.
1552 * @returns {SourceLocation} The `loc` object.
1553 */
1554 getNameLocationInGlobalDirectiveComment(sourceCode, comment, name) {
1555 const namePattern = new RegExp(`[\\s,]${lodash.escapeRegExp(name)}(?:$|[\\s,:])`, "gu");
1556
1557 // To ignore the first text "global".
1558 namePattern.lastIndex = comment.value.indexOf("global") + 6;
1559
1560 // Search a given variable name.
1561 const match = namePattern.exec(comment.value);
1562
1563 // Convert the index to loc.
1564 const start = sourceCode.getLocFromIndex(
1565 comment.range[0] +
1566 "/*".length +
1567 (match ? match.index + 1 : 0)
1568 );
1569 const end = {
1570 line: start.line,
1571 column: start.column + (match ? name.length : 1)
1572 };
1573
1574 return { start, end };
1575 },
1576
1577 /**
1578 * Determines whether the given raw string contains an octal escape sequence.
1579 *
1580 * "\1", "\2" ... "\7"
1581 * "\00", "\01" ... "\09"
1582 *
1583 * "\0", when not followed by a digit, is not an octal escape sequence.
1584 * @param {string} rawString A string in its raw representation.
1585 * @returns {boolean} `true` if the string contains at least one octal escape sequence.
1586 */
1587 hasOctalEscapeSequence(rawString) {
1588 return OCTAL_ESCAPE_PATTERN.test(rawString);
1589 },
1590
1591 isLogicalExpression,
1592 isCoalesceExpression,
1593 isMixedLogicalAndCoalesceExpressions
1594 };