source: imaps-frontend/node_modules/eslint/lib/rules/utils/ast-utils.js@ d565449

main
Last change on this file since d565449 was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

  • Property mode set to 100644
File size: 79.0 KB
Line 
1/**
2 * @fileoverview Common utils for AST.
3 * @author Gyandeep Singh
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Requirements
10//------------------------------------------------------------------------------
11
12const { KEYS: eslintVisitorKeys } = require("eslint-visitor-keys");
13const esutils = require("esutils");
14const espree = require("espree");
15const escapeRegExp = require("escape-string-regexp");
16const {
17 breakableTypePattern,
18 createGlobalLinebreakMatcher,
19 lineBreakPattern,
20 shebangPattern
21} = require("../../shared/ast-utils");
22
23//------------------------------------------------------------------------------
24// Helpers
25//------------------------------------------------------------------------------
26
27const anyFunctionPattern = /^(?:Function(?:Declaration|Expression)|ArrowFunctionExpression)$/u;
28const anyLoopPattern = /^(?:DoWhile|For|ForIn|ForOf|While)Statement$/u;
29const arrayMethodWithThisArgPattern = /^(?:every|filter|find(?:Last)?(?:Index)?|flatMap|forEach|map|some)$/u;
30const arrayOrTypedArrayPattern = /Array$/u;
31const bindOrCallOrApplyPattern = /^(?:bind|call|apply)$/u;
32const thisTagPattern = /^[\s*]*@this/mu;
33
34
35const COMMENTS_IGNORE_PATTERN = /^\s*(?:eslint|jshint\s+|jslint\s+|istanbul\s+|globals?\s+|exported\s+|jscs)/u;
36const ESLINT_DIRECTIVE_PATTERN = /^(?:eslint[- ]|(?:globals?|exported) )/u;
37const LINEBREAKS = new Set(["\r\n", "\r", "\n", "\u2028", "\u2029"]);
38
39// A set of node types that can contain a list of statements
40const STATEMENT_LIST_PARENTS = new Set(["Program", "BlockStatement", "StaticBlock", "SwitchCase"]);
41
42const DECIMAL_INTEGER_PATTERN = /^(?:0|0[0-7]*[89]\d*|[1-9](?:_?\d)*)$/u;
43
44// Tests the presence of at least one LegacyOctalEscapeSequence or NonOctalDecimalEscapeSequence in a raw string
45const OCTAL_OR_NON_OCTAL_DECIMAL_ESCAPE_PATTERN = /^(?:[^\\]|\\.)*\\(?:[1-9]|0[0-9])/su;
46
47const LOGICAL_ASSIGNMENT_OPERATORS = new Set(["&&=", "||=", "??="]);
48
49/**
50 * Checks reference if is non initializer and writable.
51 * @param {Reference} reference A reference to check.
52 * @param {int} index The index of the reference in the references.
53 * @param {Reference[]} references The array that the reference belongs to.
54 * @returns {boolean} Success/Failure
55 * @private
56 */
57function isModifyingReference(reference, index, references) {
58 const identifier = reference.identifier;
59
60 /*
61 * Destructuring assignments can have multiple default value, so
62 * possibly there are multiple writeable references for the same
63 * identifier.
64 */
65 const modifyingDifferentIdentifier = index === 0 ||
66 references[index - 1].identifier !== identifier;
67
68 return (identifier &&
69 reference.init === false &&
70 reference.isWrite() &&
71 modifyingDifferentIdentifier
72 );
73}
74
75/**
76 * Checks whether the given string starts with uppercase or not.
77 * @param {string} s The string to check.
78 * @returns {boolean} `true` if the string starts with uppercase.
79 */
80function startsWithUpperCase(s) {
81 return s[0] !== s[0].toLocaleLowerCase();
82}
83
84/**
85 * Checks whether or not a node is a constructor.
86 * @param {ASTNode} node A function node to check.
87 * @returns {boolean} Whether or not a node is a constructor.
88 */
89function isES5Constructor(node) {
90 return (node.id && startsWithUpperCase(node.id.name));
91}
92
93/**
94 * Finds a function node from ancestors of a node.
95 * @param {ASTNode} node A start node to find.
96 * @returns {Node|null} A found function node.
97 */
98function getUpperFunction(node) {
99 for (let currentNode = node; currentNode; currentNode = currentNode.parent) {
100 if (anyFunctionPattern.test(currentNode.type)) {
101 return currentNode;
102 }
103 }
104 return null;
105}
106
107/**
108 * Checks whether a given node is a function node or not.
109 * The following types are function nodes:
110 *
111 * - ArrowFunctionExpression
112 * - FunctionDeclaration
113 * - FunctionExpression
114 * @param {ASTNode|null} node A node to check.
115 * @returns {boolean} `true` if the node is a function node.
116 */
117function isFunction(node) {
118 return Boolean(node && anyFunctionPattern.test(node.type));
119}
120
121/**
122 * Checks whether a given node is a loop node or not.
123 * The following types are loop nodes:
124 *
125 * - DoWhileStatement
126 * - ForInStatement
127 * - ForOfStatement
128 * - ForStatement
129 * - WhileStatement
130 * @param {ASTNode|null} node A node to check.
131 * @returns {boolean} `true` if the node is a loop node.
132 */
133function isLoop(node) {
134 return Boolean(node && anyLoopPattern.test(node.type));
135}
136
137/**
138 * Checks whether the given node is in a loop or not.
139 * @param {ASTNode} node The node to check.
140 * @returns {boolean} `true` if the node is in a loop.
141 */
142function isInLoop(node) {
143 for (let currentNode = node; currentNode && !isFunction(currentNode); currentNode = currentNode.parent) {
144 if (isLoop(currentNode)) {
145 return true;
146 }
147 }
148
149 return false;
150}
151
152/**
153 * Determines whether the given node is a `null` literal.
154 * @param {ASTNode} node The node to check
155 * @returns {boolean} `true` if the node is a `null` literal
156 */
157function isNullLiteral(node) {
158
159 /*
160 * Checking `node.value === null` does not guarantee that a literal is a null literal.
161 * When parsing values that cannot be represented in the current environment (e.g. unicode
162 * regexes in Node 4), `node.value` is set to `null` because it wouldn't be possible to
163 * set `node.value` to a unicode regex. To make sure a literal is actually `null`, check
164 * `node.regex` instead. Also see: https://github.com/eslint/eslint/issues/8020
165 */
166 return node.type === "Literal" && node.value === null && !node.regex && !node.bigint;
167}
168
169/**
170 * Checks whether or not a node is `null` or `undefined`.
171 * @param {ASTNode} node A node to check.
172 * @returns {boolean} Whether or not the node is a `null` or `undefined`.
173 * @public
174 */
175function isNullOrUndefined(node) {
176 return (
177 isNullLiteral(node) ||
178 (node.type === "Identifier" && node.name === "undefined") ||
179 (node.type === "UnaryExpression" && node.operator === "void")
180 );
181}
182
183/**
184 * Checks whether or not a node is callee.
185 * @param {ASTNode} node A node to check.
186 * @returns {boolean} Whether or not the node is callee.
187 */
188function isCallee(node) {
189 return node.parent.type === "CallExpression" && node.parent.callee === node;
190}
191
192/**
193 * Returns the result of the string conversion applied to the evaluated value of the given expression node,
194 * if it can be determined statically.
195 *
196 * This function returns a `string` value for all `Literal` nodes and simple `TemplateLiteral` nodes only.
197 * In all other cases, this function returns `null`.
198 * @param {ASTNode} node Expression node.
199 * @returns {string|null} String value if it can be determined. Otherwise, `null`.
200 */
201function getStaticStringValue(node) {
202 switch (node.type) {
203 case "Literal":
204 if (node.value === null) {
205 if (isNullLiteral(node)) {
206 return String(node.value); // "null"
207 }
208 if (node.regex) {
209 return `/${node.regex.pattern}/${node.regex.flags}`;
210 }
211 if (node.bigint) {
212 return node.bigint;
213 }
214
215 // Otherwise, this is an unknown literal. The function will return null.
216
217 } else {
218 return String(node.value);
219 }
220 break;
221 case "TemplateLiteral":
222 if (node.expressions.length === 0 && node.quasis.length === 1) {
223 return node.quasis[0].value.cooked;
224 }
225 break;
226
227 // no default
228 }
229
230 return null;
231}
232
233/**
234 * Gets the property name of a given node.
235 * The node can be a MemberExpression, a Property, or a MethodDefinition.
236 *
237 * If the name is dynamic, this returns `null`.
238 *
239 * For examples:
240 *
241 * a.b // => "b"
242 * a["b"] // => "b"
243 * a['b'] // => "b"
244 * a[`b`] // => "b"
245 * a[100] // => "100"
246 * a[b] // => null
247 * a["a" + "b"] // => null
248 * a[tag`b`] // => null
249 * a[`${b}`] // => null
250 *
251 * let a = {b: 1} // => "b"
252 * let a = {["b"]: 1} // => "b"
253 * let a = {['b']: 1} // => "b"
254 * let a = {[`b`]: 1} // => "b"
255 * let a = {[100]: 1} // => "100"
256 * let a = {[b]: 1} // => null
257 * let a = {["a" + "b"]: 1} // => null
258 * let a = {[tag`b`]: 1} // => null
259 * let a = {[`${b}`]: 1} // => null
260 * @param {ASTNode} node The node to get.
261 * @returns {string|null} The property name if static. Otherwise, null.
262 */
263function getStaticPropertyName(node) {
264 let prop;
265
266 switch (node && node.type) {
267 case "ChainExpression":
268 return getStaticPropertyName(node.expression);
269
270 case "Property":
271 case "PropertyDefinition":
272 case "MethodDefinition":
273 prop = node.key;
274 break;
275
276 case "MemberExpression":
277 prop = node.property;
278 break;
279
280 // no default
281 }
282
283 if (prop) {
284 if (prop.type === "Identifier" && !node.computed) {
285 return prop.name;
286 }
287
288 return getStaticStringValue(prop);
289 }
290
291 return null;
292}
293
294/**
295 * Retrieve `ChainExpression#expression` value if the given node a `ChainExpression` node. Otherwise, pass through it.
296 * @param {ASTNode} node The node to address.
297 * @returns {ASTNode} The `ChainExpression#expression` value if the node is a `ChainExpression` node. Otherwise, the node.
298 */
299function skipChainExpression(node) {
300 return node && node.type === "ChainExpression" ? node.expression : node;
301}
302
303/**
304 * Check if the `actual` is an expected value.
305 * @param {string} actual The string value to check.
306 * @param {string | RegExp} expected The expected string value or pattern.
307 * @returns {boolean} `true` if the `actual` is an expected value.
308 */
309function checkText(actual, expected) {
310 return typeof expected === "string"
311 ? actual === expected
312 : expected.test(actual);
313}
314
315/**
316 * Check if a given node is an Identifier node with a given name.
317 * @param {ASTNode} node The node to check.
318 * @param {string | RegExp} name The expected name or the expected pattern of the object name.
319 * @returns {boolean} `true` if the node is an Identifier node with the name.
320 */
321function isSpecificId(node, name) {
322 return node.type === "Identifier" && checkText(node.name, name);
323}
324
325/**
326 * Check if a given node is member access with a given object name and property name pair.
327 * This is regardless of optional or not.
328 * @param {ASTNode} node The node to check.
329 * @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.
330 * @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.
331 * @returns {boolean} `true` if the node is member access with the object name and property name pair.
332 * The node is a `MemberExpression` or `ChainExpression`.
333 */
334function isSpecificMemberAccess(node, objectName, propertyName) {
335 const checkNode = skipChainExpression(node);
336
337 if (checkNode.type !== "MemberExpression") {
338 return false;
339 }
340
341 if (objectName && !isSpecificId(checkNode.object, objectName)) {
342 return false;
343 }
344
345 if (propertyName) {
346 const actualPropertyName = getStaticPropertyName(checkNode);
347
348 if (typeof actualPropertyName !== "string" || !checkText(actualPropertyName, propertyName)) {
349 return false;
350 }
351 }
352
353 return true;
354}
355
356/**
357 * Check if two literal nodes are the same value.
358 * @param {ASTNode} left The Literal node to compare.
359 * @param {ASTNode} right The other Literal node to compare.
360 * @returns {boolean} `true` if the two literal nodes are the same value.
361 */
362function equalLiteralValue(left, right) {
363
364 // RegExp literal.
365 if (left.regex || right.regex) {
366 return Boolean(
367 left.regex &&
368 right.regex &&
369 left.regex.pattern === right.regex.pattern &&
370 left.regex.flags === right.regex.flags
371 );
372 }
373
374 // BigInt literal.
375 if (left.bigint || right.bigint) {
376 return left.bigint === right.bigint;
377 }
378
379 return left.value === right.value;
380}
381
382/**
383 * Check if two expressions reference the same value. For example:
384 * a = a
385 * a.b = a.b
386 * a[0] = a[0]
387 * a['b'] = a['b']
388 * @param {ASTNode} left The left side of the comparison.
389 * @param {ASTNode} right The right side of the comparison.
390 * @param {boolean} [disableStaticComputedKey] Don't address `a.b` and `a["b"]` are the same if `true`. For backward compatibility.
391 * @returns {boolean} `true` if both sides match and reference the same value.
392 */
393function isSameReference(left, right, disableStaticComputedKey = false) {
394 if (left.type !== right.type) {
395
396 // Handle `a.b` and `a?.b` are samely.
397 if (left.type === "ChainExpression") {
398 return isSameReference(left.expression, right, disableStaticComputedKey);
399 }
400 if (right.type === "ChainExpression") {
401 return isSameReference(left, right.expression, disableStaticComputedKey);
402 }
403
404 return false;
405 }
406
407 switch (left.type) {
408 case "Super":
409 case "ThisExpression":
410 return true;
411
412 case "Identifier":
413 case "PrivateIdentifier":
414 return left.name === right.name;
415 case "Literal":
416 return equalLiteralValue(left, right);
417
418 case "ChainExpression":
419 return isSameReference(left.expression, right.expression, disableStaticComputedKey);
420
421 case "MemberExpression": {
422 if (!disableStaticComputedKey) {
423 const nameA = getStaticPropertyName(left);
424
425 // x.y = x["y"]
426 if (nameA !== null) {
427 return (
428 isSameReference(left.object, right.object, disableStaticComputedKey) &&
429 nameA === getStaticPropertyName(right)
430 );
431 }
432 }
433
434 /*
435 * x[0] = x[0]
436 * x[y] = x[y]
437 * x.y = x.y
438 */
439 return (
440 left.computed === right.computed &&
441 isSameReference(left.object, right.object, disableStaticComputedKey) &&
442 isSameReference(left.property, right.property, disableStaticComputedKey)
443 );
444 }
445
446 default:
447 return false;
448 }
449}
450
451/**
452 * Checks whether or not a node is `Reflect.apply`.
453 * @param {ASTNode} node A node to check.
454 * @returns {boolean} Whether or not the node is a `Reflect.apply`.
455 */
456function isReflectApply(node) {
457 return isSpecificMemberAccess(node, "Reflect", "apply");
458}
459
460/**
461 * Checks whether or not a node is `Array.from`.
462 * @param {ASTNode} node A node to check.
463 * @returns {boolean} Whether or not the node is a `Array.from`.
464 */
465function isArrayFromMethod(node) {
466 return isSpecificMemberAccess(node, arrayOrTypedArrayPattern, "from");
467}
468
469/**
470 * Checks whether or not a node is a method which expects a function as a first argument, and `thisArg` as a second argument.
471 * @param {ASTNode} node A node to check.
472 * @returns {boolean} Whether or not the node is a method which expects a function as a first argument, and `thisArg` as a second argument.
473 */
474function isMethodWhichHasThisArg(node) {
475 return isSpecificMemberAccess(node, null, arrayMethodWithThisArgPattern);
476}
477
478/**
479 * Creates the negate function of the given function.
480 * @param {Function} f The function to negate.
481 * @returns {Function} Negated function.
482 */
483function negate(f) {
484 return token => !f(token);
485}
486
487/**
488 * Checks whether or not a node has a `@this` tag in its comments.
489 * @param {ASTNode} node A node to check.
490 * @param {SourceCode} sourceCode A SourceCode instance to get comments.
491 * @returns {boolean} Whether or not the node has a `@this` tag in its comments.
492 */
493function hasJSDocThisTag(node, sourceCode) {
494 const jsdocComment = sourceCode.getJSDocComment(node);
495
496 if (jsdocComment && thisTagPattern.test(jsdocComment.value)) {
497 return true;
498 }
499
500 // Checks `@this` in its leading comments for callbacks,
501 // because callbacks don't have its JSDoc comment.
502 // e.g.
503 // sinon.test(/* @this sinon.Sandbox */function() { this.spy(); });
504 return sourceCode.getCommentsBefore(node).some(comment => thisTagPattern.test(comment.value));
505}
506
507/**
508 * Determines if a node is surrounded by parentheses.
509 * @param {SourceCode} sourceCode The ESLint source code object
510 * @param {ASTNode} node The node to be checked.
511 * @returns {boolean} True if the node is parenthesised.
512 * @private
513 */
514function isParenthesised(sourceCode, node) {
515 const previousToken = sourceCode.getTokenBefore(node),
516 nextToken = sourceCode.getTokenAfter(node);
517
518 return Boolean(previousToken && nextToken) &&
519 previousToken.value === "(" && previousToken.range[1] <= node.range[0] &&
520 nextToken.value === ")" && nextToken.range[0] >= node.range[1];
521}
522
523/**
524 * Checks if the given token is a `=` token or not.
525 * @param {Token} token The token to check.
526 * @returns {boolean} `true` if the token is a `=` token.
527 */
528function isEqToken(token) {
529 return token.value === "=" && token.type === "Punctuator";
530}
531
532/**
533 * Checks if the given token is an arrow token or not.
534 * @param {Token} token The token to check.
535 * @returns {boolean} `true` if the token is an arrow token.
536 */
537function isArrowToken(token) {
538 return token.value === "=>" && token.type === "Punctuator";
539}
540
541/**
542 * Checks if the given token is a comma token or not.
543 * @param {Token} token The token to check.
544 * @returns {boolean} `true` if the token is a comma token.
545 */
546function isCommaToken(token) {
547 return token.value === "," && token.type === "Punctuator";
548}
549
550/**
551 * Checks if the given token is a dot token or not.
552 * @param {Token} token The token to check.
553 * @returns {boolean} `true` if the token is a dot token.
554 */
555function isDotToken(token) {
556 return token.value === "." && token.type === "Punctuator";
557}
558
559/**
560 * Checks if the given token is a `?.` token or not.
561 * @param {Token} token The token to check.
562 * @returns {boolean} `true` if the token is a `?.` token.
563 */
564function isQuestionDotToken(token) {
565 return token.value === "?." && token.type === "Punctuator";
566}
567
568/**
569 * Checks if the given token is a semicolon token or not.
570 * @param {Token} token The token to check.
571 * @returns {boolean} `true` if the token is a semicolon token.
572 */
573function isSemicolonToken(token) {
574 return token.value === ";" && token.type === "Punctuator";
575}
576
577/**
578 * Checks if the given token is a colon token or not.
579 * @param {Token} token The token to check.
580 * @returns {boolean} `true` if the token is a colon token.
581 */
582function isColonToken(token) {
583 return token.value === ":" && token.type === "Punctuator";
584}
585
586/**
587 * Checks if the given token is an opening parenthesis token or not.
588 * @param {Token} token The token to check.
589 * @returns {boolean} `true` if the token is an opening parenthesis token.
590 */
591function isOpeningParenToken(token) {
592 return token.value === "(" && token.type === "Punctuator";
593}
594
595/**
596 * Checks if the given token is a closing parenthesis token or not.
597 * @param {Token} token The token to check.
598 * @returns {boolean} `true` if the token is a closing parenthesis token.
599 */
600function isClosingParenToken(token) {
601 return token.value === ")" && token.type === "Punctuator";
602}
603
604/**
605 * Checks if the given token is an opening square bracket token or not.
606 * @param {Token} token The token to check.
607 * @returns {boolean} `true` if the token is an opening square bracket token.
608 */
609function isOpeningBracketToken(token) {
610 return token.value === "[" && token.type === "Punctuator";
611}
612
613/**
614 * Checks if the given token is a closing square bracket token or not.
615 * @param {Token} token The token to check.
616 * @returns {boolean} `true` if the token is a closing square bracket token.
617 */
618function isClosingBracketToken(token) {
619 return token.value === "]" && token.type === "Punctuator";
620}
621
622/**
623 * Checks if the given token is an opening brace token or not.
624 * @param {Token} token The token to check.
625 * @returns {boolean} `true` if the token is an opening brace token.
626 */
627function isOpeningBraceToken(token) {
628 return token.value === "{" && token.type === "Punctuator";
629}
630
631/**
632 * Checks if the given token is a closing brace token or not.
633 * @param {Token} token The token to check.
634 * @returns {boolean} `true` if the token is a closing brace token.
635 */
636function isClosingBraceToken(token) {
637 return token.value === "}" && token.type === "Punctuator";
638}
639
640/**
641 * Checks if the given token is a comment token or not.
642 * @param {Token} token The token to check.
643 * @returns {boolean} `true` if the token is a comment token.
644 */
645function isCommentToken(token) {
646 return token.type === "Line" || token.type === "Block" || token.type === "Shebang";
647}
648
649/**
650 * Checks if the given token is a keyword token or not.
651 * @param {Token} token The token to check.
652 * @returns {boolean} `true` if the token is a keyword token.
653 */
654function isKeywordToken(token) {
655 return token.type === "Keyword";
656}
657
658/**
659 * Gets the `(` token of the given function node.
660 * @param {ASTNode} node The function node to get.
661 * @param {SourceCode} sourceCode The source code object to get tokens.
662 * @returns {Token} `(` token.
663 */
664function getOpeningParenOfParams(node, sourceCode) {
665
666 // If the node is an arrow function and doesn't have parens, this returns the identifier of the first param.
667 if (node.type === "ArrowFunctionExpression" && node.params.length === 1) {
668 const argToken = sourceCode.getFirstToken(node.params[0]);
669 const maybeParenToken = sourceCode.getTokenBefore(argToken);
670
671 return isOpeningParenToken(maybeParenToken) ? maybeParenToken : argToken;
672 }
673
674 // Otherwise, returns paren.
675 return node.id
676 ? sourceCode.getTokenAfter(node.id, isOpeningParenToken)
677 : sourceCode.getFirstToken(node, isOpeningParenToken);
678}
679
680/**
681 * Checks whether or not the tokens of two given nodes are same.
682 * @param {ASTNode} left A node 1 to compare.
683 * @param {ASTNode} right A node 2 to compare.
684 * @param {SourceCode} sourceCode The ESLint source code object.
685 * @returns {boolean} the source code for the given node.
686 */
687function equalTokens(left, right, sourceCode) {
688 const tokensL = sourceCode.getTokens(left);
689 const tokensR = sourceCode.getTokens(right);
690
691 if (tokensL.length !== tokensR.length) {
692 return false;
693 }
694 for (let i = 0; i < tokensL.length; ++i) {
695 if (tokensL[i].type !== tokensR[i].type ||
696 tokensL[i].value !== tokensR[i].value
697 ) {
698 return false;
699 }
700 }
701
702 return true;
703}
704
705/**
706 * Check if the given node is a true logical expression or not.
707 *
708 * The three binary expressions logical-or (`||`), logical-and (`&&`), and
709 * coalesce (`??`) are known as `ShortCircuitExpression`.
710 * But ESTree represents those by `LogicalExpression` node.
711 *
712 * This function rejects coalesce expressions of `LogicalExpression` node.
713 * @param {ASTNode} node The node to check.
714 * @returns {boolean} `true` if the node is `&&` or `||`.
715 * @see https://tc39.es/ecma262/#prod-ShortCircuitExpression
716 */
717function isLogicalExpression(node) {
718 return (
719 node.type === "LogicalExpression" &&
720 (node.operator === "&&" || node.operator === "||")
721 );
722}
723
724/**
725 * Check if the given node is a nullish coalescing expression or not.
726 *
727 * The three binary expressions logical-or (`||`), logical-and (`&&`), and
728 * coalesce (`??`) are known as `ShortCircuitExpression`.
729 * But ESTree represents those by `LogicalExpression` node.
730 *
731 * This function finds only coalesce expressions of `LogicalExpression` node.
732 * @param {ASTNode} node The node to check.
733 * @returns {boolean} `true` if the node is `??`.
734 */
735function isCoalesceExpression(node) {
736 return node.type === "LogicalExpression" && node.operator === "??";
737}
738
739/**
740 * Check if given two nodes are the pair of a logical expression and a coalesce expression.
741 * @param {ASTNode} left A node to check.
742 * @param {ASTNode} right Another node to check.
743 * @returns {boolean} `true` if the two nodes are the pair of a logical expression and a coalesce expression.
744 */
745function isMixedLogicalAndCoalesceExpressions(left, right) {
746 return (
747 (isLogicalExpression(left) && isCoalesceExpression(right)) ||
748 (isCoalesceExpression(left) && isLogicalExpression(right))
749 );
750}
751
752/**
753 * Checks if the given operator is a logical assignment operator.
754 * @param {string} operator The operator to check.
755 * @returns {boolean} `true` if the operator is a logical assignment operator.
756 */
757function isLogicalAssignmentOperator(operator) {
758 return LOGICAL_ASSIGNMENT_OPERATORS.has(operator);
759}
760
761/**
762 * Get the colon token of the given SwitchCase node.
763 * @param {ASTNode} node The SwitchCase node to get.
764 * @param {SourceCode} sourceCode The source code object to get tokens.
765 * @returns {Token} The colon token of the node.
766 */
767function getSwitchCaseColonToken(node, sourceCode) {
768 if (node.test) {
769 return sourceCode.getTokenAfter(node.test, isColonToken);
770 }
771 return sourceCode.getFirstToken(node, 1);
772}
773
774/**
775 * Gets ESM module export name represented by the given node.
776 * @param {ASTNode} node `Identifier` or string `Literal` node in a position
777 * that represents a module export name:
778 * - `ImportSpecifier#imported`
779 * - `ExportSpecifier#local` (if it is a re-export from another module)
780 * - `ExportSpecifier#exported`
781 * - `ExportAllDeclaration#exported`
782 * @returns {string} The module export name.
783 */
784function getModuleExportName(node) {
785 if (node.type === "Identifier") {
786 return node.name;
787 }
788
789 // string literal
790 return node.value;
791}
792
793/**
794 * Returns literal's value converted to the Boolean type
795 * @param {ASTNode} node any `Literal` node
796 * @returns {boolean | null} `true` when node is truthy, `false` when node is falsy,
797 * `null` when it cannot be determined.
798 */
799function getBooleanValue(node) {
800 if (node.value === null) {
801
802 /*
803 * it might be a null literal or bigint/regex literal in unsupported environments .
804 * https://github.com/estree/estree/blob/14df8a024956ea289bd55b9c2226a1d5b8a473ee/es5.md#regexpliteral
805 * https://github.com/estree/estree/blob/14df8a024956ea289bd55b9c2226a1d5b8a473ee/es2020.md#bigintliteral
806 */
807
808 if (node.raw === "null") {
809 return false;
810 }
811
812 // regex is always truthy
813 if (typeof node.regex === "object") {
814 return true;
815 }
816
817 return null;
818 }
819
820 return !!node.value;
821}
822
823/**
824 * Checks if a branch node of LogicalExpression short circuits the whole condition
825 * @param {ASTNode} node The branch of main condition which needs to be checked
826 * @param {string} operator The operator of the main LogicalExpression.
827 * @returns {boolean} true when condition short circuits whole condition
828 */
829function isLogicalIdentity(node, operator) {
830 switch (node.type) {
831 case "Literal":
832 return (operator === "||" && getBooleanValue(node) === true) ||
833 (operator === "&&" && getBooleanValue(node) === false);
834
835 case "UnaryExpression":
836 return (operator === "&&" && node.operator === "void");
837
838 case "LogicalExpression":
839
840 /*
841 * handles `a && false || b`
842 * `false` is an identity element of `&&` but not `||`
843 */
844 return operator === node.operator &&
845 (
846 isLogicalIdentity(node.left, operator) ||
847 isLogicalIdentity(node.right, operator)
848 );
849
850 case "AssignmentExpression":
851 return ["||=", "&&="].includes(node.operator) &&
852 operator === node.operator.slice(0, -1) &&
853 isLogicalIdentity(node.right, operator);
854
855 // no default
856 }
857 return false;
858}
859
860/**
861 * Checks if an identifier is a reference to a global variable.
862 * @param {Scope} scope The scope in which the identifier is referenced.
863 * @param {ASTNode} node An identifier node to check.
864 * @returns {boolean} `true` if the identifier is a reference to a global variable.
865 */
866function isReferenceToGlobalVariable(scope, node) {
867 const reference = scope.references.find(ref => ref.identifier === node);
868
869 return Boolean(
870 reference &&
871 reference.resolved &&
872 reference.resolved.scope.type === "global" &&
873 reference.resolved.defs.length === 0
874 );
875}
876
877
878/**
879 * Checks if a node has a constant truthiness value.
880 * @param {Scope} scope Scope in which the node appears.
881 * @param {ASTNode} node The AST node to check.
882 * @param {boolean} inBooleanPosition `true` if checking the test of a
883 * condition. `false` in all other cases. When `false`, checks if -- for
884 * both string and number -- if coerced to that type, the value will
885 * be constant.
886 * @returns {boolean} true when node's truthiness is constant
887 * @private
888 */
889function isConstant(scope, node, inBooleanPosition) {
890
891 // node.elements can return null values in the case of sparse arrays ex. [,]
892 if (!node) {
893 return true;
894 }
895 switch (node.type) {
896 case "Literal":
897 case "ArrowFunctionExpression":
898 case "FunctionExpression":
899 return true;
900 case "ClassExpression":
901 case "ObjectExpression":
902
903 /**
904 * In theory objects like:
905 *
906 * `{toString: () => a}`
907 * `{valueOf: () => a}`
908 *
909 * Or a classes like:
910 *
911 * `class { static toString() { return a } }`
912 * `class { static valueOf() { return a } }`
913 *
914 * Are not constant verifiably when `inBooleanPosition` is
915 * false, but it's an edge case we've opted not to handle.
916 */
917 return true;
918 case "TemplateLiteral":
919 return (inBooleanPosition && node.quasis.some(quasi => quasi.value.cooked.length)) ||
920 node.expressions.every(exp => isConstant(scope, exp, false));
921
922 case "ArrayExpression": {
923 if (!inBooleanPosition) {
924 return node.elements.every(element => isConstant(scope, element, false));
925 }
926 return true;
927 }
928
929 case "UnaryExpression":
930 if (
931 node.operator === "void" ||
932 node.operator === "typeof" && inBooleanPosition
933 ) {
934 return true;
935 }
936
937 if (node.operator === "!") {
938 return isConstant(scope, node.argument, true);
939 }
940
941 return isConstant(scope, node.argument, false);
942
943 case "BinaryExpression":
944 return isConstant(scope, node.left, false) &&
945 isConstant(scope, node.right, false) &&
946 node.operator !== "in";
947
948 case "LogicalExpression": {
949 const isLeftConstant = isConstant(scope, node.left, inBooleanPosition);
950 const isRightConstant = isConstant(scope, node.right, inBooleanPosition);
951 const isLeftShortCircuit = (isLeftConstant && isLogicalIdentity(node.left, node.operator));
952 const isRightShortCircuit = (inBooleanPosition && isRightConstant && isLogicalIdentity(node.right, node.operator));
953
954 return (isLeftConstant && isRightConstant) ||
955 isLeftShortCircuit ||
956 isRightShortCircuit;
957 }
958 case "NewExpression":
959 return inBooleanPosition;
960 case "AssignmentExpression":
961 if (node.operator === "=") {
962 return isConstant(scope, node.right, inBooleanPosition);
963 }
964
965 if (["||=", "&&="].includes(node.operator) && inBooleanPosition) {
966 return isLogicalIdentity(node.right, node.operator.slice(0, -1));
967 }
968
969 return false;
970
971 case "SequenceExpression":
972 return isConstant(scope, node.expressions[node.expressions.length - 1], inBooleanPosition);
973 case "SpreadElement":
974 return isConstant(scope, node.argument, inBooleanPosition);
975 case "CallExpression":
976 if (node.callee.type === "Identifier" && node.callee.name === "Boolean") {
977 if (node.arguments.length === 0 || isConstant(scope, node.arguments[0], true)) {
978 return isReferenceToGlobalVariable(scope, node.callee);
979 }
980 }
981 return false;
982 case "Identifier":
983 return node.name === "undefined" && isReferenceToGlobalVariable(scope, node);
984
985 // no default
986 }
987 return false;
988}
989
990/**
991 * Checks whether a node is an ExpressionStatement at the top level of a file or function body.
992 * A top-level ExpressionStatement node is a directive if it contains a single unparenthesized
993 * string literal and if it occurs either as the first sibling or immediately after another
994 * directive.
995 * @param {ASTNode} node The node to check.
996 * @returns {boolean} Whether or not the node is an ExpressionStatement at the top level of a
997 * file or function body.
998 */
999function isTopLevelExpressionStatement(node) {
1000 if (node.type !== "ExpressionStatement") {
1001 return false;
1002 }
1003 const parent = node.parent;
1004
1005 return parent.type === "Program" || (parent.type === "BlockStatement" && isFunction(parent.parent));
1006
1007}
1008
1009/**
1010 * Check whether the given node is a part of a directive prologue or not.
1011 * @param {ASTNode} node The node to check.
1012 * @returns {boolean} `true` if the node is a part of directive prologue.
1013 */
1014function isDirective(node) {
1015 return node.type === "ExpressionStatement" && typeof node.directive === "string";
1016}
1017
1018/**
1019 * Tests if a node appears at the beginning of an ancestor ExpressionStatement node.
1020 * @param {ASTNode} node The node to check.
1021 * @returns {boolean} Whether the node appears at the beginning of an ancestor ExpressionStatement node.
1022 */
1023function isStartOfExpressionStatement(node) {
1024 const start = node.range[0];
1025 let ancestor = node;
1026
1027 while ((ancestor = ancestor.parent) && ancestor.range[0] === start) {
1028 if (ancestor.type === "ExpressionStatement") {
1029 return true;
1030 }
1031 }
1032 return false;
1033}
1034
1035/**
1036 * Determines whether an opening parenthesis `(`, bracket `[` or backtick ``` ` ``` needs to be preceded by a semicolon.
1037 * This opening parenthesis or bracket should be at the start of an `ExpressionStatement` or at the start of the body of an `ArrowFunctionExpression`.
1038 * @type {(sourceCode: SourceCode, node: ASTNode) => boolean}
1039 * @param {SourceCode} sourceCode The source code object.
1040 * @param {ASTNode} node A node at the position where an opening parenthesis or bracket will be inserted.
1041 * @returns {boolean} Whether a semicolon is required before the opening parenthesis or braket.
1042 */
1043let needsPrecedingSemicolon;
1044
1045{
1046 const BREAK_OR_CONTINUE = new Set(["BreakStatement", "ContinueStatement"]);
1047
1048 // Declaration types that must contain a string Literal node at the end.
1049 const DECLARATIONS = new Set(["ExportAllDeclaration", "ExportNamedDeclaration", "ImportDeclaration"]);
1050
1051 const IDENTIFIER_OR_KEYWORD = new Set(["Identifier", "Keyword"]);
1052
1053 // Keywords that can immediately precede an ExpressionStatement node, mapped to the their node types.
1054 const NODE_TYPES_BY_KEYWORD = {
1055 __proto__: null,
1056 break: "BreakStatement",
1057 continue: "ContinueStatement",
1058 debugger: "DebuggerStatement",
1059 do: "DoWhileStatement",
1060 else: "IfStatement",
1061 return: "ReturnStatement",
1062 yield: "YieldExpression"
1063 };
1064
1065 /*
1066 * Before an opening parenthesis, postfix `++` and `--` always trigger ASI;
1067 * the tokens `:`, `;`, `{` and `=>` don't expect a semicolon, as that would count as an empty statement.
1068 */
1069 const PUNCTUATORS = new Set([":", ";", "{", "=>", "++", "--"]);
1070
1071 /*
1072 * Statements that can contain an `ExpressionStatement` after a closing parenthesis.
1073 * DoWhileStatement is an exception in that it always triggers ASI after the closing parenthesis.
1074 */
1075 const STATEMENTS = new Set([
1076 "DoWhileStatement",
1077 "ForInStatement",
1078 "ForOfStatement",
1079 "ForStatement",
1080 "IfStatement",
1081 "WhileStatement",
1082 "WithStatement"
1083 ]);
1084
1085 needsPrecedingSemicolon =
1086 function(sourceCode, node) {
1087 const prevToken = sourceCode.getTokenBefore(node);
1088
1089 if (!prevToken || prevToken.type === "Punctuator" && PUNCTUATORS.has(prevToken.value)) {
1090 return false;
1091 }
1092
1093 const prevNode = sourceCode.getNodeByRangeIndex(prevToken.range[0]);
1094
1095 if (isClosingParenToken(prevToken)) {
1096 return !STATEMENTS.has(prevNode.type);
1097 }
1098
1099 if (isClosingBraceToken(prevToken)) {
1100 return (
1101 prevNode.type === "BlockStatement" && prevNode.parent.type === "FunctionExpression" ||
1102 prevNode.type === "ClassBody" && prevNode.parent.type === "ClassExpression" ||
1103 prevNode.type === "ObjectExpression"
1104 );
1105 }
1106
1107 if (IDENTIFIER_OR_KEYWORD.has(prevToken.type)) {
1108 if (BREAK_OR_CONTINUE.has(prevNode.parent.type)) {
1109 return false;
1110 }
1111
1112 const keyword = prevToken.value;
1113 const nodeType = NODE_TYPES_BY_KEYWORD[keyword];
1114
1115 return prevNode.type !== nodeType;
1116 }
1117
1118 if (prevToken.type === "String") {
1119 return !DECLARATIONS.has(prevNode.parent.type);
1120 }
1121
1122 return true;
1123 };
1124}
1125
1126//------------------------------------------------------------------------------
1127// Public Interface
1128//------------------------------------------------------------------------------
1129
1130module.exports = {
1131 COMMENTS_IGNORE_PATTERN,
1132 LINEBREAKS,
1133 LINEBREAK_MATCHER: lineBreakPattern,
1134 SHEBANG_MATCHER: shebangPattern,
1135 STATEMENT_LIST_PARENTS,
1136
1137 /**
1138 * Determines whether two adjacent tokens are on the same line.
1139 * @param {Object} left The left token object.
1140 * @param {Object} right The right token object.
1141 * @returns {boolean} Whether or not the tokens are on the same line.
1142 * @public
1143 */
1144 isTokenOnSameLine(left, right) {
1145 return left.loc.end.line === right.loc.start.line;
1146 },
1147
1148 isNullOrUndefined,
1149 isCallee,
1150 isES5Constructor,
1151 getUpperFunction,
1152 isFunction,
1153 isLoop,
1154 isInLoop,
1155 isArrayFromMethod,
1156 isParenthesised,
1157 createGlobalLinebreakMatcher,
1158 equalTokens,
1159
1160 isArrowToken,
1161 isClosingBraceToken,
1162 isClosingBracketToken,
1163 isClosingParenToken,
1164 isColonToken,
1165 isCommaToken,
1166 isCommentToken,
1167 isDotToken,
1168 isQuestionDotToken,
1169 isKeywordToken,
1170 isNotClosingBraceToken: negate(isClosingBraceToken),
1171 isNotClosingBracketToken: negate(isClosingBracketToken),
1172 isNotClosingParenToken: negate(isClosingParenToken),
1173 isNotColonToken: negate(isColonToken),
1174 isNotCommaToken: negate(isCommaToken),
1175 isNotDotToken: negate(isDotToken),
1176 isNotQuestionDotToken: negate(isQuestionDotToken),
1177 isNotOpeningBraceToken: negate(isOpeningBraceToken),
1178 isNotOpeningBracketToken: negate(isOpeningBracketToken),
1179 isNotOpeningParenToken: negate(isOpeningParenToken),
1180 isNotSemicolonToken: negate(isSemicolonToken),
1181 isOpeningBraceToken,
1182 isOpeningBracketToken,
1183 isOpeningParenToken,
1184 isSemicolonToken,
1185 isEqToken,
1186
1187 /**
1188 * Checks whether or not a given node is a string literal.
1189 * @param {ASTNode} node A node to check.
1190 * @returns {boolean} `true` if the node is a string literal.
1191 */
1192 isStringLiteral(node) {
1193 return (
1194 (node.type === "Literal" && typeof node.value === "string") ||
1195 node.type === "TemplateLiteral"
1196 );
1197 },
1198
1199 /**
1200 * Checks whether a given node is a breakable statement or not.
1201 * The node is breakable if the node is one of the following type:
1202 *
1203 * - DoWhileStatement
1204 * - ForInStatement
1205 * - ForOfStatement
1206 * - ForStatement
1207 * - SwitchStatement
1208 * - WhileStatement
1209 * @param {ASTNode} node A node to check.
1210 * @returns {boolean} `true` if the node is breakable.
1211 */
1212 isBreakableStatement(node) {
1213 return breakableTypePattern.test(node.type);
1214 },
1215
1216 /**
1217 * Gets references which are non initializer and writable.
1218 * @param {Reference[]} references An array of references.
1219 * @returns {Reference[]} An array of only references which are non initializer and writable.
1220 * @public
1221 */
1222 getModifyingReferences(references) {
1223 return references.filter(isModifyingReference);
1224 },
1225
1226 /**
1227 * Validate that a string passed in is surrounded by the specified character
1228 * @param {string} val The text to check.
1229 * @param {string} character The character to see if it's surrounded by.
1230 * @returns {boolean} True if the text is surrounded by the character, false if not.
1231 * @private
1232 */
1233 isSurroundedBy(val, character) {
1234 return val[0] === character && val[val.length - 1] === character;
1235 },
1236
1237 /**
1238 * Returns whether the provided node is an ESLint directive comment or not
1239 * @param {Line|Block} node The comment token to be checked
1240 * @returns {boolean} `true` if the node is an ESLint directive comment
1241 */
1242 isDirectiveComment(node) {
1243 const comment = node.value.trim();
1244
1245 return (
1246 node.type === "Line" && comment.startsWith("eslint-") ||
1247 node.type === "Block" && ESLINT_DIRECTIVE_PATTERN.test(comment)
1248 );
1249 },
1250
1251 /**
1252 * Gets the trailing statement of a given node.
1253 *
1254 * if (code)
1255 * consequent;
1256 *
1257 * When taking this `IfStatement`, returns `consequent;` statement.
1258 * @param {ASTNode} A node to get.
1259 * @returns {ASTNode|null} The trailing statement's node.
1260 */
1261 getTrailingStatement: esutils.ast.trailingStatement,
1262
1263 /**
1264 * Finds the variable by a given name in a given scope and its upper scopes.
1265 * @param {eslint-scope.Scope} initScope A scope to start find.
1266 * @param {string} name A variable name to find.
1267 * @returns {eslint-scope.Variable|null} A found variable or `null`.
1268 */
1269 getVariableByName(initScope, name) {
1270 let scope = initScope;
1271
1272 while (scope) {
1273 const variable = scope.set.get(name);
1274
1275 if (variable) {
1276 return variable;
1277 }
1278
1279 scope = scope.upper;
1280 }
1281
1282 return null;
1283 },
1284
1285 /**
1286 * Checks whether or not a given function node is the default `this` binding.
1287 *
1288 * First, this checks the node:
1289 *
1290 * - The given node is not in `PropertyDefinition#value` position.
1291 * - The given node is not `StaticBlock`.
1292 * - The function name does not start with uppercase. It's a convention to capitalize the names
1293 * of constructor functions. This check is not performed if `capIsConstructor` is set to `false`.
1294 * - The function does not have a JSDoc comment that has a @this tag.
1295 *
1296 * Next, this checks the location of the node.
1297 * If the location is below, this judges `this` is valid.
1298 *
1299 * - The location is not on an object literal.
1300 * - The location is not assigned to a variable which starts with an uppercase letter. Applies to anonymous
1301 * functions only, as the name of the variable is considered to be the name of the function in this case.
1302 * This check is not performed if `capIsConstructor` is set to `false`.
1303 * - The location is not on an ES2015 class.
1304 * - Its `bind`/`call`/`apply` method is not called directly.
1305 * - The function is not a callback of array methods (such as `.forEach()`) if `thisArg` is given.
1306 * @param {ASTNode} node A function node to check. It also can be an implicit function, like `StaticBlock`
1307 * or any expression that is `PropertyDefinition#value` node.
1308 * @param {SourceCode} sourceCode A SourceCode instance to get comments.
1309 * @param {boolean} [capIsConstructor = true] `false` disables the assumption that functions which name starts
1310 * with an uppercase or are assigned to a variable which name starts with an uppercase are constructors.
1311 * @returns {boolean} The function node is the default `this` binding.
1312 */
1313 isDefaultThisBinding(node, sourceCode, { capIsConstructor = true } = {}) {
1314
1315 /*
1316 * Class field initializers are implicit functions, but ESTree doesn't have the AST node of field initializers.
1317 * Therefore, A expression node at `PropertyDefinition#value` is a function.
1318 * In this case, `this` is always not default binding.
1319 */
1320 if (node.parent.type === "PropertyDefinition" && node.parent.value === node) {
1321 return false;
1322 }
1323
1324 // Class static blocks are implicit functions. In this case, `this` is always not default binding.
1325 if (node.type === "StaticBlock") {
1326 return false;
1327 }
1328
1329 if (
1330 (capIsConstructor && isES5Constructor(node)) ||
1331 hasJSDocThisTag(node, sourceCode)
1332 ) {
1333 return false;
1334 }
1335 const isAnonymous = node.id === null;
1336 let currentNode = node;
1337
1338 while (currentNode) {
1339 const parent = currentNode.parent;
1340
1341 switch (parent.type) {
1342
1343 /*
1344 * Looks up the destination.
1345 * e.g., obj.foo = nativeFoo || function foo() { ... };
1346 */
1347 case "LogicalExpression":
1348 case "ConditionalExpression":
1349 case "ChainExpression":
1350 currentNode = parent;
1351 break;
1352
1353 /*
1354 * If the upper function is IIFE, checks the destination of the return value.
1355 * e.g.
1356 * obj.foo = (function() {
1357 * // setup...
1358 * return function foo() { ... };
1359 * })();
1360 * obj.foo = (() =>
1361 * function foo() { ... }
1362 * )();
1363 */
1364 case "ReturnStatement": {
1365 const func = getUpperFunction(parent);
1366
1367 if (func === null || !isCallee(func)) {
1368 return true;
1369 }
1370 currentNode = func.parent;
1371 break;
1372 }
1373 case "ArrowFunctionExpression":
1374 if (currentNode !== parent.body || !isCallee(parent)) {
1375 return true;
1376 }
1377 currentNode = parent.parent;
1378 break;
1379
1380 /*
1381 * e.g.
1382 * var obj = { foo() { ... } };
1383 * var obj = { foo: function() { ... } };
1384 * class A { constructor() { ... } }
1385 * class A { foo() { ... } }
1386 * class A { get foo() { ... } }
1387 * class A { set foo() { ... } }
1388 * class A { static foo() { ... } }
1389 * class A { foo = function() { ... } }
1390 */
1391 case "Property":
1392 case "PropertyDefinition":
1393 case "MethodDefinition":
1394 return parent.value !== currentNode;
1395
1396 /*
1397 * e.g.
1398 * obj.foo = function foo() { ... };
1399 * Foo = function() { ... };
1400 * [obj.foo = function foo() { ... }] = a;
1401 * [Foo = function() { ... }] = a;
1402 */
1403 case "AssignmentExpression":
1404 case "AssignmentPattern":
1405 if (parent.left.type === "MemberExpression") {
1406 return false;
1407 }
1408 if (
1409 capIsConstructor &&
1410 isAnonymous &&
1411 parent.left.type === "Identifier" &&
1412 startsWithUpperCase(parent.left.name)
1413 ) {
1414 return false;
1415 }
1416 return true;
1417
1418 /*
1419 * e.g.
1420 * var Foo = function() { ... };
1421 */
1422 case "VariableDeclarator":
1423 return !(
1424 capIsConstructor &&
1425 isAnonymous &&
1426 parent.init === currentNode &&
1427 parent.id.type === "Identifier" &&
1428 startsWithUpperCase(parent.id.name)
1429 );
1430
1431 /*
1432 * e.g.
1433 * var foo = function foo() { ... }.bind(obj);
1434 * (function foo() { ... }).call(obj);
1435 * (function foo() { ... }).apply(obj, []);
1436 */
1437 case "MemberExpression":
1438 if (
1439 parent.object === currentNode &&
1440 isSpecificMemberAccess(parent, null, bindOrCallOrApplyPattern)
1441 ) {
1442 const maybeCalleeNode = parent.parent.type === "ChainExpression"
1443 ? parent.parent
1444 : parent;
1445
1446 return !(
1447 isCallee(maybeCalleeNode) &&
1448 maybeCalleeNode.parent.arguments.length >= 1 &&
1449 !isNullOrUndefined(maybeCalleeNode.parent.arguments[0])
1450 );
1451 }
1452 return true;
1453
1454 /*
1455 * e.g.
1456 * Reflect.apply(function() {}, obj, []);
1457 * Array.from([], function() {}, obj);
1458 * list.forEach(function() {}, obj);
1459 */
1460 case "CallExpression":
1461 if (isReflectApply(parent.callee)) {
1462 return (
1463 parent.arguments.length !== 3 ||
1464 parent.arguments[0] !== currentNode ||
1465 isNullOrUndefined(parent.arguments[1])
1466 );
1467 }
1468 if (isArrayFromMethod(parent.callee)) {
1469 return (
1470 parent.arguments.length !== 3 ||
1471 parent.arguments[1] !== currentNode ||
1472 isNullOrUndefined(parent.arguments[2])
1473 );
1474 }
1475 if (isMethodWhichHasThisArg(parent.callee)) {
1476 return (
1477 parent.arguments.length !== 2 ||
1478 parent.arguments[0] !== currentNode ||
1479 isNullOrUndefined(parent.arguments[1])
1480 );
1481 }
1482 return true;
1483
1484 // Otherwise `this` is default.
1485 default:
1486 return true;
1487 }
1488 }
1489
1490 /* c8 ignore next */
1491 return true;
1492 },
1493
1494 /**
1495 * Get the precedence level based on the node type
1496 * @param {ASTNode} node node to evaluate
1497 * @returns {int} precedence level
1498 * @private
1499 */
1500 getPrecedence(node) {
1501 switch (node.type) {
1502 case "SequenceExpression":
1503 return 0;
1504
1505 case "AssignmentExpression":
1506 case "ArrowFunctionExpression":
1507 case "YieldExpression":
1508 return 1;
1509
1510 case "ConditionalExpression":
1511 return 3;
1512
1513 case "LogicalExpression":
1514 switch (node.operator) {
1515 case "||":
1516 case "??":
1517 return 4;
1518 case "&&":
1519 return 5;
1520
1521 // no default
1522 }
1523
1524 /* falls through */
1525
1526 case "BinaryExpression":
1527
1528 switch (node.operator) {
1529 case "|":
1530 return 6;
1531 case "^":
1532 return 7;
1533 case "&":
1534 return 8;
1535 case "==":
1536 case "!=":
1537 case "===":
1538 case "!==":
1539 return 9;
1540 case "<":
1541 case "<=":
1542 case ">":
1543 case ">=":
1544 case "in":
1545 case "instanceof":
1546 return 10;
1547 case "<<":
1548 case ">>":
1549 case ">>>":
1550 return 11;
1551 case "+":
1552 case "-":
1553 return 12;
1554 case "*":
1555 case "/":
1556 case "%":
1557 return 13;
1558 case "**":
1559 return 15;
1560
1561 // no default
1562 }
1563
1564 /* falls through */
1565
1566 case "UnaryExpression":
1567 case "AwaitExpression":
1568 return 16;
1569
1570 case "UpdateExpression":
1571 return 17;
1572
1573 case "CallExpression":
1574 case "ChainExpression":
1575 case "ImportExpression":
1576 return 18;
1577
1578 case "NewExpression":
1579 return 19;
1580
1581 default:
1582 if (node.type in eslintVisitorKeys) {
1583 return 20;
1584 }
1585
1586 /*
1587 * if the node is not a standard node that we know about, then assume it has the lowest precedence
1588 * this will mean that rules will wrap unknown nodes in parentheses where applicable instead of
1589 * unwrapping them and potentially changing the meaning of the code or introducing a syntax error.
1590 */
1591 return -1;
1592 }
1593 },
1594
1595 /**
1596 * Checks whether the given node is an empty block node or not.
1597 * @param {ASTNode|null} node The node to check.
1598 * @returns {boolean} `true` if the node is an empty block.
1599 */
1600 isEmptyBlock(node) {
1601 return Boolean(node && node.type === "BlockStatement" && node.body.length === 0);
1602 },
1603
1604 /**
1605 * Checks whether the given node is an empty function node or not.
1606 * @param {ASTNode|null} node The node to check.
1607 * @returns {boolean} `true` if the node is an empty function.
1608 */
1609 isEmptyFunction(node) {
1610 return isFunction(node) && module.exports.isEmptyBlock(node.body);
1611 },
1612
1613 /**
1614 * Get directives from directive prologue of a Program or Function node.
1615 * @param {ASTNode} node The node to check.
1616 * @returns {ASTNode[]} The directives found in the directive prologue.
1617 */
1618 getDirectivePrologue(node) {
1619 const directives = [];
1620
1621 // Directive prologues only occur at the top of files or functions.
1622 if (
1623 node.type === "Program" ||
1624 node.type === "FunctionDeclaration" ||
1625 node.type === "FunctionExpression" ||
1626
1627 /*
1628 * Do not check arrow functions with implicit return.
1629 * `() => "use strict";` returns the string `"use strict"`.
1630 */
1631 (node.type === "ArrowFunctionExpression" && node.body.type === "BlockStatement")
1632 ) {
1633 const statements = node.type === "Program" ? node.body : node.body.body;
1634
1635 for (const statement of statements) {
1636 if (
1637 statement.type === "ExpressionStatement" &&
1638 statement.expression.type === "Literal"
1639 ) {
1640 directives.push(statement);
1641 } else {
1642 break;
1643 }
1644 }
1645 }
1646
1647 return directives;
1648 },
1649
1650 /**
1651 * Determines whether this node is a decimal integer literal. If a node is a decimal integer literal, a dot added
1652 * after the node will be parsed as a decimal point, rather than a property-access dot.
1653 * @param {ASTNode} node The node to check.
1654 * @returns {boolean} `true` if this node is a decimal integer.
1655 * @example
1656 *
1657 * 0 // true
1658 * 5 // true
1659 * 50 // true
1660 * 5_000 // true
1661 * 1_234_56 // true
1662 * 08 // true
1663 * 0192 // true
1664 * 5. // false
1665 * .5 // false
1666 * 5.0 // false
1667 * 5.00_00 // false
1668 * 05 // false
1669 * 0x5 // false
1670 * 0b101 // false
1671 * 0b11_01 // false
1672 * 0o5 // false
1673 * 5e0 // false
1674 * 5e1_000 // false
1675 * 5n // false
1676 * 1_000n // false
1677 * "5" // false
1678 *
1679 */
1680 isDecimalInteger(node) {
1681 return node.type === "Literal" && typeof node.value === "number" &&
1682 DECIMAL_INTEGER_PATTERN.test(node.raw);
1683 },
1684
1685 /**
1686 * Determines whether this token is a decimal integer numeric token.
1687 * This is similar to isDecimalInteger(), but for tokens.
1688 * @param {Token} token The token to check.
1689 * @returns {boolean} `true` if this token is a decimal integer.
1690 */
1691 isDecimalIntegerNumericToken(token) {
1692 return token.type === "Numeric" && DECIMAL_INTEGER_PATTERN.test(token.value);
1693 },
1694
1695 /**
1696 * Gets the name and kind of the given function node.
1697 *
1698 * - `function foo() {}` .................... `function 'foo'`
1699 * - `(function foo() {})` .................. `function 'foo'`
1700 * - `(function() {})` ...................... `function`
1701 * - `function* foo() {}` ................... `generator function 'foo'`
1702 * - `(function* foo() {})` ................. `generator function 'foo'`
1703 * - `(function*() {})` ..................... `generator function`
1704 * - `() => {}` ............................. `arrow function`
1705 * - `async () => {}` ....................... `async arrow function`
1706 * - `({ foo: function foo() {} })` ......... `method 'foo'`
1707 * - `({ foo: function() {} })` ............. `method 'foo'`
1708 * - `({ ['foo']: function() {} })` ......... `method 'foo'`
1709 * - `({ [foo]: function() {} })` ........... `method`
1710 * - `({ foo() {} })` ....................... `method 'foo'`
1711 * - `({ foo: function* foo() {} })` ........ `generator method 'foo'`
1712 * - `({ foo: function*() {} })` ............ `generator method 'foo'`
1713 * - `({ ['foo']: function*() {} })` ........ `generator method 'foo'`
1714 * - `({ [foo]: function*() {} })` .......... `generator method`
1715 * - `({ *foo() {} })` ...................... `generator method 'foo'`
1716 * - `({ foo: async function foo() {} })` ... `async method 'foo'`
1717 * - `({ foo: async function() {} })` ....... `async method 'foo'`
1718 * - `({ ['foo']: async function() {} })` ... `async method 'foo'`
1719 * - `({ [foo]: async function() {} })` ..... `async method`
1720 * - `({ async foo() {} })` ................. `async method 'foo'`
1721 * - `({ get foo() {} })` ................... `getter 'foo'`
1722 * - `({ set foo(a) {} })` .................. `setter 'foo'`
1723 * - `class A { constructor() {} }` ......... `constructor`
1724 * - `class A { foo() {} }` ................. `method 'foo'`
1725 * - `class A { *foo() {} }` ................ `generator method 'foo'`
1726 * - `class A { async foo() {} }` ........... `async method 'foo'`
1727 * - `class A { ['foo']() {} }` ............. `method 'foo'`
1728 * - `class A { *['foo']() {} }` ............ `generator method 'foo'`
1729 * - `class A { async ['foo']() {} }` ....... `async method 'foo'`
1730 * - `class A { [foo]() {} }` ............... `method`
1731 * - `class A { *[foo]() {} }` .............. `generator method`
1732 * - `class A { async [foo]() {} }` ......... `async method`
1733 * - `class A { get foo() {} }` ............. `getter 'foo'`
1734 * - `class A { set foo(a) {} }` ............ `setter 'foo'`
1735 * - `class A { static foo() {} }` .......... `static method 'foo'`
1736 * - `class A { static *foo() {} }` ......... `static generator method 'foo'`
1737 * - `class A { static async foo() {} }` .... `static async method 'foo'`
1738 * - `class A { static get foo() {} }` ...... `static getter 'foo'`
1739 * - `class A { static set foo(a) {} }` ..... `static setter 'foo'`
1740 * - `class A { foo = () => {}; }` .......... `method 'foo'`
1741 * - `class A { foo = function() {}; }` ..... `method 'foo'`
1742 * - `class A { foo = function bar() {}; }` . `method 'foo'`
1743 * - `class A { static foo = () => {}; }` ... `static method 'foo'`
1744 * - `class A { '#foo' = () => {}; }` ....... `method '#foo'`
1745 * - `class A { #foo = () => {}; }` ......... `private method #foo`
1746 * - `class A { static #foo = () => {}; }` .. `static private method #foo`
1747 * - `class A { '#foo'() {} }` .............. `method '#foo'`
1748 * - `class A { #foo() {} }` ................ `private method #foo`
1749 * - `class A { static #foo() {} }` ......... `static private method #foo`
1750 * @param {ASTNode} node The function node to get.
1751 * @returns {string} The name and kind of the function node.
1752 */
1753 getFunctionNameWithKind(node) {
1754 const parent = node.parent;
1755 const tokens = [];
1756
1757 if (parent.type === "MethodDefinition" || parent.type === "PropertyDefinition") {
1758
1759 // The proposal uses `static` word consistently before visibility words: https://github.com/tc39/proposal-static-class-features
1760 if (parent.static) {
1761 tokens.push("static");
1762 }
1763 if (!parent.computed && parent.key.type === "PrivateIdentifier") {
1764 tokens.push("private");
1765 }
1766 }
1767 if (node.async) {
1768 tokens.push("async");
1769 }
1770 if (node.generator) {
1771 tokens.push("generator");
1772 }
1773
1774 if (parent.type === "Property" || parent.type === "MethodDefinition") {
1775 if (parent.kind === "constructor") {
1776 return "constructor";
1777 }
1778 if (parent.kind === "get") {
1779 tokens.push("getter");
1780 } else if (parent.kind === "set") {
1781 tokens.push("setter");
1782 } else {
1783 tokens.push("method");
1784 }
1785 } else if (parent.type === "PropertyDefinition") {
1786 tokens.push("method");
1787 } else {
1788 if (node.type === "ArrowFunctionExpression") {
1789 tokens.push("arrow");
1790 }
1791 tokens.push("function");
1792 }
1793
1794 if (parent.type === "Property" || parent.type === "MethodDefinition" || parent.type === "PropertyDefinition") {
1795 if (!parent.computed && parent.key.type === "PrivateIdentifier") {
1796 tokens.push(`#${parent.key.name}`);
1797 } else {
1798 const name = getStaticPropertyName(parent);
1799
1800 if (name !== null) {
1801 tokens.push(`'${name}'`);
1802 } else if (node.id) {
1803 tokens.push(`'${node.id.name}'`);
1804 }
1805 }
1806 } else if (node.id) {
1807 tokens.push(`'${node.id.name}'`);
1808 }
1809
1810 return tokens.join(" ");
1811 },
1812
1813 /**
1814 * Gets the location of the given function node for reporting.
1815 *
1816 * - `function foo() {}`
1817 * ^^^^^^^^^^^^
1818 * - `(function foo() {})`
1819 * ^^^^^^^^^^^^
1820 * - `(function() {})`
1821 * ^^^^^^^^
1822 * - `function* foo() {}`
1823 * ^^^^^^^^^^^^^
1824 * - `(function* foo() {})`
1825 * ^^^^^^^^^^^^^
1826 * - `(function*() {})`
1827 * ^^^^^^^^^
1828 * - `() => {}`
1829 * ^^
1830 * - `async () => {}`
1831 * ^^
1832 * - `({ foo: function foo() {} })`
1833 * ^^^^^^^^^^^^^^^^^
1834 * - `({ foo: function() {} })`
1835 * ^^^^^^^^^^^^^
1836 * - `({ ['foo']: function() {} })`
1837 * ^^^^^^^^^^^^^^^^^
1838 * - `({ [foo]: function() {} })`
1839 * ^^^^^^^^^^^^^^^
1840 * - `({ foo() {} })`
1841 * ^^^
1842 * - `({ foo: function* foo() {} })`
1843 * ^^^^^^^^^^^^^^^^^^
1844 * - `({ foo: function*() {} })`
1845 * ^^^^^^^^^^^^^^
1846 * - `({ ['foo']: function*() {} })`
1847 * ^^^^^^^^^^^^^^^^^^
1848 * - `({ [foo]: function*() {} })`
1849 * ^^^^^^^^^^^^^^^^
1850 * - `({ *foo() {} })`
1851 * ^^^^
1852 * - `({ foo: async function foo() {} })`
1853 * ^^^^^^^^^^^^^^^^^^^^^^^
1854 * - `({ foo: async function() {} })`
1855 * ^^^^^^^^^^^^^^^^^^^
1856 * - `({ ['foo']: async function() {} })`
1857 * ^^^^^^^^^^^^^^^^^^^^^^^
1858 * - `({ [foo]: async function() {} })`
1859 * ^^^^^^^^^^^^^^^^^^^^^
1860 * - `({ async foo() {} })`
1861 * ^^^^^^^^^
1862 * - `({ get foo() {} })`
1863 * ^^^^^^^
1864 * - `({ set foo(a) {} })`
1865 * ^^^^^^^
1866 * - `class A { constructor() {} }`
1867 * ^^^^^^^^^^^
1868 * - `class A { foo() {} }`
1869 * ^^^
1870 * - `class A { *foo() {} }`
1871 * ^^^^
1872 * - `class A { async foo() {} }`
1873 * ^^^^^^^^^
1874 * - `class A { ['foo']() {} }`
1875 * ^^^^^^^
1876 * - `class A { *['foo']() {} }`
1877 * ^^^^^^^^
1878 * - `class A { async ['foo']() {} }`
1879 * ^^^^^^^^^^^^^
1880 * - `class A { [foo]() {} }`
1881 * ^^^^^
1882 * - `class A { *[foo]() {} }`
1883 * ^^^^^^
1884 * - `class A { async [foo]() {} }`
1885 * ^^^^^^^^^^^
1886 * - `class A { get foo() {} }`
1887 * ^^^^^^^
1888 * - `class A { set foo(a) {} }`
1889 * ^^^^^^^
1890 * - `class A { static foo() {} }`
1891 * ^^^^^^^^^^
1892 * - `class A { static *foo() {} }`
1893 * ^^^^^^^^^^^
1894 * - `class A { static async foo() {} }`
1895 * ^^^^^^^^^^^^^^^^
1896 * - `class A { static get foo() {} }`
1897 * ^^^^^^^^^^^^^^
1898 * - `class A { static set foo(a) {} }`
1899 * ^^^^^^^^^^^^^^
1900 * - `class A { foo = function() {} }`
1901 * ^^^^^^^^^^^^^^
1902 * - `class A { static foo = function() {} }`
1903 * ^^^^^^^^^^^^^^^^^^^^^
1904 * - `class A { foo = (a, b) => {} }`
1905 * ^^^^^^
1906 * @param {ASTNode} node The function node to get.
1907 * @param {SourceCode} sourceCode The source code object to get tokens.
1908 * @returns {string} The location of the function node for reporting.
1909 */
1910 getFunctionHeadLoc(node, sourceCode) {
1911 const parent = node.parent;
1912 let start = null;
1913 let end = null;
1914
1915 if (parent.type === "Property" || parent.type === "MethodDefinition" || parent.type === "PropertyDefinition") {
1916 start = parent.loc.start;
1917 end = getOpeningParenOfParams(node, sourceCode).loc.start;
1918 } else if (node.type === "ArrowFunctionExpression") {
1919 const arrowToken = sourceCode.getTokenBefore(node.body, isArrowToken);
1920
1921 start = arrowToken.loc.start;
1922 end = arrowToken.loc.end;
1923 } else {
1924 start = node.loc.start;
1925 end = getOpeningParenOfParams(node, sourceCode).loc.start;
1926 }
1927
1928 return {
1929 start: Object.assign({}, start),
1930 end: Object.assign({}, end)
1931 };
1932 },
1933
1934 /**
1935 * Gets next location when the result is not out of bound, otherwise returns null.
1936 *
1937 * Assumptions:
1938 *
1939 * - The given location represents a valid location in the given source code.
1940 * - Columns are 0-based.
1941 * - Lines are 1-based.
1942 * - Column immediately after the last character in a line (not incl. linebreaks) is considered to be a valid location.
1943 * - If the source code ends with a linebreak, `sourceCode.lines` array will have an extra element (empty string) at the end.
1944 * The start (column 0) of that extra line is considered to be a valid location.
1945 *
1946 * Examples of successive locations (line, column):
1947 *
1948 * code: foo
1949 * locations: (1, 0) -> (1, 1) -> (1, 2) -> (1, 3) -> null
1950 *
1951 * code: foo<LF>
1952 * locations: (1, 0) -> (1, 1) -> (1, 2) -> (1, 3) -> (2, 0) -> null
1953 *
1954 * code: foo<CR><LF>
1955 * locations: (1, 0) -> (1, 1) -> (1, 2) -> (1, 3) -> (2, 0) -> null
1956 *
1957 * code: a<LF>b
1958 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (2, 1) -> null
1959 *
1960 * code: a<LF>b<LF>
1961 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (2, 1) -> (3, 0) -> null
1962 *
1963 * code: a<CR><LF>b<CR><LF>
1964 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (2, 1) -> (3, 0) -> null
1965 *
1966 * code: a<LF><LF>
1967 * locations: (1, 0) -> (1, 1) -> (2, 0) -> (3, 0) -> null
1968 *
1969 * code: <LF>
1970 * locations: (1, 0) -> (2, 0) -> null
1971 *
1972 * code:
1973 * locations: (1, 0) -> null
1974 * @param {SourceCode} sourceCode The sourceCode
1975 * @param {{line: number, column: number}} location The location
1976 * @returns {{line: number, column: number} | null} Next location
1977 */
1978 getNextLocation(sourceCode, { line, column }) {
1979 if (column < sourceCode.lines[line - 1].length) {
1980 return {
1981 line,
1982 column: column + 1
1983 };
1984 }
1985
1986 if (line < sourceCode.lines.length) {
1987 return {
1988 line: line + 1,
1989 column: 0
1990 };
1991 }
1992
1993 return null;
1994 },
1995
1996 /**
1997 * Gets the parenthesized text of a node. This is similar to sourceCode.getText(node), but it also includes any parentheses
1998 * surrounding the node.
1999 * @param {SourceCode} sourceCode The source code object
2000 * @param {ASTNode} node An expression node
2001 * @returns {string} The text representing the node, with all surrounding parentheses included
2002 */
2003 getParenthesisedText(sourceCode, node) {
2004 let leftToken = sourceCode.getFirstToken(node);
2005 let rightToken = sourceCode.getLastToken(node);
2006
2007 while (
2008 sourceCode.getTokenBefore(leftToken) &&
2009 sourceCode.getTokenBefore(leftToken).type === "Punctuator" &&
2010 sourceCode.getTokenBefore(leftToken).value === "(" &&
2011 sourceCode.getTokenAfter(rightToken) &&
2012 sourceCode.getTokenAfter(rightToken).type === "Punctuator" &&
2013 sourceCode.getTokenAfter(rightToken).value === ")"
2014 ) {
2015 leftToken = sourceCode.getTokenBefore(leftToken);
2016 rightToken = sourceCode.getTokenAfter(rightToken);
2017 }
2018
2019 return sourceCode.getText().slice(leftToken.range[0], rightToken.range[1]);
2020 },
2021
2022 /**
2023 * Determine if a node has a possibility to be an Error object
2024 * @param {ASTNode} node ASTNode to check
2025 * @returns {boolean} True if there is a chance it contains an Error obj
2026 */
2027 couldBeError(node) {
2028 switch (node.type) {
2029 case "Identifier":
2030 case "CallExpression":
2031 case "NewExpression":
2032 case "MemberExpression":
2033 case "TaggedTemplateExpression":
2034 case "YieldExpression":
2035 case "AwaitExpression":
2036 case "ChainExpression":
2037 return true; // possibly an error object.
2038
2039 case "AssignmentExpression":
2040 if (["=", "&&="].includes(node.operator)) {
2041 return module.exports.couldBeError(node.right);
2042 }
2043
2044 if (["||=", "??="].includes(node.operator)) {
2045 return module.exports.couldBeError(node.left) || module.exports.couldBeError(node.right);
2046 }
2047
2048 /**
2049 * All other assignment operators are mathematical assignment operators (arithmetic or bitwise).
2050 * An assignment expression with a mathematical operator can either evaluate to a primitive value,
2051 * or throw, depending on the operands. Thus, it cannot evaluate to an `Error` object.
2052 */
2053 return false;
2054
2055 case "SequenceExpression": {
2056 const exprs = node.expressions;
2057
2058 return exprs.length !== 0 && module.exports.couldBeError(exprs[exprs.length - 1]);
2059 }
2060
2061 case "LogicalExpression":
2062
2063 /*
2064 * If the && operator short-circuits, the left side was falsy and therefore not an error, and if it
2065 * doesn't short-circuit, it takes the value from the right side, so the right side must always be
2066 * a plausible error. A future improvement could verify that the left side could be truthy by
2067 * excluding falsy literals.
2068 */
2069 if (node.operator === "&&") {
2070 return module.exports.couldBeError(node.right);
2071 }
2072
2073 return module.exports.couldBeError(node.left) || module.exports.couldBeError(node.right);
2074
2075 case "ConditionalExpression":
2076 return module.exports.couldBeError(node.consequent) || module.exports.couldBeError(node.alternate);
2077
2078 default:
2079 return false;
2080 }
2081 },
2082
2083 /**
2084 * Check if a given node is a numeric literal or not.
2085 * @param {ASTNode} node The node to check.
2086 * @returns {boolean} `true` if the node is a number or bigint literal.
2087 */
2088 isNumericLiteral(node) {
2089 return (
2090 node.type === "Literal" &&
2091 (typeof node.value === "number" || Boolean(node.bigint))
2092 );
2093 },
2094
2095 /**
2096 * Determines whether two tokens can safely be placed next to each other without merging into a single token
2097 * @param {Token|string} leftValue The left token. If this is a string, it will be tokenized and the last token will be used.
2098 * @param {Token|string} rightValue The right token. If this is a string, it will be tokenized and the first token will be used.
2099 * @returns {boolean} If the tokens cannot be safely placed next to each other, returns `false`. If the tokens can be placed
2100 * next to each other, behavior is undefined (although it should return `true` in most cases).
2101 */
2102 canTokensBeAdjacent(leftValue, rightValue) {
2103 const espreeOptions = {
2104 ecmaVersion: espree.latestEcmaVersion,
2105 comment: true,
2106 range: true
2107 };
2108
2109 let leftToken;
2110
2111 if (typeof leftValue === "string") {
2112 let tokens;
2113
2114 try {
2115 tokens = espree.tokenize(leftValue, espreeOptions);
2116 } catch {
2117 return false;
2118 }
2119
2120 const comments = tokens.comments;
2121
2122 leftToken = tokens[tokens.length - 1];
2123 if (comments.length) {
2124 const lastComment = comments[comments.length - 1];
2125
2126 if (!leftToken || lastComment.range[0] > leftToken.range[0]) {
2127 leftToken = lastComment;
2128 }
2129 }
2130 } else {
2131 leftToken = leftValue;
2132 }
2133
2134 /*
2135 * If a hashbang comment was passed as a token object from SourceCode,
2136 * its type will be "Shebang" because of the way ESLint itself handles hashbangs.
2137 * If a hashbang comment was passed in a string and then tokenized in this function,
2138 * its type will be "Hashbang" because of the way Espree tokenizes hashbangs.
2139 */
2140 if (leftToken.type === "Shebang" || leftToken.type === "Hashbang") {
2141 return false;
2142 }
2143
2144 let rightToken;
2145
2146 if (typeof rightValue === "string") {
2147 let tokens;
2148
2149 try {
2150 tokens = espree.tokenize(rightValue, espreeOptions);
2151 } catch {
2152 return false;
2153 }
2154
2155 const comments = tokens.comments;
2156
2157 rightToken = tokens[0];
2158 if (comments.length) {
2159 const firstComment = comments[0];
2160
2161 if (!rightToken || firstComment.range[0] < rightToken.range[0]) {
2162 rightToken = firstComment;
2163 }
2164 }
2165 } else {
2166 rightToken = rightValue;
2167 }
2168
2169 if (leftToken.type === "Punctuator" || rightToken.type === "Punctuator") {
2170 if (leftToken.type === "Punctuator" && rightToken.type === "Punctuator") {
2171 const PLUS_TOKENS = new Set(["+", "++"]);
2172 const MINUS_TOKENS = new Set(["-", "--"]);
2173
2174 return !(
2175 PLUS_TOKENS.has(leftToken.value) && PLUS_TOKENS.has(rightToken.value) ||
2176 MINUS_TOKENS.has(leftToken.value) && MINUS_TOKENS.has(rightToken.value)
2177 );
2178 }
2179 if (leftToken.type === "Punctuator" && leftToken.value === "/") {
2180 return !["Block", "Line", "RegularExpression"].includes(rightToken.type);
2181 }
2182 return true;
2183 }
2184
2185 if (
2186 leftToken.type === "String" || rightToken.type === "String" ||
2187 leftToken.type === "Template" || rightToken.type === "Template"
2188 ) {
2189 return true;
2190 }
2191
2192 if (leftToken.type !== "Numeric" && rightToken.type === "Numeric" && rightToken.value.startsWith(".")) {
2193 return true;
2194 }
2195
2196 if (leftToken.type === "Block" || rightToken.type === "Block" || rightToken.type === "Line") {
2197 return true;
2198 }
2199
2200 if (rightToken.type === "PrivateIdentifier") {
2201 return true;
2202 }
2203
2204 return false;
2205 },
2206
2207 /**
2208 * Get the `loc` object of a given name in a `/*globals` directive comment.
2209 * @param {SourceCode} sourceCode The source code to convert index to loc.
2210 * @param {Comment} comment The `/*globals` directive comment which include the name.
2211 * @param {string} name The name to find.
2212 * @returns {SourceLocation} The `loc` object.
2213 */
2214 getNameLocationInGlobalDirectiveComment(sourceCode, comment, name) {
2215 const namePattern = new RegExp(`[\\s,]${escapeRegExp(name)}(?:$|[\\s,:])`, "gu");
2216
2217 // To ignore the first text "global".
2218 namePattern.lastIndex = comment.value.indexOf("global") + 6;
2219
2220 // Search a given variable name.
2221 const match = namePattern.exec(comment.value);
2222
2223 // Convert the index to loc.
2224 const start = sourceCode.getLocFromIndex(
2225 comment.range[0] +
2226 "/*".length +
2227 (match ? match.index + 1 : 0)
2228 );
2229 const end = {
2230 line: start.line,
2231 column: start.column + (match ? name.length : 1)
2232 };
2233
2234 return { start, end };
2235 },
2236
2237 /**
2238 * Determines whether the given raw string contains an octal escape sequence
2239 * or a non-octal decimal escape sequence ("\8", "\9").
2240 *
2241 * "\1", "\2" ... "\7", "\8", "\9"
2242 * "\00", "\01" ... "\07", "\08", "\09"
2243 *
2244 * "\0", when not followed by a digit, is not an octal escape sequence.
2245 * @param {string} rawString A string in its raw representation.
2246 * @returns {boolean} `true` if the string contains at least one octal escape sequence
2247 * or at least one non-octal decimal escape sequence.
2248 */
2249 hasOctalOrNonOctalDecimalEscapeSequence(rawString) {
2250 return OCTAL_OR_NON_OCTAL_DECIMAL_ESCAPE_PATTERN.test(rawString);
2251 },
2252
2253 /**
2254 * Determines whether the given node is a template literal without expressions.
2255 * @param {ASTNode} node Node to check.
2256 * @returns {boolean} True if the node is a template literal without expressions.
2257 */
2258 isStaticTemplateLiteral(node) {
2259 return node.type === "TemplateLiteral" && node.expressions.length === 0;
2260 },
2261
2262 isReferenceToGlobalVariable,
2263 isLogicalExpression,
2264 isCoalesceExpression,
2265 isMixedLogicalAndCoalesceExpressions,
2266 isNullLiteral,
2267 getStaticStringValue,
2268 getStaticPropertyName,
2269 skipChainExpression,
2270 isSpecificId,
2271 isSpecificMemberAccess,
2272 equalLiteralValue,
2273 isSameReference,
2274 isLogicalAssignmentOperator,
2275 getSwitchCaseColonToken,
2276 getModuleExportName,
2277 isConstant,
2278 isTopLevelExpressionStatement,
2279 isDirective,
2280 isStartOfExpressionStatement,
2281 needsPrecedingSemicolon
2282};
Note: See TracBrowser for help on using the repository browser.