From a399282a25484ed1f909bcd6edc87a14fafea1e4 Mon Sep 17 00:00:00 2001 From: Henry Zhu Date: Thu, 21 May 2015 08:41:20 -0400 Subject: [PATCH 1/8] visit most flow types - fixes #108 --- index.js | 77 ++++++++++++++++++++++++++++++++++-- test/non-regression.js | 88 +++++++++++++++++++++++++++++++++++------- 2 files changed, 149 insertions(+), 16 deletions(-) diff --git a/index.js b/index.js index 613d1fc7..78e443eb 100644 --- a/index.js +++ b/index.js @@ -76,19 +76,90 @@ function monkeypatch() { } } - // monkeypatch referencer methods to visit decorators + // visit decorators that are in: ClassDeclaration / ClassExpression var visitClass = referencer.prototype.visitClass; referencer.prototype.visitClass = function (node) { - // visit decorators that are in: Class Declaration visitDecorators.call(this, node); visitClass.call(this, node); } + // visit decorators that are in: Property / MethodDefinition var visitProperty = referencer.prototype.visitProperty; referencer.prototype.visitProperty = function (node) { - // visit decorators that are in: Visit Property / MethodDefinition visitDecorators.call(this, node); visitProperty.call(this, node); } + + function visitTypeAnnotation(node) { + // visit function type parameters + if (node.typeParameters) { + node.typeParameters.params.forEach(function (p) { + if (p.type === 'Identifier') { + this.visit(p); + } else { + visitTypeAnnotation.call(this, p); + } + }.bind(this)); + } + + // visit function parameters + if (node.params) { + for (var i = 0; i < node.params.length; i++) { + visitTypeAnnotation.call(this, node.params[i]); + } + } + + // visit return type + if (node.returnType) { + visitTypeAnnotation.call(this, node.returnType); + } + + // visit type + if (node.typeAnnotation) { + visitTypeAnnotation.call(this, node.typeAnnotation); + } + + // visit rest + if (t.isFunctionTypeAnnotation(node) && node.rest) { + visitTypeAnnotation.call(this, node.rest); + } + + // object properties - properties + if (t.isObjectTypeAnnotation(node) && node.properties) { + for (var i = 0; i < node.properties.length; i++) { + visitTypeAnnotation.call(this, node.properties[i].value); + } + } + + // object properties - indexers + if (t.isObjectTypeAnnotation(node) && node.indexers) { + for (var i = 0; i < node.indexers.length; i++) { + visitTypeAnnotation.call(this, node.indexers[i].key); + visitTypeAnnotation.call(this, node.indexers[i].value); + } + } + + if (node.id) { + this.visit(node.id); + } + } + + // visit flow type in FunctionDeclaration, FunctionExpression, ArrowFunctionExpression + var visitFunction = referencer.prototype.visitFunction; + referencer.prototype.visitFunction = function (node) { + visitTypeAnnotation.call(this, node); + visitFunction.call(this, node); + } + + // visit flow type in VariableDeclaration + var variableDeclaration = referencer.prototype.VariableDeclaration; + referencer.prototype.VariableDeclaration = function (node) { + if (node.declarations) { + node.declarations.forEach(function (d) { + visitTypeAnnotation.call(this, d.id); + }.bind(this)); + } + variableDeclaration.call(this, node); + } } exports.attachComments = function (ast, comments, tokens) { diff --git a/test/non-regression.js b/test/non-regression.js index 1b2a2a8a..53a7b16f 100644 --- a/test/non-regression.js +++ b/test/non-regression.js @@ -124,20 +124,82 @@ describe("verify", function () { ); }); - it("flow type", function () { - verifyAndAssertMessages( - "type SomeNewType = any;", - {}, - [] - ); - }); + describe("flow", function () { + it("type alias", function () { + verifyAndAssertMessages( + "type SomeNewType = any;", + {}, + [] + ); + }); - it("type cast expression #102", function () { - verifyAndAssertMessages( - "for (let a of (a: Array)) {}", - {}, - [] - ); + it("type cast expression #102", function () { + verifyAndAssertMessages( + "for (let a of (a: Array)) {}", + {}, + [] + ); + }); + + it("multiple nullable type annotations and return #108", function () { + verifyAndAssertMessages([ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "import type Foo3 from 'foo';", + "function log(foo: ?Foo, foo2: ?Foo2): ?Foo3 {", + "console.log(foo, foo2);", + "}", + "log(1, 2);" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("type parameters #108", function () { + verifyAndAssertMessages([ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "function log() {}", + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("nested type annotations #108", function () { + verifyAndAssertMessages([ + "import type Foo from 'foo';", + "function foo(callback: () => Foo){", + "return callback();", + "}" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("type in var declaration #108", function () { + verifyAndAssertMessages([ + "import type Foo from 'foo';", + "var x: Foo = 1;", + "x;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("object type annotation #108", function () { + verifyAndAssertMessages([ + "import type Foo from 'foo';", + "var a: {numVal: Foo};", + "a;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1}, + [] + ); + }); }); it("class usage", function () { From 651ae9244c3498bf52efef77aa8120f1ea96e87f Mon Sep 17 00:00:00 2001 From: Henry Zhu Date: Thu, 21 May 2015 23:14:09 -0400 Subject: [PATCH 2/8] refactor, flow fixes, visit var dec, class dec, tests --- acorn-to-esprima.js | 17 ++-- index.js | 192 ++++++++++++++++++++++++++++------------- test/non-regression.js | 85 ++++++++++++++++-- 3 files changed, 221 insertions(+), 73 deletions(-) diff --git a/acorn-to-esprima.js b/acorn-to-esprima.js index d8ac33df..a4e005e4 100644 --- a/acorn-to-esprima.js +++ b/acorn-to-esprima.js @@ -117,7 +117,7 @@ function convertTemplateType(tokens) { // create Template token function replaceWithTemplateType(start, end) { var templateToken = { - type: 'Template', + type: "Template", value: createTemplateValue(start, end), range: [tokens[start].start, tokens[end].end], loc: { @@ -183,16 +183,21 @@ var astTransformVisitor = { delete node.argument; } + if (this.isRestElement()) { + return node.argument; + } + if (this.isTypeCastExpression()) { return node.expression; } - if (this.isFlow()) { - return this.remove(); - } + // flow - if (this.isRestElement()) { - return node.argument; + if (this.isDeclareModule() || + this.isDeclareClass() || + this.isDeclareFunction() || + this.isDeclareVariable()) { + return this.remove(); } // modules diff --git a/index.js b/index.js index 78e443eb..15a2e64a 100644 --- a/index.js +++ b/index.js @@ -48,10 +48,10 @@ function monkeypatch() { opts.sourceType = "module"; // Don't visit TypeAlias when analyzing scope, but retain them for other // eslint rules. - var TypeAliasKeys = estraverse.VisitorKeys.TypeAlias; - estraverse.VisitorKeys.TypeAlias = []; + // var TypeAliasKeys = estraverse.VisitorKeys.TypeAlias; + // estraverse.VisitorKeys.TypeAlias = []; var results = analyze.call(this, ast, opts); - estraverse.VisitorKeys.TypeAlias = TypeAliasKeys; + // estraverse.VisitorKeys.TypeAlias = TypeAliasKeys; return results; }; @@ -62,8 +62,18 @@ function monkeypatch() { } catch (err) { throw new ReferenceError("couldn't resolve escope/referencer"); } + var referencerMod = createModule(referencerLoc); var referencer = require(referencerLoc); + // reference Defition + var definitionLoc; + try { + var definitionLoc = Module._resolveFilename("./definition", referencerMod); + } catch (err) { + throw new ReferenceError("couldn't resolve escope/definition"); + } + var Definition = require(definitionLoc).Definition; + // if there are decotators, then visit each function visitDecorators(node) { if (!node.decorators) { @@ -76,89 +86,151 @@ function monkeypatch() { } } - // visit decorators that are in: ClassDeclaration / ClassExpression - var visitClass = referencer.prototype.visitClass; - referencer.prototype.visitClass = function (node) { - visitDecorators.call(this, node); - visitClass.call(this, node); - } - // visit decorators that are in: Property / MethodDefinition - var visitProperty = referencer.prototype.visitProperty; - referencer.prototype.visitProperty = function (node) { - visitDecorators.call(this, node); - visitProperty.call(this, node); - } - function visitTypeAnnotation(node) { - // visit function type parameters if (node.typeParameters) { - node.typeParameters.params.forEach(function (p) { - if (p.type === 'Identifier') { - this.visit(p); + node.typeParameters.params.forEach(function(p) { + checkIdentifierOrVisit.call(this, p); + }.bind(this)); + } else if (t.isTypeAnnotation(node) || node.typeAnnotation) { + visitTypeAnnotation.call(this, node.typeAnnotation); + } else if (t.isArrayTypeAnnotation(node)) { + checkIdentifierOrVisit.call(this, node.elementType); + } else if (t.isFunctionTypeAnnotation(node)) { + if (node.returnType) { + checkIdentifierOrVisit.call(this, node.returnType); + } + if (node.rest) { + checkIdentifierOrVisit.call(this, node.rest); + } + if (node.params) { + node.params.forEach(function(p) { + checkIdentifierOrVisit.call(this, p); + }.bind(this)); + } + } else if (t.isGenericTypeAnnotation(node)) { + if (node.id) { + if (node.id.type === "Identifier") { + checkIdentifierOrVisit.call(this, node.id); } else { - visitTypeAnnotation.call(this, p); + visitTypeAnnotation.call(this, node.id); } - }.bind(this)); - } - - // visit function parameters - if (node.params) { - for (var i = 0; i < node.params.length; i++) { - visitTypeAnnotation.call(this, node.params[i]); + } else if (node.typeParameters) { + checkIdentifierOrVisit.call(this, node.typeParameters); } + } else if (t.isObjectTypeAnnotation(node)) { + if (node.callProperties) { + node.callProperties.forEach(function(p) { + checkIdentifierOrVisit.call(this, p.value); + }.bind(this)); + } + if (node.indexers) { + node.indexers.forEach(function(p) { + checkIdentifierOrVisit.call(this, p.key); + checkIdentifierOrVisit.call(this, p.value); + }.bind(this)); + } + if (node.properties) { + node.properties.forEach(function(p) { + checkIdentifierOrVisit.call(this, p.value); + }.bind(this)); + } + } else if (t.isQualifiedTypeIdentifier(node)) { + // only visit node.qualification, not node.id + checkIdentifierOrVisit.call(this, node.qualification); + } else if (t.isTypeofTypeAnnotation(node)) { + checkIdentifierOrVisit.call(this, node.argument); + } else if (t.isTypeParameterDeclaration(node) || + t.isTypeParameterInstantiation(node)) { + node.params.forEach(function(p) { + checkIdentifierOrVisit.call(this, p); + }.bind(this)); + } else if (t.isIntersectionTypeAnnotation(node) || + t.isTupleTypeAnnotation(node) || + t.isUnionTypeAnnotation(node)) { + node.types.forEach(function(t) { + checkIdentifierOrVisit.call(this, t); + }.bind(this)); } + } - // visit return type - if (node.returnType) { - visitTypeAnnotation.call(this, node.returnType); - } - - // visit type + function checkIdentifierOrVisit(node) { if (node.typeAnnotation) { visitTypeAnnotation.call(this, node.typeAnnotation); + } else if (node.type === "Identifier") { + this.visit(node); + } else if (node.id && node.id.type === "Identifier") { + this.visit(node.id); } + } - // visit rest - if (t.isFunctionTypeAnnotation(node) && node.rest) { - visitTypeAnnotation.call(this, node.rest); - } - - // object properties - properties - if (t.isObjectTypeAnnotation(node) && node.properties) { - for (var i = 0; i < node.properties.length; i++) { - visitTypeAnnotation.call(this, node.properties[i].value); - } - } - - // object properties - indexers - if (t.isObjectTypeAnnotation(node) && node.indexers) { - for (var i = 0; i < node.indexers.length; i++) { - visitTypeAnnotation.call(this, node.indexers[i].key); - visitTypeAnnotation.call(this, node.indexers[i].value); - } - } - + // visit decorators that are in: ClassDeclaration / ClassExpression + var visitClass = referencer.prototype.visitClass; + referencer.prototype.visitClass = function(node) { + visitDecorators.call(this, node); + // visit class if (node.id) { this.visit(node.id); } - } + // visit flow type: ClassImplements + if (node.implements) { + node.implements.forEach(function(i) { + checkIdentifierOrVisit.call(this, i); + }.bind(this)); + } + visitClass.call(this, node); + }; + // visit decorators that are in: Property / MethodDefinition + var visitProperty = referencer.prototype.visitProperty; + referencer.prototype.visitProperty = function(node) { + visitDecorators.call(this, node); + visitProperty.call(this, node); + }; // visit flow type in FunctionDeclaration, FunctionExpression, ArrowFunctionExpression var visitFunction = referencer.prototype.visitFunction; - referencer.prototype.visitFunction = function (node) { - visitTypeAnnotation.call(this, node); + referencer.prototype.visitFunction = function(node) { + if (node.returnType) { + checkIdentifierOrVisit.call(this, node.returnType); + } + // only visit if function parameters have types + if (node.params) { + node.params.forEach(function(p) { + if (p.typeAnnotation) { + checkIdentifierOrVisit.call(this, p); + } + }.bind(this)); + } + if (node.typeParameters) { + node.typeParameters.params.forEach(function(p) { + checkIdentifierOrVisit.call(this, p); + }.bind(this)); + } visitFunction.call(this, node); - } + }; // visit flow type in VariableDeclaration var variableDeclaration = referencer.prototype.VariableDeclaration; - referencer.prototype.VariableDeclaration = function (node) { + referencer.prototype.VariableDeclaration = function(node) { if (node.declarations) { - node.declarations.forEach(function (d) { + node.declarations.forEach(function(d) { visitTypeAnnotation.call(this, d.id); }.bind(this)); } variableDeclaration.call(this, node); + }; + + referencer.prototype.TypeAlias = function(node) { + this.currentScope().__define( + node.id, + new Definition( + "Variable", + node.id, + node, + null, + null, + null + ) + ); } } diff --git a/test/non-regression.js b/test/non-regression.js index 53a7b16f..19bb54af 100644 --- a/test/non-regression.js +++ b/test/non-regression.js @@ -125,10 +125,19 @@ describe("verify", function () { }); describe("flow", function () { + it("check regular function", function () { + verifyAndAssertMessages([ + "function a(b, c) { b += 1; c += 1; } a;", + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1}, + [] + ); + }); + it("type alias", function () { verifyAndAssertMessages( "type SomeNewType = any;", - {}, + { "no-undef": 1 }, [] ); }); @@ -156,30 +165,32 @@ describe("verify", function () { ); }); - it("type parameters #108", function () { + it("type parameters", function () { verifyAndAssertMessages([ "import type Foo from 'foo';", "import type Foo2 from 'foo';", "function log() {}", + "log();" ].join("\n"), { "no-unused-vars": 1, "no-undef": 1 }, [] ); }); - it("nested type annotations #108", function () { + it("nested type annotations", function () { verifyAndAssertMessages([ "import type Foo from 'foo';", - "function foo(callback: () => Foo){", + "function foo(callback: () => Foo) {", "return callback();", - "}" + "}", + "foo();" ].join("\n"), { "no-unused-vars": 1, "no-undef": 1 }, [] ); }); - it("type in var declaration #108", function () { + it("type in var declaration", function () { verifyAndAssertMessages([ "import type Foo from 'foo';", "var x: Foo = 1;", @@ -190,7 +201,7 @@ describe("verify", function () { ); }); - it("object type annotation #108", function () { + it("object type annotation", function () { verifyAndAssertMessages([ "import type Foo from 'foo';", "var a: {numVal: Foo};", @@ -200,6 +211,66 @@ describe("verify", function () { [] ); }); + + it("object property types", function () { + verifyAndAssertMessages([ + "var a = {", + "circle: (null : ?{ setNativeProps(props: Object): void })", + "};", + "a;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1}, + [] + ); + }); + + it("namespaced types", function () { + verifyAndAssertMessages([ + "var React = require('react-native');", + "var b = {", + "openExternalExample: (null: ?React.Component)", + "};", + "var c = {", + "render(): React.Component {}", + "};", + "b;", + "c;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1}, + [] + ); + }); + + it("ArrayTypeAnnotation", function () { + verifyAndAssertMessages([ + "import type Foo from 'foo';", + "var x: Foo[]; x;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1}, + [] + ); + }); + + it("ClassImplements", function () { + verifyAndAssertMessages([ + "import type Foo from 'foo';", + "import type Bar from 'foo';", + "class Foo implements Bar {}" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1}, + [] + ); + }); + + it("type alias creates declaration + usage", function () { + verifyAndAssertMessages([ + "type Foo = any;", + "var x : Foo = 1; x;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1}, + [] + ); + }); }); it("class usage", function () { From 807854243b5a5bdde476b593904e655c641413b0 Mon Sep 17 00:00:00 2001 From: Henry Zhu Date: Mon, 25 May 2015 12:08:49 -0400 Subject: [PATCH 3/8] refactor to make adding future types simplier --- index.js | 152 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 95 insertions(+), 57 deletions(-) diff --git a/index.js b/index.js index 15a2e64a..a2695f08 100644 --- a/index.js +++ b/index.js @@ -65,7 +65,7 @@ function monkeypatch() { var referencerMod = createModule(referencerLoc); var referencer = require(referencerLoc); - // reference Defition + // reference Definition var definitionLoc; try { var definitionLoc = Module._resolveFilename("./definition", referencerMod); @@ -86,70 +86,108 @@ function monkeypatch() { } } + // part of t.VISITOR_KEYS; + var visitorKeysMap = { + // Others + "ArrayPattern": ["elements", "typeAnnotation"], + "ClassDeclaration": ["id", "body", "superClass", "typeParameters", "superTypeParameters", "implements", "decorators"], + "ClassExpression": ["id", "body", "superClass", "typeParameters", "superTypeParameters", "implements", "decorators"], + "FunctionDeclaration": ["id", "params", "body", "returnType", "typeParameters"], + "FunctionExpression": ["id", "params", "body", "returnType", "typeParameters"], + "Identifier": ["typeAnnotation"], + "ObjectPattern": ["properties", "typeAnnotation"], + "RestElement": ["argument", "typeAnnotation"], + + // Flow specific + "ArrayTypeAnnotation": ["elementType"], + "ClassImplements": ["id", "typeParameters"], + "ClassProperty": ["key", "value", "typeAnnotation", "decorators"], + "DeclareClass": ["id", "typeParameters", "extends", "body"], + "DeclareFunction": ["id"], + "DeclareModule": ["id", "body"], + "DeclareVariable": ["id"], + "FunctionTypeAnnotation": ["typeParameters", "params", "rest", "returnType"], + "FunctionTypeParam": ["name", "typeAnnotation"], + "GenericTypeAnnotation": ["id", "typeParameters"], + "InterfaceExtends": ["id", "typeParameters"], + "InterfaceDeclaration": ["id", "typeParameters", "extends", "body"], + "IntersectionTypeAnnotation": ["types"], + "NullableTypeAnnotation": ["typeAnnotation"], + "TupleTypeAnnotation": ["types"], + "TypeofTypeAnnotation": ["argument"], + "TypeAlias": ["id", "typeParameters", "right"], + "TypeAnnotation": ["typeAnnotation"], + "TypeCastExpression": ["expression", "typeAnnotation"], + "TypeParameterDeclaration": ["params"], + "TypeParameterInstantiation": ["params"], + "ObjectTypeAnnotation": ["properties", "indexers", "callProperties"], + "ObjectTypeCallProperty": ["value"], + "ObjectTypeIndexer": ["id", "key", "value"], + "ObjectTypeProperty": ["key", "value"], + "QualifiedTypeIdentifier": ["qualification"], // don't check "id"? + "UnionTypeAnnotation": ["types"] + }; + + var propertyTypes = { + // loops + callProperties: { type: "loop", values: ["value"] }, + indexers: { type: "loop", values: ["key", "value"] }, + properties: { type: "loop", values: ["value"] }, + types: { type: "loop" }, + params: { type: "loop" }, + // single property + argument: { type: "single" }, + elementType: { type: "single" }, + qualification: { type: "single" }, + rest: { type: "single" }, + returnType: { type: "single" }, + // others + typeAnnotation: { type: "typeAnnotation" }, + typeParameters: { type: "typeParameters" }, + id: { type: "id" } + }; + function visitTypeAnnotation(node) { - if (node.typeParameters) { - node.typeParameters.params.forEach(function(p) { - checkIdentifierOrVisit.call(this, p); - }.bind(this)); - } else if (t.isTypeAnnotation(node) || node.typeAnnotation) { - visitTypeAnnotation.call(this, node.typeAnnotation); - } else if (t.isArrayTypeAnnotation(node)) { - checkIdentifierOrVisit.call(this, node.elementType); - } else if (t.isFunctionTypeAnnotation(node)) { - if (node.returnType) { - checkIdentifierOrVisit.call(this, node.returnType); - } - if (node.rest) { - checkIdentifierOrVisit.call(this, node.rest); - } - if (node.params) { - node.params.forEach(function(p) { - checkIdentifierOrVisit.call(this, p); - }.bind(this)); + // get property to check (params, id, etc...) + var visitorValues = visitorKeysMap[node.type]; + if (!visitorValues) { + return; + } + + // can have multiple properties + for (var i = 0; i < visitorValues.length; i++) { + var visitorValue = visitorValues[i]; + var propertyType = propertyTypes[visitorValue]; + var nodeProperty = node[visitorValue]; + // check if property or type is defined + if (!propertyType || !nodeProperty) { + continue; } - } else if (t.isGenericTypeAnnotation(node)) { - if (node.id) { + if (propertyType.type === "loop") { + for (var j = 0; j < nodeProperty.length; j++) { + if (Array.isArray(propertyType.values)) { + for (var k = 0; k < propertyType.values.length; k++) { + checkIdentifierOrVisit.call(this, nodeProperty[j][propertyType.values[k]]); + } + } else { + checkIdentifierOrVisit.call(this, nodeProperty[j]); + } + } + } else if (propertyType.type === "single") { + checkIdentifierOrVisit.call(this, nodeProperty); + } else if (propertyType.type === "typeAnnotation") { + visitTypeAnnotation.call(this, node.typeAnnotation); + } else if (propertyType.type === "typeParameters") { + for (var j = 0; j < node.typeParameters.params.length; j++) { + checkIdentifierOrVisit.call(this, node.typeParameters.params[j]); + } + } else if (propertyType.type === "id") { if (node.id.type === "Identifier") { checkIdentifierOrVisit.call(this, node.id); } else { visitTypeAnnotation.call(this, node.id); } - } else if (node.typeParameters) { - checkIdentifierOrVisit.call(this, node.typeParameters); - } - } else if (t.isObjectTypeAnnotation(node)) { - if (node.callProperties) { - node.callProperties.forEach(function(p) { - checkIdentifierOrVisit.call(this, p.value); - }.bind(this)); } - if (node.indexers) { - node.indexers.forEach(function(p) { - checkIdentifierOrVisit.call(this, p.key); - checkIdentifierOrVisit.call(this, p.value); - }.bind(this)); - } - if (node.properties) { - node.properties.forEach(function(p) { - checkIdentifierOrVisit.call(this, p.value); - }.bind(this)); - } - } else if (t.isQualifiedTypeIdentifier(node)) { - // only visit node.qualification, not node.id - checkIdentifierOrVisit.call(this, node.qualification); - } else if (t.isTypeofTypeAnnotation(node)) { - checkIdentifierOrVisit.call(this, node.argument); - } else if (t.isTypeParameterDeclaration(node) || - t.isTypeParameterInstantiation(node)) { - node.params.forEach(function(p) { - checkIdentifierOrVisit.call(this, p); - }.bind(this)); - } else if (t.isIntersectionTypeAnnotation(node) || - t.isTupleTypeAnnotation(node) || - t.isUnionTypeAnnotation(node)) { - node.types.forEach(function(t) { - checkIdentifierOrVisit.call(this, t); - }.bind(this)); } } From d04be47236b6808681ce710763b5fb786ffe8953 Mon Sep 17 00:00:00 2001 From: Henry Zhu Date: Mon, 1 Jun 2015 17:33:51 -0400 Subject: [PATCH 4/8] add a lot more tests --- index.js | 16 +- test/non-regression.js | 593 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 599 insertions(+), 10 deletions(-) diff --git a/index.js b/index.js index a2695f08..bf512f3f 100644 --- a/index.js +++ b/index.js @@ -160,7 +160,7 @@ function monkeypatch() { var propertyType = propertyTypes[visitorValue]; var nodeProperty = node[visitorValue]; // check if property or type is defined - if (!propertyType || !nodeProperty) { + if (propertyType == null || nodeProperty == null) { continue; } if (propertyType.type === "loop") { @@ -196,8 +196,8 @@ function monkeypatch() { visitTypeAnnotation.call(this, node.typeAnnotation); } else if (node.type === "Identifier") { this.visit(node); - } else if (node.id && node.id.type === "Identifier") { - this.visit(node.id); + } else { + visitTypeAnnotation.call(this, node); } } @@ -215,6 +215,16 @@ function monkeypatch() { checkIdentifierOrVisit.call(this, i); }.bind(this)); } + if (node.typeParameters) { + node.typeParameters.params.forEach(function(p) { + checkIdentifierOrVisit.call(this, p); + }.bind(this)); + } + if (node.superTypeParameters) { + node.superTypeParameters.params.forEach(function(p) { + checkIdentifierOrVisit.call(this, p); + }.bind(this)); + } visitClass.call(this, node); }; // visit decorators that are in: Property / MethodDefinition diff --git a/test/non-regression.js b/test/non-regression.js index 19bb54af..ee4991aa 100644 --- a/test/non-regression.js +++ b/test/non-regression.js @@ -129,7 +129,7 @@ describe("verify", function () { verifyAndAssertMessages([ "function a(b, c) { b += 1; c += 1; } a;", ].join("\n"), - { "no-unused-vars": 1, "no-undef": 1}, + { "no-unused-vars": 1, "no-undef": 1 }, [] ); }); @@ -207,7 +207,7 @@ describe("verify", function () { "var a: {numVal: Foo};", "a;" ].join("\n"), - { "no-unused-vars": 1, "no-undef": 1}, + { "no-unused-vars": 1, "no-undef": 1 }, [] ); }); @@ -219,7 +219,7 @@ describe("verify", function () { "};", "a;" ].join("\n"), - { "no-unused-vars": 1, "no-undef": 1}, + { "no-unused-vars": 1, "no-undef": 1 }, [] ); }); @@ -236,7 +236,7 @@ describe("verify", function () { "b;", "c;" ].join("\n"), - { "no-unused-vars": 1, "no-undef": 1}, + { "no-unused-vars": 1, "no-undef": 1 }, [] ); }); @@ -246,7 +246,7 @@ describe("verify", function () { "import type Foo from 'foo';", "var x: Foo[]; x;" ].join("\n"), - { "no-unused-vars": 1, "no-undef": 1}, + { "no-unused-vars": 1, "no-undef": 1 }, [] ); }); @@ -257,7 +257,7 @@ describe("verify", function () { "import type Bar from 'foo';", "class Foo implements Bar {}" ].join("\n"), - { "no-unused-vars": 1, "no-undef": 1}, + { "no-unused-vars": 1, "no-undef": 1 }, [] ); }); @@ -267,7 +267,586 @@ describe("verify", function () { "type Foo = any;", "var x : Foo = 1; x;" ].join("\n"), - { "no-unused-vars": 1, "no-undef": 1}, + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("1", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "export default function(a: Foo, b: ?Foo2, c){ a; b; c; }" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("2", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "export default function(a: () => Foo){ a; }" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("3", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "export default function(a: (_:Foo) => Foo2){ a; }" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("4", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "import type Foo3 from 'foo';", + "export default function(a: (_1:Foo, _2:Foo2) => Foo3){ a; }" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("5", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "export default function(a: (_1:Foo, ...foo:Array) => number){ a; }" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("6", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "export default function(): Foo {}" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("7", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "export default function():() => Foo {}" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("8", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "export default function():(_?:Foo) => Foo2{}" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("9", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "export default function () {}" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }) + + it("10", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "var a=function() {}; a;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("11", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "import type Foo3 from 'foo';", + "var a={*id(x: Foo2): Foo3 { x; }}; a;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("12", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "import type Foo3 from 'foo';", + "var a={async id(x: Foo2): Foo3 { x; }}; a;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("13", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "import type Foo3 from 'foo';", + "var a={123(x: Foo2): Foo3 { x; }}; a;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("14", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "class Bar {set fooProp(value:Foo):Foo2{ value; }}" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("15", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "class Foo {get fooProp():Foo{}}" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("16", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "var numVal:Foo; numVal;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("17", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "var a: {numVal: Foo;}; a;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("18", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "import type Foo3 from 'foo';", + "var a: ?{numVal: Foo; [indexer: Foo2]: Foo3}; a;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("19", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "var a: {numVal: Foo; subObj?: ?{strVal: Foo2}}; a;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("20", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "import type Foo3 from 'foo';", + "import type Foo4 from 'foo';", + "var a: { [a: Foo]: Foo2; [b: Foo3]: Foo4; }; a;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("21", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "import type Foo3 from 'foo';", + "var a: {add(x:Foo, ...y:Array): Foo3}; a;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("22", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "import type Foo3 from 'foo';", + "var a: { id(x: Foo2): Foo3; }; a;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("23", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "var a:Array = [1, 2, 3]; a;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("24", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "import Baz from 'foo';", + "class Bar extends Baz { };" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("25", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "import type Foo3 from 'foo';", + "class Bar { bar():Foo3 { return 42; }}" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("26", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "class Bar { static prop1:Foo; prop2:Foo2; }" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("27", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "var x : Foo | Foo2 = 4; x;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("28", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "var x : () => Foo | () => Foo2; x;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("29", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "var x: typeof Foo | number = Foo2; x;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("30", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + 'var {x}: {x: Foo; } = { x: "hello" }; x;' + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("31", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + 'var [x]: Array = [ "hello" ]; x;' + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("32", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "export default function({x}: { x: Foo; }) {}" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("33", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "function foo([x]: Array) { x; } foo();" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("34", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "var a: Map >; a;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("35", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "var a: ?Promise[]; a;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("36", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "var a:(...rest:Array) => Foo2; a;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("37", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "import type Foo3 from 'foo';", + "import type Foo4 from 'foo';", + "var a: (x: Foo2, ...y:Foo3[]) => Foo4; a;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("38", function () { + verifyAndAssertMessages( + [ + 'import type {foo, bar} from "baz";', + 'foo; bar;' + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("39", function () { + verifyAndAssertMessages( + [ + 'import type {foo as bar} from "baz";', + 'bar;' + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("40", function () { + verifyAndAssertMessages( + [ + 'import type from "foo";', + 'type;' + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("41", function () { + verifyAndAssertMessages( + [ + 'import type, {foo} from "bar";', + 'type; foo;' + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("42", function () { + verifyAndAssertMessages( + [ + 'import type * as namespace from "bar";', + 'namespace;' + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("43", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "var a: Foo[]; a;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("44", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "var a: ?Foo[]; a;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("45", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "var a: (?Foo)[]; a;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("46", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "var a: () => Foo[]; a;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("47", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "var a: (() => Foo)[]; a;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("48", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "var a: typeof Foo[]; a;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("49", function () { + verifyAndAssertMessages( + [ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", + "import type Foo3 from 'foo';", + "var a : [Foo, Foo2,] = [123, 'duck',]; a;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, [] ); }); From 2f75fe251cbd62bc56efa0cd636100986c1dde86 Mon Sep 17 00:00:00 2001 From: Henry Zhu Date: Mon, 1 Jun 2015 18:54:40 -0400 Subject: [PATCH 5/8] remove .bind, visit class methods --- index.js | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/index.js b/index.js index bf512f3f..aaeba83d 100644 --- a/index.js +++ b/index.js @@ -211,19 +211,19 @@ function monkeypatch() { } // visit flow type: ClassImplements if (node.implements) { - node.implements.forEach(function(i) { - checkIdentifierOrVisit.call(this, i); - }.bind(this)); + for (var i = 0; i < node.implements.length; i++) { + checkIdentifierOrVisit.call(this, node.implements[i]); + } } if (node.typeParameters) { - node.typeParameters.params.forEach(function(p) { - checkIdentifierOrVisit.call(this, p); - }.bind(this)); + for (var i = 0; i < node.typeParameters.params.length; i++) { + checkIdentifierOrVisit.call(this, node.typeParameters.params[i]); + } } if (node.superTypeParameters) { - node.superTypeParameters.params.forEach(function(p) { - checkIdentifierOrVisit.call(this, p); - }.bind(this)); + for (var i = 0; i < node.superTypeParameters.params.length; i++) { + checkIdentifierOrVisit.call(this, node.superTypeParameters.params[i]); + } } visitClass.call(this, node); }; @@ -242,16 +242,16 @@ function monkeypatch() { } // only visit if function parameters have types if (node.params) { - node.params.forEach(function(p) { - if (p.typeAnnotation) { - checkIdentifierOrVisit.call(this, p); + for (var i = 0; i < node.params.length; i++) { + if (node.params[i].typeAnnotation) { + checkIdentifierOrVisit.call(this, node.params[i]); } - }.bind(this)); + } } if (node.typeParameters) { - node.typeParameters.params.forEach(function(p) { - checkIdentifierOrVisit.call(this, p); - }.bind(this)); + for (var i = 0; i < node.typeParameters.params.length; i++) { + checkIdentifierOrVisit.call(this, node.typeParameters.params[i]); + } } visitFunction.call(this, node); }; @@ -260,9 +260,9 @@ function monkeypatch() { var variableDeclaration = referencer.prototype.VariableDeclaration; referencer.prototype.VariableDeclaration = function(node) { if (node.declarations) { - node.declarations.forEach(function(d) { - visitTypeAnnotation.call(this, d.id); - }.bind(this)); + for (var i = 0; i < node.declarations.length; i++) { + checkIdentifierOrVisit.call(this, node.declarations[i].id); + } } variableDeclaration.call(this, node); }; @@ -279,6 +279,9 @@ function monkeypatch() { null ) ); + if (node.right) { + visitTypeAnnotation.call(this, node.right); + } } } From 60dd5d32c148917573a44e3eb7d088bd507bbf10 Mon Sep 17 00:00:00 2001 From: Henry Zhu Date: Mon, 1 Jun 2015 19:18:46 -0400 Subject: [PATCH 6/8] use t.VISITOR_KEYS --- index.js | 58 +++++++++++++++------------------------------------- package.json | 3 ++- 2 files changed, 19 insertions(+), 42 deletions(-) diff --git a/index.js b/index.js index aaeba83d..d53c44d3 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ var acornToEsprima = require("./acorn-to-esprima"); var traverse = require("babel-core").traverse; var assign = require("lodash.assign"); +var pick = require("lodash.pick"); var Module = require("module"); var parse = require("babel-core").parse; var path = require("path"); @@ -86,47 +87,22 @@ function monkeypatch() { } } - // part of t.VISITOR_KEYS; - var visitorKeysMap = { - // Others - "ArrayPattern": ["elements", "typeAnnotation"], - "ClassDeclaration": ["id", "body", "superClass", "typeParameters", "superTypeParameters", "implements", "decorators"], - "ClassExpression": ["id", "body", "superClass", "typeParameters", "superTypeParameters", "implements", "decorators"], - "FunctionDeclaration": ["id", "params", "body", "returnType", "typeParameters"], - "FunctionExpression": ["id", "params", "body", "returnType", "typeParameters"], - "Identifier": ["typeAnnotation"], - "ObjectPattern": ["properties", "typeAnnotation"], - "RestElement": ["argument", "typeAnnotation"], - - // Flow specific - "ArrayTypeAnnotation": ["elementType"], - "ClassImplements": ["id", "typeParameters"], - "ClassProperty": ["key", "value", "typeAnnotation", "decorators"], - "DeclareClass": ["id", "typeParameters", "extends", "body"], - "DeclareFunction": ["id"], - "DeclareModule": ["id", "body"], - "DeclareVariable": ["id"], - "FunctionTypeAnnotation": ["typeParameters", "params", "rest", "returnType"], - "FunctionTypeParam": ["name", "typeAnnotation"], - "GenericTypeAnnotation": ["id", "typeParameters"], - "InterfaceExtends": ["id", "typeParameters"], - "InterfaceDeclaration": ["id", "typeParameters", "extends", "body"], - "IntersectionTypeAnnotation": ["types"], - "NullableTypeAnnotation": ["typeAnnotation"], - "TupleTypeAnnotation": ["types"], - "TypeofTypeAnnotation": ["argument"], - "TypeAlias": ["id", "typeParameters", "right"], - "TypeAnnotation": ["typeAnnotation"], - "TypeCastExpression": ["expression", "typeAnnotation"], - "TypeParameterDeclaration": ["params"], - "TypeParameterInstantiation": ["params"], - "ObjectTypeAnnotation": ["properties", "indexers", "callProperties"], - "ObjectTypeCallProperty": ["value"], - "ObjectTypeIndexer": ["id", "key", "value"], - "ObjectTypeProperty": ["key", "value"], - "QualifiedTypeIdentifier": ["qualification"], // don't check "id"? - "UnionTypeAnnotation": ["types"] - }; + // iterate through part of t.VISITOR_KEYS + var visitorKeysMap = pick(t.VISITOR_KEYS, function(k) { + return t.FLIPPED_ALIAS_KEYS.Flow.concat([ + "ArrayPattern", + "ClassDeclaration", + "ClassExpression", + "FunctionDeclaration", + "FunctionExpression", + "Identifier", + "ObjectPattern", + "RestElement" + ]).indexOf(k) === -1; + }); + + // don't check "id" to prevent "no-undef" for 'Component' with: 'let x: React.Component' + visitorKeysMap.QualifiedTypeIdentifier = ["qualification"]; var propertyTypes = { // loops diff --git a/package.json b/package.json index 95d6c347..feef1820 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ }, "dependencies": { "babel-core": "^5.1.8", - "lodash.assign": "^3.0.0" + "lodash.assign": "^3.0.0", + "lodash.pick": "^3.1.0" }, "scripts": { "test": "mocha" From bdc7cf6d2eeac472cfce1012cf17de87e143ff40 Mon Sep 17 00:00:00 2001 From: Henry Zhu Date: Mon, 1 Jun 2015 19:24:46 -0400 Subject: [PATCH 7/8] a few more tests --- index.js | 5 +++++ test/non-regression.js | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/index.js b/index.js index d53c44d3..76f22401 100644 --- a/index.js +++ b/index.js @@ -258,6 +258,11 @@ function monkeypatch() { if (node.right) { visitTypeAnnotation.call(this, node.right); } + if (node.typeParameters) { + for (var i = 0; i < node.typeParameters.params.length; i++) { + checkIdentifierOrVisit.call(this, node.typeParameters.params[i]); + } + } } } diff --git a/test/non-regression.js b/test/non-regression.js index ee4991aa..f4236afa 100644 --- a/test/non-regression.js +++ b/test/non-regression.js @@ -272,6 +272,29 @@ describe("verify", function () { ); }); + it("type alias with type parameters", function () { + verifyAndAssertMessages([ + "import type Bar from 'foo';", + "import type Foo2 from 'foo';", + "import type Foo3 from 'foo';", + "type Foo = Bar", + "var x : Foo = 1; x;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + + it("export type alias", function () { + verifyAndAssertMessages([ + "import type Foo2 from 'foo';", + "export type Foo = Foo2;" + ].join("\n"), + { "no-unused-vars": 1, "no-undef": 1 }, + [] + ); + }); + it("1", function () { verifyAndAssertMessages( [ From 39a52d4548b65c4e244e987ac46b6ac70c71490f Mon Sep 17 00:00:00 2001 From: Henry Zhu Date: Mon, 1 Jun 2015 20:58:37 -0400 Subject: [PATCH 8/8] visit type casts --- acorn-to-esprima.js | 18 ++++++++++++++++-- index.js | 11 +++-------- test/non-regression.js | 4 +++- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/acorn-to-esprima.js b/acorn-to-esprima.js index a4e005e4..324758fd 100644 --- a/acorn-to-esprima.js +++ b/acorn-to-esprima.js @@ -187,8 +187,22 @@ var astTransformVisitor = { return node.argument; } - if (this.isTypeCastExpression()) { - return node.expression; + // prevent "no-undef" + // for "Component" in: "let x: React.Component" + if (this.isQualifiedTypeIdentifier()) { + delete node.id; + } + // for "b" in: "var a: { b: Foo }" + if (this.isObjectTypeProperty()) { + delete node.key; + } + // for "indexer" in: "var a: {[indexer: string]: number}" + if (this.isObjectTypeIndexer()) { + delete node.id; + } + // for "param" in: "var a: { func(param: Foo): Bar };" + if (this.isFunctionTypeParam()) { + delete node.name; } // flow diff --git a/index.js b/index.js index 76f22401..0977fd42 100644 --- a/index.js +++ b/index.js @@ -47,12 +47,7 @@ function monkeypatch() { escope.analyze = function (ast, opts) { opts.ecmaVersion = 6; opts.sourceType = "module"; - // Don't visit TypeAlias when analyzing scope, but retain them for other - // eslint rules. - // var TypeAliasKeys = estraverse.VisitorKeys.TypeAlias; - // estraverse.VisitorKeys.TypeAlias = []; var results = analyze.call(this, ast, opts); - // estraverse.VisitorKeys.TypeAlias = TypeAliasKeys; return results; }; @@ -101,9 +96,6 @@ function monkeypatch() { ]).indexOf(k) === -1; }); - // don't check "id" to prevent "no-undef" for 'Component' with: 'let x: React.Component' - visitorKeysMap.QualifiedTypeIdentifier = ["qualification"]; - var propertyTypes = { // loops callProperties: { type: "loop", values: ["value"] }, @@ -206,6 +198,9 @@ function monkeypatch() { // visit decorators that are in: Property / MethodDefinition var visitProperty = referencer.prototype.visitProperty; referencer.prototype.visitProperty = function(node) { + if (node.value.type === 'TypeCastExpression') { + visitTypeAnnotation.call(this, node.value); + } visitDecorators.call(this, node); visitProperty.call(this, node); }; diff --git a/test/non-regression.js b/test/non-regression.js index f4236afa..9ec3b209 100644 --- a/test/non-regression.js +++ b/test/non-regression.js @@ -214,8 +214,10 @@ describe("verify", function () { it("object property types", function () { verifyAndAssertMessages([ + "import type Foo from 'foo';", + "import type Foo2 from 'foo';", "var a = {", - "circle: (null : ?{ setNativeProps(props: Object): void })", + "circle: (null : ?{ setNativeProps(props: Foo): Foo2 })", "};", "a;" ].join("\n"),