no-extra-parens.js 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669
  1. /**
  2. * @fileoverview Disallow parenthesising higher precedence subexpressions.
  3. * @author Michael Ficarra
  4. * @deprecated in ESLint v8.53.0
  5. */
  6. "use strict";
  7. //------------------------------------------------------------------------------
  8. // Rule Definition
  9. //------------------------------------------------------------------------------
  10. const {
  11. isParenthesized: isParenthesizedRaw,
  12. } = require("@eslint-community/eslint-utils");
  13. const astUtils = require("./utils/ast-utils.js");
  14. /** @type {import('../types').Rule.RuleModule} */
  15. module.exports = {
  16. meta: {
  17. deprecated: {
  18. message: "Formatting rules are being moved out of ESLint core.",
  19. url: "https://eslint.org/blog/2023/10/deprecating-formatting-rules/",
  20. deprecatedSince: "8.53.0",
  21. availableUntil: "11.0.0",
  22. replacedBy: [
  23. {
  24. message:
  25. "ESLint Stylistic now maintains deprecated stylistic core rules.",
  26. url: "https://eslint.style/guide/migration",
  27. plugin: {
  28. name: "@stylistic/eslint-plugin",
  29. url: "https://eslint.style",
  30. },
  31. rule: {
  32. name: "no-extra-parens",
  33. url: "https://eslint.style/rules/no-extra-parens",
  34. },
  35. },
  36. ],
  37. },
  38. type: "layout",
  39. docs: {
  40. description: "Disallow unnecessary parentheses",
  41. recommended: false,
  42. url: "https://eslint.org/docs/latest/rules/no-extra-parens",
  43. },
  44. fixable: "code",
  45. schema: {
  46. anyOf: [
  47. {
  48. type: "array",
  49. items: [
  50. {
  51. enum: ["functions"],
  52. },
  53. ],
  54. minItems: 0,
  55. maxItems: 1,
  56. },
  57. {
  58. type: "array",
  59. items: [
  60. {
  61. enum: ["all"],
  62. },
  63. {
  64. type: "object",
  65. properties: {
  66. conditionalAssign: { type: "boolean" },
  67. ternaryOperandBinaryExpressions: {
  68. type: "boolean",
  69. },
  70. nestedBinaryExpressions: { type: "boolean" },
  71. returnAssign: { type: "boolean" },
  72. ignoreJSX: {
  73. enum: [
  74. "none",
  75. "all",
  76. "single-line",
  77. "multi-line",
  78. ],
  79. },
  80. enforceForArrowConditionals: {
  81. type: "boolean",
  82. },
  83. enforceForSequenceExpressions: {
  84. type: "boolean",
  85. },
  86. enforceForNewInMemberExpressions: {
  87. type: "boolean",
  88. },
  89. enforceForFunctionPrototypeMethods: {
  90. type: "boolean",
  91. },
  92. allowParensAfterCommentPattern: {
  93. type: "string",
  94. },
  95. },
  96. additionalProperties: false,
  97. },
  98. ],
  99. minItems: 0,
  100. maxItems: 2,
  101. },
  102. ],
  103. },
  104. messages: {
  105. unexpected: "Unnecessary parentheses around expression.",
  106. },
  107. },
  108. create(context) {
  109. const sourceCode = context.sourceCode;
  110. const tokensToIgnore = new WeakSet();
  111. const precedence = astUtils.getPrecedence;
  112. const ALL_NODES = context.options[0] !== "functions";
  113. const EXCEPT_COND_ASSIGN =
  114. ALL_NODES &&
  115. context.options[1] &&
  116. context.options[1].conditionalAssign === false;
  117. const EXCEPT_COND_TERNARY =
  118. ALL_NODES &&
  119. context.options[1] &&
  120. context.options[1].ternaryOperandBinaryExpressions === false;
  121. const NESTED_BINARY =
  122. ALL_NODES &&
  123. context.options[1] &&
  124. context.options[1].nestedBinaryExpressions === false;
  125. const EXCEPT_RETURN_ASSIGN =
  126. ALL_NODES &&
  127. context.options[1] &&
  128. context.options[1].returnAssign === false;
  129. const IGNORE_JSX =
  130. ALL_NODES && context.options[1] && context.options[1].ignoreJSX;
  131. const IGNORE_ARROW_CONDITIONALS =
  132. ALL_NODES &&
  133. context.options[1] &&
  134. context.options[1].enforceForArrowConditionals === false;
  135. const IGNORE_SEQUENCE_EXPRESSIONS =
  136. ALL_NODES &&
  137. context.options[1] &&
  138. context.options[1].enforceForSequenceExpressions === false;
  139. const IGNORE_NEW_IN_MEMBER_EXPR =
  140. ALL_NODES &&
  141. context.options[1] &&
  142. context.options[1].enforceForNewInMemberExpressions === false;
  143. const IGNORE_FUNCTION_PROTOTYPE_METHODS =
  144. ALL_NODES &&
  145. context.options[1] &&
  146. context.options[1].enforceForFunctionPrototypeMethods === false;
  147. const ALLOW_PARENS_AFTER_COMMENT_PATTERN =
  148. ALL_NODES &&
  149. context.options[1] &&
  150. context.options[1].allowParensAfterCommentPattern;
  151. const PRECEDENCE_OF_ASSIGNMENT_EXPR = precedence({
  152. type: "AssignmentExpression",
  153. });
  154. const PRECEDENCE_OF_UPDATE_EXPR = precedence({
  155. type: "UpdateExpression",
  156. });
  157. let reportsBuffer;
  158. /**
  159. * Determines whether the given node is a `call` or `apply` method call, invoked directly on a `FunctionExpression` node.
  160. * Example: function(){}.call()
  161. * @param {ASTNode} node The node to be checked.
  162. * @returns {boolean} True if the node is an immediate `call` or `apply` method call.
  163. * @private
  164. */
  165. function isImmediateFunctionPrototypeMethodCall(node) {
  166. const callNode = astUtils.skipChainExpression(node);
  167. if (callNode.type !== "CallExpression") {
  168. return false;
  169. }
  170. const callee = astUtils.skipChainExpression(callNode.callee);
  171. return (
  172. callee.type === "MemberExpression" &&
  173. callee.object.type === "FunctionExpression" &&
  174. ["call", "apply"].includes(
  175. astUtils.getStaticPropertyName(callee),
  176. )
  177. );
  178. }
  179. /**
  180. * Determines if this rule should be enforced for a node given the current configuration.
  181. * @param {ASTNode} node The node to be checked.
  182. * @returns {boolean} True if the rule should be enforced for this node.
  183. * @private
  184. */
  185. function ruleApplies(node) {
  186. if (node.type === "JSXElement" || node.type === "JSXFragment") {
  187. const isSingleLine = node.loc.start.line === node.loc.end.line;
  188. switch (IGNORE_JSX) {
  189. // Exclude this JSX element from linting
  190. case "all":
  191. return false;
  192. // Exclude this JSX element if it is multi-line element
  193. case "multi-line":
  194. return isSingleLine;
  195. // Exclude this JSX element if it is single-line element
  196. case "single-line":
  197. return !isSingleLine;
  198. // Nothing special to be done for JSX elements
  199. case "none":
  200. break;
  201. // no default
  202. }
  203. }
  204. if (
  205. node.type === "SequenceExpression" &&
  206. IGNORE_SEQUENCE_EXPRESSIONS
  207. ) {
  208. return false;
  209. }
  210. if (
  211. isImmediateFunctionPrototypeMethodCall(node) &&
  212. IGNORE_FUNCTION_PROTOTYPE_METHODS
  213. ) {
  214. return false;
  215. }
  216. return (
  217. ALL_NODES ||
  218. node.type === "FunctionExpression" ||
  219. node.type === "ArrowFunctionExpression"
  220. );
  221. }
  222. /**
  223. * Determines if a node is surrounded by parentheses.
  224. * @param {ASTNode} node The node to be checked.
  225. * @returns {boolean} True if the node is parenthesised.
  226. * @private
  227. */
  228. function isParenthesised(node) {
  229. return isParenthesizedRaw(1, node, sourceCode);
  230. }
  231. /**
  232. * Determines if a node is surrounded by parentheses twice.
  233. * @param {ASTNode} node The node to be checked.
  234. * @returns {boolean} True if the node is doubly parenthesised.
  235. * @private
  236. */
  237. function isParenthesisedTwice(node) {
  238. return isParenthesizedRaw(2, node, sourceCode);
  239. }
  240. /**
  241. * Determines if a node is surrounded by (potentially) invalid parentheses.
  242. * @param {ASTNode} node The node to be checked.
  243. * @returns {boolean} True if the node is incorrectly parenthesised.
  244. * @private
  245. */
  246. function hasExcessParens(node) {
  247. return ruleApplies(node) && isParenthesised(node);
  248. }
  249. /**
  250. * Determines if a node that is expected to be parenthesised is surrounded by
  251. * (potentially) invalid extra parentheses.
  252. * @param {ASTNode} node The node to be checked.
  253. * @returns {boolean} True if the node is has an unexpected extra pair of parentheses.
  254. * @private
  255. */
  256. function hasDoubleExcessParens(node) {
  257. return ruleApplies(node) && isParenthesisedTwice(node);
  258. }
  259. /**
  260. * Determines if a node that is expected to be parenthesised is surrounded by
  261. * (potentially) invalid extra parentheses with considering precedence level of the node.
  262. * If the preference level of the node is not higher or equal to precedence lower limit, it also checks
  263. * whether the node is surrounded by parentheses twice or not.
  264. * @param {ASTNode} node The node to be checked.
  265. * @param {number} precedenceLowerLimit The lower limit of precedence.
  266. * @returns {boolean} True if the node is has an unexpected extra pair of parentheses.
  267. * @private
  268. */
  269. function hasExcessParensWithPrecedence(node, precedenceLowerLimit) {
  270. if (ruleApplies(node) && isParenthesised(node)) {
  271. if (
  272. precedence(node) >= precedenceLowerLimit ||
  273. isParenthesisedTwice(node)
  274. ) {
  275. return true;
  276. }
  277. }
  278. return false;
  279. }
  280. /**
  281. * Determines if a node test expression is allowed to have a parenthesised assignment
  282. * @param {ASTNode} node The node to be checked.
  283. * @returns {boolean} True if the assignment can be parenthesised.
  284. * @private
  285. */
  286. function isCondAssignException(node) {
  287. return (
  288. EXCEPT_COND_ASSIGN && node.test.type === "AssignmentExpression"
  289. );
  290. }
  291. /**
  292. * Determines if a node is in a return statement
  293. * @param {ASTNode} node The node to be checked.
  294. * @returns {boolean} True if the node is in a return statement.
  295. * @private
  296. */
  297. function isInReturnStatement(node) {
  298. for (
  299. let currentNode = node;
  300. currentNode;
  301. currentNode = currentNode.parent
  302. ) {
  303. if (
  304. currentNode.type === "ReturnStatement" ||
  305. (currentNode.type === "ArrowFunctionExpression" &&
  306. currentNode.body.type !== "BlockStatement")
  307. ) {
  308. return true;
  309. }
  310. }
  311. return false;
  312. }
  313. /**
  314. * Determines if a constructor function is newed-up with parens
  315. * @param {ASTNode} newExpression The NewExpression node to be checked.
  316. * @returns {boolean} True if the constructor is called with parens.
  317. * @private
  318. */
  319. function isNewExpressionWithParens(newExpression) {
  320. const lastToken = sourceCode.getLastToken(newExpression);
  321. const penultimateToken = sourceCode.getTokenBefore(lastToken);
  322. return (
  323. newExpression.arguments.length > 0 ||
  324. // The expression should end with its own parens, e.g., new new foo() is not a new expression with parens
  325. (astUtils.isOpeningParenToken(penultimateToken) &&
  326. astUtils.isClosingParenToken(lastToken) &&
  327. newExpression.callee.range[1] < newExpression.range[1])
  328. );
  329. }
  330. /**
  331. * Determines if a node is or contains an assignment expression
  332. * @param {ASTNode} node The node to be checked.
  333. * @returns {boolean} True if the node is or contains an assignment expression.
  334. * @private
  335. */
  336. function containsAssignment(node) {
  337. if (node.type === "AssignmentExpression") {
  338. return true;
  339. }
  340. if (
  341. node.type === "ConditionalExpression" &&
  342. (node.consequent.type === "AssignmentExpression" ||
  343. node.alternate.type === "AssignmentExpression")
  344. ) {
  345. return true;
  346. }
  347. if (
  348. (node.left && node.left.type === "AssignmentExpression") ||
  349. (node.right && node.right.type === "AssignmentExpression")
  350. ) {
  351. return true;
  352. }
  353. return false;
  354. }
  355. /**
  356. * Determines if a node is contained by or is itself a return statement and is allowed to have a parenthesised assignment
  357. * @param {ASTNode} node The node to be checked.
  358. * @returns {boolean} True if the assignment can be parenthesised.
  359. * @private
  360. */
  361. function isReturnAssignException(node) {
  362. if (!EXCEPT_RETURN_ASSIGN || !isInReturnStatement(node)) {
  363. return false;
  364. }
  365. if (node.type === "ReturnStatement") {
  366. return node.argument && containsAssignment(node.argument);
  367. }
  368. if (
  369. node.type === "ArrowFunctionExpression" &&
  370. node.body.type !== "BlockStatement"
  371. ) {
  372. return containsAssignment(node.body);
  373. }
  374. return containsAssignment(node);
  375. }
  376. /**
  377. * Determines if a node following a [no LineTerminator here] restriction is
  378. * surrounded by (potentially) invalid extra parentheses.
  379. * @param {Token} token The token preceding the [no LineTerminator here] restriction.
  380. * @param {ASTNode} node The node to be checked.
  381. * @returns {boolean} True if the node is incorrectly parenthesised.
  382. * @private
  383. */
  384. function hasExcessParensNoLineTerminator(token, node) {
  385. if (token.loc.end.line === node.loc.start.line) {
  386. return hasExcessParens(node);
  387. }
  388. return hasDoubleExcessParens(node);
  389. }
  390. /**
  391. * Determines whether a node should be preceded by an additional space when removing parens
  392. * @param {ASTNode} node node to evaluate; must be surrounded by parentheses
  393. * @returns {boolean} `true` if a space should be inserted before the node
  394. * @private
  395. */
  396. function requiresLeadingSpace(node) {
  397. const leftParenToken = sourceCode.getTokenBefore(node);
  398. const tokenBeforeLeftParen = sourceCode.getTokenBefore(
  399. leftParenToken,
  400. { includeComments: true },
  401. );
  402. const tokenAfterLeftParen = sourceCode.getTokenAfter(
  403. leftParenToken,
  404. { includeComments: true },
  405. );
  406. return (
  407. tokenBeforeLeftParen &&
  408. tokenBeforeLeftParen.range[1] === leftParenToken.range[0] &&
  409. leftParenToken.range[1] === tokenAfterLeftParen.range[0] &&
  410. !astUtils.canTokensBeAdjacent(
  411. tokenBeforeLeftParen,
  412. tokenAfterLeftParen,
  413. )
  414. );
  415. }
  416. /**
  417. * Determines whether a node should be followed by an additional space when removing parens
  418. * @param {ASTNode} node node to evaluate; must be surrounded by parentheses
  419. * @returns {boolean} `true` if a space should be inserted after the node
  420. * @private
  421. */
  422. function requiresTrailingSpace(node) {
  423. const nextTwoTokens = sourceCode.getTokensAfter(node, { count: 2 });
  424. const rightParenToken = nextTwoTokens[0];
  425. const tokenAfterRightParen = nextTwoTokens[1];
  426. const tokenBeforeRightParen = sourceCode.getLastToken(node);
  427. return (
  428. rightParenToken &&
  429. tokenAfterRightParen &&
  430. !sourceCode.isSpaceBetweenTokens(
  431. rightParenToken,
  432. tokenAfterRightParen,
  433. ) &&
  434. !astUtils.canTokensBeAdjacent(
  435. tokenBeforeRightParen,
  436. tokenAfterRightParen,
  437. )
  438. );
  439. }
  440. /**
  441. * Determines if a given expression node is an IIFE
  442. * @param {ASTNode} node The node to check
  443. * @returns {boolean} `true` if the given node is an IIFE
  444. */
  445. function isIIFE(node) {
  446. const maybeCallNode = astUtils.skipChainExpression(node);
  447. return (
  448. maybeCallNode.type === "CallExpression" &&
  449. maybeCallNode.callee.type === "FunctionExpression"
  450. );
  451. }
  452. /**
  453. * Determines if the given node can be the assignment target in destructuring or the LHS of an assignment.
  454. * This is to avoid an autofix that could change behavior because parsers mistakenly allow invalid syntax,
  455. * such as `(a = b) = c` and `[(a = b) = c] = []`. Ideally, this function shouldn't be necessary.
  456. * @param {ASTNode} [node] The node to check
  457. * @returns {boolean} `true` if the given node can be a valid assignment target
  458. */
  459. function canBeAssignmentTarget(node) {
  460. return (
  461. node &&
  462. (node.type === "Identifier" || node.type === "MemberExpression")
  463. );
  464. }
  465. /**
  466. * Checks if a node is fixable.
  467. * A node is fixable if removing a single pair of surrounding parentheses does not turn it
  468. * into a directive after fixing other nodes.
  469. * Almost all nodes are fixable, except if all of the following conditions are met:
  470. * The node is a string Literal
  471. * It has a single pair of parentheses
  472. * It is the only child of an ExpressionStatement
  473. * @param {ASTNode} node The node to evaluate.
  474. * @returns {boolean} Whether or not the node is fixable.
  475. * @private
  476. */
  477. function isFixable(node) {
  478. // if it's not a string literal it can be autofixed
  479. if (node.type !== "Literal" || typeof node.value !== "string") {
  480. return true;
  481. }
  482. if (isParenthesisedTwice(node)) {
  483. return true;
  484. }
  485. return !astUtils.isTopLevelExpressionStatement(node.parent);
  486. }
  487. /**
  488. * Report the node
  489. * @param {ASTNode} node node to evaluate
  490. * @returns {void}
  491. * @private
  492. */
  493. function report(node) {
  494. const leftParenToken = sourceCode.getTokenBefore(node);
  495. const rightParenToken = sourceCode.getTokenAfter(node);
  496. if (!isParenthesisedTwice(node)) {
  497. if (tokensToIgnore.has(sourceCode.getFirstToken(node))) {
  498. return;
  499. }
  500. if (isIIFE(node) && !isParenthesised(node.callee)) {
  501. return;
  502. }
  503. if (ALLOW_PARENS_AFTER_COMMENT_PATTERN) {
  504. const commentsBeforeLeftParenToken =
  505. sourceCode.getCommentsBefore(leftParenToken);
  506. const totalCommentsBeforeLeftParenTokenCount =
  507. commentsBeforeLeftParenToken.length;
  508. const ignorePattern = new RegExp(
  509. ALLOW_PARENS_AFTER_COMMENT_PATTERN,
  510. "u",
  511. );
  512. if (
  513. totalCommentsBeforeLeftParenTokenCount > 0 &&
  514. ignorePattern.test(
  515. commentsBeforeLeftParenToken[
  516. totalCommentsBeforeLeftParenTokenCount - 1
  517. ].value,
  518. )
  519. ) {
  520. return;
  521. }
  522. }
  523. }
  524. /**
  525. * Finishes reporting
  526. * @returns {void}
  527. * @private
  528. */
  529. function finishReport() {
  530. context.report({
  531. node,
  532. loc: leftParenToken.loc,
  533. messageId: "unexpected",
  534. fix: isFixable(node)
  535. ? fixer => {
  536. const parenthesizedSource =
  537. sourceCode.text.slice(
  538. leftParenToken.range[1],
  539. rightParenToken.range[0],
  540. );
  541. return fixer.replaceTextRange(
  542. [
  543. leftParenToken.range[0],
  544. rightParenToken.range[1],
  545. ],
  546. (requiresLeadingSpace(node) ? " " : "") +
  547. parenthesizedSource +
  548. (requiresTrailingSpace(node)
  549. ? " "
  550. : ""),
  551. );
  552. }
  553. : null,
  554. });
  555. }
  556. if (reportsBuffer) {
  557. reportsBuffer.reports.push({ node, finishReport });
  558. return;
  559. }
  560. finishReport();
  561. }
  562. /**
  563. * Evaluate a argument of the node.
  564. * @param {ASTNode} node node to evaluate
  565. * @returns {void}
  566. * @private
  567. */
  568. function checkArgumentWithPrecedence(node) {
  569. if (
  570. hasExcessParensWithPrecedence(node.argument, precedence(node))
  571. ) {
  572. report(node.argument);
  573. }
  574. }
  575. /**
  576. * Check if a member expression contains a call expression
  577. * @param {ASTNode} node MemberExpression node to evaluate
  578. * @returns {boolean} true if found, false if not
  579. */
  580. function doesMemberExpressionContainCallExpression(node) {
  581. let currentNode = node.object;
  582. let currentNodeType = node.object.type;
  583. while (currentNodeType === "MemberExpression") {
  584. currentNode = currentNode.object;
  585. currentNodeType = currentNode.type;
  586. }
  587. return currentNodeType === "CallExpression";
  588. }
  589. /**
  590. * Evaluate a new call
  591. * @param {ASTNode} node node to evaluate
  592. * @returns {void}
  593. * @private
  594. */
  595. function checkCallNew(node) {
  596. const callee = node.callee;
  597. if (hasExcessParensWithPrecedence(callee, precedence(node))) {
  598. if (
  599. hasDoubleExcessParens(callee) ||
  600. !(
  601. isIIFE(node) ||
  602. // (new A)(); new (new A)();
  603. (callee.type === "NewExpression" &&
  604. !isNewExpressionWithParens(callee) &&
  605. !(
  606. node.type === "NewExpression" &&
  607. !isNewExpressionWithParens(node)
  608. )) ||
  609. // new (a().b)(); new (a.b().c);
  610. (node.type === "NewExpression" &&
  611. callee.type === "MemberExpression" &&
  612. doesMemberExpressionContainCallExpression(
  613. callee,
  614. )) ||
  615. // (a?.b)(); (a?.())();
  616. (!node.optional && callee.type === "ChainExpression")
  617. )
  618. ) {
  619. report(node.callee);
  620. }
  621. }
  622. node.arguments
  623. .filter(arg =>
  624. hasExcessParensWithPrecedence(
  625. arg,
  626. PRECEDENCE_OF_ASSIGNMENT_EXPR,
  627. ),
  628. )
  629. .forEach(report);
  630. }
  631. /**
  632. * Evaluate binary logicals
  633. * @param {ASTNode} node node to evaluate
  634. * @returns {void}
  635. * @private
  636. */
  637. function checkBinaryLogical(node) {
  638. const prec = precedence(node);
  639. const leftPrecedence = precedence(node.left);
  640. const rightPrecedence = precedence(node.right);
  641. const isExponentiation = node.operator === "**";
  642. const shouldSkipLeft =
  643. NESTED_BINARY &&
  644. (node.left.type === "BinaryExpression" ||
  645. node.left.type === "LogicalExpression");
  646. const shouldSkipRight =
  647. NESTED_BINARY &&
  648. (node.right.type === "BinaryExpression" ||
  649. node.right.type === "LogicalExpression");
  650. if (!shouldSkipLeft && hasExcessParens(node.left)) {
  651. if (
  652. (!(
  653. ["AwaitExpression", "UnaryExpression"].includes(
  654. node.left.type,
  655. ) && isExponentiation
  656. ) &&
  657. !astUtils.isMixedLogicalAndCoalesceExpressions(
  658. node.left,
  659. node,
  660. ) &&
  661. (leftPrecedence > prec ||
  662. (leftPrecedence === prec && !isExponentiation))) ||
  663. isParenthesisedTwice(node.left)
  664. ) {
  665. report(node.left);
  666. }
  667. }
  668. if (!shouldSkipRight && hasExcessParens(node.right)) {
  669. if (
  670. (!astUtils.isMixedLogicalAndCoalesceExpressions(
  671. node.right,
  672. node,
  673. ) &&
  674. (rightPrecedence > prec ||
  675. (rightPrecedence === prec && isExponentiation))) ||
  676. isParenthesisedTwice(node.right)
  677. ) {
  678. report(node.right);
  679. }
  680. }
  681. }
  682. /**
  683. * Check the parentheses around the super class of the given class definition.
  684. * @param {ASTNode} node The node of class declarations to check.
  685. * @returns {void}
  686. */
  687. function checkClass(node) {
  688. if (!node.superClass) {
  689. return;
  690. }
  691. /*
  692. * If `node.superClass` is a LeftHandSideExpression, parentheses are extra.
  693. * Otherwise, parentheses are needed.
  694. */
  695. const hasExtraParens =
  696. precedence(node.superClass) > PRECEDENCE_OF_UPDATE_EXPR
  697. ? hasExcessParens(node.superClass)
  698. : hasDoubleExcessParens(node.superClass);
  699. if (hasExtraParens) {
  700. report(node.superClass);
  701. }
  702. }
  703. /**
  704. * Check the parentheses around the argument of the given spread operator.
  705. * @param {ASTNode} node The node of spread elements/properties to check.
  706. * @returns {void}
  707. */
  708. function checkSpreadOperator(node) {
  709. if (
  710. hasExcessParensWithPrecedence(
  711. node.argument,
  712. PRECEDENCE_OF_ASSIGNMENT_EXPR,
  713. )
  714. ) {
  715. report(node.argument);
  716. }
  717. }
  718. /**
  719. * Checks the parentheses for an ExpressionStatement or ExportDefaultDeclaration
  720. * @param {ASTNode} node The ExpressionStatement.expression or ExportDefaultDeclaration.declaration node
  721. * @returns {void}
  722. */
  723. function checkExpressionOrExportStatement(node) {
  724. const firstToken = isParenthesised(node)
  725. ? sourceCode.getTokenBefore(node)
  726. : sourceCode.getFirstToken(node);
  727. const secondToken = sourceCode.getTokenAfter(
  728. firstToken,
  729. astUtils.isNotOpeningParenToken,
  730. );
  731. const thirdToken = secondToken
  732. ? sourceCode.getTokenAfter(secondToken)
  733. : null;
  734. const tokenAfterClosingParens = secondToken
  735. ? sourceCode.getTokenAfter(
  736. secondToken,
  737. astUtils.isNotClosingParenToken,
  738. )
  739. : null;
  740. if (
  741. astUtils.isOpeningParenToken(firstToken) &&
  742. (astUtils.isOpeningBraceToken(secondToken) ||
  743. (secondToken.type === "Keyword" &&
  744. (secondToken.value === "function" ||
  745. secondToken.value === "class" ||
  746. (secondToken.value === "let" &&
  747. tokenAfterClosingParens &&
  748. (astUtils.isOpeningBracketToken(
  749. tokenAfterClosingParens,
  750. ) ||
  751. tokenAfterClosingParens.type ===
  752. "Identifier")))) ||
  753. (secondToken &&
  754. secondToken.type === "Identifier" &&
  755. secondToken.value === "async" &&
  756. thirdToken &&
  757. thirdToken.type === "Keyword" &&
  758. thirdToken.value === "function"))
  759. ) {
  760. tokensToIgnore.add(secondToken);
  761. }
  762. const hasExtraParens =
  763. node.parent.type === "ExportDefaultDeclaration"
  764. ? hasExcessParensWithPrecedence(
  765. node,
  766. PRECEDENCE_OF_ASSIGNMENT_EXPR,
  767. )
  768. : hasExcessParens(node);
  769. if (hasExtraParens) {
  770. report(node);
  771. }
  772. }
  773. /**
  774. * Finds the path from the given node to the specified ancestor.
  775. * @param {ASTNode} node First node in the path.
  776. * @param {ASTNode} ancestor Last node in the path.
  777. * @returns {ASTNode[]} Path, including both nodes.
  778. * @throws {Error} If the given node does not have the specified ancestor.
  779. */
  780. function pathToAncestor(node, ancestor) {
  781. const path = [node];
  782. let currentNode = node;
  783. while (currentNode !== ancestor) {
  784. currentNode = currentNode.parent;
  785. /* c8 ignore start */
  786. if (currentNode === null) {
  787. throw new Error(
  788. "Nodes are not in the ancestor-descendant relationship.",
  789. );
  790. } /* c8 ignore stop */
  791. path.push(currentNode);
  792. }
  793. return path;
  794. }
  795. /**
  796. * Finds the path from the given node to the specified descendant.
  797. * @param {ASTNode} node First node in the path.
  798. * @param {ASTNode} descendant Last node in the path.
  799. * @returns {ASTNode[]} Path, including both nodes.
  800. * @throws {Error} If the given node does not have the specified descendant.
  801. */
  802. function pathToDescendant(node, descendant) {
  803. return pathToAncestor(descendant, node).reverse();
  804. }
  805. /**
  806. * Checks whether the syntax of the given ancestor of an 'in' expression inside a for-loop initializer
  807. * is preventing the 'in' keyword from being interpreted as a part of an ill-formed for-in loop.
  808. * @param {ASTNode} node Ancestor of an 'in' expression.
  809. * @param {ASTNode} child Child of the node, ancestor of the same 'in' expression or the 'in' expression itself.
  810. * @returns {boolean} True if the keyword 'in' would be interpreted as the 'in' operator, without any parenthesis.
  811. */
  812. function isSafelyEnclosingInExpression(node, child) {
  813. switch (node.type) {
  814. case "ArrayExpression":
  815. case "ArrayPattern":
  816. case "BlockStatement":
  817. case "ObjectExpression":
  818. case "ObjectPattern":
  819. case "TemplateLiteral":
  820. return true;
  821. case "ArrowFunctionExpression":
  822. case "FunctionExpression":
  823. return node.params.includes(child);
  824. case "CallExpression":
  825. case "NewExpression":
  826. return node.arguments.includes(child);
  827. case "MemberExpression":
  828. return node.computed && node.property === child;
  829. case "ConditionalExpression":
  830. return node.consequent === child;
  831. default:
  832. return false;
  833. }
  834. }
  835. /**
  836. * Starts a new reports buffering. Warnings will be stored in a buffer instead of being reported immediately.
  837. * An additional logic that requires multiple nodes (e.g. a whole subtree) may dismiss some of the stored warnings.
  838. * @returns {void}
  839. */
  840. function startNewReportsBuffering() {
  841. reportsBuffer = {
  842. upper: reportsBuffer,
  843. inExpressionNodes: [],
  844. reports: [],
  845. };
  846. }
  847. /**
  848. * Ends the current reports buffering.
  849. * @returns {void}
  850. */
  851. function endCurrentReportsBuffering() {
  852. const { upper, inExpressionNodes, reports } = reportsBuffer;
  853. if (upper) {
  854. upper.inExpressionNodes.push(...inExpressionNodes);
  855. upper.reports.push(...reports);
  856. } else {
  857. // flush remaining reports
  858. reports.forEach(({ finishReport }) => finishReport());
  859. }
  860. reportsBuffer = upper;
  861. }
  862. /**
  863. * Checks whether the given node is in the current reports buffer.
  864. * @param {ASTNode} node Node to check.
  865. * @returns {boolean} True if the node is in the current buffer, false otherwise.
  866. */
  867. function isInCurrentReportsBuffer(node) {
  868. return reportsBuffer.reports.some(r => r.node === node);
  869. }
  870. /**
  871. * Removes the given node from the current reports buffer.
  872. * @param {ASTNode} node Node to remove.
  873. * @returns {void}
  874. */
  875. function removeFromCurrentReportsBuffer(node) {
  876. reportsBuffer.reports = reportsBuffer.reports.filter(
  877. r => r.node !== node,
  878. );
  879. }
  880. /**
  881. * Checks whether a node is a MemberExpression at NewExpression's callee.
  882. * @param {ASTNode} node node to check.
  883. * @returns {boolean} True if the node is a MemberExpression at NewExpression's callee. false otherwise.
  884. */
  885. function isMemberExpInNewCallee(node) {
  886. if (node.type === "MemberExpression") {
  887. return node.parent.type === "NewExpression" &&
  888. node.parent.callee === node
  889. ? true
  890. : node.parent.object === node &&
  891. isMemberExpInNewCallee(node.parent);
  892. }
  893. return false;
  894. }
  895. /**
  896. * Checks if the left-hand side of an assignment is an identifier, the operator is one of
  897. * `=`, `&&=`, `||=` or `??=` and the right-hand side is an anonymous class or function.
  898. *
  899. * As per https://tc39.es/ecma262/#sec-assignment-operators-runtime-semantics-evaluation, an
  900. * assignment involving one of the operators `=`, `&&=`, `||=` or `??=` where the right-hand
  901. * side is an anonymous class or function and the left-hand side is an *unparenthesized*
  902. * identifier has different semantics than other assignments.
  903. * Specifically, when an expression like `foo = function () {}` is evaluated, `foo.name`
  904. * will be set to the string "foo", i.e. the identifier name. The same thing does not happen
  905. * when evaluating `(foo) = function () {}`.
  906. * Since the parenthesizing of the identifier in the left-hand side is significant in this
  907. * special case, the parentheses, if present, should not be flagged as unnecessary.
  908. * @param {ASTNode} node an AssignmentExpression node.
  909. * @returns {boolean} `true` if the left-hand side of the assignment is an identifier, the
  910. * operator is one of `=`, `&&=`, `||=` or `??=` and the right-hand side is an anonymous
  911. * class or function; otherwise, `false`.
  912. */
  913. function isAnonymousFunctionAssignmentException({
  914. left,
  915. operator,
  916. right,
  917. }) {
  918. if (
  919. left.type === "Identifier" &&
  920. ["=", "&&=", "||=", "??="].includes(operator)
  921. ) {
  922. const rhsType = right.type;
  923. if (rhsType === "ArrowFunctionExpression") {
  924. return true;
  925. }
  926. if (
  927. (rhsType === "FunctionExpression" ||
  928. rhsType === "ClassExpression") &&
  929. !right.id
  930. ) {
  931. return true;
  932. }
  933. }
  934. return false;
  935. }
  936. return {
  937. ArrayExpression(node) {
  938. node.elements
  939. .filter(
  940. e =>
  941. e &&
  942. hasExcessParensWithPrecedence(
  943. e,
  944. PRECEDENCE_OF_ASSIGNMENT_EXPR,
  945. ),
  946. )
  947. .forEach(report);
  948. },
  949. ArrayPattern(node) {
  950. node.elements
  951. .filter(e => canBeAssignmentTarget(e) && hasExcessParens(e))
  952. .forEach(report);
  953. },
  954. ArrowFunctionExpression(node) {
  955. if (isReturnAssignException(node)) {
  956. return;
  957. }
  958. if (
  959. node.body.type === "ConditionalExpression" &&
  960. IGNORE_ARROW_CONDITIONALS
  961. ) {
  962. return;
  963. }
  964. if (node.body.type !== "BlockStatement") {
  965. const firstBodyToken = sourceCode.getFirstToken(
  966. node.body,
  967. astUtils.isNotOpeningParenToken,
  968. );
  969. const tokenBeforeFirst =
  970. sourceCode.getTokenBefore(firstBodyToken);
  971. if (
  972. astUtils.isOpeningParenToken(tokenBeforeFirst) &&
  973. astUtils.isOpeningBraceToken(firstBodyToken)
  974. ) {
  975. tokensToIgnore.add(firstBodyToken);
  976. }
  977. if (
  978. hasExcessParensWithPrecedence(
  979. node.body,
  980. PRECEDENCE_OF_ASSIGNMENT_EXPR,
  981. )
  982. ) {
  983. report(node.body);
  984. }
  985. }
  986. },
  987. AssignmentExpression(node) {
  988. if (
  989. canBeAssignmentTarget(node.left) &&
  990. hasExcessParens(node.left) &&
  991. (!isAnonymousFunctionAssignmentException(node) ||
  992. isParenthesisedTwice(node.left))
  993. ) {
  994. report(node.left);
  995. }
  996. if (
  997. !isReturnAssignException(node) &&
  998. hasExcessParensWithPrecedence(node.right, precedence(node))
  999. ) {
  1000. report(node.right);
  1001. }
  1002. },
  1003. BinaryExpression(node) {
  1004. if (reportsBuffer && node.operator === "in") {
  1005. reportsBuffer.inExpressionNodes.push(node);
  1006. }
  1007. checkBinaryLogical(node);
  1008. },
  1009. CallExpression: checkCallNew,
  1010. ConditionalExpression(node) {
  1011. if (isReturnAssignException(node)) {
  1012. return;
  1013. }
  1014. const availableTypes = new Set([
  1015. "BinaryExpression",
  1016. "LogicalExpression",
  1017. ]);
  1018. if (
  1019. !(
  1020. EXCEPT_COND_TERNARY &&
  1021. availableTypes.has(node.test.type)
  1022. ) &&
  1023. !isCondAssignException(node) &&
  1024. hasExcessParensWithPrecedence(
  1025. node.test,
  1026. precedence({
  1027. type: "LogicalExpression",
  1028. operator: "||",
  1029. }),
  1030. )
  1031. ) {
  1032. report(node.test);
  1033. }
  1034. if (
  1035. !(
  1036. EXCEPT_COND_TERNARY &&
  1037. availableTypes.has(node.consequent.type)
  1038. ) &&
  1039. hasExcessParensWithPrecedence(
  1040. node.consequent,
  1041. PRECEDENCE_OF_ASSIGNMENT_EXPR,
  1042. )
  1043. ) {
  1044. report(node.consequent);
  1045. }
  1046. if (
  1047. !(
  1048. EXCEPT_COND_TERNARY &&
  1049. availableTypes.has(node.alternate.type)
  1050. ) &&
  1051. hasExcessParensWithPrecedence(
  1052. node.alternate,
  1053. PRECEDENCE_OF_ASSIGNMENT_EXPR,
  1054. )
  1055. ) {
  1056. report(node.alternate);
  1057. }
  1058. },
  1059. DoWhileStatement(node) {
  1060. if (
  1061. hasExcessParens(node.test) &&
  1062. !isCondAssignException(node)
  1063. ) {
  1064. report(node.test);
  1065. }
  1066. },
  1067. ExportDefaultDeclaration: node =>
  1068. checkExpressionOrExportStatement(node.declaration),
  1069. ExpressionStatement: node =>
  1070. checkExpressionOrExportStatement(node.expression),
  1071. ForInStatement(node) {
  1072. if (node.left.type !== "VariableDeclaration") {
  1073. const firstLeftToken = sourceCode.getFirstToken(
  1074. node.left,
  1075. astUtils.isNotOpeningParenToken,
  1076. );
  1077. if (
  1078. firstLeftToken.value === "let" &&
  1079. astUtils.isOpeningBracketToken(
  1080. sourceCode.getTokenAfter(
  1081. firstLeftToken,
  1082. astUtils.isNotClosingParenToken,
  1083. ),
  1084. )
  1085. ) {
  1086. // ForInStatement#left expression cannot start with `let[`.
  1087. tokensToIgnore.add(firstLeftToken);
  1088. }
  1089. }
  1090. if (hasExcessParens(node.left)) {
  1091. report(node.left);
  1092. }
  1093. if (hasExcessParens(node.right)) {
  1094. report(node.right);
  1095. }
  1096. },
  1097. ForOfStatement(node) {
  1098. if (node.left.type !== "VariableDeclaration") {
  1099. const firstLeftToken = sourceCode.getFirstToken(
  1100. node.left,
  1101. astUtils.isNotOpeningParenToken,
  1102. );
  1103. if (firstLeftToken.value === "let") {
  1104. // ForOfStatement#left expression cannot start with `let`.
  1105. tokensToIgnore.add(firstLeftToken);
  1106. }
  1107. }
  1108. if (hasExcessParens(node.left)) {
  1109. report(node.left);
  1110. }
  1111. if (
  1112. hasExcessParensWithPrecedence(
  1113. node.right,
  1114. PRECEDENCE_OF_ASSIGNMENT_EXPR,
  1115. )
  1116. ) {
  1117. report(node.right);
  1118. }
  1119. },
  1120. ForStatement(node) {
  1121. if (
  1122. node.test &&
  1123. hasExcessParens(node.test) &&
  1124. !isCondAssignException(node)
  1125. ) {
  1126. report(node.test);
  1127. }
  1128. if (node.update && hasExcessParens(node.update)) {
  1129. report(node.update);
  1130. }
  1131. if (node.init) {
  1132. if (node.init.type !== "VariableDeclaration") {
  1133. const firstToken = sourceCode.getFirstToken(
  1134. node.init,
  1135. astUtils.isNotOpeningParenToken,
  1136. );
  1137. if (
  1138. firstToken.value === "let" &&
  1139. astUtils.isOpeningBracketToken(
  1140. sourceCode.getTokenAfter(
  1141. firstToken,
  1142. astUtils.isNotClosingParenToken,
  1143. ),
  1144. )
  1145. ) {
  1146. // ForStatement#init expression cannot start with `let[`.
  1147. tokensToIgnore.add(firstToken);
  1148. }
  1149. }
  1150. startNewReportsBuffering();
  1151. if (hasExcessParens(node.init)) {
  1152. report(node.init);
  1153. }
  1154. }
  1155. },
  1156. "ForStatement > *.init:exit"(node) {
  1157. /*
  1158. * Removing parentheses around `in` expressions might change semantics and cause errors.
  1159. *
  1160. * For example, this valid for loop:
  1161. * for (let a = (b in c); ;);
  1162. * after removing parentheses would be treated as an invalid for-in loop:
  1163. * for (let a = b in c; ;);
  1164. */
  1165. if (reportsBuffer.reports.length) {
  1166. reportsBuffer.inExpressionNodes.forEach(
  1167. inExpressionNode => {
  1168. const path = pathToDescendant(
  1169. node,
  1170. inExpressionNode,
  1171. );
  1172. let nodeToExclude;
  1173. for (let i = 0; i < path.length; i++) {
  1174. const pathNode = path[i];
  1175. if (i < path.length - 1) {
  1176. const nextPathNode = path[i + 1];
  1177. if (
  1178. isSafelyEnclosingInExpression(
  1179. pathNode,
  1180. nextPathNode,
  1181. )
  1182. ) {
  1183. // The 'in' expression in safely enclosed by the syntax of its ancestor nodes (e.g. by '{}' or '[]').
  1184. return;
  1185. }
  1186. }
  1187. if (isParenthesised(pathNode)) {
  1188. if (isInCurrentReportsBuffer(pathNode)) {
  1189. // This node was supposed to be reported, but parentheses might be necessary.
  1190. if (isParenthesisedTwice(pathNode)) {
  1191. /*
  1192. * This node is parenthesised twice, it certainly has at least one pair of `extra` parentheses.
  1193. * If the --fix option is on, the current fixing iteration will remove only one pair of parentheses.
  1194. * The remaining pair is safely enclosing the 'in' expression.
  1195. */
  1196. return;
  1197. }
  1198. // Exclude the outermost node only.
  1199. if (!nodeToExclude) {
  1200. nodeToExclude = pathNode;
  1201. }
  1202. // Don't break the loop here, there might be some safe nodes or parentheses that will stay inside.
  1203. } else {
  1204. // This node will stay parenthesised, the 'in' expression in safely enclosed by '()'.
  1205. return;
  1206. }
  1207. }
  1208. }
  1209. // Exclude the node from the list (i.e. treat parentheses as necessary)
  1210. removeFromCurrentReportsBuffer(nodeToExclude);
  1211. },
  1212. );
  1213. }
  1214. endCurrentReportsBuffering();
  1215. },
  1216. IfStatement(node) {
  1217. if (
  1218. hasExcessParens(node.test) &&
  1219. !isCondAssignException(node)
  1220. ) {
  1221. report(node.test);
  1222. }
  1223. },
  1224. ImportExpression(node) {
  1225. const { source } = node;
  1226. if (source.type === "SequenceExpression") {
  1227. if (hasDoubleExcessParens(source)) {
  1228. report(source);
  1229. }
  1230. } else if (hasExcessParens(source)) {
  1231. report(source);
  1232. }
  1233. },
  1234. LogicalExpression: checkBinaryLogical,
  1235. MemberExpression(node) {
  1236. const shouldAllowWrapOnce =
  1237. isMemberExpInNewCallee(node) &&
  1238. doesMemberExpressionContainCallExpression(node);
  1239. const nodeObjHasExcessParens = shouldAllowWrapOnce
  1240. ? hasDoubleExcessParens(node.object)
  1241. : hasExcessParens(node.object) &&
  1242. !(
  1243. isImmediateFunctionPrototypeMethodCall(
  1244. node.parent,
  1245. ) &&
  1246. node.parent.callee === node &&
  1247. IGNORE_FUNCTION_PROTOTYPE_METHODS
  1248. );
  1249. if (
  1250. nodeObjHasExcessParens &&
  1251. precedence(node.object) >= precedence(node) &&
  1252. (node.computed ||
  1253. !(
  1254. astUtils.isDecimalInteger(node.object) ||
  1255. // RegExp literal is allowed to have parens (#1589)
  1256. (node.object.type === "Literal" &&
  1257. node.object.regex)
  1258. ))
  1259. ) {
  1260. report(node.object);
  1261. }
  1262. if (
  1263. nodeObjHasExcessParens &&
  1264. node.object.type === "CallExpression"
  1265. ) {
  1266. report(node.object);
  1267. }
  1268. if (
  1269. nodeObjHasExcessParens &&
  1270. !IGNORE_NEW_IN_MEMBER_EXPR &&
  1271. node.object.type === "NewExpression" &&
  1272. isNewExpressionWithParens(node.object)
  1273. ) {
  1274. report(node.object);
  1275. }
  1276. if (
  1277. nodeObjHasExcessParens &&
  1278. node.optional &&
  1279. node.object.type === "ChainExpression"
  1280. ) {
  1281. report(node.object);
  1282. }
  1283. if (node.computed && hasExcessParens(node.property)) {
  1284. report(node.property);
  1285. }
  1286. },
  1287. "MethodDefinition[computed=true]"(node) {
  1288. if (
  1289. hasExcessParensWithPrecedence(
  1290. node.key,
  1291. PRECEDENCE_OF_ASSIGNMENT_EXPR,
  1292. )
  1293. ) {
  1294. report(node.key);
  1295. }
  1296. },
  1297. NewExpression: checkCallNew,
  1298. ObjectExpression(node) {
  1299. node.properties
  1300. .filter(
  1301. property =>
  1302. property.value &&
  1303. hasExcessParensWithPrecedence(
  1304. property.value,
  1305. PRECEDENCE_OF_ASSIGNMENT_EXPR,
  1306. ),
  1307. )
  1308. .forEach(property => report(property.value));
  1309. },
  1310. ObjectPattern(node) {
  1311. node.properties
  1312. .filter(property => {
  1313. const value = property.value;
  1314. return (
  1315. canBeAssignmentTarget(value) &&
  1316. hasExcessParens(value)
  1317. );
  1318. })
  1319. .forEach(property => report(property.value));
  1320. },
  1321. Property(node) {
  1322. if (node.computed) {
  1323. const { key } = node;
  1324. if (
  1325. key &&
  1326. hasExcessParensWithPrecedence(
  1327. key,
  1328. PRECEDENCE_OF_ASSIGNMENT_EXPR,
  1329. )
  1330. ) {
  1331. report(key);
  1332. }
  1333. }
  1334. },
  1335. PropertyDefinition(node) {
  1336. if (
  1337. node.computed &&
  1338. hasExcessParensWithPrecedence(
  1339. node.key,
  1340. PRECEDENCE_OF_ASSIGNMENT_EXPR,
  1341. )
  1342. ) {
  1343. report(node.key);
  1344. }
  1345. if (
  1346. node.value &&
  1347. hasExcessParensWithPrecedence(
  1348. node.value,
  1349. PRECEDENCE_OF_ASSIGNMENT_EXPR,
  1350. )
  1351. ) {
  1352. report(node.value);
  1353. }
  1354. },
  1355. RestElement(node) {
  1356. const argument = node.argument;
  1357. if (
  1358. canBeAssignmentTarget(argument) &&
  1359. hasExcessParens(argument)
  1360. ) {
  1361. report(argument);
  1362. }
  1363. },
  1364. ReturnStatement(node) {
  1365. const returnToken = sourceCode.getFirstToken(node);
  1366. if (isReturnAssignException(node)) {
  1367. return;
  1368. }
  1369. if (
  1370. node.argument &&
  1371. hasExcessParensNoLineTerminator(
  1372. returnToken,
  1373. node.argument,
  1374. ) &&
  1375. // RegExp literal is allowed to have parens (#1589)
  1376. !(node.argument.type === "Literal" && node.argument.regex)
  1377. ) {
  1378. report(node.argument);
  1379. }
  1380. },
  1381. SequenceExpression(node) {
  1382. const precedenceOfNode = precedence(node);
  1383. node.expressions
  1384. .filter(e =>
  1385. hasExcessParensWithPrecedence(e, precedenceOfNode),
  1386. )
  1387. .forEach(report);
  1388. },
  1389. SwitchCase(node) {
  1390. if (node.test && hasExcessParens(node.test)) {
  1391. report(node.test);
  1392. }
  1393. },
  1394. SwitchStatement(node) {
  1395. if (hasExcessParens(node.discriminant)) {
  1396. report(node.discriminant);
  1397. }
  1398. },
  1399. ThrowStatement(node) {
  1400. const throwToken = sourceCode.getFirstToken(node);
  1401. if (
  1402. hasExcessParensNoLineTerminator(throwToken, node.argument)
  1403. ) {
  1404. report(node.argument);
  1405. }
  1406. },
  1407. UnaryExpression: checkArgumentWithPrecedence,
  1408. UpdateExpression(node) {
  1409. if (node.prefix) {
  1410. checkArgumentWithPrecedence(node);
  1411. } else {
  1412. const { argument } = node;
  1413. const operatorToken = sourceCode.getLastToken(node);
  1414. if (
  1415. argument.loc.end.line === operatorToken.loc.start.line
  1416. ) {
  1417. checkArgumentWithPrecedence(node);
  1418. } else {
  1419. if (hasDoubleExcessParens(argument)) {
  1420. report(argument);
  1421. }
  1422. }
  1423. }
  1424. },
  1425. AwaitExpression: checkArgumentWithPrecedence,
  1426. VariableDeclarator(node) {
  1427. if (
  1428. node.init &&
  1429. hasExcessParensWithPrecedence(
  1430. node.init,
  1431. PRECEDENCE_OF_ASSIGNMENT_EXPR,
  1432. ) &&
  1433. // RegExp literal is allowed to have parens (#1589)
  1434. !(node.init.type === "Literal" && node.init.regex)
  1435. ) {
  1436. report(node.init);
  1437. }
  1438. },
  1439. WhileStatement(node) {
  1440. if (
  1441. hasExcessParens(node.test) &&
  1442. !isCondAssignException(node)
  1443. ) {
  1444. report(node.test);
  1445. }
  1446. },
  1447. WithStatement(node) {
  1448. if (hasExcessParens(node.object)) {
  1449. report(node.object);
  1450. }
  1451. },
  1452. YieldExpression(node) {
  1453. if (node.argument) {
  1454. const yieldToken = sourceCode.getFirstToken(node);
  1455. if (
  1456. (precedence(node.argument) >= precedence(node) &&
  1457. hasExcessParensNoLineTerminator(
  1458. yieldToken,
  1459. node.argument,
  1460. )) ||
  1461. hasDoubleExcessParens(node.argument)
  1462. ) {
  1463. report(node.argument);
  1464. }
  1465. }
  1466. },
  1467. ClassDeclaration: checkClass,
  1468. ClassExpression: checkClass,
  1469. SpreadElement: checkSpreadOperator,
  1470. SpreadProperty: checkSpreadOperator,
  1471. ExperimentalSpreadProperty: checkSpreadOperator,
  1472. TemplateLiteral(node) {
  1473. node.expressions
  1474. .filter(e => e && hasExcessParens(e))
  1475. .forEach(report);
  1476. },
  1477. AssignmentPattern(node) {
  1478. const { left, right } = node;
  1479. if (canBeAssignmentTarget(left) && hasExcessParens(left)) {
  1480. report(left);
  1481. }
  1482. if (
  1483. right &&
  1484. hasExcessParensWithPrecedence(
  1485. right,
  1486. PRECEDENCE_OF_ASSIGNMENT_EXPR,
  1487. )
  1488. ) {
  1489. report(right);
  1490. }
  1491. },
  1492. };
  1493. },
  1494. };