UNPKG

23 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 const scopeManager = this.scopeManager;
272 const upperScope = this.currentScope();
273 const { id, typeParameters, params, returnType } = node;
274 // Ignore this if other overload have already existed.
275 if (id) {
276 const variable = upperScope.set.get(id.name);
277 const defs = variable === null || variable === void 0 ? void 0 : variable.defs;
278 const existed = defs === null || defs === void 0 ? void 0 : defs.some((d) => d.type === 'FunctionName');
279 if (!existed) {
280 upperScope.__define(id, new experimental_utils_1.TSESLintScope.Definition('FunctionName', id, node, null, null, null));
281 }
282 }
283 // Open the function scope.
284 scopeManager.__nestEmptyFunctionScope(node);
285 const innerScope = this.currentScope();
286 // Process the type parameters
287 this.visit(typeParameters);
288 // Process parameter declarations.
289 for (let i = 0; i < params.length; ++i) {
290 this.visitPattern(params[i], { processRightHandNodes: true }, (pattern, info) => {
291 innerScope.__define(pattern, new experimental_utils_1.TSESLintScope.ParameterDefinition(pattern, node, i, info.rest));
292 // Set `variable.eslintUsed` to tell ESLint that the variable is used.
293 const variable = innerScope.set.get(pattern.name);
294 if (variable) {
295 variable.eslintUsed = true;
296 }
297 this.referencingDefaultValue(pattern, info.assignments, null, true);
298 });
299 }
300 // Process the return type.
301 this.visit(returnType);
302 // Close the function scope.
303 this.close(node);
304 }
305 /**
306 * Create reference objects for the references in parameters and return type.
307 * @param node The TSEmptyBodyFunctionExpression node to visit.
308 */
309 TSEmptyBodyFunctionExpression(node) {
310 const upperTypeMode = this.typeMode;
311 const { typeParameters, params, returnType } = node;
312 this.typeMode = true;
313 this.visit(typeParameters);
314 params.forEach(this.visit, this);
315 this.visit(returnType);
316 this.typeMode = upperTypeMode;
317 }
318 /**
319 * Don't make variable because it declares only types.
320 * Switch to the type mode and visit child nodes to find `typeof x` expression in type declarations.
321 * @param node The TSInterfaceDeclaration node to visit.
322 */
323 TSInterfaceDeclaration(node) {
324 this.visitTypeNodes(node);
325 }
326 /**
327 * Don't make variable because it declares only types.
328 * Switch to the type mode and visit child nodes to find `typeof x` expression in type declarations.
329 * @param node The TSClassImplements node to visit.
330 */
331 TSClassImplements(node) {
332 this.visitTypeNodes(node);
333 }
334 /**
335 * Don't make variable because it declares only types.
336 * Switch to the type mode and visit child nodes to find `typeof x` expression in type declarations.
337 * @param node The TSIndexSignature node to visit.
338 */
339 TSIndexSignature(node) {
340 this.visitTypeNodes(node);
341 }
342 /**
343 * Visit type assertion.
344 * @param node The TSTypeAssertion node to visit.
345 */
346 TSTypeAssertion(node) {
347 if (this.typeMode) {
348 this.visit(node.typeAnnotation);
349 }
350 else {
351 this.typeMode = true;
352 this.visit(node.typeAnnotation);
353 this.typeMode = false;
354 }
355 this.visit(node.expression);
356 }
357 /**
358 * Visit as expression.
359 * @param node The TSAsExpression node to visit.
360 */
361 TSAsExpression(node) {
362 this.visit(node.expression);
363 if (this.typeMode) {
364 this.visit(node.typeAnnotation);
365 }
366 else {
367 this.typeMode = true;
368 this.visit(node.typeAnnotation);
369 this.typeMode = false;
370 }
371 }
372 /**
373 * Switch to the type mode and visit child nodes to find `typeof x` expression in type declarations.
374 * @param node The TSTypeAnnotation node to visit.
375 */
376 TSTypeAnnotation(node) {
377 this.visitTypeNodes(node);
378 }
379 /**
380 * Switch to the type mode and visit child nodes to find `typeof x` expression in type declarations.
381 * @param node The TSTypeParameterDeclaration node to visit.
382 */
383 TSTypeParameterDeclaration(node) {
384 this.visitTypeNodes(node);
385 }
386 /**
387 * Create reference objects for the references in `typeof` expression.
388 * @param node The TSTypeQuery node to visit.
389 */
390 TSTypeQuery(node) {
391 if (this.typeMode) {
392 this.typeMode = false;
393 this.visitChildren(node);
394 this.typeMode = true;
395 }
396 else {
397 this.visitChildren(node);
398 }
399 }
400 /**
401 * @param node The TSTypeParameter node to visit.
402 */
403 TSTypeParameter(node) {
404 this.visitTypeNodes(node);
405 }
406 /**
407 * @param node The TSInferType node to visit.
408 */
409 TSInferType(node) {
410 this.visitTypeNodes(node);
411 }
412 /**
413 * @param node The TSTypeReference node to visit.
414 */
415 TSTypeReference(node) {
416 this.visitTypeNodes(node);
417 }
418 /**
419 * @param node The TSTypeLiteral node to visit.
420 */
421 TSTypeLiteral(node) {
422 this.visitTypeNodes(node);
423 }
424 /**
425 * @param node The TSLiteralType node to visit.
426 */
427 TSLiteralType(node) {
428 this.visitTypeNodes(node);
429 }
430 /**
431 * @param node The TSIntersectionType node to visit.
432 */
433 TSIntersectionType(node) {
434 this.visitTypeNodes(node);
435 }
436 /**
437 * @param node The TSConditionalType node to visit.
438 */
439 TSConditionalType(node) {
440 this.visitTypeNodes(node);
441 }
442 /**
443 * @param node The TSIndexedAccessType node to visit.
444 */
445 TSIndexedAccessType(node) {
446 this.visitTypeNodes(node);
447 }
448 /**
449 * @param node The TSMappedType node to visit.
450 */
451 TSMappedType(node) {
452 this.visitTypeNodes(node);
453 }
454 /**
455 * @param node The TSOptionalType node to visit.
456 */
457 TSOptionalType(node) {
458 this.visitTypeNodes(node);
459 }
460 /**
461 * @param node The TSParenthesizedType node to visit.
462 */
463 TSParenthesizedType(node) {
464 this.visitTypeNodes(node);
465 }
466 /**
467 * @param node The TSRestType node to visit.
468 */
469 TSRestType(node) {
470 this.visitTypeNodes(node);
471 }
472 /**
473 * @param node The TSTupleType node to visit.
474 */
475 TSTupleType(node) {
476 this.visitTypeNodes(node);
477 }
478 /**
479 * Create reference objects for the object part. (This is `obj.prop`)
480 * @param node The TSQualifiedName node to visit.
481 */
482 TSQualifiedName(node) {
483 this.visit(node.left);
484 }
485 /**
486 * Create reference objects for the references in computed keys.
487 * @param node The TSPropertySignature node to visit.
488 */
489 TSPropertySignature(node) {
490 const upperTypeMode = this.typeMode;
491 const { computed, key, typeAnnotation, initializer } = node;
492 if (computed) {
493 this.typeMode = false;
494 this.visit(key);
495 this.typeMode = true;
496 }
497 else {
498 this.typeMode = true;
499 this.visit(key);
500 }
501 this.visit(typeAnnotation);
502 this.visit(initializer);
503 this.typeMode = upperTypeMode;
504 }
505 /**
506 * Create reference objects for the references in computed keys.
507 * @param node The TSMethodSignature node to visit.
508 */
509 TSMethodSignature(node) {
510 const upperTypeMode = this.typeMode;
511 const { computed, key, typeParameters, params, returnType } = node;
512 if (computed) {
513 this.typeMode = false;
514 this.visit(key);
515 this.typeMode = true;
516 }
517 else {
518 this.typeMode = true;
519 this.visit(key);
520 }
521 this.visit(typeParameters);
522 params.forEach(this.visit, this);
523 this.visit(returnType);
524 this.typeMode = upperTypeMode;
525 }
526 /**
527 * Create variable object for the enum.
528 * The enum declaration creates a scope for the enum members.
529 *
530 * enum E {
531 * A,
532 * B,
533 * C = A + B // A and B are references to the enum member.
534 * }
535 *
536 * const a = 0
537 * enum E {
538 * A = a // a is above constant.
539 * }
540 *
541 * @param node The TSEnumDeclaration node to visit.
542 */
543 TSEnumDeclaration(node) {
544 const { id, members } = node;
545 const scopeManager = this.scopeManager;
546 const scope = this.currentScope();
547 if (id) {
548 scope.__define(id, new experimental_utils_1.TSESLintScope.Definition('EnumName', id, node));
549 }
550 scopeManager.__nestEnumScope(node);
551 for (const member of members) {
552 this.visit(member);
553 }
554 this.close(node);
555 }
556 /**
557 * Create variable object for the enum member and create reference object for the initializer.
558 * And visit the initializer.
559 *
560 * @param node The TSEnumMember node to visit.
561 */
562 TSEnumMember(node) {
563 const { id, initializer } = node;
564 const scope = this.currentScope();
565 scope.__define(id, new experimental_utils_1.TSESLintScope.Definition('EnumMemberName', id, node));
566 if (initializer) {
567 scope.__referencing(id, experimental_utils_1.TSESLintScope.Reference.WRITE, initializer, null, false, true);
568 this.visit(initializer);
569 }
570 }
571 /**
572 * Create the variable object for the module name, and visit children.
573 * @param node The TSModuleDeclaration node to visit.
574 */
575 TSModuleDeclaration(node) {
576 const scope = this.currentScope();
577 const { id, body } = node;
578 if (node.global) {
579 this.visitGlobalAugmentation(node);
580 return;
581 }
582 if (id && id.type === experimental_utils_1.AST_NODE_TYPES.Identifier) {
583 scope.__define(id, new experimental_utils_1.TSESLintScope.Definition('NamespaceName', id, node, null, null, null));
584 }
585 this.visit(body);
586 }
587 TSTypeAliasDeclaration(node) {
588 this.typeMode = true;
589 this.visitChildren(node);
590 this.typeMode = false;
591 }
592 /**
593 * Process the module block.
594 * @param node The TSModuleBlock node to visit.
595 */
596 TSModuleBlock(node) {
597 this.scopeManager.__nestBlockScope(node);
598 this.visitChildren(node);
599 this.close(node);
600 }
601 TSAbstractClassProperty(node) {
602 this.ClassProperty(node);
603 }
604 TSAbstractMethodDefinition(node) {
605 this.MethodDefinition(node);
606 }
607 /**
608 * Process import equal declaration
609 * @param node The TSImportEqualsDeclaration node to visit.
610 */
611 TSImportEqualsDeclaration(node) {
612 const { id, moduleReference } = node;
613 if (id && id.type === experimental_utils_1.AST_NODE_TYPES.Identifier) {
614 this.currentScope().__define(id, new experimental_utils_1.TSESLintScope.Definition('ImportBinding', id, node, null, null, null));
615 }
616 this.visit(moduleReference);
617 }
618 /**
619 * Process the global augmentation.
620 * 1. Set the global scope as the current scope.
621 * 2. Configure the global scope to set `variable.eslintUsed = true` for all defined variables. This means `no-unused-vars` doesn't warn those.
622 * @param node The TSModuleDeclaration node to visit.
623 */
624 visitGlobalAugmentation(node) {
625 const scopeManager = this.scopeManager;
626 const currentScope = this.currentScope();
627 const globalScope = scopeManager.globalScope;
628 const originalDefine = globalScope.__define;
629 globalScope.__define = overrideDefine(originalDefine);
630 scopeManager.__currentScope = globalScope;
631 // Skip TSModuleBlock to avoid to create that block scope.
632 if (node.body && node.body.type === experimental_utils_1.AST_NODE_TYPES.TSModuleBlock) {
633 node.body.body.forEach(this.visit, this);
634 }
635 scopeManager.__currentScope = currentScope;
636 globalScope.__define = originalDefine;
637 }
638 /**
639 * Process decorators.
640 * @param decorators The decorator nodes to visit.
641 */
642 visitDecorators(decorators) {
643 if (decorators) {
644 decorators.forEach(this.visit, this);
645 }
646 }
647 /**
648 * Process all child of type nodes
649 * @param node node to be processed
650 */
651 visitTypeNodes(node) {
652 if (this.typeMode) {
653 this.visitChildren(node);
654 }
655 else {
656 this.typeMode = true;
657 this.visitChildren(node);
658 this.typeMode = false;
659 }
660 }
661}
662function analyzeScope(ast, parserOptions) {
663 var _a;
664 const options = {
665 ignoreEval: true,
666 optimistic: false,
667 directive: false,
668 nodejsScope: parserOptions.sourceType === 'script' &&
669 (parserOptions.ecmaFeatures &&
670 parserOptions.ecmaFeatures.globalReturn) === true,
671 impliedStrict: false,
672 sourceType: parserOptions.sourceType,
673 ecmaVersion: (_a = parserOptions.ecmaVersion) !== null && _a !== void 0 ? _a : 2018,
674 childVisitorKeys: typescript_estree_1.visitorKeys,
675 fallback: eslint_visitor_keys_1.getKeys,
676 };
677 const scopeManager = new scope_manager_1.ScopeManager(options);
678 const referencer = new Referencer(options, scopeManager);
679 referencer.visit(ast);
680 return scopeManager;
681}
682exports.analyzeScope = analyzeScope;
683//# sourceMappingURL=analyze-scope.js.map
\No newline at end of file