UNPKG

23.1 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const experimental_utils_1 = require("@typescript-eslint/experimental-utils");
4const eslint_visitor_keys_1 = require("eslint-visitor-keys");
5const scope_manager_1 = require("./scope/scope-manager");
6const typescript_estree_1 = require("@typescript-eslint/typescript-estree");
7/**
8 * Define the override function of `Scope#__define` for global augmentation.
9 * @param {Function} define The original Scope#__define method.
10 * @returns {Function} The override function.
11 */
12function overrideDefine(define) {
13 return function (node, definition) {
14 define.call(this, node, definition);
15 // Set `variable.eslintUsed` to tell ESLint that the variable is exported.
16 const variable = 'name' in node &&
17 typeof node.name === 'string' &&
18 this.set.get(node.name);
19 if (variable) {
20 variable.eslintUsed = true;
21 }
22 };
23}
24class PatternVisitor extends experimental_utils_1.TSESLintScope.PatternVisitor {
25 constructor(options, rootPattern, callback) {
26 super(options, rootPattern, callback);
27 }
28 Identifier(node) {
29 super.Identifier(node);
30 if (node.decorators) {
31 this.rightHandNodes.push(...node.decorators);
32 }
33 if (node.typeAnnotation) {
34 this.rightHandNodes.push(node.typeAnnotation);
35 }
36 }
37 ArrayPattern(node) {
38 node.elements.forEach(this.visit, this);
39 if (node.decorators) {
40 this.rightHandNodes.push(...node.decorators);
41 }
42 if (node.typeAnnotation) {
43 this.rightHandNodes.push(node.typeAnnotation);
44 }
45 }
46 ObjectPattern(node) {
47 node.properties.forEach(this.visit, this);
48 if (node.decorators) {
49 this.rightHandNodes.push(...node.decorators);
50 }
51 if (node.typeAnnotation) {
52 this.rightHandNodes.push(node.typeAnnotation);
53 }
54 }
55 RestElement(node) {
56 super.RestElement(node);
57 if (node.decorators) {
58 this.rightHandNodes.push(...node.decorators);
59 }
60 if (node.typeAnnotation) {
61 this.rightHandNodes.push(node.typeAnnotation);
62 }
63 }
64 TSParameterProperty(node) {
65 this.visit(node.parameter);
66 if (node.decorators) {
67 this.rightHandNodes.push(...node.decorators);
68 }
69 }
70}
71class Referencer extends experimental_utils_1.TSESLintScope.Referencer {
72 constructor(options, scopeManager) {
73 super(options, scopeManager);
74 this.typeMode = false;
75 }
76 /**
77 * Override to use PatternVisitor we overrode.
78 * @param node The Identifier node to visit.
79 * @param [options] The flag to visit right-hand side nodes.
80 * @param callback The callback function for left-hand side nodes.
81 */
82 visitPattern(node, options, callback) {
83 if (!node) {
84 return;
85 }
86 if (typeof options === 'function') {
87 callback = options;
88 options = { processRightHandNodes: false };
89 }
90 const visitor = new PatternVisitor(this.options, node, callback);
91 visitor.visit(node);
92 if (options.processRightHandNodes) {
93 visitor.rightHandNodes.forEach(this.visit, this);
94 }
95 }
96 /**
97 * Override.
98 * Visit `node.typeParameters` and `node.returnType` additionally to find `typeof` expressions.
99 * @param node The function node to visit.
100 */
101 visitFunction(node) {
102 const { type, id, typeParameters, params, returnType, body } = node;
103 const scopeManager = this.scopeManager;
104 const upperScope = this.currentScope();
105 // Process the name.
106 if (type === experimental_utils_1.AST_NODE_TYPES.FunctionDeclaration && id) {
107 upperScope.__define(id, new experimental_utils_1.TSESLintScope.Definition('FunctionName', id, node, null, null, null));
108 // Remove overload definition to avoid confusion of no-redeclare rule.
109 const { defs, identifiers } = upperScope.set.get(id.name);
110 for (let i = 0; i < defs.length; ++i) {
111 const def = defs[i];
112 if (def.type === 'FunctionName' &&
113 def.node.type === experimental_utils_1.AST_NODE_TYPES.TSDeclareFunction) {
114 defs.splice(i, 1);
115 identifiers.splice(i, 1);
116 break;
117 }
118 }
119 }
120 else if (type === experimental_utils_1.AST_NODE_TYPES.FunctionExpression && id) {
121 scopeManager.__nestFunctionExpressionNameScope(node);
122 }
123 // Open the function scope.
124 scopeManager.__nestFunctionScope(node, this.isInnerMethodDefinition);
125 const innerScope = this.currentScope();
126 // Process the type parameters
127 this.visit(typeParameters);
128 // Process parameter declarations.
129 for (let i = 0; i < params.length; ++i) {
130 this.visitPattern(params[i], { processRightHandNodes: true }, (pattern, info) => {
131 if (pattern.type !== experimental_utils_1.AST_NODE_TYPES.Identifier ||
132 pattern.name !== 'this') {
133 innerScope.__define(pattern, new experimental_utils_1.TSESLintScope.ParameterDefinition(pattern, node, i, info.rest));
134 this.referencingDefaultValue(pattern, info.assignments, null, true);
135 }
136 });
137 }
138 // Process the return type.
139 this.visit(returnType);
140 // Process the body.
141 if (body && body.type === experimental_utils_1.AST_NODE_TYPES.BlockStatement) {
142 this.visitChildren(body);
143 }
144 else {
145 this.visit(body);
146 }
147 // Close the function scope.
148 this.close(node);
149 }
150 /**
151 * Override.
152 * Visit decorators.
153 * @param node The class node to visit.
154 */
155 visitClass(node) {
156 this.visitDecorators(node.decorators);
157 const upperTypeMode = this.typeMode;
158 this.typeMode = true;
159 if (node.superTypeParameters) {
160 this.visit(node.superTypeParameters);
161 }
162 if (node.implements) {
163 node.implements.forEach(this.visit, this);
164 }
165 this.typeMode = upperTypeMode;
166 super.visitClass(node);
167 }
168 /**
169 * Visit typeParameters.
170 * @param node The node to visit.
171 */
172 visitTypeParameters(node) {
173 if (node.typeParameters) {
174 const upperTypeMode = this.typeMode;
175 this.typeMode = true;
176 this.visit(node.typeParameters);
177 this.typeMode = upperTypeMode;
178 }
179 }
180 /**
181 * Override.
182 */
183 JSXOpeningElement(node) {
184 this.visit(node.name);
185 this.visitTypeParameters(node);
186 node.attributes.forEach(this.visit, this);
187 }
188 /**
189 * Override.
190 * Don't create the reference object in the type mode.
191 * @param node The Identifier node to visit.
192 */
193 Identifier(node) {
194 this.visitDecorators(node.decorators);
195 if (!this.typeMode) {
196 super.Identifier(node);
197 }
198 this.visit(node.typeAnnotation);
199 }
200 /**
201 * Override.
202 * Visit decorators.
203 * @param node The MethodDefinition node to visit.
204 */
205 MethodDefinition(node) {
206 this.visitDecorators(node.decorators);
207 super.MethodDefinition(node);
208 }
209 /**
210 * Don't create the reference object for the key if not computed.
211 * @param node The ClassProperty node to visit.
212 */
213 ClassProperty(node) {
214 const upperTypeMode = this.typeMode;
215 const { computed, decorators, key, typeAnnotation, value } = node;
216 this.typeMode = false;
217 this.visitDecorators(decorators);
218 if (computed) {
219 this.visit(key);
220 }
221 this.typeMode = true;
222 this.visit(typeAnnotation);
223 this.typeMode = false;
224 this.visit(value);
225 this.typeMode = upperTypeMode;
226 }
227 /**
228 * Visit new expression.
229 * @param node The NewExpression node to visit.
230 */
231 NewExpression(node) {
232 this.visitTypeParameters(node);
233 this.visit(node.callee);
234 node.arguments.forEach(this.visit, this);
235 }
236 /**
237 * Override.
238 * Visit call expression.
239 * @param node The CallExpression node to visit.
240 */
241 CallExpression(node) {
242 this.visitTypeParameters(node);
243 this.visit(node.callee);
244 node.arguments.forEach(this.visit, this);
245 }
246 /**
247 * Visit optional member expression.
248 * @param node The OptionalMemberExpression node to visit.
249 */
250 OptionalMemberExpression(node) {
251 this.visit(node.object);
252 if (node.computed) {
253 this.visit(node.property);
254 }
255 }
256 /**
257 * Visit optional call expression.
258 * @param node The OptionalMemberExpression node to visit.
259 */
260 OptionalCallExpression(node) {
261 this.visitTypeParameters(node);
262 this.visit(node.callee);
263 node.arguments.forEach(this.visit, this);
264 }
265 /**
266 * Define the variable of this function declaration only once.
267 * Because to avoid confusion of `no-redeclare` rule by overloading.
268 * @param node The TSDeclareFunction node to visit.
269 */
270 TSDeclareFunction(node) {
271 var _a, _b;
272 const scopeManager = this.scopeManager;
273 const upperScope = this.currentScope();
274 const { id, typeParameters, params, returnType } = node;
275 // Ignore this if other overload have already existed.
276 if (id) {
277 const variable = upperScope.set.get(id.name);
278 const defs = (_a = variable) === null || _a === void 0 ? void 0 : _a.defs;
279 const existed = (_b = defs) === null || _b === void 0 ? void 0 : _b.some((d) => d.type === 'FunctionName');
280 if (!existed) {
281 upperScope.__define(id, new experimental_utils_1.TSESLintScope.Definition('FunctionName', id, node, null, null, null));
282 }
283 }
284 // Open the function scope.
285 scopeManager.__nestEmptyFunctionScope(node);
286 const innerScope = this.currentScope();
287 // Process the type parameters
288 this.visit(typeParameters);
289 // Process parameter declarations.
290 for (let i = 0; i < params.length; ++i) {
291 this.visitPattern(params[i], { processRightHandNodes: true }, (pattern, info) => {
292 innerScope.__define(pattern, new experimental_utils_1.TSESLintScope.ParameterDefinition(pattern, node, i, info.rest));
293 // Set `variable.eslintUsed` to tell ESLint that the variable is used.
294 const variable = innerScope.set.get(pattern.name);
295 if (variable) {
296 variable.eslintUsed = true;
297 }
298 this.referencingDefaultValue(pattern, info.assignments, null, true);
299 });
300 }
301 // Process the return type.
302 this.visit(returnType);
303 // Close the function scope.
304 this.close(node);
305 }
306 /**
307 * Create reference objects for the references in parameters and return type.
308 * @param node The TSEmptyBodyFunctionExpression node to visit.
309 */
310 TSEmptyBodyFunctionExpression(node) {
311 const upperTypeMode = this.typeMode;
312 const { typeParameters, params, returnType } = node;
313 this.typeMode = true;
314 this.visit(typeParameters);
315 params.forEach(this.visit, this);
316 this.visit(returnType);
317 this.typeMode = upperTypeMode;
318 }
319 /**
320 * Don't make variable because it declares only types.
321 * Switch to the type mode and visit child nodes to find `typeof x` expression in type declarations.
322 * @param node The TSInterfaceDeclaration node to visit.
323 */
324 TSInterfaceDeclaration(node) {
325 this.visitTypeNodes(node);
326 }
327 /**
328 * Don't make variable because it declares only types.
329 * Switch to the type mode and visit child nodes to find `typeof x` expression in type declarations.
330 * @param node The TSClassImplements node to visit.
331 */
332 TSClassImplements(node) {
333 this.visitTypeNodes(node);
334 }
335 /**
336 * Don't make variable because it declares only types.
337 * Switch to the type mode and visit child nodes to find `typeof x` expression in type declarations.
338 * @param node The TSIndexSignature node to visit.
339 */
340 TSIndexSignature(node) {
341 this.visitTypeNodes(node);
342 }
343 /**
344 * Visit type assertion.
345 * @param node The TSTypeAssertion node to visit.
346 */
347 TSTypeAssertion(node) {
348 if (this.typeMode) {
349 this.visit(node.typeAnnotation);
350 }
351 else {
352 this.typeMode = true;
353 this.visit(node.typeAnnotation);
354 this.typeMode = false;
355 }
356 this.visit(node.expression);
357 }
358 /**
359 * Visit as expression.
360 * @param node The TSAsExpression node to visit.
361 */
362 TSAsExpression(node) {
363 this.visit(node.expression);
364 if (this.typeMode) {
365 this.visit(node.typeAnnotation);
366 }
367 else {
368 this.typeMode = true;
369 this.visit(node.typeAnnotation);
370 this.typeMode = false;
371 }
372 }
373 /**
374 * Switch to the type mode and visit child nodes to find `typeof x` expression in type declarations.
375 * @param node The TSTypeAnnotation node to visit.
376 */
377 TSTypeAnnotation(node) {
378 this.visitTypeNodes(node);
379 }
380 /**
381 * Switch to the type mode and visit child nodes to find `typeof x` expression in type declarations.
382 * @param node The TSTypeParameterDeclaration node to visit.
383 */
384 TSTypeParameterDeclaration(node) {
385 this.visitTypeNodes(node);
386 }
387 /**
388 * Create reference objects for the references in `typeof` expression.
389 * @param node The TSTypeQuery node to visit.
390 */
391 TSTypeQuery(node) {
392 if (this.typeMode) {
393 this.typeMode = false;
394 this.visitChildren(node);
395 this.typeMode = true;
396 }
397 else {
398 this.visitChildren(node);
399 }
400 }
401 /**
402 * @param node The TSTypeParameter node to visit.
403 */
404 TSTypeParameter(node) {
405 this.visitTypeNodes(node);
406 }
407 /**
408 * @param node The TSInferType node to visit.
409 */
410 TSInferType(node) {
411 this.visitTypeNodes(node);
412 }
413 /**
414 * @param node The TSTypeReference node to visit.
415 */
416 TSTypeReference(node) {
417 this.visitTypeNodes(node);
418 }
419 /**
420 * @param node The TSTypeLiteral node to visit.
421 */
422 TSTypeLiteral(node) {
423 this.visitTypeNodes(node);
424 }
425 /**
426 * @param node The TSLiteralType node to visit.
427 */
428 TSLiteralType(node) {
429 this.visitTypeNodes(node);
430 }
431 /**
432 * @param node The TSIntersectionType node to visit.
433 */
434 TSIntersectionType(node) {
435 this.visitTypeNodes(node);
436 }
437 /**
438 * @param node The TSConditionalType node to visit.
439 */
440 TSConditionalType(node) {
441 this.visitTypeNodes(node);
442 }
443 /**
444 * @param node The TSIndexedAccessType node to visit.
445 */
446 TSIndexedAccessType(node) {
447 this.visitTypeNodes(node);
448 }
449 /**
450 * @param node The TSMappedType node to visit.
451 */
452 TSMappedType(node) {
453 this.visitTypeNodes(node);
454 }
455 /**
456 * @param node The TSOptionalType node to visit.
457 */
458 TSOptionalType(node) {
459 this.visitTypeNodes(node);
460 }
461 /**
462 * @param node The TSParenthesizedType node to visit.
463 */
464 TSParenthesizedType(node) {
465 this.visitTypeNodes(node);
466 }
467 /**
468 * @param node The TSRestType node to visit.
469 */
470 TSRestType(node) {
471 this.visitTypeNodes(node);
472 }
473 /**
474 * @param node The TSTupleType node to visit.
475 */
476 TSTupleType(node) {
477 this.visitTypeNodes(node);
478 }
479 /**
480 * Create reference objects for the object part. (This is `obj.prop`)
481 * @param node The TSQualifiedName node to visit.
482 */
483 TSQualifiedName(node) {
484 this.visit(node.left);
485 }
486 /**
487 * Create reference objects for the references in computed keys.
488 * @param node The TSPropertySignature node to visit.
489 */
490 TSPropertySignature(node) {
491 const upperTypeMode = this.typeMode;
492 const { computed, key, typeAnnotation, initializer } = node;
493 if (computed) {
494 this.typeMode = false;
495 this.visit(key);
496 this.typeMode = true;
497 }
498 else {
499 this.typeMode = true;
500 this.visit(key);
501 }
502 this.visit(typeAnnotation);
503 this.visit(initializer);
504 this.typeMode = upperTypeMode;
505 }
506 /**
507 * Create reference objects for the references in computed keys.
508 * @param node The TSMethodSignature node to visit.
509 */
510 TSMethodSignature(node) {
511 const upperTypeMode = this.typeMode;
512 const { computed, key, typeParameters, params, returnType } = node;
513 if (computed) {
514 this.typeMode = false;
515 this.visit(key);
516 this.typeMode = true;
517 }
518 else {
519 this.typeMode = true;
520 this.visit(key);
521 }
522 this.visit(typeParameters);
523 params.forEach(this.visit, this);
524 this.visit(returnType);
525 this.typeMode = upperTypeMode;
526 }
527 /**
528 * Create variable object for the enum.
529 * The enum declaration creates a scope for the enum members.
530 *
531 * enum E {
532 * A,
533 * B,
534 * C = A + B // A and B are references to the enum member.
535 * }
536 *
537 * const a = 0
538 * enum E {
539 * A = a // a is above constant.
540 * }
541 *
542 * @param node The TSEnumDeclaration node to visit.
543 */
544 TSEnumDeclaration(node) {
545 const { id, members } = node;
546 const scopeManager = this.scopeManager;
547 const scope = this.currentScope();
548 if (id) {
549 scope.__define(id, new experimental_utils_1.TSESLintScope.Definition('EnumName', id, node));
550 }
551 scopeManager.__nestEnumScope(node);
552 for (const member of members) {
553 this.visit(member);
554 }
555 this.close(node);
556 }
557 /**
558 * Create variable object for the enum member and create reference object for the initializer.
559 * And visit the initializer.
560 *
561 * @param node The TSEnumMember node to visit.
562 */
563 TSEnumMember(node) {
564 const { id, initializer } = node;
565 const scope = this.currentScope();
566 scope.__define(id, new experimental_utils_1.TSESLintScope.Definition('EnumMemberName', id, node));
567 if (initializer) {
568 scope.__referencing(id, experimental_utils_1.TSESLintScope.Reference.WRITE, initializer, null, false, true);
569 this.visit(initializer);
570 }
571 }
572 /**
573 * Create the variable object for the module name, and visit children.
574 * @param node The TSModuleDeclaration node to visit.
575 */
576 TSModuleDeclaration(node) {
577 const scope = this.currentScope();
578 const { id, body } = node;
579 if (node.global) {
580 this.visitGlobalAugmentation(node);
581 return;
582 }
583 if (id && id.type === experimental_utils_1.AST_NODE_TYPES.Identifier) {
584 scope.__define(id, new experimental_utils_1.TSESLintScope.Definition('NamespaceName', id, node, null, null, null));
585 }
586 this.visit(body);
587 }
588 TSTypeAliasDeclaration(node) {
589 this.typeMode = true;
590 this.visitChildren(node);
591 this.typeMode = false;
592 }
593 /**
594 * Process the module block.
595 * @param node The TSModuleBlock node to visit.
596 */
597 TSModuleBlock(node) {
598 this.scopeManager.__nestBlockScope(node);
599 this.visitChildren(node);
600 this.close(node);
601 }
602 TSAbstractClassProperty(node) {
603 this.ClassProperty(node);
604 }
605 TSAbstractMethodDefinition(node) {
606 this.MethodDefinition(node);
607 }
608 /**
609 * Process import equal declaration
610 * @param node The TSImportEqualsDeclaration node to visit.
611 */
612 TSImportEqualsDeclaration(node) {
613 const { id, moduleReference } = node;
614 if (id && id.type === experimental_utils_1.AST_NODE_TYPES.Identifier) {
615 this.currentScope().__define(id, new experimental_utils_1.TSESLintScope.Definition('ImportBinding', id, node, null, null, null));
616 }
617 this.visit(moduleReference);
618 }
619 /**
620 * Process the global augmentation.
621 * 1. Set the global scope as the current scope.
622 * 2. Configure the global scope to set `variable.eslintUsed = true` for all defined variables. This means `no-unused-vars` doesn't warn those.
623 * @param node The TSModuleDeclaration node to visit.
624 */
625 visitGlobalAugmentation(node) {
626 const scopeManager = this.scopeManager;
627 const currentScope = this.currentScope();
628 const globalScope = scopeManager.globalScope;
629 const originalDefine = globalScope.__define;
630 globalScope.__define = overrideDefine(originalDefine);
631 scopeManager.__currentScope = globalScope;
632 // Skip TSModuleBlock to avoid to create that block scope.
633 if (node.body && node.body.type === experimental_utils_1.AST_NODE_TYPES.TSModuleBlock) {
634 node.body.body.forEach(this.visit, this);
635 }
636 scopeManager.__currentScope = currentScope;
637 globalScope.__define = originalDefine;
638 }
639 /**
640 * Process decorators.
641 * @param decorators The decorator nodes to visit.
642 */
643 visitDecorators(decorators) {
644 if (decorators) {
645 decorators.forEach(this.visit, this);
646 }
647 }
648 /**
649 * Process all child of type nodes
650 * @param node node to be processed
651 */
652 visitTypeNodes(node) {
653 if (this.typeMode) {
654 this.visitChildren(node);
655 }
656 else {
657 this.typeMode = true;
658 this.visitChildren(node);
659 this.typeMode = false;
660 }
661 }
662}
663function analyzeScope(ast, parserOptions) {
664 var _a;
665 const options = {
666 ignoreEval: true,
667 optimistic: false,
668 directive: false,
669 nodejsScope: parserOptions.sourceType === 'script' &&
670 (parserOptions.ecmaFeatures &&
671 parserOptions.ecmaFeatures.globalReturn) === true,
672 impliedStrict: false,
673 sourceType: parserOptions.sourceType,
674 ecmaVersion: (_a = parserOptions.ecmaVersion, (_a !== null && _a !== void 0 ? _a : 2018)),
675 childVisitorKeys: typescript_estree_1.visitorKeys,
676 fallback: eslint_visitor_keys_1.getKeys,
677 };
678 const scopeManager = new scope_manager_1.ScopeManager(options);
679 const referencer = new Referencer(options, scopeManager);
680 referencer.visit(ast);
681 return scopeManager;
682}
683exports.analyzeScope = analyzeScope;
684//# sourceMappingURL=analyze-scope.js.map
\No newline at end of file