Skip to content

fix(parser): fix visiting props of TSDeclareFunction #244

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Feb 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions packages/eslint-plugin/tests/eslint-rules/no-undef.test.ts
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
function eachr<Key, Value>(subject: Map<Key, Value>): typeof subject;
Loading