.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint-scope / lib / referencer.js
1 /*
2   Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>
3
4   Redistribution and use in source and binary forms, with or without
5   modification, are permitted provided that the following conditions are met:
6
7     * Redistributions of source code must retain the above copyright
8       notice, this list of conditions and the following disclaimer.
9     * Redistributions in binary form must reproduce the above copyright
10       notice, this list of conditions and the following disclaimer in the
11       documentation and/or other materials provided with the distribution.
12
13   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16   ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
17   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24 "use strict";
25
26 /* eslint-disable no-underscore-dangle */
27 /* eslint-disable no-undefined */
28
29 const Syntax = require("estraverse").Syntax;
30 const esrecurse = require("esrecurse");
31 const Reference = require("./reference");
32 const Variable = require("./variable");
33 const PatternVisitor = require("./pattern-visitor");
34 const definition = require("./definition");
35 const assert = require("assert");
36
37 const ParameterDefinition = definition.ParameterDefinition;
38 const Definition = definition.Definition;
39
40 /**
41  * Traverse identifier in pattern
42  * @param {Object} options - options
43  * @param {pattern} rootPattern - root pattern
44  * @param {Refencer} referencer - referencer
45  * @param {callback} callback - callback
46  * @returns {void}
47  */
48 function traverseIdentifierInPattern(options, rootPattern, referencer, callback) {
49
50     // Call the callback at left hand identifier nodes, and Collect right hand nodes.
51     const visitor = new PatternVisitor(options, rootPattern, callback);
52
53     visitor.visit(rootPattern);
54
55     // Process the right hand nodes recursively.
56     if (referencer !== null && referencer !== undefined) {
57         visitor.rightHandNodes.forEach(referencer.visit, referencer);
58     }
59 }
60
61 // Importing ImportDeclaration.
62 // http://people.mozilla.org/~jorendorff/es6-draft.html#sec-moduledeclarationinstantiation
63 // https://github.com/estree/estree/blob/master/es6.md#importdeclaration
64 // FIXME: Now, we don't create module environment, because the context is
65 // implementation dependent.
66
67 class Importer extends esrecurse.Visitor {
68     constructor(declaration, referencer) {
69         super(null, referencer.options);
70         this.declaration = declaration;
71         this.referencer = referencer;
72     }
73
74     visitImport(id, specifier) {
75         this.referencer.visitPattern(id, pattern => {
76             this.referencer.currentScope().__define(pattern,
77                 new Definition(
78                     Variable.ImportBinding,
79                     pattern,
80                     specifier,
81                     this.declaration,
82                     null,
83                     null
84                 ));
85         });
86     }
87
88     ImportNamespaceSpecifier(node) {
89         const local = (node.local || node.id);
90
91         if (local) {
92             this.visitImport(local, node);
93         }
94     }
95
96     ImportDefaultSpecifier(node) {
97         const local = (node.local || node.id);
98
99         this.visitImport(local, node);
100     }
101
102     ImportSpecifier(node) {
103         const local = (node.local || node.id);
104
105         if (node.name) {
106             this.visitImport(node.name, node);
107         } else {
108             this.visitImport(local, node);
109         }
110     }
111 }
112
113 // Referencing variables and creating bindings.
114 class Referencer extends esrecurse.Visitor {
115     constructor(options, scopeManager) {
116         super(null, options);
117         this.options = options;
118         this.scopeManager = scopeManager;
119         this.parent = null;
120         this.isInnerMethodDefinition = false;
121     }
122
123     currentScope() {
124         return this.scopeManager.__currentScope;
125     }
126
127     close(node) {
128         while (this.currentScope() && node === this.currentScope().block) {
129             this.scopeManager.__currentScope = this.currentScope().__close(this.scopeManager);
130         }
131     }
132
133     pushInnerMethodDefinition(isInnerMethodDefinition) {
134         const previous = this.isInnerMethodDefinition;
135
136         this.isInnerMethodDefinition = isInnerMethodDefinition;
137         return previous;
138     }
139
140     popInnerMethodDefinition(isInnerMethodDefinition) {
141         this.isInnerMethodDefinition = isInnerMethodDefinition;
142     }
143
144     referencingDefaultValue(pattern, assignments, maybeImplicitGlobal, init) {
145         const scope = this.currentScope();
146
147         assignments.forEach(assignment => {
148             scope.__referencing(
149                 pattern,
150                 Reference.WRITE,
151                 assignment.right,
152                 maybeImplicitGlobal,
153                 pattern !== assignment.left,
154                 init
155             );
156         });
157     }
158
159     visitPattern(node, options, callback) {
160         let visitPatternOptions = options;
161         let visitPatternCallback = callback;
162
163         if (typeof options === "function") {
164             visitPatternCallback = options;
165             visitPatternOptions = { processRightHandNodes: false };
166         }
167
168         traverseIdentifierInPattern(
169             this.options,
170             node,
171             visitPatternOptions.processRightHandNodes ? this : null,
172             visitPatternCallback
173         );
174     }
175
176     visitFunction(node) {
177         let i, iz;
178
179         // FunctionDeclaration name is defined in upper scope
180         // NOTE: Not referring variableScope. It is intended.
181         // Since
182         //  in ES5, FunctionDeclaration should be in FunctionBody.
183         //  in ES6, FunctionDeclaration should be block scoped.
184
185         if (node.type === Syntax.FunctionDeclaration) {
186
187             // id is defined in upper scope
188             this.currentScope().__define(node.id,
189                 new Definition(
190                     Variable.FunctionName,
191                     node.id,
192                     node,
193                     null,
194                     null,
195                     null
196                 ));
197         }
198
199         // FunctionExpression with name creates its special scope;
200         // FunctionExpressionNameScope.
201         if (node.type === Syntax.FunctionExpression && node.id) {
202             this.scopeManager.__nestFunctionExpressionNameScope(node);
203         }
204
205         // Consider this function is in the MethodDefinition.
206         this.scopeManager.__nestFunctionScope(node, this.isInnerMethodDefinition);
207
208         const that = this;
209
210         /**
211          * Visit pattern callback
212          * @param {pattern} pattern - pattern
213          * @param {Object} info - info
214          * @returns {void}
215          */
216         function visitPatternCallback(pattern, info) {
217             that.currentScope().__define(pattern,
218                 new ParameterDefinition(
219                     pattern,
220                     node,
221                     i,
222                     info.rest
223                 ));
224
225             that.referencingDefaultValue(pattern, info.assignments, null, true);
226         }
227
228         // Process parameter declarations.
229         for (i = 0, iz = node.params.length; i < iz; ++i) {
230             this.visitPattern(node.params[i], { processRightHandNodes: true }, visitPatternCallback);
231         }
232
233         // if there's a rest argument, add that
234         if (node.rest) {
235             this.visitPattern({
236                 type: "RestElement",
237                 argument: node.rest
238             }, pattern => {
239                 this.currentScope().__define(pattern,
240                     new ParameterDefinition(
241                         pattern,
242                         node,
243                         node.params.length,
244                         true
245                     ));
246             });
247         }
248
249         // In TypeScript there are a number of function-like constructs which have no body,
250         // so check it exists before traversing
251         if (node.body) {
252
253             // Skip BlockStatement to prevent creating BlockStatement scope.
254             if (node.body.type === Syntax.BlockStatement) {
255                 this.visitChildren(node.body);
256             } else {
257                 this.visit(node.body);
258             }
259         }
260
261         this.close(node);
262     }
263
264     visitClass(node) {
265         if (node.type === Syntax.ClassDeclaration) {
266             this.currentScope().__define(node.id,
267                 new Definition(
268                     Variable.ClassName,
269                     node.id,
270                     node,
271                     null,
272                     null,
273                     null
274                 ));
275         }
276
277         this.visit(node.superClass);
278
279         this.scopeManager.__nestClassScope(node);
280
281         if (node.id) {
282             this.currentScope().__define(node.id,
283                 new Definition(
284                     Variable.ClassName,
285                     node.id,
286                     node
287                 ));
288         }
289         this.visit(node.body);
290
291         this.close(node);
292     }
293
294     visitProperty(node) {
295         let previous;
296
297         if (node.computed) {
298             this.visit(node.key);
299         }
300
301         const isMethodDefinition = node.type === Syntax.MethodDefinition;
302
303         if (isMethodDefinition) {
304             previous = this.pushInnerMethodDefinition(true);
305         }
306         this.visit(node.value);
307         if (isMethodDefinition) {
308             this.popInnerMethodDefinition(previous);
309         }
310     }
311
312     visitForIn(node) {
313         if (node.left.type === Syntax.VariableDeclaration && node.left.kind !== "var") {
314             this.scopeManager.__nestForScope(node);
315         }
316
317         if (node.left.type === Syntax.VariableDeclaration) {
318             this.visit(node.left);
319             this.visitPattern(node.left.declarations[0].id, pattern => {
320                 this.currentScope().__referencing(pattern, Reference.WRITE, node.right, null, true, true);
321             });
322         } else {
323             this.visitPattern(node.left, { processRightHandNodes: true }, (pattern, info) => {
324                 let maybeImplicitGlobal = null;
325
326                 if (!this.currentScope().isStrict) {
327                     maybeImplicitGlobal = {
328                         pattern,
329                         node
330                     };
331                 }
332                 this.referencingDefaultValue(pattern, info.assignments, maybeImplicitGlobal, false);
333                 this.currentScope().__referencing(pattern, Reference.WRITE, node.right, maybeImplicitGlobal, true, false);
334             });
335         }
336         this.visit(node.right);
337         this.visit(node.body);
338
339         this.close(node);
340     }
341
342     visitVariableDeclaration(variableTargetScope, type, node, index) {
343
344         const decl = node.declarations[index];
345         const init = decl.init;
346
347         this.visitPattern(decl.id, { processRightHandNodes: true }, (pattern, info) => {
348             variableTargetScope.__define(
349                 pattern,
350                 new Definition(
351                     type,
352                     pattern,
353                     decl,
354                     node,
355                     index,
356                     node.kind
357                 )
358             );
359
360             this.referencingDefaultValue(pattern, info.assignments, null, true);
361             if (init) {
362                 this.currentScope().__referencing(pattern, Reference.WRITE, init, null, !info.topLevel, true);
363             }
364         });
365     }
366
367     AssignmentExpression(node) {
368         if (PatternVisitor.isPattern(node.left)) {
369             if (node.operator === "=") {
370                 this.visitPattern(node.left, { processRightHandNodes: true }, (pattern, info) => {
371                     let maybeImplicitGlobal = null;
372
373                     if (!this.currentScope().isStrict) {
374                         maybeImplicitGlobal = {
375                             pattern,
376                             node
377                         };
378                     }
379                     this.referencingDefaultValue(pattern, info.assignments, maybeImplicitGlobal, false);
380                     this.currentScope().__referencing(pattern, Reference.WRITE, node.right, maybeImplicitGlobal, !info.topLevel, false);
381                 });
382             } else {
383                 this.currentScope().__referencing(node.left, Reference.RW, node.right);
384             }
385         } else {
386             this.visit(node.left);
387         }
388         this.visit(node.right);
389     }
390
391     CatchClause(node) {
392         this.scopeManager.__nestCatchScope(node);
393
394         this.visitPattern(node.param, { processRightHandNodes: true }, (pattern, info) => {
395             this.currentScope().__define(pattern,
396                 new Definition(
397                     Variable.CatchClause,
398                     node.param,
399                     node,
400                     null,
401                     null,
402                     null
403                 ));
404             this.referencingDefaultValue(pattern, info.assignments, null, true);
405         });
406         this.visit(node.body);
407
408         this.close(node);
409     }
410
411     Program(node) {
412         this.scopeManager.__nestGlobalScope(node);
413
414         if (this.scopeManager.__isNodejsScope()) {
415
416             // Force strictness of GlobalScope to false when using node.js scope.
417             this.currentScope().isStrict = false;
418             this.scopeManager.__nestFunctionScope(node, false);
419         }
420
421         if (this.scopeManager.__isES6() && this.scopeManager.isModule()) {
422             this.scopeManager.__nestModuleScope(node);
423         }
424
425         if (this.scopeManager.isStrictModeSupported() && this.scopeManager.isImpliedStrict()) {
426             this.currentScope().isStrict = true;
427         }
428
429         this.visitChildren(node);
430         this.close(node);
431     }
432
433     Identifier(node) {
434         this.currentScope().__referencing(node);
435     }
436
437     UpdateExpression(node) {
438         if (PatternVisitor.isPattern(node.argument)) {
439             this.currentScope().__referencing(node.argument, Reference.RW, null);
440         } else {
441             this.visitChildren(node);
442         }
443     }
444
445     MemberExpression(node) {
446         this.visit(node.object);
447         if (node.computed) {
448             this.visit(node.property);
449         }
450     }
451
452     Property(node) {
453         this.visitProperty(node);
454     }
455
456     MethodDefinition(node) {
457         this.visitProperty(node);
458     }
459
460     BreakStatement() {} // eslint-disable-line class-methods-use-this
461
462     ContinueStatement() {} // eslint-disable-line class-methods-use-this
463
464     LabeledStatement(node) {
465         this.visit(node.body);
466     }
467
468     ForStatement(node) {
469
470         // Create ForStatement declaration.
471         // NOTE: In ES6, ForStatement dynamically generates
472         // per iteration environment. However, escope is
473         // a static analyzer, we only generate one scope for ForStatement.
474         if (node.init && node.init.type === Syntax.VariableDeclaration && node.init.kind !== "var") {
475             this.scopeManager.__nestForScope(node);
476         }
477
478         this.visitChildren(node);
479
480         this.close(node);
481     }
482
483     ClassExpression(node) {
484         this.visitClass(node);
485     }
486
487     ClassDeclaration(node) {
488         this.visitClass(node);
489     }
490
491     CallExpression(node) {
492
493         // Check this is direct call to eval
494         if (!this.scopeManager.__ignoreEval() && node.callee.type === Syntax.Identifier && node.callee.name === "eval") {
495
496             // NOTE: This should be `variableScope`. Since direct eval call always creates Lexical environment and
497             // let / const should be enclosed into it. Only VariableDeclaration affects on the caller's environment.
498             this.currentScope().variableScope.__detectEval();
499         }
500         this.visitChildren(node);
501     }
502
503     BlockStatement(node) {
504         if (this.scopeManager.__isES6()) {
505             this.scopeManager.__nestBlockScope(node);
506         }
507
508         this.visitChildren(node);
509
510         this.close(node);
511     }
512
513     ThisExpression() {
514         this.currentScope().variableScope.__detectThis();
515     }
516
517     WithStatement(node) {
518         this.visit(node.object);
519
520         // Then nest scope for WithStatement.
521         this.scopeManager.__nestWithScope(node);
522
523         this.visit(node.body);
524
525         this.close(node);
526     }
527
528     VariableDeclaration(node) {
529         const variableTargetScope = (node.kind === "var") ? this.currentScope().variableScope : this.currentScope();
530
531         for (let i = 0, iz = node.declarations.length; i < iz; ++i) {
532             const decl = node.declarations[i];
533
534             this.visitVariableDeclaration(variableTargetScope, Variable.Variable, node, i);
535             if (decl.init) {
536                 this.visit(decl.init);
537             }
538         }
539     }
540
541     // sec 13.11.8
542     SwitchStatement(node) {
543         this.visit(node.discriminant);
544
545         if (this.scopeManager.__isES6()) {
546             this.scopeManager.__nestSwitchScope(node);
547         }
548
549         for (let i = 0, iz = node.cases.length; i < iz; ++i) {
550             this.visit(node.cases[i]);
551         }
552
553         this.close(node);
554     }
555
556     FunctionDeclaration(node) {
557         this.visitFunction(node);
558     }
559
560     FunctionExpression(node) {
561         this.visitFunction(node);
562     }
563
564     ForOfStatement(node) {
565         this.visitForIn(node);
566     }
567
568     ForInStatement(node) {
569         this.visitForIn(node);
570     }
571
572     ArrowFunctionExpression(node) {
573         this.visitFunction(node);
574     }
575
576     ImportDeclaration(node) {
577         assert(this.scopeManager.__isES6() && this.scopeManager.isModule(), "ImportDeclaration should appear when the mode is ES6 and in the module context.");
578
579         const importer = new Importer(node, this);
580
581         importer.visit(node);
582     }
583
584     visitExportDeclaration(node) {
585         if (node.source) {
586             return;
587         }
588         if (node.declaration) {
589             this.visit(node.declaration);
590             return;
591         }
592
593         this.visitChildren(node);
594     }
595
596     // TODO: ExportDeclaration doesn't exist. for bc?
597     ExportDeclaration(node) {
598         this.visitExportDeclaration(node);
599     }
600
601     ExportAllDeclaration(node) {
602         this.visitExportDeclaration(node);
603     }
604
605     ExportDefaultDeclaration(node) {
606         this.visitExportDeclaration(node);
607     }
608
609     ExportNamedDeclaration(node) {
610         this.visitExportDeclaration(node);
611     }
612
613     ExportSpecifier(node) {
614
615         // TODO: `node.id` doesn't exist. for bc?
616         const local = (node.id || node.local);
617
618         this.visit(local);
619     }
620
621     MetaProperty() { // eslint-disable-line class-methods-use-this
622
623         // do nothing.
624     }
625 }
626
627 module.exports = Referencer;
628
629 /* vim: set sw=4 ts=4 et tw=80 : */