Skip to content

Commit b40def8

Browse files
authored
fix(parser): fix visiting props of TSDeclareFunction (typescript-eslint#244)
1 parent 7be5657 commit b40def8

File tree

7 files changed

+1101
-149
lines changed

7 files changed

+1101
-149
lines changed

Diff for: packages/eslint-plugin/tests/eslint-rules/no-undef.test.ts

+11
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,17 @@ export type SomeThing = {
5656
`
5757
export abstract class Foo {}
5858
export class FooBar extends Foo {}
59+
`,
60+
// https://github.com/typescript-eslint/typescript-eslint/issues/18
61+
`
62+
function eachr<Key, Value>(subject: Map<Key, Value>): typeof subject;
63+
function eachr(subject: Object | Array<Value>): typeof subject {
64+
return subject
65+
}
66+
`,
67+
// https://github.com/typescript-eslint/typescript-eslint/issues/18
68+
`
69+
function eachr<Key, Value>(subject: Map<Key, Value>): typeof subject;
5970
`
6071
],
6172
invalid: []

Diff for: packages/parser/src/analyze-scope.ts

+41-27
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
import { ScopeManager } from 'eslint-scope';
1+
import { ScopeManager } from './scope/scope-manager';
22
import { Definition, ParameterDefinition } from 'eslint-scope/lib/definition';
33
import OriginalPatternVisitor from 'eslint-scope/lib/pattern-visitor';
44
import Reference from 'eslint-scope/lib/reference';
55
import OriginalReferencer from 'eslint-scope/lib/referencer';
6-
import { Scope } from 'eslint-scope/lib/scope';
76
import { getKeys as fallback } from 'eslint-visitor-keys';
87
import { ParserOptions } from './parser-options';
98
import { visitorKeys as childVisitorKeys } from './visitor-keys';
@@ -30,18 +29,6 @@ function overrideDefine(define: any) {
3029
};
3130
}
3231

33-
/** The scope class for enum. */
34-
class EnumScope extends Scope {
35-
constructor(
36-
scopeManager: ScopeManager,
37-
upperScope: Scope,
38-
block: TSESTree.Node | null
39-
) {
40-
// @ts-ignore
41-
super(scopeManager, 'enum', upperScope, block, false);
42-
}
43-
}
44-
4532
class PatternVisitor extends OriginalPatternVisitor {
4633
constructor(
4734
options: PatternVisitorOptions,
@@ -99,7 +86,7 @@ class PatternVisitor extends OriginalPatternVisitor {
9986
}
10087
}
10188

102-
class Referencer extends OriginalReferencer {
89+
class Referencer extends OriginalReferencer<ScopeManager> {
10390
protected typeMode: boolean;
10491

10592
constructor(options: any, scopeManager: ScopeManager) {
@@ -176,13 +163,13 @@ class Referencer extends OriginalReferencer {
176163
scopeManager.__nestFunctionExpressionNameScope(node);
177164
}
178165

179-
// Process the type parameters
180-
this.visit(typeParameters);
181-
182166
// Open the function scope.
183167
scopeManager.__nestFunctionScope(node, this.isInnerMethodDefinition);
184168
const innerScope = this.currentScope();
185169

170+
// Process the type parameters
171+
this.visit(typeParameters);
172+
186173
// Process parameter declarations.
187174
for (let i = 0; i < params.length; ++i) {
188175
this.visitPattern(
@@ -344,29 +331,56 @@ class Referencer extends OriginalReferencer {
344331
* @param node The TSDeclareFunction node to visit.
345332
*/
346333
TSDeclareFunction(node: TSESTree.TSDeclareFunction): void {
347-
const upperTypeMode = this.typeMode;
348-
const scope = this.currentScope();
334+
const scopeManager = this.scopeManager;
335+
const upperScope = this.currentScope();
349336
const { id, typeParameters, params, returnType } = node;
350337

351338
// Ignore this if other overloadings have already existed.
352339
if (id) {
353-
const variable = scope.set.get(id.name);
340+
const variable = upperScope.set.get(id.name);
354341
const defs = variable && variable.defs;
355342
const existed = defs && defs.some(d => d.type === 'FunctionName');
356343
if (!existed) {
357-
scope.__define(
344+
upperScope.__define(
358345
id,
359346
new Definition('FunctionName', id, node, null, null, null)
360347
);
361348
}
362349
}
363350

364-
// Find `typeof` expressions.
365-
this.typeMode = true;
351+
// Open the function scope.
352+
scopeManager.__nestEmptyFunctionScope(node);
353+
const innerScope = this.currentScope();
354+
355+
// Process the type parameters
366356
this.visit(typeParameters);
367-
params.forEach(this.visit, this);
357+
358+
// Process parameter declarations.
359+
for (let i = 0; i < params.length; ++i) {
360+
this.visitPattern(
361+
params[i],
362+
{ processRightHandNodes: true },
363+
(pattern, info) => {
364+
innerScope.__define(
365+
pattern,
366+
new ParameterDefinition(pattern, node, i, info.rest)
367+
);
368+
369+
// Set `variable.eslintUsed` to tell ESLint that the variable is used.
370+
const variable = innerScope.set.get(pattern.name);
371+
if (variable) {
372+
variable.eslintUsed = true;
373+
}
374+
this.referencingDefaultValue(pattern, info.assignments, null, true);
375+
}
376+
);
377+
}
378+
379+
// Process the return type.
368380
this.visit(returnType);
369-
this.typeMode = upperTypeMode;
381+
382+
// Close the function scope.
383+
this.close(node);
370384
}
371385

372386
/**
@@ -645,7 +659,7 @@ class Referencer extends OriginalReferencer {
645659
scope.__define(id, new Definition('EnumName', id, node));
646660
}
647661

648-
scopeManager.__nestScope(new EnumScope(scopeManager, scope, node));
662+
scopeManager.__nestEnumScope(node);
649663
for (const member of members) {
650664
this.visit(member);
651665
}

Diff for: packages/parser/src/scope/scope-manager.ts

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { TSESTree } from '@typescript-eslint/typescript-estree';
2+
3+
import EslintScopeManager, {
4+
ScopeManagerOptions
5+
} from 'eslint-scope/lib/scope-manager';
6+
import { EmptyFunctionScope, EnumScope } from './scopes';
7+
import { Scope } from 'eslint-scope/lib/scope';
8+
9+
/**
10+
* based on eslint-scope
11+
*/
12+
export class ScopeManager extends EslintScopeManager {
13+
scopes!: Scope[];
14+
globalScope!: Scope;
15+
16+
constructor(options: ScopeManagerOptions) {
17+
super(options);
18+
}
19+
20+
/** @internal */
21+
__nestEnumScope(node: TSESTree.TSEnumDeclaration) {
22+
return this.__nestScope(new EnumScope(this, this.__currentScope, node));
23+
}
24+
25+
/** @internal */
26+
__nestEmptyFunctionScope(node: TSESTree.TSDeclareFunction) {
27+
return this.__nestScope(
28+
new EmptyFunctionScope(this, this.__currentScope, node)
29+
);
30+
}
31+
}

Diff for: packages/parser/src/scope/scopes.ts

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { Scope } from 'eslint-scope/lib/scope';
2+
import { ScopeManager } from './scope-manager';
3+
import { TSESTree } from '@typescript-eslint/typescript-estree';
4+
5+
/** The scope class for enum. */
6+
export class EnumScope extends Scope {
7+
constructor(
8+
scopeManager: ScopeManager,
9+
upperScope: Scope,
10+
block: TSESTree.TSEnumDeclaration | null
11+
) {
12+
super(scopeManager, 'enum', upperScope, block, false);
13+
}
14+
}
15+
16+
/** The scope class for empty functions. */
17+
export class EmptyFunctionScope extends Scope {
18+
constructor(
19+
scopeManager: ScopeManager,
20+
upperScope: Scope,
21+
block: TSESTree.TSDeclareFunction | null
22+
) {
23+
super(scopeManager, 'empty-function', upperScope, block, false);
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
function eachr<Key, Value>(subject: Map<Key, Value>): typeof subject;

0 commit comments

Comments
 (0)