Fix type param and interface declaration scoping (babel/babel-eslint#449)

This commit is contained in:
Andres Suarez 2017-03-23 12:49:19 -04:00
parent 700f62e28e
commit 1c5400a670
2 changed files with 83 additions and 20 deletions

View File

@ -180,18 +180,6 @@ function monkeypatch() {
} }
} }
function visitTypeParameters(typeParameters) {
var params = typeParameters.params;
// visit bounds on polymorphpic types, eg; `Foo` in `fn<T: Foo>(a: T): T`
for (var i = 0; i < params.length; i++) {
var param = params[i];
if (param.typeAnnotation) {
visitTypeAnnotation.call(this, param.typeAnnotation);
}
}
}
function checkIdentifierOrVisit(node) { function checkIdentifierOrVisit(node) {
if (node.typeAnnotation) { if (node.typeAnnotation) {
visitTypeAnnotation.call(this, node.typeAnnotation); visitTypeAnnotation.call(this, node.typeAnnotation);
@ -209,6 +197,9 @@ function monkeypatch() {
for (var j = 0; j < node.typeParameters.params.length; j++) { for (var j = 0; j < node.typeParameters.params.length; j++) {
var name = node.typeParameters.params[j]; var name = node.typeParameters.params[j];
scope.__define(name, new Definition("TypeParameter", name, name)); scope.__define(name, new Definition("TypeParameter", name, name));
if (name.typeAnnotation) {
checkIdentifierOrVisit.call(this, name);
}
} }
scope.__define = function() { scope.__define = function() {
return parentScope.__define.apply(parentScope, arguments); return parentScope.__define.apply(parentScope, arguments);
@ -222,7 +213,7 @@ function monkeypatch() {
visitDecorators.call(this, node); visitDecorators.call(this, node);
var typeParamScope; var typeParamScope;
if (node.typeParameters) { if (node.typeParameters) {
typeParamScope = nestTypeParamScope(this.scopeManager, node); typeParamScope = nestTypeParamScope.call(this, this.scopeManager, node);
} }
// visit flow type: ClassImplements // visit flow type: ClassImplements
if (node.implements) { if (node.implements) {
@ -264,8 +255,7 @@ function monkeypatch() {
referencer.prototype.visitFunction = function(node) { referencer.prototype.visitFunction = function(node) {
var typeParamScope; var typeParamScope;
if (node.typeParameters) { if (node.typeParameters) {
typeParamScope = nestTypeParamScope(this.scopeManager, node); typeParamScope = nestTypeParamScope.call(this, this.scopeManager, node);
visitTypeParameters.call(this, node.typeParameters);
} }
if (node.returnType) { if (node.returnType) {
checkIdentifierOrVisit.call(this, node.returnType); checkIdentifierOrVisit.call(this, node.returnType);
@ -328,11 +318,27 @@ function monkeypatch() {
); );
} }
referencer.prototype.InterfaceDeclaration = function(node) {
createScopeVariable.call(this, node, node.id);
var typeParamScope;
if (node.typeParameters) {
typeParamScope = nestTypeParamScope.call(this, this.scopeManager, node);
}
// TODO: Handle mixins
for (var i = 0; i < node.extends.length; i++) {
visitTypeAnnotation.call(this, node.extends[i]);
}
visitTypeAnnotation.call(this, node.body);
if (typeParamScope) {
this.close(node);
}
};
referencer.prototype.TypeAlias = function(node) { referencer.prototype.TypeAlias = function(node) {
createScopeVariable.call(this, node, node.id); createScopeVariable.call(this, node, node.id);
var typeParamScope; var typeParamScope;
if (node.typeParameters) { if (node.typeParameters) {
typeParamScope = nestTypeParamScope(this.scopeManager, node); typeParamScope = nestTypeParamScope.call(this, this.scopeManager, node);
} }
if (node.right) { if (node.right) {
visitTypeAnnotation.call(this, node.right); visitTypeAnnotation.call(this, node.right);
@ -352,7 +358,7 @@ function monkeypatch() {
var typeParamScope; var typeParamScope;
if (node.typeParameters) { if (node.typeParameters) {
typeParamScope = nestTypeParamScope(this.scopeManager, node); typeParamScope = nestTypeParamScope.call(this, this.scopeManager, node);
} }
if (typeParamScope) { if (typeParamScope) {
this.close(node); this.close(node);

View File

@ -222,16 +222,73 @@ describe("verify", () => {
); );
}); });
it("type parameter bounds", () => { it("interface declaration", () => {
verifyAndAssertMessages(
unpad(`
interface Foo {};
interface Bar {
foo: Foo,
};
`),
{ "no-unused-vars": 1, "no-undef": 1 },
[ "2:11 'Bar' is defined but never used. no-unused-vars" ]
);
});
it("type parameter bounds (classes)", () => {
verifyAndAssertMessages(
unpad(`
import type {Foo, Foo2} from 'foo';
import Base from 'base';
class Log<T1: Foo, T2: Foo2, T3, T4> extends Base<T3> {
messages: {[T1]: T2};
}
new Log();
`),
{ "no-unused-vars": 1, "no-undef": 1 },
[ "3:34 'T4' is defined but never used. no-unused-vars" ]
);
});
it("type parameter bounds (interfaces)", () => {
verifyAndAssertMessages(
unpad(`
import type {Foo, Foo2, Bar} from '';
interface Log<T1: Foo, T2: Foo2, T3, T4> extends Bar<T3> {
messages: {[T1]: T2};
}
`),
{ "no-unused-vars": 1, "no-undef": 1 },
[ "2:11 'Log' is defined but never used. no-unused-vars",
"2:38 'T4' is defined but never used. no-unused-vars" ]
);
});
it("type parameter bounds (type aliases)", () => {
verifyAndAssertMessages(
unpad(`
import type {Foo, Foo2, Foo3} from 'foo';
type Log<T1: Foo, T2: Foo2, T3> = {
messages: {[T1]: T2};
delay: Foo3;
};
`),
{ "no-unused-vars": 1, "no-undef": 1 },
[ "2:6 'Log' is defined but never used. no-unused-vars",
"2:29 'T3' is defined but never used. no-unused-vars" ]
);
});
it("type parameter bounds (functions)", () => {
verifyAndAssertMessages( verifyAndAssertMessages(
unpad(` unpad(`
import type Foo from 'foo'; import type Foo from 'foo';
import type Foo2 from 'foo'; import type Foo2 from 'foo';
function log<T1: Foo, T2: Foo2>(a: T1, b: T2) { return a + b; } function log<T1: Foo, T2: Foo2, T3, T4>(a: T1, b: T2): T3 { return a + b; }
log(1, 2); log(1, 2);
`), `),
{ "no-unused-vars": 1, "no-undef": 1 }, { "no-unused-vars": 1, "no-undef": 1 },
[] [ "3:37 'T4' is defined but never used. no-unused-vars" ]
); );
}); });