source: imaps-frontend/node_modules/eslint/lib/rules/no-loss-of-precision.js@ d565449

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

Update repo after prototype presentation

  • Property mode set to 100644
File size: 8.1 KB
Line 
1/**
2 * @fileoverview Rule to flag numbers that will lose significant figure precision at runtime
3 * @author Jacob Moore
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Rule Definition
10//------------------------------------------------------------------------------
11
12/** @type {import('../shared/types').Rule} */
13module.exports = {
14 meta: {
15 type: "problem",
16
17 docs: {
18 description: "Disallow literal numbers that lose precision",
19 recommended: true,
20 url: "https://eslint.org/docs/latest/rules/no-loss-of-precision"
21 },
22 schema: [],
23 messages: {
24 noLossOfPrecision: "This number literal will lose precision at runtime."
25 }
26 },
27
28 create(context) {
29
30 /**
31 * Returns whether the node is number literal
32 * @param {Node} node the node literal being evaluated
33 * @returns {boolean} true if the node is a number literal
34 */
35 function isNumber(node) {
36 return typeof node.value === "number";
37 }
38
39 /**
40 * Gets the source code of the given number literal. Removes `_` numeric separators from the result.
41 * @param {Node} node the number `Literal` node
42 * @returns {string} raw source code of the literal, without numeric separators
43 */
44 function getRaw(node) {
45 return node.raw.replace(/_/gu, "");
46 }
47
48 /**
49 * Checks whether the number is base ten
50 * @param {ASTNode} node the node being evaluated
51 * @returns {boolean} true if the node is in base ten
52 */
53 function isBaseTen(node) {
54 const prefixes = ["0x", "0X", "0b", "0B", "0o", "0O"];
55
56 return prefixes.every(prefix => !node.raw.startsWith(prefix)) &&
57 !/^0[0-7]+$/u.test(node.raw);
58 }
59
60 /**
61 * Checks that the user-intended non-base ten number equals the actual number after is has been converted to the Number type
62 * @param {Node} node the node being evaluated
63 * @returns {boolean} true if they do not match
64 */
65 function notBaseTenLosesPrecision(node) {
66 const rawString = getRaw(node).toUpperCase();
67 let base = 0;
68
69 if (rawString.startsWith("0B")) {
70 base = 2;
71 } else if (rawString.startsWith("0X")) {
72 base = 16;
73 } else {
74 base = 8;
75 }
76
77 return !rawString.endsWith(node.value.toString(base).toUpperCase());
78 }
79
80 /**
81 * Adds a decimal point to the numeric string at index 1
82 * @param {string} stringNumber the numeric string without any decimal point
83 * @returns {string} the numeric string with a decimal point in the proper place
84 */
85 function addDecimalPointToNumber(stringNumber) {
86 return `${stringNumber[0]}.${stringNumber.slice(1)}`;
87 }
88
89 /**
90 * Returns the number stripped of leading zeros
91 * @param {string} numberAsString the string representation of the number
92 * @returns {string} the stripped string
93 */
94 function removeLeadingZeros(numberAsString) {
95 for (let i = 0; i < numberAsString.length; i++) {
96 if (numberAsString[i] !== "0") {
97 return numberAsString.slice(i);
98 }
99 }
100 return numberAsString;
101 }
102
103 /**
104 * Returns the number stripped of trailing zeros
105 * @param {string} numberAsString the string representation of the number
106 * @returns {string} the stripped string
107 */
108 function removeTrailingZeros(numberAsString) {
109 for (let i = numberAsString.length - 1; i >= 0; i--) {
110 if (numberAsString[i] !== "0") {
111 return numberAsString.slice(0, i + 1);
112 }
113 }
114 return numberAsString;
115 }
116
117 /**
118 * Converts an integer to an object containing the integer's coefficient and order of magnitude
119 * @param {string} stringInteger the string representation of the integer being converted
120 * @returns {Object} the object containing the integer's coefficient and order of magnitude
121 */
122 function normalizeInteger(stringInteger) {
123 const significantDigits = removeTrailingZeros(removeLeadingZeros(stringInteger));
124
125 return {
126 magnitude: stringInteger.startsWith("0") ? stringInteger.length - 2 : stringInteger.length - 1,
127 coefficient: addDecimalPointToNumber(significantDigits)
128 };
129 }
130
131 /**
132 *
133 * Converts a float to an object containing the floats's coefficient and order of magnitude
134 * @param {string} stringFloat the string representation of the float being converted
135 * @returns {Object} the object containing the integer's coefficient and order of magnitude
136 */
137 function normalizeFloat(stringFloat) {
138 const trimmedFloat = removeLeadingZeros(stringFloat);
139
140 if (trimmedFloat.startsWith(".")) {
141 const decimalDigits = trimmedFloat.slice(1);
142 const significantDigits = removeLeadingZeros(decimalDigits);
143
144 return {
145 magnitude: significantDigits.length - decimalDigits.length - 1,
146 coefficient: addDecimalPointToNumber(significantDigits)
147 };
148
149 }
150 return {
151 magnitude: trimmedFloat.indexOf(".") - 1,
152 coefficient: addDecimalPointToNumber(trimmedFloat.replace(".", ""))
153
154 };
155 }
156
157 /**
158 * Converts a base ten number to proper scientific notation
159 * @param {string} stringNumber the string representation of the base ten number to be converted
160 * @returns {string} the number converted to scientific notation
161 */
162 function convertNumberToScientificNotation(stringNumber) {
163 const splitNumber = stringNumber.replace("E", "e").split("e");
164 const originalCoefficient = splitNumber[0];
165 const normalizedNumber = stringNumber.includes(".") ? normalizeFloat(originalCoefficient)
166 : normalizeInteger(originalCoefficient);
167 const normalizedCoefficient = normalizedNumber.coefficient;
168 const magnitude = splitNumber.length > 1 ? (parseInt(splitNumber[1], 10) + normalizedNumber.magnitude)
169 : normalizedNumber.magnitude;
170
171 return `${normalizedCoefficient}e${magnitude}`;
172 }
173
174 /**
175 * Checks that the user-intended base ten number equals the actual number after is has been converted to the Number type
176 * @param {Node} node the node being evaluated
177 * @returns {boolean} true if they do not match
178 */
179 function baseTenLosesPrecision(node) {
180 const normalizedRawNumber = convertNumberToScientificNotation(getRaw(node));
181 const requestedPrecision = normalizedRawNumber.split("e")[0].replace(".", "").length;
182
183 if (requestedPrecision > 100) {
184 return true;
185 }
186 const storedNumber = node.value.toPrecision(requestedPrecision);
187 const normalizedStoredNumber = convertNumberToScientificNotation(storedNumber);
188
189 return normalizedRawNumber !== normalizedStoredNumber;
190 }
191
192
193 /**
194 * Checks that the user-intended number equals the actual number after is has been converted to the Number type
195 * @param {Node} node the node being evaluated
196 * @returns {boolean} true if they do not match
197 */
198 function losesPrecision(node) {
199 return isBaseTen(node) ? baseTenLosesPrecision(node) : notBaseTenLosesPrecision(node);
200 }
201
202
203 return {
204 Literal(node) {
205 if (node.value && isNumber(node) && losesPrecision(node)) {
206 context.report({
207 messageId: "noLossOfPrecision",
208 node
209 });
210 }
211 }
212 };
213 }
214};
Note: See TracBrowser for help on using the repository browser.