source: imaps-frontend/node_modules/eslint/lib/rules/radix.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: 6.8 KB
Line 
1/**
2 * @fileoverview Rule to flag use of parseInt without a radix argument
3 * @author James Allardice
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Requirements
10//------------------------------------------------------------------------------
11
12const astUtils = require("./utils/ast-utils");
13
14//------------------------------------------------------------------------------
15// Helpers
16//------------------------------------------------------------------------------
17
18const MODE_ALWAYS = "always",
19 MODE_AS_NEEDED = "as-needed";
20
21const validRadixValues = new Set(Array.from({ length: 37 - 2 }, (_, index) => index + 2));
22
23/**
24 * Checks whether a given variable is shadowed or not.
25 * @param {eslint-scope.Variable} variable A variable to check.
26 * @returns {boolean} `true` if the variable is shadowed.
27 */
28function isShadowed(variable) {
29 return variable.defs.length >= 1;
30}
31
32/**
33 * Checks whether a given node is a MemberExpression of `parseInt` method or not.
34 * @param {ASTNode} node A node to check.
35 * @returns {boolean} `true` if the node is a MemberExpression of `parseInt`
36 * method.
37 */
38function isParseIntMethod(node) {
39 return (
40 node.type === "MemberExpression" &&
41 !node.computed &&
42 node.property.type === "Identifier" &&
43 node.property.name === "parseInt"
44 );
45}
46
47/**
48 * Checks whether a given node is a valid value of radix or not.
49 *
50 * The following values are invalid.
51 *
52 * - A literal except integers between 2 and 36.
53 * - undefined.
54 * @param {ASTNode} radix A node of radix to check.
55 * @returns {boolean} `true` if the node is valid.
56 */
57function isValidRadix(radix) {
58 return !(
59 (radix.type === "Literal" && !validRadixValues.has(radix.value)) ||
60 (radix.type === "Identifier" && radix.name === "undefined")
61 );
62}
63
64/**
65 * Checks whether a given node is a default value of radix or not.
66 * @param {ASTNode} radix A node of radix to check.
67 * @returns {boolean} `true` if the node is the literal node of `10`.
68 */
69function isDefaultRadix(radix) {
70 return radix.type === "Literal" && radix.value === 10;
71}
72
73//------------------------------------------------------------------------------
74// Rule Definition
75//------------------------------------------------------------------------------
76
77/** @type {import('../shared/types').Rule} */
78module.exports = {
79 meta: {
80 type: "suggestion",
81
82 docs: {
83 description: "Enforce the consistent use of the radix argument when using `parseInt()`",
84 recommended: false,
85 url: "https://eslint.org/docs/latest/rules/radix"
86 },
87
88 hasSuggestions: true,
89
90 schema: [
91 {
92 enum: ["always", "as-needed"]
93 }
94 ],
95
96 messages: {
97 missingParameters: "Missing parameters.",
98 redundantRadix: "Redundant radix parameter.",
99 missingRadix: "Missing radix parameter.",
100 invalidRadix: "Invalid radix parameter, must be an integer between 2 and 36.",
101 addRadixParameter10: "Add radix parameter `10` for parsing decimal numbers."
102 }
103 },
104
105 create(context) {
106 const mode = context.options[0] || MODE_ALWAYS;
107 const sourceCode = context.sourceCode;
108
109 /**
110 * Checks the arguments of a given CallExpression node and reports it if it
111 * offends this rule.
112 * @param {ASTNode} node A CallExpression node to check.
113 * @returns {void}
114 */
115 function checkArguments(node) {
116 const args = node.arguments;
117
118 switch (args.length) {
119 case 0:
120 context.report({
121 node,
122 messageId: "missingParameters"
123 });
124 break;
125
126 case 1:
127 if (mode === MODE_ALWAYS) {
128 context.report({
129 node,
130 messageId: "missingRadix",
131 suggest: [
132 {
133 messageId: "addRadixParameter10",
134 fix(fixer) {
135 const tokens = sourceCode.getTokens(node);
136 const lastToken = tokens[tokens.length - 1]; // Parenthesis.
137 const secondToLastToken = tokens[tokens.length - 2]; // May or may not be a comma.
138 const hasTrailingComma = secondToLastToken.type === "Punctuator" && secondToLastToken.value === ",";
139
140 return fixer.insertTextBefore(lastToken, hasTrailingComma ? " 10," : ", 10");
141 }
142 }
143 ]
144 });
145 }
146 break;
147
148 default:
149 if (mode === MODE_AS_NEEDED && isDefaultRadix(args[1])) {
150 context.report({
151 node,
152 messageId: "redundantRadix"
153 });
154 } else if (!isValidRadix(args[1])) {
155 context.report({
156 node,
157 messageId: "invalidRadix"
158 });
159 }
160 break;
161 }
162 }
163
164 return {
165 "Program:exit"(node) {
166 const scope = sourceCode.getScope(node);
167 let variable;
168
169 // Check `parseInt()`
170 variable = astUtils.getVariableByName(scope, "parseInt");
171 if (variable && !isShadowed(variable)) {
172 variable.references.forEach(reference => {
173 const idNode = reference.identifier;
174
175 if (astUtils.isCallee(idNode)) {
176 checkArguments(idNode.parent);
177 }
178 });
179 }
180
181 // Check `Number.parseInt()`
182 variable = astUtils.getVariableByName(scope, "Number");
183 if (variable && !isShadowed(variable)) {
184 variable.references.forEach(reference => {
185 const parentNode = reference.identifier.parent;
186 const maybeCallee = parentNode.parent.type === "ChainExpression"
187 ? parentNode.parent
188 : parentNode;
189
190 if (isParseIntMethod(parentNode) && astUtils.isCallee(maybeCallee)) {
191 checkArguments(maybeCallee.parent);
192 }
193 });
194 }
195 }
196 };
197 }
198};
Note: See TracBrowser for help on using the repository browser.