Actualizacion maquina principal
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / no-import-assign.js
1 /**
2  * @fileoverview Rule to flag updates of imported bindings.
3  * @author Toru Nagashima <https://github.com/mysticatea>
4  */
5
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Helpers
10 //------------------------------------------------------------------------------
11
12 const { findVariable, getPropertyName } = require("eslint-utils");
13
14 const MutationMethods = {
15     Object: new Set([
16         "assign", "defineProperties", "defineProperty", "freeze",
17         "setPrototypeOf"
18     ]),
19     Reflect: new Set([
20         "defineProperty", "deleteProperty", "set", "setPrototypeOf"
21     ])
22 };
23
24 /**
25  * Check if a given node is LHS of an assignment node.
26  * @param {ASTNode} node The node to check.
27  * @returns {boolean} `true` if the node is LHS.
28  */
29 function isAssignmentLeft(node) {
30     const { parent } = node;
31
32     return (
33         (
34             parent.type === "AssignmentExpression" &&
35             parent.left === node
36         ) ||
37
38         // Destructuring assignments
39         parent.type === "ArrayPattern" ||
40         (
41             parent.type === "Property" &&
42             parent.value === node &&
43             parent.parent.type === "ObjectPattern"
44         ) ||
45         parent.type === "RestElement" ||
46         (
47             parent.type === "AssignmentPattern" &&
48             parent.left === node
49         )
50     );
51 }
52
53 /**
54  * Check if a given node is the operand of mutation unary operator.
55  * @param {ASTNode} node The node to check.
56  * @returns {boolean} `true` if the node is the operand of mutation unary operator.
57  */
58 function isOperandOfMutationUnaryOperator(node) {
59     const { parent } = node;
60
61     return (
62         (
63             parent.type === "UpdateExpression" &&
64             parent.argument === node
65         ) ||
66         (
67             parent.type === "UnaryExpression" &&
68             parent.operator === "delete" &&
69             parent.argument === node
70         )
71     );
72 }
73
74 /**
75  * Check if a given node is the iteration variable of `for-in`/`for-of` syntax.
76  * @param {ASTNode} node The node to check.
77  * @returns {boolean} `true` if the node is the iteration variable.
78  */
79 function isIterationVariable(node) {
80     const { parent } = node;
81
82     return (
83         (
84             parent.type === "ForInStatement" &&
85             parent.left === node
86         ) ||
87         (
88             parent.type === "ForOfStatement" &&
89             parent.left === node
90         )
91     );
92 }
93
94 /**
95  * Check if a given node is the iteration variable of `for-in`/`for-of` syntax.
96  * @param {ASTNode} node The node to check.
97  * @param {Scope} scope A `escope.Scope` object to find variable (whichever).
98  * @returns {boolean} `true` if the node is the iteration variable.
99  */
100 function isArgumentOfWellKnownMutationFunction(node, scope) {
101     const { parent } = node;
102
103     if (
104         parent.type === "CallExpression" &&
105         parent.arguments[0] === node &&
106         parent.callee.type === "MemberExpression" &&
107         parent.callee.object.type === "Identifier"
108     ) {
109         const { callee } = parent;
110         const { object } = callee;
111
112         if (Object.keys(MutationMethods).includes(object.name)) {
113             const variable = findVariable(scope, object);
114
115             return (
116                 variable !== null &&
117                 variable.scope.type === "global" &&
118                 MutationMethods[object.name].has(getPropertyName(callee, scope))
119             );
120         }
121     }
122
123     return false;
124 }
125
126 /**
127  * Check if the identifier node is placed at to update members.
128  * @param {ASTNode} id The Identifier node to check.
129  * @param {Scope} scope A `escope.Scope` object to find variable (whichever).
130  * @returns {boolean} `true` if the member of `id` was updated.
131  */
132 function isMemberWrite(id, scope) {
133     const { parent } = id;
134
135     return (
136         (
137             parent.type === "MemberExpression" &&
138             parent.object === id &&
139             (
140                 isAssignmentLeft(parent) ||
141                 isOperandOfMutationUnaryOperator(parent) ||
142                 isIterationVariable(parent)
143             )
144         ) ||
145         isArgumentOfWellKnownMutationFunction(id, scope)
146     );
147 }
148
149 /**
150  * Get the mutation node.
151  * @param {ASTNode} id The Identifier node to get.
152  * @returns {ASTNode} The mutation node.
153  */
154 function getWriteNode(id) {
155     let node = id.parent;
156
157     while (
158         node &&
159         node.type !== "AssignmentExpression" &&
160         node.type !== "UpdateExpression" &&
161         node.type !== "UnaryExpression" &&
162         node.type !== "CallExpression" &&
163         node.type !== "ForInStatement" &&
164         node.type !== "ForOfStatement"
165     ) {
166         node = node.parent;
167     }
168
169     return node || id;
170 }
171
172 //------------------------------------------------------------------------------
173 // Rule Definition
174 //------------------------------------------------------------------------------
175
176 module.exports = {
177     meta: {
178         type: "problem",
179
180         docs: {
181             description: "disallow assigning to imported bindings",
182             category: "Possible Errors",
183             recommended: false,
184             url: "https://eslint.org/docs/rules/no-import-assign"
185         },
186
187         schema: [],
188
189         messages: {
190             readonly: "'{{name}}' is read-only.",
191             readonlyMember: "The members of '{{name}}' are read-only."
192         }
193     },
194
195     create(context) {
196         return {
197             ImportDeclaration(node) {
198                 const scope = context.getScope();
199
200                 for (const variable of context.getDeclaredVariables(node)) {
201                     const shouldCheckMembers = variable.defs.some(
202                         d => d.node.type === "ImportNamespaceSpecifier"
203                     );
204                     let prevIdNode = null;
205
206                     for (const reference of variable.references) {
207                         const idNode = reference.identifier;
208
209                         /*
210                          * AssignmentPattern (e.g. `[a = 0] = b`) makes two write
211                          * references for the same identifier. This should skip
212                          * the one of the two in order to prevent redundant reports.
213                          */
214                         if (idNode === prevIdNode) {
215                             continue;
216                         }
217                         prevIdNode = idNode;
218
219                         if (reference.isWrite()) {
220                             context.report({
221                                 node: getWriteNode(idNode),
222                                 messageId: "readonly",
223                                 data: { name: idNode.name }
224                             });
225                         } else if (shouldCheckMembers && isMemberWrite(idNode, scope)) {
226                             context.report({
227                                 node: getWriteNode(idNode),
228                                 messageId: "readonlyMember",
229                                 data: { name: idNode.name }
230                             });
231                         }
232                     }
233                 }
234             }
235         };
236
237     }
238 };