source: imaps-frontend/node_modules/eslint/lib/rules/prefer-exponentiation-operator.js

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

Update repo after prototype presentation

  • Property mode set to 100644
File size: 7.6 KB
Line 
1/**
2 * @fileoverview Rule to disallow Math.pow in favor of the ** operator
3 * @author Milos Djermanovic
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Requirements
10//------------------------------------------------------------------------------
11
12const astUtils = require("./utils/ast-utils");
13const { CALL, ReferenceTracker } = require("@eslint-community/eslint-utils");
14
15//------------------------------------------------------------------------------
16// Helpers
17//------------------------------------------------------------------------------
18
19const PRECEDENCE_OF_EXPONENTIATION_EXPR = astUtils.getPrecedence({ type: "BinaryExpression", operator: "**" });
20
21/**
22 * Determines whether the given node needs parens if used as the base in an exponentiation binary expression.
23 * @param {ASTNode} base The node to check.
24 * @returns {boolean} `true` if the node needs to be parenthesised.
25 */
26function doesBaseNeedParens(base) {
27 return (
28
29 // '**' is right-associative, parens are needed when Math.pow(a ** b, c) is converted to (a ** b) ** c
30 astUtils.getPrecedence(base) <= PRECEDENCE_OF_EXPONENTIATION_EXPR ||
31
32 // An unary operator cannot be used immediately before an exponentiation expression
33 base.type === "AwaitExpression" ||
34 base.type === "UnaryExpression"
35 );
36}
37
38/**
39 * Determines whether the given node needs parens if used as the exponent in an exponentiation binary expression.
40 * @param {ASTNode} exponent The node to check.
41 * @returns {boolean} `true` if the node needs to be parenthesised.
42 */
43function doesExponentNeedParens(exponent) {
44
45 // '**' is right-associative, there is no need for parens when Math.pow(a, b ** c) is converted to a ** b ** c
46 return astUtils.getPrecedence(exponent) < PRECEDENCE_OF_EXPONENTIATION_EXPR;
47}
48
49/**
50 * Determines whether an exponentiation binary expression at the place of the given node would need parens.
51 * @param {ASTNode} node A node that would be replaced by an exponentiation binary expression.
52 * @param {SourceCode} sourceCode A SourceCode object.
53 * @returns {boolean} `true` if the expression needs to be parenthesised.
54 */
55function doesExponentiationExpressionNeedParens(node, sourceCode) {
56 const parent = node.parent.type === "ChainExpression" ? node.parent.parent : node.parent;
57
58 const parentPrecedence = astUtils.getPrecedence(parent);
59 const needsParens = (
60 parent.type === "ClassDeclaration" ||
61 (
62 parent.type.endsWith("Expression") &&
63 (parentPrecedence === -1 || parentPrecedence >= PRECEDENCE_OF_EXPONENTIATION_EXPR) &&
64 !(parent.type === "BinaryExpression" && parent.operator === "**" && parent.right === node) &&
65 !((parent.type === "CallExpression" || parent.type === "NewExpression") && parent.arguments.includes(node)) &&
66 !(parent.type === "MemberExpression" && parent.computed && parent.property === node) &&
67 !(parent.type === "ArrayExpression")
68 )
69 );
70
71 return needsParens && !astUtils.isParenthesised(sourceCode, node);
72}
73
74/**
75 * Optionally parenthesizes given text.
76 * @param {string} text The text to parenthesize.
77 * @param {boolean} shouldParenthesize If `true`, the text will be parenthesised.
78 * @returns {string} parenthesised or unchanged text.
79 */
80function parenthesizeIfShould(text, shouldParenthesize) {
81 return shouldParenthesize ? `(${text})` : text;
82}
83
84//------------------------------------------------------------------------------
85// Rule Definition
86//------------------------------------------------------------------------------
87
88/** @type {import('../shared/types').Rule} */
89module.exports = {
90 meta: {
91 type: "suggestion",
92
93 docs: {
94 description: "Disallow the use of `Math.pow` in favor of the `**` operator",
95 recommended: false,
96 url: "https://eslint.org/docs/latest/rules/prefer-exponentiation-operator"
97 },
98
99 schema: [],
100 fixable: "code",
101
102 messages: {
103 useExponentiation: "Use the '**' operator instead of 'Math.pow'."
104 }
105 },
106
107 create(context) {
108 const sourceCode = context.sourceCode;
109
110 /**
111 * Reports the given node.
112 * @param {ASTNode} node 'Math.pow()' node to report.
113 * @returns {void}
114 */
115 function report(node) {
116 context.report({
117 node,
118 messageId: "useExponentiation",
119 fix(fixer) {
120 if (
121 node.arguments.length !== 2 ||
122 node.arguments.some(arg => arg.type === "SpreadElement") ||
123 sourceCode.getCommentsInside(node).length > 0
124 ) {
125 return null;
126 }
127
128 const base = node.arguments[0],
129 exponent = node.arguments[1],
130 baseText = sourceCode.getText(base),
131 exponentText = sourceCode.getText(exponent),
132 shouldParenthesizeBase = doesBaseNeedParens(base),
133 shouldParenthesizeExponent = doesExponentNeedParens(exponent),
134 shouldParenthesizeAll = doesExponentiationExpressionNeedParens(node, sourceCode);
135
136 let prefix = "",
137 suffix = "";
138
139 if (!shouldParenthesizeAll) {
140 if (!shouldParenthesizeBase) {
141 const firstReplacementToken = sourceCode.getFirstToken(base),
142 tokenBefore = sourceCode.getTokenBefore(node);
143
144 if (
145 tokenBefore &&
146 tokenBefore.range[1] === node.range[0] &&
147 !astUtils.canTokensBeAdjacent(tokenBefore, firstReplacementToken)
148 ) {
149 prefix = " "; // a+Math.pow(++b, c) -> a+ ++b**c
150 }
151 }
152 if (!shouldParenthesizeExponent) {
153 const lastReplacementToken = sourceCode.getLastToken(exponent),
154 tokenAfter = sourceCode.getTokenAfter(node);
155
156 if (
157 tokenAfter &&
158 node.range[1] === tokenAfter.range[0] &&
159 !astUtils.canTokensBeAdjacent(lastReplacementToken, tokenAfter)
160 ) {
161 suffix = " "; // Math.pow(a, b)in c -> a**b in c
162 }
163 }
164 }
165
166 const baseReplacement = parenthesizeIfShould(baseText, shouldParenthesizeBase),
167 exponentReplacement = parenthesizeIfShould(exponentText, shouldParenthesizeExponent),
168 replacement = parenthesizeIfShould(`${baseReplacement}**${exponentReplacement}`, shouldParenthesizeAll);
169
170 return fixer.replaceText(node, `${prefix}${replacement}${suffix}`);
171 }
172 });
173 }
174
175 return {
176 Program(node) {
177 const scope = sourceCode.getScope(node);
178 const tracker = new ReferenceTracker(scope);
179 const trackMap = {
180 Math: {
181 pow: { [CALL]: true }
182 }
183 };
184
185 for (const { node: refNode } of tracker.iterateGlobalReferences(trackMap)) {
186 report(refNode);
187 }
188 }
189 };
190 }
191};
Note: See TracBrowser for help on using the repository browser.