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