2 * @fileoverview Common utils for AST.
3 * @author Gyandeep Singh
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 const esutils = require("esutils");
13 const espree = require("espree");
14 const lodash = require("lodash");
17 createGlobalLinebreakMatcher,
20 } = require("../../shared/ast-utils");
22 //------------------------------------------------------------------------------
24 //------------------------------------------------------------------------------
26 const anyFunctionPattern = /^(?:Function(?:Declaration|Expression)|ArrowFunctionExpression)$/u;
27 const anyLoopPattern = /^(?:DoWhile|For|ForIn|ForOf|While)Statement$/u;
28 const arrayOrTypedArrayPattern = /Array$/u;
29 const arrayMethodPattern = /^(?:every|filter|find|findIndex|forEach|map|some)$/u;
30 const bindOrCallOrApplyPattern = /^(?:bind|call|apply)$/u;
31 const thisTagPattern = /^[\s*]*@this/mu;
34 const COMMENTS_IGNORE_PATTERN = /^\s*(?:eslint|jshint\s+|jslint\s+|istanbul\s+|globals?\s+|exported\s+|jscs)/u;
35 const LINEBREAKS = new Set(["\r\n", "\r", "\n", "\u2028", "\u2029"]);
37 // A set of node types that can contain a list of statements
38 const STATEMENT_LIST_PARENTS = new Set(["Program", "BlockStatement", "SwitchCase"]);
40 const DECIMAL_INTEGER_PATTERN = /^(0|[1-9]\d*)$/u;
41 const OCTAL_ESCAPE_PATTERN = /^(?:[^\\]|\\[^0-7]|\\0(?![0-9]))*\\(?:[1-7]|0[0-9])/u;
44 * Checks reference if is non initializer and writable.
45 * @param {Reference} reference A reference to check.
46 * @param {int} index The index of the reference in the references.
47 * @param {Reference[]} references The array that the reference belongs to.
48 * @returns {boolean} Success/Failure
51 function isModifyingReference(reference, index, references) {
52 const identifier = reference.identifier;
55 * Destructuring assignments can have multiple default value, so
56 * possibly there are multiple writeable references for the same
59 const modifyingDifferentIdentifier = index === 0 ||
60 references[index - 1].identifier !== identifier;
63 reference.init === false &&
64 reference.isWrite() &&
65 modifyingDifferentIdentifier
70 * Checks whether the given string starts with uppercase or not.
71 * @param {string} s The string to check.
72 * @returns {boolean} `true` if the string starts with uppercase.
74 function startsWithUpperCase(s) {
75 return s[0] !== s[0].toLocaleLowerCase();
79 * Checks whether or not a node is a constructor.
80 * @param {ASTNode} node A function node to check.
81 * @returns {boolean} Wehether or not a node is a constructor.
83 function isES5Constructor(node) {
84 return (node.id && startsWithUpperCase(node.id.name));
88 * Finds a function node from ancestors of a node.
89 * @param {ASTNode} node A start node to find.
90 * @returns {Node|null} A found function node.
92 function getUpperFunction(node) {
93 for (let currentNode = node; currentNode; currentNode = currentNode.parent) {
94 if (anyFunctionPattern.test(currentNode.type)) {
102 * Checks whether a given node is a function node or not.
103 * The following types are function nodes:
105 * - ArrowFunctionExpression
106 * - FunctionDeclaration
107 * - FunctionExpression
108 * @param {ASTNode|null} node A node to check.
109 * @returns {boolean} `true` if the node is a function node.
111 function isFunction(node) {
112 return Boolean(node && anyFunctionPattern.test(node.type));
116 * Checks whether a given node is a loop node or not.
117 * The following types are loop nodes:
124 * @param {ASTNode|null} node A node to check.
125 * @returns {boolean} `true` if the node is a loop node.
127 function isLoop(node) {
128 return Boolean(node && anyLoopPattern.test(node.type));
132 * Checks whether the given node is in a loop or not.
133 * @param {ASTNode} node The node to check.
134 * @returns {boolean} `true` if the node is in a loop.
136 function isInLoop(node) {
137 for (let currentNode = node; currentNode && !isFunction(currentNode); currentNode = currentNode.parent) {
138 if (isLoop(currentNode)) {
147 * Checks whether or not a node is `null` or `undefined`.
148 * @param {ASTNode} node A node to check.
149 * @returns {boolean} Whether or not the node is a `null` or `undefined`.
152 function isNullOrUndefined(node) {
154 module.exports.isNullLiteral(node) ||
155 (node.type === "Identifier" && node.name === "undefined") ||
156 (node.type === "UnaryExpression" && node.operator === "void")
161 * Checks whether or not a node is callee.
162 * @param {ASTNode} node A node to check.
163 * @returns {boolean} Whether or not the node is callee.
165 function isCallee(node) {
166 return node.parent.type === "CallExpression" && node.parent.callee === node;
170 * Checks whether or not a node is `Reflect.apply`.
171 * @param {ASTNode} node A node to check.
172 * @returns {boolean} Whether or not the node is a `Reflect.apply`.
174 function isReflectApply(node) {
176 node.type === "MemberExpression" &&
177 node.object.type === "Identifier" &&
178 node.object.name === "Reflect" &&
179 node.property.type === "Identifier" &&
180 node.property.name === "apply" &&
181 node.computed === false
186 * Checks whether or not a node is `Array.from`.
187 * @param {ASTNode} node A node to check.
188 * @returns {boolean} Whether or not the node is a `Array.from`.
190 function isArrayFromMethod(node) {
192 node.type === "MemberExpression" &&
193 node.object.type === "Identifier" &&
194 arrayOrTypedArrayPattern.test(node.object.name) &&
195 node.property.type === "Identifier" &&
196 node.property.name === "from" &&
197 node.computed === false
202 * Checks whether or not a node is a method which has `thisArg`.
203 * @param {ASTNode} node A node to check.
204 * @returns {boolean} Whether or not the node is a method which has `thisArg`.
206 function isMethodWhichHasThisArg(node) {
208 let currentNode = node;
209 currentNode.type === "MemberExpression" && !currentNode.computed;
210 currentNode = currentNode.property
212 if (currentNode.property.type === "Identifier") {
213 return arrayMethodPattern.test(currentNode.property.name);
221 * Creates the negate function of the given function.
222 * @param {Function} f The function to negate.
223 * @returns {Function} Negated function.
226 return token => !f(token);
230 * Checks whether or not a node has a `@this` tag in its comments.
231 * @param {ASTNode} node A node to check.
232 * @param {SourceCode} sourceCode A SourceCode instance to get comments.
233 * @returns {boolean} Whether or not the node has a `@this` tag in its comments.
235 function hasJSDocThisTag(node, sourceCode) {
236 const jsdocComment = sourceCode.getJSDocComment(node);
238 if (jsdocComment && thisTagPattern.test(jsdocComment.value)) {
242 // Checks `@this` in its leading comments for callbacks,
243 // because callbacks don't have its JSDoc comment.
245 // sinon.test(/* @this sinon.Sandbox */function() { this.spy(); });
246 return sourceCode.getCommentsBefore(node).some(comment => thisTagPattern.test(comment.value));
250 * Determines if a node is surrounded by parentheses.
251 * @param {SourceCode} sourceCode The ESLint source code object
252 * @param {ASTNode} node The node to be checked.
253 * @returns {boolean} True if the node is parenthesised.
256 function isParenthesised(sourceCode, node) {
257 const previousToken = sourceCode.getTokenBefore(node),
258 nextToken = sourceCode.getTokenAfter(node);
260 return Boolean(previousToken && nextToken) &&
261 previousToken.value === "(" && previousToken.range[1] <= node.range[0] &&
262 nextToken.value === ")" && nextToken.range[0] >= node.range[1];
266 * Checks if the given token is an arrow token or not.
267 * @param {Token} token The token to check.
268 * @returns {boolean} `true` if the token is an arrow token.
270 function isArrowToken(token) {
271 return token.value === "=>" && token.type === "Punctuator";
275 * Checks if the given token is a comma token or not.
276 * @param {Token} token The token to check.
277 * @returns {boolean} `true` if the token is a comma token.
279 function isCommaToken(token) {
280 return token.value === "," && token.type === "Punctuator";
284 * Checks if the given token is a dot token or not.
285 * @param {Token} token The token to check.
286 * @returns {boolean} `true` if the token is a dot token.
288 function isDotToken(token) {
289 return token.value === "." && token.type === "Punctuator";
293 * Checks if the given token is a semicolon token or not.
294 * @param {Token} token The token to check.
295 * @returns {boolean} `true` if the token is a semicolon token.
297 function isSemicolonToken(token) {
298 return token.value === ";" && token.type === "Punctuator";
302 * Checks if the given token is a colon token or not.
303 * @param {Token} token The token to check.
304 * @returns {boolean} `true` if the token is a colon token.
306 function isColonToken(token) {
307 return token.value === ":" && token.type === "Punctuator";
311 * Checks if the given token is an opening parenthesis token or not.
312 * @param {Token} token The token to check.
313 * @returns {boolean} `true` if the token is an opening parenthesis token.
315 function isOpeningParenToken(token) {
316 return token.value === "(" && token.type === "Punctuator";
320 * Checks if the given token is a closing parenthesis token or not.
321 * @param {Token} token The token to check.
322 * @returns {boolean} `true` if the token is a closing parenthesis token.
324 function isClosingParenToken(token) {
325 return token.value === ")" && token.type === "Punctuator";
329 * Checks if the given token is an opening square bracket token or not.
330 * @param {Token} token The token to check.
331 * @returns {boolean} `true` if the token is an opening square bracket token.
333 function isOpeningBracketToken(token) {
334 return token.value === "[" && token.type === "Punctuator";
338 * Checks if the given token is a closing square bracket token or not.
339 * @param {Token} token The token to check.
340 * @returns {boolean} `true` if the token is a closing square bracket token.
342 function isClosingBracketToken(token) {
343 return token.value === "]" && token.type === "Punctuator";
347 * Checks if the given token is an opening brace token or not.
348 * @param {Token} token The token to check.
349 * @returns {boolean} `true` if the token is an opening brace token.
351 function isOpeningBraceToken(token) {
352 return token.value === "{" && token.type === "Punctuator";
356 * Checks if the given token is a closing brace token or not.
357 * @param {Token} token The token to check.
358 * @returns {boolean} `true` if the token is a closing brace token.
360 function isClosingBraceToken(token) {
361 return token.value === "}" && token.type === "Punctuator";
365 * Checks if the given token is a comment token or not.
366 * @param {Token} token The token to check.
367 * @returns {boolean} `true` if the token is a comment token.
369 function isCommentToken(token) {
370 return token.type === "Line" || token.type === "Block" || token.type === "Shebang";
374 * Checks if the given token is a keyword token or not.
375 * @param {Token} token The token to check.
376 * @returns {boolean} `true` if the token is a keyword token.
378 function isKeywordToken(token) {
379 return token.type === "Keyword";
383 * Gets the `(` token of the given function node.
384 * @param {ASTNode} node The function node to get.
385 * @param {SourceCode} sourceCode The source code object to get tokens.
386 * @returns {Token} `(` token.
388 function getOpeningParenOfParams(node, sourceCode) {
390 ? sourceCode.getTokenAfter(node.id, isOpeningParenToken)
391 : sourceCode.getFirstToken(node, isOpeningParenToken);
395 * Checks whether or not the tokens of two given nodes are same.
396 * @param {ASTNode} left A node 1 to compare.
397 * @param {ASTNode} right A node 2 to compare.
398 * @param {SourceCode} sourceCode The ESLint source code object.
399 * @returns {boolean} the source code for the given node.
401 function equalTokens(left, right, sourceCode) {
402 const tokensL = sourceCode.getTokens(left);
403 const tokensR = sourceCode.getTokens(right);
405 if (tokensL.length !== tokensR.length) {
408 for (let i = 0; i < tokensL.length; ++i) {
409 if (tokensL[i].type !== tokensR[i].type ||
410 tokensL[i].value !== tokensR[i].value
419 //------------------------------------------------------------------------------
421 //------------------------------------------------------------------------------
424 COMMENTS_IGNORE_PATTERN,
426 LINEBREAK_MATCHER: lineBreakPattern,
427 SHEBANG_MATCHER: shebangPattern,
428 STATEMENT_LIST_PARENTS,
431 * Determines whether two adjacent tokens are on the same line.
432 * @param {Object} left The left token object.
433 * @param {Object} right The right token object.
434 * @returns {boolean} Whether or not the tokens are on the same line.
437 isTokenOnSameLine(left, right) {
438 return left.loc.end.line === right.loc.start.line;
450 createGlobalLinebreakMatcher,
455 isClosingBracketToken,
462 isNotClosingBraceToken: negate(isClosingBraceToken),
463 isNotClosingBracketToken: negate(isClosingBracketToken),
464 isNotClosingParenToken: negate(isClosingParenToken),
465 isNotColonToken: negate(isColonToken),
466 isNotCommaToken: negate(isCommaToken),
467 isNotDotToken: negate(isDotToken),
468 isNotOpeningBraceToken: negate(isOpeningBraceToken),
469 isNotOpeningBracketToken: negate(isOpeningBracketToken),
470 isNotOpeningParenToken: negate(isOpeningParenToken),
471 isNotSemicolonToken: negate(isSemicolonToken),
473 isOpeningBracketToken,
478 * Checks whether or not a given node is a string literal.
479 * @param {ASTNode} node A node to check.
480 * @returns {boolean} `true` if the node is a string literal.
482 isStringLiteral(node) {
484 (node.type === "Literal" && typeof node.value === "string") ||
485 node.type === "TemplateLiteral"
490 * Checks whether a given node is a breakable statement or not.
491 * The node is breakable if the node is one of the following type:
499 * @param {ASTNode} node A node to check.
500 * @returns {boolean} `true` if the node is breakable.
502 isBreakableStatement(node) {
503 return breakableTypePattern.test(node.type);
507 * Gets references which are non initializer and writable.
508 * @param {Reference[]} references An array of references.
509 * @returns {Reference[]} An array of only references which are non initializer and writable.
512 getModifyingReferences(references) {
513 return references.filter(isModifyingReference);
517 * Validate that a string passed in is surrounded by the specified character
518 * @param {string} val The text to check.
519 * @param {string} character The character to see if it's surrounded by.
520 * @returns {boolean} True if the text is surrounded by the character, false if not.
523 isSurroundedBy(val, character) {
524 return val[0] === character && val[val.length - 1] === character;
528 * Returns whether the provided node is an ESLint directive comment or not
529 * @param {Line|Block} node The comment token to be checked
530 * @returns {boolean} `true` if the node is an ESLint directive comment
532 isDirectiveComment(node) {
533 const comment = node.value.trim();
536 node.type === "Line" && comment.indexOf("eslint-") === 0 ||
537 node.type === "Block" && (
538 comment.indexOf("global ") === 0 ||
539 comment.indexOf("eslint ") === 0 ||
540 comment.indexOf("eslint-") === 0
546 * Gets the trailing statement of a given node.
551 * When taking this `IfStatement`, returns `consequent;` statement.
552 * @param {ASTNode} A node to get.
553 * @returns {ASTNode|null} The trailing statement's node.
555 getTrailingStatement: esutils.ast.trailingStatement,
558 * Finds the variable by a given name in a given scope and its upper scopes.
559 * @param {eslint-scope.Scope} initScope A scope to start find.
560 * @param {string} name A variable name to find.
561 * @returns {eslint-scope.Variable|null} A found variable or `null`.
563 getVariableByName(initScope, name) {
564 let scope = initScope;
567 const variable = scope.set.get(name);
580 * Checks whether or not a given function node is the default `this` binding.
582 * First, this checks the node:
584 * - The function name does not start with uppercase. It's a convention to capitalize the names
585 * of constructor functions. This check is not performed if `capIsConstructor` is set to `false`.
586 * - The function does not have a JSDoc comment that has a @this tag.
588 * Next, this checks the location of the node.
589 * If the location is below, this judges `this` is valid.
591 * - The location is not on an object literal.
592 * - The location is not assigned to a variable which starts with an uppercase letter. Applies to anonymous
593 * functions only, as the name of the variable is considered to be the name of the function in this case.
594 * This check is not performed if `capIsConstructor` is set to `false`.
595 * - The location is not on an ES2015 class.
596 * - Its `bind`/`call`/`apply` method is not called directly.
597 * - The function is not a callback of array methods (such as `.forEach()`) if `thisArg` is given.
598 * @param {ASTNode} node A function node to check.
599 * @param {SourceCode} sourceCode A SourceCode instance to get comments.
600 * @param {boolean} [capIsConstructor = true] `false` disables the assumption that functions which name starts
601 * with an uppercase or are assigned to a variable which name starts with an uppercase are constructors.
602 * @returns {boolean} The function node is the default `this` binding.
604 isDefaultThisBinding(node, sourceCode, { capIsConstructor = true } = {}) {
606 (capIsConstructor && isES5Constructor(node)) ||
607 hasJSDocThisTag(node, sourceCode)
611 const isAnonymous = node.id === null;
612 let currentNode = node;
614 while (currentNode) {
615 const parent = currentNode.parent;
617 switch (parent.type) {
620 * Looks up the destination.
621 * e.g., obj.foo = nativeFoo || function foo() { ... };
623 case "LogicalExpression":
624 case "ConditionalExpression":
625 currentNode = parent;
629 * If the upper function is IIFE, checks the destination of the return value.
631 * obj.foo = (function() {
633 * return function foo() { ... };
636 * function foo() { ... }
639 case "ReturnStatement": {
640 const func = getUpperFunction(parent);
642 if (func === null || !isCallee(func)) {
645 currentNode = func.parent;
648 case "ArrowFunctionExpression":
649 if (currentNode !== parent.body || !isCallee(parent)) {
652 currentNode = parent.parent;
657 * var obj = { foo() { ... } };
658 * var obj = { foo: function() { ... } };
659 * class A { constructor() { ... } }
660 * class A { foo() { ... } }
661 * class A { get foo() { ... } }
662 * class A { set foo() { ... } }
663 * class A { static foo() { ... } }
666 case "MethodDefinition":
667 return parent.value !== currentNode;
671 * obj.foo = function foo() { ... };
672 * Foo = function() { ... };
673 * [obj.foo = function foo() { ... }] = a;
674 * [Foo = function() { ... }] = a;
676 case "AssignmentExpression":
677 case "AssignmentPattern":
678 if (parent.left.type === "MemberExpression") {
684 parent.left.type === "Identifier" &&
685 startsWithUpperCase(parent.left.name)
693 * var Foo = function() { ... };
695 case "VariableDeclarator":
699 parent.init === currentNode &&
700 parent.id.type === "Identifier" &&
701 startsWithUpperCase(parent.id.name)
706 * var foo = function foo() { ... }.bind(obj);
707 * (function foo() { ... }).call(obj);
708 * (function foo() { ... }).apply(obj, []);
710 case "MemberExpression":
712 parent.object !== currentNode ||
713 parent.property.type !== "Identifier" ||
714 !bindOrCallOrApplyPattern.test(parent.property.name) ||
716 parent.parent.arguments.length === 0 ||
717 isNullOrUndefined(parent.parent.arguments[0])
722 * Reflect.apply(function() {}, obj, []);
723 * Array.from([], function() {}, obj);
724 * list.forEach(function() {}, obj);
726 case "CallExpression":
727 if (isReflectApply(parent.callee)) {
729 parent.arguments.length !== 3 ||
730 parent.arguments[0] !== currentNode ||
731 isNullOrUndefined(parent.arguments[1])
734 if (isArrayFromMethod(parent.callee)) {
736 parent.arguments.length !== 3 ||
737 parent.arguments[1] !== currentNode ||
738 isNullOrUndefined(parent.arguments[2])
741 if (isMethodWhichHasThisArg(parent.callee)) {
743 parent.arguments.length !== 2 ||
744 parent.arguments[0] !== currentNode ||
745 isNullOrUndefined(parent.arguments[1])
750 // Otherwise `this` is default.
756 /* istanbul ignore next */
761 * Get the precedence level based on the node type
762 * @param {ASTNode} node node to evaluate
763 * @returns {int} precedence level
766 getPrecedence(node) {
768 case "SequenceExpression":
771 case "AssignmentExpression":
772 case "ArrowFunctionExpression":
773 case "YieldExpression":
776 case "ConditionalExpression":
779 case "LogicalExpression":
780 switch (node.operator) {
791 case "BinaryExpression":
793 switch (node.operator) {
831 case "UnaryExpression":
832 case "AwaitExpression":
835 case "UpdateExpression":
838 case "CallExpression":
839 case "ImportExpression":
842 case "NewExpression":
851 * Checks whether the given node is an empty block node or not.
852 * @param {ASTNode|null} node The node to check.
853 * @returns {boolean} `true` if the node is an empty block.
856 return Boolean(node && node.type === "BlockStatement" && node.body.length === 0);
860 * Checks whether the given node is an empty function node or not.
861 * @param {ASTNode|null} node The node to check.
862 * @returns {boolean} `true` if the node is an empty function.
864 isEmptyFunction(node) {
865 return isFunction(node) && module.exports.isEmptyBlock(node.body);
869 * Gets the property name of a given node.
870 * The node can be a MemberExpression, a Property, or a MethodDefinition.
872 * If the name is dynamic, this returns `null`.
882 * a["a" + "b"] // => null
883 * a[tag`b`] // => null
884 * a[`${b}`] // => null
886 * let a = {b: 1} // => "b"
887 * let a = {["b"]: 1} // => "b"
888 * let a = {['b']: 1} // => "b"
889 * let a = {[`b`]: 1} // => "b"
890 * let a = {[100]: 1} // => "100"
891 * let a = {[b]: 1} // => null
892 * let a = {["a" + "b"]: 1} // => null
893 * let a = {[tag`b`]: 1} // => null
894 * let a = {[`${b}`]: 1} // => null
895 * @param {ASTNode} node The node to get.
896 * @returns {string|null} The property name if static. Otherwise, null.
898 getStaticPropertyName(node) {
901 switch (node && node.type) {
903 case "MethodDefinition":
907 case "MemberExpression":
908 prop = node.property;
914 switch (prop && prop.type) {
916 return String(prop.value);
918 case "TemplateLiteral":
919 if (prop.expressions.length === 0 && prop.quasis.length === 1) {
920 return prop.quasis[0].value.cooked;
925 if (!node.computed) {
937 * Get directives from directive prologue of a Program or Function node.
938 * @param {ASTNode} node The node to check.
939 * @returns {ASTNode[]} The directives found in the directive prologue.
941 getDirectivePrologue(node) {
942 const directives = [];
944 // Directive prologues only occur at the top of files or functions.
946 node.type === "Program" ||
947 node.type === "FunctionDeclaration" ||
948 node.type === "FunctionExpression" ||
951 * Do not check arrow functions with implicit return.
952 * `() => "use strict";` returns the string `"use strict"`.
954 (node.type === "ArrowFunctionExpression" && node.body.type === "BlockStatement")
956 const statements = node.type === "Program" ? node.body : node.body.body;
958 for (const statement of statements) {
960 statement.type === "ExpressionStatement" &&
961 statement.expression.type === "Literal"
963 directives.push(statement);
975 * Determines whether this node is a decimal integer literal. If a node is a decimal integer literal, a dot added
976 * after the node will be parsed as a decimal point, rather than a property-access dot.
977 * @param {ASTNode} node The node to check.
978 * @returns {boolean} `true` if this node is a decimal integer.
991 isDecimalInteger(node) {
992 return node.type === "Literal" && typeof node.value === "number" &&
993 DECIMAL_INTEGER_PATTERN.test(node.raw);
997 * Determines whether this token is a decimal integer numeric token.
998 * This is similar to isDecimalInteger(), but for tokens.
999 * @param {Token} token The token to check.
1000 * @returns {boolean} `true` if this token is a decimal integer.
1002 isDecimalIntegerNumericToken(token) {
1003 return token.type === "Numeric" && DECIMAL_INTEGER_PATTERN.test(token.value);
1007 * Gets the name and kind of the given function node.
1009 * - `function foo() {}` .................... `function 'foo'`
1010 * - `(function foo() {})` .................. `function 'foo'`
1011 * - `(function() {})` ...................... `function`
1012 * - `function* foo() {}` ................... `generator function 'foo'`
1013 * - `(function* foo() {})` ................. `generator function 'foo'`
1014 * - `(function*() {})` ..................... `generator function`
1015 * - `() => {}` ............................. `arrow function`
1016 * - `async () => {}` ....................... `async arrow function`
1017 * - `({ foo: function foo() {} })` ......... `method 'foo'`
1018 * - `({ foo: function() {} })` ............. `method 'foo'`
1019 * - `({ ['foo']: function() {} })` ......... `method 'foo'`
1020 * - `({ [foo]: function() {} })` ........... `method`
1021 * - `({ foo() {} })` ....................... `method 'foo'`
1022 * - `({ foo: function* foo() {} })` ........ `generator method 'foo'`
1023 * - `({ foo: function*() {} })` ............ `generator method 'foo'`
1024 * - `({ ['foo']: function*() {} })` ........ `generator method 'foo'`
1025 * - `({ [foo]: function*() {} })` .......... `generator method`
1026 * - `({ *foo() {} })` ...................... `generator method 'foo'`
1027 * - `({ foo: async function foo() {} })` ... `async method 'foo'`
1028 * - `({ foo: async function() {} })` ....... `async method 'foo'`
1029 * - `({ ['foo']: async function() {} })` ... `async method 'foo'`
1030 * - `({ [foo]: async function() {} })` ..... `async method`
1031 * - `({ async foo() {} })` ................. `async method 'foo'`
1032 * - `({ get foo() {} })` ................... `getter 'foo'`
1033 * - `({ set foo(a) {} })` .................. `setter 'foo'`
1034 * - `class A { constructor() {} }` ......... `constructor`
1035 * - `class A { foo() {} }` ................. `method 'foo'`
1036 * - `class A { *foo() {} }` ................ `generator method 'foo'`
1037 * - `class A { async foo() {} }` ........... `async method 'foo'`
1038 * - `class A { ['foo']() {} }` ............. `method 'foo'`
1039 * - `class A { *['foo']() {} }` ............ `generator method 'foo'`
1040 * - `class A { async ['foo']() {} }` ....... `async method 'foo'`
1041 * - `class A { [foo]() {} }` ............... `method`
1042 * - `class A { *[foo]() {} }` .............. `generator method`
1043 * - `class A { async [foo]() {} }` ......... `async method`
1044 * - `class A { get foo() {} }` ............. `getter 'foo'`
1045 * - `class A { set foo(a) {} }` ............ `setter 'foo'`
1046 * - `class A { static foo() {} }` .......... `static method 'foo'`
1047 * - `class A { static *foo() {} }` ......... `static generator method 'foo'`
1048 * - `class A { static async foo() {} }` .... `static async method 'foo'`
1049 * - `class A { static get foo() {} }` ...... `static getter 'foo'`
1050 * - `class A { static set foo(a) {} }` ..... `static setter 'foo'`
1051 * @param {ASTNode} node The function node to get.
1052 * @returns {string} The name and kind of the function node.
1054 getFunctionNameWithKind(node) {
1055 const parent = node.parent;
1058 if (parent.type === "MethodDefinition" && parent.static) {
1059 tokens.push("static");
1062 tokens.push("async");
1064 if (node.generator) {
1065 tokens.push("generator");
1068 if (node.type === "ArrowFunctionExpression") {
1069 tokens.push("arrow", "function");
1070 } else if (parent.type === "Property" || parent.type === "MethodDefinition") {
1071 if (parent.kind === "constructor") {
1072 return "constructor";
1074 if (parent.kind === "get") {
1075 tokens.push("getter");
1076 } else if (parent.kind === "set") {
1077 tokens.push("setter");
1079 tokens.push("method");
1082 tokens.push("function");
1086 tokens.push(`'${node.id.name}'`);
1088 const name = module.exports.getStaticPropertyName(parent);
1090 if (name !== null) {
1091 tokens.push(`'${name}'`);
1095 return tokens.join(" ");
1099 * Gets the location of the given function node for reporting.
1101 * - `function foo() {}`
1103 * - `(function foo() {})`
1105 * - `(function() {})`
1107 * - `function* foo() {}`
1109 * - `(function* foo() {})`
1111 * - `(function*() {})`
1115 * - `async () => {}`
1117 * - `({ foo: function foo() {} })`
1119 * - `({ foo: function() {} })`
1121 * - `({ ['foo']: function() {} })`
1123 * - `({ [foo]: function() {} })`
1125 * - `({ foo() {} })`
1127 * - `({ foo: function* foo() {} })`
1128 * ^^^^^^^^^^^^^^^^^^
1129 * - `({ foo: function*() {} })`
1131 * - `({ ['foo']: function*() {} })`
1132 * ^^^^^^^^^^^^^^^^^^
1133 * - `({ [foo]: function*() {} })`
1135 * - `({ *foo() {} })`
1137 * - `({ foo: async function foo() {} })`
1138 * ^^^^^^^^^^^^^^^^^^^^^^^
1139 * - `({ foo: async function() {} })`
1140 * ^^^^^^^^^^^^^^^^^^^
1141 * - `({ ['foo']: async function() {} })`
1142 * ^^^^^^^^^^^^^^^^^^^^^^^
1143 * - `({ [foo]: async function() {} })`
1144 * ^^^^^^^^^^^^^^^^^^^^^
1145 * - `({ async foo() {} })`
1147 * - `({ get foo() {} })`
1149 * - `({ set foo(a) {} })`
1151 * - `class A { constructor() {} }`
1153 * - `class A { foo() {} }`
1155 * - `class A { *foo() {} }`
1157 * - `class A { async foo() {} }`
1159 * - `class A { ['foo']() {} }`
1161 * - `class A { *['foo']() {} }`
1163 * - `class A { async ['foo']() {} }`
1165 * - `class A { [foo]() {} }`
1167 * - `class A { *[foo]() {} }`
1169 * - `class A { async [foo]() {} }`
1171 * - `class A { get foo() {} }`
1173 * - `class A { set foo(a) {} }`
1175 * - `class A { static foo() {} }`
1177 * - `class A { static *foo() {} }`
1179 * - `class A { static async foo() {} }`
1181 * - `class A { static get foo() {} }`
1183 * - `class A { static set foo(a) {} }`
1185 * @param {ASTNode} node The function node to get.
1186 * @param {SourceCode} sourceCode The source code object to get tokens.
1187 * @returns {string} The location of the function node for reporting.
1189 getFunctionHeadLoc(node, sourceCode) {
1190 const parent = node.parent;
1194 if (node.type === "ArrowFunctionExpression") {
1195 const arrowToken = sourceCode.getTokenBefore(node.body, isArrowToken);
1197 start = arrowToken.loc.start;
1198 end = arrowToken.loc.end;
1199 } else if (parent.type === "Property" || parent.type === "MethodDefinition") {
1200 start = parent.loc.start;
1201 end = getOpeningParenOfParams(node, sourceCode).loc.start;
1203 start = node.loc.start;
1204 end = getOpeningParenOfParams(node, sourceCode).loc.start;
1208 start: Object.assign({}, start),
1209 end: Object.assign({}, end)
1214 * Gets next location when the result is not out of bound, otherwise returns null.
1215 * @param {SourceCode} sourceCode The sourceCode
1216 * @param {{line: number, column: number}} location The location
1217 * @returns {{line: number, column: number} | null} Next location
1219 getNextLocation(sourceCode, location) {
1220 const index = sourceCode.getIndexFromLoc(location);
1222 // Avoid out of bound location
1223 if (index + 1 > sourceCode.text.length) {
1227 return sourceCode.getLocFromIndex(index + 1);
1231 * Gets the parenthesized text of a node. This is similar to sourceCode.getText(node), but it also includes any parentheses
1232 * surrounding the node.
1233 * @param {SourceCode} sourceCode The source code object
1234 * @param {ASTNode} node An expression node
1235 * @returns {string} The text representing the node, with all surrounding parentheses included
1237 getParenthesisedText(sourceCode, node) {
1238 let leftToken = sourceCode.getFirstToken(node);
1239 let rightToken = sourceCode.getLastToken(node);
1242 sourceCode.getTokenBefore(leftToken) &&
1243 sourceCode.getTokenBefore(leftToken).type === "Punctuator" &&
1244 sourceCode.getTokenBefore(leftToken).value === "(" &&
1245 sourceCode.getTokenAfter(rightToken) &&
1246 sourceCode.getTokenAfter(rightToken).type === "Punctuator" &&
1247 sourceCode.getTokenAfter(rightToken).value === ")"
1249 leftToken = sourceCode.getTokenBefore(leftToken);
1250 rightToken = sourceCode.getTokenAfter(rightToken);
1253 return sourceCode.getText().slice(leftToken.range[0], rightToken.range[1]);
1257 * Determine if a node has a possiblity to be an Error object
1258 * @param {ASTNode} node ASTNode to check
1259 * @returns {boolean} True if there is a chance it contains an Error obj
1261 couldBeError(node) {
1262 switch (node.type) {
1264 case "CallExpression":
1265 case "NewExpression":
1266 case "MemberExpression":
1267 case "TaggedTemplateExpression":
1268 case "YieldExpression":
1269 case "AwaitExpression":
1270 return true; // possibly an error object.
1272 case "AssignmentExpression":
1273 return module.exports.couldBeError(node.right);
1275 case "SequenceExpression": {
1276 const exprs = node.expressions;
1278 return exprs.length !== 0 && module.exports.couldBeError(exprs[exprs.length - 1]);
1281 case "LogicalExpression":
1282 return module.exports.couldBeError(node.left) || module.exports.couldBeError(node.right);
1284 case "ConditionalExpression":
1285 return module.exports.couldBeError(node.consequent) || module.exports.couldBeError(node.alternate);
1293 * Determines whether the given node is a `null` literal.
1294 * @param {ASTNode} node The node to check
1295 * @returns {boolean} `true` if the node is a `null` literal
1297 isNullLiteral(node) {
1300 * Checking `node.value === null` does not guarantee that a literal is a null literal.
1301 * When parsing values that cannot be represented in the current environment (e.g. unicode
1302 * regexes in Node 4), `node.value` is set to `null` because it wouldn't be possible to
1303 * set `node.value` to a unicode regex. To make sure a literal is actually `null`, check
1304 * `node.regex` instead. Also see: https://github.com/eslint/eslint/issues/8020
1306 return node.type === "Literal" && node.value === null && !node.regex && !node.bigint;
1310 * Determines whether two tokens can safely be placed next to each other without merging into a single token
1311 * @param {Token|string} leftValue The left token. If this is a string, it will be tokenized and the last token will be used.
1312 * @param {Token|string} rightValue The right token. If this is a string, it will be tokenized and the first token will be used.
1313 * @returns {boolean} If the tokens cannot be safely placed next to each other, returns `false`. If the tokens can be placed
1314 * next to each other, behavior is undefined (although it should return `true` in most cases).
1316 canTokensBeAdjacent(leftValue, rightValue) {
1319 if (typeof leftValue === "string") {
1320 const leftTokens = espree.tokenize(leftValue, { ecmaVersion: 2015 });
1322 leftToken = leftTokens[leftTokens.length - 1];
1324 leftToken = leftValue;
1327 const rightToken = typeof rightValue === "string" ? espree.tokenize(rightValue, { ecmaVersion: 2015 })[0] : rightValue;
1329 if (leftToken.type === "Punctuator" || rightToken.type === "Punctuator") {
1330 if (leftToken.type === "Punctuator" && rightToken.type === "Punctuator") {
1331 const PLUS_TOKENS = new Set(["+", "++"]);
1332 const MINUS_TOKENS = new Set(["-", "--"]);
1335 PLUS_TOKENS.has(leftToken.value) && PLUS_TOKENS.has(rightToken.value) ||
1336 MINUS_TOKENS.has(leftToken.value) && MINUS_TOKENS.has(rightToken.value)
1343 leftToken.type === "String" || rightToken.type === "String" ||
1344 leftToken.type === "Template" || rightToken.type === "Template"
1349 if (leftToken.type !== "Numeric" && rightToken.type === "Numeric" && rightToken.value.startsWith(".")) {
1357 * Get the `loc` object of a given name in a `/*globals` directive comment.
1358 * @param {SourceCode} sourceCode The source code to convert index to loc.
1359 * @param {Comment} comment The `/*globals` directive comment which include the name.
1360 * @param {string} name The name to find.
1361 * @returns {SourceLocation} The `loc` object.
1363 getNameLocationInGlobalDirectiveComment(sourceCode, comment, name) {
1364 const namePattern = new RegExp(`[\\s,]${lodash.escapeRegExp(name)}(?:$|[\\s,:])`, "gu");
1366 // To ignore the first text "global".
1367 namePattern.lastIndex = comment.value.indexOf("global") + 6;
1369 // Search a given variable name.
1370 const match = namePattern.exec(comment.value);
1372 // Convert the index to loc.
1373 return sourceCode.getLocFromIndex(
1376 (match ? match.index + 1 : 0)
1381 * Determines whether the given raw string contains an octal escape sequence.
1383 * "\1", "\2" ... "\7"
1384 * "\00", "\01" ... "\09"
1386 * "\0", when not followed by a digit, is not an octal escape sequence.
1387 * @param {string} rawString A string in its raw representation.
1388 * @returns {boolean} `true` if the string contains at least one octal escape sequence.
1390 hasOctalEscapeSequence(rawString) {
1391 return OCTAL_ESCAPE_PATTERN.test(rawString);