Skip to content

Commit

Permalink
fix(parser): fix visiting props of TSDeclareFunction (#244)
Browse files Browse the repository at this point in the history
  • Loading branch information
armano2 committed Feb 12, 2019
1 parent 7be5657 commit b40def8
Show file tree
Hide file tree
Showing 7 changed files with 1,101 additions and 149 deletions.
11 changes: 11 additions & 0 deletions packages/eslint-plugin/tests/eslint-rules/no-undef.test.ts
Expand Up @@ -56,6 +56,17 @@ export type SomeThing = {
`
export abstract class Foo {}
export class FooBar extends Foo {}
`,
// https://github.com/typescript-eslint/typescript-eslint/issues/18
`
function eachr<Key, Value>(subject: Map<Key, Value>): typeof subject;
function eachr(subject: Object | Array<Value>): typeof subject {
return subject
}
`,
// https://github.com/typescript-eslint/typescript-eslint/issues/18
`
function eachr<Key, Value>(subject: Map<Key, Value>): typeof subject;
`
],
invalid: []
Expand Down
68 changes: 41 additions & 27 deletions packages/parser/src/analyze-scope.ts
@@ -1,9 +1,8 @@
import { ScopeManager } from 'eslint-scope';
import { ScopeManager } from './scope/scope-manager';
import { Definition, ParameterDefinition } from 'eslint-scope/lib/definition';
import OriginalPatternVisitor from 'eslint-scope/lib/pattern-visitor';
import Reference from 'eslint-scope/lib/reference';
import OriginalReferencer from 'eslint-scope/lib/referencer';
import { Scope } from 'eslint-scope/lib/scope';
import { getKeys as fallback } from 'eslint-visitor-keys';
import { ParserOptions } from './parser-options';
import { visitorKeys as childVisitorKeys } from './visitor-keys';
Expand All @@ -30,18 +29,6 @@ function overrideDefine(define: any) {
};
}

/** The scope class for enum. */
class EnumScope extends Scope {
constructor(
scopeManager: ScopeManager,
upperScope: Scope,
block: TSESTree.Node | null
) {
// @ts-ignore
super(scopeManager, 'enum', upperScope, block, false);
}
}

class PatternVisitor extends OriginalPatternVisitor {
constructor(
options: PatternVisitorOptions,
Expand Down Expand Up @@ -99,7 +86,7 @@ class PatternVisitor extends OriginalPatternVisitor {
}
}

class Referencer extends OriginalReferencer {
class Referencer extends OriginalReferencer<ScopeManager> {
protected typeMode: boolean;

constructor(options: any, scopeManager: ScopeManager) {
Expand Down Expand Up @@ -176,13 +163,13 @@ class Referencer extends OriginalReferencer {
scopeManager.__nestFunctionExpressionNameScope(node);
}

// Process the type parameters
this.visit(typeParameters);

// Open the function scope.
scopeManager.__nestFunctionScope(node, this.isInnerMethodDefinition);
const innerScope = this.currentScope();

// Process the type parameters
this.visit(typeParameters);

// Process parameter declarations.
for (let i = 0; i < params.length; ++i) {
this.visitPattern(
Expand Down Expand Up @@ -344,29 +331,56 @@ class Referencer extends OriginalReferencer {
* @param node The TSDeclareFunction node to visit.
*/
TSDeclareFunction(node: TSESTree.TSDeclareFunction): void {
const upperTypeMode = this.typeMode;
const scope = this.currentScope();
const scopeManager = this.scopeManager;
const upperScope = this.currentScope();
const { id, typeParameters, params, returnType } = node;

// Ignore this if other overloadings have already existed.
if (id) {
const variable = scope.set.get(id.name);
const variable = upperScope.set.get(id.name);
const defs = variable && variable.defs;
const existed = defs && defs.some(d => d.type === 'FunctionName');
if (!existed) {
scope.__define(
upperScope.__define(
id,
new Definition('FunctionName', id, node, null, null, null)
);
}
}

// Find `typeof` expressions.
this.typeMode = true;
// Open the function scope.
scopeManager.__nestEmptyFunctionScope(node);
const innerScope = this.currentScope();

// Process the type parameters
this.visit(typeParameters);
params.forEach(this.visit, this);

// Process parameter declarations.
for (let i = 0; i < params.length; ++i) {
this.visitPattern(
params[i],
{ processRightHandNodes: true },
(pattern, info) => {
innerScope.__define(
pattern,
new ParameterDefinition(pattern, node, i, info.rest)
);

// Set `variable.eslintUsed` to tell ESLint that the variable is used.
const variable = innerScope.set.get(pattern.name);
if (variable) {
variable.eslintUsed = true;
}
this.referencingDefaultValue(pattern, info.assignments, null, true);
}
);
}

// Process the return type.
this.visit(returnType);
this.typeMode = upperTypeMode;

// Close the function scope.
this.close(node);
}

/**
Expand Down Expand Up @@ -645,7 +659,7 @@ class Referencer extends OriginalReferencer {
scope.__define(id, new Definition('EnumName', id, node));
}

scopeManager.__nestScope(new EnumScope(scopeManager, scope, node));
scopeManager.__nestEnumScope(node);
for (const member of members) {
this.visit(member);
}
Expand Down
31 changes: 31 additions & 0 deletions packages/parser/src/scope/scope-manager.ts
@@ -0,0 +1,31 @@
import { TSESTree } from '@typescript-eslint/typescript-estree';

import EslintScopeManager, {
ScopeManagerOptions
} from 'eslint-scope/lib/scope-manager';
import { EmptyFunctionScope, EnumScope } from './scopes';
import { Scope } from 'eslint-scope/lib/scope';

/**
* based on eslint-scope
*/
export class ScopeManager extends EslintScopeManager {
scopes!: Scope[];
globalScope!: Scope;

constructor(options: ScopeManagerOptions) {
super(options);
}

/** @internal */
__nestEnumScope(node: TSESTree.TSEnumDeclaration) {
return this.__nestScope(new EnumScope(this, this.__currentScope, node));
}

/** @internal */
__nestEmptyFunctionScope(node: TSESTree.TSDeclareFunction) {
return this.__nestScope(
new EmptyFunctionScope(this, this.__currentScope, node)
);
}
}
25 changes: 25 additions & 0 deletions packages/parser/src/scope/scopes.ts
@@ -0,0 +1,25 @@
import { Scope } from 'eslint-scope/lib/scope';
import { ScopeManager } from './scope-manager';
import { TSESTree } from '@typescript-eslint/typescript-estree';

/** The scope class for enum. */
export class EnumScope extends Scope {
constructor(
scopeManager: ScopeManager,
upperScope: Scope,
block: TSESTree.TSEnumDeclaration | null
) {
super(scopeManager, 'enum', upperScope, block, false);
}
}

/** The scope class for empty functions. */
export class EmptyFunctionScope extends Scope {
constructor(
scopeManager: ScopeManager,
upperScope: Scope,
block: TSESTree.TSDeclareFunction | null
) {
super(scopeManager, 'empty-function', upperScope, block, false);
}
}
@@ -0,0 +1 @@
function eachr<Key, Value>(subject: Map<Key, Value>): typeof subject;

0 comments on commit b40def8

Please sign in to comment.