diff --git a/package.json b/package.json index 4d840540db75..91b01831c72a 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,19 @@ "recast": "^0.11.18" }, "devDependencies": { - "glob": "^7.1.1" + "glob": "^7.1.1", + "jest": "^18.0.0", + "flow-parser": "^0.37.0" + }, + "scripts": { + "test": "jest" + }, + "jest": { + "setupFiles": [ + "/tests_config/run_spec.js" + ], + "testRegex": [ + "jsfmt\\.spec\\.js$" + ] } } diff --git a/tests/abnormal/__snapshots__/jsfmt.spec.js.snap b/tests/abnormal/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..3963bef56d30 --- /dev/null +++ b/tests/abnormal/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,127 @@ +exports[`test break-continue.js 1`] = ` +"function foo() { + while(true) { break; } +} + +function bar() { + L: do { continue L; } while(false) +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:925 + if (endsWithBrace(doBody)) + ^ + +ReferenceError: endsWithBrace is not defined + at genericPrintNoParens (/src/printer.js:925:11) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:963:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test return.js 1`] = ` +"function bar(x:number) { } +function foo() { + var x = null; + if (x == null) return; + bar(x); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +function bar(x: number) { + +} +function foo() { + var x = null; + if (x == null) + return; + bar(x); +} + +" +`; + +exports[`test toplevel_break.js 1`] = ` +"// @flow + +break; // error, illegal +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unsyntactic break (3:0) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp$1.parseBreakContinueStatement (/node_modules/babylon/lib/index.js:1843:44) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1703:19) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) + at Parser.parse (/node_modules/babylon/lib/index.js:1543:17) + at Object.parse$1 [as parse] (/node_modules/babylon/lib/index.js:6472:37) + at Object.parse (/index.js:34:26) + at Object.parse (/node_modules/recast/lib/parser.js:26:34) +" +`; + +exports[`test toplevel_continue.js 1`] = ` +"// @flow + +continue; // error, illegal +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unsyntactic continue (3:0) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp$1.parseBreakContinueStatement (/node_modules/babylon/lib/index.js:1843:44) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1703:19) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) + at Parser.parse (/node_modules/babylon/lib/index.js:1543:17) + at Object.parse$1 [as parse] (/node_modules/babylon/lib/index.js:6472:37) + at Object.parse (/index.js:34:26) + at Object.parse (/node_modules/recast/lib/parser.js:26:34) +" +`; + +exports[`test toplevel_return.js 1`] = ` +"// @flow + +return; // error, illegal +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: \'return\' outside of function (3:0) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp$1.parseReturnStatement (/node_modules/babylon/lib/index.js:1939:10) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1722:19) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) + at Parser.parse (/node_modules/babylon/lib/index.js:1543:17) + at Object.parse$1 [as parse] (/node_modules/babylon/lib/index.js:6472:37) + at Object.parse (/index.js:34:26) + at Object.parse (/node_modules/recast/lib/parser.js:26:34) +" +`; + +exports[`test toplevel_throw.js 1`] = ` +"// @flow + +throw new Error(\'foo\'); // no error +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +throw new Error(\"foo\");// no error + +" +`; diff --git a/tests/abnormal/break-continue.js b/tests/abnormal/break-continue.js new file mode 100644 index 000000000000..1f10210ca25b --- /dev/null +++ b/tests/abnormal/break-continue.js @@ -0,0 +1,7 @@ +function foo() { + while(true) { break; } +} + +function bar() { + L: do { continue L; } while(false) +} diff --git a/tests/abnormal/jsfmt.spec.js b/tests/abnormal/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/abnormal/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/abnormal/return.js b/tests/abnormal/return.js new file mode 100644 index 000000000000..c2c32814d414 --- /dev/null +++ b/tests/abnormal/return.js @@ -0,0 +1,6 @@ +function bar(x:number) { } +function foo() { + var x = null; + if (x == null) return; + bar(x); +} diff --git a/tests/abnormal/toplevel_break.js b/tests/abnormal/toplevel_break.js new file mode 100644 index 000000000000..15dd4120aefd --- /dev/null +++ b/tests/abnormal/toplevel_break.js @@ -0,0 +1,3 @@ +// @flow + +break; // error, illegal diff --git a/tests/abnormal/toplevel_continue.js b/tests/abnormal/toplevel_continue.js new file mode 100644 index 000000000000..63ecd26c0a9e --- /dev/null +++ b/tests/abnormal/toplevel_continue.js @@ -0,0 +1,3 @@ +// @flow + +continue; // error, illegal diff --git a/tests/abnormal/toplevel_return.js b/tests/abnormal/toplevel_return.js new file mode 100644 index 000000000000..64233d781de2 --- /dev/null +++ b/tests/abnormal/toplevel_return.js @@ -0,0 +1,3 @@ +// @flow + +return; // error, illegal diff --git a/tests/abnormal/toplevel_throw.js b/tests/abnormal/toplevel_throw.js new file mode 100644 index 000000000000..91f0b353915d --- /dev/null +++ b/tests/abnormal/toplevel_throw.js @@ -0,0 +1,3 @@ +// @flow + +throw new Error('foo'); // no error diff --git a/tests/annot/__snapshots__/jsfmt.spec.js.snap b/tests/annot/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..b4924ebb8612 --- /dev/null +++ b/tests/annot/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,234 @@ +exports[`test annot.js 1`] = ` +"function foo(str:string, i:number):string { + return str; +} +var bar: (str:number, i:number)=> string = foo; + +var qux = function(str:string, i:number):number { return foo(str,i); } + +var obj: {str:string; i:number; j:boolean} = {str: \"...\", i: \"...\", k: false}; + +var arr: Array = [1,2,\"...\"]; + +// array sugar +var array: number[] = [1,2,\"...\"]; + +var matrix: number[][] = [[1,2],[3,4]]; +var matrix_parens: (number[])[] = matrix; + +var nullable_array: ?number[] = null; +var nullable_array_parens: ?(number[]) = nullable_array; + +var array_of_nullable: (?number)[] = [null, 3]; + +var array_of_tuple: [number, string][] = [[0, \"foo\"], [1, \"bar\"]]; +var array_of_tuple_parens: ([number, string])[] = array_of_tuple; + +type ObjType = { \'bar-foo\': string; \'foo-bar\': number; }; +var test_obj: ObjType = { \'bar-foo\': \'23\' }; + +// param type annos are strict UBs like var type annos +function param_anno(n:number):void { + n = \"hey\"; // error +} + +// another error on param UB, more typical of www (mis)use-cases +// this one cribbed from API.atlas.js +function param_anno2( + batchRequests: Array<{method: string; path: string; params: ?Object}>, + ): void { + + // error below, since we\'re assigning elements to batchRequests + // which lack a path property. + // just assign result to new var instead of reassigning to param. + + // Transform the requests to the format the Graph API expects. + batchRequests = batchRequests.map((request) => { + return { + method: request.method, + params: request.params, + relative_url: request.path, + }; + }); + // ... + } + +var toz : null = 3; + +var zer : null = null; + +function foobar(n : ?number) : number | null | void { return n; } +function barfoo(n : number | null | void) : ?number { return n; } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test forward_ref.js 1`] = ` +"let myClassInstance: MyClass = null; // forward ref ok, null ~> class error + +function bar(): MyClass { + return null; // forward ref ok, null ~> class error +} + +class MyClass { } // looked up above + +function foo() { + let myClassInstance: MyClass = mk(); // ok (no confusion across scopes) + function mk() { return new MyClass(); } + + class MyClass { } // looked up above +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +let myClassInstance: MyClass = null;// forward ref ok, null ~> class error +function bar(): MyClass { + return null;// forward ref ok, null ~> class error +} +class MyClass {}// looked up above +function foo() { + let myClassInstance: MyClass = mk();// ok (no confusion across scopes) + function mk() { + return new MyClass(); + } + class MyClass {}// looked up above +} + +" +`; + +exports[`test issue-530.js 1`] = ` +"function foo(...args: any) { } + +module.exports = foo; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +function foo(...args) { + +} +module.exports = foo; + +" +`; + +exports[`test leak.js 1`] = ` +"/** @flow */ + +/* This test documents an example we ran into of a type annotation leaking. + * + * When foo() calls bar(), we should make sure the type of x matches the type + * annotation for y and stop. We should type the body of bar() with the type + * annotation of y. + * + * However, the leaky type annotation meant that we were flowing x\'s type to y + * and type checking the body of bar() using the stricter dictionary type, + * leading to an error. + */ + +type MyObj = Object; + +function foo(x: {[key: string]: mixed}) { + bar(x); +} + +function bar(y: MyObj): string { + return y.foo; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** @flow */ +/* This test documents an example we ran into of a type annotation leaking. + * + * When foo() calls bar(), we should make sure the type of x matches the type + * annotation for y and stop. We should type the body of bar() with the type + * annotation of y. + * + * However, the leaky type annotation meant that we were flowing x\'s type to y + * and type checking the body of bar() using the stricter dictionary type, + * leading to an error. + */ +type MyObj = Object; +function foo(x: { [key: string]: mixed }) { + bar(x); +} +function bar(y: MyObj): string { + return y.foo; +} + +" +`; + +exports[`test other.js 1`] = ` +"class C { } +module.exports = (C: any); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class C {} +module.exports = (C: any); + +" +`; + +exports[`test scope.js 1`] = ` +"type Merge = (a: T, b: T) => T; + +// hypothetical immutable map +declare class Map { + (): Map; + insertWith(fn: Merge, k: K, v: V): Map; +} + +declare function foldr(fn: (a: A, b: B) => B, b: B, as: A[]): B; + +function insertMany(merge: Merge, vs: [K,V][], m: Map): Map { + function f([k,v]: [K,V], m: Map): Map { + return m.insertWith(merge, k, v) + } + return foldr(f, m, vs) +} + +class Foo { + bar() { + return function(a: A, b: B, c: C): void { + ([a,b,c] : [A,B,C]); + } + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1479:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test.js 1`] = ` +"var C = require(\'./other\'); +((0: C): string); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var C = require(\"./other\"); +((0: C): string); + +" +`; diff --git a/tests/annot/annot.js b/tests/annot/annot.js new file mode 100644 index 000000000000..4d1b25a5ad64 --- /dev/null +++ b/tests/annot/annot.js @@ -0,0 +1,60 @@ +function foo(str:string, i:number):string { + return str; +} +var bar: (str:number, i:number)=> string = foo; + +var qux = function(str:string, i:number):number { return foo(str,i); } + +var obj: {str:string; i:number; j:boolean} = {str: "...", i: "...", k: false}; + +var arr: Array = [1,2,"..."]; + +// array sugar +var array: number[] = [1,2,"..."]; + +var matrix: number[][] = [[1,2],[3,4]]; +var matrix_parens: (number[])[] = matrix; + +var nullable_array: ?number[] = null; +var nullable_array_parens: ?(number[]) = nullable_array; + +var array_of_nullable: (?number)[] = [null, 3]; + +var array_of_tuple: [number, string][] = [[0, "foo"], [1, "bar"]]; +var array_of_tuple_parens: ([number, string])[] = array_of_tuple; + +type ObjType = { 'bar-foo': string; 'foo-bar': number; }; +var test_obj: ObjType = { 'bar-foo': '23' }; + +// param type annos are strict UBs like var type annos +function param_anno(n:number):void { + n = "hey"; // error +} + +// another error on param UB, more typical of www (mis)use-cases +// this one cribbed from API.atlas.js +function param_anno2( + batchRequests: Array<{method: string; path: string; params: ?Object}>, + ): void { + + // error below, since we're assigning elements to batchRequests + // which lack a path property. + // just assign result to new var instead of reassigning to param. + + // Transform the requests to the format the Graph API expects. + batchRequests = batchRequests.map((request) => { + return { + method: request.method, + params: request.params, + relative_url: request.path, + }; + }); + // ... + } + +var toz : null = 3; + +var zer : null = null; + +function foobar(n : ?number) : number | null | void { return n; } +function barfoo(n : number | null | void) : ?number { return n; } diff --git a/tests/annot/any/A.js b/tests/annot/any/A.js new file mode 100644 index 000000000000..093d2d63a2ce --- /dev/null +++ b/tests/annot/any/A.js @@ -0,0 +1,9 @@ +type T = any; + +export default class { + p: T; + + constructor() { + this.p = 0; + } +} diff --git a/tests/annot/any/B.js b/tests/annot/any/B.js new file mode 100644 index 000000000000..1e0776d87a61 --- /dev/null +++ b/tests/annot/any/B.js @@ -0,0 +1,5 @@ +import A from "./A" + +class B extends A { + p: string; // OK, string ~> any +} diff --git a/tests/annot/any/__snapshots__/jsfmt.spec.js.snap b/tests/annot/any/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..a65f786d3a96 --- /dev/null +++ b/tests/annot/any/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,36 @@ +exports[`test A.js 1`] = ` +"type T = any; + +export default class { + p: T; + + constructor() { + this.p = 0; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +type T = any; +export default class { + p: T; + constructor() { + this.p = 0; + } +} + +" +`; + +exports[`test B.js 1`] = ` +"import A from "./A" + +class B extends A { + p: string; // OK, string ~> any +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +import A from "./A"; +class B extends A { + p: string;// OK, string ~> any +} + +" +`; diff --git a/tests/annot/any/jsfmt.spec.js b/tests/annot/any/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/annot/any/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/annot/forward_ref.js b/tests/annot/forward_ref.js new file mode 100644 index 000000000000..8061d65a3aad --- /dev/null +++ b/tests/annot/forward_ref.js @@ -0,0 +1,14 @@ +let myClassInstance: MyClass = null; // forward ref ok, null ~> class error + +function bar(): MyClass { + return null; // forward ref ok, null ~> class error +} + +class MyClass { } // looked up above + +function foo() { + let myClassInstance: MyClass = mk(); // ok (no confusion across scopes) + function mk() { return new MyClass(); } + + class MyClass { } // looked up above +} diff --git a/tests/annot/issue-530.js b/tests/annot/issue-530.js new file mode 100644 index 000000000000..bd8d893e516d --- /dev/null +++ b/tests/annot/issue-530.js @@ -0,0 +1,3 @@ +function foo(...args: any) { } + +module.exports = foo; diff --git a/tests/annot/jsfmt.spec.js b/tests/annot/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/annot/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/annot/leak.js b/tests/annot/leak.js new file mode 100644 index 000000000000..bfc8254c74be --- /dev/null +++ b/tests/annot/leak.js @@ -0,0 +1,22 @@ +/** @flow */ + +/* This test documents an example we ran into of a type annotation leaking. + * + * When foo() calls bar(), we should make sure the type of x matches the type + * annotation for y and stop. We should type the body of bar() with the type + * annotation of y. + * + * However, the leaky type annotation meant that we were flowing x's type to y + * and type checking the body of bar() using the stricter dictionary type, + * leading to an error. + */ + +type MyObj = Object; + +function foo(x: {[key: string]: mixed}) { + bar(x); +} + +function bar(y: MyObj): string { + return y.foo; +} diff --git a/tests/annot/other.js b/tests/annot/other.js new file mode 100644 index 000000000000..0407c8397d80 --- /dev/null +++ b/tests/annot/other.js @@ -0,0 +1,2 @@ +class C { } +module.exports = (C: any); diff --git a/tests/annot/scope.js b/tests/annot/scope.js new file mode 100644 index 000000000000..3f0b7ec6c5c8 --- /dev/null +++ b/tests/annot/scope.js @@ -0,0 +1,24 @@ +type Merge = (a: T, b: T) => T; + +// hypothetical immutable map +declare class Map { + (): Map; + insertWith(fn: Merge, k: K, v: V): Map; +} + +declare function foldr(fn: (a: A, b: B) => B, b: B, as: A[]): B; + +function insertMany(merge: Merge, vs: [K,V][], m: Map): Map { + function f([k,v]: [K,V], m: Map): Map { + return m.insertWith(merge, k, v) + } + return foldr(f, m, vs) +} + +class Foo { + bar() { + return function(a: A, b: B, c: C): void { + ([a,b,c] : [A,B,C]); + } + } +} diff --git a/tests/annot/test.js b/tests/annot/test.js new file mode 100644 index 000000000000..94f65159e8d0 --- /dev/null +++ b/tests/annot/test.js @@ -0,0 +1,2 @@ +var C = require('./other'); +((0: C): string); diff --git a/tests/annot2/A.js b/tests/annot2/A.js new file mode 100644 index 000000000000..06abcbf1ddd4 --- /dev/null +++ b/tests/annot2/A.js @@ -0,0 +1,14 @@ +/** + * @providesModule A + * @flow + */ + +import type T from "T"; + +export default class { + p: T; + + constructor() { + this.p = 0; + } +} diff --git a/tests/annot2/B.js b/tests/annot2/B.js new file mode 100644 index 000000000000..823a7e8935ff --- /dev/null +++ b/tests/annot2/B.js @@ -0,0 +1,9 @@ +/** + * @flow + */ + +import A from "A" + +class B extends A { + p: string; // OK, string ~> any +} diff --git a/tests/annot2/T.js b/tests/annot2/T.js new file mode 100644 index 000000000000..d90bf5a54587 --- /dev/null +++ b/tests/annot2/T.js @@ -0,0 +1,3 @@ +/** + * @providesModule T + */ diff --git a/tests/annot2/__snapshots__/jsfmt.spec.js.snap b/tests/annot2/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..dbc3d4a41a3c --- /dev/null +++ b/tests/annot2/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,62 @@ +exports[`test A.js 1`] = ` +"/** + * @providesModule A + * @flow + */ + +import type T from "T"; + +export default class { + p: T; + + constructor() { + this.p = 0; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule A + * @flow + */ +import type T from "T"; +export default class { + p: T; + constructor() { + this.p = 0; + } +} + +" +`; + +exports[`test B.js 1`] = ` +"/** + * @flow + */ + +import A from "A" + +class B extends A { + p: string; // OK, string ~> any +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +import A from "A"; +class B extends A { + p: string;// OK, string ~> any +} + +" +`; + +exports[`test T.js 1`] = ` +"/** + * @providesModule T + */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; diff --git a/tests/annot2/jsfmt.spec.js b/tests/annot2/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/annot2/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/any/__snapshots__/jsfmt.spec.js.snap b/tests/any/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..c15d72b9755c --- /dev/null +++ b/tests/any/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,202 @@ +exports[`test any.js 1`] = ` +"// @flow + +function foo(x:any):any { return x; } +function bar(x:any):mixed { return x; } +function qux(x:mixed):any { return x; } + +var x:string = foo(0); +var y:string = bar(0); +var z:string = qux(0); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +function foo(x: any): any { + return x; +} +function bar(x: any): mixed { + return x; +} +function qux(x: mixed): any { + return x; +} +var x: string = foo(0); +var y: string = bar(0); +var z: string = qux(0); + +" +`; + +exports[`test anyexportflowfile.js 1`] = ` +"// @flow + +module.exports = ((x: any) => x: any); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +module.exports = ((x: any) => x: any); + +" +`; + +exports[`test flowfixme.js 1`] = ` +"/* + FlowFixMe is a synonym for any, used by the Flow team to + signal a needed mod to JS devs. + + @flow + */ + +// no param +function foo(x:$FlowFixMe):$FlowFixMe { return x; } +function bar(x:$FlowFixMe):mixed { return x; } +// param (info only) +function qux(x:$FlowFixMe):$FlowFixMe { return x; } +// ...params are still checked. unknown type +function baz(x:$FlowFixMe): $FlowFixMe { return x; } + +var x:string = foo(0); +var y:string = bar(0); +var z:string = qux(0); +var w:string = baz(0); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test flowissue.js 1`] = ` +"/* + $FlowIssue is a synonym for any, used by JS devs to signal + a potential typechecker bug to the Flow team. + + @flow + */ + +// no param +function foo(x:$FlowIssue):$FlowIssue { return x; } +function bar(x:$FlowIssue):mixed { return x; } +// param (info only) +function qux(x:$FlowIssue):$FlowIssue { return x; } +// ...params are still checked. unknown type +function baz(x:$FlowIssue): $FlowIssue { return x; } + +var x:string = foo(0); +var y:string = bar(0); +var z:string = qux(0); +var w:string = baz(0); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test nonflowfile.js 1`] = ` +"// @noflow + +module.exports = (x) => x; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @noflow +module.exports = x => x; + +" +`; + +exports[`test propagate.js 1`] = ` +"// @flow + +declare class C { + bar(n1: number, n2: number): number; + bar(s1: string, s2: string): string; +} + +function foo(c: C, x: any): string { + let y = x.y; + return c.bar(0, y); // should be able to select first case and error +} + +var any_fun1 = require(\'./nonflowfile\'); +function bar1(x: mixed) { + if (any_fun1(x)) { + (x: boolean); + } +} + +var any_fun2 = require(\'./anyexportflowfile\'); +function bar2(x: mixed) { + if (any_fun2(x)) { + (x: boolean); + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test reach.js 1`] = ` +"/** + * like class and function values, any-typed values may be used in + * type annotations. Here we test propagation of any through the + * annotation - without it, the body of the if will be unreachable + */ + +type AsyncRequest = any; + +function foo(o: ?AsyncRequest) { + if (o) { + var n: number = o; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * like class and function values, any-typed values may be used in + * type annotations. Here we test propagation of any through the + * annotation - without it, the body of the if will be unreachable + */ +type AsyncRequest = any; +function foo(o: ?AsyncRequest) { + if (o) { + var n: number = o; + } +} + +" +`; diff --git a/tests/any/any.js b/tests/any/any.js new file mode 100644 index 000000000000..6f5bf81494d7 --- /dev/null +++ b/tests/any/any.js @@ -0,0 +1,9 @@ +// @flow + +function foo(x:any):any { return x; } +function bar(x:any):mixed { return x; } +function qux(x:mixed):any { return x; } + +var x:string = foo(0); +var y:string = bar(0); +var z:string = qux(0); diff --git a/tests/any/anyexportflowfile.js b/tests/any/anyexportflowfile.js new file mode 100644 index 000000000000..61671d18a14e --- /dev/null +++ b/tests/any/anyexportflowfile.js @@ -0,0 +1,3 @@ +// @flow + +module.exports = ((x: any) => x: any); diff --git a/tests/any/flowfixme.js b/tests/any/flowfixme.js new file mode 100644 index 000000000000..bb3886027a77 --- /dev/null +++ b/tests/any/flowfixme.js @@ -0,0 +1,19 @@ +/* + FlowFixMe is a synonym for any, used by the Flow team to + signal a needed mod to JS devs. + + @flow + */ + +// no param +function foo(x:$FlowFixMe):$FlowFixMe { return x; } +function bar(x:$FlowFixMe):mixed { return x; } +// param (info only) +function qux(x:$FlowFixMe):$FlowFixMe { return x; } +// ...params are still checked. unknown type +function baz(x:$FlowFixMe): $FlowFixMe { return x; } + +var x:string = foo(0); +var y:string = bar(0); +var z:string = qux(0); +var w:string = baz(0); diff --git a/tests/any/flowissue.js b/tests/any/flowissue.js new file mode 100644 index 000000000000..2fbd2f280369 --- /dev/null +++ b/tests/any/flowissue.js @@ -0,0 +1,19 @@ +/* + $FlowIssue is a synonym for any, used by JS devs to signal + a potential typechecker bug to the Flow team. + + @flow + */ + +// no param +function foo(x:$FlowIssue):$FlowIssue { return x; } +function bar(x:$FlowIssue):mixed { return x; } +// param (info only) +function qux(x:$FlowIssue):$FlowIssue { return x; } +// ...params are still checked. unknown type +function baz(x:$FlowIssue): $FlowIssue { return x; } + +var x:string = foo(0); +var y:string = bar(0); +var z:string = qux(0); +var w:string = baz(0); diff --git a/tests/any/jsfmt.spec.js b/tests/any/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/any/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/any/nonflowfile.js b/tests/any/nonflowfile.js new file mode 100644 index 000000000000..f03c2cd668ba --- /dev/null +++ b/tests/any/nonflowfile.js @@ -0,0 +1,3 @@ +// @noflow + +module.exports = (x) => x; diff --git a/tests/any/propagate.js b/tests/any/propagate.js new file mode 100644 index 000000000000..1e073c54d2cf --- /dev/null +++ b/tests/any/propagate.js @@ -0,0 +1,25 @@ +// @flow + +declare class C { + bar(n1: number, n2: number): number; + bar(s1: string, s2: string): string; +} + +function foo(c: C, x: any): string { + let y = x.y; + return c.bar(0, y); // should be able to select first case and error +} + +var any_fun1 = require('./nonflowfile'); +function bar1(x: mixed) { + if (any_fun1(x)) { + (x: boolean); + } +} + +var any_fun2 = require('./anyexportflowfile'); +function bar2(x: mixed) { + if (any_fun2(x)) { + (x: boolean); + } +} diff --git a/tests/any/reach.js b/tests/any/reach.js new file mode 100644 index 000000000000..ec1232f6b989 --- /dev/null +++ b/tests/any/reach.js @@ -0,0 +1,13 @@ +/** + * like class and function values, any-typed values may be used in + * type annotations. Here we test propagation of any through the + * annotation - without it, the body of the if will be unreachable + */ + +type AsyncRequest = any; + +function foo(o: ?AsyncRequest) { + if (o) { + var n: number = o; + } +} diff --git a/tests/arith/Arith.js b/tests/arith/Arith.js new file mode 100644 index 000000000000..818619d805bd --- /dev/null +++ b/tests/arith/Arith.js @@ -0,0 +1,91 @@ + +/* @providesModule Arith */ + +function num(x:number) { } + +function str(x:string) { } + +function foo() { + var x = 0; + var y = "..."; + var z = {}; + num(x+x); + num(x+y); // error + str(x+y); + str(x+x); // error + str(z+y); // error +} + +// test MaybeT(NumT) +function bar0(x: ?number, y: number) { + num(x + y); +} +function bar1(x: number, y: ?number) { + num(x + y); +} + +// test OptionalT(NumT) +function bar2(x?: number, y: number) { + num(x + y); +} +function bar3(x: number, y?: number) { + num(x + y); +} + +// test OptionalT(MaybeT(NumT)) +function bar4(x?: ?number, y: number) { + num(x + y); +} +function bar5(x: number, y?: ?number) { + num(x + y); +} + +num(null + null); // === 0 +num(undefined + undefined); // === NaN + +num(null + 1); // === 1 +num(1 + null); // === 1 +num(undefined + 1); // === NaN +num(1 + undefined); // === NaN + +num(null + true); // === 1 +num(true + null); // === 1 +num(undefined + true); // === NaN +num(true + undefined); // === NaN + +str("foo" + true); // error +str(true + "foo"); // error +str("foo" + null); // error +str(null + "foo"); // error +str("foo" + undefined); // error +str(undefined + "foo"); // error + +let tests = [ + function(x: mixed, y: mixed) { + (x + y); // error + (x + 0); // error + (0 + x); // error + (x + ""); // error + ("" + x); // error + (x + {}); // error + ({} + x); // error + }, + + // when one side is a string or number and the other is invalid, we + // assume you are expecting a string or number (respectively), rather than + // erroring twice saying number !~> string and obj !~> string. + function() { + ((1 + {}): number); // error: object !~> number + (({} + 1): number); // error: object !~> number + (("1" + {}): string); // error: object !~> string + (({} + "1"): string); // error: object !~> string + }, + + function(x: any, y: number, z: string) { + (x + y: string); // ok + (y + x: string); // ok + + (x + z: empty); // error, string ~> empty + (z + x: empty); // error, string ~> empty + }, +]; diff --git a/tests/arith/__snapshots__/jsfmt.spec.js.snap b/tests/arith/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..22d3fa7b2599 --- /dev/null +++ b/tests/arith/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,318 @@ +exports[`test Arith.js 1`] = ` +" +/* @providesModule Arith */ + +function num(x:number) { } + +function str(x:string) { } + +function foo() { + var x = 0; + var y = \"...\"; + var z = {}; + num(x+x); + num(x+y); // error + str(x+y); + str(x+x); // error + str(z+y); // error +} + +// test MaybeT(NumT) +function bar0(x: ?number, y: number) { + num(x + y); +} +function bar1(x: number, y: ?number) { + num(x + y); +} + +// test OptionalT(NumT) +function bar2(x?: number, y: number) { + num(x + y); +} +function bar3(x: number, y?: number) { + num(x + y); +} + +// test OptionalT(MaybeT(NumT)) +function bar4(x?: ?number, y: number) { + num(x + y); +} +function bar5(x: number, y?: ?number) { + num(x + y); +} + +num(null + null); // === 0 +num(undefined + undefined); // === NaN + +num(null + 1); // === 1 +num(1 + null); // === 1 +num(undefined + 1); // === NaN +num(1 + undefined); // === NaN + +num(null + true); // === 1 +num(true + null); // === 1 +num(undefined + true); // === NaN +num(true + undefined); // === NaN + +str(\"foo\" + true); // error +str(true + \"foo\"); // error +str(\"foo\" + null); // error +str(null + \"foo\"); // error +str(\"foo\" + undefined); // error +str(undefined + \"foo\"); // error + +let tests = [ + function(x: mixed, y: mixed) { + (x + y); // error + (x + 0); // error + (0 + x); // error + (x + \"\"); // error + (\"\" + x); // error + (x + {}); // error + ({} + x); // error + }, + + // when one side is a string or number and the other is invalid, we + // assume you are expecting a string or number (respectively), rather than + // erroring twice saying number !~> string and obj !~> string. + function() { + ((1 + {}): number); // error: object !~> number + (({} + 1): number); // error: object !~> number + ((\"1\" + {}): string); // error: object !~> string + (({} + \"1\"): string); // error: object !~> string + }, + + function(x: any, y: number, z: string) { + (x + y: string); // ok + (y + x: string); // ok + + (x + z: empty); // error, string ~> empty + (z + x: empty); // error, string ~> empty + }, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @providesModule Arith */ +function num(x: number) { + +} +function str(x: string) { + +} +function foo() { + var x = 0; + var y = \"...\"; + var z = {}; + num(x + x); + num(x + y);// error + str(x + y); + str(x + x);// error + str(z + y);// error +} +// test MaybeT(NumT) +function bar0(x: ?number, y: number) { + num(x + y); +} +function bar1(x: number, y: ?number) { + num(x + y); +} +// test OptionalT(NumT) +function bar2(x: number, y: number) { + num(x + y); +} +function bar3(x: number, y: number) { + num(x + y); +} +// test OptionalT(MaybeT(NumT)) +function bar4(x: ?number, y: number) { + num(x + y); +} +function bar5(x: number, y: ?number) { + num(x + y); +} +num(null + null);// === 0 +num(undefined + undefined);// === NaN +num(null + 1);// === 1 +num(1 + null);// === 1 +num(undefined + 1);// === NaN +num(1 + undefined);// === NaN +num(null + true);// === 1 +num(true + null);// === 1 +num(undefined + true);// === NaN +num(true + undefined);// === NaN +str(\"foo\" + true);// error +str(true + \"foo\");// error +str(\"foo\" + null);// error +str(null + \"foo\");// error +str(\"foo\" + undefined);// error +str(undefined + \"foo\");// error +let tests = [ + function(x: mixed, y: mixed) { + x + y;// error + x + 0;// error + 0 + x;// error + x + \"\";// error + \"\" + x;// error + x + {};// error + ({}) + x;// error + }, + // when one side is a string or number and the other is invalid, we + // assume you are expecting a string or number (respectively), rather than + // erroring twice saying number !~> string and obj !~> string. + function() { + (1 + {}: number);// error: object !~> number + ({} + 1: number);// error: object !~> number + (\"1\" + {}: string);// error: object !~> string + ({} + \"1\": string);// error: object !~> string + }, + function(x: any, y: number, z: string) { + (x + y: string);// ok + (y + x: string);// ok + (x + z: empty);// error, string ~> empty + (z + x: empty);// error, string ~> empty + } +]; + +" +`; + +exports[`test exponent.js 1`] = ` +"/* @flow */ + +let x: number = 2 ** 3; +x **= 4; + +let y: string = \"123\"; +y **= 2; // error + +1 + 2 ** 3 + 4; +2 ** 2; +(-2) ** 2; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +let x: number = 2 ** 3; +x **= 4; +let y: string = \"123\"; +y **= 2;// error +1 + 2 ** 3 + 4; +2 ** 2; +-2 ** 2; + +" +`; + +exports[`test generic.js 1`] = ` +"/* @flow */ + +function f(a: A): A { return a + a; } // error +function f(a: A, b: B): A {return a + b; } // error +function f(a: A, b: B): A {return b + a; } // error +function f(a: A, b: B): B {return a + b; } // error +function f(a: A, b: B): B {return b + a; } // error +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:340:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test mult.js 1`] = ` +"/* @flow */ + +function num(x:number) { } + +num(null * 1); +num(1 * null); + +let x: number = 2 * 3; +x *= 4; + +let y: string = \"123\"; +y *= 2; // error +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +function num(x: number) { + +} +num(null * 1); +num(1 * null); +let x: number = 2 * 3; +x *= 4; +let y: string = \"123\"; +y *= 2;// error + +" +`; + +exports[`test relational.js 1`] = ` +"/* @flow */ + +(1 < 2); +(1 < \"foo\"); // error +(\"foo\" < 1); // error +(\"foo\" < \"bar\"); +(1 < {foo: 1}); // error +({foo: 1} < 1); // error +({foo: 1} < {foo: 1}); // error +(\"foo\" < {foo: 1}); // error +({foo: 1} < \"foo\"); // error + +var x = (null : ?number); +(1 < x); // 2 errors: null !~> number; undefined !~> number +(x < 1); // 2 errors: null !~> number; undefined !~> number + +(null < null); // error +(undefined < null); // error +(null < undefined); // error +(undefined < undefined); // error +(NaN < 1); +(1 < NaN); +(NaN < NaN); + +let tests = [ + function(x: any, y: number, z: string) { + (x > y); + (x > z); + }, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +1 < 2; +1 < \"foo\";// error +\"foo\" < 1;// error +\"foo\" < \"bar\"; +1 < { foo: 1 };// error +({ foo: 1 }) < 1;// error +({ foo: 1 }) < { foo: 1 };// error +\"foo\" < { foo: 1 };// error +({ foo: 1 }) < \"foo\";// error +var x = (null: ?number); +1 < x;// 2 errors: null !~> number; undefined !~> number +x < 1;// 2 errors: null !~> number; undefined !~> number +null < null;// error +undefined < null;// error +null < undefined;// error +undefined < undefined;// error +NaN < 1; +1 < NaN; +NaN < NaN; +let tests = [ + function(x: any, y: number, z: string) { + x > y; + x > z; + } +]; + +" +`; diff --git a/tests/arith/exponent.js b/tests/arith/exponent.js new file mode 100644 index 000000000000..2bac6484428a --- /dev/null +++ b/tests/arith/exponent.js @@ -0,0 +1,11 @@ +/* @flow */ + +let x: number = 2 ** 3; +x **= 4; + +let y: string = "123"; +y **= 2; // error + +1 + 2 ** 3 + 4; +2 ** 2; +(-2) ** 2; diff --git a/tests/arith/generic.js b/tests/arith/generic.js new file mode 100644 index 000000000000..217c990ecb2e --- /dev/null +++ b/tests/arith/generic.js @@ -0,0 +1,7 @@ +/* @flow */ + +function f(a: A): A { return a + a; } // error +function f(a: A, b: B): A {return a + b; } // error +function f(a: A, b: B): A {return b + a; } // error +function f(a: A, b: B): B {return a + b; } // error +function f(a: A, b: B): B {return b + a; } // error diff --git a/tests/arith/jsfmt.spec.js b/tests/arith/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/arith/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/arith/mult.js b/tests/arith/mult.js new file mode 100644 index 000000000000..43620ffbc80c --- /dev/null +++ b/tests/arith/mult.js @@ -0,0 +1,12 @@ +/* @flow */ + +function num(x:number) { } + +num(null * 1); +num(1 * null); + +let x: number = 2 * 3; +x *= 4; + +let y: string = "123"; +y *= 2; // error diff --git a/tests/arith/relational.js b/tests/arith/relational.js new file mode 100644 index 000000000000..f86edee6953e --- /dev/null +++ b/tests/arith/relational.js @@ -0,0 +1,30 @@ +/* @flow */ + +(1 < 2); +(1 < "foo"); // error +("foo" < 1); // error +("foo" < "bar"); +(1 < {foo: 1}); // error +({foo: 1} < 1); // error +({foo: 1} < {foo: 1}); // error +("foo" < {foo: 1}); // error +({foo: 1} < "foo"); // error + +var x = (null : ?number); +(1 < x); // 2 errors: null !~> number; undefined !~> number +(x < 1); // 2 errors: null !~> number; undefined !~> number + +(null < null); // error +(undefined < null); // error +(null < undefined); // error +(undefined < undefined); // error +(NaN < 1); +(1 < NaN); +(NaN < NaN); + +let tests = [ + function(x: any, y: number, z: string) { + (x > y); + (x > z); + }, +]; diff --git a/tests/array-filter/__snapshots__/jsfmt.spec.js.snap b/tests/array-filter/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..bd957a9257f6 --- /dev/null +++ b/tests/array-filter/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,63 @@ +exports[`test test.js 1`] = ` +"/* @flow */ + +function filterOutVoids (arr: Array): Array { + return arr.filter(Boolean) +} + +function filterOutSmall (arr: Array): Array { + return arr.filter(num => num && num > 10) +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:340:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test2.js 1`] = ` +"/* @flow */ + +function filterItems(items: Array): Array { + return items.map(item => { + if (typeof item === \'string\') { + return item.length > 2 ? item : null; + } else { + return item*10; + } + }).filter(Boolean); +} + +const filteredItems = filterItems([\'foo\', \'b\', 1, 2]); + +console.log(filteredItems); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1497:36) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/array-filter/jsfmt.spec.js b/tests/array-filter/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/array-filter/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/array-filter/test.js b/tests/array-filter/test.js new file mode 100644 index 000000000000..9dc5b1096fd7 --- /dev/null +++ b/tests/array-filter/test.js @@ -0,0 +1,9 @@ +/* @flow */ + +function filterOutVoids (arr: Array): Array { + return arr.filter(Boolean) +} + +function filterOutSmall (arr: Array): Array { + return arr.filter(num => num && num > 10) +} diff --git a/tests/array-filter/test2.js b/tests/array-filter/test2.js new file mode 100644 index 000000000000..a51043419dfd --- /dev/null +++ b/tests/array-filter/test2.js @@ -0,0 +1,15 @@ +/* @flow */ + +function filterItems(items: Array): Array { + return items.map(item => { + if (typeof item === 'string') { + return item.length > 2 ? item : null; + } else { + return item*10; + } + }).filter(Boolean); +} + +const filteredItems = filterItems(['foo', 'b', 1, 2]); + +console.log(filteredItems); diff --git a/tests/array_spread/__snapshots__/jsfmt.spec.js.snap b/tests/array_spread/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..d761c1fb2b0e --- /dev/null +++ b/tests/array_spread/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,27 @@ +exports[`test test.js 1`] = ` +"var A = [1,2,3]; +var B = [...A]; +var C = [1,2,3]; +B.sort((a, b) => a - b); +C.sort((a, b) => a - b); + +var x: Array = [\'1\', \'2\']; +var y: Array = [\'3\', ...x]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/array_spread/jsfmt.spec.js b/tests/array_spread/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/array_spread/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/array_spread/test.js b/tests/array_spread/test.js new file mode 100644 index 000000000000..73789d89ad35 --- /dev/null +++ b/tests/array_spread/test.js @@ -0,0 +1,8 @@ +var A = [1,2,3]; +var B = [...A]; +var C = [1,2,3]; +B.sort((a, b) => a - b); +C.sort((a, b) => a - b); + +var x: Array = ['1', '2']; +var y: Array = ['3', ...x]; diff --git a/tests/arraylib/__snapshots__/jsfmt.spec.js.snap b/tests/arraylib/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..22c7fb2420eb --- /dev/null +++ b/tests/arraylib/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,76 @@ +exports[`test array_lib.js 1`] = ` +"/* @flow */ +function foo(x:string) { } + +var a = [0]; +var b = a.map(function (x) { foo(x); return \"\" + x; }); + +var c: number = a[0]; +var d: number = b[0]; + +var e:Array = a.reverse(); + +var f = [\"\"]; +var g:number = f.map(function () { return 0; })[0]; + +var h: Array = [1,2,3]; +var i: Array = [\'a\', \'b\', \'c\']; +var j: Array = h.concat(i); +var k: Array = h.concat(h); +var l: Array = h.concat(1,2,3); +var m: Array = h.concat(\'a\', \'b\', \'c\'); +var n: Array = h.concat(\'a\', \'b\', \'c\'); // Error + +function reduce_test() { + /* Adapted from the following source: + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce + */ + [0, 1, 2, 3, 4].reduce(function(previousValue, currentValue, index, array) { + return previousValue + currentValue + array[index]; + }); + + [0, 1, 2, 3, 4].reduce(function(previousValue, currentValue, index, array) { + return previousValue + currentValue + array[index]; + }, 10); + + var total = [0, 1, 2, 3].reduce(function(a, b) { + return a + b; + }); + + var flattened = [[0, 1], [2, 3], [4, 5]].reduce(function(a, b) { + return a.concat(b); + }); + + /* Added later, because the above is insufficient */ + + // acc is element type of array when no init is provided + [\"\"].reduce((acc, str) => acc * str.length); // error, string ~> number + [\"\"].reduceRight((acc, str) => acc * str.length); // error, string ~> number +} + +function from_test() { + var a: Array = Array.from([1, 2, 3], function(val, index) { + return index % 2 ? \"foo\" : String(val); + }); + var b: Array = Array.from([1, 2, 3], function(val) { + return String(val); + }); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/arraylib/array_lib.js b/tests/arraylib/array_lib.js new file mode 100644 index 000000000000..6db0cbc2d025 --- /dev/null +++ b/tests/arraylib/array_lib.js @@ -0,0 +1,57 @@ +/* @flow */ +function foo(x:string) { } + +var a = [0]; +var b = a.map(function (x) { foo(x); return "" + x; }); + +var c: number = a[0]; +var d: number = b[0]; + +var e:Array = a.reverse(); + +var f = [""]; +var g:number = f.map(function () { return 0; })[0]; + +var h: Array = [1,2,3]; +var i: Array = ['a', 'b', 'c']; +var j: Array = h.concat(i); +var k: Array = h.concat(h); +var l: Array = h.concat(1,2,3); +var m: Array = h.concat('a', 'b', 'c'); +var n: Array = h.concat('a', 'b', 'c'); // Error + +function reduce_test() { + /* Adapted from the following source: + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce + */ + [0, 1, 2, 3, 4].reduce(function(previousValue, currentValue, index, array) { + return previousValue + currentValue + array[index]; + }); + + [0, 1, 2, 3, 4].reduce(function(previousValue, currentValue, index, array) { + return previousValue + currentValue + array[index]; + }, 10); + + var total = [0, 1, 2, 3].reduce(function(a, b) { + return a + b; + }); + + var flattened = [[0, 1], [2, 3], [4, 5]].reduce(function(a, b) { + return a.concat(b); + }); + + /* Added later, because the above is insufficient */ + + // acc is element type of array when no init is provided + [""].reduce((acc, str) => acc * str.length); // error, string ~> number + [""].reduceRight((acc, str) => acc * str.length); // error, string ~> number +} + +function from_test() { + var a: Array = Array.from([1, 2, 3], function(val, index) { + return index % 2 ? "foo" : String(val); + }); + var b: Array = Array.from([1, 2, 3], function(val) { + return String(val); + }); +} diff --git a/tests/arraylib/jsfmt.spec.js b/tests/arraylib/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/arraylib/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/arrays/Arrays.js b/tests/arrays/Arrays.js new file mode 100644 index 000000000000..cc3cd2f6956d --- /dev/null +++ b/tests/arrays/Arrays.js @@ -0,0 +1,39 @@ + +/* @providesModule Arrays */ + +function foo(x:string) { } + +var a = []; +a[0] = 1; +a[1] = "..."; + +foo(a[1]); +var y; +a.forEach(x => y=x); + +// for literals, composite element type is union of individuals +// note: test both tuple and non-tuple inferred literals +var alittle: Array = [0, 1, 2, 3, null]; +var abig: Array = [0, 1, 2, 3, 4, 5, 6, 8, null]; + +var abig2: Array<{x:number; y:number}> = [ + {x:0, y:0}, + {x:0, y:0}, + {x:0, y:0}, + {x:0, y:0}, + {x:0, y:0}, + {x:0, y:0}, + {x:0, y:0}, + {x:0, y:0}, + {x:0, y:0}, + {x:0, y:0}, + {x:0, y:0}, + {x:0, y:0}, + {x:0, y:0}, + {x:0, y:0, a:true}, + {x:0, y:0, b:"hey"}, + {x:0, y:0, c:1}, + {x:0, y:0, c:"hey"} +]; + +module.exports = "arrays"; diff --git a/tests/arrays/__snapshots__/jsfmt.spec.js.snap b/tests/arrays/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..e2440d0d381c --- /dev/null +++ b/tests/arrays/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,77 @@ +exports[`test Arrays.js 1`] = ` +" +/* @providesModule Arrays */ + +function foo(x:string) { } + +var a = []; +a[0] = 1; +a[1] = \"...\"; + +foo(a[1]); +var y; +a.forEach(x => y=x); + +// for literals, composite element type is union of individuals +// note: test both tuple and non-tuple inferred literals +var alittle: Array = [0, 1, 2, 3, null]; +var abig: Array = [0, 1, 2, 3, 4, 5, 6, 8, null]; + +var abig2: Array<{x:number; y:number}> = [ + {x:0, y:0}, + {x:0, y:0}, + {x:0, y:0}, + {x:0, y:0}, + {x:0, y:0}, + {x:0, y:0}, + {x:0, y:0}, + {x:0, y:0}, + {x:0, y:0}, + {x:0, y:0}, + {x:0, y:0}, + {x:0, y:0}, + {x:0, y:0}, + {x:0, y:0, a:true}, + {x:0, y:0, b:\"hey\"}, + {x:0, y:0, c:1}, + {x:0, y:0, c:\"hey\"} +]; + +module.exports = \"arrays\"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test numeric_elem.js 1`] = ` +"var arr = []; +var day = new Date; + +// Date instances are numeric (see Flow_js.numeric) and thus can index into +// arrays. +arr[day] = 0; +(arr[day]: string); // error: number ~> string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var arr = [ ]; +var day = new Date(); +// Date instances are numeric (see Flow_js.numeric) and thus can index into +// arrays. +arr[day] = 0; +(arr[day]: string);// error: number ~> string + +" +`; diff --git a/tests/arrays/jsfmt.spec.js b/tests/arrays/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/arrays/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/arrays/numeric_elem.js b/tests/arrays/numeric_elem.js new file mode 100644 index 000000000000..afdffa392b14 --- /dev/null +++ b/tests/arrays/numeric_elem.js @@ -0,0 +1,7 @@ +var arr = []; +var day = new Date; + +// Date instances are numeric (see Flow_js.numeric) and thus can index into +// arrays. +arr[day] = 0; +(arr[day]: string); // error: number ~> string diff --git a/tests/arrows/__snapshots__/jsfmt.spec.js.snap b/tests/arrows/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..c62df90c14c6 --- /dev/null +++ b/tests/arrows/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,60 @@ +exports[`test advanced_arrows.js 1`] = ` +"/** + * @flow + */ + +var add = (x: number, y: number): number => x + y; + +var bad = (x: number): string => x; // Error! + +var ident = (x: T): T => x; +(ident(1): number); +(ident(\"hi\"): number); // Error +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:367:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test arrows.js 1`] = ` +"function selectBestEffortImageForWidth( + maxWidth: number, + images: Array +): Image { + var maxPixelWidth = maxWidth; + //images = images.sort(function (a, b) { return a.width - b.width }); + images = images.sort((a, b) => (a.width - b.width) + \"\"); + return images.find(image => image.width >= maxPixelWidth) || + images[images.length - 1]; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/arrows/advanced_arrows.js b/tests/arrows/advanced_arrows.js new file mode 100644 index 000000000000..010ca4c71a77 --- /dev/null +++ b/tests/arrows/advanced_arrows.js @@ -0,0 +1,11 @@ +/** + * @flow + */ + +var add = (x: number, y: number): number => x + y; + +var bad = (x: number): string => x; // Error! + +var ident = (x: T): T => x; +(ident(1): number); +(ident("hi"): number); // Error diff --git a/tests/arrows/arrows.js b/tests/arrows/arrows.js new file mode 100644 index 000000000000..f25cc0fb3c40 --- /dev/null +++ b/tests/arrows/arrows.js @@ -0,0 +1,10 @@ +function selectBestEffortImageForWidth( + maxWidth: number, + images: Array +): Image { + var maxPixelWidth = maxWidth; + //images = images.sort(function (a, b) { return a.width - b.width }); + images = images.sort((a, b) => (a.width - b.width) + ""); + return images.find(image => image.width >= maxPixelWidth) || + images[images.length - 1]; +} diff --git a/tests/arrows/jsfmt.spec.js b/tests/arrows/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/arrows/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/ast_tokens/__snapshots__/jsfmt.spec.js.snap b/tests/ast_tokens/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..a4b93d5aafe9 --- /dev/null +++ b/tests/ast_tokens/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,34 @@ +exports[`test foo.js 1`] = ` +"// Regex literals require a lexer mode change +/asdf/g; + +// JSX also requires a lexer mode change +
asdf
; + +// Async arrow functions require backtracking +var a = async () => 42; + +// Type annotations have a special lex mode +var a: number = 42; + +// Token stream should continue even with parse errors +var var +a; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (14:4) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$2.parseBindingAtom (/node_modules/babylon/lib/index.js:2929:12) + at Parser.pp$1.parseVarHead (/node_modules/babylon/lib/index.js:2226:18) + at Parser.parseVarHead (/node_modules/babylon/lib/index.js:5611:13) + at Parser.pp$1.parseVar (/node_modules/babylon/lib/index.js:2209:10) + at Parser.pp$1.parseVarStatement (/node_modules/babylon/lib/index.js:2042:8) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1735:19) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) +" +`; diff --git a/tests/ast_tokens/foo.js b/tests/ast_tokens/foo.js new file mode 100644 index 000000000000..cca10855f7a7 --- /dev/null +++ b/tests/ast_tokens/foo.js @@ -0,0 +1,15 @@ +// Regex literals require a lexer mode change +/asdf/g; + +// JSX also requires a lexer mode change +
asdf
; + +// Async arrow functions require backtracking +var a = async () => 42; + +// Type annotations have a special lex mode +var a: number = 42; + +// Token stream should continue even with parse errors +var var +a; diff --git a/tests/ast_tokens/jsfmt.spec.js b/tests/ast_tokens/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/ast_tokens/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/async/__snapshots__/jsfmt.spec.js.snap b/tests/async/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..a3c8cadc6de3 --- /dev/null +++ b/tests/async/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,471 @@ +exports[`test async.js 1`] = ` +"// @flow + +// \"For async functions, a Promise is returned, +// and the type of return expressions must be T.\" +// + +async function f0(): Promise { + return 1; +} + +async function f1(): Promise { + return 1; // error, number != bool +} + +// await: (p: Promise | T) => T +// + +async function f2(p: Promise): Promise { + var x: number = await p; + var y: number = await 1; + return x + y; +} + +async function f3(p: Promise): Promise { + return await p; +} + +// TODO: this is one of those bad generic errors, currently: +// \"inconsistent use of library definitions\" with two core.js locs +async function f4(p: Promise): Promise { + return await p; // error, number != bool +} + +// async arrow functions +// + +var f5: () => Promise = async () => await 1; + +// async methods +// + +class C { + async m() { return 1; } + async mt(a: T): Promise { return a; } + static async m(a): void { await a; } // error, void != Promise + static async mt(a: T): Promise { return a; } +} + +// async function props + +var obj = { f: async () => await 1 }; +var objf : () => Promise = obj.f; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test async_base_class.js 1`] = ` +"// This is kind of weird, but it should parse. This works in babel without the +// parens around (await promise). From the es6 and async/await specs I (nmote) +// am not clear on whether it should. In any case it\'s a strange corner case +// that is probably not important to support. +class C {}; + +var P: Promise> = new Promise(function (resolve, reject) { + resolve(C); +}); + +async function foo() { + class Bar extends (await P) { } + return Bar; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test async_parse.js 1`] = ` +"async function f() {} +async function ft(a: T) {} + +class C { + async m() {} + async mt(a: T) {} + static async m(a) {} + static async mt(a: T) {} +} + +var e = async function () {}; +var et = async function (a: T) {}; + +var n = new async function() {}; + +var o = { async m() {} }; +var ot = { async m(a: T) {} }; +var oz = { async async() {} }; + +var x = { async : 5 }; +console.log(x.async); + +var async = 3; +var y = { async }; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:340:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test async_promise.js 1`] = ` +"async function f(): Promise { + return Promise.resolve(1); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test async_return_void.js 1`] = ` +"// @flow + +async function foo1(): Promise { + return; +} + +async function foo2(): Promise { + return undefined; +} + +async function foo3(): Promise { + function bar() { } + return bar(); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test async2.js 1`] = ` +"// @flow + +// misc basic + +function test1() { + async function foo() { + return 42; + } + + async function bar() { + var a = await foo(); + var b: number = a; // valid + var c: string = a; // Error: number ~> string + } +} + +// +// void returns: +// + +// inference should produce return type Promise +// in the absence of an explicit return +// + +function test2() { + async function voidoid1() { + console.log(\"HEY\"); + } + + var voidoid2: () => Promise = voidoid1; // ok + + var voidoid3: () => void = voidoid1; // error, void != Promise +} + +// annotated return type of Promise should work +// + +function test3() { + async function voidoid4(): Promise { // ok + console.log(\"HEY\"); + } +} + +// other annotated return types should fail +// (note: misannotated return types with explicit +// return statements are covered in async.js) +// + +function test4() { + async function voidoid5(): void { // error, void != Promise + console.log(\"HEY\"); + } +} + +function test5() { + async function voidoid6() + : Promise { // error, number != void + console.log(\"HEY\"); + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test async3.js 1`] = ` +"// @flow + +/** + * test nested-promise unwrapping. + * Note: currently we don\'t do this properly in the underlying + * type of the Promise class, which causes spurious errors to + * be raised here. Once that\'s fixed, the errors here will go + * away. + */ + +async function foo() { + return 42; +} + +async function bar() { + return foo(); +} + +async function baz() { + + // a should now be typed as number, but is in fact + // Promise until nested-promise unwrap is fixed + var a = await bar(); + + // TODO this is valid code, but currently gives Promise ~> number error + // due to lack of transitive Promise unwrap. + var b: number = a; + + // should be number ~> string error, but currently gives + // Promise ~> string error for the same reason + var c: string = a; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +/** + * test nested-promise unwrapping. + * Note: currently we don\'t do this properly in the underlying + * type of the Promise class, which causes spurious errors to + * be raised here. Once that\'s fixed, the errors here will go + * away. + */ +async function foo() { + return 42; +} +async function bar() { + return foo(); +} +async function baz() { + // a should now be typed as number, but is in fact + // Promise until nested-promise unwrap is fixed + var a = await bar(); + // TODO this is valid code, but currently gives Promise ~> number error + // due to lack of transitive Promise unwrap. + var b: number = a; + // should be number ~> string error, but currently gives + // Promise ~> string error for the same reason + var c: string = a; +} + +" +`; + +exports[`test await_not_in_async.js 1`] = ` +"// note: currently, await-within-async enforcement is a +// done by the parser, which bails after a single error. + +function f() { + await 1; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: await is a reserved word (5:2) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp$3.checkReservedWord (/node_modules/babylon/lib/index.js:4143:10) + at Parser.pp$3.parseIdentifier (/node_modules/babylon/lib/index.js:4121:12) + at Parser.pp$3.parseExprAtom (/node_modules/babylon/lib/index.js:3490:21) + at Parser.parseExprAtom (/node_modules/babylon/lib/index.js:6408:22) + at Parser.pp$3.parseExprSubscripts (/node_modules/babylon/lib/index.js:3337:19) + at Parser.pp$3.parseMaybeUnary (/node_modules/babylon/lib/index.js:3317:19) + at Parser.pp$3.parseExprOps (/node_modules/babylon/lib/index.js:3247:19) + at Parser.pp$3.parseMaybeConditional (/node_modules/babylon/lib/index.js:3224:19) + at Parser.pp$3.parseMaybeAssign (/node_modules/babylon/lib/index.js:3187:19) +" +`; + +exports[`test await_not_in_async2.js 1`] = ` +"// note: currently, await-within-async enforcement is a +// done by the parser, which bails after a single error. + +function f(x) { return x; } + +f(await 1); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: await is a reserved word (6:2) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp$3.checkReservedWord (/node_modules/babylon/lib/index.js:4143:10) + at Parser.pp$3.parseIdentifier (/node_modules/babylon/lib/index.js:4121:12) + at Parser.pp$3.parseExprAtom (/node_modules/babylon/lib/index.js:3490:21) + at Parser.parseExprAtom (/node_modules/babylon/lib/index.js:6408:22) + at Parser.pp$3.parseExprSubscripts (/node_modules/babylon/lib/index.js:3337:19) + at Parser.pp$3.parseMaybeUnary (/node_modules/babylon/lib/index.js:3317:19) + at Parser.pp$3.parseExprOps (/node_modules/babylon/lib/index.js:3247:19) + at Parser.pp$3.parseMaybeConditional (/node_modules/babylon/lib/index.js:3224:19) + at Parser.pp$3.parseMaybeAssign (/node_modules/babylon/lib/index.js:3187:19) +" +`; + +exports[`test await_not_in_async3.js 1`] = ` +"// note: currently, await-within-async enforcement is a +// done by the parser, which bails after a single error. + +async function f(x) { return x; } + +f(await 1); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: await is a reserved word (6:2) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp$3.checkReservedWord (/node_modules/babylon/lib/index.js:4143:10) + at Parser.pp$3.parseIdentifier (/node_modules/babylon/lib/index.js:4121:12) + at Parser.pp$3.parseExprAtom (/node_modules/babylon/lib/index.js:3490:21) + at Parser.parseExprAtom (/node_modules/babylon/lib/index.js:6408:22) + at Parser.pp$3.parseExprSubscripts (/node_modules/babylon/lib/index.js:3337:19) + at Parser.pp$3.parseMaybeUnary (/node_modules/babylon/lib/index.js:3317:19) + at Parser.pp$3.parseExprOps (/node_modules/babylon/lib/index.js:3247:19) + at Parser.pp$3.parseMaybeConditional (/node_modules/babylon/lib/index.js:3224:19) + at Parser.pp$3.parseMaybeAssign (/node_modules/babylon/lib/index.js:3187:19) +" +`; + +exports[`test await_parse.js 1`] = ` +"async function f() { await 1; } +async function ft(a: T) { await 1; } + +class C { + async m() { await 1; } + async mt(a: T) { await 1; } + static async m(a) { await 1; } + static async mt(a: T) { await 1; } +} + +var e = async function () { await 1; }; +var et = async function (a: T) { await 1; }; + +var n = new async function() { await 1; }; + +var o = { async m() { await 1; } }; +var ot = { async m(a: T) { await 1; } }; +var oz = { async async(async) { await async; } }; + +var x = { await : 5 }; +console.log(x.await); + +var await = 3; +var y = { await }; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: await is a reserved word (23:4) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp$3.checkReservedWord (/node_modules/babylon/lib/index.js:4143:10) + at Parser.pp$2.checkLVal (/node_modules/babylon/lib/index.js:2990:12) + at Parser.checkLVal (/node_modules/babylon/lib/index.js:5446:22) + at Parser.pp$1.parseVarHead (/node_modules/babylon/lib/index.js:2227:8) + at Parser.parseVarHead (/node_modules/babylon/lib/index.js:5611:13) + at Parser.pp$1.parseVar (/node_modules/babylon/lib/index.js:2209:10) + at Parser.pp$1.parseVarStatement (/node_modules/babylon/lib/index.js:2042:8) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1735:19) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) +" +`; diff --git a/tests/async/async.js b/tests/async/async.js new file mode 100644 index 000000000000..85d27b27b3a9 --- /dev/null +++ b/tests/async/async.js @@ -0,0 +1,52 @@ +// @flow + +// "For async functions, a Promise is returned, +// and the type of return expressions must be T." +// + +async function f0(): Promise { + return 1; +} + +async function f1(): Promise { + return 1; // error, number != bool +} + +// await: (p: Promise | T) => T +// + +async function f2(p: Promise): Promise { + var x: number = await p; + var y: number = await 1; + return x + y; +} + +async function f3(p: Promise): Promise { + return await p; +} + +// TODO: this is one of those bad generic errors, currently: +// "inconsistent use of library definitions" with two core.js locs +async function f4(p: Promise): Promise { + return await p; // error, number != bool +} + +// async arrow functions +// + +var f5: () => Promise = async () => await 1; + +// async methods +// + +class C { + async m() { return 1; } + async mt(a: T): Promise { return a; } + static async m(a): void { await a; } // error, void != Promise + static async mt(a: T): Promise { return a; } +} + +// async function props + +var obj = { f: async () => await 1 }; +var objf : () => Promise = obj.f; diff --git a/tests/async/async2.js b/tests/async/async2.js new file mode 100644 index 000000000000..79e69a7c1070 --- /dev/null +++ b/tests/async/async2.js @@ -0,0 +1,60 @@ +// @flow + +// misc basic + +function test1() { + async function foo() { + return 42; + } + + async function bar() { + var a = await foo(); + var b: number = a; // valid + var c: string = a; // Error: number ~> string + } +} + +// +// void returns: +// + +// inference should produce return type Promise +// in the absence of an explicit return +// + +function test2() { + async function voidoid1() { + console.log("HEY"); + } + + var voidoid2: () => Promise = voidoid1; // ok + + var voidoid3: () => void = voidoid1; // error, void != Promise +} + +// annotated return type of Promise should work +// + +function test3() { + async function voidoid4(): Promise { // ok + console.log("HEY"); + } +} + +// other annotated return types should fail +// (note: misannotated return types with explicit +// return statements are covered in async.js) +// + +function test4() { + async function voidoid5(): void { // error, void != Promise + console.log("HEY"); + } +} + +function test5() { + async function voidoid6() + : Promise { // error, number != void + console.log("HEY"); + } +} diff --git a/tests/async/async3.js b/tests/async/async3.js new file mode 100644 index 000000000000..d0062d5f21a7 --- /dev/null +++ b/tests/async/async3.js @@ -0,0 +1,32 @@ +// @flow + +/** + * test nested-promise unwrapping. + * Note: currently we don't do this properly in the underlying + * type of the Promise class, which causes spurious errors to + * be raised here. Once that's fixed, the errors here will go + * away. + */ + +async function foo() { + return 42; +} + +async function bar() { + return foo(); +} + +async function baz() { + + // a should now be typed as number, but is in fact + // Promise until nested-promise unwrap is fixed + var a = await bar(); + + // TODO this is valid code, but currently gives Promise ~> number error + // due to lack of transitive Promise unwrap. + var b: number = a; + + // should be number ~> string error, but currently gives + // Promise ~> string error for the same reason + var c: string = a; +} diff --git a/tests/async/async_base_class.js b/tests/async/async_base_class.js new file mode 100644 index 000000000000..de7afc58ec61 --- /dev/null +++ b/tests/async/async_base_class.js @@ -0,0 +1,14 @@ +// This is kind of weird, but it should parse. This works in babel without the +// parens around (await promise). From the es6 and async/await specs I (nmote) +// am not clear on whether it should. In any case it's a strange corner case +// that is probably not important to support. +class C {}; + +var P: Promise> = new Promise(function (resolve, reject) { + resolve(C); +}); + +async function foo() { + class Bar extends (await P) { } + return Bar; +} diff --git a/tests/async/async_parse.js b/tests/async/async_parse.js new file mode 100644 index 000000000000..e633ae4efc65 --- /dev/null +++ b/tests/async/async_parse.js @@ -0,0 +1,24 @@ +async function f() {} +async function ft(a: T) {} + +class C { + async m() {} + async mt(a: T) {} + static async m(a) {} + static async mt(a: T) {} +} + +var e = async function () {}; +var et = async function (a: T) {}; + +var n = new async function() {}; + +var o = { async m() {} }; +var ot = { async m(a: T) {} }; +var oz = { async async() {} }; + +var x = { async : 5 }; +console.log(x.async); + +var async = 3; +var y = { async }; diff --git a/tests/async/async_promise.js b/tests/async/async_promise.js new file mode 100644 index 000000000000..8c2ce87dbd42 --- /dev/null +++ b/tests/async/async_promise.js @@ -0,0 +1,3 @@ +async function f(): Promise { + return Promise.resolve(1); +} diff --git a/tests/async/async_return_void.js b/tests/async/async_return_void.js new file mode 100644 index 000000000000..1e312f63386e --- /dev/null +++ b/tests/async/async_return_void.js @@ -0,0 +1,14 @@ +// @flow + +async function foo1(): Promise { + return; +} + +async function foo2(): Promise { + return undefined; +} + +async function foo3(): Promise { + function bar() { } + return bar(); +} diff --git a/tests/async/await_not_in_async.js b/tests/async/await_not_in_async.js new file mode 100644 index 000000000000..cbcc4191ac41 --- /dev/null +++ b/tests/async/await_not_in_async.js @@ -0,0 +1,6 @@ +// note: currently, await-within-async enforcement is a +// done by the parser, which bails after a single error. + +function f() { + await 1; +} diff --git a/tests/async/await_not_in_async2.js b/tests/async/await_not_in_async2.js new file mode 100644 index 000000000000..0037d0905eb3 --- /dev/null +++ b/tests/async/await_not_in_async2.js @@ -0,0 +1,6 @@ +// note: currently, await-within-async enforcement is a +// done by the parser, which bails after a single error. + +function f(x) { return x; } + +f(await 1); diff --git a/tests/async/await_not_in_async3.js b/tests/async/await_not_in_async3.js new file mode 100644 index 000000000000..cacb5dc6749a --- /dev/null +++ b/tests/async/await_not_in_async3.js @@ -0,0 +1,6 @@ +// note: currently, await-within-async enforcement is a +// done by the parser, which bails after a single error. + +async function f(x) { return x; } + +f(await 1); diff --git a/tests/async/await_parse.js b/tests/async/await_parse.js new file mode 100644 index 000000000000..10149204ecd7 --- /dev/null +++ b/tests/async/await_parse.js @@ -0,0 +1,24 @@ +async function f() { await 1; } +async function ft(a: T) { await 1; } + +class C { + async m() { await 1; } + async mt(a: T) { await 1; } + static async m(a) { await 1; } + static async mt(a: T) { await 1; } +} + +var e = async function () { await 1; }; +var et = async function (a: T) { await 1; }; + +var n = new async function() { await 1; }; + +var o = { async m() { await 1; } }; +var ot = { async m(a: T) { await 1; } }; +var oz = { async async(async) { await async; } }; + +var x = { await : 5 }; +console.log(x.await); + +var await = 3; +var y = { await }; diff --git a/tests/async/jsfmt.spec.js b/tests/async/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/async/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/async_iteration/__snapshots__/jsfmt.spec.js.snap b/tests/async_iteration/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..a9bab4ede39d --- /dev/null +++ b/tests/async_iteration/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,213 @@ +exports[`test delegate_yield.js 1`] = ` +"async function *delegate_next() { + async function *inner() { + var x: void = yield; // error: number ~> void + } + yield *inner(); +} +delegate_next().next(0); + +async function *delegate_yield() { + async function *inner() { + yield 0; + } + yield *inner(); +} +(async () => { + for await (const x of delegate_yield()) { + (x: void); // error: number ~> void + } +}); + +async function *delegate_return() { + async function *inner() { + return 0; + } + var x: void = yield *inner(); // error: number ~> void +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +async function* delegate_next() { + async function* inner() { + var x: void = yield;// error: number ~> void + } + yield* inner(); +} +delegate_next().next(0); +async function* delegate_yield() { + async function* inner() { + yield 0; + } + yield* inner(); +} +async () => { + for await (const x of delegate_yield()) { + (x: void);// error: number ~> void + } +}; +async function* delegate_return() { + async function* inner() { + return 0; + } + var x: void = yield* inner();// error: number ~> void +} + +" +`; + +exports[`test generator.js 1`] = ` +"declare interface File { + readLine(): Promise; + close(): void; + EOF: boolean; +} + +declare function fileOpen(path: string): Promise; + +async function* readLines(path) { + let file: File = await fileOpen(path); + + try { + while (!file.EOF) { + yield await file.readLine(); + } + } finally { + file.close(); + } +} + +async function f() { + for await (const line of readLines(\"/path/to/file\")) { + (line: void); // error: string ~> void + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1391 + fromString(\", \").join(path.map(print, \"extends\")) + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1391:28) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:238:16 + at FastPath.call (/src/fast-path.js:113:16) +" +`; + +exports[`test return.js 1`] = ` +"declare var gen: AsyncGenerator; + +// You can pass whatever you like to return, it doesn\'t need to be related to +// the AsyncGenerator\'s return type +gen.return(0).then(result => { + (result.value: void); // error: string | number ~> void +}); + +// However, a generator can \"refuse\" the return by catching an exception and +// yielding or returning internally. +async function *refuse_return() { + try { + yield 1; + } finally { + return 0; + } +} +refuse_return().return(\"string\").then(result => { + if (result.done) { + (result.value: string); // error: number | void ~> string + } +}); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test throw.js 1`] = ` +"async function *catch_return() { + try { + yield 0; + } catch (e) { + return e; + } +} + +(async () => { + catch_return().throw(\"\").then(({value}) => { + if (value !== undefined) { + (value: void); // error: number ~> void + } + }); +}); + +async function *yield_return() { + try { + yield 0; + return; + } catch (e) { + yield e; + } +} + +(async () => { + yield_return().throw(\"\").then(({value}) => { + if (value !== undefined) { + (value: void); // error: number ~> void + } + }); +}); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +async function* catch_return() { + try { + yield 0; + } catch (e) { + return e; + } +} +async () => { + catch_return().throw(\"\").then( + ({ value }) => { + if (value !== undefined) { + (value: void);// error: number ~> void + } + } + ); +}; +async function* yield_return() { + try { + yield 0; + return; + } catch (e) { + yield e; + } +} +async () => { + yield_return().throw(\"\").then( + ({ value }) => { + if (value !== undefined) { + (value: void);// error: number ~> void + } + } + ); +}; + +" +`; diff --git a/tests/async_iteration/delegate_yield.js b/tests/async_iteration/delegate_yield.js new file mode 100644 index 000000000000..965cfadfaea7 --- /dev/null +++ b/tests/async_iteration/delegate_yield.js @@ -0,0 +1,26 @@ +async function *delegate_next() { + async function *inner() { + var x: void = yield; // error: number ~> void + } + yield *inner(); +} +delegate_next().next(0); + +async function *delegate_yield() { + async function *inner() { + yield 0; + } + yield *inner(); +} +(async () => { + for await (const x of delegate_yield()) { + (x: void); // error: number ~> void + } +}); + +async function *delegate_return() { + async function *inner() { + return 0; + } + var x: void = yield *inner(); // error: number ~> void +} diff --git a/tests/async_iteration/generator.js b/tests/async_iteration/generator.js new file mode 100644 index 000000000000..76213a59f63d --- /dev/null +++ b/tests/async_iteration/generator.js @@ -0,0 +1,25 @@ +declare interface File { + readLine(): Promise; + close(): void; + EOF: boolean; +} + +declare function fileOpen(path: string): Promise; + +async function* readLines(path) { + let file: File = await fileOpen(path); + + try { + while (!file.EOF) { + yield await file.readLine(); + } + } finally { + file.close(); + } +} + +async function f() { + for await (const line of readLines("/path/to/file")) { + (line: void); // error: string ~> void + } +} diff --git a/tests/async_iteration/jsfmt.spec.js b/tests/async_iteration/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/async_iteration/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/async_iteration/return.js b/tests/async_iteration/return.js new file mode 100644 index 000000000000..28cbc3deb4c1 --- /dev/null +++ b/tests/async_iteration/return.js @@ -0,0 +1,22 @@ +declare var gen: AsyncGenerator; + +// You can pass whatever you like to return, it doesn't need to be related to +// the AsyncGenerator's return type +gen.return(0).then(result => { + (result.value: void); // error: string | number ~> void +}); + +// However, a generator can "refuse" the return by catching an exception and +// yielding or returning internally. +async function *refuse_return() { + try { + yield 1; + } finally { + return 0; + } +} +refuse_return().return("string").then(result => { + if (result.done) { + (result.value: string); // error: number | void ~> string + } +}); diff --git a/tests/async_iteration/throw.js b/tests/async_iteration/throw.js new file mode 100644 index 000000000000..ae6c629bbfa5 --- /dev/null +++ b/tests/async_iteration/throw.js @@ -0,0 +1,32 @@ +async function *catch_return() { + try { + yield 0; + } catch (e) { + return e; + } +} + +(async () => { + catch_return().throw("").then(({value}) => { + if (value !== undefined) { + (value: void); // error: number ~> void + } + }); +}); + +async function *yield_return() { + try { + yield 0; + return; + } catch (e) { + yield e; + } +} + +(async () => { + yield_return().throw("").then(({value}) => { + if (value !== undefined) { + (value: void); // error: number ~> void + } + }); +}); diff --git a/tests/autocomplete/__snapshots__/jsfmt.spec.js.snap b/tests/autocomplete/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..8c4dbb8e1566 --- /dev/null +++ b/tests/autocomplete/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,582 @@ +exports[`test bar.js 1`] = ` +"// @flow + +var o = require(\'./unknown\'); +o.x. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (5:0) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$3.parseIdentifier (/node_modules/babylon/lib/index.js:4128:10) + at Parser.pp$3.parseSubscripts (/node_modules/babylon/lib/index.js:3360:30) + at Parser.pp$3.parseExprSubscripts (/node_modules/babylon/lib/index.js:3347:15) + at Parser.pp$3.parseMaybeUnary (/node_modules/babylon/lib/index.js:3317:19) + at Parser.pp$3.parseExprOps (/node_modules/babylon/lib/index.js:3247:19) + at Parser.pp$3.parseMaybeConditional (/node_modules/babylon/lib/index.js:3224:19) + at Parser.pp$3.parseMaybeAssign (/node_modules/babylon/lib/index.js:3187:19) + at Parser.parseMaybeAssign (/node_modules/babylon/lib/index.js:5700:20) +" +`; + +exports[`test bool.js 1`] = ` +"// @flow + +true. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (4:0) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$3.parseIdentifier (/node_modules/babylon/lib/index.js:4128:10) + at Parser.pp$3.parseSubscripts (/node_modules/babylon/lib/index.js:3360:30) + at Parser.pp$3.parseExprSubscripts (/node_modules/babylon/lib/index.js:3347:15) + at Parser.pp$3.parseMaybeUnary (/node_modules/babylon/lib/index.js:3317:19) + at Parser.pp$3.parseExprOps (/node_modules/babylon/lib/index.js:3247:19) + at Parser.pp$3.parseMaybeConditional (/node_modules/babylon/lib/index.js:3224:19) + at Parser.pp$3.parseMaybeAssign (/node_modules/babylon/lib/index.js:3187:19) + at Parser.parseMaybeAssign (/node_modules/babylon/lib/index.js:5700:20) +" +`; + +exports[`test customfun.js 1`] = ` +"// @flow + +declare var idx: $Facebookism$Idx; +declare var merge: $Facebookism$Merge; +declare var mergeDeepInto: $Facebookism$MergeDeepInto; +declare var mergeInto: $Facebookism$MergeInto; +declare var mixin: $Facebookism$Mixin; +declare var objectGetPrototypeOf: Object$GetPrototypeOf +declare var objectAssign: Object$Assign + +m +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +declare var idx: $Facebookism$Idx; +declare var merge: $Facebookism$Merge; +declare var mergeDeepInto: $Facebookism$MergeDeepInto; +declare var mergeInto: $Facebookism$MergeInto; +declare var mixin: $Facebookism$Mixin; +declare var objectGetPrototypeOf: Object$GetPrototypeOf; +declare var objectAssign: Object$Assign; +m; + +" +`; + +exports[`test foo.js 1`] = ` +"/** + * @flow + */ + +var obj = { + num: 123, + str: \'hello\', +}; + +obj. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (11:0) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$3.parseIdentifier (/node_modules/babylon/lib/index.js:4128:10) + at Parser.pp$3.parseSubscripts (/node_modules/babylon/lib/index.js:3360:30) + at Parser.pp$3.parseExprSubscripts (/node_modules/babylon/lib/index.js:3347:15) + at Parser.pp$3.parseMaybeUnary (/node_modules/babylon/lib/index.js:3317:19) + at Parser.pp$3.parseExprOps (/node_modules/babylon/lib/index.js:3247:19) + at Parser.pp$3.parseMaybeConditional (/node_modules/babylon/lib/index.js:3224:19) + at Parser.pp$3.parseMaybeAssign (/node_modules/babylon/lib/index.js:3187:19) + at Parser.parseMaybeAssign (/node_modules/babylon/lib/index.js:5700:20) +" +`; + +exports[`test foo_parse_fail.js 1`] = ` +"/** + * @flow + */ + +var obj = { + num: 123, + str: \'hello\', +}; + +console.log(obj. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (11:0) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$3.parseIdentifier (/node_modules/babylon/lib/index.js:4128:10) + at Parser.pp$3.parseSubscripts (/node_modules/babylon/lib/index.js:3360:30) + at Parser.pp$3.parseExprSubscripts (/node_modules/babylon/lib/index.js:3347:15) + at Parser.pp$3.parseMaybeUnary (/node_modules/babylon/lib/index.js:3317:19) + at Parser.pp$3.parseExprOps (/node_modules/babylon/lib/index.js:3247:19) + at Parser.pp$3.parseMaybeConditional (/node_modules/babylon/lib/index.js:3224:19) + at Parser.pp$3.parseMaybeAssign (/node_modules/babylon/lib/index.js:3187:19) + at Parser.parseMaybeAssign (/node_modules/babylon/lib/index.js:5700:20) +" +`; + +exports[`test fun.js 1`] = ` +"/* @flow */ + +function foo(f: () => number) { + f. +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (5:0) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$3.parseIdentifier (/node_modules/babylon/lib/index.js:4128:10) + at Parser.pp$3.parseSubscripts (/node_modules/babylon/lib/index.js:3360:30) + at Parser.pp$3.parseExprSubscripts (/node_modules/babylon/lib/index.js:3347:15) + at Parser.pp$3.parseMaybeUnary (/node_modules/babylon/lib/index.js:3317:19) + at Parser.pp$3.parseExprOps (/node_modules/babylon/lib/index.js:3247:19) + at Parser.pp$3.parseMaybeConditional (/node_modules/babylon/lib/index.js:3224:19) + at Parser.pp$3.parseMaybeAssign (/node_modules/babylon/lib/index.js:3187:19) + at Parser.parseMaybeAssign (/node_modules/babylon/lib/index.js:5700:20) +" +`; + +exports[`test function_builtins.js 1`] = ` +"/* @flow */ + +function foo(f: Function) { + f. +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (5:0) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$3.parseIdentifier (/node_modules/babylon/lib/index.js:4128:10) + at Parser.pp$3.parseSubscripts (/node_modules/babylon/lib/index.js:3360:30) + at Parser.pp$3.parseExprSubscripts (/node_modules/babylon/lib/index.js:3347:15) + at Parser.pp$3.parseMaybeUnary (/node_modules/babylon/lib/index.js:3317:19) + at Parser.pp$3.parseExprOps (/node_modules/babylon/lib/index.js:3247:19) + at Parser.pp$3.parseMaybeConditional (/node_modules/babylon/lib/index.js:3224:19) + at Parser.pp$3.parseMaybeAssign (/node_modules/babylon/lib/index.js:3187:19) + at Parser.parseMaybeAssign (/node_modules/babylon/lib/index.js:5700:20) +" +`; + +exports[`test generics.js 1`] = ` +"// @flow + +class C { } + +function foo(o: { cn: C }) { + o. +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (7:0) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$3.parseIdentifier (/node_modules/babylon/lib/index.js:4128:10) + at Parser.pp$3.parseSubscripts (/node_modules/babylon/lib/index.js:3360:30) + at Parser.pp$3.parseExprSubscripts (/node_modules/babylon/lib/index.js:3347:15) + at Parser.pp$3.parseMaybeUnary (/node_modules/babylon/lib/index.js:3317:19) + at Parser.pp$3.parseExprOps (/node_modules/babylon/lib/index.js:3247:19) + at Parser.pp$3.parseMaybeConditional (/node_modules/babylon/lib/index.js:3224:19) + at Parser.pp$3.parseMaybeAssign (/node_modules/babylon/lib/index.js:3187:19) + at Parser.parseMaybeAssign (/node_modules/babylon/lib/index.js:5700:20) +" +`; + +exports[`test if.js 1`] = ` +"// @flow +const x = \'\'; +if (x.) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (3:6) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$3.parseIdentifier (/node_modules/babylon/lib/index.js:4128:10) + at Parser.pp$3.parseSubscripts (/node_modules/babylon/lib/index.js:3360:30) + at Parser.pp$3.parseExprSubscripts (/node_modules/babylon/lib/index.js:3347:15) + at Parser.pp$3.parseMaybeUnary (/node_modules/babylon/lib/index.js:3317:19) + at Parser.pp$3.parseExprOps (/node_modules/babylon/lib/index.js:3247:19) + at Parser.pp$3.parseMaybeConditional (/node_modules/babylon/lib/index.js:3224:19) + at Parser.pp$3.parseMaybeAssign (/node_modules/babylon/lib/index.js:3187:19) + at Parser.parseMaybeAssign (/node_modules/babylon/lib/index.js:5700:20) +" +`; + +exports[`test issue-1368.js 1`] = ` +"/* @flow */ +class Test { + prop: number; + + constructor(prop: number) { + this.prop = prop; + } +} + +class ExtendTest extends Test { + extended: string; + + constructor(extended: string) { + super(10); + this.extended = extended; + } + + method() { + this.prop = 12; + this./* here */ + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (21:2) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$3.parseIdentifier (/node_modules/babylon/lib/index.js:4128:10) + at Parser.pp$3.parseSubscripts (/node_modules/babylon/lib/index.js:3360:30) + at Parser.pp$3.parseExprSubscripts (/node_modules/babylon/lib/index.js:3347:15) + at Parser.pp$3.parseMaybeUnary (/node_modules/babylon/lib/index.js:3317:19) + at Parser.pp$3.parseExprOps (/node_modules/babylon/lib/index.js:3247:19) + at Parser.pp$3.parseMaybeConditional (/node_modules/babylon/lib/index.js:3224:19) + at Parser.pp$3.parseMaybeAssign (/node_modules/babylon/lib/index.js:3187:19) + at Parser.parseMaybeAssign (/node_modules/babylon/lib/index.js:5700:20) +" +`; + +exports[`test jsx1.js 1`] = ` +"// @flow + +var React = require(\'react\'); + +class C extends React.Component { + props: { x: number }; +} + void, o: { x?: string } }) { + return obj. +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (5:0) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$3.parseIdentifier (/node_modules/babylon/lib/index.js:4128:10) + at Parser.pp$3.parseSubscripts (/node_modules/babylon/lib/index.js:3360:30) + at Parser.pp$3.parseExprSubscripts (/node_modules/babylon/lib/index.js:3347:15) + at Parser.pp$3.parseMaybeUnary (/node_modules/babylon/lib/index.js:3317:19) + at Parser.pp$3.parseExprOps (/node_modules/babylon/lib/index.js:3247:19) + at Parser.pp$3.parseMaybeConditional (/node_modules/babylon/lib/index.js:3224:19) + at Parser.pp$3.parseMaybeAssign (/node_modules/babylon/lib/index.js:3187:19) + at Parser.parseMaybeAssign (/node_modules/babylon/lib/index.js:5700:20) +" +`; + +exports[`test override.js 1`] = ` +"// @flow + +class C { + override(): number | string { return 0; } +} + +class D extends C { + foo() { return this.override() } + override(): string { return \"\"; } + bar() { this. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (11:0) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$3.parseIdentifier (/node_modules/babylon/lib/index.js:4128:10) + at Parser.pp$3.parseSubscripts (/node_modules/babylon/lib/index.js:3360:30) + at Parser.pp$3.parseExprSubscripts (/node_modules/babylon/lib/index.js:3347:15) + at Parser.pp$3.parseMaybeUnary (/node_modules/babylon/lib/index.js:3317:19) + at Parser.pp$3.parseExprOps (/node_modules/babylon/lib/index.js:3247:19) + at Parser.pp$3.parseMaybeConditional (/node_modules/babylon/lib/index.js:3224:19) + at Parser.pp$3.parseMaybeAssign (/node_modules/babylon/lib/index.js:3187:19) + at Parser.parseMaybeAssign (/node_modules/babylon/lib/index.js:5700:20) +" +`; + +exports[`test qux.js 1`] = ` +"// @flow + +class C { x: number; } + +var c: C = new C; +c. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (7:0) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$3.parseIdentifier (/node_modules/babylon/lib/index.js:4128:10) + at Parser.pp$3.parseSubscripts (/node_modules/babylon/lib/index.js:3360:30) + at Parser.pp$3.parseExprSubscripts (/node_modules/babylon/lib/index.js:3347:15) + at Parser.pp$3.parseMaybeUnary (/node_modules/babylon/lib/index.js:3317:19) + at Parser.pp$3.parseExprOps (/node_modules/babylon/lib/index.js:3247:19) + at Parser.pp$3.parseMaybeConditional (/node_modules/babylon/lib/index.js:3224:19) + at Parser.pp$3.parseMaybeAssign (/node_modules/babylon/lib/index.js:3187:19) + at Parser.parseMaybeAssign (/node_modules/babylon/lib/index.js:5700:20) +" +`; + +exports[`test str.js 1`] = ` +"// @flow + +\"hello\". +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (4:0) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$3.parseIdentifier (/node_modules/babylon/lib/index.js:4128:10) + at Parser.pp$3.parseSubscripts (/node_modules/babylon/lib/index.js:3360:30) + at Parser.pp$3.parseExprSubscripts (/node_modules/babylon/lib/index.js:3347:15) + at Parser.pp$3.parseMaybeUnary (/node_modules/babylon/lib/index.js:3317:19) + at Parser.pp$3.parseExprOps (/node_modules/babylon/lib/index.js:3247:19) + at Parser.pp$3.parseMaybeConditional (/node_modules/babylon/lib/index.js:3224:19) + at Parser.pp$3.parseMaybeAssign (/node_modules/babylon/lib/index.js:3187:19) + at Parser.parseMaybeAssign (/node_modules/babylon/lib/index.js:5700:20) +" +`; + +exports[`test this.js 1`] = ` +"/* @flow */ + +// issue #1197 +class Foo { + baz: string; + bar() {} + hello() { + this. + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (9:2) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$3.parseIdentifier (/node_modules/babylon/lib/index.js:4128:10) + at Parser.pp$3.parseSubscripts (/node_modules/babylon/lib/index.js:3360:30) + at Parser.pp$3.parseExprSubscripts (/node_modules/babylon/lib/index.js:3347:15) + at Parser.pp$3.parseMaybeUnary (/node_modules/babylon/lib/index.js:3317:19) + at Parser.pp$3.parseExprOps (/node_modules/babylon/lib/index.js:3247:19) + at Parser.pp$3.parseMaybeConditional (/node_modules/babylon/lib/index.js:3224:19) + at Parser.pp$3.parseMaybeAssign (/node_modules/babylon/lib/index.js:3187:19) + at Parser.parseMaybeAssign (/node_modules/babylon/lib/index.js:5700:20) +" +`; + +exports[`test typeparams.js 1`] = ` +"// @flow + +class Bounds N> { + foo: F; + bar() { + this.foo(). + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (7:2) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$3.parseIdentifier (/node_modules/babylon/lib/index.js:4128:10) + at Parser.pp$3.parseSubscripts (/node_modules/babylon/lib/index.js:3360:30) + at Parser.pp$3.parseExprSubscripts (/node_modules/babylon/lib/index.js:3347:15) + at Parser.pp$3.parseMaybeUnary (/node_modules/babylon/lib/index.js:3317:19) + at Parser.pp$3.parseExprOps (/node_modules/babylon/lib/index.js:3247:19) + at Parser.pp$3.parseMaybeConditional (/node_modules/babylon/lib/index.js:3224:19) + at Parser.pp$3.parseMaybeAssign (/node_modules/babylon/lib/index.js:3187:19) + at Parser.parseMaybeAssign (/node_modules/babylon/lib/index.js:5700:20) +" +`; + +exports[`test union.js 1`] = ` +"/* @flow */ + +function foo(a: boolean, x: { bar: string }, y: Object) { + var z; + if (a) { + z = x; + } else { + z = y; + } + z. +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (11:0) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$3.parseIdentifier (/node_modules/babylon/lib/index.js:4128:10) + at Parser.pp$3.parseSubscripts (/node_modules/babylon/lib/index.js:3360:30) + at Parser.pp$3.parseExprSubscripts (/node_modules/babylon/lib/index.js:3347:15) + at Parser.pp$3.parseMaybeUnary (/node_modules/babylon/lib/index.js:3317:19) + at Parser.pp$3.parseExprOps (/node_modules/babylon/lib/index.js:3247:19) + at Parser.pp$3.parseMaybeConditional (/node_modules/babylon/lib/index.js:3224:19) + at Parser.pp$3.parseMaybeAssign (/node_modules/babylon/lib/index.js:3187:19) + at Parser.parseMaybeAssign (/node_modules/babylon/lib/index.js:5700:20) +" +`; + +exports[`test unknown.js 1`] = ` +"// @noflow +module.exports = { x: { y: \"\" } }; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @noflow +module.exports = { x: { y: \"\" } }; + +" +`; diff --git a/tests/autocomplete/bar.js b/tests/autocomplete/bar.js new file mode 100644 index 000000000000..00c2758f28f5 --- /dev/null +++ b/tests/autocomplete/bar.js @@ -0,0 +1,4 @@ +// @flow + +var o = require('./unknown'); +o.x. diff --git a/tests/autocomplete/bool.js b/tests/autocomplete/bool.js new file mode 100644 index 000000000000..31e0aa0530b1 --- /dev/null +++ b/tests/autocomplete/bool.js @@ -0,0 +1,3 @@ +// @flow + +true. diff --git a/tests/autocomplete/customfun.js b/tests/autocomplete/customfun.js new file mode 100644 index 000000000000..17a57049c5d4 --- /dev/null +++ b/tests/autocomplete/customfun.js @@ -0,0 +1,11 @@ +// @flow + +declare var idx: $Facebookism$Idx; +declare var merge: $Facebookism$Merge; +declare var mergeDeepInto: $Facebookism$MergeDeepInto; +declare var mergeInto: $Facebookism$MergeInto; +declare var mixin: $Facebookism$Mixin; +declare var objectGetPrototypeOf: Object$GetPrototypeOf +declare var objectAssign: Object$Assign + +m diff --git a/tests/autocomplete/foo.js b/tests/autocomplete/foo.js new file mode 100644 index 000000000000..fb47a498b239 --- /dev/null +++ b/tests/autocomplete/foo.js @@ -0,0 +1,10 @@ +/** + * @flow + */ + +var obj = { + num: 123, + str: 'hello', +}; + +obj. diff --git a/tests/autocomplete/foo_parse_fail.js b/tests/autocomplete/foo_parse_fail.js new file mode 100644 index 000000000000..ad60a989911a --- /dev/null +++ b/tests/autocomplete/foo_parse_fail.js @@ -0,0 +1,10 @@ +/** + * @flow + */ + +var obj = { + num: 123, + str: 'hello', +}; + +console.log(obj. diff --git a/tests/autocomplete/fun.js b/tests/autocomplete/fun.js new file mode 100644 index 000000000000..dda5e9788c36 --- /dev/null +++ b/tests/autocomplete/fun.js @@ -0,0 +1,5 @@ +/* @flow */ + +function foo(f: () => number) { + f. +} diff --git a/tests/autocomplete/function_builtins.js b/tests/autocomplete/function_builtins.js new file mode 100644 index 000000000000..9ab9ee978fd2 --- /dev/null +++ b/tests/autocomplete/function_builtins.js @@ -0,0 +1,5 @@ +/* @flow */ + +function foo(f: Function) { + f. +} diff --git a/tests/autocomplete/generics.js b/tests/autocomplete/generics.js new file mode 100644 index 000000000000..284ca1a8bed5 --- /dev/null +++ b/tests/autocomplete/generics.js @@ -0,0 +1,7 @@ +// @flow + +class C { } + +function foo(o: { cn: C }) { + o. +} diff --git a/tests/autocomplete/if.js b/tests/autocomplete/if.js new file mode 100644 index 000000000000..78fb463ed9cd --- /dev/null +++ b/tests/autocomplete/if.js @@ -0,0 +1,3 @@ +// @flow +const x = ''; +if (x.) diff --git a/tests/autocomplete/issue-1368.js b/tests/autocomplete/issue-1368.js new file mode 100644 index 000000000000..fc346f45a1d8 --- /dev/null +++ b/tests/autocomplete/issue-1368.js @@ -0,0 +1,22 @@ +/* @flow */ +class Test { + prop: number; + + constructor(prop: number) { + this.prop = prop; + } +} + +class ExtendTest extends Test { + extended: string; + + constructor(extended: string) { + super(10); + this.extended = extended; + } + + method() { + this.prop = 12; + this./* here */ + } +} diff --git a/tests/autocomplete/jsfmt.spec.js b/tests/autocomplete/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/autocomplete/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/autocomplete/jsx1.js b/tests/autocomplete/jsx1.js new file mode 100644 index 000000000000..4552fc4f0173 --- /dev/null +++ b/tests/autocomplete/jsx1.js @@ -0,0 +1,8 @@ +// @flow + +var React = require('react'); + +class C extends React.Component { + props: { x: number }; +} + void, o: { x?: string } }) { + return obj. +} diff --git a/tests/autocomplete/override.js b/tests/autocomplete/override.js new file mode 100644 index 000000000000..6b2e3dae5f46 --- /dev/null +++ b/tests/autocomplete/override.js @@ -0,0 +1,10 @@ +// @flow + +class C { + override(): number | string { return 0; } +} + +class D extends C { + foo() { return this.override() } + override(): string { return ""; } + bar() { this. diff --git a/tests/autocomplete/qux.js b/tests/autocomplete/qux.js new file mode 100644 index 000000000000..e25242a8acfb --- /dev/null +++ b/tests/autocomplete/qux.js @@ -0,0 +1,6 @@ +// @flow + +class C { x: number; } + +var c: C = new C; +c. diff --git a/tests/autocomplete/str.js b/tests/autocomplete/str.js new file mode 100644 index 000000000000..66e25873dc3c --- /dev/null +++ b/tests/autocomplete/str.js @@ -0,0 +1,3 @@ +// @flow + +"hello". diff --git a/tests/autocomplete/this.js b/tests/autocomplete/this.js new file mode 100644 index 000000000000..8fb80158b295 --- /dev/null +++ b/tests/autocomplete/this.js @@ -0,0 +1,10 @@ +/* @flow */ + +// issue #1197 +class Foo { + baz: string; + bar() {} + hello() { + this. + } +} diff --git a/tests/autocomplete/typeparams.js b/tests/autocomplete/typeparams.js new file mode 100644 index 000000000000..7231f4c719bb --- /dev/null +++ b/tests/autocomplete/typeparams.js @@ -0,0 +1,8 @@ +// @flow + +class Bounds N> { + foo: F; + bar() { + this.foo(). + } +} diff --git a/tests/autocomplete/union.js b/tests/autocomplete/union.js new file mode 100644 index 000000000000..ddf493f96c32 --- /dev/null +++ b/tests/autocomplete/union.js @@ -0,0 +1,11 @@ +/* @flow */ + +function foo(a: boolean, x: { bar: string }, y: Object) { + var z; + if (a) { + z = x; + } else { + z = y; + } + z. +} diff --git a/tests/autocomplete/unknown.js b/tests/autocomplete/unknown.js new file mode 100644 index 000000000000..22090345cfd7 --- /dev/null +++ b/tests/autocomplete/unknown.js @@ -0,0 +1,2 @@ +// @noflow +module.exports = { x: { y: "" } }; diff --git a/tests/auxiliary/__snapshots__/jsfmt.spec.js.snap b/tests/auxiliary/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..1aed9affa598 --- /dev/null +++ b/tests/auxiliary/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,7 @@ +exports[`test client.js 1`] = ` +"var y:number = x; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var y: number = x; + +" +`; diff --git a/tests/auxiliary/client.js b/tests/auxiliary/client.js new file mode 100644 index 000000000000..e3540c1251b2 --- /dev/null +++ b/tests/auxiliary/client.js @@ -0,0 +1 @@ +var y:number = x; diff --git a/tests/auxiliary/jsfmt.spec.js b/tests/auxiliary/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/auxiliary/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/auxiliary/lib/__snapshots__/jsfmt.spec.js.snap b/tests/auxiliary/lib/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..15f3efcda83f --- /dev/null +++ b/tests/auxiliary/lib/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,7 @@ +exports[`test lib.js 1`] = ` +"declare var x: string; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +declare var x: string; + +" +`; diff --git a/tests/auxiliary/lib/jsfmt.spec.js b/tests/auxiliary/lib/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/auxiliary/lib/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/auxiliary/lib/lib.js b/tests/auxiliary/lib/lib.js new file mode 100644 index 000000000000..e7640e569efc --- /dev/null +++ b/tests/auxiliary/lib/lib.js @@ -0,0 +1 @@ +declare var x: string; diff --git a/tests/binary/__snapshots__/jsfmt.spec.js.snap b/tests/binary/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..d53527910e3c --- /dev/null +++ b/tests/binary/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,112 @@ +exports[`test in.js 1`] = ` +"// @flow + +let tests = [ + // objects on RHS + function() { + ('foo' in {}); + ('foo' in { foo: null }); + (0 in {}); + (0 in { "0": null }); + }, + + // arrays on RHS + function() { + ('foo' in []); + (0 in []); + ('length' in []); + }, + + // primitive classes on RHS + function() { + ('foo' in new String('bar')); + ('foo' in new Number(123)); + }, + + // primitives on RHS + function() { + ('foo' in 123); // error + ('foo' in 'bar'); // error + ('foo' in void 0); // error + ('foo' in null); // error + }, + + // bogus stuff on LHS + function() { + (null in {}); // error + (void 0 in {}); // error + ({} in {}); // error + ([] in {}); // error + (false in []); // error + }, + + // in predicates + function() { + if ('foo' in 123) {} // error + if (!'foo' in {}) {} // error, !'foo' is a boolean + if (!('foo' in {})) {} + }, + + // annotations on RHS + function(x: Object, y: mixed) { + ('foo' in x); // ok + ('foo' in y); // error + }, +] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +let tests = [ + // objects on RHS + function() { + "foo" in {}; + "foo" in { foo: null }; + 0 in {}; + 0 in { "0": null }; + }, + // arrays on RHS + function() { + "foo" in [ ]; + 0 in [ ]; + "length" in [ ]; + }, + // primitive classes on RHS + function() { + "foo" in new String("bar"); + "foo" in new Number(123); + }, + // primitives on RHS + function() { + "foo" in 123;// error + "foo" in "bar";// error + "foo" in void 0;// error + "foo" in null;// error + }, + // bogus stuff on LHS + function() { + null in {};// error + void 0 in {};// error + ({}) in {};// error + [ ] in {};// error + false in [ ];// error + }, + // in predicates + function() { + if ("foo" in 123) { + + }// error + if (!"foo" in {}) { + + }// error, !'foo' is a boolean + if (!("foo" in {})) { + + } + }, + // annotations on RHS + function(x: Object, y: mixed) { + "foo" in x;// ok + "foo" in y;// error + } +]; + +" +`; diff --git a/tests/binary/in.js b/tests/binary/in.js new file mode 100644 index 000000000000..a66b1d84f69b --- /dev/null +++ b/tests/binary/in.js @@ -0,0 +1,54 @@ +// @flow + +let tests = [ + // objects on RHS + function() { + ('foo' in {}); + ('foo' in { foo: null }); + (0 in {}); + (0 in { "0": null }); + }, + + // arrays on RHS + function() { + ('foo' in []); + (0 in []); + ('length' in []); + }, + + // primitive classes on RHS + function() { + ('foo' in new String('bar')); + ('foo' in new Number(123)); + }, + + // primitives on RHS + function() { + ('foo' in 123); // error + ('foo' in 'bar'); // error + ('foo' in void 0); // error + ('foo' in null); // error + }, + + // bogus stuff on LHS + function() { + (null in {}); // error + (void 0 in {}); // error + ({} in {}); // error + ([] in {}); // error + (false in []); // error + }, + + // in predicates + function() { + if ('foo' in 123) {} // error + if (!'foo' in {}) {} // error, !'foo' is a boolean + if (!('foo' in {})) {} + }, + + // annotations on RHS + function(x: Object, y: mixed) { + ('foo' in x); // ok + ('foo' in y); // error + }, +] diff --git a/tests/binary/jsfmt.spec.js b/tests/binary/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/binary/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/binding/__snapshots__/jsfmt.spec.js.snap b/tests/binding/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..ba9f6c6fc639 --- /dev/null +++ b/tests/binding/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,594 @@ +exports[`test const.js 1`] = ` +"const x = 0; + +// errors: const cannot be reassigned +x++; +x--; +x += 0; +x -= 0; +x /= 0; +x %= 0; +x <<= 0 +x >>= 0; +x >>>= 0; +x |= 0; +x ^= 0; +x &= 0; + +// regression tests -- OK to assign consts like this: + +const { foo } = { foo: \"foo\" } +const [ bar ] = [\"bar\"]; +(foo: number); // error: string ~> number +(bar: number); // error: string ~> number + +declare var bazzes: { baz: string }[]; +for (const { baz } of bazzes) { + (baz: number); // error: string ~> number +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +const x = 0; +// errors: const cannot be reassigned +x++; +x--; +x += 0; +x -= 0; +x /= 0; +x %= 0; +x <<= 0; +x >>= 0; +x >>>= 0; +x |= 0; +x ^= 0; +x &= 0; +// regression tests -- OK to assign consts like this: +const { foo } = { foo: \"foo\" }; +const [ bar ] = [ \"bar\" ]; +(foo: number);// error: string ~> number +(bar: number);// error: string ~> number +declare var bazzes: { baz: string }[]; +for (const { baz } of bazzes) { + (baz: number);// error: string ~> number +} + +" +`; + +exports[`test rebinding.js 1`] = ` +"/* @flow + * test errors on illegal rebinding/reassignment + * + * type class let const var (reassign) + * type x x x x x x + * class x x x x x + * let x x x x x + * const x x x x x x + * var x x x x + */ + +// type x * + +function type_type() { + type A = number; + type A = number; // error: name already bound +} + +function type_class() { + type A = number; + class A {} // error: name already bound +} + +function type_let() { + type A = number; + let A = 0; // error: name already bound +} + +function type_const() { + type A = number; + const A = 0; // error: name already bound +} + +function type_var() { + type A = number; + var A = 0; // error: name already bound +} + +function type_reassign() { + type A = number; + A = 42; // error: type alias ref\'d from value pos +} + +// class x * + +function class_type() { + class A {} + type A = number; // error: name already bound +} + +function class_class() { + class A {} + class A {} // error: name already bound +} + +function class_let() { + class A {} + let A = 0; // error: name already bound +} + +function class_const() { + class A {} + const A = 0; // error: name already bound +} + +function class_var() { + class A {} + var A = 0; // error: name already bound +} + +// let x * + +function let_type() { + let A = 0; + type A = number; // error: name already bound +} + +function let_class() { + let A = 0; + class A {} // error: name already bound +} + +function let_let() { + let A = 0; + let A = 0; // error: name already bound +} + +function let_const() { + let A = 0; + const A = 0; // error: name already bound +} + +function let_var() { + let A = 0; + var A = 0; // error: name already bound +} + +// const x * + +function const_type() { + const A = 0; + type A = number; // error: name already bound +} + +function const_class() { + const A = 0; + class A {} // error: name already bound +} + +function const_let() { + const A = 0; + let A = 0; // error: name already bound +} + +function const_const() { + const A = 0; + const A = 0; // error: name already bound +} + +function const_var() { + const A = 0; + var A = 0; // error: name already bound +} + +function const_reassign() { + const A = 0; + A = 42; // error: cannot be reassigned +} + +// var x * + +function var_type() { + var A = 0; + type A = number; // error: name already bound +} + +function var_class() { + var A = 0; + class A {} // error: name already bound +} + +function var_let() { + var A = 0; + let A = 0; // error: name already bound +} + +function var_const() { + var A = 0; + const A = 0; // error: name already bound +} + +function var_var() { + var A = 0; + var A = 0; // OK +} + +// function x * + +function function_toplevel() { + function a() {}; + function a() {}; // OK +} + +function function_block() { + { + function a() {}; + function a() {}; // error: name already bound + } +} + +// corner cases + +function var_shadow_nested_scope() { + { + let x = 0; + { + var x = 0; // error: name already bound + } + } +} + +function type_shadow_nested_scope() { + { + let x = 0; + { + type x = string; // error: name already bound + } + } +} + +// fn params name clash + +function fn_params_name_clash(x, x /* error: x already bound */) {} +function fn_params_clash_fn_binding(x,y) { + let x = 0; // error: x already bound + var y = 0; // OK +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Argument name clash in strict mode (193:33) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp$2.checkLVal (/node_modules/babylon/lib/index.js:3007:16) + at Parser.checkLVal (/node_modules/babylon/lib/index.js:5446:22) + at Parser.pp$3.parseFunctionBody (/node_modules/babylon/lib/index.js:4072:12) + at Parser.parseFunctionBody (/node_modules/babylon/lib/index.js:5211:20) + at Parser.pp$1.parseFunction (/node_modules/babylon/lib/index.js:2257:8) + at Parser.pp$1.parseFunctionStatement (/node_modules/babylon/lib/index.js:1926:15) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1712:19) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) +" +`; + +exports[`test scope.js 1`] = ` +"function block_scope() { + let a: number = 0; + var b: number = 0; + { + let a = \"\"; // ok: local to block + var b = \"\"; // error: string ~> number + } +} + +function switch_scope(x: string) { + let a: number = 0; + var b: number = 0; + switch (x) { + case \"foo\": + let a = \"\"; // ok: local to switch + var b = \"\"; // error: string ~> number + break; + case \"bar\": + let a = \"\"; // error: a already bound in switch + break; + } +} + +// a switch is a single lexical scope, so lets and non-disjoint +// cases can mix in odd ways. our support for edge cases is not +// yet perfect. +function switch_scope2(x: number) { + switch (x) { + case 0: + a = \"\"; // error: assign before declaration + break; + case 1: + var b = a; // error: use before declaration + break; + case 2: + let a = \"\"; + break; + case 3: + a = \"\"; // error: skipped initializer + break; + case 4: + var c:string = a; // error: skipped initializer + break; + } + a = \"\"; // error: a no longer in scope +} + +function try_scope() { + let a: number = 0; + try { + let a = \"\"; // ok + } catch (e) { + let a = \"\"; // ok + } finally { + let a = \"\"; // ok + } +} + +function for_scope_let() { + let a: number = 0; + for (let a = \"\" /* ok: local to init */;;) {} +} + +function for_scope_var() { + var a: number = 0; + for (var a = \"\" /* error: string ~> number */;;) {} +} + +function for_in_scope_let(o: Object) { + let a: number = 0; + for (let a /* ok: local to init */ in o) {} +} + +function for_in_scope_var(o: Object) { + var a: number = 0; + for (var a /* error: string ~> number */ in o) {} +} + +function for_of_scope_let(xs: string[]) { + let a: number = 0; + for (let a /* ok: local to init */ of xs) {} +} + +function for_of_scope_var(xs: string[]) { + var a: number = 0; + for (var a /* error: string ~> number */ of xs) {} +} + +function default_param_1() { + // function binding in scope in default expr + function f( + x: () => string = f // error: number ~> string + ): number { + return 0; + } +} + +function default_param_2() { + // fn body bindings not visible from param scope + let a = \"\"; + function f0(x = () => a): number { + let a = 0; + return x(); // error: string ~> number + } + function f1(x = b /* error: cannot resolve b */): number { + let b = 0; + return x; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1020 + }, \"consequent\").indent(options.tabWidth)); + ^ + +TypeError: path.call(...).indent is not a function + at genericPrintNoParens (/src/printer.js:1020:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1005:36) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test tdz.js 1`] = ` +"/** @flow */ + +// -- types --- + +// type aliases are hoisted and always available + +type T1 = T2; // ok +type T2 = number; + +// --- lets --- + +// to be correct, we would +// - not allow forward refs to lets from value positions, +// while let is in TDZ. +// - allow forward refs to lets from type positions. +// +// currently we\'re too lenient about TDZ in closures - +// from value positions, we currently enforce TDZ only in-scope. +// this is unsound - a let or const may remain uninitialized when +// a lambda runs. But a simple conservative approach would prohibit +// forward references to let/consts from within lambdas entirely, +// which would be annoying. TODO + +function f0() { + var v = x * c; // errors, let + const referenced before decl + let x = 0; + const c = 0; +} + +function f1(b) { + x = 10; // error, attempt to write to let before decl + let x = 0; + if (b) { + y = 10; // error, attempt to write to let before decl + let y = 0; + } +} + +function f2() { + { + var v = x * c; // errors, let + const referenced before decl + } + let x = 0; + const c = 0; +} + +// functions are let-scoped and hoisted +function f3() { + var s: string = foo(); // ok, finds hoisted outer + { + var n: number = foo(); // ok, finds hoisted inner + function foo() { return 0; } + } + var s2: string = foo(); // ok, hoisted outer not clobbered + function foo() { return \"\"; } +} + +// out-of-scope TDZ not enforced. sometimes right... +function f4() { + function g() { return x + c; } // ok, g doesn\'t run in TDZ + let x = 0; + const c = 0; +} + +// ...sometimes wrong +function f5() { + function g() { return x; } + g(); // should error, but doesn\'t currently + let x = 0; + const c = 0; +} + +// - from type positions, we currently error on forward refs to any +// value (i.e., class or function). this is a basic flaw in our +// phasing of AST traversal, and will be fixed. +// + +var x: C; // ok + +var y = new C(); // error: let ref before decl from value position + +class C {} + +var z: C = new C(); // ok + +// --- vars --- + +// it\'s possible to annotate a var with a non-maybe type, +// but leave it uninitialized until later (as long as it\'s +// initialized before use) + +var a: number; // not an error per se - only if used before init + +function f(n: number) { return n; } + +f(a); // error: undefined ~/> number + +a = 10; + +f(a); // ok, a: number (not ?number) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** @flow */ +// -- types --- +// type aliases are hoisted and always available +type T1 = T2;// ok +type T2 = number; +// --- lets --- +// to be correct, we would +// - not allow forward refs to lets from value positions, +// while let is in TDZ. +// - allow forward refs to lets from type positions. +// +// currently we\'re too lenient about TDZ in closures - +// from value positions, we currently enforce TDZ only in-scope. +// this is unsound - a let or const may remain uninitialized when +// a lambda runs. But a simple conservative approach would prohibit +// forward references to let/consts from within lambdas entirely, +// which would be annoying. TODO +function f0() { + var v = x * c;// errors, let + const referenced before decl + let x = 0; + const c = 0; +} +function f1(b) { + x = 10;// error, attempt to write to let before decl + let x = 0; + if (b) { + y = 10;// error, attempt to write to let before decl + let y = 0; + } +} +function f2() { + { + var v = x * c;// errors, let + const referenced before decl + } + let x = 0; + const c = 0; +} +// functions are let-scoped and hoisted +function f3() { + var s: string = foo();// ok, finds hoisted outer + { + var n: number = foo();// ok, finds hoisted inner + function foo() { + return 0; + } + } + var s2: string = foo();// ok, hoisted outer not clobbered + function foo() { + return \"\"; + } +} +// out-of-scope TDZ not enforced. sometimes right... +function f4() { + function g() { + return x + c; + }// ok, g doesn\'t run in TDZ + let x = 0; + const c = 0; +} +// ...sometimes wrong +function f5() { + function g() { + return x; + } + g();// should error, but doesn\'t currently + let x = 0; + const c = 0; +} +// - from type positions, we currently error on forward refs to any +// value (i.e., class or function). this is a basic flaw in our +// phasing of AST traversal, and will be fixed. +// +var x: C;// ok +var y = new C();// error: let ref before decl from value position +class C {} +var z: C = new C();// ok +// --- vars --- +// it\'s possible to annotate a var with a non-maybe type, +// but leave it uninitialized until later (as long as it\'s +// initialized before use) +var a: number;// not an error per se - only if used before init +function f(n: number) { + return n; +} +f(a);// error: undefined ~/> number +a = 10; +f(a);// ok, a: number (not ?number) + +" +`; diff --git a/tests/binding/const.js b/tests/binding/const.js new file mode 100644 index 000000000000..387e96cb0fa2 --- /dev/null +++ b/tests/binding/const.js @@ -0,0 +1,27 @@ +const x = 0; + +// errors: const cannot be reassigned +x++; +x--; +x += 0; +x -= 0; +x /= 0; +x %= 0; +x <<= 0 +x >>= 0; +x >>>= 0; +x |= 0; +x ^= 0; +x &= 0; + +// regression tests -- OK to assign consts like this: + +const { foo } = { foo: "foo" } +const [ bar ] = ["bar"]; +(foo: number); // error: string ~> number +(bar: number); // error: string ~> number + +declare var bazzes: { baz: string }[]; +for (const { baz } of bazzes) { + (baz: number); // error: string ~> number +} diff --git a/tests/binding/jsfmt.spec.js b/tests/binding/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/binding/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/binding/rebinding.js b/tests/binding/rebinding.js new file mode 100644 index 000000000000..195f096b3f54 --- /dev/null +++ b/tests/binding/rebinding.js @@ -0,0 +1,197 @@ +/* @flow + * test errors on illegal rebinding/reassignment + * + * type class let const var (reassign) + * type x x x x x x + * class x x x x x + * let x x x x x + * const x x x x x x + * var x x x x + */ + +// type x * + +function type_type() { + type A = number; + type A = number; // error: name already bound +} + +function type_class() { + type A = number; + class A {} // error: name already bound +} + +function type_let() { + type A = number; + let A = 0; // error: name already bound +} + +function type_const() { + type A = number; + const A = 0; // error: name already bound +} + +function type_var() { + type A = number; + var A = 0; // error: name already bound +} + +function type_reassign() { + type A = number; + A = 42; // error: type alias ref'd from value pos +} + +// class x * + +function class_type() { + class A {} + type A = number; // error: name already bound +} + +function class_class() { + class A {} + class A {} // error: name already bound +} + +function class_let() { + class A {} + let A = 0; // error: name already bound +} + +function class_const() { + class A {} + const A = 0; // error: name already bound +} + +function class_var() { + class A {} + var A = 0; // error: name already bound +} + +// let x * + +function let_type() { + let A = 0; + type A = number; // error: name already bound +} + +function let_class() { + let A = 0; + class A {} // error: name already bound +} + +function let_let() { + let A = 0; + let A = 0; // error: name already bound +} + +function let_const() { + let A = 0; + const A = 0; // error: name already bound +} + +function let_var() { + let A = 0; + var A = 0; // error: name already bound +} + +// const x * + +function const_type() { + const A = 0; + type A = number; // error: name already bound +} + +function const_class() { + const A = 0; + class A {} // error: name already bound +} + +function const_let() { + const A = 0; + let A = 0; // error: name already bound +} + +function const_const() { + const A = 0; + const A = 0; // error: name already bound +} + +function const_var() { + const A = 0; + var A = 0; // error: name already bound +} + +function const_reassign() { + const A = 0; + A = 42; // error: cannot be reassigned +} + +// var x * + +function var_type() { + var A = 0; + type A = number; // error: name already bound +} + +function var_class() { + var A = 0; + class A {} // error: name already bound +} + +function var_let() { + var A = 0; + let A = 0; // error: name already bound +} + +function var_const() { + var A = 0; + const A = 0; // error: name already bound +} + +function var_var() { + var A = 0; + var A = 0; // OK +} + +// function x * + +function function_toplevel() { + function a() {}; + function a() {}; // OK +} + +function function_block() { + { + function a() {}; + function a() {}; // error: name already bound + } +} + +// corner cases + +function var_shadow_nested_scope() { + { + let x = 0; + { + var x = 0; // error: name already bound + } + } +} + +function type_shadow_nested_scope() { + { + let x = 0; + { + type x = string; // error: name already bound + } + } +} + +// fn params name clash + +function fn_params_name_clash(x, x /* error: x already bound */) {} +function fn_params_clash_fn_binding(x,y) { + let x = 0; // error: x already bound + var y = 0; // OK +} diff --git a/tests/binding/scope.js b/tests/binding/scope.js new file mode 100644 index 000000000000..8aa169fad3e8 --- /dev/null +++ b/tests/binding/scope.js @@ -0,0 +1,109 @@ +function block_scope() { + let a: number = 0; + var b: number = 0; + { + let a = ""; // ok: local to block + var b = ""; // error: string ~> number + } +} + +function switch_scope(x: string) { + let a: number = 0; + var b: number = 0; + switch (x) { + case "foo": + let a = ""; // ok: local to switch + var b = ""; // error: string ~> number + break; + case "bar": + let a = ""; // error: a already bound in switch + break; + } +} + +// a switch is a single lexical scope, so lets and non-disjoint +// cases can mix in odd ways. our support for edge cases is not +// yet perfect. +function switch_scope2(x: number) { + switch (x) { + case 0: + a = ""; // error: assign before declaration + break; + case 1: + var b = a; // error: use before declaration + break; + case 2: + let a = ""; + break; + case 3: + a = ""; // error: skipped initializer + break; + case 4: + var c:string = a; // error: skipped initializer + break; + } + a = ""; // error: a no longer in scope +} + +function try_scope() { + let a: number = 0; + try { + let a = ""; // ok + } catch (e) { + let a = ""; // ok + } finally { + let a = ""; // ok + } +} + +function for_scope_let() { + let a: number = 0; + for (let a = "" /* ok: local to init */;;) {} +} + +function for_scope_var() { + var a: number = 0; + for (var a = "" /* error: string ~> number */;;) {} +} + +function for_in_scope_let(o: Object) { + let a: number = 0; + for (let a /* ok: local to init */ in o) {} +} + +function for_in_scope_var(o: Object) { + var a: number = 0; + for (var a /* error: string ~> number */ in o) {} +} + +function for_of_scope_let(xs: string[]) { + let a: number = 0; + for (let a /* ok: local to init */ of xs) {} +} + +function for_of_scope_var(xs: string[]) { + var a: number = 0; + for (var a /* error: string ~> number */ of xs) {} +} + +function default_param_1() { + // function binding in scope in default expr + function f( + x: () => string = f // error: number ~> string + ): number { + return 0; + } +} + +function default_param_2() { + // fn body bindings not visible from param scope + let a = ""; + function f0(x = () => a): number { + let a = 0; + return x(); // error: string ~> number + } + function f1(x = b /* error: cannot resolve b */): number { + let b = 0; + return x; + } +} diff --git a/tests/binding/tdz.js b/tests/binding/tdz.js new file mode 100644 index 000000000000..da081b0999c1 --- /dev/null +++ b/tests/binding/tdz.js @@ -0,0 +1,100 @@ +/** @flow */ + +// -- types --- + +// type aliases are hoisted and always available + +type T1 = T2; // ok +type T2 = number; + +// --- lets --- + +// to be correct, we would +// - not allow forward refs to lets from value positions, +// while let is in TDZ. +// - allow forward refs to lets from type positions. +// +// currently we're too lenient about TDZ in closures - +// from value positions, we currently enforce TDZ only in-scope. +// this is unsound - a let or const may remain uninitialized when +// a lambda runs. But a simple conservative approach would prohibit +// forward references to let/consts from within lambdas entirely, +// which would be annoying. TODO + +function f0() { + var v = x * c; // errors, let + const referenced before decl + let x = 0; + const c = 0; +} + +function f1(b) { + x = 10; // error, attempt to write to let before decl + let x = 0; + if (b) { + y = 10; // error, attempt to write to let before decl + let y = 0; + } +} + +function f2() { + { + var v = x * c; // errors, let + const referenced before decl + } + let x = 0; + const c = 0; +} + +// functions are let-scoped and hoisted +function f3() { + var s: string = foo(); // ok, finds hoisted outer + { + var n: number = foo(); // ok, finds hoisted inner + function foo() { return 0; } + } + var s2: string = foo(); // ok, hoisted outer not clobbered + function foo() { return ""; } +} + +// out-of-scope TDZ not enforced. sometimes right... +function f4() { + function g() { return x + c; } // ok, g doesn't run in TDZ + let x = 0; + const c = 0; +} + +// ...sometimes wrong +function f5() { + function g() { return x; } + g(); // should error, but doesn't currently + let x = 0; + const c = 0; +} + +// - from type positions, we currently error on forward refs to any +// value (i.e., class or function). this is a basic flaw in our +// phasing of AST traversal, and will be fixed. +// + +var x: C; // ok + +var y = new C(); // error: let ref before decl from value position + +class C {} + +var z: C = new C(); // ok + +// --- vars --- + +// it's possible to annotate a var with a non-maybe type, +// but leave it uninitialized until later (as long as it's +// initialized before use) + +var a: number; // not an error per se - only if used before init + +function f(n: number) { return n; } + +f(a); // error: undefined ~/> number + +a = 10; + +f(a); // ok, a: number (not ?number) diff --git a/tests/bom/FormData.js b/tests/bom/FormData.js new file mode 100644 index 000000000000..79db76483ad1 --- /dev/null +++ b/tests/bom/FormData.js @@ -0,0 +1,67 @@ +/* @flow */ + +// constructor +const a: FormData = new FormData(); // correct +new FormData(''); // incorrect +new FormData(document.createElement('input')); // incorrect +new FormData(document.createElement('form')); // correct + +// has +const b: boolean = a.has('foo'); // correct + +// get +const c: ?(string | File) = a.get('foo'); // correct +const d: string = a.get('foo'); // incorrect +const e: Blob = a.get('foo'); // incorrect +const f: ?(string | File | Blob) = a.get('foo'); // incorrect +a.get(2); // incorrect + +// getAll +const a1: Array = a.getAll('foo'); // correct +const a2: Array = a.getAll('foo'); // incorrect +const a3: Array = a.getAll('foo'); // incorrect +a.getAll(23); // incorrect + +// set +a.set('foo', 'bar'); // correct +a.set('foo', {}); // incorrect +a.set(2, 'bar'); // incorrect +a.set('foo', 'bar', 'baz'); // incorrect +a.set('bar', new File([], 'q')) // correct +a.set('bar', new File([], 'q'), 'x') // correct +a.set('bar', new File([], 'q'), 2) // incorrect +a.set('bar', new Blob) // correct +a.set('bar', new Blob, 'x') // correct +a.set('bar', new Blob, 2) // incorrect + +// append +a.append('foo', 'bar'); // correct +a.append('foo', {}); // incorrect +a.append(2, 'bar'); // incorrect +a.append('foo', 'bar', 'baz'); // incorrect +a.append('foo', 'bar'); // incorrect +a.append('bar', new File([], 'q')) // correct +a.append('bar', new File([], 'q'), 'x') // correct +a.append('bar', new File([], 'q'), 2) // incorrect +a.append('bar', new Blob) // correct +a.append('bar', new Blob, 'x') // correct +a.append('bar', new Blob, 2) // incorrect + +// delete +a.delete('xx'); // correct +a.delete(3); // incorrect + +// keys +for (let x: string of a.keys()) {} // correct +for (let x: number of a.keys()) {} // incorrect + +// values +for (let x: string | File of a.values()) {} // correct +for (let x: string | File | Blob of a.values()) {} // incorrect + +// entries +for (let [x, y]: [string, string | File] of a.entries()) {} // correct +for (let [x, y]: [string, string | File | Blob] of a.entries()) {} // incorrect +for (let [x, y]: [number, string] of a.entries()) {} // incorrect +for (let [x, y]: [string, number] of a.entries()) {} // incorrect +for (let [x, y]: [number, number] of a.entries()) {} // incorrect diff --git a/tests/bom/MutationObserver.js b/tests/bom/MutationObserver.js new file mode 100644 index 000000000000..e1d588b5f7a8 --- /dev/null +++ b/tests/bom/MutationObserver.js @@ -0,0 +1,29 @@ +/* @flow */ + +// constructor +function callback(arr: Array, observer: MutationObserver): void { + return; +} +const o: MutationObserver = new MutationObserver(callback); // correct +new MutationObserver((arr: Array) => true); // correct +new MutationObserver(() => {}); // correct +new MutationObserver(); // incorrect +new MutationObserver(42); // incorrect +new MutationObserver((n: number) => {}); // incorrect + +// observe +const div = document.createElement('div'); +o.observe(div, { attributes: true, attributeFilter: ['style'] }); // correct +o.observe(div, { characterData: true, invalid: true }); // correct +o.observe(); // incorrect +o.observe('invalid'); // incorrect +o.observe(div); // incorrect +o.observe(div, {}); // incorrect +o.observe(div, { subtree: true }); // incorrect +o.observe(div, { attributes: true, attributeFilter: true }); // incorrect + +// takeRecords +o.takeRecords(); // correct + +// disconnect +o.disconnect(); // correct diff --git a/tests/bom/__snapshots__/jsfmt.spec.js.snap b/tests/bom/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..6483f642c78d --- /dev/null +++ b/tests/bom/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,135 @@ +exports[`test FormData.js 1`] = ` +"/* @flow */ + +// constructor +const a: FormData = new FormData(); // correct +new FormData(\'\'); // incorrect +new FormData(document.createElement(\'input\')); // incorrect +new FormData(document.createElement(\'form\')); // correct + +// has +const b: boolean = a.has(\'foo\'); // correct + +// get +const c: ?(string | File) = a.get(\'foo\'); // correct +const d: string = a.get(\'foo\'); // incorrect +const e: Blob = a.get(\'foo\'); // incorrect +const f: ?(string | File | Blob) = a.get(\'foo\'); // incorrect +a.get(2); // incorrect + +// getAll +const a1: Array = a.getAll(\'foo\'); // correct +const a2: Array = a.getAll(\'foo\'); // incorrect +const a3: Array = a.getAll(\'foo\'); // incorrect +a.getAll(23); // incorrect + +// set +a.set(\'foo\', \'bar\'); // correct +a.set(\'foo\', {}); // incorrect +a.set(2, \'bar\'); // incorrect +a.set(\'foo\', \'bar\', \'baz\'); // incorrect +a.set(\'bar\', new File([], \'q\')) // correct +a.set(\'bar\', new File([], \'q\'), \'x\') // correct +a.set(\'bar\', new File([], \'q\'), 2) // incorrect +a.set(\'bar\', new Blob) // correct +a.set(\'bar\', new Blob, \'x\') // correct +a.set(\'bar\', new Blob, 2) // incorrect + +// append +a.append(\'foo\', \'bar\'); // correct +a.append(\'foo\', {}); // incorrect +a.append(2, \'bar\'); // incorrect +a.append(\'foo\', \'bar\', \'baz\'); // incorrect +a.append(\'foo\', \'bar\'); // incorrect +a.append(\'bar\', new File([], \'q\')) // correct +a.append(\'bar\', new File([], \'q\'), \'x\') // correct +a.append(\'bar\', new File([], \'q\'), 2) // incorrect +a.append(\'bar\', new Blob) // correct +a.append(\'bar\', new Blob, \'x\') // correct +a.append(\'bar\', new Blob, 2) // incorrect + +// delete +a.delete(\'xx\'); // correct +a.delete(3); // incorrect + +// keys +for (let x: string of a.keys()) {} // correct +for (let x: number of a.keys()) {} // incorrect + +// values +for (let x: string | File of a.values()) {} // correct +for (let x: string | File | Blob of a.values()) {} // incorrect + +// entries +for (let [x, y]: [string, string | File] of a.entries()) {} // correct +for (let [x, y]: [string, string | File | Blob] of a.entries()) {} // incorrect +for (let [x, y]: [number, string] of a.entries()) {} // incorrect +for (let [x, y]: [string, number] of a.entries()) {} // incorrect +for (let [x, y]: [number, number] of a.entries()) {} // incorrect +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1412:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test MutationObserver.js 1`] = ` +"/* @flow */ + +// constructor +function callback(arr: Array, observer: MutationObserver): void { + return; +} +const o: MutationObserver = new MutationObserver(callback); // correct +new MutationObserver((arr: Array) => true); // correct +new MutationObserver(() => {}); // correct +new MutationObserver(); // incorrect +new MutationObserver(42); // incorrect +new MutationObserver((n: number) => {}); // incorrect + +// observe +const div = document.createElement(\'div\'); +o.observe(div, { attributes: true, attributeFilter: [\'style\'] }); // correct +o.observe(div, { characterData: true, invalid: true }); // correct +o.observe(); // incorrect +o.observe(\'invalid\'); // incorrect +o.observe(div); // incorrect +o.observe(div, {}); // incorrect +o.observe(div, { subtree: true }); // incorrect +o.observe(div, { attributes: true, attributeFilter: true }); // incorrect + +// takeRecords +o.takeRecords(); // correct + +// disconnect +o.disconnect(); // correct +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/bom/jsfmt.spec.js b/tests/bom/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/bom/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/bounded_poly/__snapshots__/jsfmt.spec.js.snap b/tests/bounded_poly/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..609f1e49a3b5 --- /dev/null +++ b/tests/bounded_poly/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,68 @@ +exports[`test scope.js 1`] = ` +"function foo(x:X, y:Y):void { } +foo(0, \"\"); + +function bar(x:X, y:Y): number { return y*0; } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:340:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test.js 1`] = ` +"function foo(x: T): T { + var _ = x * 1; // OK + var y: string = x; // error + return x; // OK +} + +class C { + bar(x: U): T { + return x; // error, since T: number and U: number does not imply U: T + } + qux(x: U): T { + var _ = x * 1; // OK, since T: number and U: T implies U: number + var y: string = x; // error + return x; // OK, since U: T + } +} + +function example(o: T): T { o.x = 0; return o; } +var obj1: {x: number; y: string} = example({x: 0, y: \"\"}); +var obj2: {x: number} = example({x: 0}); + +var c: C = new C; // error, since T = string is incompatible with number +var q: number = c.qux(0); +/* 2 more errors, since argument U = number is incompatible with T = string, and + * result T = string is incompatible with number */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:340:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/bounded_poly/jsfmt.spec.js b/tests/bounded_poly/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/bounded_poly/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/bounded_poly/scope.js b/tests/bounded_poly/scope.js new file mode 100644 index 000000000000..0bade54aae3c --- /dev/null +++ b/tests/bounded_poly/scope.js @@ -0,0 +1,4 @@ +function foo(x:X, y:Y):void { } +foo(0, ""); + +function bar(x:X, y:Y): number { return y*0; } diff --git a/tests/bounded_poly/test.js b/tests/bounded_poly/test.js new file mode 100644 index 000000000000..cddb17861a72 --- /dev/null +++ b/tests/bounded_poly/test.js @@ -0,0 +1,25 @@ +function foo(x: T): T { + var _ = x * 1; // OK + var y: string = x; // error + return x; // OK +} + +class C { + bar(x: U): T { + return x; // error, since T: number and U: number does not imply U: T + } + qux(x: U): T { + var _ = x * 1; // OK, since T: number and U: T implies U: number + var y: string = x; // error + return x; // OK, since U: T + } +} + +function example(o: T): T { o.x = 0; return o; } +var obj1: {x: number; y: string} = example({x: 0, y: ""}); +var obj2: {x: number} = example({x: 0}); + +var c: C = new C; // error, since T = string is incompatible with number +var q: number = c.qux(0); +/* 2 more errors, since argument U = number is incompatible with T = string, and + * result T = string is incompatible with number */ diff --git a/tests/break/__snapshots__/jsfmt.spec.js.snap b/tests/break/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..03752e1fb8e3 --- /dev/null +++ b/tests/break/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,87 @@ +exports[`test break.js 1`] = ` +"function foo(b) { + var x = b ? null: false; + var z; + while(b) { + if (x == null) { z = \"\"; break; } + var y:number = x; // error: boolean !~> number + } + var w:number = z; // 2 errors: ?string !~> number +} + +function bar(b) { + var x = b ? null: false; + if (x == null) return; + switch (\"\") { + case 0: + var y:number = x; // error: boolean !~> number + x = \"\"; + case 1: + var z:number = x; // 2 errors: (boolean | string) !~> number + break; + case 2: + } + var w:number = x; // 2 errors: (boolean | string) !~> number +} + +function bar2(b) { + var x = b ? null: false; + if (x == null) return; + switch (\"\") { + case 0: { + let y:number = x; // error: boolean !~> number + x = \"\"; + } + case 1: { + let z:number = x; // 2 errors: (boolean | string) !~> number + break; + } + case 2: + } + var w:number = x; // 2 errors: (boolean | string) !~> number +} + +function qux(b) { + var z = 0; + while(b) { + var y:number = z; + if (b) { z = \"\"; continue; } // error: string !~> number + z = 0; + } + var w:number = z; // error: string !~> number +} + +// same basic test as foo(), but with const. probes the +// logic that still uses havoc to do env resets. +function test_const() { + let st: string = \'abc\'; + + for (let i = 1; i < 100; i++) { + const fooRes: ?string = \"HEY\"; + if (!fooRes) { + break; + } + + st = fooRes; // no error + } + + return st; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1020 + }, \"consequent\").indent(options.tabWidth)); + ^ + +TypeError: path.call(...).indent is not a function + at genericPrintNoParens (/src/printer.js:1020:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1005:36) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/break/break.js b/tests/break/break.js new file mode 100644 index 000000000000..67986714a41d --- /dev/null +++ b/tests/break/break.js @@ -0,0 +1,68 @@ +function foo(b) { + var x = b ? null: false; + var z; + while(b) { + if (x == null) { z = ""; break; } + var y:number = x; // error: boolean !~> number + } + var w:number = z; // 2 errors: ?string !~> number +} + +function bar(b) { + var x = b ? null: false; + if (x == null) return; + switch ("") { + case 0: + var y:number = x; // error: boolean !~> number + x = ""; + case 1: + var z:number = x; // 2 errors: (boolean | string) !~> number + break; + case 2: + } + var w:number = x; // 2 errors: (boolean | string) !~> number +} + +function bar2(b) { + var x = b ? null: false; + if (x == null) return; + switch ("") { + case 0: { + let y:number = x; // error: boolean !~> number + x = ""; + } + case 1: { + let z:number = x; // 2 errors: (boolean | string) !~> number + break; + } + case 2: + } + var w:number = x; // 2 errors: (boolean | string) !~> number +} + +function qux(b) { + var z = 0; + while(b) { + var y:number = z; + if (b) { z = ""; continue; } // error: string !~> number + z = 0; + } + var w:number = z; // error: string !~> number +} + +// same basic test as foo(), but with const. probes the +// logic that still uses havoc to do env resets. +function test_const() { + let st: string = 'abc'; + + for (let i = 1; i < 100; i++) { + const fooRes: ?string = "HEY"; + if (!fooRes) { + break; + } + + st = fooRes; // no error + } + + return st; +} diff --git a/tests/break/jsfmt.spec.js b/tests/break/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/break/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/builtin_uses/__snapshots__/jsfmt.spec.js.snap b/tests/builtin_uses/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..a833f9c2987d --- /dev/null +++ b/tests/builtin_uses/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,21 @@ +exports[`test test.js 1`] = ` +"var o = Object.freeze({ foo: 0 }); +(o.foo: string); +module.exports = o; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var o = Object.freeze({ foo: 0 }); +(o.foo: string); +module.exports = o; + +" +`; + +exports[`test test2.js 1`] = ` +"var o = require('./test'); +(o.foo: string); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var o = require("./test"); +(o.foo: string); + +" +`; diff --git a/tests/builtin_uses/jsfmt.spec.js b/tests/builtin_uses/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/builtin_uses/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/builtin_uses/test.js b/tests/builtin_uses/test.js new file mode 100644 index 000000000000..3adc7cba9b8b --- /dev/null +++ b/tests/builtin_uses/test.js @@ -0,0 +1,3 @@ +var o = Object.freeze({ foo: 0 }); +(o.foo: string); +module.exports = o; diff --git a/tests/builtin_uses/test2.js b/tests/builtin_uses/test2.js new file mode 100644 index 000000000000..678f4c9af730 --- /dev/null +++ b/tests/builtin_uses/test2.js @@ -0,0 +1,2 @@ +var o = require('./test'); +(o.foo: string); diff --git a/tests/builtins/__snapshots__/jsfmt.spec.js.snap b/tests/builtins/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..3be55c8e21da --- /dev/null +++ b/tests/builtins/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,78 @@ +exports[`test array.js 1`] = ` +"var a = [\"...\"]; +var b = a.map (function (x) { return 0; }); +var c: string = b[0]; // error: number !~> string + +var array = []; +function f() { + array = array.map (function () { return \"...\"; }); + var x:number = array[0]; // error: string !~> number +} + +var Foo = require(\'./genericfoo\'); +var foo = new Foo(); +function g() { + var foo1 = foo.map (function() { return \"...\"; }); + var x:number = foo1.get(); // error: string !~> number + foo = foo1; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var a = [ \"...\" ]; +var b = a.map( + function(x) { + return 0; + } +); +var c: string = b[0];// error: number !~> string +var array = [ ]; +function f() { + array = array.map( + function() { + return \"...\"; + } + ); + var x: number = array[0];// error: string !~> number +} +var Foo = require(\"./genericfoo\"); +var foo = new Foo(); +function g() { + var foo1 = foo.map( + function() { + return \"...\"; + } + ); + var x: number = foo1.get();// error: string !~> number + foo = foo1; +} + +" +`; + +exports[`test genericfoo.js 1`] = ` +"class Foo { + x:T; + self():Foo { return this; } + map(callbackfn: () => U): Foo { return new Foo(); } + set(x:T): void { } + get(): T { return this.x; } +} + +module.exports = Foo; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1172:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/builtins/array.js b/tests/builtins/array.js new file mode 100644 index 000000000000..c84ddcb02bee --- /dev/null +++ b/tests/builtins/array.js @@ -0,0 +1,17 @@ +var a = ["..."]; +var b = a.map (function (x) { return 0; }); +var c: string = b[0]; // error: number !~> string + +var array = []; +function f() { + array = array.map (function () { return "..."; }); + var x:number = array[0]; // error: string !~> number +} + +var Foo = require('./genericfoo'); +var foo = new Foo(); +function g() { + var foo1 = foo.map (function() { return "..."; }); + var x:number = foo1.get(); // error: string !~> number + foo = foo1; +} diff --git a/tests/builtins/genericfoo.js b/tests/builtins/genericfoo.js new file mode 100644 index 000000000000..53d5feb2438f --- /dev/null +++ b/tests/builtins/genericfoo.js @@ -0,0 +1,9 @@ +class Foo { + x:T; + self():Foo { return this; } + map(callbackfn: () => U): Foo { return new Foo(); } + set(x:T): void { } + get(): T { return this.x; } +} + +module.exports = Foo; diff --git a/tests/builtins/jsfmt.spec.js b/tests/builtins/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/builtins/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/call_properties/A.js b/tests/call_properties/A.js new file mode 100644 index 000000000000..8f5e02ce82a0 --- /dev/null +++ b/tests/call_properties/A.js @@ -0,0 +1,30 @@ +// You should be able to call objects with call properties +function a(f: { (): string }, g: { (x: number): string } ): string { + return f() + g(123); +} + +// ...and get an error if the return type is wrong +function b(f: { (): string }): number { + return f(); +} + +// ...or if the param type is wrong +function c(f: { (x: number): number }): number { + return f("hello"); +} + +// ...or if the arity is wrong +function d(f: { (x: number): number }): number { + return f(); +} + +// ...or if there is no call property +function e(f: {}): number { + return f(); +} + +// Make sure we complain even if the object literal is unsealed. +function f(): number { + var x = {}; + return x(); +} diff --git a/tests/call_properties/B.js b/tests/call_properties/B.js new file mode 100644 index 000000000000..5441f2f7e235 --- /dev/null +++ b/tests/call_properties/B.js @@ -0,0 +1,20 @@ +// You should be able to use a function as an object with a call property +var a: { (x: number): string } = function (x: number): string { return "hi"; }; + +// ...and it should notice when the return type is wrong +var b: { (x: number): number } = function (x: number): string { return "hi"; }; + +// ...or if the param type is wrong +var c: { (x: string): string } = function (x: number): string { return "hi"; }; + +// ...or if the arity is wrong +var d: { (): string } = function (x: number): string { return "hi"; }; + +// ...but subtyping rules still apply +var e: { (x: any): void } = function() { } // arity +var f: { (): mixed } = function(): string { return "hi" } // return type +var g: { (x: string): void } = function(x: mixed) { } // param type + +// A function can be an object +var y : {} = function (x: number): string { return "hi"; }; +var z : Object = function (x: number): string { return "hi"; }; diff --git a/tests/call_properties/C.js b/tests/call_properties/C.js new file mode 100644 index 000000000000..75576e3a16b0 --- /dev/null +++ b/tests/call_properties/C.js @@ -0,0 +1,34 @@ +// You should be able to use an object as a function +function a(x: { (z: number): string }): (z: number) => string { + return x; +} + +// ...and it should notice when the return type is wrong +function b(x: { (z: number): string }): (z: number) => number { + return x; +} + +// ...or if the param type is wrong +function c(x: { (z: number): string }): (z: string) => string { + return x; +} + +// ...or if the arity is wrong +function d(x: { (z: number): string }): () => string { + return x; +} + +// ...or if it doesn't have a call property +function e(x: {}): () => string { + return x; +} + +// AnyFunT should also be allowed +function f(x: { (z: number): string }): Function { + return x; +} + +// ... but only if the object is callable +function g(x: {}): Function { + return x; // error +} diff --git a/tests/call_properties/D.js b/tests/call_properties/D.js new file mode 100644 index 000000000000..38a1874289e5 --- /dev/null +++ b/tests/call_properties/D.js @@ -0,0 +1,22 @@ +// Multiple call properties should also be supported +function a(f: { (): string; (x: number): string }): string { + return f() + f(123); +} + +// It should be fine when a function satisfies them all +var b: { (): string; (x: number): string } = + function (x?: number): string { return "hi"; }; + +// ...but should notice when a function doesn't satisfy them all +var c: { (): string; (x: number): string } = + function (x: number): string { return "hi"; }; + +// Only one call property needs to match the function +function d(x: { (): string; (x: number): string }): () => string { + return x; +} + +// ...but you need at least one +function e(x: { (): string; (x: number): string }): () => number { + return x; +} diff --git a/tests/call_properties/E.js b/tests/call_properties/E.js new file mode 100644 index 000000000000..873d9eccc4cb --- /dev/null +++ b/tests/call_properties/E.js @@ -0,0 +1,10 @@ +// Expecting properties that don't exist should be an error +var a : { someProp: number } = function () {}; + +// Expecting properties that do exist should be fine +var b : { apply: Function } = function () {}; + +// Expecting properties in the functions statics should be fine +var f = function () {}; +f.myProp = 123; +var c : { myProp: number } = f; diff --git a/tests/call_properties/F.js b/tests/call_properties/F.js new file mode 100644 index 000000000000..c3e37bfd3f68 --- /dev/null +++ b/tests/call_properties/F.js @@ -0,0 +1,21 @@ +// You should be able to use an arrow function as an object with a call property + +var a: { (x: number): string } = (x) => x.toString() + +// ...and it should notice when the return type is wrong +var b: { (x: number): number } = (x) => "hi" + +// ...or if the param type is wrong +var c: { (x: string): string } = (x) => x.toFixed() + +// ...or if the arity is wrong +var d: { (): string } = (x) => "hi" + +// ...but subtyping rules still apply +var e: { (x: any): void } = () => { } // arity +var f: { (): mixed } = () => "hi" // return type +var g: { (x: Date): void } = (x) => { x * 2 } // param type (date < number) + +// A function can be an object +var y : {} = (x) => "hi" +var z : Object = (x) => "hi" diff --git a/tests/call_properties/__snapshots__/jsfmt.spec.js.snap b/tests/call_properties/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..e18cbd0809a3 --- /dev/null +++ b/tests/call_properties/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,256 @@ +exports[`test A.js 1`] = ` +"// You should be able to call objects with call properties +function a(f: { (): string }, g: { (x: number): string } ): string { + return f() + g(123); +} + +// ...and get an error if the return type is wrong +function b(f: { (): string }): number { + return f(); +} + +// ...or if the param type is wrong +function c(f: { (x: number): number }): number { + return f(\"hello\"); +} + +// ...or if the arity is wrong +function d(f: { (x: number): number }): number { + return f(); +} + +// ...or if there is no call property +function e(f: {}): number { + return f(); +} + +// Make sure we complain even if the object literal is unsealed. +function f(): number { + var x = {}; + return x(); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1425:19) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test B.js 1`] = ` +"// You should be able to use a function as an object with a call property +var a: { (x: number): string } = function (x: number): string { return \"hi\"; }; + +// ...and it should notice when the return type is wrong +var b: { (x: number): number } = function (x: number): string { return \"hi\"; }; + +// ...or if the param type is wrong +var c: { (x: string): string } = function (x: number): string { return \"hi\"; }; + +// ...or if the arity is wrong +var d: { (): string } = function (x: number): string { return \"hi\"; }; + +// ...but subtyping rules still apply +var e: { (x: any): void } = function() { } // arity +var f: { (): mixed } = function(): string { return \"hi\" } // return type +var g: { (x: string): void } = function(x: mixed) { } // param type + +// A function can be an object +var y : {} = function (x: number): string { return \"hi\"; }; +var z : Object = function (x: number): string { return \"hi\"; }; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1425:19) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test C.js 1`] = ` +"// You should be able to use an object as a function +function a(x: { (z: number): string }): (z: number) => string { + return x; +} + +// ...and it should notice when the return type is wrong +function b(x: { (z: number): string }): (z: number) => number { + return x; +} + +// ...or if the param type is wrong +function c(x: { (z: number): string }): (z: string) => string { + return x; +} + +// ...or if the arity is wrong +function d(x: { (z: number): string }): () => string { + return x; +} + +// ...or if it doesn\'t have a call property +function e(x: {}): () => string { + return x; +} + +// AnyFunT should also be allowed +function f(x: { (z: number): string }): Function { + return x; +} + +// ... but only if the object is callable +function g(x: {}): Function { + return x; // error +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1425:19) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test D.js 1`] = ` +"// Multiple call properties should also be supported +function a(f: { (): string; (x: number): string }): string { + return f() + f(123); +} + +// It should be fine when a function satisfies them all +var b: { (): string; (x: number): string } = + function (x?: number): string { return \"hi\"; }; + +// ...but should notice when a function doesn\'t satisfy them all +var c: { (): string; (x: number): string } = + function (x: number): string { return \"hi\"; }; + +// Only one call property needs to match the function +function d(x: { (): string; (x: number): string }): () => string { + return x; +} + +// ...but you need at least one +function e(x: { (): string; (x: number): string }): () => number { + return x; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1425:19) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test E.js 1`] = ` +"// Expecting properties that don\'t exist should be an error +var a : { someProp: number } = function () {}; + +// Expecting properties that do exist should be fine +var b : { apply: Function } = function () {}; + +// Expecting properties in the functions statics should be fine +var f = function () {}; +f.myProp = 123; +var c : { myProp: number } = f; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Expecting properties that don\'t exist should be an error +var a: { someProp: number } = function() { + +}; +// Expecting properties that do exist should be fine +var b: { apply: Function } = function() { + +}; +// Expecting properties in the functions statics should be fine +var f = function() { + +}; +f.myProp = 123; +var c: { myProp: number } = f; + +" +`; + +exports[`test F.js 1`] = ` +"// You should be able to use an arrow function as an object with a call property + +var a: { (x: number): string } = (x) => x.toString() + +// ...and it should notice when the return type is wrong +var b: { (x: number): number } = (x) => \"hi\" + +// ...or if the param type is wrong +var c: { (x: string): string } = (x) => x.toFixed() + +// ...or if the arity is wrong +var d: { (): string } = (x) => \"hi\" + +// ...but subtyping rules still apply +var e: { (x: any): void } = () => { } // arity +var f: { (): mixed } = () => \"hi\" // return type +var g: { (x: Date): void } = (x) => { x * 2 } // param type (date < number) + +// A function can be an object +var y : {} = (x) => \"hi\" +var z : Object = (x) => \"hi\" +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1425:19) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/call_properties/jsfmt.spec.js b/tests/call_properties/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/call_properties/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/callable/__snapshots__/jsfmt.spec.js.snap b/tests/callable/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..f699cf940cc0 --- /dev/null +++ b/tests/callable/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,70 @@ +exports[`test optional.js 1`] = ` +"type F = { + (x: string): number; + p?: string; +} + +function f(x) { + return x.length; +} + +(f: F); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1425:19) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test primitives.js 1`] = ` +"var x = Boolean(4); +function foo(fn:(value:any)=>boolean) { } +foo(Boolean); + +var dict: { [k: string]: any } = {}; +dict(); // error, callable signature not found + +interface ICall { + (x: string): void; +} +declare var icall: ICall; +icall(0); // error, number ~> string +icall.call(null, 0); // error, number ~> string + +type Callable = { + (x: string): void; +} + +declare var callable: Callable; +callable(0); // error, number ~> string +callable.call(null, 0); // error, number ~> string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/callable/jsfmt.spec.js b/tests/callable/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/callable/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/callable/optional.js b/tests/callable/optional.js new file mode 100644 index 000000000000..6d16aeec66c7 --- /dev/null +++ b/tests/callable/optional.js @@ -0,0 +1,10 @@ +type F = { + (x: string): number; + p?: string; +} + +function f(x) { + return x.length; +} + +(f: F); diff --git a/tests/callable/primitives.js b/tests/callable/primitives.js new file mode 100644 index 000000000000..2fa4c16f0ea5 --- /dev/null +++ b/tests/callable/primitives.js @@ -0,0 +1,21 @@ +var x = Boolean(4); +function foo(fn:(value:any)=>boolean) { } +foo(Boolean); + +var dict: { [k: string]: any } = {}; +dict(); // error, callable signature not found + +interface ICall { + (x: string): void; +} +declare var icall: ICall; +icall(0); // error, number ~> string +icall.call(null, 0); // error, number ~> string + +type Callable = { + (x: string): void; +} + +declare var callable: Callable; +callable(0); // error, number ~> string +callable.call(null, 0); // error, number ~> string diff --git a/tests/check-contents/__snapshots__/jsfmt.spec.js.snap b/tests/check-contents/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..202940e64222 --- /dev/null +++ b/tests/check-contents/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,30 @@ +exports[`test not_flow.js 1`] = ` +"1 * \'foo\'; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +1 * \"foo\"; + +" +`; + +exports[`test syntax_error.js 1`] = ` +"/* @flow */ + +( +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (4:0) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$3.parseExprAtom (/node_modules/babylon/lib/index.js:3592:12) + at Parser.parseExprAtom (/node_modules/babylon/lib/index.js:6408:22) + at Parser.pp$3.parseExprSubscripts (/node_modules/babylon/lib/index.js:3337:19) + at Parser.pp$3.parseMaybeUnary (/node_modules/babylon/lib/index.js:3317:19) + at Parser.pp$3.parseExprOps (/node_modules/babylon/lib/index.js:3247:19) + at Parser.pp$3.parseMaybeConditional (/node_modules/babylon/lib/index.js:3224:19) + at Parser.pp$3.parseMaybeAssign (/node_modules/babylon/lib/index.js:3187:19) + at Parser.parseMaybeAssign (/node_modules/babylon/lib/index.js:5700:20) +" +`; diff --git a/tests/check-contents/jsfmt.spec.js b/tests/check-contents/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/check-contents/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/check-contents/not_flow.js b/tests/check-contents/not_flow.js new file mode 100644 index 000000000000..f2562df539af --- /dev/null +++ b/tests/check-contents/not_flow.js @@ -0,0 +1 @@ +1 * 'foo'; diff --git a/tests/check-contents/syntax_error.js b/tests/check-contents/syntax_error.js new file mode 100644 index 000000000000..6ac6aff468a8 --- /dev/null +++ b/tests/check-contents/syntax_error.js @@ -0,0 +1,3 @@ +/* @flow */ + +( diff --git a/tests/class_fields/__snapshots__/jsfmt.spec.js.snap b/tests/class_fields/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..ec8cdbd64a47 --- /dev/null +++ b/tests/class_fields/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,523 @@ +exports[`test base_class.js 1`] = ` +"// @flow + +class Base { + unannotatedField; + annotatedField: number; + initializedField = 42; + initializedFieldWithThis = this.initializedField; + annotatedInitializedFieldValid: ?number = 42; + annotatedInitializedFieldInvalid: number = \'asdf\'; // Error: string ~> number + + static unannotatedField; + static annotatedField: number; + static initializedField = \'asdf\'; + static initializedFieldWithThis = this.initializedField; + static annotatedInitializedFieldValid: ?number = 42; + static annotatedInitializedFieldInvalid: number = \'asdf\'; // Error: string ~> number +} + +var o = new Base(); + +/** + * Unannotated fields are open. + */ +(o.unannotatedField: string); +(o.unannotatedField: number); +(Base.unannotatedField: string); +(Base.unannotatedField: number); + +/** + * Annotated (but uninitialized) fields still have a type. + */ +(o.annotatedField: number); +(o.annotatedField: string); // Error: number ~> string +(Base.annotatedField: number); +(Base.annotatedField: string); // Error: number ~> string + +/** + * Initialized (but unannotated) fields assume the type of their initializer. + */ +(o.initializedField: number); +(o.initializedField: string); // Error: number ~> string +(Base.initializedField: string); +(Base.initializedField: number); // Error: string ~> number + +/** + * Initialized fields can reference \`this\`. + */ +(o.initializedFieldWithThis: number); +(o.initializedFieldWithThis: string); // Error: number ~> string +(Base.initializedFieldWithThis: string); +(Base.initializedFieldWithThis: number); // Error: string ~> number + +/** + * Initialized + annotated fields take the type of the annotation. + * (Note that this matters when the annotation is more general than the type of + * the initializer) + */ +(o.annotatedInitializedFieldValid: ?number); +(o.annotatedInitializedFieldValid: number); // Error: ?number ~> number +(Base.annotatedInitializedFieldValid: ?number); +(Base.annotatedInitializedFieldValid: number); // Error: ?number ~> number + +/** + * Initialized + annotated fields where the init/annot combo is a mismatch + * should assume the type of the annotation. + * + * (This happens in addition to erroring at the site of initialization) + */ +(o.annotatedInitializedFieldInvalid: number); +(o.annotatedInitializedFieldInvalid: string); // Error: number ~> string +(Base.annotatedInitializedFieldInvalid: number); +(Base.annotatedInitializedFieldInvalid: string); // Error: number ~> string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +class Base { + unannotatedField; + annotatedField: number; + initializedField = 42; + initializedFieldWithThis = this.initializedField; + annotatedInitializedFieldValid: ?number = 42; + annotatedInitializedFieldInvalid: number = \"asdf\";// Error: string ~> number + static unannotatedField; + static annotatedField: number; + static initializedField = \"asdf\"; + static initializedFieldWithThis = this.initializedField; + static annotatedInitializedFieldValid: ?number = 42; + static annotatedInitializedFieldInvalid: number = \"asdf\";// Error: string ~> number +} +var o = new Base(); +/** + * Unannotated fields are open. + */ +(o.unannotatedField: string); +(o.unannotatedField: number); +(Base.unannotatedField: string); +(Base.unannotatedField: number); +/** + * Annotated (but uninitialized) fields still have a type. + */ +(o.annotatedField: number); +(o.annotatedField: string);// Error: number ~> string +(Base.annotatedField: number); +(Base.annotatedField: string);// Error: number ~> string +/** + * Initialized (but unannotated) fields assume the type of their initializer. + */ +(o.initializedField: number); +(o.initializedField: string);// Error: number ~> string +(Base.initializedField: string); +(Base.initializedField: number);// Error: string ~> number +/** + * Initialized fields can reference \`this\`. + */ +(o.initializedFieldWithThis: number); +(o.initializedFieldWithThis: string);// Error: number ~> string +(Base.initializedFieldWithThis: string); +(Base.initializedFieldWithThis: number);// Error: string ~> number +/** + * Initialized + annotated fields take the type of the annotation. + * (Note that this matters when the annotation is more general than the type of + * the initializer) + */ +(o.annotatedInitializedFieldValid: ?number); +(o.annotatedInitializedFieldValid: number);// Error: ?number ~> number +(Base.annotatedInitializedFieldValid: ?number); +(Base.annotatedInitializedFieldValid: number);// Error: ?number ~> number +/** + * Initialized + annotated fields where the init/annot combo is a mismatch + * should assume the type of the annotation. + * + * (This happens in addition to erroring at the site of initialization) + */ +(o.annotatedInitializedFieldInvalid: number); +(o.annotatedInitializedFieldInvalid: string);// Error: number ~> string +(Base.annotatedInitializedFieldInvalid: number); +(Base.annotatedInitializedFieldInvalid: string);// Error: number ~> string + +" +`; + +exports[`test derived_class.js 1`] = ` +"// @flow + +class Base { + base_unannotatedField; + base_annotatedField: number; + base_initializedField = 42; + base_initializedFieldWithThis = this.base_initializedField; + base_annotatedInitializedFieldValid: ?number = 42; + base_annotatedInitializedFieldInvalid: number = \'asdf\'; // Error: string ~> number + + static base_unannotatedField; + static base_annotatedField: number; + static base_initializedField = \'asdf\'; + static base_initializedFieldWithThis = this.base_initializedField; + static base_annotatedInitializedFieldValid: ?number = 42; + static base_annotatedInitializedFieldInvalid: number = \'asdf\'; // Error: string ~> number + + inherited_initializer = 42; + static inherited_initializer = 42; +} + +class Child extends Base { + child_unannotatedField; + child_annotatedField: number; + child_initializedField = 42; + child_initializedFieldWithThis = this.child_initializedField; + child_annotatedInitializedFieldValid: ?number = 42; + child_annotatedInitializedFieldInvalid: number = \'asdf\'; // Error: string ~> number + + static child_unannotatedField; + static child_annotatedField: number; + static child_initializedField = \'asdf\'; + static child_initializedFieldWithThis = this.child_initializedField; + static child_annotatedInitializedFieldValid: ?number = 42; + static child_annotatedInitializedFieldInvalid: number = \'asdf\'; // Error: string ~> number + + inherited_initializer; + static inherited_initializer; +} + +var o = new Child(); + +/** + * Unannotated fields are open. + */ +(o.base_unannotatedField: string); +(o.base_unannotatedField: number); +(Child.base_unannotatedField: string); +(Child.base_unannotatedField: number); + +(o.child_unannotatedField: string); +(o.child_unannotatedField: number); +(Child.child_unannotatedField: string); +(Child.child_unannotatedField: number); + + +/** + * Annotated (but uninitialized) fields still have a type. + */ +(o.base_annotatedField: number); +(o.base_annotatedField: string); // Error: number ~> string +(Child.base_annotatedField: number); +(Child.base_annotatedField: string); // Error: number ~> string + +(o.child_annotatedField: number); +(o.child_annotatedField: string); // Error: number ~> string +(Child.child_annotatedField: number); +(Child.child_annotatedField: string); // Error: number ~> string + +/** + * Initialized (but unannotated) fields assume the type of their initializer. + */ +(o.base_initializedField: number); +(o.base_initializedField: string); // Error: number ~> string +(Child.base_initializedField: string); +(Child.base_initializedField: number); // Error: string ~> number + +(o.child_initializedField: number); +(o.child_initializedField: string); // Error: number ~> string +(Child.child_initializedField: string); +(Child.child_initializedField: number); // Error: string ~> number + +/** + * Initialized fields can reference \`this\`. + */ +(o.base_initializedFieldWithThis: number); +(o.base_initializedFieldWithThis: string); // Error: number ~> string +(Child.base_initializedFieldWithThis: string); +(Child.base_initializedFieldWithThis: number); // Error: string ~> number + +(o.child_initializedFieldWithThis: number); +(o.child_initializedFieldWithThis: string); // Error: number ~> string +(Child.child_initializedFieldWithThis: string); +(Child.child_initializedFieldWithThis: number); // Error: string ~> number + +/** + * Initialized + annotated fields take the type of the annotation. + * (Note that this matters when the annotation is more general than the type of + * the initializer) + */ +(o.base_annotatedInitializedFieldValid: ?number); +(o.base_annotatedInitializedFieldValid: number); // Error: ?number ~> number +(Child.base_annotatedInitializedFieldValid: ?number); +(Child.base_annotatedInitializedFieldValid: number); // Error: ?number ~> number + +(o.child_annotatedInitializedFieldValid: ?number); +(o.child_annotatedInitializedFieldValid: number); // Error: ?number ~> number +(Child.child_annotatedInitializedFieldValid: ?number); +(Child.child_annotatedInitializedFieldValid: number); // Error: ?number ~> number + +/** + * Initialized + annotated fields where the init/annot combo is a mismatch + * should assume the type of the annotation. + * + * (This happens in addition to erroring at the site of initialization) + */ +(o.base_annotatedInitializedFieldInvalid: number); +(o.base_annotatedInitializedFieldInvalid: string); // Error: number ~> string +(Child.base_annotatedInitializedFieldInvalid: number); +(Child.base_annotatedInitializedFieldInvalid: string); // Error: number ~> string + +(o.child_annotatedInitializedFieldInvalid: number); +(o.child_annotatedInitializedFieldInvalid: string); // Error: number ~> string +(Child.child_annotatedInitializedFieldInvalid: number); +(Child.child_annotatedInitializedFieldInvalid: string); // Error: number ~> string + +/** + * Derived fields without an initializer that shadow base fields *with* an + * initializer should have the type of the base field. + */ +(o.inherited_initializer: number); +(o.inherited_initializer: string); // Error: number ~> string +(Child.inherited_initializer: number); +(Child.inherited_initializer: string); // Error: number ~> string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +class Base { + base_unannotatedField; + base_annotatedField: number; + base_initializedField = 42; + base_initializedFieldWithThis = this.base_initializedField; + base_annotatedInitializedFieldValid: ?number = 42; + base_annotatedInitializedFieldInvalid: number = \"asdf\";// Error: string ~> number + static base_unannotatedField; + static base_annotatedField: number; + static base_initializedField = \"asdf\"; + static base_initializedFieldWithThis = this.base_initializedField; + static base_annotatedInitializedFieldValid: ?number = 42; + static base_annotatedInitializedFieldInvalid: number = \"asdf\";// Error: string ~> number + inherited_initializer = 42; + static inherited_initializer = 42; +} +class Child extends Base { + child_unannotatedField; + child_annotatedField: number; + child_initializedField = 42; + child_initializedFieldWithThis = this.child_initializedField; + child_annotatedInitializedFieldValid: ?number = 42; + child_annotatedInitializedFieldInvalid: number = \"asdf\";// Error: string ~> number + static child_unannotatedField; + static child_annotatedField: number; + static child_initializedField = \"asdf\"; + static child_initializedFieldWithThis = this.child_initializedField; + static child_annotatedInitializedFieldValid: ?number = 42; + static child_annotatedInitializedFieldInvalid: number = \"asdf\";// Error: string ~> number + inherited_initializer; + static inherited_initializer; +} +var o = new Child(); +/** + * Unannotated fields are open. + */ +(o.base_unannotatedField: string); +(o.base_unannotatedField: number); +(Child.base_unannotatedField: string); +(Child.base_unannotatedField: number); +(o.child_unannotatedField: string); +(o.child_unannotatedField: number); +(Child.child_unannotatedField: string); +(Child.child_unannotatedField: number); +/** + * Annotated (but uninitialized) fields still have a type. + */ +(o.base_annotatedField: number); +(o.base_annotatedField: string);// Error: number ~> string +(Child.base_annotatedField: number); +(Child.base_annotatedField: string);// Error: number ~> string +(o.child_annotatedField: number); +(o.child_annotatedField: string);// Error: number ~> string +(Child.child_annotatedField: number); +(Child.child_annotatedField: string);// Error: number ~> string +/** + * Initialized (but unannotated) fields assume the type of their initializer. + */ +(o.base_initializedField: number); +(o.base_initializedField: string);// Error: number ~> string +(Child.base_initializedField: string); +(Child.base_initializedField: number);// Error: string ~> number +(o.child_initializedField: number); +(o.child_initializedField: string);// Error: number ~> string +(Child.child_initializedField: string); +(Child.child_initializedField: number);// Error: string ~> number +/** + * Initialized fields can reference \`this\`. + */ +(o.base_initializedFieldWithThis: number); +(o.base_initializedFieldWithThis: string);// Error: number ~> string +(Child.base_initializedFieldWithThis: string); +(Child.base_initializedFieldWithThis: number);// Error: string ~> number +(o.child_initializedFieldWithThis: number); +(o.child_initializedFieldWithThis: string);// Error: number ~> string +(Child.child_initializedFieldWithThis: string); +(Child.child_initializedFieldWithThis: number);// Error: string ~> number +/** + * Initialized + annotated fields take the type of the annotation. + * (Note that this matters when the annotation is more general than the type of + * the initializer) + */ +(o.base_annotatedInitializedFieldValid: ?number); +(o.base_annotatedInitializedFieldValid: number);// Error: ?number ~> number +(Child.base_annotatedInitializedFieldValid: ?number); +(Child.base_annotatedInitializedFieldValid: number);// Error: ?number ~> number +(o.child_annotatedInitializedFieldValid: ?number); +(o.child_annotatedInitializedFieldValid: number);// Error: ?number ~> number +(Child.child_annotatedInitializedFieldValid: ?number); +(Child.child_annotatedInitializedFieldValid: number);// Error: ?number ~> number +/** + * Initialized + annotated fields where the init/annot combo is a mismatch + * should assume the type of the annotation. + * + * (This happens in addition to erroring at the site of initialization) + */ +(o.base_annotatedInitializedFieldInvalid: number); +(o.base_annotatedInitializedFieldInvalid: string);// Error: number ~> string +(Child.base_annotatedInitializedFieldInvalid: number); +(Child.base_annotatedInitializedFieldInvalid: string);// Error: number ~> string +(o.child_annotatedInitializedFieldInvalid: number); +(o.child_annotatedInitializedFieldInvalid: string);// Error: number ~> string +(Child.child_annotatedInitializedFieldInvalid: number); +(Child.child_annotatedInitializedFieldInvalid: string);// Error: number ~> string +/** + * Derived fields without an initializer that shadow base fields *with* an + * initializer should have the type of the base field. + */ +(o.inherited_initializer: number); +(o.inherited_initializer: string);// Error: number ~> string +(Child.inherited_initializer: number); +(Child.inherited_initializer: string);// Error: number ~> string + +" +`; + +exports[`test generic_class.js 1`] = ` +"// @flow + +/** + * Fields annotated with a generic should assume a type once the type param + * is instantiated. + */ +class ClassAnnotated { + p: T; + static p: T; +} + +var o1 = new ClassAnnotated(); +o1.p = 42; +(o1.p: number); +(o1.p: string); // Error: number ~> string +ClassAnnotated.p = 42; +(ClassAnnotated.p: number); +(ClassAnnotated.p: string); // Error: number ~> string + + +/** + * It\'s always an error to initialized a generically-typed field with an + * expression of any type other than the generic itself. + */ +class ClassGenericInitialized { + invalid: T = 42; // Error: number ~> Generic + valid: T = ((42:any):T); + + static invalid: T = 42; // Error: number ~> Generic + static valid: T = ((42:any):T); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1172:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test scoping.js 1`] = ` +"// @flow + +var someVar = 42; + +class Foo { + outer = someVar; + selfTyped: Foo; + selfTypedInit = new Foo(); + + static outer = someVar; + static selfTyped: Foo; + static selfTypedInit = new Foo(); + + constructor() { + var someVar = \'asdf\'; + } +} + +/** + * Field initializers execute in a scope immediately under the scope outside the + * class definition. + */ +(new Foo().outer: number); +(new Foo().outer: string); // Error: number ~> string +(Foo.outer: number); +(Foo.outer: string); // Error: number ~> string + +/** + * Field initializers should be able to refer to the class type in their type + * annotations. + */ +(new Foo().selfTyped: Foo); +(new Foo().selfTyped: number); // Error: Foo ~> number +(Foo.selfTyped: Foo); +(Foo.selfTyped: number); // Error: Foo ~> number + +(new Foo().selfTypedInit: Foo); +(new Foo().selfTypedInit: number); // Error: Foo ~> number +(Foo.selfTypedInit: Foo); +(Foo.selfTypedInit: number); // Error: Foo ~> number +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +var someVar = 42; +class Foo { + outer = someVar; + selfTyped: Foo; + selfTypedInit = new Foo(); + static outer = someVar; + static selfTyped: Foo; + static selfTypedInit = new Foo(); + constructor() { + var someVar = \"asdf\"; + } +} +/** + * Field initializers execute in a scope immediately under the scope outside the + * class definition. + */ +(new Foo().outer: number); +(new Foo().outer: string);// Error: number ~> string +(Foo.outer: number); +(Foo.outer: string);// Error: number ~> string +/** + * Field initializers should be able to refer to the class type in their type + * annotations. + */ +(new Foo().selfTyped: Foo); +(new Foo().selfTyped: number);// Error: Foo ~> number +(Foo.selfTyped: Foo); +(Foo.selfTyped: number);// Error: Foo ~> number +(new Foo().selfTypedInit: Foo); +(new Foo().selfTypedInit: number);// Error: Foo ~> number +(Foo.selfTypedInit: Foo); +(Foo.selfTypedInit: number);// Error: Foo ~> number + +" +`; diff --git a/tests/class_fields/base_class.js b/tests/class_fields/base_class.js new file mode 100644 index 000000000000..b5ad46bcca31 --- /dev/null +++ b/tests/class_fields/base_class.js @@ -0,0 +1,72 @@ +// @flow + +class Base { + unannotatedField; + annotatedField: number; + initializedField = 42; + initializedFieldWithThis = this.initializedField; + annotatedInitializedFieldValid: ?number = 42; + annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number + + static unannotatedField; + static annotatedField: number; + static initializedField = 'asdf'; + static initializedFieldWithThis = this.initializedField; + static annotatedInitializedFieldValid: ?number = 42; + static annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number +} + +var o = new Base(); + +/** + * Unannotated fields are open. + */ +(o.unannotatedField: string); +(o.unannotatedField: number); +(Base.unannotatedField: string); +(Base.unannotatedField: number); + +/** + * Annotated (but uninitialized) fields still have a type. + */ +(o.annotatedField: number); +(o.annotatedField: string); // Error: number ~> string +(Base.annotatedField: number); +(Base.annotatedField: string); // Error: number ~> string + +/** + * Initialized (but unannotated) fields assume the type of their initializer. + */ +(o.initializedField: number); +(o.initializedField: string); // Error: number ~> string +(Base.initializedField: string); +(Base.initializedField: number); // Error: string ~> number + +/** + * Initialized fields can reference `this`. + */ +(o.initializedFieldWithThis: number); +(o.initializedFieldWithThis: string); // Error: number ~> string +(Base.initializedFieldWithThis: string); +(Base.initializedFieldWithThis: number); // Error: string ~> number + +/** + * Initialized + annotated fields take the type of the annotation. + * (Note that this matters when the annotation is more general than the type of + * the initializer) + */ +(o.annotatedInitializedFieldValid: ?number); +(o.annotatedInitializedFieldValid: number); // Error: ?number ~> number +(Base.annotatedInitializedFieldValid: ?number); +(Base.annotatedInitializedFieldValid: number); // Error: ?number ~> number + +/** + * Initialized + annotated fields where the init/annot combo is a mismatch + * should assume the type of the annotation. + * + * (This happens in addition to erroring at the site of initialization) + */ +(o.annotatedInitializedFieldInvalid: number); +(o.annotatedInitializedFieldInvalid: string); // Error: number ~> string +(Base.annotatedInitializedFieldInvalid: number); +(Base.annotatedInitializedFieldInvalid: string); // Error: number ~> string diff --git a/tests/class_fields/derived_class.js b/tests/class_fields/derived_class.js new file mode 100644 index 000000000000..89b40c1664fd --- /dev/null +++ b/tests/class_fields/derived_class.js @@ -0,0 +1,134 @@ +// @flow + +class Base { + base_unannotatedField; + base_annotatedField: number; + base_initializedField = 42; + base_initializedFieldWithThis = this.base_initializedField; + base_annotatedInitializedFieldValid: ?number = 42; + base_annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number + + static base_unannotatedField; + static base_annotatedField: number; + static base_initializedField = 'asdf'; + static base_initializedFieldWithThis = this.base_initializedField; + static base_annotatedInitializedFieldValid: ?number = 42; + static base_annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number + + inherited_initializer = 42; + static inherited_initializer = 42; +} + +class Child extends Base { + child_unannotatedField; + child_annotatedField: number; + child_initializedField = 42; + child_initializedFieldWithThis = this.child_initializedField; + child_annotatedInitializedFieldValid: ?number = 42; + child_annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number + + static child_unannotatedField; + static child_annotatedField: number; + static child_initializedField = 'asdf'; + static child_initializedFieldWithThis = this.child_initializedField; + static child_annotatedInitializedFieldValid: ?number = 42; + static child_annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number + + inherited_initializer; + static inherited_initializer; +} + +var o = new Child(); + +/** + * Unannotated fields are open. + */ +(o.base_unannotatedField: string); +(o.base_unannotatedField: number); +(Child.base_unannotatedField: string); +(Child.base_unannotatedField: number); + +(o.child_unannotatedField: string); +(o.child_unannotatedField: number); +(Child.child_unannotatedField: string); +(Child.child_unannotatedField: number); + + +/** + * Annotated (but uninitialized) fields still have a type. + */ +(o.base_annotatedField: number); +(o.base_annotatedField: string); // Error: number ~> string +(Child.base_annotatedField: number); +(Child.base_annotatedField: string); // Error: number ~> string + +(o.child_annotatedField: number); +(o.child_annotatedField: string); // Error: number ~> string +(Child.child_annotatedField: number); +(Child.child_annotatedField: string); // Error: number ~> string + +/** + * Initialized (but unannotated) fields assume the type of their initializer. + */ +(o.base_initializedField: number); +(o.base_initializedField: string); // Error: number ~> string +(Child.base_initializedField: string); +(Child.base_initializedField: number); // Error: string ~> number + +(o.child_initializedField: number); +(o.child_initializedField: string); // Error: number ~> string +(Child.child_initializedField: string); +(Child.child_initializedField: number); // Error: string ~> number + +/** + * Initialized fields can reference `this`. + */ +(o.base_initializedFieldWithThis: number); +(o.base_initializedFieldWithThis: string); // Error: number ~> string +(Child.base_initializedFieldWithThis: string); +(Child.base_initializedFieldWithThis: number); // Error: string ~> number + +(o.child_initializedFieldWithThis: number); +(o.child_initializedFieldWithThis: string); // Error: number ~> string +(Child.child_initializedFieldWithThis: string); +(Child.child_initializedFieldWithThis: number); // Error: string ~> number + +/** + * Initialized + annotated fields take the type of the annotation. + * (Note that this matters when the annotation is more general than the type of + * the initializer) + */ +(o.base_annotatedInitializedFieldValid: ?number); +(o.base_annotatedInitializedFieldValid: number); // Error: ?number ~> number +(Child.base_annotatedInitializedFieldValid: ?number); +(Child.base_annotatedInitializedFieldValid: number); // Error: ?number ~> number + +(o.child_annotatedInitializedFieldValid: ?number); +(o.child_annotatedInitializedFieldValid: number); // Error: ?number ~> number +(Child.child_annotatedInitializedFieldValid: ?number); +(Child.child_annotatedInitializedFieldValid: number); // Error: ?number ~> number + +/** + * Initialized + annotated fields where the init/annot combo is a mismatch + * should assume the type of the annotation. + * + * (This happens in addition to erroring at the site of initialization) + */ +(o.base_annotatedInitializedFieldInvalid: number); +(o.base_annotatedInitializedFieldInvalid: string); // Error: number ~> string +(Child.base_annotatedInitializedFieldInvalid: number); +(Child.base_annotatedInitializedFieldInvalid: string); // Error: number ~> string + +(o.child_annotatedInitializedFieldInvalid: number); +(o.child_annotatedInitializedFieldInvalid: string); // Error: number ~> string +(Child.child_annotatedInitializedFieldInvalid: number); +(Child.child_annotatedInitializedFieldInvalid: string); // Error: number ~> string + +/** + * Derived fields without an initializer that shadow base fields *with* an + * initializer should have the type of the base field. + */ +(o.inherited_initializer: number); +(o.inherited_initializer: string); // Error: number ~> string +(Child.inherited_initializer: number); +(Child.inherited_initializer: string); // Error: number ~> string diff --git a/tests/class_fields/generic_class.js b/tests/class_fields/generic_class.js new file mode 100644 index 000000000000..d214ff6ceee7 --- /dev/null +++ b/tests/class_fields/generic_class.js @@ -0,0 +1,31 @@ +// @flow + +/** + * Fields annotated with a generic should assume a type once the type param + * is instantiated. + */ +class ClassAnnotated { + p: T; + static p: T; +} + +var o1 = new ClassAnnotated(); +o1.p = 42; +(o1.p: number); +(o1.p: string); // Error: number ~> string +ClassAnnotated.p = 42; +(ClassAnnotated.p: number); +(ClassAnnotated.p: string); // Error: number ~> string + + +/** + * It's always an error to initialized a generically-typed field with an + * expression of any type other than the generic itself. + */ +class ClassGenericInitialized { + invalid: T = 42; // Error: number ~> Generic + valid: T = ((42:any):T); + + static invalid: T = 42; // Error: number ~> Generic + static valid: T = ((42:any):T); +} diff --git a/tests/class_fields/jsfmt.spec.js b/tests/class_fields/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/class_fields/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/class_fields/scoping.js b/tests/class_fields/scoping.js new file mode 100644 index 000000000000..462b51293237 --- /dev/null +++ b/tests/class_fields/scoping.js @@ -0,0 +1,40 @@ +// @flow + +var someVar = 42; + +class Foo { + outer = someVar; + selfTyped: Foo; + selfTypedInit = new Foo(); + + static outer = someVar; + static selfTyped: Foo; + static selfTypedInit = new Foo(); + + constructor() { + var someVar = 'asdf'; + } +} + +/** + * Field initializers execute in a scope immediately under the scope outside the + * class definition. + */ +(new Foo().outer: number); +(new Foo().outer: string); // Error: number ~> string +(Foo.outer: number); +(Foo.outer: string); // Error: number ~> string + +/** + * Field initializers should be able to refer to the class type in their type + * annotations. + */ +(new Foo().selfTyped: Foo); +(new Foo().selfTyped: number); // Error: Foo ~> number +(Foo.selfTyped: Foo); +(Foo.selfTyped: number); // Error: Foo ~> number + +(new Foo().selfTypedInit: Foo); +(new Foo().selfTypedInit: number); // Error: Foo ~> number +(Foo.selfTypedInit: Foo); +(Foo.selfTypedInit: number); // Error: Foo ~> number diff --git a/tests/class_munging/__snapshots__/jsfmt.spec.js.snap b/tests/class_munging/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..7e538f3c192d --- /dev/null +++ b/tests/class_munging/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,69 @@ +exports[`test with_munging.js 1`] = ` +"/** + * @flow + */ + +class Foo { + _method(): string { + return 'this is private'; + } +} + +class Bar extends Foo { + test() { + (this._method(): string); // error + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +class Foo { + _method(): string { + return "this is private"; + } +} +class Bar extends Foo { + test() { + (this._method(): string);// error + } +} + +" +`; + +exports[`test without_munging.js 1`] = ` +"/** + * @flow + * @preventMunge + */ + +class Foo { + _method(): string { + return 'this is not private'; + } +} + +class Bar extends Foo { + test() { + (this._method(): string); // ok + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + * @preventMunge + */ +class Foo { + _method(): string { + return "this is not private"; + } +} +class Bar extends Foo { + test() { + (this._method(): string);// ok + } +} + +" +`; diff --git a/tests/class_munging/jsfmt.spec.js b/tests/class_munging/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/class_munging/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/class_munging/with_munging.js b/tests/class_munging/with_munging.js new file mode 100644 index 000000000000..ead68e9fbaba --- /dev/null +++ b/tests/class_munging/with_munging.js @@ -0,0 +1,15 @@ +/** + * @flow + */ + +class Foo { + _method(): string { + return 'this is private'; + } +} + +class Bar extends Foo { + test() { + (this._method(): string); // error + } +} diff --git a/tests/class_munging/without_munging.js b/tests/class_munging/without_munging.js new file mode 100644 index 000000000000..400dcc84a81a --- /dev/null +++ b/tests/class_munging/without_munging.js @@ -0,0 +1,16 @@ +/** + * @flow + * @preventMunge + */ + +class Foo { + _method(): string { + return 'this is not private'; + } +} + +class Bar extends Foo { + test() { + (this._method(): string); // ok + } +} diff --git a/tests/class_statics/__snapshots__/jsfmt.spec.js.snap b/tests/class_statics/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..9b81d21be6d0 --- /dev/null +++ b/tests/class_statics/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,80 @@ +exports[`test test.js 1`] = ` +"class A { + static x: number; + static y: string; + static foo(x: number) { } + static bar(y: string) { } +} +A.qux = function(x: string) { } // error? + +class B extends A { + static x: string; // error? + static foo(x: string) { } // error? + static main() { + B.x = 0; // error + B.x = \"\"; + B.foo(0); // error + B.foo(\"\"); + B.y = 0; // error + B.bar(0); // error + B.qux(0); // error + } + static create(): A { + return new this(); + } + + static badCreate(): number { + return new this(); // error B ~> number + } +} + +class C { + static x: X; + static bar(x: X) { } + static create(): C<*> { + return new this(); + } +} + +class D extends C { + static main() { + D.foo(0); // error? + + D.bar(0); + } +} + +var d: C<*> = D.create(); +(new A: typeof A); +(B: typeof A); + +class E { + static x: number; + static foo(): string { + this.bar(); // error + return this.x; // error + } +} + +// note: above classdefs are sufficiently annotated to export +module.exports = { + A: A, B: B, C: C, D: D, E: E +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1172:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/class_statics/jsfmt.spec.js b/tests/class_statics/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/class_statics/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/class_statics/test.js b/tests/class_statics/test.js new file mode 100644 index 000000000000..4fea29a3ed5b --- /dev/null +++ b/tests/class_statics/test.js @@ -0,0 +1,61 @@ +class A { + static x: number; + static y: string; + static foo(x: number) { } + static bar(y: string) { } +} +A.qux = function(x: string) { } // error? + +class B extends A { + static x: string; // error? + static foo(x: string) { } // error? + static main() { + B.x = 0; // error + B.x = ""; + B.foo(0); // error + B.foo(""); + B.y = 0; // error + B.bar(0); // error + B.qux(0); // error + } + static create(): A { + return new this(); + } + + static badCreate(): number { + return new this(); // error B ~> number + } +} + +class C { + static x: X; + static bar(x: X) { } + static create(): C<*> { + return new this(); + } +} + +class D extends C { + static main() { + D.foo(0); // error? + + D.bar(0); + } +} + +var d: C<*> = D.create(); +(new A: typeof A); +(B: typeof A); + +class E { + static x: number; + static foo(): string { + this.bar(); // error + return this.x; // error + } +} + +// note: above classdefs are sufficiently annotated to export +module.exports = { + A: A, B: B, C: C, D: D, E: E +} diff --git a/tests/class_subtyping/__snapshots__/jsfmt.spec.js.snap b/tests/class_subtyping/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..7d3266513137 --- /dev/null +++ b/tests/class_subtyping/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,91 @@ +exports[`test test.js 1`] = ` +"/* @flow */ +class A {} +class B {} + +module.exports = { A, B }; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +class A {} +class B {} +module.exports = { A, B }; + +" +`; + +exports[`test test2.js 1`] = ` +"/* @flow */ +var I = require(\"./test.js\"); + +class C extends I.A {} + +var x: I.A = new C(); +var y: I.B = new C(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var I = require(\"./test.js\"); +class C extends I.A {} +var x: I.A = new C(); +var y: I.B = new C(); + +" +`; + +exports[`test test3.js 1`] = ` +"class A {} +class B extends A {} +class C extends B {} + +var c: C> = new C; // none of the type args matter +var a: A> = c; // the third type arg is incorrect +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1172:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test4.js 1`] = ` +"class C { x: X; } + +function foo(c: C, x: X) { } + +type O = { f: number }; + +foo((new C: C), { f_: 0 }); + +class D extends C { + bar() { this.x; } +} + +foo(new D, { f_: 0 }); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1172:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/class_subtyping/jsfmt.spec.js b/tests/class_subtyping/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/class_subtyping/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/class_subtyping/test.js b/tests/class_subtyping/test.js new file mode 100644 index 000000000000..68aa0c6e71ae --- /dev/null +++ b/tests/class_subtyping/test.js @@ -0,0 +1,5 @@ +/* @flow */ +class A {} +class B {} + +module.exports = { A, B }; diff --git a/tests/class_subtyping/test2.js b/tests/class_subtyping/test2.js new file mode 100644 index 000000000000..0601a3576687 --- /dev/null +++ b/tests/class_subtyping/test2.js @@ -0,0 +1,7 @@ +/* @flow */ +var I = require("./test.js"); + +class C extends I.A {} + +var x: I.A = new C(); +var y: I.B = new C(); diff --git a/tests/class_subtyping/test3.js b/tests/class_subtyping/test3.js new file mode 100644 index 000000000000..4f76d86d500f --- /dev/null +++ b/tests/class_subtyping/test3.js @@ -0,0 +1,6 @@ +class A {} +class B extends A {} +class C extends B {} + +var c: C> = new C; // none of the type args matter +var a: A> = c; // the third type arg is incorrect diff --git a/tests/class_subtyping/test4.js b/tests/class_subtyping/test4.js new file mode 100644 index 000000000000..9cd7abb32b4e --- /dev/null +++ b/tests/class_subtyping/test4.js @@ -0,0 +1,13 @@ +class C { x: X; } + +function foo(c: C, x: X) { } + +type O = { f: number }; + +foo((new C: C), { f_: 0 }); + +class D extends C { + bar() { this.x; } +} + +foo(new D, { f_: 0 }); diff --git a/tests/class_type/__snapshots__/jsfmt.spec.js.snap b/tests/class_type/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..915e603e6b78 --- /dev/null +++ b/tests/class_type/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,64 @@ +exports[`test test.js 1`] = ` +"class A { } +function foo(x: Class
): A { + return new x(); // OK +} + +class B { + constructor(_: any) { } +} +function bar(x: Class): B { + return new x(); // error (too few args) +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test2.js 1`] = ` +"// A function to typecheck values against their types. Covariance of Class<.> +// makes it useless in such a function (when limited to classes and instances), +// since everything can be trivially satisfied by going to \`mixed\`. +declare function check(cls: $Type, inst: X): void; + +class A { } +class B extends A { } +class C { } + +check(B, new A); +check(A, new B); +check(C, new A); +check(C, new B); +check(B, new C); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/class_type/jsfmt.spec.js b/tests/class_type/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/class_type/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/class_type/test.js b/tests/class_type/test.js new file mode 100644 index 000000000000..a72a3903bee0 --- /dev/null +++ b/tests/class_type/test.js @@ -0,0 +1,11 @@ +class A { } +function foo(x: Class): A { + return new x(); // OK +} + +class B { + constructor(_: any) { } +} +function bar(x: Class): B { + return new x(); // error (too few args) +} diff --git a/tests/class_type/test2.js b/tests/class_type/test2.js new file mode 100644 index 000000000000..071653cd3db7 --- /dev/null +++ b/tests/class_type/test2.js @@ -0,0 +1,14 @@ +// A function to typecheck values against their types. Covariance of Class<.> +// makes it useless in such a function (when limited to classes and instances), +// since everything can be trivially satisfied by going to `mixed`. +declare function check(cls: $Type, inst: X): void; + +class A { } +class B extends A { } +class C { } + +check(B, new A); +check(A, new B); +check(C, new A); +check(C, new B); +check(B, new C); diff --git a/tests/classes/A.js b/tests/classes/A.js new file mode 100644 index 000000000000..37bfb2d47616 --- /dev/null +++ b/tests/classes/A.js @@ -0,0 +1,5 @@ +class A { + foo(x:number):void { } +} + +module.exports = A; diff --git a/tests/classes/B.js b/tests/classes/B.js new file mode 100644 index 000000000000..84a52066d088 --- /dev/null +++ b/tests/classes/B.js @@ -0,0 +1,8 @@ +var A = require('./A'); + +class B extends A { } + +let b = new B(); +(b.foo: number); // error, number !~> function + +module.exports = B; diff --git a/tests/classes/C.js b/tests/classes/C.js new file mode 100644 index 000000000000..1837b2616d9b --- /dev/null +++ b/tests/classes/C.js @@ -0,0 +1,10 @@ +var B = require('./B'); + +class C extends B { + foo(x:string):void { } +} + +let c = new C(); +(c.foo: number); // error, number !~> function + +module.exports = C; diff --git a/tests/classes/D.js b/tests/classes/D.js new file mode 100644 index 000000000000..5f4cae464ab3 --- /dev/null +++ b/tests/classes/D.js @@ -0,0 +1,3 @@ +class D { } +class E { } +new E().x diff --git a/tests/classes/__snapshots__/jsfmt.spec.js.snap b/tests/classes/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..1f1021805669 --- /dev/null +++ b/tests/classes/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,249 @@ +exports[`test A.js 1`] = ` +"class A { + foo(x:number):void { } +} + +module.exports = A; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class A { + foo(x: number): void { + + } +} +module.exports = A; + +" +`; + +exports[`test B.js 1`] = ` +"var A = require(\'./A\'); + +class B extends A { } + +let b = new B(); +(b.foo: number); // error, number !~> function + +module.exports = B; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var A = require(\"./A\"); +class B extends A {} +let b = new B(); +(b.foo: number);// error, number !~> function +module.exports = B; + +" +`; + +exports[`test C.js 1`] = ` +"var B = require(\'./B\'); + +class C extends B { + foo(x:string):void { } +} + +let c = new C(); +(c.foo: number); // error, number !~> function + +module.exports = C; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var B = require(\"./B\"); +class C extends B { + foo(x: string): void { + + } +} +let c = new C(); +(c.foo: number);// error, number !~> function +module.exports = C; + +" +`; + +exports[`test D.js 1`] = ` +"class D { } +class E { } +new E().x +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class D {} +class E {} +new E().x; + +" +`; + +exports[`test class_shapes.js 1`] = ` +"/* @flow */ + +type Foo = { + a: string; // exists in TestClass + b: string; // doesn\'t exist + c?: ?string; // exists in TestClass, optional + d?: number; // doesn\'t exist +} + +class TestClass { + a: string; + c: ?string; +} + +var x = new TestClass(); + +x.a; // ok +x.b; // error, TestClass has no b +x.c; // ok +x.d; // error, TestClass has no d + +var y : Foo = x; +y.b; // error, doesn\'t exist in TestClass +y.d; // ok, it\'s optional + +class Test2Superclass { + a: number; // conflicts with cast to Foo + c: ?number; // conflicts with cast to Foo +} +class Test2Class extends Test2Superclass { + b: number; // conflicts with cast to Foo +} + +var z = new Test2Class(); +var w : Foo = z; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +type Foo = { + // exists in TestClass a: string, + // doesn\'t exist b: string, + // exists in TestClass, optional c?: ?string, + // doesn\'t exist d?: number +}; +class TestClass { + a: string; + c: ?string; +} +var x = new TestClass(); +x.a;// ok +x.b;// error, TestClass has no b +x.c;// ok +x.d;// error, TestClass has no d +var y: Foo = x; +y.b;// error, doesn\'t exist in TestClass +y.d;// ok, it\'s optional +class Test2Superclass { + a: number;// conflicts with cast to Foo + c: ?number;// conflicts with cast to Foo +} +class Test2Class extends Test2Superclass { + b: number;// conflicts with cast to Foo +} +var z = new Test2Class(); +var w: Foo = z; + +" +`; + +exports[`test expr.js 1`] = ` +"var Bar = class Foo { + static factory(): Foo { // OK: Foo is a type in this scope + return new Foo() // OK: Foo is a runtime binding in this scope + } +}; + +var bar1: Bar = new Bar() // OK +var bar2: Bar = Bar.factory() // OK + +// NB: Don\'t write expected errors using Foo to avoid error collapse hiding an +// unexpected failure in the above code. + +var B = class Baz { } +var b = new Baz(); // error: Baz is not a runtime binding in this scope + +var C = class Qux { } +var c: Qux = new C(); // error: Qux is not a type in this scope + +// OK: anon classes create no binding, but can be bound manually +var Anon = class { } +var anon: Anon = new Anon(); + +class Alias { } +var _Alias = class Alias { + static factory(): Alias { + return new Alias(); + } +} +var alias1: Alias = new _Alias(); // error: bad pun +var alias2: Alias = _Alias.factory(); // error: bad pun +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var Bar = class Foo { + factory(): Foo { + // OK: Foo is a type in this scope + return new Foo();// OK: Foo is a runtime binding in this scope + } +}; +var bar1: Bar = new Bar();// OK +var bar2: Bar = Bar.factory();// OK +// NB: Don\'t write expected errors using Foo to avoid error collapse hiding an +// unexpected failure in the above code. +var B = class Baz {}; +var b = new Baz();// error: Baz is not a runtime binding in this scope +var C = class Qux {}; +var c: Qux = new C();// error: Qux is not a type in this scope +// OK: anon classes create no binding, but can be bound manually +var Anon = class {}; +var anon: Anon = new Anon(); +class Alias {} +var _Alias = class Alias { + factory(): Alias { + return new Alias(); + } +}; +var alias1: Alias = new _Alias();// error: bad pun +var alias2: Alias = _Alias.factory();// error: bad pun + +" +`; + +exports[`test loc.js 1`] = ` +"/* @flow */ + +type Foo = number + +class Foo {} // error, shadows type Foo +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +type Foo = number; +class Foo {}// error, shadows type Foo + +" +`; + +exports[`test statics.js 1`] = ` +"/* @flow */ + +class C { + static p: string; +} +C.p = \"hi\"; + +// Class static fields are compatible with object types +(C: {p:string}); // ok +(C: {p:number}); // errors, string ~> number & vice versa (unify) + +declare var o: {p:number}; +(o: Class); // error, object type incompatible with class type +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/classes/class_shapes.js b/tests/classes/class_shapes.js new file mode 100644 index 000000000000..d35b273d6be5 --- /dev/null +++ b/tests/classes/class_shapes.js @@ -0,0 +1,35 @@ +/* @flow */ + +type Foo = { + a: string; // exists in TestClass + b: string; // doesn't exist + c?: ?string; // exists in TestClass, optional + d?: number; // doesn't exist +} + +class TestClass { + a: string; + c: ?string; +} + +var x = new TestClass(); + +x.a; // ok +x.b; // error, TestClass has no b +x.c; // ok +x.d; // error, TestClass has no d + +var y : Foo = x; +y.b; // error, doesn't exist in TestClass +y.d; // ok, it's optional + +class Test2Superclass { + a: number; // conflicts with cast to Foo + c: ?number; // conflicts with cast to Foo +} +class Test2Class extends Test2Superclass { + b: number; // conflicts with cast to Foo +} + +var z = new Test2Class(); +var w : Foo = z; diff --git a/tests/classes/expr.js b/tests/classes/expr.js new file mode 100644 index 000000000000..7fcca75c1f8f --- /dev/null +++ b/tests/classes/expr.js @@ -0,0 +1,30 @@ +var Bar = class Foo { + static factory(): Foo { // OK: Foo is a type in this scope + return new Foo() // OK: Foo is a runtime binding in this scope + } +}; + +var bar1: Bar = new Bar() // OK +var bar2: Bar = Bar.factory() // OK + +// NB: Don't write expected errors using Foo to avoid error collapse hiding an +// unexpected failure in the above code. + +var B = class Baz { } +var b = new Baz(); // error: Baz is not a runtime binding in this scope + +var C = class Qux { } +var c: Qux = new C(); // error: Qux is not a type in this scope + +// OK: anon classes create no binding, but can be bound manually +var Anon = class { } +var anon: Anon = new Anon(); + +class Alias { } +var _Alias = class Alias { + static factory(): Alias { + return new Alias(); + } +} +var alias1: Alias = new _Alias(); // error: bad pun +var alias2: Alias = _Alias.factory(); // error: bad pun diff --git a/tests/classes/jsfmt.spec.js b/tests/classes/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/classes/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/classes/loc.js b/tests/classes/loc.js new file mode 100644 index 000000000000..8f6066465e8f --- /dev/null +++ b/tests/classes/loc.js @@ -0,0 +1,5 @@ +/* @flow */ + +type Foo = number + +class Foo {} // error, shadows type Foo diff --git a/tests/classes/statics.js b/tests/classes/statics.js new file mode 100644 index 000000000000..75c9d5e7bdef --- /dev/null +++ b/tests/classes/statics.js @@ -0,0 +1,13 @@ +/* @flow */ + +class C { + static p: string; +} +C.p = "hi"; + +// Class static fields are compatible with object types +(C: {p:string}); // ok +(C: {p:number}); // errors, string ~> number & vice versa (unify) + +declare var o: {p:number}; +(o: Class); // error, object type incompatible with class type diff --git a/tests/closure/Closure.js b/tests/closure/Closure.js new file mode 100644 index 000000000000..baeae7811a1f --- /dev/null +++ b/tests/closure/Closure.js @@ -0,0 +1,80 @@ +/*** + * Test tracking of variable types across closure calls. + * @flow + */ + +function takes_string(_:string) { } + +// global write from function +// + +var global_x = "hello"; + +function global_f() { } +function global_g() { global_x = 42; } + +global_f(); +takes_string(global_x); // ok + +global_g(); +takes_string(global_x); // error + +global_x = 42; // shouldn't pollute linear refinement + +// local write from function +// + +function local_func() { + + var local_x = "hello"; + + function local_f() { } + function local_g() { local_x = 42; } + + local_f(); + takes_string(local_x); // ok + + local_g(); + takes_string(local_x); // error + + local_x = 42; // shouldn't pollute linear refinement +} + +// global write from method +// + +var global_y = "hello"; + +var global_o = { + f: function() { }, + g: function() { global_y = 42; } +} + +global_o.f(); +takes_string(global_y); // ok + +global_o.g(); +takes_string(global_y); // error + +global_y = 42; // shouldn't pollute linear refinement + +// local write from method +// + +function local_meth() { + + var local_y = "hello"; + + var local_o = { + f: function() { }, + g: function() { local_y = 42; } + } + + local_o.f(); + takes_string(local_y); // ok + + local_o.g(); + takes_string(local_y); // error + + local_y = 42; // shouldn't pollute linear refinement +} diff --git a/tests/closure/__snapshots__/jsfmt.spec.js.snap b/tests/closure/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..e0f87eb8caa1 --- /dev/null +++ b/tests/closure/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,256 @@ +exports[`test Closure.js 1`] = ` +"/*** + * Test tracking of variable types across closure calls. + * @flow + */ + +function takes_string(_:string) { } + +// global write from function +// + +var global_x = \"hello\"; + +function global_f() { } +function global_g() { global_x = 42; } + +global_f(); +takes_string(global_x); // ok + +global_g(); +takes_string(global_x); // error + +global_x = 42; // shouldn\'t pollute linear refinement + +// local write from function +// + +function local_func() { + + var local_x = \"hello\"; + + function local_f() { } + function local_g() { local_x = 42; } + + local_f(); + takes_string(local_x); // ok + + local_g(); + takes_string(local_x); // error + + local_x = 42; // shouldn\'t pollute linear refinement +} + +// global write from method +// + +var global_y = \"hello\"; + +var global_o = { + f: function() { }, + g: function() { global_y = 42; } +} + +global_o.f(); +takes_string(global_y); // ok + +global_o.g(); +takes_string(global_y); // error + +global_y = 42; // shouldn\'t pollute linear refinement + +// local write from method +// + +function local_meth() { + + var local_y = \"hello\"; + + var local_o = { + f: function() { }, + g: function() { local_y = 42; } + } + + local_o.f(); + takes_string(local_y); // ok + + local_o.g(); + takes_string(local_y); // error + + local_y = 42; // shouldn\'t pollute linear refinement +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/*** + * Test tracking of variable types across closure calls. + * @flow + */ +function takes_string(_: string) { + +} +// global write from function +// +var global_x = \"hello\"; +function global_f() { + +} +function global_g() { + global_x = 42; +} +global_f(); +takes_string(global_x);// ok +global_g(); +takes_string(global_x);// error +global_x = 42;// shouldn\'t pollute linear refinement +// local write from function +// +function local_func() { + var local_x = \"hello\"; + function local_f() { + + } + function local_g() { + local_x = 42; + } + local_f(); + takes_string(local_x);// ok + local_g(); + takes_string(local_x);// error + local_x = 42;// shouldn\'t pollute linear refinement +} +// global write from method +// +var global_y = \"hello\"; +var global_o = { + f: function() { + + }, + g: function() { + global_y = 42; + } +}; +global_o.f(); +takes_string(global_y);// ok +global_o.g(); +takes_string(global_y);// error +global_y = 42;// shouldn\'t pollute linear refinement +// local write from method +// +function local_meth() { + var local_y = \"hello\"; + var local_o = { + f: function() { + + }, + g: function() { + local_y = 42; + } + }; + local_o.f(); + takes_string(local_y);// ok + local_o.g(); + takes_string(local_y);// error + local_y = 42;// shouldn\'t pollute linear refinement +} + +" +`; + +exports[`test cond_havoc.js 1`] = ` +"// @flow + +// from sam, https://github.com/facebook/flow/issues/780 +// call to f() within if should properly havoc x. +// +function example(b: bool): number { + var x = 0; + function f() { x = \"\" } + if (b) { + f(); + } + return x; // error, string ~/~> number (return type anno) TODO +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +// from sam, https://github.com/facebook/flow/issues/780 +// call to f() within if should properly havoc x. +// +function example(b: boolean): number { + var x = 0; + function f() { + x = \"\"; + } + if (b) { + f(); + } + return x;// error, string ~/~> number (return type anno) TODO +} + +" +`; + +exports[`test const.js 1`] = ` +"/*** + * consts retain refinements + * @flow + */ + +// global, anybody can call it at any time +var call_me: () => void = () => {}; + +function g(x: ?number) { + + const const_x = x; + if (const_x) { + // ok: if const_x is truthy here, it\'s truthy everywhere + call_me = () => { var y:number = const_x; }; + } + + var var_x = x; + if (var_x) { + // error: var_x might no longer be truthy when call_me is called + call_me = () => { var y:number = var_x; }; // error + } + var_x = null; +} + +function h(x: number | string | boolean) { + + const const_x = x; + if (typeof(const_x) == \"number\") { + call_me = () => { var y:number = const_x; }; // ok + } else if (typeof(const_x) == \"string\") { + call_me = () => { var y:string = const_x; }; // ok + } else if (typeof(const_x) == \"boolean\") { + call_me = () => { var y:boolean = const_x; }; // ok + } + + var var_x = x; + if (typeof(var_x) == \"number\") { + call_me = () => { var y:number = var_x; }; // error + } else if (typeof(var_x) == \"string\") { + call_me = () => { var y:string = var_x; }; // error + } else if (typeof(var_x) == \"boolean\") { + call_me = () => { var y:boolean = var_x; }; // error + } +} + +// in a galaxy far far away +call_me(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/closure/cond_havoc.js b/tests/closure/cond_havoc.js new file mode 100644 index 000000000000..d4364f3280a2 --- /dev/null +++ b/tests/closure/cond_havoc.js @@ -0,0 +1,13 @@ +// @flow + +// from sam, https://github.com/facebook/flow/issues/780 +// call to f() within if should properly havoc x. +// +function example(b: bool): number { + var x = 0; + function f() { x = "" } + if (b) { + f(); + } + return x; // error, string ~/~> number (return type anno) TODO +} diff --git a/tests/closure/const.js b/tests/closure/const.js new file mode 100644 index 000000000000..e16e12efc5c8 --- /dev/null +++ b/tests/closure/const.js @@ -0,0 +1,47 @@ +/*** + * consts retain refinements + * @flow + */ + +// global, anybody can call it at any time +var call_me: () => void = () => {}; + +function g(x: ?number) { + + const const_x = x; + if (const_x) { + // ok: if const_x is truthy here, it's truthy everywhere + call_me = () => { var y:number = const_x; }; + } + + var var_x = x; + if (var_x) { + // error: var_x might no longer be truthy when call_me is called + call_me = () => { var y:number = var_x; }; // error + } + var_x = null; +} + +function h(x: number | string | boolean) { + + const const_x = x; + if (typeof(const_x) == "number") { + call_me = () => { var y:number = const_x; }; // ok + } else if (typeof(const_x) == "string") { + call_me = () => { var y:string = const_x; }; // ok + } else if (typeof(const_x) == "boolean") { + call_me = () => { var y:boolean = const_x; }; // ok + } + + var var_x = x; + if (typeof(var_x) == "number") { + call_me = () => { var y:number = var_x; }; // error + } else if (typeof(var_x) == "string") { + call_me = () => { var y:string = var_x; }; // error + } else if (typeof(var_x) == "boolean") { + call_me = () => { var y:boolean = var_x; }; // error + } +} + +// in a galaxy far far away +call_me(); diff --git a/tests/closure/jsfmt.spec.js b/tests/closure/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/closure/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/commonjs/Abs.js b/tests/commonjs/Abs.js new file mode 100644 index 000000000000..965e99f9f476 --- /dev/null +++ b/tests/commonjs/Abs.js @@ -0,0 +1,4 @@ + +function f(x:string) { } + +module.exports = f; diff --git a/tests/commonjs/Rel.js b/tests/commonjs/Rel.js new file mode 100644 index 000000000000..f6b1b97972c4 --- /dev/null +++ b/tests/commonjs/Rel.js @@ -0,0 +1,4 @@ + +var f = require('./Abs'); + +f(0); diff --git a/tests/commonjs/__snapshots__/jsfmt.spec.js.snap b/tests/commonjs/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..e28e7d3eacff --- /dev/null +++ b/tests/commonjs/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,25 @@ +exports[`test Abs.js 1`] = ` +" +function f(x:string) { } + +module.exports = f; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +function f(x: string) { + +} +module.exports = f; + +" +`; + +exports[`test Rel.js 1`] = ` +" +var f = require('./Abs'); + +f(0); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var f = require("./Abs"); +f(0); + +" +`; diff --git a/tests/commonjs/jsfmt.spec.js b/tests/commonjs/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/commonjs/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/computed_props/__snapshots__/jsfmt.spec.js.snap b/tests/computed_props/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..b9d5978c4d68 --- /dev/null +++ b/tests/computed_props/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,133 @@ +exports[`test test.js 1`] = ` +"var ColorId = { + RED: 'R', + GREEN: 'G', + BLUE: 'B', +}; + +var ColorNumber = { + RED: 'ff0000', + GREEN: '00ff00', + BLUE: '0000ff', +}; + +var ColorIdToNumber = { + [ColorId.RED]: ColorNumber.RED, + [ColorId.GREEN]: ColorNumber.GREEN, + [ColorId.BLUE]: ColorNumber.BLUE, +}; + +(ColorIdToNumber[ColorId.RED]: 'ffffff'); // oops + +ColorIdToNumber.XXX; // oops + +module.exports = { ColorId, ColorNumber }; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var ColorId = { RED: "R", GREEN: "G", BLUE: "B" }; +var ColorNumber = { RED: "ff0000", GREEN: "00ff00", BLUE: "0000ff" }; +var ColorIdToNumber = { + [ColorId.RED]: ColorNumber.RED, + [ColorId.GREEN]: ColorNumber.GREEN, + [ColorId.BLUE]: ColorNumber.BLUE +}; +(ColorIdToNumber[ColorId.RED]: "ffffff");// oops +ColorIdToNumber.XXX;// oops +module.exports = { ColorId, ColorNumber }; + +" +`; + +exports[`test test2.js 1`] = ` +"var { ColorId, ColorNumber } = require('./test'); +var ColorIdToNumber = { + [ColorId.RED]: ColorNumber.RED, + [ColorId.GREEN]: ColorNumber.GREEN, + [ColorId.BLUE]: ColorNumber.BLUE, +}; + +(ColorIdToNumber[ColorId.GREEN]: 'ffffff'); // oops + +module.exports = ColorIdToNumber; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var { ColorId, ColorNumber } = require("./test"); +var ColorIdToNumber = { + [ColorId.RED]: ColorNumber.RED, + [ColorId.GREEN]: ColorNumber.GREEN, + [ColorId.BLUE]: ColorNumber.BLUE +}; +(ColorIdToNumber[ColorId.GREEN]: "ffffff");// oops +module.exports = ColorIdToNumber; + +" +`; + +exports[`test test3.js 1`] = ` +"var { ColorId } = require('./test'); +var ColorIdToNumber = require('./test2'); + +(ColorIdToNumber[ColorId.BLUE]: 'ffffff'); // oops +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var { ColorId } = require("./test"); +var ColorIdToNumber = require("./test2"); +(ColorIdToNumber[ColorId.BLUE]: "ffffff");// oops + +" +`; + +exports[`test test4.js 1`] = ` +"module.exports = 'hello'; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +module.exports = "hello"; + +" +`; + +exports[`test test5.js 1`] = ` +"var hello = require('./test4'); +var dummy = require('./test'); +module.exports = { + ...dummy, + [hello]: 'world', + ...dummy, +}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var hello = require("./test4"); +var dummy = require("./test"); +module.exports = { ...dummy, [hello]: "world", ...dummy }; + +" +`; + +exports[`test test6.js 1`] = ` +"var o = require('./test5'); +(o.hello: 'nothing'); // oops +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var o = require("./test5"); +(o.hello: "nothing");// oops + +" +`; + +exports[`test test7.js 1`] = ` +"var obj = {x: 0, m() { return this.x }} +var x: string = obj['m'](); // error, number ~> string + +var arr = [function() { return this.length }]; +var y: string = arr[0](); // error: number ~> string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var obj = { + x: 0, + m() { + return this.x; + } +}; +var x: string = obj["m"]();// error, number ~> string +var arr = [ + function() { + return this.length; + } +]; +var y: string = arr[0]();// error: number ~> string + +" +`; diff --git a/tests/computed_props/jsfmt.spec.js b/tests/computed_props/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/computed_props/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/computed_props/test.js b/tests/computed_props/test.js new file mode 100644 index 000000000000..4c65f895474e --- /dev/null +++ b/tests/computed_props/test.js @@ -0,0 +1,23 @@ +var ColorId = { + RED: 'R', + GREEN: 'G', + BLUE: 'B', +}; + +var ColorNumber = { + RED: 'ff0000', + GREEN: '00ff00', + BLUE: '0000ff', +}; + +var ColorIdToNumber = { + [ColorId.RED]: ColorNumber.RED, + [ColorId.GREEN]: ColorNumber.GREEN, + [ColorId.BLUE]: ColorNumber.BLUE, +}; + +(ColorIdToNumber[ColorId.RED]: 'ffffff'); // oops + +ColorIdToNumber.XXX; // oops + +module.exports = { ColorId, ColorNumber }; diff --git a/tests/computed_props/test2.js b/tests/computed_props/test2.js new file mode 100644 index 000000000000..02f05991d5ed --- /dev/null +++ b/tests/computed_props/test2.js @@ -0,0 +1,10 @@ +var { ColorId, ColorNumber } = require('./test'); +var ColorIdToNumber = { + [ColorId.RED]: ColorNumber.RED, + [ColorId.GREEN]: ColorNumber.GREEN, + [ColorId.BLUE]: ColorNumber.BLUE, +}; + +(ColorIdToNumber[ColorId.GREEN]: 'ffffff'); // oops + +module.exports = ColorIdToNumber; diff --git a/tests/computed_props/test3.js b/tests/computed_props/test3.js new file mode 100644 index 000000000000..90bd0ad2a413 --- /dev/null +++ b/tests/computed_props/test3.js @@ -0,0 +1,4 @@ +var { ColorId } = require('./test'); +var ColorIdToNumber = require('./test2'); + +(ColorIdToNumber[ColorId.BLUE]: 'ffffff'); // oops diff --git a/tests/computed_props/test4.js b/tests/computed_props/test4.js new file mode 100644 index 000000000000..76e8a568d58d --- /dev/null +++ b/tests/computed_props/test4.js @@ -0,0 +1 @@ +module.exports = 'hello'; diff --git a/tests/computed_props/test5.js b/tests/computed_props/test5.js new file mode 100644 index 000000000000..4979c54c80bd --- /dev/null +++ b/tests/computed_props/test5.js @@ -0,0 +1,7 @@ +var hello = require('./test4'); +var dummy = require('./test'); +module.exports = { + ...dummy, + [hello]: 'world', + ...dummy, +}; diff --git a/tests/computed_props/test6.js b/tests/computed_props/test6.js new file mode 100644 index 000000000000..89ac05f48a7f --- /dev/null +++ b/tests/computed_props/test6.js @@ -0,0 +1,2 @@ +var o = require('./test5'); +(o.hello: 'nothing'); // oops diff --git a/tests/computed_props/test7.js b/tests/computed_props/test7.js new file mode 100644 index 000000000000..003700429d8d --- /dev/null +++ b/tests/computed_props/test7.js @@ -0,0 +1,5 @@ +var obj = {x: 0, m() { return this.x }} +var x: string = obj['m'](); // error, number ~> string + +var arr = [function() { return this.length }]; +var y: string = arr[0](); // error: number ~> string diff --git a/tests/conditional/__snapshots__/jsfmt.spec.js.snap b/tests/conditional/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..a1d029d6c9ed --- /dev/null +++ b/tests/conditional/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,50 @@ +exports[`test conditional.js 1`] = ` +"/* @flow */ + +function a(): number { + var x: ?string = null; + return x ? 1 : 0; +} + +function b(): number { + var x: ?number = null; + return x != null ? x : 0; +} + +function c(): number { + // equivalent to \`return (x && 1) || 0\` + var x = false; + var temp = (x ? 1 : x); + return temp ? temp : 0; +} + +function d(): string { // expected \`: number | boolean\` + // equivalent to \`return x != null && x\` + var x: ?number = null; + return (x != null) ? x : (x != null); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +function a(): number { + var x: ?string = null; + return (x ? 1 : 0); +} +function b(): number { + var x: ?number = null; + return (x != null ? x : 0); +} +function c(): number { + // equivalent to \`return (x && 1) || 0\` + var x = false; + var temp = (x ? 1 : x); + return (temp ? temp : 0); +} +function d(): string { + // expected \`: number | boolean\` + // equivalent to \`return x != null && x\` + var x: ?number = null; + return (x != null ? x : x != null); +} + +" +`; diff --git a/tests/conditional/conditional.js b/tests/conditional/conditional.js new file mode 100644 index 000000000000..b66892cfd1a2 --- /dev/null +++ b/tests/conditional/conditional.js @@ -0,0 +1,24 @@ +/* @flow */ + +function a(): number { + var x: ?string = null; + return x ? 1 : 0; +} + +function b(): number { + var x: ?number = null; + return x != null ? x : 0; +} + +function c(): number { + // equivalent to `return (x && 1) || 0` + var x = false; + var temp = (x ? 1 : x); + return temp ? temp : 0; +} + +function d(): string { // expected `: number | boolean` + // equivalent to `return x != null && x` + var x: ?number = null; + return (x != null) ? x : (x != null); +} diff --git a/tests/conditional/jsfmt.spec.js b/tests/conditional/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/conditional/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/config_all/__snapshots__/jsfmt.spec.js.snap b/tests/config_all/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..abf7e57cd722 --- /dev/null +++ b/tests/config_all/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,7 @@ +exports[`test no_at_flow.js 1`] = ` +"var x: number = "not a number"; // Error: string ~> number +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var x: number = "not a number";// Error: string ~> number + +" +`; diff --git a/tests/config_all/jsfmt.spec.js b/tests/config_all/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/config_all/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/config_all/no_at_flow.js b/tests/config_all/no_at_flow.js new file mode 100644 index 000000000000..3d01d67f0e7e --- /dev/null +++ b/tests/config_all/no_at_flow.js @@ -0,0 +1 @@ +var x: number = "not a number"; // Error: string ~> number diff --git a/tests/config_all_false/__snapshots__/jsfmt.spec.js.snap b/tests/config_all_false/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..dc6f37acc919 --- /dev/null +++ b/tests/config_all_false/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,7 @@ +exports[`test no_at_flow.js 1`] = ` +"var x: number = "not a number"; // No error +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var x: number = "not a number";// No error + +" +`; diff --git a/tests/config_all_false/jsfmt.spec.js b/tests/config_all_false/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/config_all_false/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/config_all_false/no_at_flow.js b/tests/config_all_false/no_at_flow.js new file mode 100644 index 000000000000..656a221a89c6 --- /dev/null +++ b/tests/config_all_false/no_at_flow.js @@ -0,0 +1 @@ +var x: number = "not a number"; // No error diff --git a/tests/config_all_weak/__snapshots__/jsfmt.spec.js.snap b/tests/config_all_weak/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..a9e1e0a2a26c --- /dev/null +++ b/tests/config_all_weak/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,10 @@ +exports[`test no_at_flow.js 1`] = ` +"var x; + +x.length; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var x; +x.length; + +" +`; diff --git a/tests/config_all_weak/jsfmt.spec.js b/tests/config_all_weak/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/config_all_weak/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/config_all_weak/no_at_flow.js b/tests/config_all_weak/no_at_flow.js new file mode 100644 index 000000000000..eacadf62df20 --- /dev/null +++ b/tests/config_all_weak/no_at_flow.js @@ -0,0 +1,3 @@ +var x; + +x.length; diff --git a/tests/config_file_extensions/__snapshots__/jsfmt.spec.js.snap b/tests/config_file_extensions/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..dfca8fc58e4b --- /dev/null +++ b/tests/config_file_extensions/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,25 @@ +exports[`test test.js 1`] = ` +"/* + * @flow + */ + +function foo(x) { + var a: number = 'asdf'; + return x * 10; +} + +// This file should be ignored, so this should not result in an error +foo('Hello, world!'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* + * @flow + */ +function foo(x) { + var a: number = "asdf"; + return x * 10; +} +// This file should be ignored, so this should not result in an error +foo("Hello, world!"); + +" +`; diff --git a/tests/config_file_extensions/jsfmt.spec.js b/tests/config_file_extensions/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/config_file_extensions/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/config_file_extensions/test.js b/tests/config_file_extensions/test.js new file mode 100644 index 000000000000..ec34cbda765b --- /dev/null +++ b/tests/config_file_extensions/test.js @@ -0,0 +1,11 @@ +/* + * @flow + */ + +function foo(x) { + var a: number = 'asdf'; + return x * 10; +} + +// This file should be ignored, so this should not result in an error +foo('Hello, world!'); diff --git a/tests/config_ignore/__snapshots__/jsfmt.spec.js.snap b/tests/config_ignore/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..e2b382361017 --- /dev/null +++ b/tests/config_ignore/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,12 @@ +exports[`test foo.js 1`] = ` +"/* @flow */ + +// No error, this file is ignored +var x: number = "string"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +// No error, this file is ignored +var x: number = "string"; + +" +`; diff --git a/tests/config_ignore/dir/__snapshots__/jsfmt.spec.js.snap b/tests/config_ignore/dir/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..d18373a3898c --- /dev/null +++ b/tests/config_ignore/dir/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,10 @@ +exports[`test foo.js 1`] = ` +"/* @flow */ + +var x: number = "string"; // Error string ~> number +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var x: number = "string";// Error string ~> number + +" +`; diff --git a/tests/config_ignore/dir/foo.js b/tests/config_ignore/dir/foo.js new file mode 100644 index 000000000000..b9901bd5b99b --- /dev/null +++ b/tests/config_ignore/dir/foo.js @@ -0,0 +1,3 @@ +/* @flow */ + +var x: number = "string"; // Error string ~> number diff --git a/tests/config_ignore/dir/jsfmt.spec.js b/tests/config_ignore/dir/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/config_ignore/dir/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/config_ignore/foo.js b/tests/config_ignore/foo.js new file mode 100644 index 000000000000..1b0029abb16f --- /dev/null +++ b/tests/config_ignore/foo.js @@ -0,0 +1,4 @@ +/* @flow */ + +// No error, this file is ignored +var x: number = "string"; diff --git a/tests/config_ignore/jsfmt.spec.js b/tests/config_ignore/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/config_ignore/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/config_module_name_mapper_PROJECT_ROOT-1.0/__snapshots__/jsfmt.spec.js.snap b/tests/config_module_name_mapper_PROJECT_ROOT-1.0/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..049efd334d2b --- /dev/null +++ b/tests/config_module_name_mapper_PROJECT_ROOT-1.0/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,15 @@ +exports[`test main.js 1`] = ` +"// @flow + +import {test} from 'testmodule'; + +var a: number = test; +var b: string = test; // Error: number ~> string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +import { test } from "testmodule"; +var a: number = test; +var b: string = test;// Error: number ~> string + +" +`; diff --git a/tests/config_module_name_mapper_PROJECT_ROOT-1.0/jsfmt.spec.js b/tests/config_module_name_mapper_PROJECT_ROOT-1.0/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/config_module_name_mapper_PROJECT_ROOT-1.0/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/config_module_name_mapper_PROJECT_ROOT-1.0/main.js b/tests/config_module_name_mapper_PROJECT_ROOT-1.0/main.js new file mode 100644 index 000000000000..8168ba9c3ce0 --- /dev/null +++ b/tests/config_module_name_mapper_PROJECT_ROOT-1.0/main.js @@ -0,0 +1,6 @@ +// @flow + +import {test} from 'testmodule'; + +var a: number = test; +var b: string = test; // Error: number ~> string diff --git a/tests/config_module_name_mapper_PROJECT_ROOT-1.0/src/__snapshots__/jsfmt.spec.js.snap b/tests/config_module_name_mapper_PROJECT_ROOT-1.0/src/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..8f51a9c7ac9d --- /dev/null +++ b/tests/config_module_name_mapper_PROJECT_ROOT-1.0/src/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,10 @@ +exports[`test testmodule.js 1`] = ` +"// @flow + +export let test = 42; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +export let test = 42; + +" +`; diff --git a/tests/config_module_name_mapper_PROJECT_ROOT-1.0/src/jsfmt.spec.js b/tests/config_module_name_mapper_PROJECT_ROOT-1.0/src/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/config_module_name_mapper_PROJECT_ROOT-1.0/src/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/config_module_name_mapper_PROJECT_ROOT-1.0/src/testmodule.js b/tests/config_module_name_mapper_PROJECT_ROOT-1.0/src/testmodule.js new file mode 100644 index 000000000000..1e0fc93cca56 --- /dev/null +++ b/tests/config_module_name_mapper_PROJECT_ROOT-1.0/src/testmodule.js @@ -0,0 +1,3 @@ +// @flow + +export let test = 42; diff --git a/tests/config_module_name_mapper_filetype/__snapshots__/jsfmt.spec.js.snap b/tests/config_module_name_mapper_filetype/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..2ad57396e31f --- /dev/null +++ b/tests/config_module_name_mapper_filetype/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,12 @@ +exports[`test test.js 1`] = ` +"// @flow + +import {className} from "./SomeCSSFile.css"; +import {doesntExist} from "./SomeCSSFile.css"; // Error: \`doestExist\` isn't an export +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +import { className } from "./SomeCSSFile.css"; +import { doesntExist } from "./SomeCSSFile.css";// Error: \`doestExist\` isn't an export + +" +`; diff --git a/tests/config_module_name_mapper_filetype/jsfmt.spec.js b/tests/config_module_name_mapper_filetype/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/config_module_name_mapper_filetype/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/config_module_name_mapper_filetype/test.js b/tests/config_module_name_mapper_filetype/test.js new file mode 100644 index 000000000000..8b209dd801c6 --- /dev/null +++ b/tests/config_module_name_mapper_filetype/test.js @@ -0,0 +1,4 @@ +// @flow + +import {className} from "./SomeCSSFile.css"; +import {doesntExist} from "./SomeCSSFile.css"; // Error: `doestExist` isn't an export diff --git a/tests/config_module_name_rewrite_haste/A.js b/tests/config_module_name_rewrite_haste/A.js new file mode 100644 index 000000000000..ca5185b4bcf6 --- /dev/null +++ b/tests/config_module_name_rewrite_haste/A.js @@ -0,0 +1,13 @@ +/* @flow */ + +var m1 = require('1DoesntExist'); +import {numVal as numVal1} from '1DoesntExist'; +var a_1: number = m1.numVal; +var a_2: number = numVal1; + +// Error: 'Exists2' is not a valid module name +// +// This tests that, for haste, the first name_mapper regexp that happens to +// match the given module name string is picked. +var m2 = require('2DoesntExist'); // Error +import {numVal as numVal2} from '3DoesntExist'; // Error diff --git a/tests/config_module_name_rewrite_haste/Exists.js b/tests/config_module_name_rewrite_haste/Exists.js new file mode 100644 index 000000000000..b91779d3ebe0 --- /dev/null +++ b/tests/config_module_name_rewrite_haste/Exists.js @@ -0,0 +1,8 @@ +/** + * @providesModule Exists + * @flow + */ + +module.exports = { + numVal: 42 +}; diff --git a/tests/config_module_name_rewrite_haste/__snapshots__/jsfmt.spec.js.snap b/tests/config_module_name_rewrite_haste/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..6ccef0e73a9b --- /dev/null +++ b/tests/config_module_name_rewrite_haste/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,48 @@ +exports[`test A.js 1`] = ` +"/* @flow */ + +var m1 = require('1DoesntExist'); +import {numVal as numVal1} from '1DoesntExist'; +var a_1: number = m1.numVal; +var a_2: number = numVal1; + +// Error: 'Exists2' is not a valid module name +// +// This tests that, for haste, the first name_mapper regexp that happens to +// match the given module name string is picked. +var m2 = require('2DoesntExist'); // Error +import {numVal as numVal2} from '3DoesntExist'; // Error +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var m1 = require("1DoesntExist"); +import { numVal as numVal1 } from "1DoesntExist"; +var a_1: number = m1.numVal; +var a_2: number = numVal1; +// Error: 'Exists2' is not a valid module name +// +// This tests that, for haste, the first name_mapper regexp that happens to +// match the given module name string is picked. +var m2 = require("2DoesntExist");// Error +import { numVal as numVal2 } from "3DoesntExist";// Error + +" +`; + +exports[`test Exists.js 1`] = ` +"/** + * @providesModule Exists + * @flow + */ + +module.exports = { + numVal: 42 +}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule Exists + * @flow + */ +module.exports = { numVal: 42 }; + +" +`; diff --git a/tests/config_module_name_rewrite_haste/jsfmt.spec.js b/tests/config_module_name_rewrite_haste/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/config_module_name_rewrite_haste/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/config_module_name_rewrite_node/A.js b/tests/config_module_name_rewrite_node/A.js new file mode 100644 index 000000000000..5f42adbf50d1 --- /dev/null +++ b/tests/config_module_name_rewrite_node/A.js @@ -0,0 +1,25 @@ +/* @flow */ + +var m1 = require('./1DoesntExist'); +var a_1: number = m1.numVal; +var a_2: string = m1.numVal; // Error: number ~> string +import {numVal} from './1DoesntExist'; +var a_3: number = numVal; +var a_4: string = numVal; // Error: number ~> string + +// This tests that, for node, the first name mapping that both matches *and* +// results in a valid module filename is picked. +var m2 = require('./2DoesntExist'); +var b_1: number = m2.numVal; +var b_2: string = m2.numVal; // Error: number ~> string +import {numVal as numVal2} from './3DoesntExist'; +var b_3: number = numVal2; +var b_4: string = numVal2; // Error: number ~> string + +// node_modules/Exists/index.js +var m3 = require('4DoesntExist'); +var c_1: number = m3.numVal; +var c_2: string = m3.numVal; // Error: number ~> string +import {numVal as numVal3} from '5DoesntExist'; +var c_3: number = numVal3; +var c_4: string = numVal3; // Error: number ~> string diff --git a/tests/config_module_name_rewrite_node/Exists.js b/tests/config_module_name_rewrite_node/Exists.js new file mode 100644 index 000000000000..1b63e9c8bbf7 --- /dev/null +++ b/tests/config_module_name_rewrite_node/Exists.js @@ -0,0 +1,5 @@ +/* @flow */ + +module.exports = { + numVal: 42 +}; diff --git a/tests/config_module_name_rewrite_node/__snapshots__/jsfmt.spec.js.snap b/tests/config_module_name_rewrite_node/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..e8252c84db6e --- /dev/null +++ b/tests/config_module_name_rewrite_node/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,65 @@ +exports[`test A.js 1`] = ` +"/* @flow */ + +var m1 = require('./1DoesntExist'); +var a_1: number = m1.numVal; +var a_2: string = m1.numVal; // Error: number ~> string +import {numVal} from './1DoesntExist'; +var a_3: number = numVal; +var a_4: string = numVal; // Error: number ~> string + +// This tests that, for node, the first name mapping that both matches *and* +// results in a valid module filename is picked. +var m2 = require('./2DoesntExist'); +var b_1: number = m2.numVal; +var b_2: string = m2.numVal; // Error: number ~> string +import {numVal as numVal2} from './3DoesntExist'; +var b_3: number = numVal2; +var b_4: string = numVal2; // Error: number ~> string + +// node_modules/Exists/index.js +var m3 = require('4DoesntExist'); +var c_1: number = m3.numVal; +var c_2: string = m3.numVal; // Error: number ~> string +import {numVal as numVal3} from '5DoesntExist'; +var c_3: number = numVal3; +var c_4: string = numVal3; // Error: number ~> string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var m1 = require("./1DoesntExist"); +var a_1: number = m1.numVal; +var a_2: string = m1.numVal;// Error: number ~> string +import { numVal } from "./1DoesntExist"; +var a_3: number = numVal; +var a_4: string = numVal;// Error: number ~> string +// This tests that, for node, the first name mapping that both matches *and* +// results in a valid module filename is picked. +var m2 = require("./2DoesntExist"); +var b_1: number = m2.numVal; +var b_2: string = m2.numVal;// Error: number ~> string +import { numVal as numVal2 } from "./3DoesntExist"; +var b_3: number = numVal2; +var b_4: string = numVal2;// Error: number ~> string +// node_modules/Exists/index.js +var m3 = require("4DoesntExist"); +var c_1: number = m3.numVal; +var c_2: string = m3.numVal;// Error: number ~> string +import { numVal as numVal3 } from "5DoesntExist"; +var c_3: number = numVal3; +var c_4: string = numVal3;// Error: number ~> string + +" +`; + +exports[`test Exists.js 1`] = ` +"/* @flow */ + +module.exports = { + numVal: 42 +}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +module.exports = { numVal: 42 }; + +" +`; diff --git a/tests/config_module_name_rewrite_node/jsfmt.spec.js b/tests/config_module_name_rewrite_node/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/config_module_name_rewrite_node/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/config_module_system_node_resolve_dirname/__snapshots__/jsfmt.spec.js.snap b/tests/config_module_system_node_resolve_dirname/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..f6a5d798672e --- /dev/null +++ b/tests/config_module_system_node_resolve_dirname/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,15 @@ +exports[`test toplevel.js 1`] = ` +"// @flow + +import {name} from "testproj"; + +(name: "node_modules/testproj"); +(name: "custom_resolve_dir/testproj"); // Error: Resolve from node_modules first! +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +import { name } from "testproj"; +(name: "node_modules/testproj"); +(name: "custom_resolve_dir/testproj");// Error: Resolve from node_modules first! + +" +`; diff --git a/tests/config_module_system_node_resolve_dirname/custom_resolve_dir/testproj/__snapshots__/jsfmt.spec.js.snap b/tests/config_module_system_node_resolve_dirname/custom_resolve_dir/testproj/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..075fd4fa9f24 --- /dev/null +++ b/tests/config_module_system_node_resolve_dirname/custom_resolve_dir/testproj/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,10 @@ +exports[`test index.js 1`] = ` +"// @flow + +export var name: "otherdir/testproj" = "otherdir/testproj"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +export var name: "otherdir/testproj" = "otherdir/testproj"; + +" +`; diff --git a/tests/config_module_system_node_resolve_dirname/custom_resolve_dir/testproj/index.js b/tests/config_module_system_node_resolve_dirname/custom_resolve_dir/testproj/index.js new file mode 100644 index 000000000000..25037710b852 --- /dev/null +++ b/tests/config_module_system_node_resolve_dirname/custom_resolve_dir/testproj/index.js @@ -0,0 +1,3 @@ +// @flow + +export var name: "otherdir/testproj" = "otherdir/testproj"; diff --git a/tests/config_module_system_node_resolve_dirname/custom_resolve_dir/testproj/jsfmt.spec.js b/tests/config_module_system_node_resolve_dirname/custom_resolve_dir/testproj/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/config_module_system_node_resolve_dirname/custom_resolve_dir/testproj/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/config_module_system_node_resolve_dirname/custom_resolve_dir/testproj2/__snapshots__/jsfmt.spec.js.snap b/tests/config_module_system_node_resolve_dirname/custom_resolve_dir/testproj2/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..23b55094cf43 --- /dev/null +++ b/tests/config_module_system_node_resolve_dirname/custom_resolve_dir/testproj2/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,10 @@ +exports[`test index.js 1`] = ` +"// @flow + +export var name: "otherdir/testproj2" = "otherdir/testproj2"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +export var name: "otherdir/testproj2" = "otherdir/testproj2"; + +" +`; diff --git a/tests/config_module_system_node_resolve_dirname/custom_resolve_dir/testproj2/index.js b/tests/config_module_system_node_resolve_dirname/custom_resolve_dir/testproj2/index.js new file mode 100644 index 000000000000..ddd1f996f728 --- /dev/null +++ b/tests/config_module_system_node_resolve_dirname/custom_resolve_dir/testproj2/index.js @@ -0,0 +1,3 @@ +// @flow + +export var name: "otherdir/testproj2" = "otherdir/testproj2"; diff --git a/tests/config_module_system_node_resolve_dirname/custom_resolve_dir/testproj2/jsfmt.spec.js b/tests/config_module_system_node_resolve_dirname/custom_resolve_dir/testproj2/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/config_module_system_node_resolve_dirname/custom_resolve_dir/testproj2/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/config_module_system_node_resolve_dirname/jsfmt.spec.js b/tests/config_module_system_node_resolve_dirname/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/config_module_system_node_resolve_dirname/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/config_module_system_node_resolve_dirname/subdir/__snapshots__/jsfmt.spec.js.snap b/tests/config_module_system_node_resolve_dirname/subdir/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..a3e521c32436 --- /dev/null +++ b/tests/config_module_system_node_resolve_dirname/subdir/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,15 @@ +exports[`test sublevel.js 1`] = ` +"// @flow + +import {name} from "testproj2"; + +(name: "node_modules/testproj2"); // Error: Resolve from sibling 'custom_resolve_dir' first! +(name: "subdir/custom_resolve_dir/testproj2"); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +import { name } from "testproj2"; +(name: "node_modules/testproj2");// Error: Resolve from sibling 'custom_resolve_dir' first! +(name: "subdir/custom_resolve_dir/testproj2"); + +" +`; diff --git a/tests/config_module_system_node_resolve_dirname/subdir/custom_resolve_dir/testproj2/__snapshots__/jsfmt.spec.js.snap b/tests/config_module_system_node_resolve_dirname/subdir/custom_resolve_dir/testproj2/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..bec06490d473 --- /dev/null +++ b/tests/config_module_system_node_resolve_dirname/subdir/custom_resolve_dir/testproj2/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,10 @@ +exports[`test index.js 1`] = ` +"// @flow + +export var name: "subdir/custom_resolve_dir/testproj2" = "subdir/custom_resolve_dir/testproj2"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +export var name: "subdir/custom_resolve_dir/testproj2" = "subdir/custom_resolve_dir/testproj2"; + +" +`; diff --git a/tests/config_module_system_node_resolve_dirname/subdir/custom_resolve_dir/testproj2/index.js b/tests/config_module_system_node_resolve_dirname/subdir/custom_resolve_dir/testproj2/index.js new file mode 100644 index 000000000000..af4294a36c2a --- /dev/null +++ b/tests/config_module_system_node_resolve_dirname/subdir/custom_resolve_dir/testproj2/index.js @@ -0,0 +1,3 @@ +// @flow + +export var name: "subdir/custom_resolve_dir/testproj2" = "subdir/custom_resolve_dir/testproj2"; diff --git a/tests/config_module_system_node_resolve_dirname/subdir/custom_resolve_dir/testproj2/jsfmt.spec.js b/tests/config_module_system_node_resolve_dirname/subdir/custom_resolve_dir/testproj2/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/config_module_system_node_resolve_dirname/subdir/custom_resolve_dir/testproj2/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/config_module_system_node_resolve_dirname/subdir/jsfmt.spec.js b/tests/config_module_system_node_resolve_dirname/subdir/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/config_module_system_node_resolve_dirname/subdir/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/config_module_system_node_resolve_dirname/subdir/sublevel.js b/tests/config_module_system_node_resolve_dirname/subdir/sublevel.js new file mode 100644 index 000000000000..e080e5378dcd --- /dev/null +++ b/tests/config_module_system_node_resolve_dirname/subdir/sublevel.js @@ -0,0 +1,6 @@ +// @flow + +import {name} from "testproj2"; + +(name: "node_modules/testproj2"); // Error: Resolve from sibling 'custom_resolve_dir' first! +(name: "subdir/custom_resolve_dir/testproj2"); diff --git a/tests/config_module_system_node_resolve_dirname/toplevel.js b/tests/config_module_system_node_resolve_dirname/toplevel.js new file mode 100644 index 000000000000..5207c12bbba2 --- /dev/null +++ b/tests/config_module_system_node_resolve_dirname/toplevel.js @@ -0,0 +1,6 @@ +// @flow + +import {name} from "testproj"; + +(name: "node_modules/testproj"); +(name: "custom_resolve_dir/testproj"); // Error: Resolve from node_modules first! diff --git a/tests/config_munging_underscores/__snapshots__/jsfmt.spec.js.snap b/tests/config_munging_underscores/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..9f42da9c160a --- /dev/null +++ b/tests/config_munging_underscores/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,98 @@ +exports[`test chain.js 1`] = ` +"/* @flow */ + +class A { + _property1: number; + static _sProperty: number; + + constructor() { + this._property1 = 5; + } + _method1(): number { + return 1; + } + static _sMethod(): string { + return "some string"; + } +} +A._sProperty = 48; + +class B extends A { + _property1: string; + static _sProperty: string; + + constructor() { + super(); + this._property1 = "another string"; + } + _method1(): string { + return "yet another string"; + } + static _sMethod(): number { + return 23; + } +} +B._sProperty = "B._sProperty string"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +class A { + _property1: number; + static _sProperty: number; + constructor() { + this._property1 = 5; + } + _method1(): number { + return 1; + } + _sMethod(): string { + return "some string"; + } +} +A._sProperty = 48; +class B extends A { + _property1: string; + static _sProperty: string; + constructor() { + super(); + this._property1 = "another string"; + } + _method1(): string { + return "yet another string"; + } + _sMethod(): number { + return 23; + } +} +B._sProperty = "B._sProperty string"; + +" +`; + +exports[`test commonjs_export.js 1`] = ` +"/* @flow */ + +class C { + _p: string; +} + +module.exports = new C; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +class C { + _p: string; +} +module.exports = new C(); + +" +`; + +exports[`test commonjs_import.js 1`] = ` +"/* @flow */ + +import {_p} from "./commonjs_export"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +import { _p } from "./commonjs_export"; + +" +`; diff --git a/tests/config_munging_underscores/chain.js b/tests/config_munging_underscores/chain.js new file mode 100644 index 000000000000..6eb45e480216 --- /dev/null +++ b/tests/config_munging_underscores/chain.js @@ -0,0 +1,34 @@ +/* @flow */ + +class A { + _property1: number; + static _sProperty: number; + + constructor() { + this._property1 = 5; + } + _method1(): number { + return 1; + } + static _sMethod(): string { + return "some string"; + } +} +A._sProperty = 48; + +class B extends A { + _property1: string; + static _sProperty: string; + + constructor() { + super(); + this._property1 = "another string"; + } + _method1(): string { + return "yet another string"; + } + static _sMethod(): number { + return 23; + } +} +B._sProperty = "B._sProperty string"; diff --git a/tests/config_munging_underscores/commonjs_export.js b/tests/config_munging_underscores/commonjs_export.js new file mode 100644 index 000000000000..bbecd1ae4574 --- /dev/null +++ b/tests/config_munging_underscores/commonjs_export.js @@ -0,0 +1,7 @@ +/* @flow */ + +class C { + _p: string; +} + +module.exports = new C; diff --git a/tests/config_munging_underscores/commonjs_import.js b/tests/config_munging_underscores/commonjs_import.js new file mode 100644 index 000000000000..f63d24a7f7ad --- /dev/null +++ b/tests/config_munging_underscores/commonjs_import.js @@ -0,0 +1,3 @@ +/* @flow */ + +import {_p} from "./commonjs_export"; diff --git a/tests/config_munging_underscores/jsfmt.spec.js b/tests/config_munging_underscores/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/config_munging_underscores/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/config_munging_underscores2/__snapshots__/jsfmt.spec.js.snap b/tests/config_munging_underscores2/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..89fc8a4c07b3 --- /dev/null +++ b/tests/config_munging_underscores2/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,69 @@ +exports[`test chain.js 1`] = ` +"/* @flow */ + +class A { + _property1: number; + static _sProperty: number; + + constructor() { + this._property1 = 5; + } + _method1(): number { + return 1; + } + static _sMethod(): string { + return "some string"; + } +} +A._sProperty = 48; + +class B extends A { + _property1: string; + static _sProperty: string; + + constructor() { + super(); + this._property1 = "another string"; + } + _method1(): string { + return "yet another string"; + } + static _sMethod(): number { + return 23; + } +} +B._sProperty = "B._sProperty string"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +class A { + _property1: number; + static _sProperty: number; + constructor() { + this._property1 = 5; + } + _method1(): number { + return 1; + } + _sMethod(): string { + return "some string"; + } +} +A._sProperty = 48; +class B extends A { + _property1: string; + static _sProperty: string; + constructor() { + super(); + this._property1 = "another string"; + } + _method1(): string { + return "yet another string"; + } + _sMethod(): number { + return 23; + } +} +B._sProperty = "B._sProperty string"; + +" +`; diff --git a/tests/config_munging_underscores2/chain.js b/tests/config_munging_underscores2/chain.js new file mode 100644 index 000000000000..6eb45e480216 --- /dev/null +++ b/tests/config_munging_underscores2/chain.js @@ -0,0 +1,34 @@ +/* @flow */ + +class A { + _property1: number; + static _sProperty: number; + + constructor() { + this._property1 = 5; + } + _method1(): number { + return 1; + } + static _sMethod(): string { + return "some string"; + } +} +A._sProperty = 48; + +class B extends A { + _property1: string; + static _sProperty: string; + + constructor() { + super(); + this._property1 = "another string"; + } + _method1(): string { + return "yet another string"; + } + static _sMethod(): number { + return 23; + } +} +B._sProperty = "B._sProperty string"; diff --git a/tests/config_munging_underscores2/jsfmt.spec.js b/tests/config_munging_underscores2/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/config_munging_underscores2/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/const_params/__snapshots__/jsfmt.spec.js.snap b/tests/const_params/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..028d8606c6c4 --- /dev/null +++ b/tests/const_params/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,63 @@ +exports[`test test.js 1`] = ` +"/** + * test handling of const params + * - reassignment prohibited + * - durable refinements + * + * Currently gated in .flowconfig: + * + * [options] + * experimental.const_params + * + * Syntax to follow + * + * @flow + */ + +function cannot_reassign(x: string) { + x = "hey"; // error, const param cannot be reassigned +} + +// Note: const params use the same machinery as explicit +// const bindings, which are tested more extensively elsewhere. +// Here we're just making sure the machinery is hooked up. +// +function durable_refi(x: ?number) { + if (x) { + // ok: if x is truthy here, it's truthy everywhere + return () => { var y:number = x; }; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * test handling of const params + * - reassignment prohibited + * - durable refinements + * + * Currently gated in .flowconfig: + * + * [options] + * experimental.const_params + * + * Syntax to follow + * + * @flow + */ +function cannot_reassign(x: string) { + x = "hey";// error, const param cannot be reassigned +} +// Note: const params use the same machinery as explicit +// const bindings, which are tested more extensively elsewhere. +// Here we're just making sure the machinery is hooked up. +// +function durable_refi(x: ?number) { + if (x) { + // ok: if x is truthy here, it's truthy everywhere + return () => { + var y: number = x; + }; + } +} + +" +`; diff --git a/tests/const_params/jsfmt.spec.js b/tests/const_params/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/const_params/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/const_params/test.js b/tests/const_params/test.js new file mode 100644 index 000000000000..0f7838c3c1fa --- /dev/null +++ b/tests/const_params/test.js @@ -0,0 +1,29 @@ +/** + * test handling of const params + * - reassignment prohibited + * - durable refinements + * + * Currently gated in .flowconfig: + * + * [options] + * experimental.const_params + * + * Syntax to follow + * + * @flow + */ + +function cannot_reassign(x: string) { + x = "hey"; // error, const param cannot be reassigned +} + +// Note: const params use the same machinery as explicit +// const bindings, which are tested more extensively elsewhere. +// Here we're just making sure the machinery is hooked up. +// +function durable_refi(x: ?number) { + if (x) { + // ok: if x is truthy here, it's truthy everywhere + return () => { var y:number = x; }; + } +} diff --git a/tests/constructor/__snapshots__/jsfmt.spec.js.snap b/tests/constructor/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..038fdeca83c6 --- /dev/null +++ b/tests/constructor/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,25 @@ +exports[`test constructor.js 1`] = ` +"class C { + constructor() { } +} + +class D { + constructor():number { } +} + +module.exports = C; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class C { + constructor() { + + } +} +class D { + constructor(): number { + + } +} +module.exports = C; + +" +`; diff --git a/tests/constructor/constructor.js b/tests/constructor/constructor.js new file mode 100644 index 000000000000..92038b6e4e2d --- /dev/null +++ b/tests/constructor/constructor.js @@ -0,0 +1,9 @@ +class C { + constructor() { } +} + +class D { + constructor():number { } +} + +module.exports = C; diff --git a/tests/constructor/jsfmt.spec.js b/tests/constructor/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/constructor/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/constructor_annots/__snapshots__/jsfmt.spec.js.snap b/tests/constructor_annots/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..b50f3d576333 --- /dev/null +++ b/tests/constructor_annots/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,63 @@ +exports[`test constructors.js 1`] = ` +"// Foo is a class-like function +function Foo() { + this.x = 0; // constructs objects with property x +} +Foo.y = 0; // has static property y +Foo.prototype = { m() { return 0; } }; + +// exporting Foo directly doesn\'t work +// Foo\'s instance and static props are not picked up +exports.Foo = Foo; + +// so you want to type Foo, by declaring it as a class +interface IFooPrototype { + m: () => number; +} +interface IFoo extends IFooPrototype { + x: boolean; // error, should have declared x: number instead + static (): void; + static y: boolean; // error, should have declared static y: number instead +} +exports.Foo2 = (Foo: Class); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected : (19:9) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.expect (/node_modules/babylon/lib/index.js:1621:33) + at Parser.pp$7.flowParseTypeInitialiser (/node_modules/babylon/lib/index.js:4424:8) + at Parser.pp$7.flowParseObjectType (/node_modules/babylon/lib/index.js:4809:27) + at Parser.pp$7.flowParseInterfaceish (/node_modules/babylon/lib/index.js:4575:20) + at Parser.pp$7.flowParseInterface (/node_modules/babylon/lib/index.js:4592:8) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5222:21) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) +" +`; + +exports[`test test.js 1`] = ` +"var Foo = require(\'./constructors\').Foo; +var x: string = new Foo().x; // error, found number instead of string +var y: string = Foo.y; // error, found number instead of string +var z: string = new Foo().m(); + +var Foo2 = require(\'./constructors\').Foo2; +var x2: string = new Foo2().x; // error, found boolean instead of string +var y2: string = Foo2.y; // error, found boolean instead of string +var z2: string = new Foo2().m(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var Foo = require(\"./constructors\").Foo; +var x: string = new Foo().x;// error, found number instead of string +var y: string = Foo.y;// error, found number instead of string +var z: string = new Foo().m(); +var Foo2 = require(\"./constructors\").Foo2; +var x2: string = new Foo2().x;// error, found boolean instead of string +var y2: string = Foo2.y;// error, found boolean instead of string +var z2: string = new Foo2().m(); + +" +`; diff --git a/tests/constructor_annots/constructors.js b/tests/constructor_annots/constructors.js new file mode 100644 index 000000000000..dac947e6d829 --- /dev/null +++ b/tests/constructor_annots/constructors.js @@ -0,0 +1,21 @@ +// Foo is a class-like function +function Foo() { + this.x = 0; // constructs objects with property x +} +Foo.y = 0; // has static property y +Foo.prototype = { m() { return 0; } }; + +// exporting Foo directly doesn't work +// Foo's instance and static props are not picked up +exports.Foo = Foo; + +// so you want to type Foo, by declaring it as a class +interface IFooPrototype { + m: () => number; +} +interface IFoo extends IFooPrototype { + x: boolean; // error, should have declared x: number instead + static (): void; + static y: boolean; // error, should have declared static y: number instead +} +exports.Foo2 = (Foo: Class); diff --git a/tests/constructor_annots/jsfmt.spec.js b/tests/constructor_annots/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/constructor_annots/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/constructor_annots/test.js b/tests/constructor_annots/test.js new file mode 100644 index 000000000000..985cc8b19f36 --- /dev/null +++ b/tests/constructor_annots/test.js @@ -0,0 +1,9 @@ +var Foo = require('./constructors').Foo; +var x: string = new Foo().x; // error, found number instead of string +var y: string = Foo.y; // error, found number instead of string +var z: string = new Foo().m(); + +var Foo2 = require('./constructors').Foo2; +var x2: string = new Foo2().x; // error, found boolean instead of string +var y2: string = Foo2.y; // error, found boolean instead of string +var z2: string = new Foo2().m(); diff --git a/tests/contents/ignore/__snapshots__/jsfmt.spec.js.snap b/tests/contents/ignore/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..71e57278fc6a --- /dev/null +++ b/tests/contents/ignore/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,18 @@ +exports[`test dummy.js 1`] = ` +"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; + +exports[`test test.js 1`] = ` +"require('./dummy'); +var xxx = 0; +xxx +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +require("./dummy"); +var xxx = 0; +xxx; + +" +`; diff --git a/tests/contents/ignore/dummy.js b/tests/contents/ignore/dummy.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/contents/ignore/jsfmt.spec.js b/tests/contents/ignore/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/contents/ignore/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/contents/ignore/test.js b/tests/contents/ignore/test.js new file mode 100644 index 000000000000..ac04887822df --- /dev/null +++ b/tests/contents/ignore/test.js @@ -0,0 +1,3 @@ +require('./dummy'); +var xxx = 0; +xxx diff --git a/tests/contents/no_flow/__snapshots__/jsfmt.spec.js.snap b/tests/contents/no_flow/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..71e57278fc6a --- /dev/null +++ b/tests/contents/no_flow/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,18 @@ +exports[`test dummy.js 1`] = ` +"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; + +exports[`test test.js 1`] = ` +"require('./dummy'); +var xxx = 0; +xxx +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +require("./dummy"); +var xxx = 0; +xxx; + +" +`; diff --git a/tests/contents/no_flow/dummy.js b/tests/contents/no_flow/dummy.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/contents/no_flow/jsfmt.spec.js b/tests/contents/no_flow/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/contents/no_flow/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/contents/no_flow/test.js b/tests/contents/no_flow/test.js new file mode 100644 index 000000000000..ac04887822df --- /dev/null +++ b/tests/contents/no_flow/test.js @@ -0,0 +1,3 @@ +require('./dummy'); +var xxx = 0; +xxx diff --git a/tests/core_tests/__snapshots__/jsfmt.spec.js.snap b/tests/core_tests/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..8d649a93948a --- /dev/null +++ b/tests/core_tests/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,253 @@ +exports[`test boolean.js 1`] = ` +"// @flow + +// Boolean (the class) tests. booleans (the literals) are not part of core.js + +let tests = [ + // constructor + function() { + new Boolean(); + new Boolean(0); + new Boolean(-0); + new Boolean(null); + new Boolean(false); + new Boolean(NaN); + new Boolean(undefined); + new Boolean(\"\"); + }, + + // toString + function() { + (true).toString(); + let x: boolean = false; + x.toString(); + (new Boolean(true)).toString(); + }, + + // valueOf + function() { + ((new Boolean(0)).valueOf(): boolean); + }, + + // casting + function() { + Boolean(); + Boolean(0); + Boolean(-0); + Boolean(null); + Boolean(false); + Boolean(NaN); + Boolean(undefined); + Boolean(\"\"); + }, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +// Boolean (the class) tests. booleans (the literals) are not part of core.js +let tests = [ + // constructor + function() { + new Boolean(); + new Boolean(0); + new Boolean(-0); + new Boolean(null); + new Boolean(false); + new Boolean(NaN); + new Boolean(undefined); + new Boolean(\"\"); + }, + // toString + function() { + true.toString(); + let x: boolean = false; + x.toString(); + new Boolean(true).toString(); + }, + // valueOf + function() { + (new Boolean(0).valueOf(): boolean); + }, + // casting + function() { + Boolean(); + Boolean(0); + Boolean(-0); + Boolean(null); + Boolean(false); + Boolean(NaN); + Boolean(undefined); + Boolean(\"\"); + } +]; + +" +`; + +exports[`test map.js 1`] = ` +"// @flow + +function* generator(): Iterable<[string, number]> { + while (true) { + yield [\'foo\', 123]; + } +} + +let tests = [ + // good constructors + function() { + let w = new Map(); + let x = new Map(null); + let y = new Map([[\'foo\', 123]]); + let z = new Map(generator()); + let a: Map = new Map(); + let b: Map = new Map([[\'foo\', 123]]); + let c: Map = new Map(generator()); + }, + + // bad constructors + function() { + let x = new Map([\'foo\', 123]); // error + let y: Map = new Map([[\'foo\', 123]]); // error + }, + + // get() + function(x: Map) { + (x.get(\'foo\'): boolean); // error, string | void + x.get(123); // error, wrong key type + }, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1237 + throw new Error(\"unprintable type: \" + JSON.stringify(n.type)); + ^ + +Error: unprintable type: \"TupleTypeAnnotation\" + at genericPrintNoParens (/src/printer.js:1237:13) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1497:36) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test regexp.js 1`] = ` +"// @flow + +let tests = [ + // constructor + function() { + new RegExp(\'foo\'); + new RegExp(/foo/); + new RegExp(\'foo\', \'i\'); + new RegExp(\'foo\', \'ig\'); + new RegExp(/foo/, \'i\'); // invalid in ES5, valid in ES6 + new RegExp(/foo/g, \'i\'); // invalid in ES5, valid in ES6 + }, + + // called as a function (equivalent to the constructor per ES6 21.2.3) + function() { + RegExp(\'foo\'); + RegExp(/foo/); + RegExp(\'foo\', \'i\'); + RegExp(\'foo\', \'ig\'); + RegExp(/foo/, \'i\'); // invalid in ES5, valid in ES6 + RegExp(/foo/g, \'i\'); // invalid in ES5, valid in ES6 + }, + + // invalid flags + function() { + RegExp(\'foo\', \'z\'); // error + new RegExp(\'foo\', \'z\'); // error + } +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +let tests = [ + // constructor + function() { + new RegExp(\"foo\"); + new RegExp(/foo/); + new RegExp(\"foo\", \"i\"); + new RegExp(\"foo\", \"ig\"); + new RegExp(/foo/, \"i\");// invalid in ES5, valid in ES6 + new RegExp(/foo/g, \"i\");// invalid in ES5, valid in ES6 + }, + // called as a function (equivalent to the constructor per ES6 21.2.3) + function() { + RegExp(\"foo\"); + RegExp(/foo/); + RegExp(\"foo\", \"i\"); + RegExp(\"foo\", \"ig\"); + RegExp(/foo/, \"i\");// invalid in ES5, valid in ES6 + RegExp(/foo/g, \"i\");// invalid in ES5, valid in ES6 + }, + // invalid flags + function() { + RegExp(\"foo\", \"z\");// error + new RegExp(\"foo\", \"z\");// error + } +]; + +" +`; + +exports[`test weakset.js 1`] = ` +"// @flow + +let ws = new WeakSet(); +let obj: Object = {}; +let dict: {foo: string} = {foo: \'bar\'}; + +ws.add(window); +ws.add(obj); +ws.add(dict); +ws.has(window); +ws.has(obj); +ws.has(dict); +ws.delete(window); +ws.delete(obj); +ws.delete(dict); + +let ws2 = new WeakSet([obj, dict]); + +let ws3 = new WeakSet([1, 2, 3]); // error, must be objects + +function* generator(): Iterable<{foo: string}> { + while (true) { + yield {foo: \'bar\'}; + } +} + +let ws4 = new WeakSet(generator()); + +function* numbers(): Iterable { + let i = 0; + while (true) { + yield i++; + } +} + +let ws5 = new WeakSet(numbers()); // error, must be objects +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/core_tests/boolean.js b/tests/core_tests/boolean.js new file mode 100644 index 000000000000..6917378f95bc --- /dev/null +++ b/tests/core_tests/boolean.js @@ -0,0 +1,42 @@ +// @flow + +// Boolean (the class) tests. booleans (the literals) are not part of core.js + +let tests = [ + // constructor + function() { + new Boolean(); + new Boolean(0); + new Boolean(-0); + new Boolean(null); + new Boolean(false); + new Boolean(NaN); + new Boolean(undefined); + new Boolean(""); + }, + + // toString + function() { + (true).toString(); + let x: boolean = false; + x.toString(); + (new Boolean(true)).toString(); + }, + + // valueOf + function() { + ((new Boolean(0)).valueOf(): boolean); + }, + + // casting + function() { + Boolean(); + Boolean(0); + Boolean(-0); + Boolean(null); + Boolean(false); + Boolean(NaN); + Boolean(undefined); + Boolean(""); + }, +]; diff --git a/tests/core_tests/jsfmt.spec.js b/tests/core_tests/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/core_tests/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/core_tests/map.js b/tests/core_tests/map.js new file mode 100644 index 000000000000..414b794c74c0 --- /dev/null +++ b/tests/core_tests/map.js @@ -0,0 +1,32 @@ +// @flow + +function* generator(): Iterable<[string, number]> { + while (true) { + yield ['foo', 123]; + } +} + +let tests = [ + // good constructors + function() { + let w = new Map(); + let x = new Map(null); + let y = new Map([['foo', 123]]); + let z = new Map(generator()); + let a: Map = new Map(); + let b: Map = new Map([['foo', 123]]); + let c: Map = new Map(generator()); + }, + + // bad constructors + function() { + let x = new Map(['foo', 123]); // error + let y: Map = new Map([['foo', 123]]); // error + }, + + // get() + function(x: Map) { + (x.get('foo'): boolean); // error, string | void + x.get(123); // error, wrong key type + }, +]; diff --git a/tests/core_tests/regexp.js b/tests/core_tests/regexp.js new file mode 100644 index 000000000000..d3bca655cb91 --- /dev/null +++ b/tests/core_tests/regexp.js @@ -0,0 +1,29 @@ +// @flow + +let tests = [ + // constructor + function() { + new RegExp('foo'); + new RegExp(/foo/); + new RegExp('foo', 'i'); + new RegExp('foo', 'ig'); + new RegExp(/foo/, 'i'); // invalid in ES5, valid in ES6 + new RegExp(/foo/g, 'i'); // invalid in ES5, valid in ES6 + }, + + // called as a function (equivalent to the constructor per ES6 21.2.3) + function() { + RegExp('foo'); + RegExp(/foo/); + RegExp('foo', 'i'); + RegExp('foo', 'ig'); + RegExp(/foo/, 'i'); // invalid in ES5, valid in ES6 + RegExp(/foo/g, 'i'); // invalid in ES5, valid in ES6 + }, + + // invalid flags + function() { + RegExp('foo', 'z'); // error + new RegExp('foo', 'z'); // error + } +]; diff --git a/tests/core_tests/weakset.js b/tests/core_tests/weakset.js new file mode 100644 index 000000000000..76d7ae1ee83c --- /dev/null +++ b/tests/core_tests/weakset.js @@ -0,0 +1,36 @@ +// @flow + +let ws = new WeakSet(); +let obj: Object = {}; +let dict: {foo: string} = {foo: 'bar'}; + +ws.add(window); +ws.add(obj); +ws.add(dict); +ws.has(window); +ws.has(obj); +ws.has(dict); +ws.delete(window); +ws.delete(obj); +ws.delete(dict); + +let ws2 = new WeakSet([obj, dict]); + +let ws3 = new WeakSet([1, 2, 3]); // error, must be objects + +function* generator(): Iterable<{foo: string}> { + while (true) { + yield {foo: 'bar'}; + } +} + +let ws4 = new WeakSet(generator()); + +function* numbers(): Iterable { + let i = 0; + while (true) { + yield i++; + } +} + +let ws5 = new WeakSet(numbers()); // error, must be objects diff --git a/tests/covariance/__snapshots__/jsfmt.spec.js.snap b/tests/covariance/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..491e6e935231 --- /dev/null +++ b/tests/covariance/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,68 @@ +exports[`test test.js 1`] = ` +"type CovArrayVerbose = Array; +var b: CovArrayVerbose = []; +var y: CovArrayVerbose = b; +y[0] = \"\"; // error + +class NVerbose { + x: CovArrayVerbose; + foo(): CovArrayVerbose { return this.x; } +} + +var nv: NVerbose = new NVerbose; +nv.x = [0]; +(nv.x[0]: string); // error +(nv.foo()[0]: string); // error + +/* TODO: use existentials for non-verbose covariance? + +type CovArray = Array<*:X>; +var c: CovArray = [0]; +var z: CovArray = c; // error + +var d: CovArray = []; +var w: CovArray = d; +w[0] = \"\"; // error + +type P = CovArray; +var p: P = []; +(p[0]: number); // not an error! +p[0] = \"\"; // error + +class M { + x: CovArray; + foo(): CovArray { return this.x; } + bar(x: string) { this.foo()[0] = x; } // error +} + +class N { + x: CovArray; + foo(): CovArray { return this.x; } + bar(e: string) { this.foo()[0] = e; } // error + qux(e: E) { this.foo()[0] = e; } +} + +var n: N = new N; +n.x = [0]; +(n.x[0]: string); // error +(n.foo()[0]: string); // not an error! + +*/ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1479:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/covariance/jsfmt.spec.js b/tests/covariance/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/covariance/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/covariance/test.js b/tests/covariance/test.js new file mode 100644 index 000000000000..9383b247fc8d --- /dev/null +++ b/tests/covariance/test.js @@ -0,0 +1,49 @@ +type CovArrayVerbose = Array; +var b: CovArrayVerbose = []; +var y: CovArrayVerbose = b; +y[0] = ""; // error + +class NVerbose { + x: CovArrayVerbose; + foo(): CovArrayVerbose { return this.x; } +} + +var nv: NVerbose = new NVerbose; +nv.x = [0]; +(nv.x[0]: string); // error +(nv.foo()[0]: string); // error + +/* TODO: use existentials for non-verbose covariance? + +type CovArray = Array<*:X>; +var c: CovArray = [0]; +var z: CovArray = c; // error + +var d: CovArray = []; +var w: CovArray = d; +w[0] = ""; // error + +type P = CovArray; +var p: P = []; +(p[0]: number); // not an error! +p[0] = ""; // error + +class M { + x: CovArray; + foo(): CovArray { return this.x; } + bar(x: string) { this.foo()[0] = x; } // error +} + +class N { + x: CovArray; + foo(): CovArray { return this.x; } + bar(e: string) { this.foo()[0] = e; } // error + qux(e: E) { this.foo()[0] = e; } +} + +var n: N = new N; +n.x = [0]; +(n.x[0]: string); // error +(n.foo()[0]: string); // not an error! + +*/ diff --git a/tests/coverage/__snapshots__/jsfmt.spec.js.snap b/tests/coverage/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..5343f59a1e2e --- /dev/null +++ b/tests/coverage/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,80 @@ +exports[`test crash.js 1`] = ` +"// This file triggers a violation of the "disjoint-or-nested ranges invariant" +// that we implicitly assume in type-at-pos and coverage implementations. In +// particular, when unchecked it causes a crash with coverage --color. + +declare module foo { +} + +declare module bar { +} + +// TODO +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// This file triggers a violation of the "disjoint-or-nested ranges invariant" +// that we implicitly assume in type-at-pos and coverage implementations. In +// particular, when unchecked it causes a crash with coverage --color. +declare module foo { + +} +declare module bar { + +}// TODO + +" +`; + +exports[`test declare_module.js 1`] = ` +"// check coverage of declare module + +declare module foo { +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// check coverage of declare module +declare module foo { + +} + +" +`; + +exports[`test no_pragma.js 1`] = ` +"let x = 0; +(x: string); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +let x = 0; +(x: string); + +" +`; + +exports[`test non-termination.js 1`] = ` +"// This file triggers a violation of the "disjoint-or-nested ranges invariant" +// that we implicitly assume in type-at-pos and coverage implementations. In +// particular, when unchecked it causes non-termination with coverage --color. + +declare module foo { +} + +declare module bar { +} + +// TODO + +declare class qux { +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// This file triggers a violation of the "disjoint-or-nested ranges invariant" +// that we implicitly assume in type-at-pos and coverage implementations. In +// particular, when unchecked it causes non-termination with coverage --color. +declare module foo { + +} +declare module bar { + +} +// TODO +declare class qux {} + +" +`; diff --git a/tests/coverage/crash.js b/tests/coverage/crash.js new file mode 100644 index 000000000000..16aa9e752c9d --- /dev/null +++ b/tests/coverage/crash.js @@ -0,0 +1,11 @@ +// This file triggers a violation of the "disjoint-or-nested ranges invariant" +// that we implicitly assume in type-at-pos and coverage implementations. In +// particular, when unchecked it causes a crash with coverage --color. + +declare module foo { +} + +declare module bar { +} + +// TODO diff --git a/tests/coverage/declare_module.js b/tests/coverage/declare_module.js new file mode 100644 index 000000000000..88ad5dab23f2 --- /dev/null +++ b/tests/coverage/declare_module.js @@ -0,0 +1,4 @@ +// check coverage of declare module + +declare module foo { +} diff --git a/tests/coverage/jsfmt.spec.js b/tests/coverage/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/coverage/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/coverage/no_pragma.js b/tests/coverage/no_pragma.js new file mode 100644 index 000000000000..865e412c16d6 --- /dev/null +++ b/tests/coverage/no_pragma.js @@ -0,0 +1,2 @@ +let x = 0; +(x: string); diff --git a/tests/coverage/non-termination.js b/tests/coverage/non-termination.js new file mode 100644 index 000000000000..86e8c24301ff --- /dev/null +++ b/tests/coverage/non-termination.js @@ -0,0 +1,14 @@ +// This file triggers a violation of the "disjoint-or-nested ranges invariant" +// that we implicitly assume in type-at-pos and coverage implementations. In +// particular, when unchecked it causes non-termination with coverage --color. + +declare module foo { +} + +declare module bar { +} + +// TODO + +declare class qux { +} diff --git a/tests/cycle/A.js b/tests/cycle/A.js new file mode 100644 index 000000000000..a49a8f290e31 --- /dev/null +++ b/tests/cycle/A.js @@ -0,0 +1,5 @@ +var B = require('./B'); + +class A extends B { } + +module.exports = A; diff --git a/tests/cycle/B.js b/tests/cycle/B.js new file mode 100644 index 000000000000..e5c1da823f74 --- /dev/null +++ b/tests/cycle/B.js @@ -0,0 +1,5 @@ +var A = require('./A'); + +//class B extends A { } + +module.exports = B; diff --git a/tests/cycle/__snapshots__/jsfmt.spec.js.snap b/tests/cycle/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..4986c12b758a --- /dev/null +++ b/tests/cycle/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,27 @@ +exports[`test A.js 1`] = ` +"var B = require('./B'); + +class A extends B { } + +module.exports = A; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var B = require("./B"); +class A extends B {} +module.exports = A; + +" +`; + +exports[`test B.js 1`] = ` +"var A = require('./A'); + +//class B extends A { } + +module.exports = B; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var A = require("./A"); +//class B extends A { } +module.exports = B; + +" +`; diff --git a/tests/cycle/jsfmt.spec.js b/tests/cycle/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/cycle/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/date/__snapshots__/jsfmt.spec.js.snap b/tests/date/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..a79fda0e82f2 --- /dev/null +++ b/tests/date/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,57 @@ +exports[`test date.js 1`] = ` +"var d = new Date(0); +var x:string = d.getTime(); + +var y:number = d; + +// valid constructors +new Date(); +new Date(1234567890); +new Date('2015/06/18'); +new Date(2015, 6); +new Date(2015, 6, 18); +new Date(2015, 6, 18, 11); +new Date(2015, 6, 18, 11, 55); +new Date(2015, 6, 18, 11, 55, 42); +new Date(2015, 6, 18, 11, 55, 42, 999); + +// invalid constructors +new Date({}); +new Date(2015, '6'); +new Date(2015, 6, '18'); +new Date(2015, 6, 18, '11'); +new Date(2015, 6, 18, 11, '55'); +new Date(2015, 6, 18, 11, 55, '42'); +new Date(2015, 6, 18, 11, 55, 42, '999'); + +// invalid constructors that we incorrectly consider valid +new Date('2015', 6); +new Date(2015, 6, 18, 11, 55, 42, 999, 'hahaha'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var d = new Date(0); +var x: string = d.getTime(); +var y: number = d; +// valid constructors +new Date(); +new Date(1234567890); +new Date("2015/06/18"); +new Date(2015, 6); +new Date(2015, 6, 18); +new Date(2015, 6, 18, 11); +new Date(2015, 6, 18, 11, 55); +new Date(2015, 6, 18, 11, 55, 42); +new Date(2015, 6, 18, 11, 55, 42, 999); +// invalid constructors +new Date({}); +new Date(2015, "6"); +new Date(2015, 6, "18"); +new Date(2015, 6, 18, "11"); +new Date(2015, 6, 18, 11, "55"); +new Date(2015, 6, 18, 11, 55, "42"); +new Date(2015, 6, 18, 11, 55, 42, "999"); +// invalid constructors that we incorrectly consider valid +new Date("2015", 6); +new Date(2015, 6, 18, 11, 55, 42, 999, "hahaha"); + +" +`; diff --git a/tests/date/date.js b/tests/date/date.js new file mode 100644 index 000000000000..dddd8aa08d2b --- /dev/null +++ b/tests/date/date.js @@ -0,0 +1,28 @@ +var d = new Date(0); +var x:string = d.getTime(); + +var y:number = d; + +// valid constructors +new Date(); +new Date(1234567890); +new Date('2015/06/18'); +new Date(2015, 6); +new Date(2015, 6, 18); +new Date(2015, 6, 18, 11); +new Date(2015, 6, 18, 11, 55); +new Date(2015, 6, 18, 11, 55, 42); +new Date(2015, 6, 18, 11, 55, 42, 999); + +// invalid constructors +new Date({}); +new Date(2015, '6'); +new Date(2015, 6, '18'); +new Date(2015, 6, 18, '11'); +new Date(2015, 6, 18, 11, '55'); +new Date(2015, 6, 18, 11, 55, '42'); +new Date(2015, 6, 18, 11, 55, 42, '999'); + +// invalid constructors that we incorrectly consider valid +new Date('2015', 6); +new Date(2015, 6, 18, 11, 55, 42, 999, 'hahaha'); diff --git a/tests/date/jsfmt.spec.js b/tests/date/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/date/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/declaration_files_haste/ExplicitProvidesModuleDifferentName.js b/tests/declaration_files_haste/ExplicitProvidesModuleDifferentName.js new file mode 100644 index 000000000000..90fdd624d92e --- /dev/null +++ b/tests/declaration_files_haste/ExplicitProvidesModuleDifferentName.js @@ -0,0 +1,6 @@ +/* + * @providesModule ExplicitProvidesModuleDifferentName + * @flow + */ + +module.exports.fun = (): string => "hello there"; diff --git a/tests/declaration_files_haste/ExplicitProvidesModuleSameName.js b/tests/declaration_files_haste/ExplicitProvidesModuleSameName.js new file mode 100644 index 000000000000..420b439f46c9 --- /dev/null +++ b/tests/declaration_files_haste/ExplicitProvidesModuleSameName.js @@ -0,0 +1,6 @@ +/* + * @providesModule ExplicitProvidesModuleSameName + * @flow + */ + +module.exports.fun = (): string => "hello there"; diff --git a/tests/declaration_files_haste/ImplicitProvidesModule.js b/tests/declaration_files_haste/ImplicitProvidesModule.js new file mode 100644 index 000000000000..9fa3b2cc07d6 --- /dev/null +++ b/tests/declaration_files_haste/ImplicitProvidesModule.js @@ -0,0 +1,6 @@ +/* + * @providesModule ImplicitProvidesModule + * @flow + */ + +module.exports.fun = (): string => "hello there"; diff --git a/tests/declaration_files_haste/__snapshots__/jsfmt.spec.js.snap b/tests/declaration_files_haste/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..53ece1a8733a --- /dev/null +++ b/tests/declaration_files_haste/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,81 @@ +exports[`test ExplicitProvidesModuleDifferentName.js 1`] = ` +"/* + * @providesModule ExplicitProvidesModuleDifferentName + * @flow + */ + +module.exports.fun = (): string => "hello there"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* + * @providesModule ExplicitProvidesModuleDifferentName + * @flow + */ +module.exports.fun = (): string => "hello there"; + +" +`; + +exports[`test ExplicitProvidesModuleSameName.js 1`] = ` +"/* + * @providesModule ExplicitProvidesModuleSameName + * @flow + */ + +module.exports.fun = (): string => "hello there"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* + * @providesModule ExplicitProvidesModuleSameName + * @flow + */ +module.exports.fun = (): string => "hello there"; + +" +`; + +exports[`test ImplicitProvidesModule.js 1`] = ` +"/* + * @providesModule ImplicitProvidesModule + * @flow + */ + +module.exports.fun = (): string => "hello there"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* + * @providesModule ImplicitProvidesModule + * @flow + */ +module.exports.fun = (): string => "hello there"; + +" +`; + +exports[`test md5.js 1`] = ` +"/* @providesModule md5 */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; + +exports[`test test.js 1`] = ` +"/* @flow */ + +var Implicit = require('ImplicitProvidesModule'); +(Implicit.fun(): string); + +var ExplicitSameName = require('ExplicitProvidesModuleSameName'); +(ExplicitSameName.fun(): string); + +var ExplicitDifferentName = require('ExplicitProvidesModuleDifferentName'); +(ExplicitDifferentName.fun(): string); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var Implicit = require("ImplicitProvidesModule"); +(Implicit.fun(): string); +var ExplicitSameName = require("ExplicitProvidesModuleSameName"); +(ExplicitSameName.fun(): string); +var ExplicitDifferentName = require("ExplicitProvidesModuleDifferentName"); +(ExplicitDifferentName.fun(): string); + +" +`; diff --git a/tests/declaration_files_haste/external/_d3/__snapshots__/jsfmt.spec.js.snap b/tests/declaration_files_haste/external/_d3/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..8eedeb757628 --- /dev/null +++ b/tests/declaration_files_haste/external/_d3/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,7 @@ +exports[`test min.js 1`] = ` +"module.exports.fun = (): string => "hello there"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +module.exports.fun = (): string => "hello there"; + +" +`; diff --git a/tests/declaration_files_haste/external/_d3/jsfmt.spec.js b/tests/declaration_files_haste/external/_d3/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/declaration_files_haste/external/_d3/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/declaration_files_haste/external/_d3/min.js b/tests/declaration_files_haste/external/_d3/min.js new file mode 100644 index 000000000000..94884777ff10 --- /dev/null +++ b/tests/declaration_files_haste/external/_d3/min.js @@ -0,0 +1 @@ +module.exports.fun = (): string => "hello there"; diff --git a/tests/declaration_files_haste/foo/bar/__snapshots__/jsfmt.spec.js.snap b/tests/declaration_files_haste/foo/bar/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..ac5bbc602ad1 --- /dev/null +++ b/tests/declaration_files_haste/foo/bar/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,28 @@ +exports[`test nested_test.js 1`] = ` +"/* @flow */ + +var docblock = require('qux/docblock'); +var min = require('d3/min.js'); +var corge = require('qux/corge'); + +// make sure we don't pick up non-header @providesModule +// annotations - see node_modules/qux/docblock.js +var unreachable = require('annotation'); + +(docblock.fun(): string); +(min.fun(): string); +(corge.fun(): string); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var docblock = require("qux/docblock"); +var min = require("d3/min.js"); +var corge = require("qux/corge"); +// make sure we don't pick up non-header @providesModule +// annotations - see node_modules/qux/docblock.js +var unreachable = require("annotation"); +(docblock.fun(): string); +(min.fun(): string); +(corge.fun(): string); + +" +`; diff --git a/tests/declaration_files_haste/foo/bar/jsfmt.spec.js b/tests/declaration_files_haste/foo/bar/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/declaration_files_haste/foo/bar/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/declaration_files_haste/foo/bar/nested_test.js b/tests/declaration_files_haste/foo/bar/nested_test.js new file mode 100644 index 000000000000..c9522bc6e6b5 --- /dev/null +++ b/tests/declaration_files_haste/foo/bar/nested_test.js @@ -0,0 +1,13 @@ +/* @flow */ + +var docblock = require('qux/docblock'); +var min = require('d3/min.js'); +var corge = require('qux/corge'); + +// make sure we don't pick up non-header @providesModule +// annotations - see node_modules/qux/docblock.js +var unreachable = require('annotation'); + +(docblock.fun(): string); +(min.fun(): string); +(corge.fun(): string); diff --git a/tests/declaration_files_haste/jsfmt.spec.js b/tests/declaration_files_haste/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/declaration_files_haste/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/declaration_files_haste/md5.js b/tests/declaration_files_haste/md5.js new file mode 100644 index 000000000000..c358d72168a6 --- /dev/null +++ b/tests/declaration_files_haste/md5.js @@ -0,0 +1 @@ +/* @providesModule md5 */ diff --git a/tests/declaration_files_haste/test.js b/tests/declaration_files_haste/test.js new file mode 100644 index 000000000000..88fe21f03875 --- /dev/null +++ b/tests/declaration_files_haste/test.js @@ -0,0 +1,10 @@ +/* @flow */ + +var Implicit = require('ImplicitProvidesModule'); +(Implicit.fun(): string); + +var ExplicitSameName = require('ExplicitProvidesModuleSameName'); +(ExplicitSameName.fun(): string); + +var ExplicitDifferentName = require('ExplicitProvidesModuleDifferentName'); +(ExplicitDifferentName.fun(): string); diff --git a/tests/declaration_files_haste/ws/__snapshots__/jsfmt.spec.js.snap b/tests/declaration_files_haste/ws/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..72caaf084cb1 --- /dev/null +++ b/tests/declaration_files_haste/ws/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,6 @@ +exports[`test index.js 1`] = ` +"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; diff --git a/tests/declaration_files_haste/ws/index.js b/tests/declaration_files_haste/ws/index.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/declaration_files_haste/ws/jsfmt.spec.js b/tests/declaration_files_haste/ws/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/declaration_files_haste/ws/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/declaration_files_haste/ws/test/__snapshots__/jsfmt.spec.js.snap b/tests/declaration_files_haste/ws/test/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..f54d0216ceb7 --- /dev/null +++ b/tests/declaration_files_haste/ws/test/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,7 @@ +exports[`test client.js 1`] = ` +"var ws = require('../'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var ws = require("../"); + +" +`; diff --git a/tests/declaration_files_haste/ws/test/client.js b/tests/declaration_files_haste/ws/test/client.js new file mode 100644 index 000000000000..2fb0ed6d4dfd --- /dev/null +++ b/tests/declaration_files_haste/ws/test/client.js @@ -0,0 +1 @@ +var ws = require('../'); diff --git a/tests/declaration_files_haste/ws/test/jsfmt.spec.js b/tests/declaration_files_haste/ws/test/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/declaration_files_haste/ws/test/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/declaration_files_incremental_haste/A.js b/tests/declaration_files_incremental_haste/A.js new file mode 100644 index 000000000000..9ad347d2c7b9 --- /dev/null +++ b/tests/declaration_files_incremental_haste/A.js @@ -0,0 +1,3 @@ +/* @providesModule A */ +class Implementation {} +export function foo(): Implementation { return new Implementation; } diff --git a/tests/declaration_files_incremental_haste/ExplicitProvidesModuleDifferentName.js b/tests/declaration_files_incremental_haste/ExplicitProvidesModuleDifferentName.js new file mode 100644 index 000000000000..b31795b56a69 --- /dev/null +++ b/tests/declaration_files_incremental_haste/ExplicitProvidesModuleDifferentName.js @@ -0,0 +1,7 @@ +/* + * @providesModule ExplicitProvidesModuleDifferentName + * @flow + */ + +class Implementation {} +module.exports.fun = (): Implementation => new Implementation; diff --git a/tests/declaration_files_incremental_haste/ExplicitProvidesModuleSameName.js b/tests/declaration_files_incremental_haste/ExplicitProvidesModuleSameName.js new file mode 100644 index 000000000000..2b8762f7b39d --- /dev/null +++ b/tests/declaration_files_incremental_haste/ExplicitProvidesModuleSameName.js @@ -0,0 +1,7 @@ +/* + * @providesModule ExplicitProvidesModuleSameName + * @flow + */ + +class Implementation {} +module.exports.fun = (): Implementation => new Implementation; diff --git a/tests/declaration_files_incremental_haste/ImplicitProvidesModule.js b/tests/declaration_files_incremental_haste/ImplicitProvidesModule.js new file mode 100644 index 000000000000..855ff08d972d --- /dev/null +++ b/tests/declaration_files_incremental_haste/ImplicitProvidesModule.js @@ -0,0 +1,7 @@ +/* + * @providesModule ImplicitProvidesModule + * @flow + */ + +class Implementation {} +module.exports.fun = (): Implementation => new Implementation; diff --git a/tests/declaration_files_incremental_haste/__snapshots__/jsfmt.spec.js.snap b/tests/declaration_files_incremental_haste/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..3abccda6a355 --- /dev/null +++ b/tests/declaration_files_incremental_haste/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,101 @@ +exports[`test A.js 1`] = ` +"/* @providesModule A */ +class Implementation {} +export function foo(): Implementation { return new Implementation; } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @providesModule A */ +class Implementation {} +export function foo(): Implementation { + return new Implementation(); +} + +" +`; + +exports[`test ExplicitProvidesModuleDifferentName.js 1`] = ` +"/* + * @providesModule ExplicitProvidesModuleDifferentName + * @flow + */ + +class Implementation {} +module.exports.fun = (): Implementation => new Implementation; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* + * @providesModule ExplicitProvidesModuleDifferentName + * @flow + */ +class Implementation {} +module.exports.fun = (): Implementation => new Implementation(); + +" +`; + +exports[`test ExplicitProvidesModuleSameName.js 1`] = ` +"/* + * @providesModule ExplicitProvidesModuleSameName + * @flow + */ + +class Implementation {} +module.exports.fun = (): Implementation => new Implementation; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* + * @providesModule ExplicitProvidesModuleSameName + * @flow + */ +class Implementation {} +module.exports.fun = (): Implementation => new Implementation(); + +" +`; + +exports[`test ImplicitProvidesModule.js 1`] = ` +"/* + * @providesModule ImplicitProvidesModule + * @flow + */ + +class Implementation {} +module.exports.fun = (): Implementation => new Implementation; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* + * @providesModule ImplicitProvidesModule + * @flow + */ +class Implementation {} +module.exports.fun = (): Implementation => new Implementation(); + +" +`; + +exports[`test md5.js 1`] = ` +"/* @providesModule md5 */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; + +exports[`test test.js 1`] = ` +"/* @flow */ + +var Implicit = require('ImplicitProvidesModule'); +(Implicit.fun(): boolean); // Error: Either Implementation ~> boolean or Declaration ~> boolean + +var ExplicitSameName = require('ExplicitProvidesModuleSameName'); +(ExplicitSameName.fun(): boolean); // Error: Either Implementation ~> boolean or Declaration ~> boolean + +var ExplicitDifferentName = require('ExplicitProvidesModuleDifferentName'); +(ExplicitDifferentName.fun(): boolean); // Error: Either Implementation ~> boolean or Declaration ~> boolean +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var Implicit = require("ImplicitProvidesModule"); +(Implicit.fun(): boolean);// Error: Either Implementation ~> boolean or Declaration ~> boolean +var ExplicitSameName = require("ExplicitProvidesModuleSameName"); +(ExplicitSameName.fun(): boolean);// Error: Either Implementation ~> boolean or Declaration ~> boolean +var ExplicitDifferentName = require("ExplicitProvidesModuleDifferentName"); +(ExplicitDifferentName.fun(): boolean);// Error: Either Implementation ~> boolean or Declaration ~> boolean + +" +`; diff --git a/tests/declaration_files_incremental_haste/external/_d3/__snapshots__/jsfmt.spec.js.snap b/tests/declaration_files_incremental_haste/external/_d3/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..67b927b140c4 --- /dev/null +++ b/tests/declaration_files_incremental_haste/external/_d3/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,9 @@ +exports[`test min.js 1`] = ` +"class Implementation {} +module.exports.fun = (): Implementation => new Implementation; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class Implementation {} +module.exports.fun = (): Implementation => new Implementation(); + +" +`; diff --git a/tests/declaration_files_incremental_haste/external/_d3/jsfmt.spec.js b/tests/declaration_files_incremental_haste/external/_d3/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/declaration_files_incremental_haste/external/_d3/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/declaration_files_incremental_haste/external/_d3/min.js b/tests/declaration_files_incremental_haste/external/_d3/min.js new file mode 100644 index 000000000000..330a5d7de8ca --- /dev/null +++ b/tests/declaration_files_incremental_haste/external/_d3/min.js @@ -0,0 +1,2 @@ +class Implementation {} +module.exports.fun = (): Implementation => new Implementation; diff --git a/tests/declaration_files_incremental_haste/foo/bar/__snapshots__/jsfmt.spec.js.snap b/tests/declaration_files_incremental_haste/foo/bar/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..b213b5938c18 --- /dev/null +++ b/tests/declaration_files_incremental_haste/foo/bar/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,21 @@ +exports[`test nested_test.js 1`] = ` +"/* @flow */ + +var docblock = require('qux/docblock'); +var min = require('d3/min.js'); +var corge = require('qux/corge'); + +(docblock.fun(): boolean); // Error: Either Implementation ~> boolean or Declaration ~> boolean +(min.fun(): boolean); // Error: Either Implementation ~> boolean or Declaration ~> boolean +(corge.fun(): boolean); // Error: Either Implementation ~> boolean or Declaration ~> boolean +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var docblock = require("qux/docblock"); +var min = require("d3/min.js"); +var corge = require("qux/corge"); +(docblock.fun(): boolean);// Error: Either Implementation ~> boolean or Declaration ~> boolean +(min.fun(): boolean);// Error: Either Implementation ~> boolean or Declaration ~> boolean +(corge.fun(): boolean);// Error: Either Implementation ~> boolean or Declaration ~> boolean + +" +`; diff --git a/tests/declaration_files_incremental_haste/foo/bar/jsfmt.spec.js b/tests/declaration_files_incremental_haste/foo/bar/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/declaration_files_incremental_haste/foo/bar/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/declaration_files_incremental_haste/foo/bar/nested_test.js b/tests/declaration_files_incremental_haste/foo/bar/nested_test.js new file mode 100644 index 000000000000..ffc6bac4625a --- /dev/null +++ b/tests/declaration_files_incremental_haste/foo/bar/nested_test.js @@ -0,0 +1,9 @@ +/* @flow */ + +var docblock = require('qux/docblock'); +var min = require('d3/min.js'); +var corge = require('qux/corge'); + +(docblock.fun(): boolean); // Error: Either Implementation ~> boolean or Declaration ~> boolean +(min.fun(): boolean); // Error: Either Implementation ~> boolean or Declaration ~> boolean +(corge.fun(): boolean); // Error: Either Implementation ~> boolean or Declaration ~> boolean diff --git a/tests/declaration_files_incremental_haste/jsfmt.spec.js b/tests/declaration_files_incremental_haste/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/declaration_files_incremental_haste/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/declaration_files_incremental_haste/md5.js b/tests/declaration_files_incremental_haste/md5.js new file mode 100644 index 000000000000..c358d72168a6 --- /dev/null +++ b/tests/declaration_files_incremental_haste/md5.js @@ -0,0 +1 @@ +/* @providesModule md5 */ diff --git a/tests/declaration_files_incremental_haste/test.js b/tests/declaration_files_incremental_haste/test.js new file mode 100644 index 000000000000..c139d4c41746 --- /dev/null +++ b/tests/declaration_files_incremental_haste/test.js @@ -0,0 +1,10 @@ +/* @flow */ + +var Implicit = require('ImplicitProvidesModule'); +(Implicit.fun(): boolean); // Error: Either Implementation ~> boolean or Declaration ~> boolean + +var ExplicitSameName = require('ExplicitProvidesModuleSameName'); +(ExplicitSameName.fun(): boolean); // Error: Either Implementation ~> boolean or Declaration ~> boolean + +var ExplicitDifferentName = require('ExplicitProvidesModuleDifferentName'); +(ExplicitDifferentName.fun(): boolean); // Error: Either Implementation ~> boolean or Declaration ~> boolean diff --git a/tests/declaration_files_incremental_haste/ws/__snapshots__/jsfmt.spec.js.snap b/tests/declaration_files_incremental_haste/ws/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..72caaf084cb1 --- /dev/null +++ b/tests/declaration_files_incremental_haste/ws/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,6 @@ +exports[`test index.js 1`] = ` +"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; diff --git a/tests/declaration_files_incremental_haste/ws/index.js b/tests/declaration_files_incremental_haste/ws/index.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/declaration_files_incremental_haste/ws/jsfmt.spec.js b/tests/declaration_files_incremental_haste/ws/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/declaration_files_incremental_haste/ws/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/declaration_files_incremental_haste/ws/test/__snapshots__/jsfmt.spec.js.snap b/tests/declaration_files_incremental_haste/ws/test/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..f54d0216ceb7 --- /dev/null +++ b/tests/declaration_files_incremental_haste/ws/test/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,7 @@ +exports[`test client.js 1`] = ` +"var ws = require('../'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var ws = require("../"); + +" +`; diff --git a/tests/declaration_files_incremental_haste/ws/test/client.js b/tests/declaration_files_incremental_haste/ws/test/client.js new file mode 100644 index 000000000000..2fb0ed6d4dfd --- /dev/null +++ b/tests/declaration_files_incremental_haste/ws/test/client.js @@ -0,0 +1 @@ +var ws = require('../'); diff --git a/tests/declaration_files_incremental_haste/ws/test/jsfmt.spec.js b/tests/declaration_files_incremental_haste/ws/test/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/declaration_files_incremental_haste/ws/test/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/declaration_files_incremental_node/A.js b/tests/declaration_files_incremental_node/A.js new file mode 100644 index 000000000000..e1abbb2ca677 --- /dev/null +++ b/tests/declaration_files_incremental_node/A.js @@ -0,0 +1,2 @@ +class Implementation {} +export function foo(): Implementation { return new Implementation; } diff --git a/tests/declaration_files_incremental_node/__snapshots__/jsfmt.spec.js.snap b/tests/declaration_files_incremental_node/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..77b78352e073 --- /dev/null +++ b/tests/declaration_files_incremental_node/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,98 @@ +exports[`test A.js 1`] = ` +"class Implementation {} +export function foo(): Implementation { return new Implementation; } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class Implementation {} +export function foo(): Implementation { + return new Implementation(); +} + +" +`; + +exports[`test test_absolute.js 1`] = ` +"/* @flow */ + +// This will require ./node_modules/B.js.flow +var B1 = require('B'); +(B1.fun(): boolean); // Error either Implementation ~> boolean or Declaration ~> boolean + +// This will require ./node_modules/B.js.flow +var B2 = require('B.js'); +(B2.fun(): boolean); // Error either Implementation ~> boolean or Declaration ~> boolean + +var C = require('package_with_full_main'); +(C.fun(): boolean); // Error either Implementation ~> boolean or Declaration ~> boolean + +var D = require('package_with_partial_main'); +(D.fun(): boolean); // Error either Implementation ~> boolean or Declaration ~> boolean + +var E = require('package_with_no_package_json'); +(E.fun(): boolean); // Error either Implementation ~> boolean or Declaration ~> boolean + +var F = require('package_with_dir_main'); +(F.fun(): boolean); // Error either Implementation ~> boolean or Declaration ~> boolean + +// This will require ./node_modules/B.js.flow +var B1 = require('B'); +(B1.fun(): boolean); // Error either Implementation ~> boolean or Declaration ~> boolean + +// This will require ./node_modules/B.js.flow +var B2 = require('B.js'); +(B2.fun(): boolean); // Error either Implementation ~> boolean or Declaration ~> boolean + +var C = require('package_with_full_main'); +(C.fun(): boolean); // Error either Implementation ~> boolean or Declaration ~> boolean + +var D = require('package_with_partial_main'); +(D.fun(): boolean); // Error either Implementation ~> boolean or Declaration ~> boolean + +var E = require('package_with_no_package_json'); +(E.fun(): boolean); // Error either Implementation ~> boolean or Declaration ~> boolean + +var F = require('package_with_dir_main'); +(F.fun(): boolean); // Error either Implementation ~> boolean or Declaration ~> boolean +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +// This will require ./node_modules/B.js.flow +var B1 = require("B"); +(B1.fun(): boolean);// Error either Implementation ~> boolean or Declaration ~> boolean +// This will require ./node_modules/B.js.flow +var B2 = require("B.js"); +(B2.fun(): boolean);// Error either Implementation ~> boolean or Declaration ~> boolean +var C = require("package_with_full_main"); +(C.fun(): boolean);// Error either Implementation ~> boolean or Declaration ~> boolean +var D = require("package_with_partial_main"); +(D.fun(): boolean);// Error either Implementation ~> boolean or Declaration ~> boolean +var E = require("package_with_no_package_json"); +(E.fun(): boolean);// Error either Implementation ~> boolean or Declaration ~> boolean +var F = require("package_with_dir_main"); +(F.fun(): boolean);// Error either Implementation ~> boolean or Declaration ~> boolean +// This will require ./node_modules/B.js.flow +var B1 = require("B"); +(B1.fun(): boolean);// Error either Implementation ~> boolean or Declaration ~> boolean +// This will require ./node_modules/B.js.flow +var B2 = require("B.js"); +(B2.fun(): boolean);// Error either Implementation ~> boolean or Declaration ~> boolean +var C = require("package_with_full_main"); +(C.fun(): boolean);// Error either Implementation ~> boolean or Declaration ~> boolean +var D = require("package_with_partial_main"); +(D.fun(): boolean);// Error either Implementation ~> boolean or Declaration ~> boolean +var E = require("package_with_no_package_json"); +(E.fun(): boolean);// Error either Implementation ~> boolean or Declaration ~> boolean +var F = require("package_with_dir_main"); +(F.fun(): boolean);// Error either Implementation ~> boolean or Declaration ~> boolean + +" +`; + +exports[`test test_relative.js 1`] = ` +"import { foo } from './A'; + +(foo(): boolean); // Error: either Implementation ~> boolean or Definition ~> boolean +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +import { foo } from "./A"; +(foo(): boolean);// Error: either Implementation ~> boolean or Definition ~> boolean + +" +`; diff --git a/tests/declaration_files_incremental_node/jsfmt.spec.js b/tests/declaration_files_incremental_node/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/declaration_files_incremental_node/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/declaration_files_incremental_node/test_absolute.js b/tests/declaration_files_incremental_node/test_absolute.js new file mode 100644 index 000000000000..ec99b67abe32 --- /dev/null +++ b/tests/declaration_files_incremental_node/test_absolute.js @@ -0,0 +1,41 @@ +/* @flow */ + +// This will require ./node_modules/B.js.flow +var B1 = require('B'); +(B1.fun(): boolean); // Error either Implementation ~> boolean or Declaration ~> boolean + +// This will require ./node_modules/B.js.flow +var B2 = require('B.js'); +(B2.fun(): boolean); // Error either Implementation ~> boolean or Declaration ~> boolean + +var C = require('package_with_full_main'); +(C.fun(): boolean); // Error either Implementation ~> boolean or Declaration ~> boolean + +var D = require('package_with_partial_main'); +(D.fun(): boolean); // Error either Implementation ~> boolean or Declaration ~> boolean + +var E = require('package_with_no_package_json'); +(E.fun(): boolean); // Error either Implementation ~> boolean or Declaration ~> boolean + +var F = require('package_with_dir_main'); +(F.fun(): boolean); // Error either Implementation ~> boolean or Declaration ~> boolean + +// This will require ./node_modules/B.js.flow +var B1 = require('B'); +(B1.fun(): boolean); // Error either Implementation ~> boolean or Declaration ~> boolean + +// This will require ./node_modules/B.js.flow +var B2 = require('B.js'); +(B2.fun(): boolean); // Error either Implementation ~> boolean or Declaration ~> boolean + +var C = require('package_with_full_main'); +(C.fun(): boolean); // Error either Implementation ~> boolean or Declaration ~> boolean + +var D = require('package_with_partial_main'); +(D.fun(): boolean); // Error either Implementation ~> boolean or Declaration ~> boolean + +var E = require('package_with_no_package_json'); +(E.fun(): boolean); // Error either Implementation ~> boolean or Declaration ~> boolean + +var F = require('package_with_dir_main'); +(F.fun(): boolean); // Error either Implementation ~> boolean or Declaration ~> boolean diff --git a/tests/declaration_files_incremental_node/test_relative.js b/tests/declaration_files_incremental_node/test_relative.js new file mode 100644 index 000000000000..abd7f0b8ca8f --- /dev/null +++ b/tests/declaration_files_incremental_node/test_relative.js @@ -0,0 +1,3 @@ +import { foo } from './A'; + +(foo(): boolean); // Error: either Implementation ~> boolean or Definition ~> boolean diff --git a/tests/declaration_files_node/A.js b/tests/declaration_files_node/A.js new file mode 100644 index 000000000000..d948c87f95a2 --- /dev/null +++ b/tests/declaration_files_node/A.js @@ -0,0 +1,3 @@ +/* @flow */ + +module.exports.fun = (): string => 'hello there!'; diff --git a/tests/declaration_files_node/CJS.js b/tests/declaration_files_node/CJS.js new file mode 100644 index 000000000000..a0dfc0144a62 --- /dev/null +++ b/tests/declaration_files_node/CJS.js @@ -0,0 +1,2 @@ +// @flow +module.exports = 42; diff --git a/tests/declaration_files_node/__snapshots__/jsfmt.spec.js.snap b/tests/declaration_files_node/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..2b9e69b03631 --- /dev/null +++ b/tests/declaration_files_node/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,91 @@ +exports[`test A.js 1`] = ` +"/* @flow */ + +module.exports.fun = (): string => 'hello there!'; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +module.exports.fun = (): string => "hello there!"; + +" +`; + +exports[`test CJS.js 1`] = ` +"// @flow +module.exports = 42; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +module.exports = 42; + +" +`; + +exports[`test test_absolute.js 1`] = ` +"/* @flow */ + +// This will require ./node_modules/B.js.flow +var B1 = require('B'); +(B1.fun(): string); // Error number ~> string + +// This will require ./node_modules/B.js.flow +var B2 = require('B.js'); +(B2.fun(): string); // Error number ~> string + +var C = require('package_with_full_main'); +(C.fun(): string); // Error number ~> string + +var D = require('package_with_partial_main'); +(D.fun(): string); // Error number ~> string + +var E = require('package_with_no_package_json'); +(E.fun(): string); // Error number ~> string + +var F = require('package_with_dir_main'); +(F.fun(): string); // Error number ~> string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +// This will require ./node_modules/B.js.flow +var B1 = require("B"); +(B1.fun(): string);// Error number ~> string +// This will require ./node_modules/B.js.flow +var B2 = require("B.js"); +(B2.fun(): string);// Error number ~> string +var C = require("package_with_full_main"); +(C.fun(): string);// Error number ~> string +var D = require("package_with_partial_main"); +(D.fun(): string);// Error number ~> string +var E = require("package_with_no_package_json"); +(E.fun(): string);// Error number ~> string +var F = require("package_with_dir_main"); +(F.fun(): string);// Error number ~> string + +" +`; + +exports[`test test_relative.js 1`] = ` +"/* @flow */ + +// This will require ./A.js.flow +var A1 = require('./A'); +(A1.fun(): string); // Error number ~> string + +// This will require ./A.js.flow +var A2 = require('./A.js'); +(A2.fun(): string); // Error number ~> string + +var CJS = require('./CJS.js'); +(CJS: string); +(CJS: number); // Error: string ~> number +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +// This will require ./A.js.flow +var A1 = require("./A"); +(A1.fun(): string);// Error number ~> string +// This will require ./A.js.flow +var A2 = require("./A.js"); +(A2.fun(): string);// Error number ~> string +var CJS = require("./CJS.js"); +(CJS: string); +(CJS: number);// Error: string ~> number + +" +`; diff --git a/tests/declaration_files_node/jsfmt.spec.js b/tests/declaration_files_node/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/declaration_files_node/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/declaration_files_node/test_absolute.js b/tests/declaration_files_node/test_absolute.js new file mode 100644 index 000000000000..cf562b0f1254 --- /dev/null +++ b/tests/declaration_files_node/test_absolute.js @@ -0,0 +1,21 @@ +/* @flow */ + +// This will require ./node_modules/B.js.flow +var B1 = require('B'); +(B1.fun(): string); // Error number ~> string + +// This will require ./node_modules/B.js.flow +var B2 = require('B.js'); +(B2.fun(): string); // Error number ~> string + +var C = require('package_with_full_main'); +(C.fun(): string); // Error number ~> string + +var D = require('package_with_partial_main'); +(D.fun(): string); // Error number ~> string + +var E = require('package_with_no_package_json'); +(E.fun(): string); // Error number ~> string + +var F = require('package_with_dir_main'); +(F.fun(): string); // Error number ~> string diff --git a/tests/declaration_files_node/test_relative.js b/tests/declaration_files_node/test_relative.js new file mode 100644 index 000000000000..340a3f68cdfe --- /dev/null +++ b/tests/declaration_files_node/test_relative.js @@ -0,0 +1,13 @@ +/* @flow */ + +// This will require ./A.js.flow +var A1 = require('./A'); +(A1.fun(): string); // Error number ~> string + +// This will require ./A.js.flow +var A2 = require('./A.js'); +(A2.fun(): string); // Error number ~> string + +var CJS = require('./CJS.js'); +(CJS: string); +(CJS: number); // Error: string ~> number diff --git a/tests/declare_class/__snapshots__/jsfmt.spec.js.snap b/tests/declare_class/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..ed66b2e693cc --- /dev/null +++ b/tests/declare_class/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,29 @@ +exports[`test declare_class.js 1`] = ` +"declare class C { + static x: number; + static foo(x: number): void; +} + +C.x = \"\"; +C.foo(\"\"); + +(C.name: string); +(C.name: number); // error, it\'s a string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/declare_class/declare_class.js b/tests/declare_class/declare_class.js new file mode 100644 index 000000000000..3096c97af9fd --- /dev/null +++ b/tests/declare_class/declare_class.js @@ -0,0 +1,10 @@ +declare class C { + static x: number; + static foo(x: number): void; +} + +C.x = ""; +C.foo(""); + +(C.name: string); +(C.name: number); // error, it's a string diff --git a/tests/declare_class/jsfmt.spec.js b/tests/declare_class/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/declare_class/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/declare_class/lib/__snapshots__/jsfmt.spec.js.snap b/tests/declare_class/lib/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..fcb6db09e975 --- /dev/null +++ b/tests/declare_class/lib/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,28 @@ +exports[`test test.js 1`] = ` +"declare class _C { + foo(): number; +} +declare var _module: { + C: Class<_C>; +} +declare class D extends _module.C { + foo(): string; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/declare_class/lib/jsfmt.spec.js b/tests/declare_class/lib/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/declare_class/lib/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/declare_class/lib/test.js b/tests/declare_class/lib/test.js new file mode 100644 index 000000000000..03ce578f62fc --- /dev/null +++ b/tests/declare_class/lib/test.js @@ -0,0 +1,9 @@ +declare class _C { + foo(): number; +} +declare var _module: { + C: Class<_C>; +} +declare class D extends _module.C { + foo(): string; +} diff --git a/tests/declare_export/B.js b/tests/declare_export/B.js new file mode 100644 index 000000000000..e4d0f1b41f44 --- /dev/null +++ b/tests/declare_export/B.js @@ -0,0 +1,3 @@ +/* @flow */ + +exports.numberValue = 42; diff --git a/tests/declare_export/C.js b/tests/declare_export/C.js new file mode 100644 index 000000000000..e667c4717b69 --- /dev/null +++ b/tests/declare_export/C.js @@ -0,0 +1 @@ +/* @flow */ diff --git a/tests/declare_export/CommonJS_Clobbering_Class.js b/tests/declare_export/CommonJS_Clobbering_Class.js new file mode 100644 index 000000000000..4fbd96118432 --- /dev/null +++ b/tests/declare_export/CommonJS_Clobbering_Class.js @@ -0,0 +1,21 @@ +/** + * @providesModule CommonJS_Clobbering_Class + * @flow + */ + +class Base { + static baseProp: number; +} + +class Test extends Base { + static childProp: number; + + static staticNumber1():number { return 1; } + static staticNumber2():number { return 2; } + static staticNumber3():number { return 3; } + + instNumber1():number { return 1; } + instNumber2():number { return 2; } +}; + +module.exports = Test; diff --git a/tests/declare_export/CommonJS_Clobbering_Lit.js b/tests/declare_export/CommonJS_Clobbering_Lit.js new file mode 100644 index 000000000000..991b7bc37c12 --- /dev/null +++ b/tests/declare_export/CommonJS_Clobbering_Lit.js @@ -0,0 +1,12 @@ +/** + * @providesModule CommonJS_Clobbering_Lit + * @flow + */ + +module.exports = { + numberValue1: 1, + numberValue2: 2, + numberValue3: 3, + numberValue4: 4, + numberValue5: 5 +}; diff --git a/tests/declare_export/CommonJS_Named.js b/tests/declare_export/CommonJS_Named.js new file mode 100644 index 000000000000..698ec4f05276 --- /dev/null +++ b/tests/declare_export/CommonJS_Named.js @@ -0,0 +1,10 @@ +/** + * @providesModule CommonJS_Named + * @flow + */ + +exports.numberValue1 = 1; +exports.numberValue2 = 2; +exports.numberValue3 = 3; +exports.numberValue4 = 4; +exports.numberValue5 = 5; diff --git a/tests/declare_export/ES6_DefaultAndNamed.js b/tests/declare_export/ES6_DefaultAndNamed.js new file mode 100644 index 000000000000..afa31fd3ea2c --- /dev/null +++ b/tests/declare_export/ES6_DefaultAndNamed.js @@ -0,0 +1,4 @@ +/* @flow */ + +declare export default number; +declare export var str: string; diff --git a/tests/declare_export/ES6_Default_AnonFunction1.js b/tests/declare_export/ES6_Default_AnonFunction1.js new file mode 100644 index 000000000000..d93d3f0a1feb --- /dev/null +++ b/tests/declare_export/ES6_Default_AnonFunction1.js @@ -0,0 +1,6 @@ +/** + * @providesModule ES6_Default_AnonFunction1 + * @flow + */ + +declare export default () => number; diff --git a/tests/declare_export/ES6_Default_AnonFunction2.js b/tests/declare_export/ES6_Default_AnonFunction2.js new file mode 100644 index 000000000000..f539ad3c1ec7 --- /dev/null +++ b/tests/declare_export/ES6_Default_AnonFunction2.js @@ -0,0 +1,6 @@ +/** + * @providesModule ES6_Default_AnonFunction2 + * @flow + */ + +declare export default () =>number; diff --git a/tests/declare_export/ES6_Default_NamedClass1.js b/tests/declare_export/ES6_Default_NamedClass1.js new file mode 100644 index 000000000000..7779c6081957 --- /dev/null +++ b/tests/declare_export/ES6_Default_NamedClass1.js @@ -0,0 +1,12 @@ +/** + * @providesModule ES6_Default_NamedClass1 + * @flow + */ + +declare export default class FooImpl { givesANum(): number; }; + +// Regression test for https://github.com/facebook/flow/issues/511 +// +// Default-exported class should also be available in local scope +declare export { FooImpl as Foo } +declare export function getAFoo(): FooImpl; diff --git a/tests/declare_export/ES6_Default_NamedClass2.js b/tests/declare_export/ES6_Default_NamedClass2.js new file mode 100644 index 000000000000..9c27f1f9b9eb --- /dev/null +++ b/tests/declare_export/ES6_Default_NamedClass2.js @@ -0,0 +1,6 @@ +/** + * @providesModule ES6_Default_NamedClass2 + * @flow + */ + +declare export default class Foo { givesANum(): number; }; diff --git a/tests/declare_export/ES6_Default_NamedFunction1.js b/tests/declare_export/ES6_Default_NamedFunction1.js new file mode 100644 index 000000000000..186dcb632069 --- /dev/null +++ b/tests/declare_export/ES6_Default_NamedFunction1.js @@ -0,0 +1,6 @@ +/** + * @providesModule ES6_Default_NamedFunction1 + * @flow + */ + +declare export default function foo():number; diff --git a/tests/declare_export/ES6_Default_NamedFunction2.js b/tests/declare_export/ES6_Default_NamedFunction2.js new file mode 100644 index 000000000000..87b3d290d93c --- /dev/null +++ b/tests/declare_export/ES6_Default_NamedFunction2.js @@ -0,0 +1,6 @@ +/** + * @providesModule ES6_Default_NamedFunction2 + * @flow + */ + +declare export default function foo():number; diff --git a/tests/declare_export/ES6_ExportAllFromMulti.js b/tests/declare_export/ES6_ExportAllFromMulti.js new file mode 100644 index 000000000000..b0f1d15ffaa4 --- /dev/null +++ b/tests/declare_export/ES6_ExportAllFromMulti.js @@ -0,0 +1,4 @@ +// @flow + +declare export * from "./ES6_ExportAllFrom_Source1"; +declare export * from "./ES6_ExportAllFrom_Source2"; diff --git a/tests/declare_export/ES6_ExportAllFrom_Intermediary1.js b/tests/declare_export/ES6_ExportAllFrom_Intermediary1.js new file mode 100644 index 000000000000..7fc67e38b8eb --- /dev/null +++ b/tests/declare_export/ES6_ExportAllFrom_Intermediary1.js @@ -0,0 +1,6 @@ +/** + * @providesModule ES6_ExportAllFrom_Intermediary1 + * @flow + */ + +declare export * from "ES6_ExportAllFrom_Source1"; diff --git a/tests/declare_export/ES6_ExportAllFrom_Intermediary2.js b/tests/declare_export/ES6_ExportAllFrom_Intermediary2.js new file mode 100644 index 000000000000..5239b4ec9b3f --- /dev/null +++ b/tests/declare_export/ES6_ExportAllFrom_Intermediary2.js @@ -0,0 +1,6 @@ +/** + * @providesModule ES6_ExportAllFrom_Intermediary2 + * @flow + */ + +declare export * from "ES6_ExportAllFrom_Source2"; diff --git a/tests/declare_export/ES6_ExportAllFrom_Source1.js b/tests/declare_export/ES6_ExportAllFrom_Source1.js new file mode 100644 index 000000000000..349a2519ab5a --- /dev/null +++ b/tests/declare_export/ES6_ExportAllFrom_Source1.js @@ -0,0 +1,6 @@ +/** + * @providesModule ES6_ExportAllFrom_Source1 + * @flow + */ + +declare export var numberValue1: number; diff --git a/tests/declare_export/ES6_ExportAllFrom_Source2.js b/tests/declare_export/ES6_ExportAllFrom_Source2.js new file mode 100644 index 000000000000..b048c4c660af --- /dev/null +++ b/tests/declare_export/ES6_ExportAllFrom_Source2.js @@ -0,0 +1,6 @@ +/** + * @providesModule ES6_ExportAllFrom_Source2 + * @flow + */ + +declare export var numberValue2: number; diff --git a/tests/declare_export/ES6_ExportFrom_Intermediary1.js b/tests/declare_export/ES6_ExportFrom_Intermediary1.js new file mode 100644 index 000000000000..42deb8f3cab4 --- /dev/null +++ b/tests/declare_export/ES6_ExportFrom_Intermediary1.js @@ -0,0 +1,9 @@ +/** + * @providesModule ES6_ExportFrom_Intermediary1 + * @flow + */ + +declare export { + numberValue1, + numberValue2 as numberValue2_renamed +} from "ES6_ExportFrom_Source1"; diff --git a/tests/declare_export/ES6_ExportFrom_Intermediary2.js b/tests/declare_export/ES6_ExportFrom_Intermediary2.js new file mode 100644 index 000000000000..bb52574f5f76 --- /dev/null +++ b/tests/declare_export/ES6_ExportFrom_Intermediary2.js @@ -0,0 +1,9 @@ +/** + * @providesModule ES6_ExportFrom_Intermediary2 + * @flow + */ + +declare export { + numberValue1, + numberValue2 as numberValue2_renamed2 +} from "ES6_ExportFrom_Source2"; diff --git a/tests/declare_export/ES6_ExportFrom_Source1.js b/tests/declare_export/ES6_ExportFrom_Source1.js new file mode 100644 index 000000000000..519dacde3334 --- /dev/null +++ b/tests/declare_export/ES6_ExportFrom_Source1.js @@ -0,0 +1,7 @@ +/** + * @providesModule ES6_ExportFrom_Source1 + * @flow + */ + +declare export var numberValue1: number; +declare export var numberValue2: number; diff --git a/tests/declare_export/ES6_ExportFrom_Source2.js b/tests/declare_export/ES6_ExportFrom_Source2.js new file mode 100644 index 000000000000..71ba5d456098 --- /dev/null +++ b/tests/declare_export/ES6_ExportFrom_Source2.js @@ -0,0 +1,7 @@ +/** + * @providesModule ES6_ExportFrom_Source2 + * @flow + */ + +declare export var numberValue1: number; +declare export var numberValue2: number; diff --git a/tests/declare_export/ES6_Named1.js b/tests/declare_export/ES6_Named1.js new file mode 100644 index 000000000000..b448ee324be3 --- /dev/null +++ b/tests/declare_export/ES6_Named1.js @@ -0,0 +1,21 @@ +/** + * @providesModule ES6_Named1 + * @flow + */ + +var specifierNumber1 = 1; +var specifierNumber2 = 2; +var specifierNumber3 = 3; +var groupedSpecifierNumber1 = 1; +var groupedSpecifierNumber2 = 2; + +declare export {specifierNumber1}; +declare export {specifierNumber2 as specifierNumber2Renamed}; +declare export {specifierNumber3}; +declare export {groupedSpecifierNumber1, groupedSpecifierNumber2}; + +declare export function givesANumber(): number; +declare export class NumberGenerator { givesANumber(): number; }; + +declare export var varDeclNumber1: number; +declare export var varDeclNumber2: number; diff --git a/tests/declare_export/ES6_Named2.js b/tests/declare_export/ES6_Named2.js new file mode 100644 index 000000000000..5f29921f40b5 --- /dev/null +++ b/tests/declare_export/ES6_Named2.js @@ -0,0 +1,19 @@ +/** + * @providesModule ES6_Named2 + * @flow + */ + +var specifierNumber4 = 1; +var specifierNumber5 = 2; +var groupedSpecifierNumber3 = 1; +var groupedSpecifierNumber4 = 2; + +declare export {specifierNumber4}; +declare export {specifierNumber5 as specifierNumber5Renamed}; +declare export {groupedSpecifierNumber3, groupedSpecifierNumber4}; + +declare export function givesANumber2(): number; +declare export class NumberGenerator2 { givesANumber(): number; }; + +declare export var varDeclNumber3: number; +declare export var varDeclNumber4: number; diff --git a/tests/declare_export/ProvidesModuleA.js b/tests/declare_export/ProvidesModuleA.js new file mode 100644 index 000000000000..f2cbd52ce005 --- /dev/null +++ b/tests/declare_export/ProvidesModuleA.js @@ -0,0 +1,10 @@ +/** + * @providesModule A + * @flow + */ + +exports.numberValue1 = 42; +exports.numberValue2 = 42; +exports.numberValue3 = 42; +exports.numberValue4 = 42; +exports.stringValue = "str"; diff --git a/tests/declare_export/ProvidesModuleCJSDefault.js b/tests/declare_export/ProvidesModuleCJSDefault.js new file mode 100644 index 000000000000..a3d9c135a2c1 --- /dev/null +++ b/tests/declare_export/ProvidesModuleCJSDefault.js @@ -0,0 +1,8 @@ +/** + * @providesModule CJSDefault + * @flow + */ + +module.exports = { + numberValue: 42 +}; diff --git a/tests/declare_export/ProvidesModuleD.js b/tests/declare_export/ProvidesModuleD.js new file mode 100644 index 000000000000..a4d5ee842062 --- /dev/null +++ b/tests/declare_export/ProvidesModuleD.js @@ -0,0 +1,4 @@ +/** + * @providesModule D + * @flow + */ diff --git a/tests/declare_export/ProvidesModuleES6Default.js b/tests/declare_export/ProvidesModuleES6Default.js new file mode 100644 index 000000000000..636a1946109c --- /dev/null +++ b/tests/declare_export/ProvidesModuleES6Default.js @@ -0,0 +1,10 @@ +/** + * @providesModule ES6Default + * @flow + */ + +/* +export default { + numberValue: 42, +}; +*/ diff --git a/tests/declare_export/SideEffects.js b/tests/declare_export/SideEffects.js new file mode 100644 index 000000000000..e667c4717b69 --- /dev/null +++ b/tests/declare_export/SideEffects.js @@ -0,0 +1 @@ +/* @flow */ diff --git a/tests/declare_export/__snapshots__/jsfmt.spec.js.snap b/tests/declare_export/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..829bbe76b0ee --- /dev/null +++ b/tests/declare_export/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,1212 @@ +exports[`test B.js 1`] = ` +"/* @flow */ + +exports.numberValue = 42; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +exports.numberValue = 42; + +" +`; + +exports[`test C.js 1`] = ` +"/* @flow */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; + +exports[`test CommonJS_Clobbering_Class.js 1`] = ` +"/** + * @providesModule CommonJS_Clobbering_Class + * @flow + */ + +class Base { + static baseProp: number; +} + +class Test extends Base { + static childProp: number; + + static staticNumber1():number { return 1; } + static staticNumber2():number { return 2; } + static staticNumber3():number { return 3; } + + instNumber1():number { return 1; } + instNumber2():number { return 2; } +}; + +module.exports = Test; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule CommonJS_Clobbering_Class + * @flow + */ +class Base { + static baseProp: number; +} +class Test extends Base { + static childProp: number; + staticNumber1(): number { + return 1; + } + staticNumber2(): number { + return 2; + } + staticNumber3(): number { + return 3; + } + instNumber1(): number { + return 1; + } + instNumber2(): number { + return 2; + } +} +module.exports = Test; + +" +`; + +exports[`test CommonJS_Clobbering_Lit.js 1`] = ` +"/** + * @providesModule CommonJS_Clobbering_Lit + * @flow + */ + +module.exports = { + numberValue1: 1, + numberValue2: 2, + numberValue3: 3, + numberValue4: 4, + numberValue5: 5 +}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule CommonJS_Clobbering_Lit + * @flow + */ +module.exports = { + numberValue1: 1, + numberValue2: 2, + numberValue3: 3, + numberValue4: 4, + numberValue5: 5 +}; + +" +`; + +exports[`test CommonJS_Named.js 1`] = ` +"/** + * @providesModule CommonJS_Named + * @flow + */ + +exports.numberValue1 = 1; +exports.numberValue2 = 2; +exports.numberValue3 = 3; +exports.numberValue4 = 4; +exports.numberValue5 = 5; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule CommonJS_Named + * @flow + */ +exports.numberValue1 = 1; +exports.numberValue2 = 2; +exports.numberValue3 = 3; +exports.numberValue4 = 4; +exports.numberValue5 = 5; + +" +`; + +exports[`test ES6_Default_AnonFunction1.js 1`] = ` +"/** + * @providesModule ES6_Default_AnonFunction1 + * @flow + */ + +declare export default () => number; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (6:8) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$1.parseExpressionStatement (/node_modules/babylon/lib/index.js:2109:8) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5246:20) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) + at Parser.parse (/node_modules/babylon/lib/index.js:1543:17) +" +`; + +exports[`test ES6_Default_AnonFunction2.js 1`] = ` +"/** + * @providesModule ES6_Default_AnonFunction2 + * @flow + */ + +declare export default () =>number; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (6:8) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$1.parseExpressionStatement (/node_modules/babylon/lib/index.js:2109:8) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5246:20) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) + at Parser.parse (/node_modules/babylon/lib/index.js:1543:17) +" +`; + +exports[`test ES6_Default_NamedClass1.js 1`] = ` +"/** + * @providesModule ES6_Default_NamedClass1 + * @flow + */ + +declare export default class FooImpl { givesANum(): number; }; + +// Regression test for https://github.com/facebook/flow/issues/511 +// +// Default-exported class should also be available in local scope +declare export { FooImpl as Foo } +declare export function getAFoo(): FooImpl; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (6:8) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$1.parseExpressionStatement (/node_modules/babylon/lib/index.js:2109:8) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5246:20) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) + at Parser.parse (/node_modules/babylon/lib/index.js:1543:17) +" +`; + +exports[`test ES6_Default_NamedClass2.js 1`] = ` +"/** + * @providesModule ES6_Default_NamedClass2 + * @flow + */ + +declare export default class Foo { givesANum(): number; }; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (6:8) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$1.parseExpressionStatement (/node_modules/babylon/lib/index.js:2109:8) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5246:20) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) + at Parser.parse (/node_modules/babylon/lib/index.js:1543:17) +" +`; + +exports[`test ES6_Default_NamedFunction1.js 1`] = ` +"/** + * @providesModule ES6_Default_NamedFunction1 + * @flow + */ + +declare export default function foo():number; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (6:8) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$1.parseExpressionStatement (/node_modules/babylon/lib/index.js:2109:8) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5246:20) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) + at Parser.parse (/node_modules/babylon/lib/index.js:1543:17) +" +`; + +exports[`test ES6_Default_NamedFunction2.js 1`] = ` +"/** + * @providesModule ES6_Default_NamedFunction2 + * @flow + */ + +declare export default function foo():number; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (6:8) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$1.parseExpressionStatement (/node_modules/babylon/lib/index.js:2109:8) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5246:20) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) + at Parser.parse (/node_modules/babylon/lib/index.js:1543:17) +" +`; + +exports[`test ES6_DefaultAndNamed.js 1`] = ` +"/* @flow */ + +declare export default number; +declare export var str: string; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (3:8) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$1.parseExpressionStatement (/node_modules/babylon/lib/index.js:2109:8) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5246:20) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) + at Parser.parse (/node_modules/babylon/lib/index.js:1543:17) +" +`; + +exports[`test ES6_ExportAllFrom_Intermediary1.js 1`] = ` +"/** + * @providesModule ES6_ExportAllFrom_Intermediary1 + * @flow + */ + +declare export * from \"ES6_ExportAllFrom_Source1\"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (6:8) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$1.parseExpressionStatement (/node_modules/babylon/lib/index.js:2109:8) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5246:20) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) + at Parser.parse (/node_modules/babylon/lib/index.js:1543:17) +" +`; + +exports[`test ES6_ExportAllFrom_Intermediary2.js 1`] = ` +"/** + * @providesModule ES6_ExportAllFrom_Intermediary2 + * @flow + */ + +declare export * from \"ES6_ExportAllFrom_Source2\"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (6:8) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$1.parseExpressionStatement (/node_modules/babylon/lib/index.js:2109:8) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5246:20) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) + at Parser.parse (/node_modules/babylon/lib/index.js:1543:17) +" +`; + +exports[`test ES6_ExportAllFrom_Source1.js 1`] = ` +"/** + * @providesModule ES6_ExportAllFrom_Source1 + * @flow + */ + +declare export var numberValue1: number; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (6:8) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$1.parseExpressionStatement (/node_modules/babylon/lib/index.js:2109:8) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5246:20) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) + at Parser.parse (/node_modules/babylon/lib/index.js:1543:17) +" +`; + +exports[`test ES6_ExportAllFrom_Source2.js 1`] = ` +"/** + * @providesModule ES6_ExportAllFrom_Source2 + * @flow + */ + +declare export var numberValue2: number; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (6:8) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$1.parseExpressionStatement (/node_modules/babylon/lib/index.js:2109:8) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5246:20) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) + at Parser.parse (/node_modules/babylon/lib/index.js:1543:17) +" +`; + +exports[`test ES6_ExportAllFromMulti.js 1`] = ` +"// @flow + +declare export * from \"./ES6_ExportAllFrom_Source1\"; +declare export * from \"./ES6_ExportAllFrom_Source2\"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (3:8) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$1.parseExpressionStatement (/node_modules/babylon/lib/index.js:2109:8) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5246:20) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) + at Parser.parse (/node_modules/babylon/lib/index.js:1543:17) +" +`; + +exports[`test ES6_ExportFrom_Intermediary1.js 1`] = ` +"/** + * @providesModule ES6_ExportFrom_Intermediary1 + * @flow + */ + +declare export { + numberValue1, + numberValue2 as numberValue2_renamed +} from \"ES6_ExportFrom_Source1\"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (6:8) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$1.parseExpressionStatement (/node_modules/babylon/lib/index.js:2109:8) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5246:20) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) + at Parser.parse (/node_modules/babylon/lib/index.js:1543:17) +" +`; + +exports[`test ES6_ExportFrom_Intermediary2.js 1`] = ` +"/** + * @providesModule ES6_ExportFrom_Intermediary2 + * @flow + */ + +declare export { + numberValue1, + numberValue2 as numberValue2_renamed2 +} from \"ES6_ExportFrom_Source2\"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (6:8) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$1.parseExpressionStatement (/node_modules/babylon/lib/index.js:2109:8) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5246:20) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) + at Parser.parse (/node_modules/babylon/lib/index.js:1543:17) +" +`; + +exports[`test ES6_ExportFrom_Source1.js 1`] = ` +"/** + * @providesModule ES6_ExportFrom_Source1 + * @flow + */ + +declare export var numberValue1: number; +declare export var numberValue2: number; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (6:8) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$1.parseExpressionStatement (/node_modules/babylon/lib/index.js:2109:8) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5246:20) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) + at Parser.parse (/node_modules/babylon/lib/index.js:1543:17) +" +`; + +exports[`test ES6_ExportFrom_Source2.js 1`] = ` +"/** + * @providesModule ES6_ExportFrom_Source2 + * @flow + */ + +declare export var numberValue1: number; +declare export var numberValue2: number; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (6:8) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$1.parseExpressionStatement (/node_modules/babylon/lib/index.js:2109:8) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5246:20) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) + at Parser.parse (/node_modules/babylon/lib/index.js:1543:17) +" +`; + +exports[`test ES6_Named1.js 1`] = ` +"/** + * @providesModule ES6_Named1 + * @flow + */ + +var specifierNumber1 = 1; +var specifierNumber2 = 2; +var specifierNumber3 = 3; +var groupedSpecifierNumber1 = 1; +var groupedSpecifierNumber2 = 2; + +declare export {specifierNumber1}; +declare export {specifierNumber2 as specifierNumber2Renamed}; +declare export {specifierNumber3}; +declare export {groupedSpecifierNumber1, groupedSpecifierNumber2}; + +declare export function givesANumber(): number; +declare export class NumberGenerator { givesANumber(): number; }; + +declare export var varDeclNumber1: number; +declare export var varDeclNumber2: number; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (12:8) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$1.parseExpressionStatement (/node_modules/babylon/lib/index.js:2109:8) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5246:20) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) + at Parser.parse (/node_modules/babylon/lib/index.js:1543:17) +" +`; + +exports[`test ES6_Named2.js 1`] = ` +"/** + * @providesModule ES6_Named2 + * @flow + */ + +var specifierNumber4 = 1; +var specifierNumber5 = 2; +var groupedSpecifierNumber3 = 1; +var groupedSpecifierNumber4 = 2; + +declare export {specifierNumber4}; +declare export {specifierNumber5 as specifierNumber5Renamed}; +declare export {groupedSpecifierNumber3, groupedSpecifierNumber4}; + +declare export function givesANumber2(): number; +declare export class NumberGenerator2 { givesANumber(): number; }; + +declare export var varDeclNumber3: number; +declare export var varDeclNumber4: number; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (11:8) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$1.parseExpressionStatement (/node_modules/babylon/lib/index.js:2109:8) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5246:20) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) + at Parser.parse (/node_modules/babylon/lib/index.js:1543:17) +" +`; + +exports[`test ProvidesModuleA.js 1`] = ` +"/** + * @providesModule A + * @flow + */ + +exports.numberValue1 = 42; +exports.numberValue2 = 42; +exports.numberValue3 = 42; +exports.numberValue4 = 42; +exports.stringValue = \"str\"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule A + * @flow + */ +exports.numberValue1 = 42; +exports.numberValue2 = 42; +exports.numberValue3 = 42; +exports.numberValue4 = 42; +exports.stringValue = \"str\"; + +" +`; + +exports[`test ProvidesModuleCJSDefault.js 1`] = ` +"/** + * @providesModule CJSDefault + * @flow + */ + +module.exports = { + numberValue: 42 +}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule CJSDefault + * @flow + */ +module.exports = { numberValue: 42 }; + +" +`; + +exports[`test ProvidesModuleD.js 1`] = ` +"/** + * @providesModule D + * @flow + */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; + +exports[`test ProvidesModuleES6Default.js 1`] = ` +"/** + * @providesModule ES6Default + * @flow + */ + +/* +export default { + numberValue: 42, +}; +*/ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; + +exports[`test SideEffects.js 1`] = ` +"/* @flow */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; + +exports[`test es6modules.js 1`] = ` +"/* @flow */ + +// ===================== // +// == Path Resolution == // +// ===================== // + +// @providesModule +import * as DefaultA from \"A\"; +var a1: number = DefaultA.numberValue1; +var a2: string = DefaultA.numberValue1; // Error: number ~> string + +// File path +import * as DefaultB from \"./B\"; +var b1: number = DefaultB.numberValue; +var b2: string = DefaultB.numberValue; // Error: number ~> string + +// C.js exists, but not as a providesModule +import DefaultC from \"C\"; // Error: No such module + +// @providesModule D exists, but not as a filename +import DefaultD from \"./D\"; // Error: No such module + +// ================================================ // +// == CommonJS Clobbering Literal Exports -> ES6 == // +// ================================================ // + +import {doesntExist1} from \"CommonJS_Clobbering_Lit\"; // Error: Not an exported binding + +import {numberValue1} from \"CommonJS_Clobbering_Lit\"; +var c1: number = numberValue1; +var c2: string = numberValue1; // Error: number ~> string + +import {numberValue2 as numVal1} from \"CommonJS_Clobbering_Lit\"; +var d1: number = numVal1; +var d2: string = numVal1; // Error: number ~> string + +import CJS_Clobb_Lit from \"CommonJS_Clobbering_Lit\"; +var e1: number = CJS_Clobb_Lit.numberValue3; +var e2: string = CJS_Clobb_Lit.numberValue3; // Error: number ~> string +CJS_Clobb_Lit.doesntExist; // Error: doesntExist isn\'t a property + +import * as CJS_Clobb_Lit_NS from \"CommonJS_Clobbering_Lit\"; +var f1: number = CJS_Clobb_Lit_NS.numberValue4; +var f2: number = CJS_Clobb_Lit_NS.default.numberValue4; +CJS_Clobb_Lit_NS.default.default; // Error: No \'default\' property on the exported obj +var f3: string = CJS_Clobb_Lit_NS.numberValue4; // Error: number ~> string +var f4: string = CJS_Clobb_Lit_NS.default.numberValue5; // Error: number ~> string + +// ============================================== // +// == CommonJS Clobbering Class Exports -> ES6 == // +// ============================================== // + +import {doesntExist2} from \"CommonJS_Clobbering_Class\"; // Error: Not an exported binding + +// The following import should error because class statics are not turned into +// named exports for now. This avoids complexities with polymorphic static +// members (where the polymophism is defined on the class itself rather than the +// method). +import {staticNumber1, baseProp, childProp} from \"CommonJS_Clobbering_Class\"; // Error + +import CJS_Clobb_Class from \"CommonJS_Clobbering_Class\"; +new CJS_Clobb_Class(); +new CJS_Clobb_Class().doesntExist; // Error: Class has no \`doesntExist\` property +var h1: number = CJS_Clobb_Class.staticNumber2(); +var h2: string = CJS_Clobb_Class.staticNumber2(); // Error: number ~> string +var h3: number = new CJS_Clobb_Class().instNumber1(); +var h4: string = new CJS_Clobb_Class().instNumber1(); // Error: number ~> string + +import * as CJS_Clobb_Class_NS from \"CommonJS_Clobbering_Class\"; +new CJS_Clobb_Class_NS(); // Error: Namespace object isn\'t constructable +var i1: number = CJS_Clobb_Class_NS.staticNumber3(); // Error: Class statics not copied to Namespace object +var i2: number = new CJS_Clobb_Class_NS.default().instNumber2(); +var i3: string = new CJS_Clobb_Class_NS.default().instNumber2(); // Error: number ~> string + +// =================================== // +// == CommonJS Named Exports -> ES6 == // +// =================================== // + +import {doesntExist3} from \"CommonJS_Named\"; // Error: Not an exported binding + +import {numberValue2} from \"CommonJS_Named\"; +var j1: number = numberValue2; +var j2: string = numberValue2; // Error: number ~> string + +import {numberValue3 as numVal3} from \"CommonJS_Named\"; +var k1: number = numVal3; +var k2: string = numVal3; // Error: number ~> string + +import * as CJS_Named from \"CommonJS_Named\"; +var l1: number = CJS_Named.numberValue1; +var l2: string = CJS_Named.numberValue1; // Error: number ~> string +CJS_Named.doesntExist; // Error: doesntExist isn\'t a property + +import * as CJS_Named_NS from \"CommonJS_Named\"; +var m1: number = CJS_Named_NS.numberValue4; +var m2: string = CJS_Named_NS.default.numberValue4; // Error: CommonJS_Named has no default export +var m3: string = CJS_Named_NS.numberValue4; // Error: number ~> string + +////////////////////////////// +// == ES6 Default -> ES6 == // +////////////////////////////// + +import {doesntExist4} from \"ES6_Default_AnonFunction1\"; // Error: Not an exported binding + +import ES6_Def_AnonFunc1 from \"ES6_Default_AnonFunction1\"; +var n1: number = ES6_Def_AnonFunc1(); +var n2: string = ES6_Def_AnonFunc1(); // Error: number ~> string + +import ES6_Def_NamedFunc1 from \"ES6_Default_NamedFunction1\"; +var o1: number = ES6_Def_NamedFunc1(); +var o2: string = ES6_Def_NamedFunc1(); // Error: number ~> string + + + + + +import ES6_Def_NamedClass1 from \"ES6_Default_NamedClass1\"; +var q1: number = new ES6_Def_NamedClass1().givesANum(); +var q2: string = new ES6_Def_NamedClass1().givesANum(); // Error: number ~> string + +//////////////////////////// +// == ES6 Named -> ES6 == // +//////////////////////////// + +import doesntExist5 from \"ES6_Named1\"; // Error: Not an exported binding + +import {specifierNumber1 as specifierNumber1_1} from \"ES6_Named1\"; +var r1: number = specifierNumber1_1; +var r2: string = specifierNumber1_1; // Error: number ~> string + +import {specifierNumber2Renamed} from \"ES6_Named1\"; +var s1: number = specifierNumber2Renamed; +var s2: string = specifierNumber2Renamed; // Error: number ~> string + +import {specifierNumber3 as specifierNumber3Renamed} from \"ES6_Named1\"; +var t1: number = specifierNumber3Renamed; +var t2: string = specifierNumber3Renamed; // Error: number ~> string + +import {groupedSpecifierNumber1, groupedSpecifierNumber2} from \"ES6_Named1\"; +var u1: number = groupedSpecifierNumber1; +var u2: number = groupedSpecifierNumber2; +var u3: string = groupedSpecifierNumber1; // Error: number ~> string +var u4: string = groupedSpecifierNumber2; // Error: number ~> string + +import {givesANumber} from \"ES6_Named1\"; +var v1: number = givesANumber(); +var v2: string = givesANumber(); // Error: number ~> string + +import {NumberGenerator} from \"ES6_Named1\"; +var w1: number = new NumberGenerator().givesANumber(); +var w2: string = new NumberGenerator().givesANumber(); // Error: number ~> string + +import {varDeclNumber1, varDeclNumber2} from \"ES6_Named1\"; +var x1: number = varDeclNumber1; +var x2: number = varDeclNumber2; +var x3: string = varDeclNumber1; // Error: number ~> string +var x4: string = varDeclNumber2; // Error: number ~> string + + + + + + + + + +import {numberValue1 as numberValue4} from \"ES6_ExportFrom_Intermediary1\"; +var aa1: number = numberValue4; +var aa2: string = numberValue4; // Error: number ~> string + +import {numberValue2_renamed} from \"ES6_ExportFrom_Intermediary1\"; +var ab1: number = numberValue2_renamed; +var ab2: string = numberValue2_renamed; // Error: number ~> string + +import {numberValue1 as numberValue5} from \"ES6_ExportAllFrom_Intermediary1\"; +var ac1: number = numberValue5; +var ac2: string = numberValue5; // Error: number ~> string + +/////////////////////////////////// +// == ES6 Default -> CommonJS == // +/////////////////////////////////// + +require(\'ES6_Default_AnonFunction2\').doesntExist; // Error: \'doesntExist\' isn\'t an export + +var ES6_Def_AnonFunc2 = require(\"ES6_Default_AnonFunction2\").default; +var ad1: number = ES6_Def_AnonFunc2(); +var ad2: string = ES6_Def_AnonFunc2(); // Error: number ~> string + +var ES6_Def_NamedFunc2 = require(\"ES6_Default_NamedFunction2\").default; +var ae1: number = ES6_Def_NamedFunc2(); +var ae2: string = ES6_Def_NamedFunc2(); // Error: number ~> string + + + + + +var ES6_Def_NamedClass2 = require(\"ES6_Default_NamedClass2\").default; +var ag1: number = new ES6_Def_NamedClass2().givesANum(); +var ag2: string = new ES6_Def_NamedClass2().givesANum(); // Error: number ~> string + +///////////////////////////////// +// == ES6 Named -> CommonJS == // +///////////////////////////////// + +var specifierNumber4 = require(\"ES6_Named2\").specifierNumber4; +var ah1: number = specifierNumber4; +var ah2: string = specifierNumber4; // Error: number ~> string + +var specifierNumber5Renamed = require(\"ES6_Named2\").specifierNumber5Renamed; +var ai1: number = specifierNumber5Renamed; +var ai2: string = specifierNumber5Renamed; // Error: number ~> string + +var groupedSpecifierNumber3 = require(\"ES6_Named2\").groupedSpecifierNumber3; +var groupedSpecifierNumber4 = require(\"ES6_Named2\").groupedSpecifierNumber4; +var aj1: number = groupedSpecifierNumber3; +var aj2: number = groupedSpecifierNumber4; +var aj3: string = groupedSpecifierNumber3; // Error: number ~> string +var aj4: string = groupedSpecifierNumber4; // Error: number ~> string + +var givesANumber2 = require(\"ES6_Named2\").givesANumber2; +var ak1: number = givesANumber2(); +var ak2: string = givesANumber2(); // Error: number ~> string + +var NumberGenerator2 = require(\"ES6_Named2\").NumberGenerator2; +var al1: number = new NumberGenerator2().givesANumber(); +var al2: string = new NumberGenerator2().givesANumber(); // Error: number ~> string + +var varDeclNumber3 = require(\"ES6_Named2\").varDeclNumber3; +var varDeclNumber4 = require(\"ES6_Named2\").varDeclNumber4; +var am1: number = varDeclNumber3; +var am2: number = varDeclNumber4; +var am3: string = varDeclNumber3; // Error: number ~> string +var am4: string = varDeclNumber4; // Error: number ~> string + + + + + + + + + +var numberValue6 = require(\"ES6_ExportFrom_Intermediary2\").numberValue1; +var ap1: number = numberValue6; +var ap2: string = numberValue6; // Error: number ~> string + +var numberValue2_renamed2 = require(\"ES6_ExportFrom_Intermediary2\").numberValue2_renamed2; +var aq1: number = numberValue2_renamed2; +var aq2: string = numberValue2_renamed2; // Error: number ~> string + +var numberValue7 = require(\"ES6_ExportAllFrom_Intermediary2\").numberValue2; +var ar1: number = numberValue7; +var ar2: string = numberValue7; // Error: number ~> string + +//////////////////////////////////////////////////////// +// == ES6 Default+Named -> ES6 import Default+Named== // +//////////////////////////////////////////////////////// + +import defaultNum, {str as namedStr} from \"./ES6_DefaultAndNamed\"; + +var as1: number = defaultNum; +var as2: string = defaultNum; // Error: number ~> string + +var as3: string = namedStr; +var as4: number = namedStr; // Error: string ~> number + +//////////////////////////////////////// +// == Side-effect only ES6 imports == // +//////////////////////////////////////// + +import \"./SideEffects\"; + +////////////////////////////////////////////// +// == Suggest export name on likely typo == // +////////////////////////////////////////////// +import specifierNumber1 from \"ES6_Named1\"; // Error: Did you mean \`import {specifierNumber1} from ...\`? +import {specifierNumber} from \"ES6_Named1\"; // Error: Did you mean \`specifierNumber1\`? + +/////////////////////////////////////////////////// +// == Multi \`export *\` should combine exports == // +/////////////////////////////////////////////////// +import { + numberValue1 as numberValue8, + numberValue2 as numberValue9 +} from \"./ES6_ExportAllFromMulti\"; + +var at1: number = numberValue8; +var at2: string = numberValue8; // Error: number ~> string + +var at3: number = numberValue9; +var at4: string = numberValue9; // Error: number ~> string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +// ===================== // +// == Path Resolution == // +// ===================== // +// @providesModule +import * as DefaultA from \"A\"; +var a1: number = DefaultA.numberValue1; +var a2: string = DefaultA.numberValue1;// Error: number ~> string +// File path +import * as DefaultB from \"./B\"; +var b1: number = DefaultB.numberValue; +var b2: string = DefaultB.numberValue;// Error: number ~> string +// C.js exists, but not as a providesModule +import DefaultC from \"C\";// Error: No such module +// @providesModule D exists, but not as a filename +import DefaultD from \"./D\";// Error: No such module +// ================================================ // +// == CommonJS Clobbering Literal Exports -> ES6 == // +// ================================================ // +import { doesntExist1 } from \"CommonJS_Clobbering_Lit\";// Error: Not an exported binding +import { numberValue1 } from \"CommonJS_Clobbering_Lit\"; +var c1: number = numberValue1; +var c2: string = numberValue1;// Error: number ~> string +import { numberValue2 as numVal1 } from \"CommonJS_Clobbering_Lit\"; +var d1: number = numVal1; +var d2: string = numVal1;// Error: number ~> string +import CJS_Clobb_Lit from \"CommonJS_Clobbering_Lit\"; +var e1: number = CJS_Clobb_Lit.numberValue3; +var e2: string = CJS_Clobb_Lit.numberValue3;// Error: number ~> string +CJS_Clobb_Lit.doesntExist;// Error: doesntExist isn\'t a property +import * as CJS_Clobb_Lit_NS from \"CommonJS_Clobbering_Lit\"; +var f1: number = CJS_Clobb_Lit_NS.numberValue4; +var f2: number = CJS_Clobb_Lit_NS.default.numberValue4; +CJS_Clobb_Lit_NS.default.default;// Error: No \'default\' property on the exported obj +var f3: string = CJS_Clobb_Lit_NS.numberValue4;// Error: number ~> string +var f4: string = CJS_Clobb_Lit_NS.default.numberValue5;// Error: number ~> string +// ============================================== // +// == CommonJS Clobbering Class Exports -> ES6 == // +// ============================================== // +import { doesntExist2 } from \"CommonJS_Clobbering_Class\";// Error: Not an exported binding +// The following import should error because class statics are not turned into +// named exports for now. This avoids complexities with polymorphic static +// members (where the polymophism is defined on the class itself rather than the +// method). +import { staticNumber1, baseProp, childProp } from \"CommonJS_Clobbering_Class\";// Error +import CJS_Clobb_Class from \"CommonJS_Clobbering_Class\"; +new CJS_Clobb_Class(); +new CJS_Clobb_Class().doesntExist;// Error: Class has no \`doesntExist\` property +var h1: number = CJS_Clobb_Class.staticNumber2(); +var h2: string = CJS_Clobb_Class.staticNumber2();// Error: number ~> string +var h3: number = new CJS_Clobb_Class().instNumber1(); +var h4: string = new CJS_Clobb_Class().instNumber1();// Error: number ~> string +import * as CJS_Clobb_Class_NS from \"CommonJS_Clobbering_Class\"; +new CJS_Clobb_Class_NS();// Error: Namespace object isn\'t constructable +var i1: number = CJS_Clobb_Class_NS.staticNumber3();// Error: Class statics not copied to Namespace object +var i2: number = new CJS_Clobb_Class_NS.default().instNumber2(); +var i3: string = new CJS_Clobb_Class_NS.default().instNumber2();// Error: number ~> string +// =================================== // +// == CommonJS Named Exports -> ES6 == // +// =================================== // +import { doesntExist3 } from \"CommonJS_Named\";// Error: Not an exported binding +import { numberValue2 } from \"CommonJS_Named\"; +var j1: number = numberValue2; +var j2: string = numberValue2;// Error: number ~> string +import { numberValue3 as numVal3 } from \"CommonJS_Named\"; +var k1: number = numVal3; +var k2: string = numVal3;// Error: number ~> string +import * as CJS_Named from \"CommonJS_Named\"; +var l1: number = CJS_Named.numberValue1; +var l2: string = CJS_Named.numberValue1;// Error: number ~> string +CJS_Named.doesntExist;// Error: doesntExist isn\'t a property +import * as CJS_Named_NS from \"CommonJS_Named\"; +var m1: number = CJS_Named_NS.numberValue4; +var m2: string = CJS_Named_NS.default.numberValue4;// Error: CommonJS_Named has no default export +var m3: string = CJS_Named_NS.numberValue4;// Error: number ~> string +////////////////////////////// +// == ES6 Default -> ES6 == // +////////////////////////////// +import { doesntExist4 } from \"ES6_Default_AnonFunction1\";// Error: Not an exported binding +import ES6_Def_AnonFunc1 from \"ES6_Default_AnonFunction1\"; +var n1: number = ES6_Def_AnonFunc1(); +var n2: string = ES6_Def_AnonFunc1();// Error: number ~> string +import ES6_Def_NamedFunc1 from \"ES6_Default_NamedFunction1\"; +var o1: number = ES6_Def_NamedFunc1(); +var o2: string = ES6_Def_NamedFunc1();// Error: number ~> string +import ES6_Def_NamedClass1 from \"ES6_Default_NamedClass1\"; +var q1: number = new ES6_Def_NamedClass1().givesANum(); +var q2: string = new ES6_Def_NamedClass1().givesANum();// Error: number ~> string +//////////////////////////// +// == ES6 Named -> ES6 == // +//////////////////////////// +import doesntExist5 from \"ES6_Named1\";// Error: Not an exported binding +import { specifierNumber1 as specifierNumber1_1 } from \"ES6_Named1\"; +var r1: number = specifierNumber1_1; +var r2: string = specifierNumber1_1;// Error: number ~> string +import { specifierNumber2Renamed } from \"ES6_Named1\"; +var s1: number = specifierNumber2Renamed; +var s2: string = specifierNumber2Renamed;// Error: number ~> string +import { specifierNumber3 as specifierNumber3Renamed } from \"ES6_Named1\"; +var t1: number = specifierNumber3Renamed; +var t2: string = specifierNumber3Renamed;// Error: number ~> string +import { groupedSpecifierNumber1, groupedSpecifierNumber2 } from \"ES6_Named1\"; +var u1: number = groupedSpecifierNumber1; +var u2: number = groupedSpecifierNumber2; +var u3: string = groupedSpecifierNumber1;// Error: number ~> string +var u4: string = groupedSpecifierNumber2;// Error: number ~> string +import { givesANumber } from \"ES6_Named1\"; +var v1: number = givesANumber(); +var v2: string = givesANumber();// Error: number ~> string +import { NumberGenerator } from \"ES6_Named1\"; +var w1: number = new NumberGenerator().givesANumber(); +var w2: string = new NumberGenerator().givesANumber();// Error: number ~> string +import { varDeclNumber1, varDeclNumber2 } from \"ES6_Named1\"; +var x1: number = varDeclNumber1; +var x2: number = varDeclNumber2; +var x3: string = varDeclNumber1;// Error: number ~> string +var x4: string = varDeclNumber2;// Error: number ~> string +import { numberValue1 as numberValue4 } from \"ES6_ExportFrom_Intermediary1\"; +var aa1: number = numberValue4; +var aa2: string = numberValue4;// Error: number ~> string +import { numberValue2_renamed } from \"ES6_ExportFrom_Intermediary1\"; +var ab1: number = numberValue2_renamed; +var ab2: string = numberValue2_renamed;// Error: number ~> string +import { numberValue1 as numberValue5 } from \"ES6_ExportAllFrom_Intermediary1\"; +var ac1: number = numberValue5; +var ac2: string = numberValue5;// Error: number ~> string +/////////////////////////////////// +// == ES6 Default -> CommonJS == // +/////////////////////////////////// +require( + \"ES6_Default_AnonFunction2\" +).doesntExist;// Error: \'doesntExist\' isn\'t an export +var ES6_Def_AnonFunc2 = require(\"ES6_Default_AnonFunction2\").default; +var ad1: number = ES6_Def_AnonFunc2(); +var ad2: string = ES6_Def_AnonFunc2();// Error: number ~> string +var ES6_Def_NamedFunc2 = require(\"ES6_Default_NamedFunction2\").default; +var ae1: number = ES6_Def_NamedFunc2(); +var ae2: string = ES6_Def_NamedFunc2();// Error: number ~> string +var ES6_Def_NamedClass2 = require(\"ES6_Default_NamedClass2\").default; +var ag1: number = new ES6_Def_NamedClass2().givesANum(); +var ag2: string = new ES6_Def_NamedClass2().givesANum();// Error: number ~> string +///////////////////////////////// +// == ES6 Named -> CommonJS == // +///////////////////////////////// +var specifierNumber4 = require(\"ES6_Named2\").specifierNumber4; +var ah1: number = specifierNumber4; +var ah2: string = specifierNumber4;// Error: number ~> string +var specifierNumber5Renamed = require(\"ES6_Named2\").specifierNumber5Renamed; +var ai1: number = specifierNumber5Renamed; +var ai2: string = specifierNumber5Renamed;// Error: number ~> string +var groupedSpecifierNumber3 = require(\"ES6_Named2\").groupedSpecifierNumber3; +var groupedSpecifierNumber4 = require(\"ES6_Named2\").groupedSpecifierNumber4; +var aj1: number = groupedSpecifierNumber3; +var aj2: number = groupedSpecifierNumber4; +var aj3: string = groupedSpecifierNumber3;// Error: number ~> string +var aj4: string = groupedSpecifierNumber4;// Error: number ~> string +var givesANumber2 = require(\"ES6_Named2\").givesANumber2; +var ak1: number = givesANumber2(); +var ak2: string = givesANumber2();// Error: number ~> string +var NumberGenerator2 = require(\"ES6_Named2\").NumberGenerator2; +var al1: number = new NumberGenerator2().givesANumber(); +var al2: string = new NumberGenerator2().givesANumber();// Error: number ~> string +var varDeclNumber3 = require(\"ES6_Named2\").varDeclNumber3; +var varDeclNumber4 = require(\"ES6_Named2\").varDeclNumber4; +var am1: number = varDeclNumber3; +var am2: number = varDeclNumber4; +var am3: string = varDeclNumber3;// Error: number ~> string +var am4: string = varDeclNumber4;// Error: number ~> string +var numberValue6 = require(\"ES6_ExportFrom_Intermediary2\").numberValue1; +var ap1: number = numberValue6; +var ap2: string = numberValue6;// Error: number ~> string +var numberValue2_renamed2 = require( + \"ES6_ExportFrom_Intermediary2\" +).numberValue2_renamed2; +var aq1: number = numberValue2_renamed2; +var aq2: string = numberValue2_renamed2;// Error: number ~> string +var numberValue7 = require(\"ES6_ExportAllFrom_Intermediary2\").numberValue2; +var ar1: number = numberValue7; +var ar2: string = numberValue7;// Error: number ~> string +//////////////////////////////////////////////////////// +// == ES6 Default+Named -> ES6 import Default+Named== // +//////////////////////////////////////////////////////// +import defaultNum, { str as namedStr } from \"./ES6_DefaultAndNamed\"; +var as1: number = defaultNum; +var as2: string = defaultNum;// Error: number ~> string +var as3: string = namedStr; +var as4: number = namedStr;// Error: string ~> number +//////////////////////////////////////// +// == Side-effect only ES6 imports == // +//////////////////////////////////////// +import \"./SideEffects\"; +////////////////////////////////////////////// +// == Suggest export name on likely typo == // +////////////////////////////////////////////// +import specifierNumber1 from \"ES6_Named1\";// Error: Did you mean \`import {specifierNumber1} from ...\`? +import { specifierNumber } from \"ES6_Named1\";// Error: Did you mean \`specifierNumber1\`? +/////////////////////////////////////////////////// +// == Multi \`export *\` should combine exports == // +/////////////////////////////////////////////////// +import { numberValue1 as numberValue8, numberValue2 as numberValue9 } from \"./ES6_ExportAllFromMulti\"; +var at1: number = numberValue8; +var at2: string = numberValue8;// Error: number ~> string +var at3: number = numberValue9; +var at4: string = numberValue9;// Error: number ~> string + +" +`; diff --git a/tests/declare_export/es6modules.js b/tests/declare_export/es6modules.js new file mode 100644 index 000000000000..a6a52fe441cb --- /dev/null +++ b/tests/declare_export/es6modules.js @@ -0,0 +1,291 @@ +/* @flow */ + +// ===================== // +// == Path Resolution == // +// ===================== // + +// @providesModule +import * as DefaultA from "A"; +var a1: number = DefaultA.numberValue1; +var a2: string = DefaultA.numberValue1; // Error: number ~> string + +// File path +import * as DefaultB from "./B"; +var b1: number = DefaultB.numberValue; +var b2: string = DefaultB.numberValue; // Error: number ~> string + +// C.js exists, but not as a providesModule +import DefaultC from "C"; // Error: No such module + +// @providesModule D exists, but not as a filename +import DefaultD from "./D"; // Error: No such module + +// ================================================ // +// == CommonJS Clobbering Literal Exports -> ES6 == // +// ================================================ // + +import {doesntExist1} from "CommonJS_Clobbering_Lit"; // Error: Not an exported binding + +import {numberValue1} from "CommonJS_Clobbering_Lit"; +var c1: number = numberValue1; +var c2: string = numberValue1; // Error: number ~> string + +import {numberValue2 as numVal1} from "CommonJS_Clobbering_Lit"; +var d1: number = numVal1; +var d2: string = numVal1; // Error: number ~> string + +import CJS_Clobb_Lit from "CommonJS_Clobbering_Lit"; +var e1: number = CJS_Clobb_Lit.numberValue3; +var e2: string = CJS_Clobb_Lit.numberValue3; // Error: number ~> string +CJS_Clobb_Lit.doesntExist; // Error: doesntExist isn't a property + +import * as CJS_Clobb_Lit_NS from "CommonJS_Clobbering_Lit"; +var f1: number = CJS_Clobb_Lit_NS.numberValue4; +var f2: number = CJS_Clobb_Lit_NS.default.numberValue4; +CJS_Clobb_Lit_NS.default.default; // Error: No 'default' property on the exported obj +var f3: string = CJS_Clobb_Lit_NS.numberValue4; // Error: number ~> string +var f4: string = CJS_Clobb_Lit_NS.default.numberValue5; // Error: number ~> string + +// ============================================== // +// == CommonJS Clobbering Class Exports -> ES6 == // +// ============================================== // + +import {doesntExist2} from "CommonJS_Clobbering_Class"; // Error: Not an exported binding + +// The following import should error because class statics are not turned into +// named exports for now. This avoids complexities with polymorphic static +// members (where the polymophism is defined on the class itself rather than the +// method). +import {staticNumber1, baseProp, childProp} from "CommonJS_Clobbering_Class"; // Error + +import CJS_Clobb_Class from "CommonJS_Clobbering_Class"; +new CJS_Clobb_Class(); +new CJS_Clobb_Class().doesntExist; // Error: Class has no `doesntExist` property +var h1: number = CJS_Clobb_Class.staticNumber2(); +var h2: string = CJS_Clobb_Class.staticNumber2(); // Error: number ~> string +var h3: number = new CJS_Clobb_Class().instNumber1(); +var h4: string = new CJS_Clobb_Class().instNumber1(); // Error: number ~> string + +import * as CJS_Clobb_Class_NS from "CommonJS_Clobbering_Class"; +new CJS_Clobb_Class_NS(); // Error: Namespace object isn't constructable +var i1: number = CJS_Clobb_Class_NS.staticNumber3(); // Error: Class statics not copied to Namespace object +var i2: number = new CJS_Clobb_Class_NS.default().instNumber2(); +var i3: string = new CJS_Clobb_Class_NS.default().instNumber2(); // Error: number ~> string + +// =================================== // +// == CommonJS Named Exports -> ES6 == // +// =================================== // + +import {doesntExist3} from "CommonJS_Named"; // Error: Not an exported binding + +import {numberValue2} from "CommonJS_Named"; +var j1: number = numberValue2; +var j2: string = numberValue2; // Error: number ~> string + +import {numberValue3 as numVal3} from "CommonJS_Named"; +var k1: number = numVal3; +var k2: string = numVal3; // Error: number ~> string + +import * as CJS_Named from "CommonJS_Named"; +var l1: number = CJS_Named.numberValue1; +var l2: string = CJS_Named.numberValue1; // Error: number ~> string +CJS_Named.doesntExist; // Error: doesntExist isn't a property + +import * as CJS_Named_NS from "CommonJS_Named"; +var m1: number = CJS_Named_NS.numberValue4; +var m2: string = CJS_Named_NS.default.numberValue4; // Error: CommonJS_Named has no default export +var m3: string = CJS_Named_NS.numberValue4; // Error: number ~> string + +////////////////////////////// +// == ES6 Default -> ES6 == // +////////////////////////////// + +import {doesntExist4} from "ES6_Default_AnonFunction1"; // Error: Not an exported binding + +import ES6_Def_AnonFunc1 from "ES6_Default_AnonFunction1"; +var n1: number = ES6_Def_AnonFunc1(); +var n2: string = ES6_Def_AnonFunc1(); // Error: number ~> string + +import ES6_Def_NamedFunc1 from "ES6_Default_NamedFunction1"; +var o1: number = ES6_Def_NamedFunc1(); +var o2: string = ES6_Def_NamedFunc1(); // Error: number ~> string + + + + + +import ES6_Def_NamedClass1 from "ES6_Default_NamedClass1"; +var q1: number = new ES6_Def_NamedClass1().givesANum(); +var q2: string = new ES6_Def_NamedClass1().givesANum(); // Error: number ~> string + +//////////////////////////// +// == ES6 Named -> ES6 == // +//////////////////////////// + +import doesntExist5 from "ES6_Named1"; // Error: Not an exported binding + +import {specifierNumber1 as specifierNumber1_1} from "ES6_Named1"; +var r1: number = specifierNumber1_1; +var r2: string = specifierNumber1_1; // Error: number ~> string + +import {specifierNumber2Renamed} from "ES6_Named1"; +var s1: number = specifierNumber2Renamed; +var s2: string = specifierNumber2Renamed; // Error: number ~> string + +import {specifierNumber3 as specifierNumber3Renamed} from "ES6_Named1"; +var t1: number = specifierNumber3Renamed; +var t2: string = specifierNumber3Renamed; // Error: number ~> string + +import {groupedSpecifierNumber1, groupedSpecifierNumber2} from "ES6_Named1"; +var u1: number = groupedSpecifierNumber1; +var u2: number = groupedSpecifierNumber2; +var u3: string = groupedSpecifierNumber1; // Error: number ~> string +var u4: string = groupedSpecifierNumber2; // Error: number ~> string + +import {givesANumber} from "ES6_Named1"; +var v1: number = givesANumber(); +var v2: string = givesANumber(); // Error: number ~> string + +import {NumberGenerator} from "ES6_Named1"; +var w1: number = new NumberGenerator().givesANumber(); +var w2: string = new NumberGenerator().givesANumber(); // Error: number ~> string + +import {varDeclNumber1, varDeclNumber2} from "ES6_Named1"; +var x1: number = varDeclNumber1; +var x2: number = varDeclNumber2; +var x3: string = varDeclNumber1; // Error: number ~> string +var x4: string = varDeclNumber2; // Error: number ~> string + + + + + + + + + +import {numberValue1 as numberValue4} from "ES6_ExportFrom_Intermediary1"; +var aa1: number = numberValue4; +var aa2: string = numberValue4; // Error: number ~> string + +import {numberValue2_renamed} from "ES6_ExportFrom_Intermediary1"; +var ab1: number = numberValue2_renamed; +var ab2: string = numberValue2_renamed; // Error: number ~> string + +import {numberValue1 as numberValue5} from "ES6_ExportAllFrom_Intermediary1"; +var ac1: number = numberValue5; +var ac2: string = numberValue5; // Error: number ~> string + +/////////////////////////////////// +// == ES6 Default -> CommonJS == // +/////////////////////////////////// + +require('ES6_Default_AnonFunction2').doesntExist; // Error: 'doesntExist' isn't an export + +var ES6_Def_AnonFunc2 = require("ES6_Default_AnonFunction2").default; +var ad1: number = ES6_Def_AnonFunc2(); +var ad2: string = ES6_Def_AnonFunc2(); // Error: number ~> string + +var ES6_Def_NamedFunc2 = require("ES6_Default_NamedFunction2").default; +var ae1: number = ES6_Def_NamedFunc2(); +var ae2: string = ES6_Def_NamedFunc2(); // Error: number ~> string + + + + + +var ES6_Def_NamedClass2 = require("ES6_Default_NamedClass2").default; +var ag1: number = new ES6_Def_NamedClass2().givesANum(); +var ag2: string = new ES6_Def_NamedClass2().givesANum(); // Error: number ~> string + +///////////////////////////////// +// == ES6 Named -> CommonJS == // +///////////////////////////////// + +var specifierNumber4 = require("ES6_Named2").specifierNumber4; +var ah1: number = specifierNumber4; +var ah2: string = specifierNumber4; // Error: number ~> string + +var specifierNumber5Renamed = require("ES6_Named2").specifierNumber5Renamed; +var ai1: number = specifierNumber5Renamed; +var ai2: string = specifierNumber5Renamed; // Error: number ~> string + +var groupedSpecifierNumber3 = require("ES6_Named2").groupedSpecifierNumber3; +var groupedSpecifierNumber4 = require("ES6_Named2").groupedSpecifierNumber4; +var aj1: number = groupedSpecifierNumber3; +var aj2: number = groupedSpecifierNumber4; +var aj3: string = groupedSpecifierNumber3; // Error: number ~> string +var aj4: string = groupedSpecifierNumber4; // Error: number ~> string + +var givesANumber2 = require("ES6_Named2").givesANumber2; +var ak1: number = givesANumber2(); +var ak2: string = givesANumber2(); // Error: number ~> string + +var NumberGenerator2 = require("ES6_Named2").NumberGenerator2; +var al1: number = new NumberGenerator2().givesANumber(); +var al2: string = new NumberGenerator2().givesANumber(); // Error: number ~> string + +var varDeclNumber3 = require("ES6_Named2").varDeclNumber3; +var varDeclNumber4 = require("ES6_Named2").varDeclNumber4; +var am1: number = varDeclNumber3; +var am2: number = varDeclNumber4; +var am3: string = varDeclNumber3; // Error: number ~> string +var am4: string = varDeclNumber4; // Error: number ~> string + + + + + + + + + +var numberValue6 = require("ES6_ExportFrom_Intermediary2").numberValue1; +var ap1: number = numberValue6; +var ap2: string = numberValue6; // Error: number ~> string + +var numberValue2_renamed2 = require("ES6_ExportFrom_Intermediary2").numberValue2_renamed2; +var aq1: number = numberValue2_renamed2; +var aq2: string = numberValue2_renamed2; // Error: number ~> string + +var numberValue7 = require("ES6_ExportAllFrom_Intermediary2").numberValue2; +var ar1: number = numberValue7; +var ar2: string = numberValue7; // Error: number ~> string + +//////////////////////////////////////////////////////// +// == ES6 Default+Named -> ES6 import Default+Named== // +//////////////////////////////////////////////////////// + +import defaultNum, {str as namedStr} from "./ES6_DefaultAndNamed"; + +var as1: number = defaultNum; +var as2: string = defaultNum; // Error: number ~> string + +var as3: string = namedStr; +var as4: number = namedStr; // Error: string ~> number + +//////////////////////////////////////// +// == Side-effect only ES6 imports == // +//////////////////////////////////////// + +import "./SideEffects"; + +////////////////////////////////////////////// +// == Suggest export name on likely typo == // +////////////////////////////////////////////// +import specifierNumber1 from "ES6_Named1"; // Error: Did you mean `import {specifierNumber1} from ...`? +import {specifierNumber} from "ES6_Named1"; // Error: Did you mean `specifierNumber1`? + +/////////////////////////////////////////////////// +// == Multi `export *` should combine exports == // +/////////////////////////////////////////////////// +import { + numberValue1 as numberValue8, + numberValue2 as numberValue9 +} from "./ES6_ExportAllFromMulti"; + +var at1: number = numberValue8; +var at2: string = numberValue8; // Error: number ~> string + +var at3: number = numberValue9; +var at4: string = numberValue9; // Error: number ~> string diff --git a/tests/declare_export/jsfmt.spec.js b/tests/declare_export/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/declare_export/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/declare_fun/__snapshots__/jsfmt.spec.js.snap b/tests/declare_fun/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..1d896985749f --- /dev/null +++ b/tests/declare_fun/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,26 @@ +exports[`test test.js 1`] = ` +"declare function foo(x: number): string; +declare function foo(x: string): number; +declare function foo(x: X): X; + +(foo(0): string); // OK +(foo(\"hello\"): number); // OK +(foo(false): void); // error, boolean ~/~ undefined +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/declare_fun/jsfmt.spec.js b/tests/declare_fun/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/declare_fun/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/declare_fun/test.js b/tests/declare_fun/test.js new file mode 100644 index 000000000000..db8a37324764 --- /dev/null +++ b/tests/declare_fun/test.js @@ -0,0 +1,7 @@ +declare function foo(x: number): string; +declare function foo(x: string): number; +declare function foo(x: X): X; + +(foo(0): string); // OK +(foo("hello"): number); // OK +(foo(false): void); // error, boolean ~/~ undefined diff --git a/tests/declare_module_exports/__snapshots__/jsfmt.spec.js.snap b/tests/declare_module_exports/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..fd7fb86fc55f --- /dev/null +++ b/tests/declare_module_exports/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,49 @@ +exports[`test main.js 1`] = ` +"// @flow + +import declare_module_exports from "declare_module_exports"; +(declare_module_exports: number); +(declare_module_exports: string); // Error: number ~> string + +// Error: Has no named export "str"! +import {str} from "declare_m_e_with_other_value_declares"; + +import type {str2} from "declare_m_e_with_other_type_declares"; +("asdf": str2); +(42: str2); // Error: number ~> string + +/** + * \`declare var exports\` is deprecated, so we have a grace period where both + * syntaxes will work. + */ + +import DEPRECATED__declare_var_exports from "DEPRECATED__declare_var_exports"; +(DEPRECATED__declare_var_exports: number); +(DEPRECATED__declare_var_exports: string); // Error: number ~> string + +import declare_m_e_with_declare_var_e from "declare_m_e_with_declare_var_e"; +(declare_m_e_with_declare_var_e: number); +(declare_m_e_with_declare_var_e: string); // Error: number ~> string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +import declare_module_exports from "declare_module_exports"; +(declare_module_exports: number); +(declare_module_exports: string);// Error: number ~> string +// Error: Has no named export "str"! +import { str } from "declare_m_e_with_other_value_declares"; +import type { str2 } from "declare_m_e_with_other_type_declares"; +("asdf": str2); +(42: str2);// Error: number ~> string +/** + * \`declare var exports\` is deprecated, so we have a grace period where both + * syntaxes will work. + */ +import DEPRECATED__declare_var_exports from "DEPRECATED__declare_var_exports"; +(DEPRECATED__declare_var_exports: number); +(DEPRECATED__declare_var_exports: string);// Error: number ~> string +import declare_m_e_with_declare_var_e from "declare_m_e_with_declare_var_e"; +(declare_m_e_with_declare_var_e: number); +(declare_m_e_with_declare_var_e: string);// Error: number ~> string + +" +`; diff --git a/tests/declare_module_exports/flow-typed/__snapshots__/jsfmt.spec.js.snap b/tests/declare_module_exports/flow-typed/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..610dfff48baf --- /dev/null +++ b/tests/declare_module_exports/flow-typed/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,60 @@ +exports[`test libs.js 1`] = ` +"declare module "declare_module_exports" { + declare module.exports: number; +} + +declare module "declare_m_e_with_other_value_declares" { + declare module.exports: number; + declare var str: string; +} + +declare module "declare_m_e_with_other_type_declares" { + declare module.exports: number; + declare type str2 = string; +} + +/** + * \`declare var exports\` is deprecated, so we have a grace period where both + * syntaxes will work. + */ + +declare module "DEPRECATED__declare_var_exports" { + declare var exports: number; +} + +/** + * Ensure that, if both are present, \`declare module.exports\` wins + */ +declare module "declare_m_e_with_declare_var_e" { + declare module.exports: number; + declare var exports: string; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +declare module "declare_module_exports" { + declare module.exports: number +} +declare module "declare_m_e_with_other_value_declares" { + declare module.exports: number + declare var str: string; +} +declare module "declare_m_e_with_other_type_declares" { + declare module.exports: number + type str2 = string; +} +/** + * \`declare var exports\` is deprecated, so we have a grace period where both + * syntaxes will work. + */ +declare module "DEPRECATED__declare_var_exports" { + declare var exports: number; +} +/** + * Ensure that, if both are present, \`declare module.exports\` wins + */ +declare module "declare_m_e_with_declare_var_e" { + declare module.exports: number + declare var exports: string; +} + +" +`; diff --git a/tests/declare_module_exports/flow-typed/jsfmt.spec.js b/tests/declare_module_exports/flow-typed/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/declare_module_exports/flow-typed/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/declare_module_exports/flow-typed/libs.js b/tests/declare_module_exports/flow-typed/libs.js new file mode 100644 index 000000000000..25ee31b0a922 --- /dev/null +++ b/tests/declare_module_exports/flow-typed/libs.js @@ -0,0 +1,30 @@ +declare module "declare_module_exports" { + declare module.exports: number; +} + +declare module "declare_m_e_with_other_value_declares" { + declare module.exports: number; + declare var str: string; +} + +declare module "declare_m_e_with_other_type_declares" { + declare module.exports: number; + declare type str2 = string; +} + +/** + * `declare var exports` is deprecated, so we have a grace period where both + * syntaxes will work. + */ + +declare module "DEPRECATED__declare_var_exports" { + declare var exports: number; +} + +/** + * Ensure that, if both are present, `declare module.exports` wins + */ +declare module "declare_m_e_with_declare_var_e" { + declare module.exports: number; + declare var exports: string; +} diff --git a/tests/declare_module_exports/jsfmt.spec.js b/tests/declare_module_exports/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/declare_module_exports/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/declare_module_exports/main.js b/tests/declare_module_exports/main.js new file mode 100644 index 000000000000..7c9cce2c344d --- /dev/null +++ b/tests/declare_module_exports/main.js @@ -0,0 +1,25 @@ +// @flow + +import declare_module_exports from "declare_module_exports"; +(declare_module_exports: number); +(declare_module_exports: string); // Error: number ~> string + +// Error: Has no named export "str"! +import {str} from "declare_m_e_with_other_value_declares"; + +import type {str2} from "declare_m_e_with_other_type_declares"; +("asdf": str2); +(42: str2); // Error: number ~> string + +/** + * `declare var exports` is deprecated, so we have a grace period where both + * syntaxes will work. + */ + +import DEPRECATED__declare_var_exports from "DEPRECATED__declare_var_exports"; +(DEPRECATED__declare_var_exports: number); +(DEPRECATED__declare_var_exports: string); // Error: number ~> string + +import declare_m_e_with_declare_var_e from "declare_m_e_with_declare_var_e"; +(declare_m_e_with_declare_var_e: number); +(declare_m_e_with_declare_var_e: string); // Error: number ~> string diff --git a/tests/declare_type/__snapshots__/jsfmt.spec.js.snap b/tests/declare_type/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..70ade6b61551 --- /dev/null +++ b/tests/declare_type/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,49 @@ +exports[`test import_declare_type.js 1`] = ` +"/** + * @flow + */ + +//////////////////////////////////////////////////////////// +// == Import Declared Type Alias From Declared Module == // +////////////////////////////////////////////////////////// +import type {baz} from \"ModuleAliasFoo\"; +import {foo} from \"ModuleAliasFoo\"; +var k1: baz = 42; +var k2: baz = \"shab\"; // Error: string to int +var k3: toz = foo(k1); // works + +import type {toz} from \"ModuleAliasFoo\"; +var k4: toz = foo(k1); // works + +////////////////////////////////////////////////////////// +// == Declared Module with exports prop (issue 880) == // +//////////////////////////////////////////////////////// + +import blah from \'foo\'; +import type { Foo, Bar, Id } from \'foo\'; + +blah(0, 0); + +({toz:3} : Foo); // error : {toz : number} ~> string + +(3 : Bar); // error : number ~> A + +(\"lol\" : Id); // error : string ~> number +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/declare_type/import_declare_type.js b/tests/declare_type/import_declare_type.js new file mode 100644 index 000000000000..3cd90be747b0 --- /dev/null +++ b/tests/declare_type/import_declare_type.js @@ -0,0 +1,30 @@ +/** + * @flow + */ + +//////////////////////////////////////////////////////////// +// == Import Declared Type Alias From Declared Module == // +////////////////////////////////////////////////////////// +import type {baz} from "ModuleAliasFoo"; +import {foo} from "ModuleAliasFoo"; +var k1: baz = 42; +var k2: baz = "shab"; // Error: string to int +var k3: toz = foo(k1); // works + +import type {toz} from "ModuleAliasFoo"; +var k4: toz = foo(k1); // works + +////////////////////////////////////////////////////////// +// == Declared Module with exports prop (issue 880) == // +//////////////////////////////////////////////////////// + +import blah from 'foo'; +import type { Foo, Bar, Id } from 'foo'; + +blah(0, 0); + +({toz:3} : Foo); // error : {toz : number} ~> string + +(3 : Bar); // error : number ~> A + +("lol" : Id); // error : string ~> number diff --git a/tests/declare_type/jsfmt.spec.js b/tests/declare_type/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/declare_type/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/declare_type/lib/DeclareModule_TypeAlias.js b/tests/declare_type/lib/DeclareModule_TypeAlias.js new file mode 100644 index 000000000000..325d344de110 --- /dev/null +++ b/tests/declare_type/lib/DeclareModule_TypeAlias.js @@ -0,0 +1,9 @@ +/** + * @flow + */ + +declare module ModuleAliasFoo { + declare type baz = number; + declare type toz = string; + declare function foo(bar : baz) : toz; +} diff --git a/tests/declare_type/lib/__snapshots__/jsfmt.spec.js.snap b/tests/declare_type/lib/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..ae6ef708d866 --- /dev/null +++ b/tests/declare_type/lib/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,65 @@ +exports[`test DeclareModule_TypeAlias.js 1`] = ` +"/** + * @flow + */ + +declare module ModuleAliasFoo { + declare type baz = number; + declare type toz = string; + declare function foo(bar : baz) : toz; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test declare_type_exports.js 1`] = ` +"/* @flow */ + +declare module \'foo\' { + declare class A { + toz : number + } + + declare var n : string; + + declare type Foo = typeof n; + declare type Bar = A; + declare type Id = T; + + declare var exports : { + (a : number, b : number) : number + }; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1479:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/declare_type/lib/declare_type_exports.js b/tests/declare_type/lib/declare_type_exports.js new file mode 100644 index 000000000000..dac1a6bd9a92 --- /dev/null +++ b/tests/declare_type/lib/declare_type_exports.js @@ -0,0 +1,17 @@ +/* @flow */ + +declare module 'foo' { + declare class A { + toz : number + } + + declare var n : string; + + declare type Foo = typeof n; + declare type Bar = A; + declare type Id = T; + + declare var exports : { + (a : number, b : number) : number + }; +} diff --git a/tests/declare_type/lib/jsfmt.spec.js b/tests/declare_type/lib/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/declare_type/lib/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/def_site_variance/__snapshots__/jsfmt.spec.js.snap b/tests/def_site_variance/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..cd46d9b9ba9b --- /dev/null +++ b/tests/def_site_variance/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,50 @@ +exports[`test test.js 1`] = ` +"class Variance<+Out,-In> { + foo(x: Out): Out { return x; } + bar(y: In): In { return y; } +} + +class A { } +class B extends A { } + +function subtyping( + v1: Variance, + v2: Variance +) { + (v1: Variance); // error on both targs (A ~/~> B) + (v2: Variance); // OK for both targs (B ~> A) +} + +class PropVariance<+Out,-In> { + inv1: Out; // error + inv2: In; // error + -co1: Out; // error + -co2: In; // ok + +con1: Out; // ok + +con2: In; // error + + inv_dict1: {[k:string]: Out}; // error + inv_dict2: {[k:string]: In}; // error + co_dict1: {+[k:string]: Out}; // ok + co_dict2: {+[k:string]: In}; // error + con_dict1: {-[k:string]: Out}; // error + con_dict2: {-[k:string]: In}; // ok +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1172:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/def_site_variance/jsfmt.spec.js b/tests/def_site_variance/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/def_site_variance/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/def_site_variance/test.js b/tests/def_site_variance/test.js new file mode 100644 index 000000000000..72f756eeaa21 --- /dev/null +++ b/tests/def_site_variance/test.js @@ -0,0 +1,31 @@ +class Variance<+Out,-In> { + foo(x: Out): Out { return x; } + bar(y: In): In { return y; } +} + +class A { } +class B extends A { } + +function subtyping( + v1: Variance, + v2: Variance +) { + (v1: Variance); // error on both targs (A ~/~> B) + (v2: Variance); // OK for both targs (B ~> A) +} + +class PropVariance<+Out,-In> { + inv1: Out; // error + inv2: In; // error + -co1: Out; // error + -co2: In; // ok + +con1: Out; // ok + +con2: In; // error + + inv_dict1: {[k:string]: Out}; // error + inv_dict2: {[k:string]: In}; // error + co_dict1: {+[k:string]: Out}; // ok + co_dict2: {+[k:string]: In}; // error + con_dict1: {-[k:string]: Out}; // error + con_dict2: {-[k:string]: In}; // ok +} diff --git a/tests/demo/1/__snapshots__/jsfmt.spec.js.snap b/tests/demo/1/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..6a6eab1a73fe --- /dev/null +++ b/tests/demo/1/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,20 @@ +exports[`test f.js 1`] = ` +"/* demo */ + +function f(x) { return 42/x; } + +var x = null; +//... + +f(x); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* demo */ +function f(x) { + return 42 / x; +} +var x = null; +//... +f(x); + +" +`; diff --git a/tests/demo/1/f.js b/tests/demo/1/f.js new file mode 100644 index 000000000000..fd73a91e9cc9 --- /dev/null +++ b/tests/demo/1/f.js @@ -0,0 +1,8 @@ +/* demo */ + +function f(x) { return 42/x; } + +var x = null; +//... + +f(x); diff --git a/tests/demo/1/jsfmt.spec.js b/tests/demo/1/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/demo/1/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/demo/2/A.js b/tests/demo/2/A.js new file mode 100644 index 000000000000..b0281e07e714 --- /dev/null +++ b/tests/demo/2/A.js @@ -0,0 +1,19 @@ +/* @providesModule Demo */ + +class A { + x: number; // instance field declaration + constructor(x) { this.x = x; } + + getX() { return this.x; } + + onLoad(callback) { + return callback(this.getX()); + } +} + +function callback(x: string) { return x.length; } + +var a = new A(42); +a.onLoad(callback); + +module.exports = A; diff --git a/tests/demo/2/B.js b/tests/demo/2/B.js new file mode 100644 index 000000000000..4c44780b9e90 --- /dev/null +++ b/tests/demo/2/B.js @@ -0,0 +1,3 @@ +var A = require('Demo'); + +var z = new A("42").getX(); diff --git a/tests/demo/2/__snapshots__/jsfmt.spec.js.snap b/tests/demo/2/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..faf41ca9a6bd --- /dev/null +++ b/tests/demo/2/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,54 @@ +exports[`test A.js 1`] = ` +"/* @providesModule Demo */ + +class A { + x: number; // instance field declaration + constructor(x) { this.x = x; } + + getX() { return this.x; } + + onLoad(callback) { + return callback(this.getX()); + } +} + +function callback(x: string) { return x.length; } + +var a = new A(42); +a.onLoad(callback); + +module.exports = A; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @providesModule Demo */ +class A { + x: number;// instance field declaration + constructor(x) { + this.x = x; + } + getX() { + return this.x; + } + onLoad(callback) { + return callback(this.getX()); + } +} +function callback(x: string) { + return x.length; +} +var a = new A(42); +a.onLoad(callback); +module.exports = A; + +" +`; + +exports[`test B.js 1`] = ` +"var A = require('Demo'); + +var z = new A("42").getX(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var A = require("Demo"); +var z = new A("42").getX(); + +" +`; diff --git a/tests/demo/2/jsfmt.spec.js b/tests/demo/2/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/demo/2/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/deps/A.js b/tests/deps/A.js new file mode 100644 index 000000000000..5a8c55d5bf63 --- /dev/null +++ b/tests/deps/A.js @@ -0,0 +1 @@ +require('./C'); diff --git a/tests/deps/B.js b/tests/deps/B.js new file mode 100644 index 000000000000..5a8c55d5bf63 --- /dev/null +++ b/tests/deps/B.js @@ -0,0 +1 @@ +require('./C'); diff --git a/tests/deps/C.js b/tests/deps/C.js new file mode 100644 index 000000000000..a006ecb7fb7d --- /dev/null +++ b/tests/deps/C.js @@ -0,0 +1,4 @@ +require('./D'); +require('./E'); +require('./F'); +require('./G'); diff --git a/tests/deps/D.js b/tests/deps/D.js new file mode 100644 index 000000000000..c5dbbb7bc2e4 --- /dev/null +++ b/tests/deps/D.js @@ -0,0 +1 @@ +require('./I'); diff --git a/tests/deps/E.js b/tests/deps/E.js new file mode 100644 index 000000000000..c5dbbb7bc2e4 --- /dev/null +++ b/tests/deps/E.js @@ -0,0 +1 @@ +require('./I'); diff --git a/tests/deps/F.js b/tests/deps/F.js new file mode 100644 index 000000000000..8b1a393741c9 --- /dev/null +++ b/tests/deps/F.js @@ -0,0 +1 @@ +// empty diff --git a/tests/deps/G.js b/tests/deps/G.js new file mode 100644 index 000000000000..4c21ad55c16d --- /dev/null +++ b/tests/deps/G.js @@ -0,0 +1 @@ +require('./H'); diff --git a/tests/deps/H.js b/tests/deps/H.js new file mode 100644 index 000000000000..8b1a393741c9 --- /dev/null +++ b/tests/deps/H.js @@ -0,0 +1 @@ +// empty diff --git a/tests/deps/I.js b/tests/deps/I.js new file mode 100644 index 000000000000..39ab86c96453 --- /dev/null +++ b/tests/deps/I.js @@ -0,0 +1 @@ +require('./A'); diff --git a/tests/deps/__snapshots__/jsfmt.spec.js.snap b/tests/deps/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..d5665196b01d --- /dev/null +++ b/tests/deps/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,77 @@ +exports[`test A.js 1`] = ` +"require('./C'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +require("./C"); + +" +`; + +exports[`test B.js 1`] = ` +"require('./C'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +require("./C"); + +" +`; + +exports[`test C.js 1`] = ` +"require('./D'); +require('./E'); +require('./F'); +require('./G'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +require("./D"); +require("./E"); +require("./F"); +require("./G"); + +" +`; + +exports[`test D.js 1`] = ` +"require('./I'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +require("./I"); + +" +`; + +exports[`test E.js 1`] = ` +"require('./I'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +require("./I"); + +" +`; + +exports[`test F.js 1`] = ` +"// empty +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; + +exports[`test G.js 1`] = ` +"require('./H'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +require("./H"); + +" +`; + +exports[`test H.js 1`] = ` +"// empty +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; + +exports[`test I.js 1`] = ` +"require('./A'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +require("./A"); + +" +`; diff --git a/tests/deps/jsfmt.spec.js b/tests/deps/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/deps/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/destructuring/__snapshots__/jsfmt.spec.js.snap b/tests/destructuring/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..874ac970940b --- /dev/null +++ b/tests/destructuring/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,480 @@ +exports[`test array_rest.js 1`] = ` +"let xs = [0, \"\", true]; +let [a, ...ys] = xs; +let [b, ...zs] = ys; +let c = zs[0]; // retain tuple info +let d = zs[1]; // run off the end + +(a: void); // error: number ~> void +(b: void); // error: string ~> void +(c: void); // error: boolean ~> void +(d: void); // error: number|string|boolean ~> void + +let [...e] = 0; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +let xs = [ 0, \"\", true ]; +let [ a, ...ys ] = xs; +let [ b, ...zs ] = ys; +let c = zs[0];// retain tuple info +let d = zs[1];// run off the end +(a: void);// error: number ~> void +(b: void);// error: string ~> void +(c: void);// error: boolean ~> void +(d: void);// error: number|string|boolean ~> void +let [ ...e ] = 0; + +" +`; + +exports[`test computed.js 1`] = ` +"var { [\"key\"]: val1 } = { key: \"val\" }; +(val1: void); // error: string ~> void + +var key: string = \"key\"; +var { [key]: val2 } = { key: \"val\" }; +(val2: void); // ok (gasp!) by existing StrT -> ElemT rule + +var { [\"key\"]: val3, ...spread } = { key: \"val\" }; +(spread.key: void); // error (gasp!) in general we don\'t know if a computed prop should be excluded from spread +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var { [\"key\"]: val1 } = { key: \"val\" }; +(val1: void);// error: string ~> void +var key: string = \"key\"; +var { [key]: val2 } = { key: \"val\" }; +(val2: void);// ok (gasp!) by existing StrT -> ElemT rule +var { [\"key\"]: val3, ...spread } = { key: \"val\" }; +(spread.key: void);// error (gasp!) in general we don\'t know if a computed prop should be excluded from spread + +" +`; + +exports[`test defaults.js 1`] = ` +"/* @flow */ + +function obj_prop_fun({p:{q=0}={q:true}}={p:{q:\"\"}}) { + // errors: + // * number ~> void, from default on _.p.q + // * boolean ~> void, from default on _.p + // * string ~> void, from default on _ + // * null ~> void, from call below + (q:void); +} +obj_prop_fun(); // ok +obj_prop_fun({}); // ok +obj_prop_fun({p:{}}); // ok +obj_prop_fun({p:{q:null}}); // ok, provides add\'l lower bound + +function obj_prop_var(o={p:{q:\"\"}}) { + var {p:{q=0}={q:true}} = o; + // errors: + // * number ~> void, from default on o.p.q + // * boolean ~> void, from default on o.p + // * string ~> void, from default on o + // * null ~> void, from call below + (q:void); +} +obj_prop_var(); // ok +obj_prop_var({}); // ok +obj_prop_var({p:{}}); // ok +obj_prop_var({p:{q:null}}); // ok, provides add\'l lower bound + +function obj_rest({p:{q,...o}={q:0,r:0}}={p:{q:0,r:\"\"}}) { + // errors: + // * number ~> void, from default on _.p + // * string ~> void, from default on _ + // * null ~> void, from call below + (o.r:void); +} +obj_rest(); // ok +obj_rest({}); // ok +obj_rest({p:{}}); // ok +obj_rest({p:{q:0,r:null}}); + +function obj_prop_annot({ + p = true // error: boolean ~> string +}: { + p: string +} = { + p: 0 // error: number ~> string +}) { + (p:void); // error: string ~> void +} + +var { + p = true // error: boolean ~> string +}: { + p: string +} = { + p: 0 // error: number ~> string +}; +(p:void); // error: string ~> void + +function obj_prop_err({x:{y}}=null) {} // error: property \`x\` cannot be accessed on null +function obj_rest_err({...o}=0) {} // error: expected object instead of number +function arr_elem_err([x]=null) {} // error: element 0 cannot be accessed on null +function arr_rest_err([...a]=null) {} // error: expected array instead of null + +function gen(x:T,{p=x}:{p:T}):T { + return p; +} + +// Default values in destructuring unwrap optional types +obj_prop_fun(({} : {p?:{q?:null}})); // ok +obj_prop_var(({} : {p?:{q?:null}})); // ok + +// union-like upper bounds preserved through destructuring +function obj_prop_opt({p}:{p?:string}={p:0}) {} +function obj_prop_maybe({p}:{p:?string}={p:0}) {} +function obj_prop_union({p}:{p:number|string}={p:true}) {} + +// TODO: union-of-objects upper bounds preserved through destructuring +function obj_prop_union2({p}:{p:number}|{p:string}={p:true}) {} + +function default_expr_scope({a, b = a}) {} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:340:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test destructuring.js 1`] = ` +"declare var a:string; +declare var b:string; +declare var c:string; +[{a1:a, b},c] = [{a1:0, b:1},2]; + +var {m} = {m:0}; +({m} = {m:m}); + +var obj; +({n: obj.x} = {n:3}); +[obj.x] = [\'foo\']; + +function foo({p, z:[r]}) { + a = p; + b = z; + c = r; +} +foo({p:0, z:[1,2]}); + +[a,,b,...c] = [0,1,true,3]; + +function bar({x, ...z}) { + var o:{x: string; y: number;} = z; +} +bar({x:\"\",y:0}); + +var spread = {y:\"\"}; +var extend: {x:number; y:string; z: boolean} = {x:0, ...spread}; + +function qux(_: {a:number}) { } +qux({a:\"\"}); +function corge({b}: {b:string}) { } +corge({b:0}); + +var {n}:{n: number} = {n: \"\"} + +function test() { + var {foo} = {bar: 123}; // error on foo + var {bar, baz} = {bar: 123} // error on baz +} + +function test() { + var x = {foo: \'abc\', bar: 123}; + var {foo, ...rest} = x; + (x.baz: string); // error, baz doesn\'t exist + (rest.baz: string); // no error, rest is unsealed +} + +module.exports = corge; + +class Base { + baseprop1: number; + baseprop2: number; +} + +class Child extends Base { + childprop1: number; + childprop2: number; +} + +var {baseprop1, childprop1, ...others} = new Child(); + +var bp1: number = baseprop1; +var bp1_err: string = baseprop1; // Error: number ~> string +var bp2: number = others.baseprop2; +var bp2_err: string = others.baseprop2; // Error: number ~> string + +var cp1: number = childprop1; +var cp1_err: string = childprop1; // Error: number ~> string +var cp2: number = others.childprop1; +var cp2_err: string = others.childprop2; // Error: number ~> string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +declare var a: string; +declare var b: string; +declare var c: string; +[ { a1: a, b }, c ] = [ { a1: 0, b: 1 }, 2 ]; +var { m } = { m: 0 }; +{ m } = { m: m }; +var obj; +{ n: obj.x } = { n: 3 }; +[ obj.x ] = [ \"foo\" ]; +function foo({ p, z: [ r ] }) { + a = p; + b = z; + c = r; +} +foo({ p: 0, z: [ 1, 2 ] }); +[ a, , b, ...c ] = [ 0, 1, true, 3 ]; +function bar({ x, ...z }) { + var o: { x: string, y: number } = z; +} +bar({ x: \"\", y: 0 }); +var spread = { y: \"\" }; +var extend: { x: number, y: string, z: boolean } = { x: 0, ...spread }; +function qux(_: { a: number }) { + +} +qux({ a: \"\" }); +function corge({ b }) { + +} +corge({ b: 0 }); +var { n } = { n: \"\" }; +function test() { + var { foo } = { bar: 123 };// error on foo + var { bar, baz } = { bar: 123 };// error on baz +} +function test() { + var x = { foo: \"abc\", bar: 123 }; + var { foo, ...rest } = x; + (x.baz: string);// error, baz doesn\'t exist + (rest.baz: string);// no error, rest is unsealed +} +module.exports = corge; +class Base { + baseprop1: number; + baseprop2: number; +} +class Child extends Base { + childprop1: number; + childprop2: number; +} +var { baseprop1, childprop1, ...others } = new Child(); +var bp1: number = baseprop1; +var bp1_err: string = baseprop1;// Error: number ~> string +var bp2: number = others.baseprop2; +var bp2_err: string = others.baseprop2;// Error: number ~> string +var cp1: number = childprop1; +var cp1_err: string = childprop1;// Error: number ~> string +var cp2: number = others.childprop1; +var cp2_err: string = others.childprop2;// Error: number ~> string + +" +`; + +exports[`test destructuring_init.js 1`] = ` +"var {foo}; +var [bar]; +var []; +var {}; +var [], {toz}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Complex binding patterns require an initialization value (1:9) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp$1.parseVar (/node_modules/babylon/lib/index.js:2215:12) + at Parser.pp$1.parseVarStatement (/node_modules/babylon/lib/index.js:2042:8) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1735:19) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) + at Parser.parse (/node_modules/babylon/lib/index.js:1543:17) + at Object.parse$1 [as parse] (/node_modules/babylon/lib/index.js:6472:37) + at Object.parse (/index.js:34:26) +" +`; + +exports[`test destructuring_param.js 1`] = ` +"function f(a, { b }) { + return a + b; +} + +function g(a, { a }) { + return a; +} + +function h({ a, { b } }, { c }, { { d } }) { + return a + b + c + d; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Argument name clash in strict mode (5:16) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp$2.checkLVal (/node_modules/babylon/lib/index.js:3007:16) + at Parser.checkLVal (/node_modules/babylon/lib/index.js:5446:22) + at Parser.pp$2.checkLVal (/node_modules/babylon/lib/index.js:3034:14) + at Parser.checkLVal (/node_modules/babylon/lib/index.js:5446:22) + at Parser.pp$3.parseFunctionBody (/node_modules/babylon/lib/index.js:4072:12) + at Parser.parseFunctionBody (/node_modules/babylon/lib/index.js:5211:20) + at Parser.pp$1.parseFunction (/node_modules/babylon/lib/index.js:2257:8) + at Parser.pp$1.parseFunctionStatement (/node_modules/babylon/lib/index.js:1926:15) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1712:19) +" +`; + +exports[`test eager.js 1`] = ` +"var x; +({x} = null); // error, property \`x\` can not be accessed on \`null\` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var x; +{ x } = null;// error, property \`x\` can not be accessed on \`null\` + +" +`; + +exports[`test poly.js 1`] = ` +"// @flow + +function obj_pattern({ prop } : { prop: X }) {} // prop: X +type Prop = { prop: X }; +function obj_pattern2({ prop } : Prop) {} // prop: X + +function arr_pattern([ elem ] : X[]) {} // elem: X +type Elem = X[]; +function arr_pattern2([ elem ] : Elem) {} // elem: X + +function tup_pattern([ proj ] : [X]) {} // proj: X +type Proj = [X]; +function tup_pattern2([ proj ] : Proj) {} // proj: X + +function rest_antipattern(...t: T) {} // nonsense +function rest_pattern(...r: X[]) {} // r: X[] + +function obj_rest_pattern({ _, ...o } : { _: any, x: X }) { // o: { x: X } + o.x; +} +type ObjRest = { _: any, x: X }; +function obj_rest_pattern({ _, ...o } : ObjRest) { // o: { x: X } + o.x; +} + +function arr_rest_pattern([ _, ...a ] : [ any, X ]) { // a: [X] + a[0]; +} +type ArrRest = [ any, X ]; +function arr_rest_pattern([ _, ...a ] : ArrRest) { // a: [X] + a[0]; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:340:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test rec.js 1`] = ` +"// @flow + +// Make sure that destructuring doesn\'t cause infinite loops when combined with +// funny doses of repositioning + +let foo = (i: number) => [i]; + +const bar = (i: number) => { + [i] = foo(i); + return [i]; +}; + +foo = (i: number) => { + return bar(i); +}; + +// Also make sure that the following doesn\'t loop + +declare var o; +var { x: o } = o; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +// Make sure that destructuring doesn\'t cause infinite loops when combined with +// funny doses of repositioning +let foo = (i: number) => [ i ]; +const bar = (i: number) => { + [ i ] = foo(i); + return [ i ]; +}; +foo = (i: number) => { + return bar(i); +}; +// Also make sure that the following doesn\'t loop +declare var o; +var { x: o } = o; + +" +`; + +exports[`test string_lit.js 1`] = ` +"var { \"key\": val } = { key: \"val\" }; +(val: void); // error: string ~> void + +var { \"with-dash\": with_dash } = { \"with-dash\": \"motivating example\" }; +(with_dash: \"motivating example\"); // ok +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var { \"key\": val } = { key: \"val\" }; +(val: void);// error: string ~> void +var { \"with-dash\": with_dash } = { \"with-dash\": \"motivating example\" }; +(with_dash: \"motivating example\");// ok + +" +`; + +exports[`test unannotated.js 1`] = ` +"// @flow + +var { x } = { + x: { foo: \"foo\" } +}; + +function bar() { + x.bar +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +var { x } = { x: { foo: \"foo\" } }; +function bar() { + x.bar; +} + +" +`; diff --git a/tests/destructuring/array_rest.js b/tests/destructuring/array_rest.js new file mode 100644 index 000000000000..df9c2364fef7 --- /dev/null +++ b/tests/destructuring/array_rest.js @@ -0,0 +1,12 @@ +let xs = [0, "", true]; +let [a, ...ys] = xs; +let [b, ...zs] = ys; +let c = zs[0]; // retain tuple info +let d = zs[1]; // run off the end + +(a: void); // error: number ~> void +(b: void); // error: string ~> void +(c: void); // error: boolean ~> void +(d: void); // error: number|string|boolean ~> void + +let [...e] = 0; diff --git a/tests/destructuring/computed.js b/tests/destructuring/computed.js new file mode 100644 index 000000000000..5f5f2d241152 --- /dev/null +++ b/tests/destructuring/computed.js @@ -0,0 +1,9 @@ +var { ["key"]: val1 } = { key: "val" }; +(val1: void); // error: string ~> void + +var key: string = "key"; +var { [key]: val2 } = { key: "val" }; +(val2: void); // ok (gasp!) by existing StrT -> ElemT rule + +var { ["key"]: val3, ...spread } = { key: "val" }; +(spread.key: void); // error (gasp!) in general we don't know if a computed prop should be excluded from spread diff --git a/tests/destructuring/defaults.js b/tests/destructuring/defaults.js new file mode 100644 index 000000000000..0c5fcfb358bc --- /dev/null +++ b/tests/destructuring/defaults.js @@ -0,0 +1,82 @@ +/* @flow */ + +function obj_prop_fun({p:{q=0}={q:true}}={p:{q:""}}) { + // errors: + // * number ~> void, from default on _.p.q + // * boolean ~> void, from default on _.p + // * string ~> void, from default on _ + // * null ~> void, from call below + (q:void); +} +obj_prop_fun(); // ok +obj_prop_fun({}); // ok +obj_prop_fun({p:{}}); // ok +obj_prop_fun({p:{q:null}}); // ok, provides add'l lower bound + +function obj_prop_var(o={p:{q:""}}) { + var {p:{q=0}={q:true}} = o; + // errors: + // * number ~> void, from default on o.p.q + // * boolean ~> void, from default on o.p + // * string ~> void, from default on o + // * null ~> void, from call below + (q:void); +} +obj_prop_var(); // ok +obj_prop_var({}); // ok +obj_prop_var({p:{}}); // ok +obj_prop_var({p:{q:null}}); // ok, provides add'l lower bound + +function obj_rest({p:{q,...o}={q:0,r:0}}={p:{q:0,r:""}}) { + // errors: + // * number ~> void, from default on _.p + // * string ~> void, from default on _ + // * null ~> void, from call below + (o.r:void); +} +obj_rest(); // ok +obj_rest({}); // ok +obj_rest({p:{}}); // ok +obj_rest({p:{q:0,r:null}}); + +function obj_prop_annot({ + p = true // error: boolean ~> string +}: { + p: string +} = { + p: 0 // error: number ~> string +}) { + (p:void); // error: string ~> void +} + +var { + p = true // error: boolean ~> string +}: { + p: string +} = { + p: 0 // error: number ~> string +}; +(p:void); // error: string ~> void + +function obj_prop_err({x:{y}}=null) {} // error: property `x` cannot be accessed on null +function obj_rest_err({...o}=0) {} // error: expected object instead of number +function arr_elem_err([x]=null) {} // error: element 0 cannot be accessed on null +function arr_rest_err([...a]=null) {} // error: expected array instead of null + +function gen(x:T,{p=x}:{p:T}):T { + return p; +} + +// Default values in destructuring unwrap optional types +obj_prop_fun(({} : {p?:{q?:null}})); // ok +obj_prop_var(({} : {p?:{q?:null}})); // ok + +// union-like upper bounds preserved through destructuring +function obj_prop_opt({p}:{p?:string}={p:0}) {} +function obj_prop_maybe({p}:{p:?string}={p:0}) {} +function obj_prop_union({p}:{p:number|string}={p:true}) {} + +// TODO: union-of-objects upper bounds preserved through destructuring +function obj_prop_union2({p}:{p:number}|{p:string}={p:true}) {} + +function default_expr_scope({a, b = a}) {} diff --git a/tests/destructuring/destructuring.js b/tests/destructuring/destructuring.js new file mode 100644 index 000000000000..697454b1fceb --- /dev/null +++ b/tests/destructuring/destructuring.js @@ -0,0 +1,71 @@ +declare var a:string; +declare var b:string; +declare var c:string; +[{a1:a, b},c] = [{a1:0, b:1},2]; + +var {m} = {m:0}; +({m} = {m:m}); + +var obj; +({n: obj.x} = {n:3}); +[obj.x] = ['foo']; + +function foo({p, z:[r]}) { + a = p; + b = z; + c = r; +} +foo({p:0, z:[1,2]}); + +[a,,b,...c] = [0,1,true,3]; + +function bar({x, ...z}) { + var o:{x: string; y: number;} = z; +} +bar({x:"",y:0}); + +var spread = {y:""}; +var extend: {x:number; y:string; z: boolean} = {x:0, ...spread}; + +function qux(_: {a:number}) { } +qux({a:""}); +function corge({b}: {b:string}) { } +corge({b:0}); + +var {n}:{n: number} = {n: ""} + +function test() { + var {foo} = {bar: 123}; // error on foo + var {bar, baz} = {bar: 123} // error on baz +} + +function test() { + var x = {foo: 'abc', bar: 123}; + var {foo, ...rest} = x; + (x.baz: string); // error, baz doesn't exist + (rest.baz: string); // no error, rest is unsealed +} + +module.exports = corge; + +class Base { + baseprop1: number; + baseprop2: number; +} + +class Child extends Base { + childprop1: number; + childprop2: number; +} + +var {baseprop1, childprop1, ...others} = new Child(); + +var bp1: number = baseprop1; +var bp1_err: string = baseprop1; // Error: number ~> string +var bp2: number = others.baseprop2; +var bp2_err: string = others.baseprop2; // Error: number ~> string + +var cp1: number = childprop1; +var cp1_err: string = childprop1; // Error: number ~> string +var cp2: number = others.childprop1; +var cp2_err: string = others.childprop2; // Error: number ~> string diff --git a/tests/destructuring/destructuring_init.js b/tests/destructuring/destructuring_init.js new file mode 100644 index 000000000000..2a62fe4344d4 --- /dev/null +++ b/tests/destructuring/destructuring_init.js @@ -0,0 +1,5 @@ +var {foo}; +var [bar]; +var []; +var {}; +var [], {toz}; diff --git a/tests/destructuring/destructuring_param.js b/tests/destructuring/destructuring_param.js new file mode 100644 index 000000000000..85b6a5d42888 --- /dev/null +++ b/tests/destructuring/destructuring_param.js @@ -0,0 +1,11 @@ +function f(a, { b }) { + return a + b; +} + +function g(a, { a }) { + return a; +} + +function h({ a, { b } }, { c }, { { d } }) { + return a + b + c + d; +} diff --git a/tests/destructuring/eager.js b/tests/destructuring/eager.js new file mode 100644 index 000000000000..5b668bb68003 --- /dev/null +++ b/tests/destructuring/eager.js @@ -0,0 +1,2 @@ +var x; +({x} = null); // error, property `x` can not be accessed on `null` diff --git a/tests/destructuring/jsfmt.spec.js b/tests/destructuring/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/destructuring/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/destructuring/poly.js b/tests/destructuring/poly.js new file mode 100644 index 000000000000..bbfb5ccf7761 --- /dev/null +++ b/tests/destructuring/poly.js @@ -0,0 +1,32 @@ +// @flow + +function obj_pattern({ prop } : { prop: X }) {} // prop: X +type Prop = { prop: X }; +function obj_pattern2({ prop } : Prop) {} // prop: X + +function arr_pattern([ elem ] : X[]) {} // elem: X +type Elem = X[]; +function arr_pattern2([ elem ] : Elem) {} // elem: X + +function tup_pattern([ proj ] : [X]) {} // proj: X +type Proj = [X]; +function tup_pattern2([ proj ] : Proj) {} // proj: X + +function rest_antipattern(...t: T) {} // nonsense +function rest_pattern(...r: X[]) {} // r: X[] + +function obj_rest_pattern({ _, ...o } : { _: any, x: X }) { // o: { x: X } + o.x; +} +type ObjRest = { _: any, x: X }; +function obj_rest_pattern({ _, ...o } : ObjRest) { // o: { x: X } + o.x; +} + +function arr_rest_pattern([ _, ...a ] : [ any, X ]) { // a: [X] + a[0]; +} +type ArrRest = [ any, X ]; +function arr_rest_pattern([ _, ...a ] : ArrRest) { // a: [X] + a[0]; +} diff --git a/tests/destructuring/rec.js b/tests/destructuring/rec.js new file mode 100644 index 000000000000..8dcd432df6cc --- /dev/null +++ b/tests/destructuring/rec.js @@ -0,0 +1,20 @@ +// @flow + +// Make sure that destructuring doesn't cause infinite loops when combined with +// funny doses of repositioning + +let foo = (i: number) => [i]; + +const bar = (i: number) => { + [i] = foo(i); + return [i]; +}; + +foo = (i: number) => { + return bar(i); +}; + +// Also make sure that the following doesn't loop + +declare var o; +var { x: o } = o; diff --git a/tests/destructuring/string_lit.js b/tests/destructuring/string_lit.js new file mode 100644 index 000000000000..95da8eaf33b8 --- /dev/null +++ b/tests/destructuring/string_lit.js @@ -0,0 +1,5 @@ +var { "key": val } = { key: "val" }; +(val: void); // error: string ~> void + +var { "with-dash": with_dash } = { "with-dash": "motivating example" }; +(with_dash: "motivating example"); // ok diff --git a/tests/destructuring/unannotated.js b/tests/destructuring/unannotated.js new file mode 100644 index 000000000000..fc2d27a34661 --- /dev/null +++ b/tests/destructuring/unannotated.js @@ -0,0 +1,9 @@ +// @flow + +var { x } = { + x: { foo: "foo" } +}; + +function bar() { + x.bar +} diff --git a/tests/dictionary/__snapshots__/jsfmt.spec.js.snap b/tests/dictionary/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..599e2a272cb1 --- /dev/null +++ b/tests/dictionary/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,561 @@ +exports[`test any.js 1`] = ` +"/* @flow */ + +const dict: {[key: string]: number} = {} +const k: any = \'foo\' +const val: string = dict[k] // error: number incompatible with string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +const dict: { [key: string]: number } = {}; +const k: any = \"foo\"; +const val: string = dict[k];// error: number incompatible with string + +" +`; + +exports[`test compatible.js 1`] = ` +"/* @flow */ + +function foo0(x: Array<{[key: string]: mixed}>): Array<{[key: string]: mixed}> { + // this adds a fooBar property to the param type, which should NOT cause + // an error in the return type because it is a dictionary. + x[0].fooBar = \'foobar\'; + return x; +} + +function foo2( + x: {[key: string]: number} +): {[key: string]: number, +toString: () => string} { + // x\'s prototype has a toString method + return x; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test dictionary.js 1`] = ` +"/* Dictionary types are object types that include an indexer, which specifies a + * key type and a value type. The presence of an indexer makes the object type + * unsealed, but all added properties must be consistent with the indexer + * signature. + * + * Dictionaries can be used to represent the common idiom of objects used as + * maps. They can also be used to represent array-like objects, e.g., NodeList + * from the DOM API. + * + * A dictionary is assumed to have every property described by it\'s key type. + * This behavior is similar to the behavior of arrays, which are assumed to have + * a value at every index. + * + * @flow + */ + +// Some logic is variance-sensitive. +class A {} +class B extends A {} +class C extends B {} + +// Just a couple of short type names. Compare to string/number. +class X {} +class Y {} + +// Any property can be set on a dict with string keys. +function set_prop_to_string_key( + o: {[k:string]:any}, +) { + o.prop = \"ok\"; +} + +// **UNSOUND** +// This is allowed by design. We don\'t track get/set and we don\'t wrap the +// return type in a maybe. +function unsound_dict_has_every_key( + o: {[k:string]:X}, +) { + (o.p: X); // ok + (o[\"p\"]: X); // ok +} + +// As with any object type, we can assign subtypes to properties. +function set_prop_covariant( + o: {[k:string]:B}, +) { + o.p = new A; // error, A ~> B + o.p = new B; // ok + o.p = new C; // ok +} + +// This isn\'t specific behavior to dictionaries, but for completeness... +function get_prop_contravariant( + o: {[k:string]:B}, +) { + (o.p: A); // ok + (o.p: B); // ok + (o.p: C); // error, C ~> B +} + +// Dot-notation can not be used to add properties to dictionaries with +// non-string keys, because keys are strings. +function add_prop_to_nonstring_key_dot( + o: {[k:number]:any}, +) { + o.prop = \"err\"; // error: string ~> number +} + +// Bracket notation can be used to add properties to dictionaries with +// non-string keys, even though all keys are strings. This is a convenient +// affordance. +function add_prop_to_nonstring_key_bracket( + o: {[k:number]:any}, +) { + o[0] = \"ok\"; +} + +// Objects can be part dict, part not by mixing an indexer with declared props. +function mix_with_declared_props( + o: {[k:number]:X,p:Y}, + x: X, + y: Y, +) { + (o[0]: X); // ok + (o.p: Y); // ok + o[0] = x; // ok + o.p = y; // ok +} + +// Indeed, dict types are still Objects and have Object.prototype stuff +function object_prototype( + o: {[k:string]:number}, +): {[k:string]:number, +toString: () => string} { + (o.toString(): boolean); // error: string ~> boolean + return o; // ok +} + +// **UNSOUND** +// Because we support non-string props w/ bracket notation, it\'s possible to +// write into a declared prop unsoundly. +function unsound_string_conversion_alias_declared_prop( + o: {[k:number]:any, \"0\":X}, +) { + o[0] = \"not-x\"; // a[\"0\"] no longer X +} + +function unification_dict_values_invariant( + x: Array<{[k:string]:B}>, +) { + let a: Array<{[k:string]:A}> = x; // error + a[0].p = new A; // in[0].p no longer B + + let b: Array<{[k:string]:B}> = x; // ok + + let c: Array<{[k:string]:C}> = x; // error + (x[0].p: C); // not true +} + +function subtype_dict_values_invariant( + x: {[k:string]:B}, +) { + let a: {[k:string]:A} = x; // error + a.p = new A; // x[0].p no longer B + + let b: {[k:string]:B} = x; // ok + + let c: {[k:string]:C} = x; // error + (x.p: C); // not true +} + +function subtype_dict_values_fresh_exception() { + let a: {[k:string]:A} = { + a: new A, // ok, A == A + b: new B, // ok, B <: A + c: new C, // ok, C <: A + }; + + let b: {[k:string]:B} = { + a: new A, // error, A not <: B + b: new B, // ok, B == B + c: new C, // ok, C <: A + }; + + let c: {[k:string]:C} = { + a: new A, // error, A not <: C + b: new B, // error, A not <: C + c: new C, // ok, C == C + }; +} + +// Actually, unsound_string_conversion_alias_declared_prop behavior makes an +// argument that we shouldn\'t really care about this, since we ignore the fact +// that coercing values to string keys can cause unintended aliasing in general. +// Barring some compelling use case for that in this context, though, we choose +// to be strict. +function unification_dict_keys_invariant( + x: Array<{[k:B]:any}>, +) { + let a: Array<{[k:A]:any}> = x; // error + let b: Array<{[k:B]:any}> = x; // ok + let c: Array<{[k:C]:any}> = x; // error +} + +function subtype_dict_keys_invariant( + x: {[k:B]:any}, +) { + let a: {[k:A]:any} = x; // error + let b: {[k:B]:any} = x; // ok + let c: {[k:C]:any} = x; // error +} + +function unification_mix_with_declared_props_invariant_l( + x: Array<{[k:string]:B}>, +) { + let a: Array<{[k:string]:B, p:A}> = x; // error: A ~> B + a[0].p = new A; // x[0].p no longer B + + let b: Array<{[k:string]:B, p:B}> = x; // ok + + let c: Array<{[k:string]:B, p:C}> = x; // error + (x[0].p: C); // not true +} + +function unification_mix_with_declared_props_invariant_r( + xa: Array<{[k:string]:A, p:B}>, + xb: Array<{[k:string]:B, p:B}>, + xc: Array<{[k:string]:C, p:B}>, +) { + let a: Array<{[k:string]:A}> = xa; // error + a[0].p = new A; // xa[0].p no longer B + + let b: Array<{[k:string]:B}> = xb; // ok + + let c: Array<{[k:string]:C}> = xc; // error + (xc[0].p: C); // not true +} + +function subtype_mix_with_declared_props_invariant_l( + x: {[k:string]:B}, +) { + let a: {[k:string]:B, p:A} = x; // error: A ~> B + a.p = new A; // x.p no longer B + + let b: {[k:string]:B, p:B} = x; // ok + + let c: {[k:string]:B, p:C} = x; // error + (x.p: C); // not true +} + +function subtype_mix_with_declared_props_invariant_r( + xa: {[k:string]:A, p:B}, + xb: {[k:string]:B, p:B}, + xc: {[k:string]:C, p:B}, +) { + let a: {[k:string]:A} = xa; // error + a.p = new A; // xa.p no longer B + + let b: {[k:string]:B} = xb; // ok + + let c: {[k:string]:C} = xc; // error + (xc.p: C); // not true +} + +function unification_dict_to_obj( + x: Array<{[k:string]:X}>, +): Array<{p:X}> { + return x; // error: if allowed, could write {p:X,q:Y} into \`x\` +} + +function unification_obj_to_dict( + x: Array<{p:X}>, +): Array<{[k:string]:X}> { + return x; // error: if allowed, could write {p:X,q:Y} into returned array +} + +function subtype_dict_to_obj( + x: {[k:string]:B}, +) { + let a: {p:A} = x; // error + a.p = new A; // x.p no longer B + + let b: {p:B} = x; // ok + + let c: {p:C} = x; // error + (x.p: C); // not true +} + +function subtype_obj_to_dict( + x: {p:B}, +) { + let a: {[k:string]:A} = x; // error + a.p = new A; // x.p no longer B + + let b: {[k:string]:B} = x; + + let c: {[k:string]:C} = x; // error + (x.p: C); // not true +} + +// Only props in l which are not in u must match indexer, but must do so +// exactly. +function subtype_obj_to_mixed( + x: {p:B, x:X}, +) { + let a: {[k:string]:A,x:X} = x; // error (as above), but exclusive of x + let b: {[k:string]:B,x:X} = x; // ok, + let c: {[k:string]:C,x:X} = x; // error (as above), but exclusive of x +} + +function unification_dict_to_mixed( + x: Array<{[k:string]:B}>, +) { + let a: Array<{[k:string]:B, p:A}> = x; // error + let b: Array<{[k:string]:B, p:B}> = x; // ok + let c: Array<{[k:string]:B, p:C}> = x; // error +} + +function subtype_dict_to_mixed( + x: {[k:string]:B}, +) { + let a: {[k:string]:B, p:A} = x; // error + let b: {[k:string]:B, p:B} = x; // ok + let c: {[k:string]:B, p:C} = x; // error +} + +function subtype_dict_to_optional_a( + x: {[k:string]:B}, +) { + let a: {p?:A} = x; // error +} + +function subtype_dict_to_optional_b( + x: {[k:string]:B}, +) { + let b: {p?:B} = x; // ok +} + +function subtype_dict_to_optional_c( + x: {[k:string]:B}, +) { + let c: {p?:C} = x; // error +} + +function subtype_optional_a_to_dict( + x: {p?:A}, +): {[k:string]:B} { // error: A ~> B + return x; +} + +function subtype_optional_b_to_dict( + x: {p?:B}, +): {[k:string]:B} { // ok + return x; +} + +function subtype_optional_c_to_dict( + x: {p?:C}, +): {[k:string]:B} { // error: C ~> B + return x; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test incompatible.js 1`] = ` +"/* @flow */ + +var x : {[key: string]: string} = {}; +var y : {[key: string]: number} = x; // 2 errors, number !~> string & vice versa +var z : {[key: number]: string} = x; // 2 errors, string !~> number & vice versa + +var a : {[key: string]: ?string} = {}; +var b : {[key: string]: string} = a; // 2 errors (null & undefined) +var c : {[key: string]: ?string} = b; // 2 errors, since c[\'x\'] = null updates b + +// 2 errors (number !~> string, string !~> number) +function foo0(x: Array<{[key: string]: number}>): Array<{[key: string]: string}> { + return x; +} + +// error, fooBar:string !~> number (x\'s dictionary) +function foo1( + x: Array<{[key: string]: number}> +): Array<{[key: string]: number, fooBar: string}> { + return x; +} + +function foo2( + x: Array<{[key: string]: mixed}> +): Array<{[key: string]: mixed, fooBar: string}> { + x[0].fooBar = 123; // OK, since number ~> mixed (x elem\'s dictionary) + return x; // error: mixed ~> string +} + +// OK, since we assume dictionaries have every key +function foo3(x: {[key: string]: number}): {foo: number} { + return x; +} + +// error: foo can\'t exist in x +function foo4(x: {[key: string]: number}): {[key: string]: number, foo: string} { + return x; +} + +// error, some prop in x could be incompatible (covariance) +function foo5(x: Array<{[key: string]: number}>): Array<{foo: number}> { + return x; +} + +// error, some prop in return could be incompatible +function foo6(x: Array<{foo: number}>): Array<{[key: string]: number}> { + return x; +} + +function foo7(x: {bar: string, [key: string]: number}) { + (x.bar: string); +} + +function foo8(x: {[key: string]: number}) { + (x.foo: string); // error + (x.foo: number); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test issue-1745.js 1`] = ` +"/* @flow */ + +class A { + x: {[k:string]: number}; + + m1() { + this.x = { bar: 0 }; // no error + } + + m2() { + this.x.foo = 0; // no error + } +} + +class B { + x: {[k:string]: number}; + + m2() { + this.x.foo = 0; // no error + } + + m1() { + this.x = { bar: 0 }; // no error + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +class A { + x: { [k: string]: number }; + m1() { + this.x = { bar: 0 };// no error + } + m2() { + this.x.foo = 0;// no error + } +} +class B { + x: { [k: string]: number }; + m2() { + this.x.foo = 0;// no error + } + m1() { + this.x = { bar: 0 };// no error + } +} + +" +`; + +exports[`test test.js 1`] = ` +"type Params = {count: number; [name: string]: string}; +type QueryFunction = (params: Params) => string; + +var o: { foo: QueryFunction } = { + foo(params) { + return params.count; // error, number ~/~ string + } +}; + +module.exports = o; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test_client.js 1`] = ` +"var o = require(\'./test\'); + +o.foo = function (params) { + return params.count; // error, number ~/~ string +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var o = require(\"./test\"); +o.foo = function(params) { + return params.count;// error, number ~/~ string +}; + +" +`; diff --git a/tests/dictionary/any.js b/tests/dictionary/any.js new file mode 100644 index 000000000000..b8b846674dab --- /dev/null +++ b/tests/dictionary/any.js @@ -0,0 +1,5 @@ +/* @flow */ + +const dict: {[key: string]: number} = {} +const k: any = 'foo' +const val: string = dict[k] // error: number incompatible with string diff --git a/tests/dictionary/compatible.js b/tests/dictionary/compatible.js new file mode 100644 index 000000000000..87299c2ca1d9 --- /dev/null +++ b/tests/dictionary/compatible.js @@ -0,0 +1,15 @@ +/* @flow */ + +function foo0(x: Array<{[key: string]: mixed}>): Array<{[key: string]: mixed}> { + // this adds a fooBar property to the param type, which should NOT cause + // an error in the return type because it is a dictionary. + x[0].fooBar = 'foobar'; + return x; +} + +function foo2( + x: {[key: string]: number} +): {[key: string]: number, +toString: () => string} { + // x's prototype has a toString method + return x; +} diff --git a/tests/dictionary/dictionary.js b/tests/dictionary/dictionary.js new file mode 100644 index 000000000000..0a7150fa447a --- /dev/null +++ b/tests/dictionary/dictionary.js @@ -0,0 +1,320 @@ +/* Dictionary types are object types that include an indexer, which specifies a + * key type and a value type. The presence of an indexer makes the object type + * unsealed, but all added properties must be consistent with the indexer + * signature. + * + * Dictionaries can be used to represent the common idiom of objects used as + * maps. They can also be used to represent array-like objects, e.g., NodeList + * from the DOM API. + * + * A dictionary is assumed to have every property described by it's key type. + * This behavior is similar to the behavior of arrays, which are assumed to have + * a value at every index. + * + * @flow + */ + +// Some logic is variance-sensitive. +class A {} +class B extends A {} +class C extends B {} + +// Just a couple of short type names. Compare to string/number. +class X {} +class Y {} + +// Any property can be set on a dict with string keys. +function set_prop_to_string_key( + o: {[k:string]:any}, +) { + o.prop = "ok"; +} + +// **UNSOUND** +// This is allowed by design. We don't track get/set and we don't wrap the +// return type in a maybe. +function unsound_dict_has_every_key( + o: {[k:string]:X}, +) { + (o.p: X); // ok + (o["p"]: X); // ok +} + +// As with any object type, we can assign subtypes to properties. +function set_prop_covariant( + o: {[k:string]:B}, +) { + o.p = new A; // error, A ~> B + o.p = new B; // ok + o.p = new C; // ok +} + +// This isn't specific behavior to dictionaries, but for completeness... +function get_prop_contravariant( + o: {[k:string]:B}, +) { + (o.p: A); // ok + (o.p: B); // ok + (o.p: C); // error, C ~> B +} + +// Dot-notation can not be used to add properties to dictionaries with +// non-string keys, because keys are strings. +function add_prop_to_nonstring_key_dot( + o: {[k:number]:any}, +) { + o.prop = "err"; // error: string ~> number +} + +// Bracket notation can be used to add properties to dictionaries with +// non-string keys, even though all keys are strings. This is a convenient +// affordance. +function add_prop_to_nonstring_key_bracket( + o: {[k:number]:any}, +) { + o[0] = "ok"; +} + +// Objects can be part dict, part not by mixing an indexer with declared props. +function mix_with_declared_props( + o: {[k:number]:X,p:Y}, + x: X, + y: Y, +) { + (o[0]: X); // ok + (o.p: Y); // ok + o[0] = x; // ok + o.p = y; // ok +} + +// Indeed, dict types are still Objects and have Object.prototype stuff +function object_prototype( + o: {[k:string]:number}, +): {[k:string]:number, +toString: () => string} { + (o.toString(): boolean); // error: string ~> boolean + return o; // ok +} + +// **UNSOUND** +// Because we support non-string props w/ bracket notation, it's possible to +// write into a declared prop unsoundly. +function unsound_string_conversion_alias_declared_prop( + o: {[k:number]:any, "0":X}, +) { + o[0] = "not-x"; // a["0"] no longer X +} + +function unification_dict_values_invariant( + x: Array<{[k:string]:B}>, +) { + let a: Array<{[k:string]:A}> = x; // error + a[0].p = new A; // in[0].p no longer B + + let b: Array<{[k:string]:B}> = x; // ok + + let c: Array<{[k:string]:C}> = x; // error + (x[0].p: C); // not true +} + +function subtype_dict_values_invariant( + x: {[k:string]:B}, +) { + let a: {[k:string]:A} = x; // error + a.p = new A; // x[0].p no longer B + + let b: {[k:string]:B} = x; // ok + + let c: {[k:string]:C} = x; // error + (x.p: C); // not true +} + +function subtype_dict_values_fresh_exception() { + let a: {[k:string]:A} = { + a: new A, // ok, A == A + b: new B, // ok, B <: A + c: new C, // ok, C <: A + }; + + let b: {[k:string]:B} = { + a: new A, // error, A not <: B + b: new B, // ok, B == B + c: new C, // ok, C <: A + }; + + let c: {[k:string]:C} = { + a: new A, // error, A not <: C + b: new B, // error, A not <: C + c: new C, // ok, C == C + }; +} + +// Actually, unsound_string_conversion_alias_declared_prop behavior makes an +// argument that we shouldn't really care about this, since we ignore the fact +// that coercing values to string keys can cause unintended aliasing in general. +// Barring some compelling use case for that in this context, though, we choose +// to be strict. +function unification_dict_keys_invariant( + x: Array<{[k:B]:any}>, +) { + let a: Array<{[k:A]:any}> = x; // error + let b: Array<{[k:B]:any}> = x; // ok + let c: Array<{[k:C]:any}> = x; // error +} + +function subtype_dict_keys_invariant( + x: {[k:B]:any}, +) { + let a: {[k:A]:any} = x; // error + let b: {[k:B]:any} = x; // ok + let c: {[k:C]:any} = x; // error +} + +function unification_mix_with_declared_props_invariant_l( + x: Array<{[k:string]:B}>, +) { + let a: Array<{[k:string]:B, p:A}> = x; // error: A ~> B + a[0].p = new A; // x[0].p no longer B + + let b: Array<{[k:string]:B, p:B}> = x; // ok + + let c: Array<{[k:string]:B, p:C}> = x; // error + (x[0].p: C); // not true +} + +function unification_mix_with_declared_props_invariant_r( + xa: Array<{[k:string]:A, p:B}>, + xb: Array<{[k:string]:B, p:B}>, + xc: Array<{[k:string]:C, p:B}>, +) { + let a: Array<{[k:string]:A}> = xa; // error + a[0].p = new A; // xa[0].p no longer B + + let b: Array<{[k:string]:B}> = xb; // ok + + let c: Array<{[k:string]:C}> = xc; // error + (xc[0].p: C); // not true +} + +function subtype_mix_with_declared_props_invariant_l( + x: {[k:string]:B}, +) { + let a: {[k:string]:B, p:A} = x; // error: A ~> B + a.p = new A; // x.p no longer B + + let b: {[k:string]:B, p:B} = x; // ok + + let c: {[k:string]:B, p:C} = x; // error + (x.p: C); // not true +} + +function subtype_mix_with_declared_props_invariant_r( + xa: {[k:string]:A, p:B}, + xb: {[k:string]:B, p:B}, + xc: {[k:string]:C, p:B}, +) { + let a: {[k:string]:A} = xa; // error + a.p = new A; // xa.p no longer B + + let b: {[k:string]:B} = xb; // ok + + let c: {[k:string]:C} = xc; // error + (xc.p: C); // not true +} + +function unification_dict_to_obj( + x: Array<{[k:string]:X}>, +): Array<{p:X}> { + return x; // error: if allowed, could write {p:X,q:Y} into `x` +} + +function unification_obj_to_dict( + x: Array<{p:X}>, +): Array<{[k:string]:X}> { + return x; // error: if allowed, could write {p:X,q:Y} into returned array +} + +function subtype_dict_to_obj( + x: {[k:string]:B}, +) { + let a: {p:A} = x; // error + a.p = new A; // x.p no longer B + + let b: {p:B} = x; // ok + + let c: {p:C} = x; // error + (x.p: C); // not true +} + +function subtype_obj_to_dict( + x: {p:B}, +) { + let a: {[k:string]:A} = x; // error + a.p = new A; // x.p no longer B + + let b: {[k:string]:B} = x; + + let c: {[k:string]:C} = x; // error + (x.p: C); // not true +} + +// Only props in l which are not in u must match indexer, but must do so +// exactly. +function subtype_obj_to_mixed( + x: {p:B, x:X}, +) { + let a: {[k:string]:A,x:X} = x; // error (as above), but exclusive of x + let b: {[k:string]:B,x:X} = x; // ok, + let c: {[k:string]:C,x:X} = x; // error (as above), but exclusive of x +} + +function unification_dict_to_mixed( + x: Array<{[k:string]:B}>, +) { + let a: Array<{[k:string]:B, p:A}> = x; // error + let b: Array<{[k:string]:B, p:B}> = x; // ok + let c: Array<{[k:string]:B, p:C}> = x; // error +} + +function subtype_dict_to_mixed( + x: {[k:string]:B}, +) { + let a: {[k:string]:B, p:A} = x; // error + let b: {[k:string]:B, p:B} = x; // ok + let c: {[k:string]:B, p:C} = x; // error +} + +function subtype_dict_to_optional_a( + x: {[k:string]:B}, +) { + let a: {p?:A} = x; // error +} + +function subtype_dict_to_optional_b( + x: {[k:string]:B}, +) { + let b: {p?:B} = x; // ok +} + +function subtype_dict_to_optional_c( + x: {[k:string]:B}, +) { + let c: {p?:C} = x; // error +} + +function subtype_optional_a_to_dict( + x: {p?:A}, +): {[k:string]:B} { // error: A ~> B + return x; +} + +function subtype_optional_b_to_dict( + x: {p?:B}, +): {[k:string]:B} { // ok + return x; +} + +function subtype_optional_c_to_dict( + x: {p?:C}, +): {[k:string]:B} { // error: C ~> B + return x; +} diff --git a/tests/dictionary/incompatible.js b/tests/dictionary/incompatible.js new file mode 100644 index 000000000000..dc73b7e71422 --- /dev/null +++ b/tests/dictionary/incompatible.js @@ -0,0 +1,57 @@ +/* @flow */ + +var x : {[key: string]: string} = {}; +var y : {[key: string]: number} = x; // 2 errors, number !~> string & vice versa +var z : {[key: number]: string} = x; // 2 errors, string !~> number & vice versa + +var a : {[key: string]: ?string} = {}; +var b : {[key: string]: string} = a; // 2 errors (null & undefined) +var c : {[key: string]: ?string} = b; // 2 errors, since c['x'] = null updates b + +// 2 errors (number !~> string, string !~> number) +function foo0(x: Array<{[key: string]: number}>): Array<{[key: string]: string}> { + return x; +} + +// error, fooBar:string !~> number (x's dictionary) +function foo1( + x: Array<{[key: string]: number}> +): Array<{[key: string]: number, fooBar: string}> { + return x; +} + +function foo2( + x: Array<{[key: string]: mixed}> +): Array<{[key: string]: mixed, fooBar: string}> { + x[0].fooBar = 123; // OK, since number ~> mixed (x elem's dictionary) + return x; // error: mixed ~> string +} + +// OK, since we assume dictionaries have every key +function foo3(x: {[key: string]: number}): {foo: number} { + return x; +} + +// error: foo can't exist in x +function foo4(x: {[key: string]: number}): {[key: string]: number, foo: string} { + return x; +} + +// error, some prop in x could be incompatible (covariance) +function foo5(x: Array<{[key: string]: number}>): Array<{foo: number}> { + return x; +} + +// error, some prop in return could be incompatible +function foo6(x: Array<{foo: number}>): Array<{[key: string]: number}> { + return x; +} + +function foo7(x: {bar: string, [key: string]: number}) { + (x.bar: string); +} + +function foo8(x: {[key: string]: number}) { + (x.foo: string); // error + (x.foo: number); +} diff --git a/tests/dictionary/issue-1745.js b/tests/dictionary/issue-1745.js new file mode 100644 index 000000000000..cef88e22aa47 --- /dev/null +++ b/tests/dictionary/issue-1745.js @@ -0,0 +1,25 @@ +/* @flow */ + +class A { + x: {[k:string]: number}; + + m1() { + this.x = { bar: 0 }; // no error + } + + m2() { + this.x.foo = 0; // no error + } +} + +class B { + x: {[k:string]: number}; + + m2() { + this.x.foo = 0; // no error + } + + m1() { + this.x = { bar: 0 }; // no error + } +} diff --git a/tests/dictionary/jsfmt.spec.js b/tests/dictionary/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/dictionary/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/dictionary/test.js b/tests/dictionary/test.js new file mode 100644 index 000000000000..8b2b99223b98 --- /dev/null +++ b/tests/dictionary/test.js @@ -0,0 +1,10 @@ +type Params = {count: number; [name: string]: string}; +type QueryFunction = (params: Params) => string; + +var o: { foo: QueryFunction } = { + foo(params) { + return params.count; // error, number ~/~ string + } +}; + +module.exports = o; diff --git a/tests/dictionary/test_client.js b/tests/dictionary/test_client.js new file mode 100644 index 000000000000..28e9c5808816 --- /dev/null +++ b/tests/dictionary/test_client.js @@ -0,0 +1,5 @@ +var o = require('./test'); + +o.foo = function (params) { + return params.count; // error, number ~/~ string +} diff --git a/tests/disjoint-union-perf/__snapshots__/jsfmt.spec.js.snap b/tests/disjoint-union-perf/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..2afb93d5ec39 --- /dev/null +++ b/tests/disjoint-union-perf/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,840 @@ +exports[`test ast.js 1`] = ` +"/** + * @flow + */ + +export type InferredType = + | \'unknown\' + | \'gender\' + | \'enum\' + | \'number-or-string\' + | \'number\' + | \'string\' + | \'error\' +; + +export type Pos = { + firstLine: number, + firstColumn: number, + lastLine: number, + lastColumn: number, +}; + +export type TypedBinaryOpNode = { + exprNodeType: \'binary_op\', + binaryOp: \'plus\' | \'multiply\' | \'divide\' | \'minus\', + lhs: TypedNode, + rhs: TypedNode, + pos: Pos, + exprType: InferredType, + typed: true, +} + +export type TypedUnaryMinusNode = { + exprNodeType: \'unary_minus\', + op: TypedNode, + pos: Pos, + exprType: InferredType, + typed: true, +} + +export type TypedNumberNode = { + exprNodeType: \'number\', + value: number, + pos: Pos, + exprType: \'number\', + typed: true, +} + +export type TypedStringLiteralNode = { + exprNodeType: \'string_literal\', + value: string, + pos: Pos, + exprType: \'string\', + typed: true, +} + +export type TypedVariableNode = { + exprNodeType: \'variable\', + name: string, + pos: Pos, + exprType: InferredType, + typed: true, +}; + +export type TypedFunctionInvocationNode = { + exprNodeType: \'function_invocation\', + name: string, + parameters: TypedNode[], + pos: Pos, + exprType: \'error\' | \'string\', + typed: true, +} + +export type TypedNode = + | TypedBinaryOpNode + | TypedUnaryMinusNode + | TypedNumberNode + | TypedStringLiteralNode + | TypedVariableNode + | TypedFunctionInvocationNode +; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test emit.js 1`] = ` +"/** + * @flow + */ +import * as t from \'./jsAst\'; + +const b = t.builders; + +import type { + TypedNode +} from \'./ast\'; + +function getBinaryOp(op: \'plus\' | \'minus\' | \'divide\' | \'multiply\') : \'+\' | \'-\' | \'*\' | \'/\' { + switch (op) { + case \'plus\': + return \'+\'; + case \'minus\': + return \'-\'; + case \'divide\': + return \'/\'; + case \'multiply\': + return \'*\'; + default: + throw new Error(\'Invalid binary operator: \' + op); + } +} + +export function emitExpression(node: TypedNode) : t.Expression { + switch (node.exprNodeType) { + case \'string_literal\': // FALLTHROUGH + case \'number\': + return b.literal(node.value); + case \'variable\': + return b.memberExpression( + b.identifier(\'vars\'), + b.identifier(node.name), + false + ); + case \'binary_op\': { + const lhs = emitExpression(node.lhs); + const rhs = emitExpression(node.rhs); + + const op = getBinaryOp(node.binaryOp); + return b.binaryExpression(op, lhs, rhs); + } + case \'unary_minus\': { + const operand = emitExpression(node.op); + return b.unaryExpression(\'-\', operand, true); + } + case \'function_invocation\': { + const callee = b.memberExpression( + b.identifier(\'fns\'), + b.identifier(node.name), + false + ); + + const args = node.parameters.map( + (n) => emitExpression(n) + ); + + return b.callExpression(callee, args); + } + default: + throw new Error(\'Unknown expression type: \' + node.type); + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test jsAst.js 1`] = ` +"/** + * @flow + */ +export type Comment = { + loc: ?SourceLocation, + value: string, + leading: boolean, + trailing: boolean, +}; + +export type SourceLocation = { + start: SourcePosition, + end: SourcePosition, + source: ?string, +}; + +export type SourcePosition = { + line: number, + column: number, +}; + +export type File = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'File\', + program: Program, +} + +export type Program = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'Program\', + body: Statement[], +} + +export type BinaryOperator = + |\'==\' + | \'!=\' + | \'===\' + | \'!==\' + | \'<\' + | \'<=\' + | \'>\' + | \'>=\' + | \'<<\' + | \'>>\' + | \'>>>\' + | \'+\' + | \'-\' + | \'*\' + | \'/\' + | \'%\' + | \'&\' // TODO Missing from the Parser API. + | \'|\' + | \'^\' + | \'in\' + | \'instanceof\' + | \'..\' +; + +export type UnaryOperator = + | \'-\' + | \'+\' + | \'!\' + | \'~\' + | \'typeof\' + | \'void\' + | \'delete\' +; + +export type AssignmentOperator = + | \'=\' + | \'+=\' + | \'-=\' + | \'*=\' + | \'/=\' + | \'%=\' + | \'<<=\' + | \'>>=\' + | \'>>>=\' + | \'|=\' + | \'^=\' + | \'&=\' +; + +export type UpdateOperator = + | \'++\' + | \'--\' +; + +export type LogicalOperator = + | \'&&\' + | \'||\' +; + +export type Node = + | EmptyStatement + | BlockStatement + | ExpressionStatement + | IfStatement + | BreakStatement + | ContinueStatement + | ReturnStatement + | ThrowStatement + | WhileStatement + | ForStatement + | ForInStatement + | TryStatement + | CatchClause + | Identifier + | Literal + | ThisExpression + | ArrayExpression + | ObjectExpreession + | Property + | FunctionExpression + | BinaryExpression + | UnaryExpression + | AssignmentExpression + | UpdateExpression + | LogicalExpression + | ConditionalExpression + | NewExpression + | CallExpression + | MemberExpression + | VariableDeclaration + | FunctionDeclaration + | VariableDeclarator +; + +export type Statement = + | BlockStatement + | EmptyStatement + | ExpressionStatement + | IfStatement + | BreakStatement + | ContinueStatement + | ReturnStatement + | ThrowStatement + | WhileStatement + | ForStatement + | ForInStatement + | TryStatement + | Declaration +; + +export type EmptyStatement = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'EmptyStatement\', +} + +export type BlockStatement = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'BlockStatement\', + body: Statement[], +} + +export type ExpressionStatement = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'ExpressionStatement\', + expression: Expression, +} + +export type IfStatement = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'IfStatement\', + test: Expression, + consequent: Statement, + alternate: ?Statement, +} + +export type BreakStatement = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'BreakStatement\', + label: ?Identifier, +} + +export type ContinueStatement = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'ContinueStatement\', + label: ?Identifier, +} + +export type ReturnStatement = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'ReturnStatement\', + argument: ?Expression, +} + +export type ThrowStatement = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'ThrowStatement\', + argument: ?Expression, +} + +export type WhileStatement = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'WhileStatement\', + test: Expression, + body: Statement, +} + +export type ForStatement = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'ForStatement\', + init: ?(VariableDeclaration | Expression), + test: ?Expression, + update: ?Expression, + body: Statement, +} + +export type ForInStatement = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'ForInStatement\', + left: VariableDeclaration | Expression, + right: Expression, + body: Statement, +} + +export type TryStatement = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'TryStatement\', + block: BlockStatement, + handler: ?CatchClause, + handlers: CatchClause[], + finalizer: ?BlockStatement, +}; + +export type CatchClause = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'CatchClause\', + param: Pattern, + guard: ?Expression, + body: BlockStatement, +}; + +export type Expression = + | Identifier + | ThisExpression + | Literal + | FunctionExpression + | BinaryExpression + | UnaryExpression + | AssignmentExpression + | UpdateExpression + | LogicalExpression + | ConditionalExpression + | NewExpression + | CallExpression + | MemberExpression + | ArrayExpression + | ObjectExpreession +; + +export type Identifier = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'Identifier\', + name: string, +} + +export type Literal = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'Literal\', + value: ?(string | boolean | number | RegExp), + regex: ?{ pattern: string, flags: string }, +} + +export type ThisExpression = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'ThisExpression\', +} + +export type ArrayExpression = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'ArrayExpression\', + elements: Expression[], +} + +export type ObjectExpreession = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'ObjectExpression\', + properties: Property[], +} + +export type Property = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'Property\', + kind: \'init\' | \'get\' | \'set\', + key: Literal | Identifier, + value: Expression, +}; + +export type FunctionExpression = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'FunctionExpression\', + id: ?Identifier, + params: Pattern[], + body: BlockStatement, +} + +export type BinaryExpression = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'BinaryExpression\', + operator: BinaryOperator, + left: Expression, + right: Expression, +} + +export type UnaryExpression = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'UnaryExpression\', + operator: UnaryOperator, + argument: Expression, + prefix: boolean, +}; + +export type AssignmentExpression = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'AssignmentExpression\', + operator: AssignmentOperator, + left: Pattern, + right: Expression, +}; + +export type UpdateExpression = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'UpdateExpression\', + operator: UpdateOperator, + argument: Expression, + prefix: boolean, +}; + +export type LogicalExpression = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'LogicalExpression\', + operator: LogicalOperator, + left: Expression, + right: Expression, +}; + +export type ConditionalExpression = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'ConditionalExpression\', + test: Expression, + consequent: Expression, + alternate: Expression, +}; + +export type NewExpression = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'NewExpression\', + callee: Expression, + arguments: Expression[], +}; + +export type CallExpression = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'CallExpression\', + callee: Expression, + arguments: Expression[], +}; + +export type MemberExpression = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'MemberExpression\', + object: Expression, + property: Identifier | Expression, + computed: bool, +} +// ast-types exports all expressions as patterns. +// That seems not like it was intended. +export type Pattern = + | Identifier +; + +export type Declaration = + | VariableDeclaration + | FunctionDeclaration +; + +export type VariableDeclaration = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'VariableDeclaration\', + kind: \'var\' | \'let\' | \'const\', + declarations: VariableDeclarator[], +} + +export type FunctionDeclaration = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'FunctionDeclaration\', + id: Identifier, + body: BlockStatement, + params: Pattern[], +} + +export type VariableDeclarator = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: \'VariableDeclarator\', + id: Pattern, + init: ?Expression, +} + +const a : any = null; + +export const builders : { + emptyStatement() : EmptyStatement, + blockStatement( + body: Statement[] + ) : BlockStatement, + expressionStatement( + expression: Expression + ) : ExpressionStatement, + ifStatement( + test: Expression, + consequent: Statement, + alternate?: Statement + ) : IfStatement, + breakStatement( + label?: Identifier + ) : BreakStatement, + continueStatement( + label?: Identifier + ) : ContinueStatement, + returnStatement( + argument: ?Expression + ) : ReturnStatement, + throwStatement( + argument: ?Expression + ) : ThrowStatement, + whileStatement( + test: Expression, + body: Statement + ) : WhileStatement, + forStatement( + init: ?(VariableDeclaration | Expression), + test: ?Expression, + update: ?Expression, + body: Statement + ) : ForStatement, + forInStatement( + left: VariableDeclaration | Expression, + right: Expression, + body: Statement + ) : ForInStatement, + tryStatement( + block: BlockStatement, + handler: ?CatchClause, + handlers: CatchClause[], + finalizer?: BlockStatement + ) : TryStatement, + catchClause( + param: Pattern, + guard: ?Expression, + body: BlockStatement + ) : CatchClause, + identifier( + name: string + ) : Identifier, + literal( + value: ?(string | boolean | number | RegExp), + regex?: { pattern: string, flags: string } + ) : Literal, + thisExpression() : ThisExpression, + arrayExpression( + elements: Expression[] + ) : ArrayExpression, + objectExpreession( + properties: Property[] + ) : ObjectExpreession, + property( + kind: \'init\' | \'get\' | \'set\', + key: Literal | Identifier, + value: Expression + ) : Property, + functionExpression( + id: ?Identifier, + params: Pattern[], + body: BlockStatement + ) : FunctionExpression, + binaryExpression( + operator: BinaryOperator, + left: Expression, + right: Expression + ) : BinaryExpression, + unaryExpression( + operator: UnaryOperator, + argument: Expression, + prefix: boolean + ) : UnaryExpression, + assignmentExpression( + operator: AssignmentOperator, + left: Pattern, + right: Expression + ) : AssignmentExpression, + updateExpression( + operator: UpdateOperator, + argument: Expression, + prefix: boolean + ) : UpdateExpression, + logicalExpression( + operator: LogicalOperator, + left: Expression, + right: Expression + ) : LogicalExpression, + conditionalExpression( + test: Expression, + consequent: Expression, + alternate: Expression + ) : ConditionalExpression, + newExpression( + callee: Expression, + arguments: Expression[] + ) : NewExpression, + callExpression( + callee: Expression, + arguments: Expression[] + ) : CallExpression, + memberExpression( + object: Expression, + property: Identifier | Expression, + computed: bool + ) : MemberExpression, + variableDeclaration( + kind: \'var\' | \'let\' | \'const\', + declarations: VariableDeclarator[] + ) : VariableDeclaration, + functionDeclaration( + id: Identifier, + body: BlockStatement, + params: Pattern[] + ) : FunctionDeclaration, + variableDeclarator( + id: Pattern, + init?: Expression + ) : VariableDeclarator, +} = a; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/disjoint-union-perf/ast.js b/tests/disjoint-union-perf/ast.js new file mode 100644 index 000000000000..8705bde03415 --- /dev/null +++ b/tests/disjoint-union-perf/ast.js @@ -0,0 +1,80 @@ +/** + * @flow + */ + +export type InferredType = + | 'unknown' + | 'gender' + | 'enum' + | 'number-or-string' + | 'number' + | 'string' + | 'error' +; + +export type Pos = { + firstLine: number, + firstColumn: number, + lastLine: number, + lastColumn: number, +}; + +export type TypedBinaryOpNode = { + exprNodeType: 'binary_op', + binaryOp: 'plus' | 'multiply' | 'divide' | 'minus', + lhs: TypedNode, + rhs: TypedNode, + pos: Pos, + exprType: InferredType, + typed: true, +} + +export type TypedUnaryMinusNode = { + exprNodeType: 'unary_minus', + op: TypedNode, + pos: Pos, + exprType: InferredType, + typed: true, +} + +export type TypedNumberNode = { + exprNodeType: 'number', + value: number, + pos: Pos, + exprType: 'number', + typed: true, +} + +export type TypedStringLiteralNode = { + exprNodeType: 'string_literal', + value: string, + pos: Pos, + exprType: 'string', + typed: true, +} + +export type TypedVariableNode = { + exprNodeType: 'variable', + name: string, + pos: Pos, + exprType: InferredType, + typed: true, +}; + +export type TypedFunctionInvocationNode = { + exprNodeType: 'function_invocation', + name: string, + parameters: TypedNode[], + pos: Pos, + exprType: 'error' | 'string', + typed: true, +} + +export type TypedNode = + | TypedBinaryOpNode + | TypedUnaryMinusNode + | TypedNumberNode + | TypedStringLiteralNode + | TypedVariableNode + | TypedFunctionInvocationNode +; diff --git a/tests/disjoint-union-perf/emit.js b/tests/disjoint-union-perf/emit.js new file mode 100644 index 000000000000..49bef648e63c --- /dev/null +++ b/tests/disjoint-union-perf/emit.js @@ -0,0 +1,65 @@ +/** + * @flow + */ +import * as t from './jsAst'; + +const b = t.builders; + +import type { + TypedNode +} from './ast'; + +function getBinaryOp(op: 'plus' | 'minus' | 'divide' | 'multiply') : '+' | '-' | '*' | '/' { + switch (op) { + case 'plus': + return '+'; + case 'minus': + return '-'; + case 'divide': + return '/'; + case 'multiply': + return '*'; + default: + throw new Error('Invalid binary operator: ' + op); + } +} + +export function emitExpression(node: TypedNode) : t.Expression { + switch (node.exprNodeType) { + case 'string_literal': // FALLTHROUGH + case 'number': + return b.literal(node.value); + case 'variable': + return b.memberExpression( + b.identifier('vars'), + b.identifier(node.name), + false + ); + case 'binary_op': { + const lhs = emitExpression(node.lhs); + const rhs = emitExpression(node.rhs); + + const op = getBinaryOp(node.binaryOp); + return b.binaryExpression(op, lhs, rhs); + } + case 'unary_minus': { + const operand = emitExpression(node.op); + return b.unaryExpression('-', operand, true); + } + case 'function_invocation': { + const callee = b.memberExpression( + b.identifier('fns'), + b.identifier(node.name), + false + ); + + const args = node.parameters.map( + (n) => emitExpression(n) + ); + + return b.callExpression(callee, args); + } + default: + throw new Error('Unknown expression type: ' + node.type); + } +} diff --git a/tests/disjoint-union-perf/jsAst.js b/tests/disjoint-union-perf/jsAst.js new file mode 100644 index 000000000000..4ca78a03e62d --- /dev/null +++ b/tests/disjoint-union-perf/jsAst.js @@ -0,0 +1,636 @@ +/** + * @flow + */ +export type Comment = { + loc: ?SourceLocation, + value: string, + leading: boolean, + trailing: boolean, +}; + +export type SourceLocation = { + start: SourcePosition, + end: SourcePosition, + source: ?string, +}; + +export type SourcePosition = { + line: number, + column: number, +}; + +export type File = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'File', + program: Program, +} + +export type Program = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'Program', + body: Statement[], +} + +export type BinaryOperator = + |'==' + | '!=' + | '===' + | '!==' + | '<' + | '<=' + | '>' + | '>=' + | '<<' + | '>>' + | '>>>' + | '+' + | '-' + | '*' + | '/' + | '%' + | '&' // TODO Missing from the Parser API. + | '|' + | '^' + | 'in' + | 'instanceof' + | '..' +; + +export type UnaryOperator = + | '-' + | '+' + | '!' + | '~' + | 'typeof' + | 'void' + | 'delete' +; + +export type AssignmentOperator = + | '=' + | '+=' + | '-=' + | '*=' + | '/=' + | '%=' + | '<<=' + | '>>=' + | '>>>=' + | '|=' + | '^=' + | '&=' +; + +export type UpdateOperator = + | '++' + | '--' +; + +export type LogicalOperator = + | '&&' + | '||' +; + +export type Node = + | EmptyStatement + | BlockStatement + | ExpressionStatement + | IfStatement + | BreakStatement + | ContinueStatement + | ReturnStatement + | ThrowStatement + | WhileStatement + | ForStatement + | ForInStatement + | TryStatement + | CatchClause + | Identifier + | Literal + | ThisExpression + | ArrayExpression + | ObjectExpreession + | Property + | FunctionExpression + | BinaryExpression + | UnaryExpression + | AssignmentExpression + | UpdateExpression + | LogicalExpression + | ConditionalExpression + | NewExpression + | CallExpression + | MemberExpression + | VariableDeclaration + | FunctionDeclaration + | VariableDeclarator +; + +export type Statement = + | BlockStatement + | EmptyStatement + | ExpressionStatement + | IfStatement + | BreakStatement + | ContinueStatement + | ReturnStatement + | ThrowStatement + | WhileStatement + | ForStatement + | ForInStatement + | TryStatement + | Declaration +; + +export type EmptyStatement = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'EmptyStatement', +} + +export type BlockStatement = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'BlockStatement', + body: Statement[], +} + +export type ExpressionStatement = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'ExpressionStatement', + expression: Expression, +} + +export type IfStatement = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'IfStatement', + test: Expression, + consequent: Statement, + alternate: ?Statement, +} + +export type BreakStatement = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'BreakStatement', + label: ?Identifier, +} + +export type ContinueStatement = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'ContinueStatement', + label: ?Identifier, +} + +export type ReturnStatement = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'ReturnStatement', + argument: ?Expression, +} + +export type ThrowStatement = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'ThrowStatement', + argument: ?Expression, +} + +export type WhileStatement = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'WhileStatement', + test: Expression, + body: Statement, +} + +export type ForStatement = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'ForStatement', + init: ?(VariableDeclaration | Expression), + test: ?Expression, + update: ?Expression, + body: Statement, +} + +export type ForInStatement = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'ForInStatement', + left: VariableDeclaration | Expression, + right: Expression, + body: Statement, +} + +export type TryStatement = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'TryStatement', + block: BlockStatement, + handler: ?CatchClause, + handlers: CatchClause[], + finalizer: ?BlockStatement, +}; + +export type CatchClause = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'CatchClause', + param: Pattern, + guard: ?Expression, + body: BlockStatement, +}; + +export type Expression = + | Identifier + | ThisExpression + | Literal + | FunctionExpression + | BinaryExpression + | UnaryExpression + | AssignmentExpression + | UpdateExpression + | LogicalExpression + | ConditionalExpression + | NewExpression + | CallExpression + | MemberExpression + | ArrayExpression + | ObjectExpreession +; + +export type Identifier = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'Identifier', + name: string, +} + +export type Literal = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'Literal', + value: ?(string | boolean | number | RegExp), + regex: ?{ pattern: string, flags: string }, +} + +export type ThisExpression = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'ThisExpression', +} + +export type ArrayExpression = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'ArrayExpression', + elements: Expression[], +} + +export type ObjectExpreession = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'ObjectExpression', + properties: Property[], +} + +export type Property = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'Property', + kind: 'init' | 'get' | 'set', + key: Literal | Identifier, + value: Expression, +}; + +export type FunctionExpression = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'FunctionExpression', + id: ?Identifier, + params: Pattern[], + body: BlockStatement, +} + +export type BinaryExpression = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'BinaryExpression', + operator: BinaryOperator, + left: Expression, + right: Expression, +} + +export type UnaryExpression = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'UnaryExpression', + operator: UnaryOperator, + argument: Expression, + prefix: boolean, +}; + +export type AssignmentExpression = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'AssignmentExpression', + operator: AssignmentOperator, + left: Pattern, + right: Expression, +}; + +export type UpdateExpression = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'UpdateExpression', + operator: UpdateOperator, + argument: Expression, + prefix: boolean, +}; + +export type LogicalExpression = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'LogicalExpression', + operator: LogicalOperator, + left: Expression, + right: Expression, +}; + +export type ConditionalExpression = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'ConditionalExpression', + test: Expression, + consequent: Expression, + alternate: Expression, +}; + +export type NewExpression = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'NewExpression', + callee: Expression, + arguments: Expression[], +}; + +export type CallExpression = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'CallExpression', + callee: Expression, + arguments: Expression[], +}; + +export type MemberExpression = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'MemberExpression', + object: Expression, + property: Identifier | Expression, + computed: bool, +} +// ast-types exports all expressions as patterns. +// That seems not like it was intended. +export type Pattern = + | Identifier +; + +export type Declaration = + | VariableDeclaration + | FunctionDeclaration +; + +export type VariableDeclaration = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'VariableDeclaration', + kind: 'var' | 'let' | 'const', + declarations: VariableDeclarator[], +} + +export type FunctionDeclaration = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'FunctionDeclaration', + id: Identifier, + body: BlockStatement, + params: Pattern[], +} + +export type VariableDeclarator = { + source: ?string, + start: SourcePosition, + end: SourcePosition, + comments: ?Array, + type: 'VariableDeclarator', + id: Pattern, + init: ?Expression, +} + +const a : any = null; + +export const builders : { + emptyStatement() : EmptyStatement, + blockStatement( + body: Statement[] + ) : BlockStatement, + expressionStatement( + expression: Expression + ) : ExpressionStatement, + ifStatement( + test: Expression, + consequent: Statement, + alternate?: Statement + ) : IfStatement, + breakStatement( + label?: Identifier + ) : BreakStatement, + continueStatement( + label?: Identifier + ) : ContinueStatement, + returnStatement( + argument: ?Expression + ) : ReturnStatement, + throwStatement( + argument: ?Expression + ) : ThrowStatement, + whileStatement( + test: Expression, + body: Statement + ) : WhileStatement, + forStatement( + init: ?(VariableDeclaration | Expression), + test: ?Expression, + update: ?Expression, + body: Statement + ) : ForStatement, + forInStatement( + left: VariableDeclaration | Expression, + right: Expression, + body: Statement + ) : ForInStatement, + tryStatement( + block: BlockStatement, + handler: ?CatchClause, + handlers: CatchClause[], + finalizer?: BlockStatement + ) : TryStatement, + catchClause( + param: Pattern, + guard: ?Expression, + body: BlockStatement + ) : CatchClause, + identifier( + name: string + ) : Identifier, + literal( + value: ?(string | boolean | number | RegExp), + regex?: { pattern: string, flags: string } + ) : Literal, + thisExpression() : ThisExpression, + arrayExpression( + elements: Expression[] + ) : ArrayExpression, + objectExpreession( + properties: Property[] + ) : ObjectExpreession, + property( + kind: 'init' | 'get' | 'set', + key: Literal | Identifier, + value: Expression + ) : Property, + functionExpression( + id: ?Identifier, + params: Pattern[], + body: BlockStatement + ) : FunctionExpression, + binaryExpression( + operator: BinaryOperator, + left: Expression, + right: Expression + ) : BinaryExpression, + unaryExpression( + operator: UnaryOperator, + argument: Expression, + prefix: boolean + ) : UnaryExpression, + assignmentExpression( + operator: AssignmentOperator, + left: Pattern, + right: Expression + ) : AssignmentExpression, + updateExpression( + operator: UpdateOperator, + argument: Expression, + prefix: boolean + ) : UpdateExpression, + logicalExpression( + operator: LogicalOperator, + left: Expression, + right: Expression + ) : LogicalExpression, + conditionalExpression( + test: Expression, + consequent: Expression, + alternate: Expression + ) : ConditionalExpression, + newExpression( + callee: Expression, + arguments: Expression[] + ) : NewExpression, + callExpression( + callee: Expression, + arguments: Expression[] + ) : CallExpression, + memberExpression( + object: Expression, + property: Identifier | Expression, + computed: bool + ) : MemberExpression, + variableDeclaration( + kind: 'var' | 'let' | 'const', + declarations: VariableDeclarator[] + ) : VariableDeclaration, + functionDeclaration( + id: Identifier, + body: BlockStatement, + params: Pattern[] + ) : FunctionDeclaration, + variableDeclarator( + id: Pattern, + init?: Expression + ) : VariableDeclarator, +} = a; diff --git a/tests/disjoint-union-perf/jsfmt.spec.js b/tests/disjoint-union-perf/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/disjoint-union-perf/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/docblock_flow/__snapshots__/jsfmt.spec.js.snap b/tests/docblock_flow/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..3994c269287c --- /dev/null +++ b/tests/docblock_flow/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,108 @@ +exports[`test license_with_flow.js 1`] = ` +"/* Copyright example */ +/* @flow */ + +("": void); // error +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* Copyright example */ +/* @flow */ +("": void);// error + +" +`; + +exports[`test max_header_tokens.js 1`] = ` +"/* @flow */ +/* second token */ +/* third token */ +/** + * After max_header_tokens (in .flowconfig), we no longer care: + * + * @flow + */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; + +exports[`test multiple_flows_1.js 1`] = ` +"/* @flow */ +/* @flow */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; + +exports[`test multiple_flows_2.js 1`] = ` +"/** + * @flow + * @noflow + */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; + +exports[`test multiple_providesModule_1.js 1`] = ` +"/** + * @providesModule Foo + * @providesModule Bar + * @flow + */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; + +exports[`test multiple_providesModule_2.js 1`] = ` +"/** + * @providesModule Foo + * @flow + */ +/** + * @providesModule Bar + */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; + +exports[`test use_strict_with_flow.js 1`] = ` +""use strict"; +/* @flow */ + +("": void); // error +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +"use strict"; +/* @flow */ +("": void);// error + +" +`; + +exports[`test with_flow.js 1`] = ` +"/* @flow */ + +("": void); // error +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +("": void);// error + +" +`; + +exports[`test without_flow.js 1`] = ` +"/* some other comment */ + +("": void); // no error +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* some other comment */ +("": void);// no error + +" +`; diff --git a/tests/docblock_flow/jsfmt.spec.js b/tests/docblock_flow/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/docblock_flow/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/docblock_flow/license_with_flow.js b/tests/docblock_flow/license_with_flow.js new file mode 100644 index 000000000000..8b3cd6be2a8d --- /dev/null +++ b/tests/docblock_flow/license_with_flow.js @@ -0,0 +1,4 @@ +/* Copyright example */ +/* @flow */ + +("": void); // error diff --git a/tests/docblock_flow/max_header_tokens.js b/tests/docblock_flow/max_header_tokens.js new file mode 100644 index 000000000000..c8eb64fe8886 --- /dev/null +++ b/tests/docblock_flow/max_header_tokens.js @@ -0,0 +1,8 @@ +/* @flow */ +/* second token */ +/* third token */ +/** + * After max_header_tokens (in .flowconfig), we no longer care: + * + * @flow + */ diff --git a/tests/docblock_flow/multiple_flows_1.js b/tests/docblock_flow/multiple_flows_1.js new file mode 100644 index 000000000000..205d73fd152c --- /dev/null +++ b/tests/docblock_flow/multiple_flows_1.js @@ -0,0 +1,2 @@ +/* @flow */ +/* @flow */ diff --git a/tests/docblock_flow/multiple_flows_2.js b/tests/docblock_flow/multiple_flows_2.js new file mode 100644 index 000000000000..421903b00b29 --- /dev/null +++ b/tests/docblock_flow/multiple_flows_2.js @@ -0,0 +1,4 @@ +/** + * @flow + * @noflow + */ diff --git a/tests/docblock_flow/multiple_providesModule_1.js b/tests/docblock_flow/multiple_providesModule_1.js new file mode 100644 index 000000000000..5d78d1deb91c --- /dev/null +++ b/tests/docblock_flow/multiple_providesModule_1.js @@ -0,0 +1,5 @@ +/** + * @providesModule Foo + * @providesModule Bar + * @flow + */ diff --git a/tests/docblock_flow/multiple_providesModule_2.js b/tests/docblock_flow/multiple_providesModule_2.js new file mode 100644 index 000000000000..01b86e701744 --- /dev/null +++ b/tests/docblock_flow/multiple_providesModule_2.js @@ -0,0 +1,7 @@ +/** + * @providesModule Foo + * @flow + */ +/** + * @providesModule Bar + */ diff --git a/tests/docblock_flow/use_strict_with_flow.js b/tests/docblock_flow/use_strict_with_flow.js new file mode 100644 index 000000000000..5d065b6a9452 --- /dev/null +++ b/tests/docblock_flow/use_strict_with_flow.js @@ -0,0 +1,4 @@ +"use strict"; +/* @flow */ + +("": void); // error diff --git a/tests/docblock_flow/with_flow.js b/tests/docblock_flow/with_flow.js new file mode 100644 index 000000000000..83ee2f86af06 --- /dev/null +++ b/tests/docblock_flow/with_flow.js @@ -0,0 +1,3 @@ +/* @flow */ + +("": void); // error diff --git a/tests/docblock_flow/without_flow.js b/tests/docblock_flow/without_flow.js new file mode 100644 index 000000000000..d8ba64110515 --- /dev/null +++ b/tests/docblock_flow/without_flow.js @@ -0,0 +1,3 @@ +/* some other comment */ + +("": void); // no error diff --git a/tests/dom/CanvasRenderingContext2D.js b/tests/dom/CanvasRenderingContext2D.js new file mode 100644 index 000000000000..f6af4fc4f579 --- /dev/null +++ b/tests/dom/CanvasRenderingContext2D.js @@ -0,0 +1,13 @@ +// @flow + +let tests = [ + // fillRect + function(ctx: CanvasRenderingContext2D) { + ctx.fillRect(0, 0, 200, 100); + }, + + // moveTo + function(ctx: CanvasRenderingContext2D) { + ctx.moveTo('0', '1'); // error: should be numbers + }, +]; diff --git a/tests/dom/CustomEvent.js b/tests/dom/CustomEvent.js new file mode 100644 index 000000000000..4c73a4d8f5bb --- /dev/null +++ b/tests/dom/CustomEvent.js @@ -0,0 +1,9 @@ +// @flow + +let tests = [ + // CustomEvent + function(document: Document) { + const event = document.createEvent('CustomEvent'); + event.initCustomEvent('butts', true, false, { nice: 42 }); + } +]; diff --git a/tests/dom/Document.js b/tests/dom/Document.js new file mode 100644 index 000000000000..7bf234607fe1 --- /dev/null +++ b/tests/dom/Document.js @@ -0,0 +1,11 @@ +// @flow + +let tests = [ + // createElement + function(document: Document) { + (document.createElement('canvas'): HTMLCanvasElement); + (document.createElement('link'): HTMLLinkElement); + (document.createElement('option'): HTMLOptionElement); + (document.createElement('select'): HTMLSelectElement); + } +]; diff --git a/tests/dom/Element.js b/tests/dom/Element.js new file mode 100644 index 000000000000..9f8c65c2fc6e --- /dev/null +++ b/tests/dom/Element.js @@ -0,0 +1,18 @@ +// @flow + +let tests = [ + // scrollIntoView + function(element: Element) { + element.scrollIntoView(); + element.scrollIntoView(false); + element.scrollIntoView({}); + element.scrollIntoView({ behavior: 'smooth', block: 'end' }); + element.scrollIntoView({ block: 'end' }); + element.scrollIntoView({ behavior: 'smooth' }); + + // fails + element.scrollIntoView({ behavior: 'invalid' }); + element.scrollIntoView({ block: 'invalid' }); + element.scrollIntoView(1); + } +]; diff --git a/tests/dom/HTMLCanvasElement.js b/tests/dom/HTMLCanvasElement.js new file mode 100644 index 000000000000..1bad9066f73e --- /dev/null +++ b/tests/dom/HTMLCanvasElement.js @@ -0,0 +1,8 @@ +// @flow + +let tests = [ + // getContext + function(el: HTMLCanvasElement) { + (el.getContext('2d'): ?CanvasRenderingContext2D); + } +]; diff --git a/tests/dom/HTMLElement.js b/tests/dom/HTMLElement.js new file mode 100644 index 000000000000..d3942a275710 --- /dev/null +++ b/tests/dom/HTMLElement.js @@ -0,0 +1,18 @@ +// @flow + +let tests = [ + // scrollIntoView + function(element: HTMLElement) { + element.scrollIntoView(); + element.scrollIntoView(false); + element.scrollIntoView({}); + element.scrollIntoView({ behavior: 'smooth', block: 'end' }); + element.scrollIntoView({ block: 'end' }); + element.scrollIntoView({ behavior: 'smooth' }); + + // fails + element.scrollIntoView({ behavior: 'invalid' }); + element.scrollIntoView({ block: 'invalid' }); + element.scrollIntoView(1); + } +]; diff --git a/tests/dom/HTMLInputElement.js b/tests/dom/HTMLInputElement.js new file mode 100644 index 000000000000..48f1413addc9 --- /dev/null +++ b/tests/dom/HTMLInputElement.js @@ -0,0 +1,12 @@ +// @flow + +let tests = [ + // setRangeText + function(el: HTMLInputElement) { + el.setRangeText('foo'); + el.setRangeText('foo', 123); // end is required + el.setRangeText('foo', 123, 234); + el.setRangeText('foo', 123, 234, 'select'); + el.setRangeText('foo', 123, 234, 'bogus'); // invalid value + } +]; diff --git a/tests/dom/URL.js b/tests/dom/URL.js new file mode 100644 index 000000000000..ecba845d56da --- /dev/null +++ b/tests/dom/URL.js @@ -0,0 +1,20 @@ +/* @flow */ + +const a = new URL('http://flowtype.org/'); // correct +const b = new URL('/docs', a); // correct +const c = new URL('/docs', 'http://flowtype.org/'); // correct + +const d: URLSearchParams = c.searchParams; // correct +const e: string = c.path; // not correct +const f: string = c.pathname; // correct +const g: string = c.hash; // correct +const h: string = c.host; // correct +const i: string = c.hostname; // correct +const j: string = c.href; // correct +const l: string = c.origin; // correct +const m: string = c.password; // correct +const n: string = c.pathname; // correct +const o: string = c.port; // correct +const p: string = c.protocol; // correct +const q: string = c.search; // correct +const r: string = c.username; // correct diff --git a/tests/dom/__snapshots__/jsfmt.spec.js.snap b/tests/dom/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..9e1cc90c0e46 --- /dev/null +++ b/tests/dom/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,700 @@ +exports[`test CanvasRenderingContext2D.js 1`] = ` +"// @flow + +let tests = [ + // fillRect + function(ctx: CanvasRenderingContext2D) { + ctx.fillRect(0, 0, 200, 100); + }, + + // moveTo + function(ctx: CanvasRenderingContext2D) { + ctx.moveTo(\'0\', \'1\'); // error: should be numbers + }, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +let tests = [ + // fillRect + function(ctx: CanvasRenderingContext2D) { + ctx.fillRect(0, 0, 200, 100); + }, + // moveTo + function(ctx: CanvasRenderingContext2D) { + ctx.moveTo(\"0\", \"1\");// error: should be numbers + } +]; + +" +`; + +exports[`test CustomEvent.js 1`] = ` +"// @flow + +let tests = [ + // CustomEvent + function(document: Document) { + const event = document.createEvent(\'CustomEvent\'); + event.initCustomEvent(\'butts\', true, false, { nice: 42 }); + } +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +let tests = [ + // CustomEvent + function(document: Document) { + const event = document.createEvent(\"CustomEvent\"); + event.initCustomEvent(\"butts\", true, false, { nice: 42 }); + } +]; + +" +`; + +exports[`test Document.js 1`] = ` +"// @flow + +let tests = [ + // createElement + function(document: Document) { + (document.createElement(\'canvas\'): HTMLCanvasElement); + (document.createElement(\'link\'): HTMLLinkElement); + (document.createElement(\'option\'): HTMLOptionElement); + (document.createElement(\'select\'): HTMLSelectElement); + } +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +let tests = [ + // createElement + function(document: Document) { + (document.createElement(\"canvas\"): HTMLCanvasElement); + (document.createElement(\"link\"): HTMLLinkElement); + (document.createElement(\"option\"): HTMLOptionElement); + (document.createElement(\"select\"): HTMLSelectElement); + } +]; + +" +`; + +exports[`test Element.js 1`] = ` +"// @flow + +let tests = [ + // scrollIntoView + function(element: Element) { + element.scrollIntoView(); + element.scrollIntoView(false); + element.scrollIntoView({}); + element.scrollIntoView({ behavior: \'smooth\', block: \'end\' }); + element.scrollIntoView({ block: \'end\' }); + element.scrollIntoView({ behavior: \'smooth\' }); + + // fails + element.scrollIntoView({ behavior: \'invalid\' }); + element.scrollIntoView({ block: \'invalid\' }); + element.scrollIntoView(1); + } +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +let tests = [ + // scrollIntoView + function(element: Element) { + element.scrollIntoView(); + element.scrollIntoView(false); + element.scrollIntoView({}); + element.scrollIntoView({ behavior: \"smooth\", block: \"end\" }); + element.scrollIntoView({ block: \"end\" }); + element.scrollIntoView({ behavior: \"smooth\" }); + // fails + element.scrollIntoView({ behavior: \"invalid\" }); + element.scrollIntoView({ block: \"invalid\" }); + element.scrollIntoView(1); + } +]; + +" +`; + +exports[`test HTMLCanvasElement.js 1`] = ` +"// @flow + +let tests = [ + // getContext + function(el: HTMLCanvasElement) { + (el.getContext(\'2d\'): ?CanvasRenderingContext2D); + } +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +let tests = [ + // getContext + function(el: HTMLCanvasElement) { + (el.getContext(\"2d\"): ?CanvasRenderingContext2D); + } +]; + +" +`; + +exports[`test HTMLElement.js 1`] = ` +"// @flow + +let tests = [ + // scrollIntoView + function(element: HTMLElement) { + element.scrollIntoView(); + element.scrollIntoView(false); + element.scrollIntoView({}); + element.scrollIntoView({ behavior: \'smooth\', block: \'end\' }); + element.scrollIntoView({ block: \'end\' }); + element.scrollIntoView({ behavior: \'smooth\' }); + + // fails + element.scrollIntoView({ behavior: \'invalid\' }); + element.scrollIntoView({ block: \'invalid\' }); + element.scrollIntoView(1); + } +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +let tests = [ + // scrollIntoView + function(element: HTMLElement) { + element.scrollIntoView(); + element.scrollIntoView(false); + element.scrollIntoView({}); + element.scrollIntoView({ behavior: \"smooth\", block: \"end\" }); + element.scrollIntoView({ block: \"end\" }); + element.scrollIntoView({ behavior: \"smooth\" }); + // fails + element.scrollIntoView({ behavior: \"invalid\" }); + element.scrollIntoView({ block: \"invalid\" }); + element.scrollIntoView(1); + } +]; + +" +`; + +exports[`test HTMLInputElement.js 1`] = ` +"// @flow + +let tests = [ + // setRangeText + function(el: HTMLInputElement) { + el.setRangeText(\'foo\'); + el.setRangeText(\'foo\', 123); // end is required + el.setRangeText(\'foo\', 123, 234); + el.setRangeText(\'foo\', 123, 234, \'select\'); + el.setRangeText(\'foo\', 123, 234, \'bogus\'); // invalid value + } +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +let tests = [ + // setRangeText + function(el: HTMLInputElement) { + el.setRangeText(\"foo\"); + el.setRangeText(\"foo\", 123);// end is required + el.setRangeText(\"foo\", 123, 234); + el.setRangeText(\"foo\", 123, 234, \"select\"); + el.setRangeText(\"foo\", 123, 234, \"bogus\");// invalid value + } +]; + +" +`; + +exports[`test URL.js 1`] = ` +"/* @flow */ + +const a = new URL(\'http://flowtype.org/\'); // correct +const b = new URL(\'/docs\', a); // correct +const c = new URL(\'/docs\', \'http://flowtype.org/\'); // correct + +const d: URLSearchParams = c.searchParams; // correct +const e: string = c.path; // not correct +const f: string = c.pathname; // correct +const g: string = c.hash; // correct +const h: string = c.host; // correct +const i: string = c.hostname; // correct +const j: string = c.href; // correct +const l: string = c.origin; // correct +const m: string = c.password; // correct +const n: string = c.pathname; // correct +const o: string = c.port; // correct +const p: string = c.protocol; // correct +const q: string = c.search; // correct +const r: string = c.username; // correct +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +const a = new URL(\"http://flowtype.org/\");// correct +const b = new URL(\"/docs\", a);// correct +const c = new URL(\"/docs\", \"http://flowtype.org/\");// correct +const d: URLSearchParams = c.searchParams;// correct +const e: string = c.path;// not correct +const f: string = c.pathname;// correct +const g: string = c.hash;// correct +const h: string = c.host;// correct +const i: string = c.hostname;// correct +const j: string = c.href;// correct +const l: string = c.origin;// correct +const m: string = c.password;// correct +const n: string = c.pathname;// correct +const o: string = c.port;// correct +const p: string = c.protocol;// correct +const q: string = c.search;// correct +const r: string = c.username;// correct + +" +`; + +exports[`test eventtarget.js 1`] = ` +"// @flow + +let listener: EventListener = function (event: Event) :void {}; + +let tests = [ + // attachEvent + function() { + let target = new EventTarget(); + (target.attachEvent(\'foo\', listener): void); // invalid, may be undefined + (target.attachEvent && target.attachEvent(\'foo\', listener): void); // valid + }, + + // detachEvent + function() { + let target = new EventTarget(); + (target.detachEvent(\'foo\', listener): void); // invalid, may be undefined + (target.detachEvent && target.detachEvent(\'foo\', listener): void); // valid + }, + + function() { + window.onmessage = (event: MessageEvent) => { + (event.target: window); + }; + }, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +let listener: EventListener = function(event: Event): void { + +}; +let tests = [ + // attachEvent + function() { + let target = new EventTarget(); + (target.attachEvent(\"foo\", listener): void);// invalid, may be undefined + (target.attachEvent && target.attachEvent(\"foo\", listener): void);// valid + }, + // detachEvent + function() { + let target = new EventTarget(); + (target.detachEvent(\"foo\", listener): void);// invalid, may be undefined + (target.detachEvent && target.detachEvent(\"foo\", listener): void);// valid + }, + function() { + window.onmessage = (event: MessageEvent) => { + (event.target: window); + }; + } +]; + +" +`; + +exports[`test path2d.js 1`] = ` +"// @flow + +let tests = [ + // arcTo + function() { + let path = new Path2D(); + (path.arcTo(0, 0, 0, 0, 10): void); // valid + (path.arcTo(0, 0, 0, 0, 10, 20, 5): void); // valid + (path.arcTo(0, 0, 0, 0, 10, \'20\', 5): void); // invalid + }, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +let tests = [ + // arcTo + function() { + let path = new Path2D(); + (path.arcTo(0, 0, 0, 0, 10): void);// valid + (path.arcTo(0, 0, 0, 0, 10, 20, 5): void);// valid + (path.arcTo(0, 0, 0, 0, 10, \"20\", 5): void);// invalid + } +]; + +" +`; + +exports[`test registerElement.js 1`] = ` +"// @flow + +let tests = [ + // should work with Object.create() + function() { + document.registerElement(\'custom-element\', { + prototype: Object.create(HTMLElement.prototype, { + createdCallback: { value: function createdCallback () { + }}, + attachedCallback: { value: function attachedCallback () { + }}, + detachedCallback: { value: function detachedCallback () { + }}, + attributeChangedCallback: { + value: function attributeChangedCallback ( + attributeLocalName, + oldAttributeValue, + newAttributeValue, + attributeNamespace + ) { + } + } + }) + }) + }, + // or with Object.assign() + function() { + document.registerElement(\'custom-element\', { + prototype: Object.assign(Object.create(HTMLElement.prototype), { + createdCallback () { + }, + attachedCallback () { + }, + detachedCallback () { + }, + attributeChangedCallback ( + attributeLocalName, + oldAttributeValue, + newAttributeValue, + attributeNamespace + ) { + } + }) + }) + }, + // should complain about invalid callback parameters + function() { + document.registerElement(\'custom-element\', { + prototype: { + attributeChangedCallback( + localName: string, + oldVal: string, // Error: This might be null + newVal: string, // Error: This might be null + namespace: string) {} + }, + }); + }, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +let tests = [ + // should work with Object.create() + function() { + document.registerElement( + \"custom-element\", + { + prototype: Object.create( + HTMLElement.prototype, + { + createdCallback: { + value: function createdCallback() { + + } + }, + attachedCallback: { + value: function attachedCallback() { + + } + }, + detachedCallback: { + value: function detachedCallback() { + + } + }, + attributeChangedCallback: { + value: function attributeChangedCallback( + attributeLocalName, + oldAttributeValue, + newAttributeValue, + attributeNamespace + ) { + + } + } + } + ) + } + ); + }, + // or with Object.assign() + function() { + document.registerElement( + \"custom-element\", + { + prototype: Object.assign( + Object.create(HTMLElement.prototype), + { + createdCallback() { + + }, + attachedCallback() { + + }, + detachedCallback() { + + }, + attributeChangedCallback(attributeLocalName, + oldAttributeValue, + newAttributeValue, + attributeNamespace) { + + } + } + ) + } + ); + }, + // should complain about invalid callback parameters + function() { + document.registerElement( + \"custom-element\", + { + prototype: { + attributeChangedCallback(localName: string, + // Error: This might be null + oldVal: string, + // Error: This might be null + newVal: string, + namespace: string) { + + } + } + } + ); + } +]; + +" +`; + +exports[`test traversal.js 1`] = ` +"// @flow + +let tests = [ + // basic functionality + function() { + const i: NodeIterator<*,*> = document.createNodeIterator(document.body); + const filter: NodeFilter = i.filter; + const response: + typeof NodeFilter.FILTER_ACCEPT | + typeof NodeFilter.FILTER_REJECT | + typeof NodeFilter.FILTER_SKIP = + filter.acceptNode(document.body); + }, + function() { + const w: TreeWalker<*,*> = document.createTreeWalker(document.body); + const filter: NodeFilter = w.filter; + const response: + typeof NodeFilter.FILTER_ACCEPT | + typeof NodeFilter.FILTER_REJECT | + typeof NodeFilter.FILTER_SKIP = + filter.acceptNode(document.body); + }, + // rootNode must be a Node + function() { + document.createNodeIterator(document.body); // valid + document.createNodeIterator({}); // invalid + }, + function() { + document.createTreeWalker(document.body); + document.createTreeWalker({}); // invalid + }, + // Type Parameters + function() { + const _root = document.body; + const i = document.createNodeIterator(_root, NodeFilter.SHOW_ELEMENT); + const root: typeof _root = i.root; + const referenceNode: typeof _root | Element = i.referenceNode; + const previousNode: Element | null = i.previousNode(); + const nextNode: Element | null = i.nextNode(); + }, + function() { + const _root = document.body.attributes[0]; + const i = document.createNodeIterator(_root, NodeFilter.SHOW_ATTRIBUTE); + const root: typeof _root = i.root; + const referenceNode: typeof _root | Attr = i.referenceNode + const previousNode: Attr | null = i.previousNode(); + const nextNode: Attr | null = i.nextNode(); + }, + function() { + const _root = document.body; + const i = document.createNodeIterator(_root, NodeFilter.SHOW_TEXT); + const root: typeof _root = i.root; + const referenceNode: typeof _root | Text = i.referenceNode; + const previousNode: Text | null = i.previousNode(); + const nextNode: Text | null = i.nextNode(); + }, + function() { + const _root = document; + const i = document.createNodeIterator(_root, NodeFilter.SHOW_DOCUMENT); + const root: typeof _root = i.root; + const referenceNode: typeof _root | Document = i.referenceNode; + const previousNode: Document | null = i.previousNode(); + const nextNode: Document | null = i.nextNode(); + }, + function() { + const _root = document; + const i = document.createNodeIterator(_root, NodeFilter.SHOW_DOCUMENT_TYPE); + const root: typeof _root = i.root; + const referenceNode: typeof _root | DocumentType = i.referenceNode; + const previousNode: DocumentType | null = i.previousNode(); + const nextNode: DocumentType | null = i.nextNode(); + }, + function() { + const _root = document.createDocumentFragment(); + const i = document.createNodeIterator(_root, NodeFilter.SHOW_DOCUMENT_FRAGMENT); + const root: typeof _root = i.root; + const referenceNode: typeof _root | DocumentFragment = i.referenceNode; + const previousNode: DocumentFragment | null = i.previousNode(); + const nextNode: DocumentFragment | null = i.nextNode(); + }, + function() { + const _root = document.body; + const i = document.createNodeIterator(_root, NodeFilter.SHOW_ALL); + const root: typeof _root = i.root; + const referenceNode: typeof _root | Node = i.referenceNode; + const previousNode: Node | null = i.previousNode(); + const nextNode: Node | null = i.nextNode(); + }, + function() { + const _root = document.body; + const w = document.createTreeWalker(_root, NodeFilter.SHOW_ELEMENT); + const root: typeof _root = w.root; + const currentNode: typeof _root | Element = w.currentNode; + const parentNode: Element | null = w.parentNode(); + const firstChild: Element | null = w.firstChild(); + const lastChild: Element | null = w.lastChild(); + const previousSibling: Element | null = w.previousSibling(); + const nextSibling: Element | null = w.nextSibling(); + const previousNode: Element | null = w.previousNode(); + const nextNode: Element | null = w.nextNode(); + }, + function() { + const _root = document.body.attributes[0]; + const w = document.createTreeWalker(_root, NodeFilter.SHOW_ATTRIBUTE); + const root: typeof _root = w.root; + const currentNode: typeof _root | Attr = w.currentNode; + const parentNode: Attr | null = w.parentNode(); + const firstChild: Attr | null = w.firstChild(); + const lastChild: Attr | null = w.lastChild(); + const previousSibling: Attr | null = w.previousSibling(); + const nextSibling: Attr | null = w.nextSibling(); + const previousNode: Attr | null = w.previousNode(); + const nextNode: Attr | null = w.nextNode(); + }, + function() { + const _root = document.body; + const w = document.createTreeWalker(_root, NodeFilter.SHOW_TEXT); + const root: typeof _root = w.root; + const currentNode: typeof _root | Text = w.currentNode; + const parentNode: Text | null = w.parentNode(); + const firstChild: Text | null = w.firstChild(); + const lastChild: Text | null = w.lastChild(); + const previousSibling: Text | null = w.previousSibling(); + const nextSibling: Text | null = w.nextSibling(); + const previousNode: Text | null = w.previousNode(); + const nextNode: Text | null = w.nextNode(); + }, + function() { + const _root = document; + const w = document.createTreeWalker(_root, NodeFilter.SHOW_DOCUMENT); + const root: typeof _root = w.root; + const currentNode: typeof _root | Document = w.currentNode; + const parentNode: Document | null = w.parentNode(); + const firstChild: Document | null = w.firstChild(); + const lastChild: Document | null = w.lastChild(); + const previousSibling: Document | null = w.previousSibling(); + const nextSibling: Document | null = w.nextSibling(); + const previousNode: Document | null = w.previousNode(); + const nextNode: Document | null = w.nextNode(); + }, + function() { + const _root = document; + const w = document.createTreeWalker(_root, NodeFilter.SHOW_DOCUMENT_TYPE); + const root: typeof _root = w.root; + const currentNode: typeof _root | DocumentType = w.currentNode; + const parentNode: DocumentType | null = w.parentNode(); + const firstChild: DocumentType | null = w.firstChild(); + const lastChild: DocumentType | null = w.lastChild(); + const previousSibling: DocumentType | null = w.previousSibling(); + const nextSibling: DocumentType | null = w.nextSibling(); + const previousNode: DocumentType | null = w.previousNode(); + const nextNode: DocumentType | null = w.nextNode(); + }, + function() { + const _root = document.createDocumentFragment(); + const w = document.createTreeWalker(_root, NodeFilter.SHOW_DOCUMENT_FRAGMENT); + const root: typeof _root = w.root; + const currentNode: typeof _root | DocumentFragment = w.currentNode; + const parentNode: DocumentFragment | null = w.parentNode(); + const firstChild: DocumentFragment | null = w.firstChild(); + const lastChild: DocumentFragment | null = w.lastChild(); + const previousSibling: DocumentFragment | null = w.previousSibling(); + const nextSibling: DocumentFragment | null = w.nextSibling(); + const previousNode: DocumentFragment | null = w.previousNode(); + const nextNode: DocumentFragment | null = w.nextNode(); + }, + function() { + const _root = document.body; + const w = document.createTreeWalker(_root, NodeFilter.SHOW_ALL); + const root: typeof _root = w.root; + const currentNode: typeof _root | Node = w.currentNode; + const parentNode: Node | null = w.parentNode(); + const firstChild: Node | null = w.firstChild(); + const lastChild: Node | null = w.lastChild(); + const previousSibling: Node | null = w.previousSibling(); + const nextSibling: Node | null = w.nextSibling(); + const previousNode: Node | null = w.previousNode(); + const nextNode: Node | null = w.nextNode(); + }, + // NodeFilterInterface + function() { + document.createNodeIterator(document.body, -1, node => NodeFilter.FILTER_ACCEPT); // valid + document.createNodeIterator(document.body, -1, node => \'accept\'); // invalid + document.createNodeIterator(document.body, -1, { accept: node => NodeFilter.FILTER_ACCEPT }); // valid + document.createNodeIterator(document.body, -1, { accept: node => \'accept\' }); // invalid + document.createNodeIterator(document.body, -1, {}); // invalid + }, + function() { + document.createTreeWalker(document.body, -1, node => NodeFilter.FILTER_ACCEPT); // valid + document.createTreeWalker(document.body, -1, node => \'accept\'); // invalid + document.createTreeWalker(document.body, -1, { accept: node => NodeFilter.FILTER_ACCEPT }); // valid + document.createTreeWalker(document.body, -1, { accept: node => \'accept\' }); // invalid + document.createTreeWalker(document.body, -1, {}); // invalid + }, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/dom/eventtarget.js b/tests/dom/eventtarget.js new file mode 100644 index 000000000000..42a732475a6b --- /dev/null +++ b/tests/dom/eventtarget.js @@ -0,0 +1,25 @@ +// @flow + +let listener: EventListener = function (event: Event) :void {}; + +let tests = [ + // attachEvent + function() { + let target = new EventTarget(); + (target.attachEvent('foo', listener): void); // invalid, may be undefined + (target.attachEvent && target.attachEvent('foo', listener): void); // valid + }, + + // detachEvent + function() { + let target = new EventTarget(); + (target.detachEvent('foo', listener): void); // invalid, may be undefined + (target.detachEvent && target.detachEvent('foo', listener): void); // valid + }, + + function() { + window.onmessage = (event: MessageEvent) => { + (event.target: window); + }; + }, +]; diff --git a/tests/dom/jsfmt.spec.js b/tests/dom/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/dom/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/dom/path2d.js b/tests/dom/path2d.js new file mode 100644 index 000000000000..5610e88c5343 --- /dev/null +++ b/tests/dom/path2d.js @@ -0,0 +1,11 @@ +// @flow + +let tests = [ + // arcTo + function() { + let path = new Path2D(); + (path.arcTo(0, 0, 0, 0, 10): void); // valid + (path.arcTo(0, 0, 0, 0, 10, 20, 5): void); // valid + (path.arcTo(0, 0, 0, 0, 10, '20', 5): void); // invalid + }, +]; diff --git a/tests/dom/registerElement.js b/tests/dom/registerElement.js new file mode 100644 index 000000000000..f42bec596fce --- /dev/null +++ b/tests/dom/registerElement.js @@ -0,0 +1,58 @@ +// @flow + +let tests = [ + // should work with Object.create() + function() { + document.registerElement('custom-element', { + prototype: Object.create(HTMLElement.prototype, { + createdCallback: { value: function createdCallback () { + }}, + attachedCallback: { value: function attachedCallback () { + }}, + detachedCallback: { value: function detachedCallback () { + }}, + attributeChangedCallback: { + value: function attributeChangedCallback ( + attributeLocalName, + oldAttributeValue, + newAttributeValue, + attributeNamespace + ) { + } + } + }) + }) + }, + // or with Object.assign() + function() { + document.registerElement('custom-element', { + prototype: Object.assign(Object.create(HTMLElement.prototype), { + createdCallback () { + }, + attachedCallback () { + }, + detachedCallback () { + }, + attributeChangedCallback ( + attributeLocalName, + oldAttributeValue, + newAttributeValue, + attributeNamespace + ) { + } + }) + }) + }, + // should complain about invalid callback parameters + function() { + document.registerElement('custom-element', { + prototype: { + attributeChangedCallback( + localName: string, + oldVal: string, // Error: This might be null + newVal: string, // Error: This might be null + namespace: string) {} + }, + }); + }, +]; diff --git a/tests/dom/traversal.js b/tests/dom/traversal.js new file mode 100644 index 000000000000..a78ae27468e3 --- /dev/null +++ b/tests/dom/traversal.js @@ -0,0 +1,195 @@ +// @flow + +let tests = [ + // basic functionality + function() { + const i: NodeIterator<*,*> = document.createNodeIterator(document.body); + const filter: NodeFilter = i.filter; + const response: + typeof NodeFilter.FILTER_ACCEPT | + typeof NodeFilter.FILTER_REJECT | + typeof NodeFilter.FILTER_SKIP = + filter.acceptNode(document.body); + }, + function() { + const w: TreeWalker<*,*> = document.createTreeWalker(document.body); + const filter: NodeFilter = w.filter; + const response: + typeof NodeFilter.FILTER_ACCEPT | + typeof NodeFilter.FILTER_REJECT | + typeof NodeFilter.FILTER_SKIP = + filter.acceptNode(document.body); + }, + // rootNode must be a Node + function() { + document.createNodeIterator(document.body); // valid + document.createNodeIterator({}); // invalid + }, + function() { + document.createTreeWalker(document.body); + document.createTreeWalker({}); // invalid + }, + // Type Parameters + function() { + const _root = document.body; + const i = document.createNodeIterator(_root, NodeFilter.SHOW_ELEMENT); + const root: typeof _root = i.root; + const referenceNode: typeof _root | Element = i.referenceNode; + const previousNode: Element | null = i.previousNode(); + const nextNode: Element | null = i.nextNode(); + }, + function() { + const _root = document.body.attributes[0]; + const i = document.createNodeIterator(_root, NodeFilter.SHOW_ATTRIBUTE); + const root: typeof _root = i.root; + const referenceNode: typeof _root | Attr = i.referenceNode + const previousNode: Attr | null = i.previousNode(); + const nextNode: Attr | null = i.nextNode(); + }, + function() { + const _root = document.body; + const i = document.createNodeIterator(_root, NodeFilter.SHOW_TEXT); + const root: typeof _root = i.root; + const referenceNode: typeof _root | Text = i.referenceNode; + const previousNode: Text | null = i.previousNode(); + const nextNode: Text | null = i.nextNode(); + }, + function() { + const _root = document; + const i = document.createNodeIterator(_root, NodeFilter.SHOW_DOCUMENT); + const root: typeof _root = i.root; + const referenceNode: typeof _root | Document = i.referenceNode; + const previousNode: Document | null = i.previousNode(); + const nextNode: Document | null = i.nextNode(); + }, + function() { + const _root = document; + const i = document.createNodeIterator(_root, NodeFilter.SHOW_DOCUMENT_TYPE); + const root: typeof _root = i.root; + const referenceNode: typeof _root | DocumentType = i.referenceNode; + const previousNode: DocumentType | null = i.previousNode(); + const nextNode: DocumentType | null = i.nextNode(); + }, + function() { + const _root = document.createDocumentFragment(); + const i = document.createNodeIterator(_root, NodeFilter.SHOW_DOCUMENT_FRAGMENT); + const root: typeof _root = i.root; + const referenceNode: typeof _root | DocumentFragment = i.referenceNode; + const previousNode: DocumentFragment | null = i.previousNode(); + const nextNode: DocumentFragment | null = i.nextNode(); + }, + function() { + const _root = document.body; + const i = document.createNodeIterator(_root, NodeFilter.SHOW_ALL); + const root: typeof _root = i.root; + const referenceNode: typeof _root | Node = i.referenceNode; + const previousNode: Node | null = i.previousNode(); + const nextNode: Node | null = i.nextNode(); + }, + function() { + const _root = document.body; + const w = document.createTreeWalker(_root, NodeFilter.SHOW_ELEMENT); + const root: typeof _root = w.root; + const currentNode: typeof _root | Element = w.currentNode; + const parentNode: Element | null = w.parentNode(); + const firstChild: Element | null = w.firstChild(); + const lastChild: Element | null = w.lastChild(); + const previousSibling: Element | null = w.previousSibling(); + const nextSibling: Element | null = w.nextSibling(); + const previousNode: Element | null = w.previousNode(); + const nextNode: Element | null = w.nextNode(); + }, + function() { + const _root = document.body.attributes[0]; + const w = document.createTreeWalker(_root, NodeFilter.SHOW_ATTRIBUTE); + const root: typeof _root = w.root; + const currentNode: typeof _root | Attr = w.currentNode; + const parentNode: Attr | null = w.parentNode(); + const firstChild: Attr | null = w.firstChild(); + const lastChild: Attr | null = w.lastChild(); + const previousSibling: Attr | null = w.previousSibling(); + const nextSibling: Attr | null = w.nextSibling(); + const previousNode: Attr | null = w.previousNode(); + const nextNode: Attr | null = w.nextNode(); + }, + function() { + const _root = document.body; + const w = document.createTreeWalker(_root, NodeFilter.SHOW_TEXT); + const root: typeof _root = w.root; + const currentNode: typeof _root | Text = w.currentNode; + const parentNode: Text | null = w.parentNode(); + const firstChild: Text | null = w.firstChild(); + const lastChild: Text | null = w.lastChild(); + const previousSibling: Text | null = w.previousSibling(); + const nextSibling: Text | null = w.nextSibling(); + const previousNode: Text | null = w.previousNode(); + const nextNode: Text | null = w.nextNode(); + }, + function() { + const _root = document; + const w = document.createTreeWalker(_root, NodeFilter.SHOW_DOCUMENT); + const root: typeof _root = w.root; + const currentNode: typeof _root | Document = w.currentNode; + const parentNode: Document | null = w.parentNode(); + const firstChild: Document | null = w.firstChild(); + const lastChild: Document | null = w.lastChild(); + const previousSibling: Document | null = w.previousSibling(); + const nextSibling: Document | null = w.nextSibling(); + const previousNode: Document | null = w.previousNode(); + const nextNode: Document | null = w.nextNode(); + }, + function() { + const _root = document; + const w = document.createTreeWalker(_root, NodeFilter.SHOW_DOCUMENT_TYPE); + const root: typeof _root = w.root; + const currentNode: typeof _root | DocumentType = w.currentNode; + const parentNode: DocumentType | null = w.parentNode(); + const firstChild: DocumentType | null = w.firstChild(); + const lastChild: DocumentType | null = w.lastChild(); + const previousSibling: DocumentType | null = w.previousSibling(); + const nextSibling: DocumentType | null = w.nextSibling(); + const previousNode: DocumentType | null = w.previousNode(); + const nextNode: DocumentType | null = w.nextNode(); + }, + function() { + const _root = document.createDocumentFragment(); + const w = document.createTreeWalker(_root, NodeFilter.SHOW_DOCUMENT_FRAGMENT); + const root: typeof _root = w.root; + const currentNode: typeof _root | DocumentFragment = w.currentNode; + const parentNode: DocumentFragment | null = w.parentNode(); + const firstChild: DocumentFragment | null = w.firstChild(); + const lastChild: DocumentFragment | null = w.lastChild(); + const previousSibling: DocumentFragment | null = w.previousSibling(); + const nextSibling: DocumentFragment | null = w.nextSibling(); + const previousNode: DocumentFragment | null = w.previousNode(); + const nextNode: DocumentFragment | null = w.nextNode(); + }, + function() { + const _root = document.body; + const w = document.createTreeWalker(_root, NodeFilter.SHOW_ALL); + const root: typeof _root = w.root; + const currentNode: typeof _root | Node = w.currentNode; + const parentNode: Node | null = w.parentNode(); + const firstChild: Node | null = w.firstChild(); + const lastChild: Node | null = w.lastChild(); + const previousSibling: Node | null = w.previousSibling(); + const nextSibling: Node | null = w.nextSibling(); + const previousNode: Node | null = w.previousNode(); + const nextNode: Node | null = w.nextNode(); + }, + // NodeFilterInterface + function() { + document.createNodeIterator(document.body, -1, node => NodeFilter.FILTER_ACCEPT); // valid + document.createNodeIterator(document.body, -1, node => 'accept'); // invalid + document.createNodeIterator(document.body, -1, { accept: node => NodeFilter.FILTER_ACCEPT }); // valid + document.createNodeIterator(document.body, -1, { accept: node => 'accept' }); // invalid + document.createNodeIterator(document.body, -1, {}); // invalid + }, + function() { + document.createTreeWalker(document.body, -1, node => NodeFilter.FILTER_ACCEPT); // valid + document.createTreeWalker(document.body, -1, node => 'accept'); // invalid + document.createTreeWalker(document.body, -1, { accept: node => NodeFilter.FILTER_ACCEPT }); // valid + document.createTreeWalker(document.body, -1, { accept: node => 'accept' }); // invalid + document.createTreeWalker(document.body, -1, {}); // invalid + }, +]; diff --git a/tests/dump-types/__snapshots__/jsfmt.spec.js.snap b/tests/dump-types/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..56a9c6930a86 --- /dev/null +++ b/tests/dump-types/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,58 @@ +exports[`test import.js 1`] = ` +"// @flow +var num = 42; +function bar() { } +bar(); +module.exports = num; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +var num = 42; +function bar() { + +} +bar(); +module.exports = num; + +" +`; + +exports[`test test.js 1`] = ` +"// @flow +var num = require(\'./import\'); +function foo(x) { } +foo(0); +var a:string = num; + +function unannotated(x) { + return x; +} + +// test deduping of inferred types +const nullToUndefined = val => val === null ? undefined : val; + +function f0(x: ?Object) { return nullToUndefined(x); } +function f1(x: ?Object) { return nullToUndefined(x); } +function f2(x: ?string) { return nullToUndefined(x); } +function f3(x: ?string) { return nullToUndefined(x); } + +declare var idx: $Facebookism$Idx; +declare var obj: {a?: {b: ?{c: null | {d: number}}}}; +const idxResult = idx(obj, obj => obj.a.b.c.d); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/dump-types/import.js b/tests/dump-types/import.js new file mode 100644 index 000000000000..9a49778fd59d --- /dev/null +++ b/tests/dump-types/import.js @@ -0,0 +1,5 @@ +// @flow +var num = 42; +function bar() { } +bar(); +module.exports = num; diff --git a/tests/dump-types/jsfmt.spec.js b/tests/dump-types/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/dump-types/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/dump-types/test.js b/tests/dump-types/test.js new file mode 100644 index 000000000000..579402b464c0 --- /dev/null +++ b/tests/dump-types/test.js @@ -0,0 +1,21 @@ +// @flow +var num = require('./import'); +function foo(x) { } +foo(0); +var a:string = num; + +function unannotated(x) { + return x; +} + +// test deduping of inferred types +const nullToUndefined = val => val === null ? undefined : val; + +function f0(x: ?Object) { return nullToUndefined(x); } +function f1(x: ?Object) { return nullToUndefined(x); } +function f2(x: ?string) { return nullToUndefined(x); } +function f3(x: ?string) { return nullToUndefined(x); } + +declare var idx: $Facebookism$Idx; +declare var obj: {a?: {b: ?{c: null | {d: number}}}}; +const idxResult = idx(obj, obj => obj.a.b.c.d); diff --git a/tests/duplicate_methods/__snapshots__/jsfmt.spec.js.snap b/tests/duplicate_methods/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..01ab603de59f --- /dev/null +++ b/tests/duplicate_methods/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,88 @@ +exports[`test test.js 1`] = ` +"class C1 { + m() { } + m() { } +} + +new C1().m(); + +class C2 { + get m() { return 42; } + m() { } +} + +new C2().m(); + +class C3 { + set m(x) { } + m() { } +} + +new C3().m(); + +class C4 { + get m() { return 42; } + set m(x: number) { } +} + +new C4().m = new C4().m - 42; + +class C5 { + m() { } + get m() { return 42; } + set m(x: number) { } +} + +new C5().m = new C5().m - 42; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class C1 { + m() { + + } + m() { + + } +} +new C1().m(); +class C2 { + get m() { + return 42; + } + m() { + + } +} +new C2().m(); +class C3 { + set m(x) { + + } + m() { + + } +} +new C3().m(); +class C4 { + get m() { + return 42; + } + set m(x: number) { + + } +} +new C4().m = new C4().m - 42; +class C5 { + m() { + + } + get m() { + return 42; + } + set m(x: number) { + + } +} +new C5().m = new C5().m - 42; + +" +`; diff --git a/tests/duplicate_methods/jsfmt.spec.js b/tests/duplicate_methods/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/duplicate_methods/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/duplicate_methods/test.js b/tests/duplicate_methods/test.js new file mode 100644 index 000000000000..3c2aa61999b7 --- /dev/null +++ b/tests/duplicate_methods/test.js @@ -0,0 +1,35 @@ +class C1 { + m() { } + m() { } +} + +new C1().m(); + +class C2 { + get m() { return 42; } + m() { } +} + +new C2().m(); + +class C3 { + set m(x) { } + m() { } +} + +new C3().m(); + +class C4 { + get m() { return 42; } + set m(x: number) { } +} + +new C4().m = new C4().m - 42; + +class C5 { + m() { } + get m() { return 42; } + set m(x: number) { } +} + +new C5().m = new C5().m - 42; diff --git a/tests/encaps/__snapshots__/jsfmt.spec.js.snap b/tests/encaps/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..3277d93da996 --- /dev/null +++ b/tests/encaps/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,34 @@ +exports[`test encaps.js 1`] = ` +"class A { } +var a = new A(); +var s1 = \`l\${a.x}r\`; // error: no prop x in A + +function tag(strings,...values) { + var x:number = strings[0]; // error: string ~> number + return x; +} +var s2 = tag \`l\${42}r\`; + +function tag2(strings,...values) { + return { foo: "" }; // ok: tagged templates can return whatever +} + +var s3 = tag2 \`la la la\`; +(s3.foo: number); // error: string ~> number +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class A {} +var a = new A(); +var s1 = \`l\${a.x}r\`;// error: no prop x in A +function tag(strings, ...values) { + var x: number = strings[0];// error: string ~> number + return x; +} +var s2 = tag\`l\${42}r\`; +function tag2(strings, ...values) { + return { foo: "" };// ok: tagged templates can return whatever +} +var s3 = tag2\`la la la\`; +(s3.foo: number);// error: string ~> number + +" +`; diff --git a/tests/encaps/encaps.js b/tests/encaps/encaps.js new file mode 100644 index 000000000000..1f1127b42137 --- /dev/null +++ b/tests/encaps/encaps.js @@ -0,0 +1,16 @@ +class A { } +var a = new A(); +var s1 = `l${a.x}r`; // error: no prop x in A + +function tag(strings,...values) { + var x:number = strings[0]; // error: string ~> number + return x; +} +var s2 = tag `l${42}r`; + +function tag2(strings,...values) { + return { foo: "" }; // ok: tagged templates can return whatever +} + +var s3 = tag2 `la la la`; +(s3.foo: number); // error: string ~> number diff --git a/tests/encaps/jsfmt.spec.js b/tests/encaps/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/encaps/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/enumerror/__snapshots__/jsfmt.spec.js.snap b/tests/enumerror/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..77a0f8471311 --- /dev/null +++ b/tests/enumerror/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,45 @@ +exports[`test enumerror.js 1`] = ` +"/** @flow */ + +function isActive(ad: {state: $Keys<{ + PAUSED: string; + ACTIVE: string; + DELETED: string; +}>}): boolean { + return ad.state === \'ACTIVE\'; +}; +isActive({state: \'PAUSE\'}); + +var MyStates = { + PAUSED: \'PAUSED\', + ACTIVE: \'ACTIVE\', + DELETED: \'DELETED\', +}; +function isActive2(ad: {state: $Keys}): boolean { + return ad.state === MyStates.ACTIVE; +}; +isActive2({state: \'PAUSE\'}); + +type Keys = $Keys<{ x: any, y: any }>; +type Union = \"x\" | \"y\" + +function keys2union(s: Keys): Union { return s; } // ok +function union2keys(s: Union): Keys { return s; } // ok +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/enumerror/enumerror.js b/tests/enumerror/enumerror.js new file mode 100644 index 000000000000..ea4d96907a0e --- /dev/null +++ b/tests/enumerror/enumerror.js @@ -0,0 +1,26 @@ +/** @flow */ + +function isActive(ad: {state: $Keys<{ + PAUSED: string; + ACTIVE: string; + DELETED: string; +}>}): boolean { + return ad.state === 'ACTIVE'; +}; +isActive({state: 'PAUSE'}); + +var MyStates = { + PAUSED: 'PAUSED', + ACTIVE: 'ACTIVE', + DELETED: 'DELETED', +}; +function isActive2(ad: {state: $Keys}): boolean { + return ad.state === MyStates.ACTIVE; +}; +isActive2({state: 'PAUSE'}); + +type Keys = $Keys<{ x: any, y: any }>; +type Union = "x" | "y" + +function keys2union(s: Keys): Union { return s; } // ok +function union2keys(s: Union): Keys { return s; } // ok diff --git a/tests/enumerror/jsfmt.spec.js b/tests/enumerror/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/enumerror/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/equals/__snapshots__/jsfmt.spec.js.snap b/tests/equals/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..450ed18fa059 --- /dev/null +++ b/tests/equals/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,27 @@ +exports[`test equals.js 1`] = ` +"/* @flow */ + +(1 == 1); +("foo" == "bar"); +(1 == null); +(null == 1); +(1 == ""); // error +("" == 1); // error + +var x = (null : ?number); +(x == 1); +(1 == x); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +1 == 1; +"foo" == "bar"; +1 == null; +null == 1; +1 == "";// error +"" == 1;// error +var x = (null: ?number); +x == 1; +1 == x; + +" +`; diff --git a/tests/equals/equals.js b/tests/equals/equals.js new file mode 100644 index 000000000000..0e1f7297cce6 --- /dev/null +++ b/tests/equals/equals.js @@ -0,0 +1,12 @@ +/* @flow */ + +(1 == 1); +("foo" == "bar"); +(1 == null); +(null == 1); +(1 == ""); // error +("" == 1); // error + +var x = (null : ?number); +(x == 1); +(1 == x); diff --git a/tests/equals/jsfmt.spec.js b/tests/equals/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/equals/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/error_messages/__snapshots__/jsfmt.spec.js.snap b/tests/error_messages/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..68bde87c7fcd --- /dev/null +++ b/tests/error_messages/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,9 @@ +exports[`test errors.js 1`] = ` +"if (typeof define === 'function' && define.amd) { } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +if (typeof define === "function" && define.amd) { + +} + +" +`; diff --git a/tests/error_messages/errors.js b/tests/error_messages/errors.js new file mode 100644 index 000000000000..6d1c725fc570 --- /dev/null +++ b/tests/error_messages/errors.js @@ -0,0 +1 @@ +if (typeof define === 'function' && define.amd) { } diff --git a/tests/error_messages/jsfmt.spec.js b/tests/error_messages/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/error_messages/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/es6modules/B.js b/tests/es6modules/B.js new file mode 100644 index 000000000000..e4d0f1b41f44 --- /dev/null +++ b/tests/es6modules/B.js @@ -0,0 +1,3 @@ +/* @flow */ + +exports.numberValue = 42; diff --git a/tests/es6modules/C.js b/tests/es6modules/C.js new file mode 100644 index 000000000000..e667c4717b69 --- /dev/null +++ b/tests/es6modules/C.js @@ -0,0 +1 @@ +/* @flow */ diff --git a/tests/es6modules/CommonJS_Clobbering_Class.js b/tests/es6modules/CommonJS_Clobbering_Class.js new file mode 100644 index 000000000000..4fbd96118432 --- /dev/null +++ b/tests/es6modules/CommonJS_Clobbering_Class.js @@ -0,0 +1,21 @@ +/** + * @providesModule CommonJS_Clobbering_Class + * @flow + */ + +class Base { + static baseProp: number; +} + +class Test extends Base { + static childProp: number; + + static staticNumber1():number { return 1; } + static staticNumber2():number { return 2; } + static staticNumber3():number { return 3; } + + instNumber1():number { return 1; } + instNumber2():number { return 2; } +}; + +module.exports = Test; diff --git a/tests/es6modules/CommonJS_Clobbering_Frozen.js b/tests/es6modules/CommonJS_Clobbering_Frozen.js new file mode 100644 index 000000000000..322f60abeee2 --- /dev/null +++ b/tests/es6modules/CommonJS_Clobbering_Frozen.js @@ -0,0 +1,8 @@ +/** + * @providesModule CommonJS_Clobbering_Frozen + * @flow + */ + +module.exports = Object.freeze({ + numberValue1: 1, +}); diff --git a/tests/es6modules/CommonJS_Clobbering_Lit.js b/tests/es6modules/CommonJS_Clobbering_Lit.js new file mode 100644 index 000000000000..991b7bc37c12 --- /dev/null +++ b/tests/es6modules/CommonJS_Clobbering_Lit.js @@ -0,0 +1,12 @@ +/** + * @providesModule CommonJS_Clobbering_Lit + * @flow + */ + +module.exports = { + numberValue1: 1, + numberValue2: 2, + numberValue3: 3, + numberValue4: 4, + numberValue5: 5 +}; diff --git a/tests/es6modules/CommonJS_Named.js b/tests/es6modules/CommonJS_Named.js new file mode 100644 index 000000000000..698ec4f05276 --- /dev/null +++ b/tests/es6modules/CommonJS_Named.js @@ -0,0 +1,10 @@ +/** + * @providesModule CommonJS_Named + * @flow + */ + +exports.numberValue1 = 1; +exports.numberValue2 = 2; +exports.numberValue3 = 3; +exports.numberValue4 = 4; +exports.numberValue5 = 5; diff --git a/tests/es6modules/ES6_DefaultAndNamed.js b/tests/es6modules/ES6_DefaultAndNamed.js new file mode 100644 index 000000000000..82df6c20aaa8 --- /dev/null +++ b/tests/es6modules/ES6_DefaultAndNamed.js @@ -0,0 +1,4 @@ +/* @flow */ + +export default 42; +export var str = 'asdf'; diff --git a/tests/es6modules/ES6_Default_AnonClass1.js b/tests/es6modules/ES6_Default_AnonClass1.js new file mode 100644 index 000000000000..2890acbb2843 --- /dev/null +++ b/tests/es6modules/ES6_Default_AnonClass1.js @@ -0,0 +1,6 @@ +/** + * @providesModule ES6_Default_AnonClass1 + * @flow + */ + +export default class { givesANum(): number { return 42; }}; diff --git a/tests/es6modules/ES6_Default_AnonClass2.js b/tests/es6modules/ES6_Default_AnonClass2.js new file mode 100644 index 000000000000..1debca797b18 --- /dev/null +++ b/tests/es6modules/ES6_Default_AnonClass2.js @@ -0,0 +1,6 @@ +/** + * @providesModule ES6_Default_AnonClass2 + * @flow + */ + +export default class { givesANum(): number { return 42; }}; diff --git a/tests/es6modules/ES6_Default_AnonFunction1.js b/tests/es6modules/ES6_Default_AnonFunction1.js new file mode 100644 index 000000000000..ab2e1bbd8721 --- /dev/null +++ b/tests/es6modules/ES6_Default_AnonFunction1.js @@ -0,0 +1,6 @@ +/** + * @providesModule ES6_Default_AnonFunction1 + * @flow + */ + +export default function():number { return 42; } diff --git a/tests/es6modules/ES6_Default_AnonFunction2.js b/tests/es6modules/ES6_Default_AnonFunction2.js new file mode 100644 index 000000000000..2c60dbbe7c5c --- /dev/null +++ b/tests/es6modules/ES6_Default_AnonFunction2.js @@ -0,0 +1,6 @@ +/** + * @providesModule ES6_Default_AnonFunction2 + * @flow + */ + +export default function():number { return 42; } diff --git a/tests/es6modules/ES6_Default_NamedClass1.js b/tests/es6modules/ES6_Default_NamedClass1.js new file mode 100644 index 000000000000..d903c252bca6 --- /dev/null +++ b/tests/es6modules/ES6_Default_NamedClass1.js @@ -0,0 +1,11 @@ +/** + * @providesModule ES6_Default_NamedClass1 + * @flow + */ + +export default class Foo { givesANum(): number { return 42; }}; + +// Regression test for https://github.com/facebook/flow/issues/511 +// +// Default-exported class should also be available in local scope +new Foo(); diff --git a/tests/es6modules/ES6_Default_NamedClass2.js b/tests/es6modules/ES6_Default_NamedClass2.js new file mode 100644 index 000000000000..a42af92016a6 --- /dev/null +++ b/tests/es6modules/ES6_Default_NamedClass2.js @@ -0,0 +1,6 @@ +/** + * @providesModule ES6_Default_NamedClass2 + * @flow + */ + +export default class Foo { givesANum(): number { return 42; }}; diff --git a/tests/es6modules/ES6_Default_NamedFunction1.js b/tests/es6modules/ES6_Default_NamedFunction1.js new file mode 100644 index 000000000000..40fb4f30dda7 --- /dev/null +++ b/tests/es6modules/ES6_Default_NamedFunction1.js @@ -0,0 +1,6 @@ +/** + * @providesModule ES6_Default_NamedFunction1 + * @flow + */ + +export default function foo():number { return 42; } diff --git a/tests/es6modules/ES6_Default_NamedFunction2.js b/tests/es6modules/ES6_Default_NamedFunction2.js new file mode 100644 index 000000000000..5f3b5e9e03e0 --- /dev/null +++ b/tests/es6modules/ES6_Default_NamedFunction2.js @@ -0,0 +1,6 @@ +/** + * @providesModule ES6_Default_NamedFunction2 + * @flow + */ + +export default function foo():number { return 42; } diff --git a/tests/es6modules/ES6_ExportAllFromMulti.js b/tests/es6modules/ES6_ExportAllFromMulti.js new file mode 100644 index 000000000000..9153402a64d5 --- /dev/null +++ b/tests/es6modules/ES6_ExportAllFromMulti.js @@ -0,0 +1,4 @@ +// @flow + +export * from "./ES6_ExportAllFrom_Source1"; +export * from "./ES6_ExportAllFrom_Source2"; diff --git a/tests/es6modules/ES6_ExportAllFrom_Intermediary1.js b/tests/es6modules/ES6_ExportAllFrom_Intermediary1.js new file mode 100644 index 000000000000..8d2469c08c42 --- /dev/null +++ b/tests/es6modules/ES6_ExportAllFrom_Intermediary1.js @@ -0,0 +1,6 @@ +/** + * @providesModule ES6_ExportAllFrom_Intermediary1 + * @flow + */ + +export * from "ES6_ExportAllFrom_Source1"; diff --git a/tests/es6modules/ES6_ExportAllFrom_Intermediary2.js b/tests/es6modules/ES6_ExportAllFrom_Intermediary2.js new file mode 100644 index 000000000000..fcd626b9f811 --- /dev/null +++ b/tests/es6modules/ES6_ExportAllFrom_Intermediary2.js @@ -0,0 +1,6 @@ +/** + * @providesModule ES6_ExportAllFrom_Intermediary2 + * @flow + */ + +export * from "ES6_ExportAllFrom_Source2"; diff --git a/tests/es6modules/ES6_ExportAllFrom_Source1.js b/tests/es6modules/ES6_ExportAllFrom_Source1.js new file mode 100644 index 000000000000..4eb8683774cf --- /dev/null +++ b/tests/es6modules/ES6_ExportAllFrom_Source1.js @@ -0,0 +1,6 @@ +/** + * @providesModule ES6_ExportAllFrom_Source1 + * @flow + */ + +export var numberValue1 = 1; diff --git a/tests/es6modules/ES6_ExportAllFrom_Source2.js b/tests/es6modules/ES6_ExportAllFrom_Source2.js new file mode 100644 index 000000000000..5616a706ecea --- /dev/null +++ b/tests/es6modules/ES6_ExportAllFrom_Source2.js @@ -0,0 +1,6 @@ +/** + * @providesModule ES6_ExportAllFrom_Source2 + * @flow + */ + +export var numberValue2 = 1; diff --git a/tests/es6modules/ES6_ExportFrom_Intermediary1.js b/tests/es6modules/ES6_ExportFrom_Intermediary1.js new file mode 100644 index 000000000000..568cf963529e --- /dev/null +++ b/tests/es6modules/ES6_ExportFrom_Intermediary1.js @@ -0,0 +1,9 @@ +/** + * @providesModule ES6_ExportFrom_Intermediary1 + * @flow + */ + +export { + numberValue1, + numberValue2 as numberValue2_renamed +} from "ES6_ExportFrom_Source1"; diff --git a/tests/es6modules/ES6_ExportFrom_Intermediary2.js b/tests/es6modules/ES6_ExportFrom_Intermediary2.js new file mode 100644 index 000000000000..f4f62e4821e1 --- /dev/null +++ b/tests/es6modules/ES6_ExportFrom_Intermediary2.js @@ -0,0 +1,9 @@ +/** + * @providesModule ES6_ExportFrom_Intermediary2 + * @flow + */ + +export { + numberValue1, + numberValue2 as numberValue2_renamed2 +} from "ES6_ExportFrom_Source2"; diff --git a/tests/es6modules/ES6_ExportFrom_Source1.js b/tests/es6modules/ES6_ExportFrom_Source1.js new file mode 100644 index 000000000000..f41da3c58823 --- /dev/null +++ b/tests/es6modules/ES6_ExportFrom_Source1.js @@ -0,0 +1,6 @@ +/** + * @providesModule ES6_ExportFrom_Source1 + * @flow + */ + +export var numberValue1 = 1, numberValue2 = 2; diff --git a/tests/es6modules/ES6_ExportFrom_Source2.js b/tests/es6modules/ES6_ExportFrom_Source2.js new file mode 100644 index 000000000000..fd7541549551 --- /dev/null +++ b/tests/es6modules/ES6_ExportFrom_Source2.js @@ -0,0 +1,6 @@ +/** + * @providesModule ES6_ExportFrom_Source2 + * @flow + */ + +export var numberValue1 = 1, numberValue2 = 2; diff --git a/tests/es6modules/ES6_Named1.js b/tests/es6modules/ES6_Named1.js new file mode 100644 index 000000000000..2e71f0b72589 --- /dev/null +++ b/tests/es6modules/ES6_Named1.js @@ -0,0 +1,22 @@ +/** + * @providesModule ES6_Named1 + * @flow + */ + +var specifierNumber1 = 1; +var specifierNumber2 = 2; +var specifierNumber3 = 3; +var groupedSpecifierNumber1 = 1; +var groupedSpecifierNumber2 = 2; + +export {specifierNumber1}; +export {specifierNumber2 as specifierNumber2Renamed}; +export {specifierNumber3}; +export {groupedSpecifierNumber1, groupedSpecifierNumber2}; + +export function givesANumber(): number { return 42; }; +export class NumberGenerator { givesANumber(): number { return 42; }}; + +export var varDeclNumber1 = 1, varDeclNumber2 = 2; +export var {destructuredObjNumber} = {destructuredObjNumber: 1}; +export var [destructuredArrNumber] = [1] diff --git a/tests/es6modules/ES6_Named2.js b/tests/es6modules/ES6_Named2.js new file mode 100644 index 000000000000..4abc37e137ca --- /dev/null +++ b/tests/es6modules/ES6_Named2.js @@ -0,0 +1,20 @@ +/** + * @providesModule ES6_Named2 + * @flow + */ + +var specifierNumber4 = 1; +var specifierNumber5 = 2; +var groupedSpecifierNumber3 = 1; +var groupedSpecifierNumber4 = 2; + +export {specifierNumber4}; +export {specifierNumber5 as specifierNumber5Renamed}; +export {groupedSpecifierNumber3, groupedSpecifierNumber4}; + +export function givesANumber2(): number { return 42; }; +export class NumberGenerator2 { givesANumber(): number { return 42; }}; + +export var varDeclNumber3 = 1, varDeclNumber4 = 2; +export var {destructuredObjNumber2} = {destructuredObjNumber2: 1}; +export var [destructuredArrNumber2] = [1] diff --git a/tests/es6modules/ExportType.js b/tests/es6modules/ExportType.js new file mode 100644 index 000000000000..3949a39d32bf --- /dev/null +++ b/tests/es6modules/ExportType.js @@ -0,0 +1,3 @@ +// @flow + +export type typeAlias = number; diff --git a/tests/es6modules/ProvidesModuleA.js b/tests/es6modules/ProvidesModuleA.js new file mode 100644 index 000000000000..f2cbd52ce005 --- /dev/null +++ b/tests/es6modules/ProvidesModuleA.js @@ -0,0 +1,10 @@ +/** + * @providesModule A + * @flow + */ + +exports.numberValue1 = 42; +exports.numberValue2 = 42; +exports.numberValue3 = 42; +exports.numberValue4 = 42; +exports.stringValue = "str"; diff --git a/tests/es6modules/ProvidesModuleCJSDefault.js b/tests/es6modules/ProvidesModuleCJSDefault.js new file mode 100644 index 000000000000..a3d9c135a2c1 --- /dev/null +++ b/tests/es6modules/ProvidesModuleCJSDefault.js @@ -0,0 +1,8 @@ +/** + * @providesModule CJSDefault + * @flow + */ + +module.exports = { + numberValue: 42 +}; diff --git a/tests/es6modules/ProvidesModuleD.js b/tests/es6modules/ProvidesModuleD.js new file mode 100644 index 000000000000..a4d5ee842062 --- /dev/null +++ b/tests/es6modules/ProvidesModuleD.js @@ -0,0 +1,4 @@ +/** + * @providesModule D + * @flow + */ diff --git a/tests/es6modules/ProvidesModuleES6Default.js b/tests/es6modules/ProvidesModuleES6Default.js new file mode 100644 index 000000000000..636a1946109c --- /dev/null +++ b/tests/es6modules/ProvidesModuleES6Default.js @@ -0,0 +1,10 @@ +/** + * @providesModule ES6Default + * @flow + */ + +/* +export default { + numberValue: 42, +}; +*/ diff --git a/tests/es6modules/SideEffects.js b/tests/es6modules/SideEffects.js new file mode 100644 index 000000000000..e667c4717b69 --- /dev/null +++ b/tests/es6modules/SideEffects.js @@ -0,0 +1 @@ +/* @flow */ diff --git a/tests/es6modules/__snapshots__/jsfmt.spec.js.snap b/tests/es6modules/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..7e854f72ac46 --- /dev/null +++ b/tests/es6modules/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,1297 @@ +exports[`test B.js 1`] = ` +"/* @flow */ + +exports.numberValue = 42; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +exports.numberValue = 42; + +" +`; + +exports[`test C.js 1`] = ` +"/* @flow */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; + +exports[`test CommonJS_Clobbering_Class.js 1`] = ` +"/** + * @providesModule CommonJS_Clobbering_Class + * @flow + */ + +class Base { + static baseProp: number; +} + +class Test extends Base { + static childProp: number; + + static staticNumber1():number { return 1; } + static staticNumber2():number { return 2; } + static staticNumber3():number { return 3; } + + instNumber1():number { return 1; } + instNumber2():number { return 2; } +}; + +module.exports = Test; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule CommonJS_Clobbering_Class + * @flow + */ +class Base { + static baseProp: number; +} +class Test extends Base { + static childProp: number; + staticNumber1(): number { + return 1; + } + staticNumber2(): number { + return 2; + } + staticNumber3(): number { + return 3; + } + instNumber1(): number { + return 1; + } + instNumber2(): number { + return 2; + } +} +module.exports = Test; + +" +`; + +exports[`test CommonJS_Clobbering_Frozen.js 1`] = ` +"/** + * @providesModule CommonJS_Clobbering_Frozen + * @flow + */ + +module.exports = Object.freeze({ + numberValue1: 1, +}); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule CommonJS_Clobbering_Frozen + * @flow + */ +module.exports = Object.freeze({ numberValue1: 1 }); + +" +`; + +exports[`test CommonJS_Clobbering_Lit.js 1`] = ` +"/** + * @providesModule CommonJS_Clobbering_Lit + * @flow + */ + +module.exports = { + numberValue1: 1, + numberValue2: 2, + numberValue3: 3, + numberValue4: 4, + numberValue5: 5 +}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule CommonJS_Clobbering_Lit + * @flow + */ +module.exports = { + numberValue1: 1, + numberValue2: 2, + numberValue3: 3, + numberValue4: 4, + numberValue5: 5 +}; + +" +`; + +exports[`test CommonJS_Named.js 1`] = ` +"/** + * @providesModule CommonJS_Named + * @flow + */ + +exports.numberValue1 = 1; +exports.numberValue2 = 2; +exports.numberValue3 = 3; +exports.numberValue4 = 4; +exports.numberValue5 = 5; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule CommonJS_Named + * @flow + */ +exports.numberValue1 = 1; +exports.numberValue2 = 2; +exports.numberValue3 = 3; +exports.numberValue4 = 4; +exports.numberValue5 = 5; + +" +`; + +exports[`test ES6_Default_AnonClass1.js 1`] = ` +"/** + * @providesModule ES6_Default_AnonClass1 + * @flow + */ + +export default class { givesANum(): number { return 42; }}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule ES6_Default_AnonClass1 + * @flow + */ +export default class { + givesANum(): number { + return 42; + } +} + +" +`; + +exports[`test ES6_Default_AnonClass2.js 1`] = ` +"/** + * @providesModule ES6_Default_AnonClass2 + * @flow + */ + +export default class { givesANum(): number { return 42; }}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule ES6_Default_AnonClass2 + * @flow + */ +export default class { + givesANum(): number { + return 42; + } +} + +" +`; + +exports[`test ES6_Default_AnonFunction1.js 1`] = ` +"/** + * @providesModule ES6_Default_AnonFunction1 + * @flow + */ + +export default function():number { return 42; } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule ES6_Default_AnonFunction1 + * @flow + */ +export default function(): number { + return 42; +} + +" +`; + +exports[`test ES6_Default_AnonFunction2.js 1`] = ` +"/** + * @providesModule ES6_Default_AnonFunction2 + * @flow + */ + +export default function():number { return 42; } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule ES6_Default_AnonFunction2 + * @flow + */ +export default function(): number { + return 42; +} + +" +`; + +exports[`test ES6_Default_NamedClass1.js 1`] = ` +"/** + * @providesModule ES6_Default_NamedClass1 + * @flow + */ + +export default class Foo { givesANum(): number { return 42; }}; + +// Regression test for https://github.com/facebook/flow/issues/511 +// +// Default-exported class should also be available in local scope +new Foo(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule ES6_Default_NamedClass1 + * @flow + */ +export default class Foo { + givesANum(): number { + return 42; + } +} +// Regression test for https://github.com/facebook/flow/issues/511 +// +// Default-exported class should also be available in local scope +new Foo(); + +" +`; + +exports[`test ES6_Default_NamedClass2.js 1`] = ` +"/** + * @providesModule ES6_Default_NamedClass2 + * @flow + */ + +export default class Foo { givesANum(): number { return 42; }}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule ES6_Default_NamedClass2 + * @flow + */ +export default class Foo { + givesANum(): number { + return 42; + } +} + +" +`; + +exports[`test ES6_Default_NamedFunction1.js 1`] = ` +"/** + * @providesModule ES6_Default_NamedFunction1 + * @flow + */ + +export default function foo():number { return 42; } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule ES6_Default_NamedFunction1 + * @flow + */ +export default function foo(): number { + return 42; +} + +" +`; + +exports[`test ES6_Default_NamedFunction2.js 1`] = ` +"/** + * @providesModule ES6_Default_NamedFunction2 + * @flow + */ + +export default function foo():number { return 42; } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule ES6_Default_NamedFunction2 + * @flow + */ +export default function foo(): number { + return 42; +} + +" +`; + +exports[`test ES6_DefaultAndNamed.js 1`] = ` +"/* @flow */ + +export default 42; +export var str = \'asdf\'; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +export default 42 +export var str = \"asdf\"; + +" +`; + +exports[`test ES6_ExportAllFrom_Intermediary1.js 1`] = ` +"/** + * @providesModule ES6_ExportAllFrom_Intermediary1 + * @flow + */ + +export * from \"ES6_ExportAllFrom_Source1\"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule ES6_ExportAllFrom_Intermediary1 + * @flow + */ +export * from \"ES6_ExportAllFrom_Source1\" + +" +`; + +exports[`test ES6_ExportAllFrom_Intermediary2.js 1`] = ` +"/** + * @providesModule ES6_ExportAllFrom_Intermediary2 + * @flow + */ + +export * from \"ES6_ExportAllFrom_Source2\"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule ES6_ExportAllFrom_Intermediary2 + * @flow + */ +export * from \"ES6_ExportAllFrom_Source2\" + +" +`; + +exports[`test ES6_ExportAllFrom_Source1.js 1`] = ` +"/** + * @providesModule ES6_ExportAllFrom_Source1 + * @flow + */ + +export var numberValue1 = 1; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule ES6_ExportAllFrom_Source1 + * @flow + */ +export var numberValue1 = 1; + +" +`; + +exports[`test ES6_ExportAllFrom_Source2.js 1`] = ` +"/** + * @providesModule ES6_ExportAllFrom_Source2 + * @flow + */ + +export var numberValue2 = 1; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule ES6_ExportAllFrom_Source2 + * @flow + */ +export var numberValue2 = 1; + +" +`; + +exports[`test ES6_ExportAllFromMulti.js 1`] = ` +"// @flow + +export * from \"./ES6_ExportAllFrom_Source1\"; +export * from \"./ES6_ExportAllFrom_Source2\"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +export * from \"./ES6_ExportAllFrom_Source1\" +export * from \"./ES6_ExportAllFrom_Source2\" + +" +`; + +exports[`test ES6_ExportFrom_Intermediary1.js 1`] = ` +"/** + * @providesModule ES6_ExportFrom_Intermediary1 + * @flow + */ + +export { + numberValue1, + numberValue2 as numberValue2_renamed +} from \"ES6_ExportFrom_Source1\"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1785 + fromString(\", \").join(path.map(print, \"specifiers\")), + ^ + +TypeError: fromString(...).join is not a function + at printExportDeclaration (/src/printer.js:1785:26) + at genericPrintNoParens (/src/printer.js:488:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:238:16 +" +`; + +exports[`test ES6_ExportFrom_Intermediary2.js 1`] = ` +"/** + * @providesModule ES6_ExportFrom_Intermediary2 + * @flow + */ + +export { + numberValue1, + numberValue2 as numberValue2_renamed2 +} from \"ES6_ExportFrom_Source2\"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1785 + fromString(\", \").join(path.map(print, \"specifiers\")), + ^ + +TypeError: fromString(...).join is not a function + at printExportDeclaration (/src/printer.js:1785:26) + at genericPrintNoParens (/src/printer.js:488:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:238:16 +" +`; + +exports[`test ES6_ExportFrom_Source1.js 1`] = ` +"/** + * @providesModule ES6_ExportFrom_Source1 + * @flow + */ + +export var numberValue1 = 1, numberValue2 = 2; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule ES6_ExportFrom_Source1 + * @flow + */ +export var numberValue1 = 1numberValue2 = 2; + +" +`; + +exports[`test ES6_ExportFrom_Source2.js 1`] = ` +"/** + * @providesModule ES6_ExportFrom_Source2 + * @flow + */ + +export var numberValue1 = 1, numberValue2 = 2; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule ES6_ExportFrom_Source2 + * @flow + */ +export var numberValue1 = 1numberValue2 = 2; + +" +`; + +exports[`test ES6_Named1.js 1`] = ` +"/** + * @providesModule ES6_Named1 + * @flow + */ + +var specifierNumber1 = 1; +var specifierNumber2 = 2; +var specifierNumber3 = 3; +var groupedSpecifierNumber1 = 1; +var groupedSpecifierNumber2 = 2; + +export {specifierNumber1}; +export {specifierNumber2 as specifierNumber2Renamed}; +export {specifierNumber3}; +export {groupedSpecifierNumber1, groupedSpecifierNumber2}; + +export function givesANumber(): number { return 42; }; +export class NumberGenerator { givesANumber(): number { return 42; }}; + +export var varDeclNumber1 = 1, varDeclNumber2 = 2; +export var {destructuredObjNumber} = {destructuredObjNumber: 1}; +export var [destructuredArrNumber] = [1] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1785 + fromString(\", \").join(path.map(print, \"specifiers\")), + ^ + +TypeError: fromString(...).join is not a function + at printExportDeclaration (/src/printer.js:1785:26) + at genericPrintNoParens (/src/printer.js:488:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:238:16 +" +`; + +exports[`test ES6_Named2.js 1`] = ` +"/** + * @providesModule ES6_Named2 + * @flow + */ + +var specifierNumber4 = 1; +var specifierNumber5 = 2; +var groupedSpecifierNumber3 = 1; +var groupedSpecifierNumber4 = 2; + +export {specifierNumber4}; +export {specifierNumber5 as specifierNumber5Renamed}; +export {groupedSpecifierNumber3, groupedSpecifierNumber4}; + +export function givesANumber2(): number { return 42; }; +export class NumberGenerator2 { givesANumber(): number { return 42; }}; + +export var varDeclNumber3 = 1, varDeclNumber4 = 2; +export var {destructuredObjNumber2} = {destructuredObjNumber2: 1}; +export var [destructuredArrNumber2] = [1] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1785 + fromString(\", \").join(path.map(print, \"specifiers\")), + ^ + +TypeError: fromString(...).join is not a function + at printExportDeclaration (/src/printer.js:1785:26) + at genericPrintNoParens (/src/printer.js:488:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:238:16 +" +`; + +exports[`test ExportType.js 1`] = ` +"// @flow + +export type typeAlias = number; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +export type typeAlias = number; + +" +`; + +exports[`test ProvidesModuleA.js 1`] = ` +"/** + * @providesModule A + * @flow + */ + +exports.numberValue1 = 42; +exports.numberValue2 = 42; +exports.numberValue3 = 42; +exports.numberValue4 = 42; +exports.stringValue = \"str\"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule A + * @flow + */ +exports.numberValue1 = 42; +exports.numberValue2 = 42; +exports.numberValue3 = 42; +exports.numberValue4 = 42; +exports.stringValue = \"str\"; + +" +`; + +exports[`test ProvidesModuleCJSDefault.js 1`] = ` +"/** + * @providesModule CJSDefault + * @flow + */ + +module.exports = { + numberValue: 42 +}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule CJSDefault + * @flow + */ +module.exports = { numberValue: 42 }; + +" +`; + +exports[`test ProvidesModuleD.js 1`] = ` +"/** + * @providesModule D + * @flow + */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; + +exports[`test ProvidesModuleES6Default.js 1`] = ` +"/** + * @providesModule ES6Default + * @flow + */ + +/* +export default { + numberValue: 42, +}; +*/ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; + +exports[`test SideEffects.js 1`] = ` +"/* @flow */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; + +exports[`test es6modules.js 1`] = ` +"/* @flow */ + +// ===================== // +// == Path Resolution == // +// ===================== // + +// @providesModule +import * as DefaultA from \"A\"; +var a1: number = DefaultA.numberValue1; +var a2: string = DefaultA.numberValue1; // Error: number ~> string + +// File path +import * as DefaultB from \"./B\"; +var b1: number = DefaultB.numberValue; +var b2: string = DefaultB.numberValue; // Error: number ~> string + +// C.js exists, but not as a providesModule +import DefaultC from \"C\"; // Error: No such module + +// @providesModule D exists, but not as a filename +import DefaultD from \"./D\"; // Error: No such module + +// ================================================ // +// == CommonJS Clobbering Literal Exports -> ES6 == // +// ================================================ // + +import {doesntExist1} from \"CommonJS_Clobbering_Lit\"; // Error: Not an exported binding + +import {numberValue1} from \"CommonJS_Clobbering_Lit\"; +var c1: number = numberValue1; +var c2: string = numberValue1; // Error: number ~> string + +import {numberValue2 as numVal1} from \"CommonJS_Clobbering_Lit\"; +var d1: number = numVal1; +var d2: string = numVal1; // Error: number ~> string + +import CJS_Clobb_Lit from \"CommonJS_Clobbering_Lit\"; +var e1: number = CJS_Clobb_Lit.numberValue3; +var e2: string = CJS_Clobb_Lit.numberValue3; // Error: number ~> string +CJS_Clobb_Lit.doesntExist; // Error: doesntExist isn\'t a property + +import * as CJS_Clobb_Lit_NS from \"CommonJS_Clobbering_Lit\"; +var f1: number = CJS_Clobb_Lit_NS.numberValue4; +var f2: number = CJS_Clobb_Lit_NS.default.numberValue4; +CJS_Clobb_Lit_NS.default.default; // Error: No \'default\' property on the exported obj +var f3: string = CJS_Clobb_Lit_NS.numberValue4; // Error: number ~> string +var f4: string = CJS_Clobb_Lit_NS.default.numberValue5; // Error: number ~> string + +// ============================================== // +// == CommonJS Clobbering Class Exports -> ES6 == // +// ============================================== // + +import {doesntExist2} from \"CommonJS_Clobbering_Class\"; // Error: Not an exported binding + +// The following import should error because class statics are not turned into +// named exports for now. This avoids complexities with polymorphic static +// members (where the polymophism is defined on the class itself rather than the +// method). +import {staticNumber1, baseProp, childProp} from \"CommonJS_Clobbering_Class\"; // Error + +import CJS_Clobb_Class from \"CommonJS_Clobbering_Class\"; +new CJS_Clobb_Class(); +new CJS_Clobb_Class().doesntExist; // Error: Class has no \`doesntExist\` property +var h1: number = CJS_Clobb_Class.staticNumber2(); +var h2: string = CJS_Clobb_Class.staticNumber2(); // Error: number ~> string +var h3: number = new CJS_Clobb_Class().instNumber1(); +var h4: string = new CJS_Clobb_Class().instNumber1(); // Error: number ~> string + +import * as CJS_Clobb_Class_NS from \"CommonJS_Clobbering_Class\"; +new CJS_Clobb_Class_NS(); // Error: Namespace object isn\'t constructable +var i1: number = CJS_Clobb_Class_NS.staticNumber3(); // Error: Class statics not copied to Namespace object +var i2: number = new CJS_Clobb_Class_NS.default().instNumber2(); +var i3: string = new CJS_Clobb_Class_NS.default().instNumber2(); // Error: number ~> string + +// =================================== // +// == CommonJS Named Exports -> ES6 == // +// =================================== // + +import {doesntExist3} from \"CommonJS_Named\"; // Error: Not an exported binding + +import {numberValue2} from \"CommonJS_Named\"; +var j1: number = numberValue2; +var j2: string = numberValue2; // Error: number ~> string + +import {numberValue3 as numVal3} from \"CommonJS_Named\"; +var k1: number = numVal3; +var k2: string = numVal3; // Error: number ~> string + +import * as CJS_Named from \"CommonJS_Named\"; +var l1: number = CJS_Named.numberValue1; +var l2: string = CJS_Named.numberValue1; // Error: number ~> string +CJS_Named.doesntExist; // Error: doesntExist isn\'t a property + +import * as CJS_Named_NS from \"CommonJS_Named\"; +var m1: number = CJS_Named_NS.numberValue4; +var m2: string = CJS_Named_NS.default.numberValue4; // Error: CommonJS_Named has no default export +var m3: string = CJS_Named_NS.numberValue4; // Error: number ~> string + +////////////////////////////// +// == ES6 Default -> ES6 == // +////////////////////////////// + +import {doesntExist4} from \"ES6_Default_AnonFunction1\"; // Error: Not an exported binding + +import ES6_Def_AnonFunc1 from \"ES6_Default_AnonFunction1\"; +var n1: number = ES6_Def_AnonFunc1(); +var n2: string = ES6_Def_AnonFunc1(); // Error: number ~> string + +import ES6_Def_NamedFunc1 from \"ES6_Default_NamedFunction1\"; +var o1: number = ES6_Def_NamedFunc1(); +var o2: string = ES6_Def_NamedFunc1(); // Error: number ~> string + +import ES6_Def_AnonClass1 from \"ES6_Default_AnonClass1\"; +var p1: number = new ES6_Def_AnonClass1().givesANum(); +var p2: string = new ES6_Def_AnonClass1().givesANum(); // Error: number ~> string + +import ES6_Def_NamedClass1 from \"ES6_Default_NamedClass1\"; +var q1: number = new ES6_Def_NamedClass1().givesANum(); +var q2: string = new ES6_Def_NamedClass1().givesANum(); // Error: number ~> string + +//////////////////////////// +// == ES6 Named -> ES6 == // +//////////////////////////// + +import doesntExist5 from \"ES6_Named1\"; // Error: Not an exported binding + +import {specifierNumber1 as specifierNumber1_1} from \"ES6_Named1\"; +var r1: number = specifierNumber1_1; +var r2: string = specifierNumber1_1; // Error: number ~> string + +import {specifierNumber2Renamed} from \"ES6_Named1\"; +var s1: number = specifierNumber2Renamed; +var s2: string = specifierNumber2Renamed; // Error: number ~> string + +import {specifierNumber3 as specifierNumber3Renamed} from \"ES6_Named1\"; +var t1: number = specifierNumber3Renamed; +var t2: string = specifierNumber3Renamed; // Error: number ~> string + +import {groupedSpecifierNumber1, groupedSpecifierNumber2} from \"ES6_Named1\"; +var u1: number = groupedSpecifierNumber1; +var u2: number = groupedSpecifierNumber2; +var u3: string = groupedSpecifierNumber1; // Error: number ~> string +var u4: string = groupedSpecifierNumber2; // Error: number ~> string + +import {givesANumber} from \"ES6_Named1\"; +var v1: number = givesANumber(); +var v2: string = givesANumber(); // Error: number ~> string + +import {NumberGenerator} from \"ES6_Named1\"; +var w1: number = new NumberGenerator().givesANumber(); +var w2: string = new NumberGenerator().givesANumber(); // Error: number ~> string + +import {varDeclNumber1, varDeclNumber2} from \"ES6_Named1\"; +var x1: number = varDeclNumber1; +var x2: number = varDeclNumber2; +var x3: string = varDeclNumber1; // Error: number ~> string +var x4: string = varDeclNumber2; // Error: number ~> string + +import {destructuredObjNumber} from \"ES6_Named1\"; +var y1: number = destructuredObjNumber; +var y2: string = destructuredObjNumber; // Error: number ~> string + +import {destructuredArrNumber} from \"ES6_Named1\"; +var z1: number = destructuredArrNumber; +var z2: string = destructuredArrNumber; // Error: number ~> string + +import {numberValue1 as numberValue4} from \"ES6_ExportFrom_Intermediary1\"; +var aa1: number = numberValue4; +var aa2: string = numberValue4; // Error: number ~> string + +import {numberValue2_renamed} from \"ES6_ExportFrom_Intermediary1\"; +var ab1: number = numberValue2_renamed; +var ab2: string = numberValue2_renamed; // Error: number ~> string + +import {numberValue1 as numberValue5} from \"ES6_ExportAllFrom_Intermediary1\"; +var ac1: number = numberValue5; +var ac2: string = numberValue5; // Error: number ~> string + +/////////////////////////////////// +// == ES6 Default -> CommonJS == // +/////////////////////////////////// + +require(\'ES6_Default_AnonFunction2\').doesntExist; // Error: \'doesntExist\' isn\'t an export + +var ES6_Def_AnonFunc2 = require(\"ES6_Default_AnonFunction2\").default; +var ad1: number = ES6_Def_AnonFunc2(); +var ad2: string = ES6_Def_AnonFunc2(); // Error: number ~> string + +var ES6_Def_NamedFunc2 = require(\"ES6_Default_NamedFunction2\").default; +var ae1: number = ES6_Def_NamedFunc2(); +var ae2: string = ES6_Def_NamedFunc2(); // Error: number ~> string + +var ES6_Def_AnonClass2 = require(\"ES6_Default_AnonClass2\").default; +var af1: number = new ES6_Def_AnonClass2().givesANum(); +var af2: string = new ES6_Def_AnonClass2().givesANum(); // Error: number ~> string + +var ES6_Def_NamedClass2 = require(\"ES6_Default_NamedClass2\").default; +var ag1: number = new ES6_Def_NamedClass2().givesANum(); +var ag2: string = new ES6_Def_NamedClass2().givesANum(); // Error: number ~> string + +///////////////////////////////// +// == ES6 Named -> CommonJS == // +///////////////////////////////// + +var specifierNumber4 = require(\"ES6_Named2\").specifierNumber4; +var ah1: number = specifierNumber4; +var ah2: string = specifierNumber4; // Error: number ~> string + +var specifierNumber5Renamed = require(\"ES6_Named2\").specifierNumber5Renamed; +var ai1: number = specifierNumber5Renamed; +var ai2: string = specifierNumber5Renamed; // Error: number ~> string + +var groupedSpecifierNumber3 = require(\"ES6_Named2\").groupedSpecifierNumber3; +var groupedSpecifierNumber4 = require(\"ES6_Named2\").groupedSpecifierNumber4; +var aj1: number = groupedSpecifierNumber3; +var aj2: number = groupedSpecifierNumber4; +var aj3: string = groupedSpecifierNumber3; // Error: number ~> string +var aj4: string = groupedSpecifierNumber4; // Error: number ~> string + +var givesANumber2 = require(\"ES6_Named2\").givesANumber2; +var ak1: number = givesANumber2(); +var ak2: string = givesANumber2(); // Error: number ~> string + +var NumberGenerator2 = require(\"ES6_Named2\").NumberGenerator2; +var al1: number = new NumberGenerator2().givesANumber(); +var al2: string = new NumberGenerator2().givesANumber(); // Error: number ~> string + +var varDeclNumber3 = require(\"ES6_Named2\").varDeclNumber3; +var varDeclNumber4 = require(\"ES6_Named2\").varDeclNumber4; +var am1: number = varDeclNumber3; +var am2: number = varDeclNumber4; +var am3: string = varDeclNumber3; // Error: number ~> string +var am4: string = varDeclNumber4; // Error: number ~> string + +var destructuredObjNumber2 = require(\"ES6_Named2\").destructuredObjNumber2; +var an1: number = destructuredObjNumber2; +var an2: string = destructuredObjNumber2; // Error: number ~> string + +var destructuredArrNumber2 = require(\"ES6_Named2\").destructuredArrNumber2; +var ao1: number = destructuredArrNumber2; +var ao2: string = destructuredArrNumber2; // Error: number ~> string + +var numberValue6 = require(\"ES6_ExportFrom_Intermediary2\").numberValue1; +var ap1: number = numberValue6; +var ap2: string = numberValue6; // Error: number ~> string + +var numberValue2_renamed2 = require(\"ES6_ExportFrom_Intermediary2\").numberValue2_renamed2; +var aq1: number = numberValue2_renamed2; +var aq2: string = numberValue2_renamed2; // Error: number ~> string + +var numberValue7 = require(\"ES6_ExportAllFrom_Intermediary2\").numberValue2; +var ar1: number = numberValue7; +var ar2: string = numberValue7; // Error: number ~> string + +//////////////////////////////////////////////////////// +// == ES6 Default+Named -> ES6 import Default+Named== // +//////////////////////////////////////////////////////// + +import defaultNum, {str as namedStr} from \"./ES6_DefaultAndNamed\"; + +var as1: number = defaultNum; +var as2: string = defaultNum; // Error: number ~> string + +var as3: string = namedStr; +var as4: number = namedStr; // Error: string ~> number + +//////////////////////////////////////// +// == Side-effect only ES6 imports == // +//////////////////////////////////////// + +import \"./SideEffects\"; + +////////////////////////////////////////////// +// == Suggest export name on likely typo == // +////////////////////////////////////////////// +import specifierNumber1 from \"ES6_Named1\"; // Error: Did you mean \`import {specifierNumber1} from ...\`? +import {specifierNumber} from \"ES6_Named1\"; // Error: Did you mean \`specifierNumber1\`? + +/////////////////////////////////////////////////// +// == Multi \`export *\` should combine exports == // +/////////////////////////////////////////////////// +import { + numberValue1 as numberValue8, + numberValue2 as numberValue9 +} from \"./ES6_ExportAllFromMulti\"; + +var at1: number = numberValue8; +var at2: string = numberValue8; // Error: number ~> string + +var at3: number = numberValue9; +var at4: string = numberValue9; // Error: number ~> string + +///////////////////////////////////////////////////////////// +// == Vanilla \`import\` cannot import a type-only export == // +///////////////////////////////////////////////////////////// +import {typeAlias} from \"./ExportType\"; // Error: Cannot vanilla-import a type alias! +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +// ===================== // +// == Path Resolution == // +// ===================== // +// @providesModule +import * as DefaultA from \"A\"; +var a1: number = DefaultA.numberValue1; +var a2: string = DefaultA.numberValue1;// Error: number ~> string +// File path +import * as DefaultB from \"./B\"; +var b1: number = DefaultB.numberValue; +var b2: string = DefaultB.numberValue;// Error: number ~> string +// C.js exists, but not as a providesModule +import DefaultC from \"C\";// Error: No such module +// @providesModule D exists, but not as a filename +import DefaultD from \"./D\";// Error: No such module +// ================================================ // +// == CommonJS Clobbering Literal Exports -> ES6 == // +// ================================================ // +import { doesntExist1 } from \"CommonJS_Clobbering_Lit\";// Error: Not an exported binding +import { numberValue1 } from \"CommonJS_Clobbering_Lit\"; +var c1: number = numberValue1; +var c2: string = numberValue1;// Error: number ~> string +import { numberValue2 as numVal1 } from \"CommonJS_Clobbering_Lit\"; +var d1: number = numVal1; +var d2: string = numVal1;// Error: number ~> string +import CJS_Clobb_Lit from \"CommonJS_Clobbering_Lit\"; +var e1: number = CJS_Clobb_Lit.numberValue3; +var e2: string = CJS_Clobb_Lit.numberValue3;// Error: number ~> string +CJS_Clobb_Lit.doesntExist;// Error: doesntExist isn\'t a property +import * as CJS_Clobb_Lit_NS from \"CommonJS_Clobbering_Lit\"; +var f1: number = CJS_Clobb_Lit_NS.numberValue4; +var f2: number = CJS_Clobb_Lit_NS.default.numberValue4; +CJS_Clobb_Lit_NS.default.default;// Error: No \'default\' property on the exported obj +var f3: string = CJS_Clobb_Lit_NS.numberValue4;// Error: number ~> string +var f4: string = CJS_Clobb_Lit_NS.default.numberValue5;// Error: number ~> string +// ============================================== // +// == CommonJS Clobbering Class Exports -> ES6 == // +// ============================================== // +import { doesntExist2 } from \"CommonJS_Clobbering_Class\";// Error: Not an exported binding +// The following import should error because class statics are not turned into +// named exports for now. This avoids complexities with polymorphic static +// members (where the polymophism is defined on the class itself rather than the +// method). +import { staticNumber1, baseProp, childProp } from \"CommonJS_Clobbering_Class\";// Error +import CJS_Clobb_Class from \"CommonJS_Clobbering_Class\"; +new CJS_Clobb_Class(); +new CJS_Clobb_Class().doesntExist;// Error: Class has no \`doesntExist\` property +var h1: number = CJS_Clobb_Class.staticNumber2(); +var h2: string = CJS_Clobb_Class.staticNumber2();// Error: number ~> string +var h3: number = new CJS_Clobb_Class().instNumber1(); +var h4: string = new CJS_Clobb_Class().instNumber1();// Error: number ~> string +import * as CJS_Clobb_Class_NS from \"CommonJS_Clobbering_Class\"; +new CJS_Clobb_Class_NS();// Error: Namespace object isn\'t constructable +var i1: number = CJS_Clobb_Class_NS.staticNumber3();// Error: Class statics not copied to Namespace object +var i2: number = new CJS_Clobb_Class_NS.default().instNumber2(); +var i3: string = new CJS_Clobb_Class_NS.default().instNumber2();// Error: number ~> string +// =================================== // +// == CommonJS Named Exports -> ES6 == // +// =================================== // +import { doesntExist3 } from \"CommonJS_Named\";// Error: Not an exported binding +import { numberValue2 } from \"CommonJS_Named\"; +var j1: number = numberValue2; +var j2: string = numberValue2;// Error: number ~> string +import { numberValue3 as numVal3 } from \"CommonJS_Named\"; +var k1: number = numVal3; +var k2: string = numVal3;// Error: number ~> string +import * as CJS_Named from \"CommonJS_Named\"; +var l1: number = CJS_Named.numberValue1; +var l2: string = CJS_Named.numberValue1;// Error: number ~> string +CJS_Named.doesntExist;// Error: doesntExist isn\'t a property +import * as CJS_Named_NS from \"CommonJS_Named\"; +var m1: number = CJS_Named_NS.numberValue4; +var m2: string = CJS_Named_NS.default.numberValue4;// Error: CommonJS_Named has no default export +var m3: string = CJS_Named_NS.numberValue4;// Error: number ~> string +////////////////////////////// +// == ES6 Default -> ES6 == // +////////////////////////////// +import { doesntExist4 } from \"ES6_Default_AnonFunction1\";// Error: Not an exported binding +import ES6_Def_AnonFunc1 from \"ES6_Default_AnonFunction1\"; +var n1: number = ES6_Def_AnonFunc1(); +var n2: string = ES6_Def_AnonFunc1();// Error: number ~> string +import ES6_Def_NamedFunc1 from \"ES6_Default_NamedFunction1\"; +var o1: number = ES6_Def_NamedFunc1(); +var o2: string = ES6_Def_NamedFunc1();// Error: number ~> string +import ES6_Def_AnonClass1 from \"ES6_Default_AnonClass1\"; +var p1: number = new ES6_Def_AnonClass1().givesANum(); +var p2: string = new ES6_Def_AnonClass1().givesANum();// Error: number ~> string +import ES6_Def_NamedClass1 from \"ES6_Default_NamedClass1\"; +var q1: number = new ES6_Def_NamedClass1().givesANum(); +var q2: string = new ES6_Def_NamedClass1().givesANum();// Error: number ~> string +//////////////////////////// +// == ES6 Named -> ES6 == // +//////////////////////////// +import doesntExist5 from \"ES6_Named1\";// Error: Not an exported binding +import { specifierNumber1 as specifierNumber1_1 } from \"ES6_Named1\"; +var r1: number = specifierNumber1_1; +var r2: string = specifierNumber1_1;// Error: number ~> string +import { specifierNumber2Renamed } from \"ES6_Named1\"; +var s1: number = specifierNumber2Renamed; +var s2: string = specifierNumber2Renamed;// Error: number ~> string +import { specifierNumber3 as specifierNumber3Renamed } from \"ES6_Named1\"; +var t1: number = specifierNumber3Renamed; +var t2: string = specifierNumber3Renamed;// Error: number ~> string +import { groupedSpecifierNumber1, groupedSpecifierNumber2 } from \"ES6_Named1\"; +var u1: number = groupedSpecifierNumber1; +var u2: number = groupedSpecifierNumber2; +var u3: string = groupedSpecifierNumber1;// Error: number ~> string +var u4: string = groupedSpecifierNumber2;// Error: number ~> string +import { givesANumber } from \"ES6_Named1\"; +var v1: number = givesANumber(); +var v2: string = givesANumber();// Error: number ~> string +import { NumberGenerator } from \"ES6_Named1\"; +var w1: number = new NumberGenerator().givesANumber(); +var w2: string = new NumberGenerator().givesANumber();// Error: number ~> string +import { varDeclNumber1, varDeclNumber2 } from \"ES6_Named1\"; +var x1: number = varDeclNumber1; +var x2: number = varDeclNumber2; +var x3: string = varDeclNumber1;// Error: number ~> string +var x4: string = varDeclNumber2;// Error: number ~> string +import { destructuredObjNumber } from \"ES6_Named1\"; +var y1: number = destructuredObjNumber; +var y2: string = destructuredObjNumber;// Error: number ~> string +import { destructuredArrNumber } from \"ES6_Named1\"; +var z1: number = destructuredArrNumber; +var z2: string = destructuredArrNumber;// Error: number ~> string +import { numberValue1 as numberValue4 } from \"ES6_ExportFrom_Intermediary1\"; +var aa1: number = numberValue4; +var aa2: string = numberValue4;// Error: number ~> string +import { numberValue2_renamed } from \"ES6_ExportFrom_Intermediary1\"; +var ab1: number = numberValue2_renamed; +var ab2: string = numberValue2_renamed;// Error: number ~> string +import { numberValue1 as numberValue5 } from \"ES6_ExportAllFrom_Intermediary1\"; +var ac1: number = numberValue5; +var ac2: string = numberValue5;// Error: number ~> string +/////////////////////////////////// +// == ES6 Default -> CommonJS == // +/////////////////////////////////// +require( + \"ES6_Default_AnonFunction2\" +).doesntExist;// Error: \'doesntExist\' isn\'t an export +var ES6_Def_AnonFunc2 = require(\"ES6_Default_AnonFunction2\").default; +var ad1: number = ES6_Def_AnonFunc2(); +var ad2: string = ES6_Def_AnonFunc2();// Error: number ~> string +var ES6_Def_NamedFunc2 = require(\"ES6_Default_NamedFunction2\").default; +var ae1: number = ES6_Def_NamedFunc2(); +var ae2: string = ES6_Def_NamedFunc2();// Error: number ~> string +var ES6_Def_AnonClass2 = require(\"ES6_Default_AnonClass2\").default; +var af1: number = new ES6_Def_AnonClass2().givesANum(); +var af2: string = new ES6_Def_AnonClass2().givesANum();// Error: number ~> string +var ES6_Def_NamedClass2 = require(\"ES6_Default_NamedClass2\").default; +var ag1: number = new ES6_Def_NamedClass2().givesANum(); +var ag2: string = new ES6_Def_NamedClass2().givesANum();// Error: number ~> string +///////////////////////////////// +// == ES6 Named -> CommonJS == // +///////////////////////////////// +var specifierNumber4 = require(\"ES6_Named2\").specifierNumber4; +var ah1: number = specifierNumber4; +var ah2: string = specifierNumber4;// Error: number ~> string +var specifierNumber5Renamed = require(\"ES6_Named2\").specifierNumber5Renamed; +var ai1: number = specifierNumber5Renamed; +var ai2: string = specifierNumber5Renamed;// Error: number ~> string +var groupedSpecifierNumber3 = require(\"ES6_Named2\").groupedSpecifierNumber3; +var groupedSpecifierNumber4 = require(\"ES6_Named2\").groupedSpecifierNumber4; +var aj1: number = groupedSpecifierNumber3; +var aj2: number = groupedSpecifierNumber4; +var aj3: string = groupedSpecifierNumber3;// Error: number ~> string +var aj4: string = groupedSpecifierNumber4;// Error: number ~> string +var givesANumber2 = require(\"ES6_Named2\").givesANumber2; +var ak1: number = givesANumber2(); +var ak2: string = givesANumber2();// Error: number ~> string +var NumberGenerator2 = require(\"ES6_Named2\").NumberGenerator2; +var al1: number = new NumberGenerator2().givesANumber(); +var al2: string = new NumberGenerator2().givesANumber();// Error: number ~> string +var varDeclNumber3 = require(\"ES6_Named2\").varDeclNumber3; +var varDeclNumber4 = require(\"ES6_Named2\").varDeclNumber4; +var am1: number = varDeclNumber3; +var am2: number = varDeclNumber4; +var am3: string = varDeclNumber3;// Error: number ~> string +var am4: string = varDeclNumber4;// Error: number ~> string +var destructuredObjNumber2 = require(\"ES6_Named2\").destructuredObjNumber2; +var an1: number = destructuredObjNumber2; +var an2: string = destructuredObjNumber2;// Error: number ~> string +var destructuredArrNumber2 = require(\"ES6_Named2\").destructuredArrNumber2; +var ao1: number = destructuredArrNumber2; +var ao2: string = destructuredArrNumber2;// Error: number ~> string +var numberValue6 = require(\"ES6_ExportFrom_Intermediary2\").numberValue1; +var ap1: number = numberValue6; +var ap2: string = numberValue6;// Error: number ~> string +var numberValue2_renamed2 = require( + \"ES6_ExportFrom_Intermediary2\" +).numberValue2_renamed2; +var aq1: number = numberValue2_renamed2; +var aq2: string = numberValue2_renamed2;// Error: number ~> string +var numberValue7 = require(\"ES6_ExportAllFrom_Intermediary2\").numberValue2; +var ar1: number = numberValue7; +var ar2: string = numberValue7;// Error: number ~> string +//////////////////////////////////////////////////////// +// == ES6 Default+Named -> ES6 import Default+Named== // +//////////////////////////////////////////////////////// +import defaultNum, { str as namedStr } from \"./ES6_DefaultAndNamed\"; +var as1: number = defaultNum; +var as2: string = defaultNum;// Error: number ~> string +var as3: string = namedStr; +var as4: number = namedStr;// Error: string ~> number +//////////////////////////////////////// +// == Side-effect only ES6 imports == // +//////////////////////////////////////// +import \"./SideEffects\"; +////////////////////////////////////////////// +// == Suggest export name on likely typo == // +////////////////////////////////////////////// +import specifierNumber1 from \"ES6_Named1\";// Error: Did you mean \`import {specifierNumber1} from ...\`? +import { specifierNumber } from \"ES6_Named1\";// Error: Did you mean \`specifierNumber1\`? +/////////////////////////////////////////////////// +// == Multi \`export *\` should combine exports == // +/////////////////////////////////////////////////// +import { numberValue1 as numberValue8, numberValue2 as numberValue9 } from \"./ES6_ExportAllFromMulti\"; +var at1: number = numberValue8; +var at2: string = numberValue8;// Error: number ~> string +var at3: number = numberValue9; +var at4: string = numberValue9;// Error: number ~> string +///////////////////////////////////////////////////////////// +// == Vanilla \`import\` cannot import a type-only export == // +///////////////////////////////////////////////////////////// +import { typeAlias } from \"./ExportType\";// Error: Cannot vanilla-import a type alias! + +" +`; + +exports[`test test_imports_are_frozen.js 1`] = ` +"/* @flow */ + +// +// Imports +// + +// CommonJS module +import * as DefaultA from \"A\"; +DefaultA.numberValue1 = 123; // Error: DefaultA is frozen + +// ES6 module +import * as ES6_Named1 from \"ES6_Named1\"; +ES6_Named1.varDeclNumber1 = 123; // Error: ES6_Named1 is frozen + +// CommonJS module that clobbers module.exports +import * as CommonJS_Star from \"CommonJS_Clobbering_Lit\"; +CommonJS_Star.numberValue1 = 123; // Error: frozen +CommonJS_Star.default.numberValue1 = 123; // ok + +import CommonJS_Clobbering_Lit from \"CommonJS_Clobbering_Lit\"; +CommonJS_Clobbering_Lit.numberValue1 = 123; // ok + +// CommonJS module that clobbers module.exports with a frozen object +import * as CommonJS_Frozen_Star from \"CommonJS_Clobbering_Frozen\"; +CommonJS_Frozen_Star.numberValue1 = 123; // Error: frozen +CommonJS_Frozen_Star.default.numberValue1 = 123; // Error: frozen + +import CommonJS_Clobbering_Frozen from \"CommonJS_Clobbering_Frozen\"; +CommonJS_Clobbering_Frozen.numberValue1 = 123; // Error: exports are frozen + + +// +// Requires +// + +function testRequires() { + // CommonJS module + var DefaultA = require(\"A\"); + DefaultA.numberValue1 = 123; // ok, not frozen by default + + // ES6 module + var ES6_Named1 = require(\"ES6_Named1\"); + ES6_Named1.numberValue = 123; // error, es6 exports are frozen + + // CommonJS module that clobbers module.exports + var CommonJS_Star = require(\"CommonJS_Clobbering_Lit\"); + CommonJS_Star.numberValue1 = 123; // ok, not frozen by default + + // CommonJS module that clobbers module.exports with a frozen object + var CommonJS_Frozen_Star = require(\"CommonJS_Clobbering_Frozen\"); + CommonJS_Frozen_Star.numberValue1 = 123; // Error: frozen +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +// +// Imports +// +// CommonJS module +import * as DefaultA from \"A\"; +DefaultA.numberValue1 = 123;// Error: DefaultA is frozen +// ES6 module +import * as ES6_Named1 from \"ES6_Named1\"; +ES6_Named1.varDeclNumber1 = 123;// Error: ES6_Named1 is frozen +// CommonJS module that clobbers module.exports +import * as CommonJS_Star from \"CommonJS_Clobbering_Lit\"; +CommonJS_Star.numberValue1 = 123;// Error: frozen +CommonJS_Star.default.numberValue1 = 123;// ok +import CommonJS_Clobbering_Lit from \"CommonJS_Clobbering_Lit\"; +CommonJS_Clobbering_Lit.numberValue1 = 123;// ok +// CommonJS module that clobbers module.exports with a frozen object +import * as CommonJS_Frozen_Star from \"CommonJS_Clobbering_Frozen\"; +CommonJS_Frozen_Star.numberValue1 = 123;// Error: frozen +CommonJS_Frozen_Star.default.numberValue1 = 123;// Error: frozen +import CommonJS_Clobbering_Frozen from \"CommonJS_Clobbering_Frozen\"; +CommonJS_Clobbering_Frozen.numberValue1 = 123;// Error: exports are frozen +// +// Requires +// +function testRequires() { + // CommonJS module + var DefaultA = require(\"A\"); + DefaultA.numberValue1 = 123;// ok, not frozen by default + // ES6 module + var ES6_Named1 = require(\"ES6_Named1\"); + ES6_Named1.numberValue = 123;// error, es6 exports are frozen + // CommonJS module that clobbers module.exports + var CommonJS_Star = require(\"CommonJS_Clobbering_Lit\"); + CommonJS_Star.numberValue1 = 123;// ok, not frozen by default + // CommonJS module that clobbers module.exports with a frozen object + var CommonJS_Frozen_Star = require(\"CommonJS_Clobbering_Frozen\"); + CommonJS_Frozen_Star.numberValue1 = 123;// Error: frozen +} + +" +`; diff --git a/tests/es6modules/es6modules.js b/tests/es6modules/es6modules.js new file mode 100644 index 000000000000..a43f876aa601 --- /dev/null +++ b/tests/es6modules/es6modules.js @@ -0,0 +1,296 @@ +/* @flow */ + +// ===================== // +// == Path Resolution == // +// ===================== // + +// @providesModule +import * as DefaultA from "A"; +var a1: number = DefaultA.numberValue1; +var a2: string = DefaultA.numberValue1; // Error: number ~> string + +// File path +import * as DefaultB from "./B"; +var b1: number = DefaultB.numberValue; +var b2: string = DefaultB.numberValue; // Error: number ~> string + +// C.js exists, but not as a providesModule +import DefaultC from "C"; // Error: No such module + +// @providesModule D exists, but not as a filename +import DefaultD from "./D"; // Error: No such module + +// ================================================ // +// == CommonJS Clobbering Literal Exports -> ES6 == // +// ================================================ // + +import {doesntExist1} from "CommonJS_Clobbering_Lit"; // Error: Not an exported binding + +import {numberValue1} from "CommonJS_Clobbering_Lit"; +var c1: number = numberValue1; +var c2: string = numberValue1; // Error: number ~> string + +import {numberValue2 as numVal1} from "CommonJS_Clobbering_Lit"; +var d1: number = numVal1; +var d2: string = numVal1; // Error: number ~> string + +import CJS_Clobb_Lit from "CommonJS_Clobbering_Lit"; +var e1: number = CJS_Clobb_Lit.numberValue3; +var e2: string = CJS_Clobb_Lit.numberValue3; // Error: number ~> string +CJS_Clobb_Lit.doesntExist; // Error: doesntExist isn't a property + +import * as CJS_Clobb_Lit_NS from "CommonJS_Clobbering_Lit"; +var f1: number = CJS_Clobb_Lit_NS.numberValue4; +var f2: number = CJS_Clobb_Lit_NS.default.numberValue4; +CJS_Clobb_Lit_NS.default.default; // Error: No 'default' property on the exported obj +var f3: string = CJS_Clobb_Lit_NS.numberValue4; // Error: number ~> string +var f4: string = CJS_Clobb_Lit_NS.default.numberValue5; // Error: number ~> string + +// ============================================== // +// == CommonJS Clobbering Class Exports -> ES6 == // +// ============================================== // + +import {doesntExist2} from "CommonJS_Clobbering_Class"; // Error: Not an exported binding + +// The following import should error because class statics are not turned into +// named exports for now. This avoids complexities with polymorphic static +// members (where the polymophism is defined on the class itself rather than the +// method). +import {staticNumber1, baseProp, childProp} from "CommonJS_Clobbering_Class"; // Error + +import CJS_Clobb_Class from "CommonJS_Clobbering_Class"; +new CJS_Clobb_Class(); +new CJS_Clobb_Class().doesntExist; // Error: Class has no `doesntExist` property +var h1: number = CJS_Clobb_Class.staticNumber2(); +var h2: string = CJS_Clobb_Class.staticNumber2(); // Error: number ~> string +var h3: number = new CJS_Clobb_Class().instNumber1(); +var h4: string = new CJS_Clobb_Class().instNumber1(); // Error: number ~> string + +import * as CJS_Clobb_Class_NS from "CommonJS_Clobbering_Class"; +new CJS_Clobb_Class_NS(); // Error: Namespace object isn't constructable +var i1: number = CJS_Clobb_Class_NS.staticNumber3(); // Error: Class statics not copied to Namespace object +var i2: number = new CJS_Clobb_Class_NS.default().instNumber2(); +var i3: string = new CJS_Clobb_Class_NS.default().instNumber2(); // Error: number ~> string + +// =================================== // +// == CommonJS Named Exports -> ES6 == // +// =================================== // + +import {doesntExist3} from "CommonJS_Named"; // Error: Not an exported binding + +import {numberValue2} from "CommonJS_Named"; +var j1: number = numberValue2; +var j2: string = numberValue2; // Error: number ~> string + +import {numberValue3 as numVal3} from "CommonJS_Named"; +var k1: number = numVal3; +var k2: string = numVal3; // Error: number ~> string + +import * as CJS_Named from "CommonJS_Named"; +var l1: number = CJS_Named.numberValue1; +var l2: string = CJS_Named.numberValue1; // Error: number ~> string +CJS_Named.doesntExist; // Error: doesntExist isn't a property + +import * as CJS_Named_NS from "CommonJS_Named"; +var m1: number = CJS_Named_NS.numberValue4; +var m2: string = CJS_Named_NS.default.numberValue4; // Error: CommonJS_Named has no default export +var m3: string = CJS_Named_NS.numberValue4; // Error: number ~> string + +////////////////////////////// +// == ES6 Default -> ES6 == // +////////////////////////////// + +import {doesntExist4} from "ES6_Default_AnonFunction1"; // Error: Not an exported binding + +import ES6_Def_AnonFunc1 from "ES6_Default_AnonFunction1"; +var n1: number = ES6_Def_AnonFunc1(); +var n2: string = ES6_Def_AnonFunc1(); // Error: number ~> string + +import ES6_Def_NamedFunc1 from "ES6_Default_NamedFunction1"; +var o1: number = ES6_Def_NamedFunc1(); +var o2: string = ES6_Def_NamedFunc1(); // Error: number ~> string + +import ES6_Def_AnonClass1 from "ES6_Default_AnonClass1"; +var p1: number = new ES6_Def_AnonClass1().givesANum(); +var p2: string = new ES6_Def_AnonClass1().givesANum(); // Error: number ~> string + +import ES6_Def_NamedClass1 from "ES6_Default_NamedClass1"; +var q1: number = new ES6_Def_NamedClass1().givesANum(); +var q2: string = new ES6_Def_NamedClass1().givesANum(); // Error: number ~> string + +//////////////////////////// +// == ES6 Named -> ES6 == // +//////////////////////////// + +import doesntExist5 from "ES6_Named1"; // Error: Not an exported binding + +import {specifierNumber1 as specifierNumber1_1} from "ES6_Named1"; +var r1: number = specifierNumber1_1; +var r2: string = specifierNumber1_1; // Error: number ~> string + +import {specifierNumber2Renamed} from "ES6_Named1"; +var s1: number = specifierNumber2Renamed; +var s2: string = specifierNumber2Renamed; // Error: number ~> string + +import {specifierNumber3 as specifierNumber3Renamed} from "ES6_Named1"; +var t1: number = specifierNumber3Renamed; +var t2: string = specifierNumber3Renamed; // Error: number ~> string + +import {groupedSpecifierNumber1, groupedSpecifierNumber2} from "ES6_Named1"; +var u1: number = groupedSpecifierNumber1; +var u2: number = groupedSpecifierNumber2; +var u3: string = groupedSpecifierNumber1; // Error: number ~> string +var u4: string = groupedSpecifierNumber2; // Error: number ~> string + +import {givesANumber} from "ES6_Named1"; +var v1: number = givesANumber(); +var v2: string = givesANumber(); // Error: number ~> string + +import {NumberGenerator} from "ES6_Named1"; +var w1: number = new NumberGenerator().givesANumber(); +var w2: string = new NumberGenerator().givesANumber(); // Error: number ~> string + +import {varDeclNumber1, varDeclNumber2} from "ES6_Named1"; +var x1: number = varDeclNumber1; +var x2: number = varDeclNumber2; +var x3: string = varDeclNumber1; // Error: number ~> string +var x4: string = varDeclNumber2; // Error: number ~> string + +import {destructuredObjNumber} from "ES6_Named1"; +var y1: number = destructuredObjNumber; +var y2: string = destructuredObjNumber; // Error: number ~> string + +import {destructuredArrNumber} from "ES6_Named1"; +var z1: number = destructuredArrNumber; +var z2: string = destructuredArrNumber; // Error: number ~> string + +import {numberValue1 as numberValue4} from "ES6_ExportFrom_Intermediary1"; +var aa1: number = numberValue4; +var aa2: string = numberValue4; // Error: number ~> string + +import {numberValue2_renamed} from "ES6_ExportFrom_Intermediary1"; +var ab1: number = numberValue2_renamed; +var ab2: string = numberValue2_renamed; // Error: number ~> string + +import {numberValue1 as numberValue5} from "ES6_ExportAllFrom_Intermediary1"; +var ac1: number = numberValue5; +var ac2: string = numberValue5; // Error: number ~> string + +/////////////////////////////////// +// == ES6 Default -> CommonJS == // +/////////////////////////////////// + +require('ES6_Default_AnonFunction2').doesntExist; // Error: 'doesntExist' isn't an export + +var ES6_Def_AnonFunc2 = require("ES6_Default_AnonFunction2").default; +var ad1: number = ES6_Def_AnonFunc2(); +var ad2: string = ES6_Def_AnonFunc2(); // Error: number ~> string + +var ES6_Def_NamedFunc2 = require("ES6_Default_NamedFunction2").default; +var ae1: number = ES6_Def_NamedFunc2(); +var ae2: string = ES6_Def_NamedFunc2(); // Error: number ~> string + +var ES6_Def_AnonClass2 = require("ES6_Default_AnonClass2").default; +var af1: number = new ES6_Def_AnonClass2().givesANum(); +var af2: string = new ES6_Def_AnonClass2().givesANum(); // Error: number ~> string + +var ES6_Def_NamedClass2 = require("ES6_Default_NamedClass2").default; +var ag1: number = new ES6_Def_NamedClass2().givesANum(); +var ag2: string = new ES6_Def_NamedClass2().givesANum(); // Error: number ~> string + +///////////////////////////////// +// == ES6 Named -> CommonJS == // +///////////////////////////////// + +var specifierNumber4 = require("ES6_Named2").specifierNumber4; +var ah1: number = specifierNumber4; +var ah2: string = specifierNumber4; // Error: number ~> string + +var specifierNumber5Renamed = require("ES6_Named2").specifierNumber5Renamed; +var ai1: number = specifierNumber5Renamed; +var ai2: string = specifierNumber5Renamed; // Error: number ~> string + +var groupedSpecifierNumber3 = require("ES6_Named2").groupedSpecifierNumber3; +var groupedSpecifierNumber4 = require("ES6_Named2").groupedSpecifierNumber4; +var aj1: number = groupedSpecifierNumber3; +var aj2: number = groupedSpecifierNumber4; +var aj3: string = groupedSpecifierNumber3; // Error: number ~> string +var aj4: string = groupedSpecifierNumber4; // Error: number ~> string + +var givesANumber2 = require("ES6_Named2").givesANumber2; +var ak1: number = givesANumber2(); +var ak2: string = givesANumber2(); // Error: number ~> string + +var NumberGenerator2 = require("ES6_Named2").NumberGenerator2; +var al1: number = new NumberGenerator2().givesANumber(); +var al2: string = new NumberGenerator2().givesANumber(); // Error: number ~> string + +var varDeclNumber3 = require("ES6_Named2").varDeclNumber3; +var varDeclNumber4 = require("ES6_Named2").varDeclNumber4; +var am1: number = varDeclNumber3; +var am2: number = varDeclNumber4; +var am3: string = varDeclNumber3; // Error: number ~> string +var am4: string = varDeclNumber4; // Error: number ~> string + +var destructuredObjNumber2 = require("ES6_Named2").destructuredObjNumber2; +var an1: number = destructuredObjNumber2; +var an2: string = destructuredObjNumber2; // Error: number ~> string + +var destructuredArrNumber2 = require("ES6_Named2").destructuredArrNumber2; +var ao1: number = destructuredArrNumber2; +var ao2: string = destructuredArrNumber2; // Error: number ~> string + +var numberValue6 = require("ES6_ExportFrom_Intermediary2").numberValue1; +var ap1: number = numberValue6; +var ap2: string = numberValue6; // Error: number ~> string + +var numberValue2_renamed2 = require("ES6_ExportFrom_Intermediary2").numberValue2_renamed2; +var aq1: number = numberValue2_renamed2; +var aq2: string = numberValue2_renamed2; // Error: number ~> string + +var numberValue7 = require("ES6_ExportAllFrom_Intermediary2").numberValue2; +var ar1: number = numberValue7; +var ar2: string = numberValue7; // Error: number ~> string + +//////////////////////////////////////////////////////// +// == ES6 Default+Named -> ES6 import Default+Named== // +//////////////////////////////////////////////////////// + +import defaultNum, {str as namedStr} from "./ES6_DefaultAndNamed"; + +var as1: number = defaultNum; +var as2: string = defaultNum; // Error: number ~> string + +var as3: string = namedStr; +var as4: number = namedStr; // Error: string ~> number + +//////////////////////////////////////// +// == Side-effect only ES6 imports == // +//////////////////////////////////////// + +import "./SideEffects"; + +////////////////////////////////////////////// +// == Suggest export name on likely typo == // +////////////////////////////////////////////// +import specifierNumber1 from "ES6_Named1"; // Error: Did you mean `import {specifierNumber1} from ...`? +import {specifierNumber} from "ES6_Named1"; // Error: Did you mean `specifierNumber1`? + +/////////////////////////////////////////////////// +// == Multi `export *` should combine exports == // +/////////////////////////////////////////////////// +import { + numberValue1 as numberValue8, + numberValue2 as numberValue9 +} from "./ES6_ExportAllFromMulti"; + +var at1: number = numberValue8; +var at2: string = numberValue8; // Error: number ~> string + +var at3: number = numberValue9; +var at4: string = numberValue9; // Error: number ~> string + +///////////////////////////////////////////////////////////// +// == Vanilla `import` cannot import a type-only export == // +///////////////////////////////////////////////////////////// +import {typeAlias} from "./ExportType"; // Error: Cannot vanilla-import a type alias! diff --git a/tests/es6modules/jsfmt.spec.js b/tests/es6modules/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/es6modules/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/es6modules/test_imports_are_frozen.js b/tests/es6modules/test_imports_are_frozen.js new file mode 100644 index 000000000000..7fd1e51490d5 --- /dev/null +++ b/tests/es6modules/test_imports_are_frozen.js @@ -0,0 +1,52 @@ +/* @flow */ + +// +// Imports +// + +// CommonJS module +import * as DefaultA from "A"; +DefaultA.numberValue1 = 123; // Error: DefaultA is frozen + +// ES6 module +import * as ES6_Named1 from "ES6_Named1"; +ES6_Named1.varDeclNumber1 = 123; // Error: ES6_Named1 is frozen + +// CommonJS module that clobbers module.exports +import * as CommonJS_Star from "CommonJS_Clobbering_Lit"; +CommonJS_Star.numberValue1 = 123; // Error: frozen +CommonJS_Star.default.numberValue1 = 123; // ok + +import CommonJS_Clobbering_Lit from "CommonJS_Clobbering_Lit"; +CommonJS_Clobbering_Lit.numberValue1 = 123; // ok + +// CommonJS module that clobbers module.exports with a frozen object +import * as CommonJS_Frozen_Star from "CommonJS_Clobbering_Frozen"; +CommonJS_Frozen_Star.numberValue1 = 123; // Error: frozen +CommonJS_Frozen_Star.default.numberValue1 = 123; // Error: frozen + +import CommonJS_Clobbering_Frozen from "CommonJS_Clobbering_Frozen"; +CommonJS_Clobbering_Frozen.numberValue1 = 123; // Error: exports are frozen + + +// +// Requires +// + +function testRequires() { + // CommonJS module + var DefaultA = require("A"); + DefaultA.numberValue1 = 123; // ok, not frozen by default + + // ES6 module + var ES6_Named1 = require("ES6_Named1"); + ES6_Named1.numberValue = 123; // error, es6 exports are frozen + + // CommonJS module that clobbers module.exports + var CommonJS_Star = require("CommonJS_Clobbering_Lit"); + CommonJS_Star.numberValue1 = 123; // ok, not frozen by default + + // CommonJS module that clobbers module.exports with a frozen object + var CommonJS_Frozen_Star = require("CommonJS_Clobbering_Frozen"); + CommonJS_Frozen_Star.numberValue1 = 123; // Error: frozen +} diff --git a/tests/es_declare_module/__snapshots__/jsfmt.spec.js.snap b/tests/es_declare_module/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..46041e8518a8 --- /dev/null +++ b/tests/es_declare_module/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,80 @@ +exports[`test es_declare_module.js 1`] = ` +"// @flow + +import {num1, str1} from "CJS_Named"; +import CJS_Named from "CJS_Named"; +(num1: number); +(num1: string); // Error: number ~> string +(str1: string); +(str1: number); // Error: string ~> number +(CJS_Named: {num1: number, str1: string}); +(CJS_Named: number); // Error: Module ~> number + +import {num2} from "CJS_Clobbered"; // Error: No such export! +import {numExport} from "CJS_Clobbered"; +(numExport: number); +(numExport: string); // Error: number ~> string +import type {numType} from "CJS_Clobbered"; +(42: numType); +('asdf': numType); // Error: string ~> number + +import {strHidden} from "ES"; // Error: No such export! +import {str3} from "ES"; +(str3: string); +(str3: number); // Error: string ~> number + +import {num3} from "ES"; +(num3: number); +(num3: string); // Error: number ~> string + +import {C} from "ES"; +import type {C as CType} from "ES"; +(new C(): C); +(42: C); // Error: number ~> C +(new C(): CType); +(42: CType); // Error: number ~> CType + +import {T} from "ES"; // Error: T is a type import, not a value +import type {T as T2} from "ES"; +(42: T2); +('asdf': T2); // Error: string ~> number + +import {exports as nope} from "ES"; // Error: Not an export +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +import { num1, str1 } from "CJS_Named"; +import CJS_Named from "CJS_Named"; +(num1: number); +(num1: string);// Error: number ~> string +(str1: string); +(str1: number);// Error: string ~> number +(CJS_Named: { num1: number, str1: string }); +(CJS_Named: number);// Error: Module ~> number +import { num2 } from "CJS_Clobbered";// Error: No such export! +import { numExport } from "CJS_Clobbered"; +(numExport: number); +(numExport: string);// Error: number ~> string +import type { numType } from "CJS_Clobbered"; +(42: numType); +("asdf": numType);// Error: string ~> number +import { strHidden } from "ES";// Error: No such export! +import { str3 } from "ES"; +(str3: string); +(str3: number);// Error: string ~> number +import { num3 } from "ES"; +(num3: number); +(num3: string);// Error: number ~> string +import { C } from "ES"; +import type { C as CType } from "ES"; +(new C(): C); +(42: C);// Error: number ~> C +(new C(): CType); +(42: CType);// Error: number ~> CType +import { T } from "ES";// Error: T is a type import, not a value +import type { T as T2 } from "ES"; +(42: T2); +("asdf": T2);// Error: string ~> number +import { exports as nope } from "ES";// Error: Not an export + +" +`; diff --git a/tests/es_declare_module/es_declare_module.js b/tests/es_declare_module/es_declare_module.js new file mode 100644 index 000000000000..6395bceca7d5 --- /dev/null +++ b/tests/es_declare_module/es_declare_module.js @@ -0,0 +1,41 @@ +// @flow + +import {num1, str1} from "CJS_Named"; +import CJS_Named from "CJS_Named"; +(num1: number); +(num1: string); // Error: number ~> string +(str1: string); +(str1: number); // Error: string ~> number +(CJS_Named: {num1: number, str1: string}); +(CJS_Named: number); // Error: Module ~> number + +import {num2} from "CJS_Clobbered"; // Error: No such export! +import {numExport} from "CJS_Clobbered"; +(numExport: number); +(numExport: string); // Error: number ~> string +import type {numType} from "CJS_Clobbered"; +(42: numType); +('asdf': numType); // Error: string ~> number + +import {strHidden} from "ES"; // Error: No such export! +import {str3} from "ES"; +(str3: string); +(str3: number); // Error: string ~> number + +import {num3} from "ES"; +(num3: number); +(num3: string); // Error: number ~> string + +import {C} from "ES"; +import type {C as CType} from "ES"; +(new C(): C); +(42: C); // Error: number ~> C +(new C(): CType); +(42: CType); // Error: number ~> CType + +import {T} from "ES"; // Error: T is a type import, not a value +import type {T as T2} from "ES"; +(42: T2); +('asdf': T2); // Error: string ~> number + +import {exports as nope} from "ES"; // Error: Not an export diff --git a/tests/es_declare_module/flow-typed/__snapshots__/jsfmt.spec.js.snap b/tests/es_declare_module/flow-typed/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..4d0c81b089be --- /dev/null +++ b/tests/es_declare_module/flow-typed/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,40 @@ +exports[`test declares.js 1`] = ` +"declare module \"CJS_Named\" { + declare var num1: number; + declare var str1: string; +} + +declare module \"CJS_Clobbered\" { + declare var num2: number; + declare type numType = number; + declare var exports: { + numExport: number, + }; +} + +declare module \"ES\" { + declare var strHidden: string; + declare export {strHidden as str3}; + declare export var num3: number; + declare export class C {} + declare export type T = number; + declare var exports: number; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (16:10) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$7.flowParseDeclare (/node_modules/babylon/lib/index.js:4490:10) + at Parser.pp$7.flowParseDeclareModule (/node_modules/babylon/lib/index.js:4518:20) + at Parser.pp$7.flowParseDeclare (/node_modules/babylon/lib/index.js:4483:19) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5235:25) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) +" +`; diff --git a/tests/es_declare_module/flow-typed/declares.js b/tests/es_declare_module/flow-typed/declares.js new file mode 100644 index 000000000000..50198afb76c9 --- /dev/null +++ b/tests/es_declare_module/flow-typed/declares.js @@ -0,0 +1,21 @@ +declare module "CJS_Named" { + declare var num1: number; + declare var str1: string; +} + +declare module "CJS_Clobbered" { + declare var num2: number; + declare type numType = number; + declare var exports: { + numExport: number, + }; +} + +declare module "ES" { + declare var strHidden: string; + declare export {strHidden as str3}; + declare export var num3: number; + declare export class C {} + declare export type T = number; + declare var exports: number; +} diff --git a/tests/es_declare_module/flow-typed/jsfmt.spec.js b/tests/es_declare_module/flow-typed/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/es_declare_module/flow-typed/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/es_declare_module/jsfmt.spec.js b/tests/es_declare_module/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/es_declare_module/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/esproposal_class_instance_fields.ignore/__snapshots__/jsfmt.spec.js.snap b/tests/esproposal_class_instance_fields.ignore/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..1f28115f0d5e --- /dev/null +++ b/tests/esproposal_class_instance_fields.ignore/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,20 @@ +exports[`test test.js 1`] = ` +"/* @flow */ + +class Foo { + annotationOnly: string; + initOnly = 'asdf' + initWithAnnotation: string = 'asdf'; + [computed]: string; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +class Foo { + annotationOnly: string; + initOnly = "asdf"; + initWithAnnotation: string = "asdf"; + [computed]: string; +} + +" +`; diff --git a/tests/esproposal_class_instance_fields.ignore/jsfmt.spec.js b/tests/esproposal_class_instance_fields.ignore/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/esproposal_class_instance_fields.ignore/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/esproposal_class_instance_fields.ignore/test.js b/tests/esproposal_class_instance_fields.ignore/test.js new file mode 100644 index 000000000000..7525cf355c95 --- /dev/null +++ b/tests/esproposal_class_instance_fields.ignore/test.js @@ -0,0 +1,8 @@ +/* @flow */ + +class Foo { + annotationOnly: string; + initOnly = 'asdf' + initWithAnnotation: string = 'asdf'; + [computed]: string; +} diff --git a/tests/esproposal_class_instance_fields.warn/__snapshots__/jsfmt.spec.js.snap b/tests/esproposal_class_instance_fields.warn/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..c2c53423e3e9 --- /dev/null +++ b/tests/esproposal_class_instance_fields.warn/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,18 @@ +exports[`test test.js 1`] = ` +"/* @flow */ + +class Foo { + annotationOnly: string; + initOnly = 'asdf'; + initWithAnnotation: string = 'asdf'; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +class Foo { + annotationOnly: string; + initOnly = "asdf"; + initWithAnnotation: string = "asdf"; +} + +" +`; diff --git a/tests/esproposal_class_instance_fields.warn/jsfmt.spec.js b/tests/esproposal_class_instance_fields.warn/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/esproposal_class_instance_fields.warn/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/esproposal_class_instance_fields.warn/test.js b/tests/esproposal_class_instance_fields.warn/test.js new file mode 100644 index 000000000000..ac74ba3e8d7f --- /dev/null +++ b/tests/esproposal_class_instance_fields.warn/test.js @@ -0,0 +1,7 @@ +/* @flow */ + +class Foo { + annotationOnly: string; + initOnly = 'asdf'; + initWithAnnotation: string = 'asdf'; +} diff --git a/tests/esproposal_class_static_fields.ignore/__snapshots__/jsfmt.spec.js.snap b/tests/esproposal_class_static_fields.ignore/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..1ce10796ca4c --- /dev/null +++ b/tests/esproposal_class_static_fields.ignore/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,18 @@ +exports[`test test.js 1`] = ` +"/* @flow */ + +class Foo { + static annotationOnly: string; + static initOnly = 'asdf'; + static initWithAnnotation: string = 'asdf'; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +class Foo { + static annotationOnly: string; + static initOnly = "asdf"; + static initWithAnnotation: string = "asdf"; +} + +" +`; diff --git a/tests/esproposal_class_static_fields.ignore/jsfmt.spec.js b/tests/esproposal_class_static_fields.ignore/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/esproposal_class_static_fields.ignore/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/esproposal_class_static_fields.ignore/test.js b/tests/esproposal_class_static_fields.ignore/test.js new file mode 100644 index 000000000000..f47f1b642f4f --- /dev/null +++ b/tests/esproposal_class_static_fields.ignore/test.js @@ -0,0 +1,7 @@ +/* @flow */ + +class Foo { + static annotationOnly: string; + static initOnly = 'asdf'; + static initWithAnnotation: string = 'asdf'; +} diff --git a/tests/esproposal_class_static_fields.warn/__snapshots__/jsfmt.spec.js.snap b/tests/esproposal_class_static_fields.warn/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..1ce10796ca4c --- /dev/null +++ b/tests/esproposal_class_static_fields.warn/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,18 @@ +exports[`test test.js 1`] = ` +"/* @flow */ + +class Foo { + static annotationOnly: string; + static initOnly = 'asdf'; + static initWithAnnotation: string = 'asdf'; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +class Foo { + static annotationOnly: string; + static initOnly = "asdf"; + static initWithAnnotation: string = "asdf"; +} + +" +`; diff --git a/tests/esproposal_class_static_fields.warn/jsfmt.spec.js b/tests/esproposal_class_static_fields.warn/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/esproposal_class_static_fields.warn/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/esproposal_class_static_fields.warn/test.js b/tests/esproposal_class_static_fields.warn/test.js new file mode 100644 index 000000000000..f47f1b642f4f --- /dev/null +++ b/tests/esproposal_class_static_fields.warn/test.js @@ -0,0 +1,7 @@ +/* @flow */ + +class Foo { + static annotationOnly: string; + static initOnly = 'asdf'; + static initWithAnnotation: string = 'asdf'; +} diff --git a/tests/esproposal_decorators.ignore/__snapshots__/jsfmt.spec.js.snap b/tests/esproposal_decorators.ignore/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..bc24fea44557 --- /dev/null +++ b/tests/esproposal_decorators.ignore/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,29 @@ +exports[`test test.js 1`] = ` +"/* @flow */ + +@decorator4 +class Foo { + @decorator1 + method1() {} + + @decorator2 + @decorator3 + method2() {} +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +@decorator4 +class Foo { + @decorator1 + method1() { + + } + @decorator2 + @decorator3 + method2() { + + } +} + +" +`; diff --git a/tests/esproposal_decorators.ignore/jsfmt.spec.js b/tests/esproposal_decorators.ignore/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/esproposal_decorators.ignore/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/esproposal_decorators.ignore/test.js b/tests/esproposal_decorators.ignore/test.js new file mode 100644 index 000000000000..972036955184 --- /dev/null +++ b/tests/esproposal_decorators.ignore/test.js @@ -0,0 +1,11 @@ +/* @flow */ + +@decorator4 +class Foo { + @decorator1 + method1() {} + + @decorator2 + @decorator3 + method2() {} +} diff --git a/tests/esproposal_decorators.warn/__snapshots__/jsfmt.spec.js.snap b/tests/esproposal_decorators.warn/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..bc24fea44557 --- /dev/null +++ b/tests/esproposal_decorators.warn/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,29 @@ +exports[`test test.js 1`] = ` +"/* @flow */ + +@decorator4 +class Foo { + @decorator1 + method1() {} + + @decorator2 + @decorator3 + method2() {} +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +@decorator4 +class Foo { + @decorator1 + method1() { + + } + @decorator2 + @decorator3 + method2() { + + } +} + +" +`; diff --git a/tests/esproposal_decorators.warn/jsfmt.spec.js b/tests/esproposal_decorators.warn/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/esproposal_decorators.warn/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/esproposal_decorators.warn/test.js b/tests/esproposal_decorators.warn/test.js new file mode 100644 index 000000000000..972036955184 --- /dev/null +++ b/tests/esproposal_decorators.warn/test.js @@ -0,0 +1,11 @@ +/* @flow */ + +@decorator4 +class Foo { + @decorator1 + method1() {} + + @decorator2 + @decorator3 + method2() {} +} diff --git a/tests/esproposal_export_star_as.enable/__snapshots__/jsfmt.spec.js.snap b/tests/esproposal_export_star_as.enable/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..3b337efbf4b7 --- /dev/null +++ b/tests/esproposal_export_star_as.enable/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,56 @@ +exports[`test dest.js 1`] = ` +"// @flow + +import {source} from \"./test\"; + +var a: number = source.num; +var b: string = source.num; // Error: num ~> string + +var c: string = source.str; +var d: number = source.str; // Error: num ~> string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +import { source } from \"./test\"; +var a: number = source.num; +var b: string = source.num;// Error: num ~> string +var c: string = source.str; +var d: number = source.str;// Error: num ~> string + +" +`; + +exports[`test source.js 1`] = ` +"// @flow + +export var str = \'asdf\'; +export var num = 42; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +export var str = \"asdf\"; +export var num = 42; + +" +`; + +exports[`test test.js 1`] = ` +"// @flow + +export * as source from \"./source\"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1785 + fromString(\", \").join(path.map(print, \"specifiers\")), + ^ + +TypeError: fromString(...).join is not a function + at printExportDeclaration (/src/printer.js:1785:26) + at genericPrintNoParens (/src/printer.js:488:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:238:16 +" +`; diff --git a/tests/esproposal_export_star_as.enable/dest.js b/tests/esproposal_export_star_as.enable/dest.js new file mode 100644 index 000000000000..99f558d3bd43 --- /dev/null +++ b/tests/esproposal_export_star_as.enable/dest.js @@ -0,0 +1,9 @@ +// @flow + +import {source} from "./test"; + +var a: number = source.num; +var b: string = source.num; // Error: num ~> string + +var c: string = source.str; +var d: number = source.str; // Error: num ~> string diff --git a/tests/esproposal_export_star_as.enable/jsfmt.spec.js b/tests/esproposal_export_star_as.enable/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/esproposal_export_star_as.enable/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/esproposal_export_star_as.enable/source.js b/tests/esproposal_export_star_as.enable/source.js new file mode 100644 index 000000000000..98d19df9fad7 --- /dev/null +++ b/tests/esproposal_export_star_as.enable/source.js @@ -0,0 +1,4 @@ +// @flow + +export var str = 'asdf'; +export var num = 42; diff --git a/tests/esproposal_export_star_as.enable/test.js b/tests/esproposal_export_star_as.enable/test.js new file mode 100644 index 000000000000..5fdc1272948f --- /dev/null +++ b/tests/esproposal_export_star_as.enable/test.js @@ -0,0 +1,3 @@ +// @flow + +export * as source from "./source"; diff --git a/tests/esproposal_export_star_as.ignore/__snapshots__/jsfmt.spec.js.snap b/tests/esproposal_export_star_as.ignore/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..5e14073ad095 --- /dev/null +++ b/tests/esproposal_export_star_as.ignore/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,56 @@ +exports[`test dest.js 1`] = ` +"// @flow + +import {source} from \"./test\"; + +var a: number = source.num; +var b: string = source.num; // Error: num ~> string + +var c: string = source.str; +var d: number = source.str; // Ignored error: num ~> string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +import { source } from \"./test\"; +var a: number = source.num; +var b: string = source.num;// Error: num ~> string +var c: string = source.str; +var d: number = source.str;// Ignored error: num ~> string + +" +`; + +exports[`test source.js 1`] = ` +"// @flow + +export var str = \'asdf\'; +export var num = 42; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +export var str = \"asdf\"; +export var num = 42; + +" +`; + +exports[`test test.js 1`] = ` +"// @flow + +export * as source from \"./source\"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1785 + fromString(\", \").join(path.map(print, \"specifiers\")), + ^ + +TypeError: fromString(...).join is not a function + at printExportDeclaration (/src/printer.js:1785:26) + at genericPrintNoParens (/src/printer.js:488:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:238:16 +" +`; diff --git a/tests/esproposal_export_star_as.ignore/dest.js b/tests/esproposal_export_star_as.ignore/dest.js new file mode 100644 index 000000000000..fa12f1ad78a9 --- /dev/null +++ b/tests/esproposal_export_star_as.ignore/dest.js @@ -0,0 +1,9 @@ +// @flow + +import {source} from "./test"; + +var a: number = source.num; +var b: string = source.num; // Error: num ~> string + +var c: string = source.str; +var d: number = source.str; // Ignored error: num ~> string diff --git a/tests/esproposal_export_star_as.ignore/jsfmt.spec.js b/tests/esproposal_export_star_as.ignore/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/esproposal_export_star_as.ignore/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/esproposal_export_star_as.ignore/source.js b/tests/esproposal_export_star_as.ignore/source.js new file mode 100644 index 000000000000..98d19df9fad7 --- /dev/null +++ b/tests/esproposal_export_star_as.ignore/source.js @@ -0,0 +1,4 @@ +// @flow + +export var str = 'asdf'; +export var num = 42; diff --git a/tests/esproposal_export_star_as.ignore/test.js b/tests/esproposal_export_star_as.ignore/test.js new file mode 100644 index 000000000000..5fdc1272948f --- /dev/null +++ b/tests/esproposal_export_star_as.ignore/test.js @@ -0,0 +1,3 @@ +// @flow + +export * as source from "./source"; diff --git a/tests/esproposal_export_star_as.warn/__snapshots__/jsfmt.spec.js.snap b/tests/esproposal_export_star_as.warn/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..9fc286f9e3a6 --- /dev/null +++ b/tests/esproposal_export_star_as.warn/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,22 @@ +exports[`test test.js 1`] = ` +"/* @flow */ + +export * as foo from \"./test\"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1785 + fromString(\", \").join(path.map(print, \"specifiers\")), + ^ + +TypeError: fromString(...).join is not a function + at printExportDeclaration (/src/printer.js:1785:26) + at genericPrintNoParens (/src/printer.js:488:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:238:16 +" +`; diff --git a/tests/esproposal_export_star_as.warn/jsfmt.spec.js b/tests/esproposal_export_star_as.warn/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/esproposal_export_star_as.warn/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/esproposal_export_star_as.warn/test.js b/tests/esproposal_export_star_as.warn/test.js new file mode 100644 index 000000000000..ee71a9b8482e --- /dev/null +++ b/tests/esproposal_export_star_as.warn/test.js @@ -0,0 +1,3 @@ +/* @flow */ + +export * as foo from "./test"; diff --git a/tests/export_default/P.js b/tests/export_default/P.js new file mode 100644 index 000000000000..a0181c306670 --- /dev/null +++ b/tests/export_default/P.js @@ -0,0 +1 @@ +module.exports = require('M'); diff --git a/tests/export_default/__snapshots__/jsfmt.spec.js.snap b/tests/export_default/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..74c813571c86 --- /dev/null +++ b/tests/export_default/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,27 @@ +exports[`test P.js 1`] = ` +"module.exports = require('M'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +module.exports = require("M"); + +" +`; + +exports[`test test.js 1`] = ` +"var M = require('M'); +var N = require('N'); +N.x = M(N.x); +var P = require('./P'); // implementation of P redirects to module M +N.y = P(N.y); +var Q = require('Q'); // declaration of Q redirects to module M +N.z = Q(N.z); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var M = require("M"); +var N = require("N"); +N.x = M(N.x); +var P = require("./P");// implementation of P redirects to module M +N.y = P(N.y); +var Q = require("Q");// declaration of Q redirects to module M +N.z = Q(N.z); + +" +`; diff --git a/tests/export_default/jsfmt.spec.js b/tests/export_default/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/export_default/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/export_default/lib/__snapshots__/jsfmt.spec.js.snap b/tests/export_default/lib/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..c474b386925f --- /dev/null +++ b/tests/export_default/lib/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,30 @@ +exports[`test lib.js 1`] = ` +"declare module M { + declare function exports(x:string): string; +} +declare module N { + declare var x: number; + declare var y: number; + declare var z: number; +} +declare module Q { + declare var exports: $Exports<\'M\'>; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/export_default/lib/jsfmt.spec.js b/tests/export_default/lib/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/export_default/lib/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/export_default/lib/lib.js b/tests/export_default/lib/lib.js new file mode 100644 index 000000000000..b2d466ba1831 --- /dev/null +++ b/tests/export_default/lib/lib.js @@ -0,0 +1,11 @@ +declare module M { + declare function exports(x:string): string; +} +declare module N { + declare var x: number; + declare var y: number; + declare var z: number; +} +declare module Q { + declare var exports: $Exports<'M'>; +} diff --git a/tests/export_default/test.js b/tests/export_default/test.js new file mode 100644 index 000000000000..be26514294ab --- /dev/null +++ b/tests/export_default/test.js @@ -0,0 +1,7 @@ +var M = require('M'); +var N = require('N'); +N.x = M(N.x); +var P = require('./P'); // implementation of P redirects to module M +N.y = P(N.y); +var Q = require('Q'); // declaration of Q redirects to module M +N.z = Q(N.z); diff --git a/tests/export_type/__snapshots__/jsfmt.spec.js.snap b/tests/export_type/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..3e7a1e3fe947 --- /dev/null +++ b/tests/export_type/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,141 @@ +exports[`test cjs_with_types.js 1`] = ` +"/* @flow */ + +export type talias4 = number; +export interface IFoo { prop: number }; + +module.exports = {} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1391 + fromString(\", \").join(path.map(print, \"extends\")) + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1391:28) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at printExportDeclaration (/src/printer.js:1774:21) + at genericPrintNoParens (/src/printer.js:488:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) +" +`; + +exports[`test importer.js 1`] = ` +"/* @flow */ + +import type { + inlinedType1, + standaloneType1, + talias1, + talias3, +} from \"./types_only\"; + +var a: inlinedType1 = 42; +var b: inlinedType1 = \'asdf\'; // Error: string ~> number + +var c: standaloneType1 = 42; +var d: standaloneType1 = \'asdf\'; // Error: string ~> number + +var e: talias1 = 42; +var f: talias1 = \'asdf\'; // Error: string ~> number + +var g: talias3 = 42; +var h: talias3 = \'asdf\'; // Error: string ~> number + +import type {talias4} from \"./cjs_with_types\"; +var i: talias4 = 42; +var j: talias4 = \'asdf\'; // Error: string ~> number + +import {IFoo, IFoo2} from \"./types_only\"; + +var k: IFoo = {prop: 42}; +var l: IFoo = {prop: \'asdf\'}; // Error: {prop:string} ~> {prop:number} + +var m: IFoo2 = {prop: \'asdf\'}; +var n: IFoo2 = {prop: 42}; // Error: {prop:number} ~> {prop:string} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +import type { inlinedType1, standaloneType1, talias1, talias3 } from \"./types_only\"; +var a: inlinedType1 = 42; +var b: inlinedType1 = \"asdf\";// Error: string ~> number +var c: standaloneType1 = 42; +var d: standaloneType1 = \"asdf\";// Error: string ~> number +var e: talias1 = 42; +var f: talias1 = \"asdf\";// Error: string ~> number +var g: talias3 = 42; +var h: talias3 = \"asdf\";// Error: string ~> number +import type { talias4 } from \"./cjs_with_types\"; +var i: talias4 = 42; +var j: talias4 = \"asdf\";// Error: string ~> number +import { IFoo, IFoo2 } from \"./types_only\"; +var k: IFoo = { prop: 42 }; +var l: IFoo = { prop: \"asdf\" };// Error: {prop:string} ~> {prop:number} +var m: IFoo2 = { prop: \"asdf\" }; +var n: IFoo2 = { prop: 42 };// Error: {prop:number} ~> {prop:string} + +" +`; + +exports[`test types_only.js 1`] = ` +"/* @flow */ + +export type inlinedType1 = number; +var a: inlinedType1 = 42; +var b: inlinedType1 = \'asdf\'; // Error: string ~> number + +type standaloneType1 = number; +export type {standaloneType1}; + +type standaloneType2 = number; +export {standaloneType2}; // Error: Missing \`type\` keyword + +export type {talias1, talias2 as talias3, IFoo2} from \"./types_only2\"; + +export interface IFoo { prop: number }; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1785 + fromString(\", \").join(path.map(print, \"specifiers\")), + ^ + +TypeError: fromString(...).join is not a function + at printExportDeclaration (/src/printer.js:1785:26) + at genericPrintNoParens (/src/printer.js:488:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:238:16 +" +`; + +exports[`test types_only2.js 1`] = ` +"/* @flow */ + +export type talias1 = number; +export type talias2 = number; +export interface IFoo2 { prop: string }; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1391 + fromString(\", \").join(path.map(print, \"extends\")) + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1391:28) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at printExportDeclaration (/src/printer.js:1774:21) + at genericPrintNoParens (/src/printer.js:488:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) +" +`; diff --git a/tests/export_type/cjs_with_types.js b/tests/export_type/cjs_with_types.js new file mode 100644 index 000000000000..1b174f3ca346 --- /dev/null +++ b/tests/export_type/cjs_with_types.js @@ -0,0 +1,6 @@ +/* @flow */ + +export type talias4 = number; +export interface IFoo { prop: number }; + +module.exports = {} diff --git a/tests/export_type/importer.js b/tests/export_type/importer.js new file mode 100644 index 000000000000..cb35f910304f --- /dev/null +++ b/tests/export_type/importer.js @@ -0,0 +1,32 @@ +/* @flow */ + +import type { + inlinedType1, + standaloneType1, + talias1, + talias3, +} from "./types_only"; + +var a: inlinedType1 = 42; +var b: inlinedType1 = 'asdf'; // Error: string ~> number + +var c: standaloneType1 = 42; +var d: standaloneType1 = 'asdf'; // Error: string ~> number + +var e: talias1 = 42; +var f: talias1 = 'asdf'; // Error: string ~> number + +var g: talias3 = 42; +var h: talias3 = 'asdf'; // Error: string ~> number + +import type {talias4} from "./cjs_with_types"; +var i: talias4 = 42; +var j: talias4 = 'asdf'; // Error: string ~> number + +import {IFoo, IFoo2} from "./types_only"; + +var k: IFoo = {prop: 42}; +var l: IFoo = {prop: 'asdf'}; // Error: {prop:string} ~> {prop:number} + +var m: IFoo2 = {prop: 'asdf'}; +var n: IFoo2 = {prop: 42}; // Error: {prop:number} ~> {prop:string} diff --git a/tests/export_type/jsfmt.spec.js b/tests/export_type/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/export_type/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/export_type/types_only.js b/tests/export_type/types_only.js new file mode 100644 index 000000000000..315e7b6ff150 --- /dev/null +++ b/tests/export_type/types_only.js @@ -0,0 +1,15 @@ +/* @flow */ + +export type inlinedType1 = number; +var a: inlinedType1 = 42; +var b: inlinedType1 = 'asdf'; // Error: string ~> number + +type standaloneType1 = number; +export type {standaloneType1}; + +type standaloneType2 = number; +export {standaloneType2}; // Error: Missing `type` keyword + +export type {talias1, talias2 as talias3, IFoo2} from "./types_only2"; + +export interface IFoo { prop: number }; diff --git a/tests/export_type/types_only2.js b/tests/export_type/types_only2.js new file mode 100644 index 000000000000..4ef72006764c --- /dev/null +++ b/tests/export_type/types_only2.js @@ -0,0 +1,5 @@ +/* @flow */ + +export type talias1 = number; +export type talias2 = number; +export interface IFoo2 { prop: string }; diff --git a/tests/extensions/__snapshots__/jsfmt.spec.js.snap b/tests/extensions/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..27b381d5b26f --- /dev/null +++ b/tests/extensions/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,9 @@ +exports[`test foo.js 1`] = ` +"var imp = require('./bar'); +imp(1337); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var imp = require("./bar"); +imp(1337); + +" +`; diff --git a/tests/extensions/foo.js b/tests/extensions/foo.js new file mode 100644 index 000000000000..66c9fcbe962d --- /dev/null +++ b/tests/extensions/foo.js @@ -0,0 +1,2 @@ +var imp = require('./bar'); +imp(1337); diff --git a/tests/extensions/jsfmt.spec.js b/tests/extensions/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/extensions/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/facebook_fbt_none/__snapshots__/jsfmt.spec.js.snap b/tests/facebook_fbt_none/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..ca33925515bf --- /dev/null +++ b/tests/facebook_fbt_none/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,23 @@ +exports[`test main.js 1`] = ` +"// @flow +var React = require(\'react\'); +(: React.Element<*>); +(: number); // Error: ReactElement ~> number +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/facebook_fbt_none/jsfmt.spec.js b/tests/facebook_fbt_none/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/facebook_fbt_none/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/facebook_fbt_none/main.js b/tests/facebook_fbt_none/main.js new file mode 100644 index 000000000000..46a530d2b702 --- /dev/null +++ b/tests/facebook_fbt_none/main.js @@ -0,0 +1,4 @@ +// @flow +var React = require('react'); +(: React.Element<*>); +(: number); // Error: ReactElement ~> number diff --git a/tests/facebook_fbt_some/__snapshots__/jsfmt.spec.js.snap b/tests/facebook_fbt_some/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..72087bf26494 --- /dev/null +++ b/tests/facebook_fbt_some/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,12 @@ +exports[`test main.js 1`] = ` +"// @flow + +(: number); +(: string); // Error (the libdef in this test marks fbt as number) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +(: number); +(: string);// Error (the libdef in this test marks fbt as number) + +" +`; diff --git a/tests/facebook_fbt_some/flow-typed/__snapshots__/jsfmt.spec.js.snap b/tests/facebook_fbt_some/flow-typed/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..80e8f68130a3 --- /dev/null +++ b/tests/facebook_fbt_some/flow-typed/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,7 @@ +exports[`test fbt.js 1`] = ` +"type Fbt = number +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +type Fbt = number; + +" +`; diff --git a/tests/facebook_fbt_some/flow-typed/fbt.js b/tests/facebook_fbt_some/flow-typed/fbt.js new file mode 100644 index 000000000000..373058cd8684 --- /dev/null +++ b/tests/facebook_fbt_some/flow-typed/fbt.js @@ -0,0 +1 @@ +type Fbt = number diff --git a/tests/facebook_fbt_some/flow-typed/jsfmt.spec.js b/tests/facebook_fbt_some/flow-typed/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/facebook_fbt_some/flow-typed/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/facebook_fbt_some/jsfmt.spec.js b/tests/facebook_fbt_some/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/facebook_fbt_some/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/facebook_fbt_some/main.js b/tests/facebook_fbt_some/main.js new file mode 100644 index 000000000000..01900f6a68e2 --- /dev/null +++ b/tests/facebook_fbt_some/main.js @@ -0,0 +1,4 @@ +// @flow + +(: number); +(: string); // Error (the libdef in this test marks fbt as number) diff --git a/tests/facebookisms/Bar.js b/tests/facebookisms/Bar.js new file mode 100644 index 000000000000..fa9036afc162 --- /dev/null +++ b/tests/facebookisms/Bar.js @@ -0,0 +1,2 @@ +var Bar = { x: 0 }; +module.exports = Bar; diff --git a/tests/facebookisms/__snapshots__/jsfmt.spec.js.snap b/tests/facebookisms/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..a92ab7b855eb --- /dev/null +++ b/tests/facebookisms/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,242 @@ +exports[`test Bar.js 1`] = ` +"var Bar = { x: 0 }; +module.exports = Bar; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var Bar = { x: 0 }; +module.exports = Bar; + +" +`; + +exports[`test copyProperties.js 1`] = ` +"// @flow + +let tests = [ + // global + function() { + (copyProperties()); // error, unknown global + }, + + // annotation + function(copyProperties: Object$Assign) { + let result = {}; + result.baz = false; + (copyProperties( + result, + { foo: 'a' }, + { bar: 123 } + ): { foo: string, bar: number, baz: boolean }); + }, + + // module from lib + function() { + const copyProperties = require('copyProperties'); + let x = { foo: 'a' }; + let y = { bar: 123 }; + (copyProperties({}, x, y): { foo: string, bar: number }); + }, + + // too few args + function(copyProperties: Object$Assign) { + copyProperties(); + (copyProperties({ foo: 'a' }): { foo: number }); // err, num !~> string + }, + + // passed as a function + function(copyProperties: Object$Assign) { + function x(cb: Function) {} + x(copyProperties); + } +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +let tests = [ + // global + function() { + copyProperties();// error, unknown global + }, + // annotation + function(copyProperties: Object$Assign) { + let result = {}; + result.baz = false; + (copyProperties(result, { foo: "a" }, { bar: 123 }): { + foo: string, + bar: number, + baz: boolean + }); + }, + // module from lib + function() { + const copyProperties = require("copyProperties"); + let x = { foo: "a" }; + let y = { bar: 123 }; + (copyProperties({}, x, y): { foo: string, bar: number }); + }, + // too few args + function(copyProperties: Object$Assign) { + copyProperties(); + (copyProperties({ foo: "a" }): { foo: number });// err, num !~> string + }, + // passed as a function + function(copyProperties: Object$Assign) { + function x(cb: Function) { + + } + x(copyProperties); + } +]; + +" +`; + +exports[`test invariant.js 1`] = ` +"/* @flow */ + +let tests = [ + function() { + let x: ?string = null; + invariant(x, 'truthy only'); // error, forgot to require invariant + }, + + function(invariant: Function) { + let x: ?string = null; + invariant(x); + (x: string); + } +] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +let tests = [ + function() { + let x: ?string = null; + invariant(x, "truthy only");// error, forgot to require invariant + }, + function(invariant: Function) { + let x: ?string = null; + invariant(x); + (x: string); + } +]; + +" +`; + +exports[`test lib.js 1`] = ` +"declare module "copyProperties" { + declare var exports: Object$Assign; +} + +declare module "mergeInto" { + declare var exports: $Facebookism$MergeInto; +} + +declare module "mixin" { + declare var exports: $Facebookism$Mixin; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +declare module "copyProperties" { + declare var exports: Object$Assign; +} +declare module "mergeInto" { + declare var exports: $Facebookism$MergeInto; +} +declare module "mixin" { + declare var exports: $Facebookism$Mixin; +} + +" +`; + +exports[`test mergeInto.js 1`] = ` +"// @flow + +let tests = [ + // global + function() { + (mergeInto()); // error, unknown global + }, + + // annotation + function(mergeInto: $Facebookism$MergeInto) { + let result = {}; + result.baz = false; + (mergeInto(result, { foo: 'a' }, { bar: 123 }): void); + (result: { foo: string, bar: number, baz: boolean }); + }, + + // module from lib + function() { + const mergeInto = require('mergeInto'); + let result: { foo?: string, bar?: number, baz: boolean } = { baz: false }; + (mergeInto(result, { foo: 'a' }, { bar: 123 }): void); + }, + + // too few args + function(mergeInto: $Facebookism$MergeInto) { + mergeInto(); + }, + + // passed as a function + function(mergeInto: $Facebookism$MergeInto) { + function x(cb: Function) {} + x(mergeInto); + } +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +let tests = [ + // global + function() { + mergeInto();// error, unknown global + }, + // annotation + function(mergeInto: $Facebookism$MergeInto) { + let result = {}; + result.baz = false; + (mergeInto(result, { foo: "a" }, { bar: 123 }): void); + (result: { foo: string, bar: number, baz: boolean }); + }, + // module from lib + function() { + const mergeInto = require("mergeInto"); + let result: { foo?: string, bar?: number, baz: boolean } = { baz: false }; + (mergeInto(result, { foo: "a" }, { bar: 123 }): void); + }, + // too few args + function(mergeInto: $Facebookism$MergeInto) { + mergeInto(); + }, + // passed as a function + function(mergeInto: $Facebookism$MergeInto) { + function x(cb: Function) { + + } + x(mergeInto); + } +]; + +" +`; + +exports[`test test.js 1`] = ` +"var Bar = require('./Bar'); +var mixin = require('mixin'); + +class Foo extends mixin(Bar) { + m() { + var x: string = this.x; + this.y = ""; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var Bar = require("./Bar"); +var mixin = require("mixin"); +class Foo extends mixin(Bar) { + m() { + var x: string = this.x; + this.y = ""; + } +} + +" +`; diff --git a/tests/facebookisms/copyProperties.js b/tests/facebookisms/copyProperties.js new file mode 100644 index 000000000000..0f20e91b630e --- /dev/null +++ b/tests/facebookisms/copyProperties.js @@ -0,0 +1,39 @@ +// @flow + +let tests = [ + // global + function() { + (copyProperties()); // error, unknown global + }, + + // annotation + function(copyProperties: Object$Assign) { + let result = {}; + result.baz = false; + (copyProperties( + result, + { foo: 'a' }, + { bar: 123 } + ): { foo: string, bar: number, baz: boolean }); + }, + + // module from lib + function() { + const copyProperties = require('copyProperties'); + let x = { foo: 'a' }; + let y = { bar: 123 }; + (copyProperties({}, x, y): { foo: string, bar: number }); + }, + + // too few args + function(copyProperties: Object$Assign) { + copyProperties(); + (copyProperties({ foo: 'a' }): { foo: number }); // err, num !~> string + }, + + // passed as a function + function(copyProperties: Object$Assign) { + function x(cb: Function) {} + x(copyProperties); + } +]; diff --git a/tests/facebookisms/invariant.js b/tests/facebookisms/invariant.js new file mode 100644 index 000000000000..7474dd69d35f --- /dev/null +++ b/tests/facebookisms/invariant.js @@ -0,0 +1,14 @@ +/* @flow */ + +let tests = [ + function() { + let x: ?string = null; + invariant(x, 'truthy only'); // error, forgot to require invariant + }, + + function(invariant: Function) { + let x: ?string = null; + invariant(x); + (x: string); + } +] diff --git a/tests/facebookisms/jsfmt.spec.js b/tests/facebookisms/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/facebookisms/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/facebookisms/lib.js b/tests/facebookisms/lib.js new file mode 100644 index 000000000000..bc96320ec47d --- /dev/null +++ b/tests/facebookisms/lib.js @@ -0,0 +1,11 @@ +declare module "copyProperties" { + declare var exports: Object$Assign; +} + +declare module "mergeInto" { + declare var exports: $Facebookism$MergeInto; +} + +declare module "mixin" { + declare var exports: $Facebookism$Mixin; +} diff --git a/tests/facebookisms/mergeInto.js b/tests/facebookisms/mergeInto.js new file mode 100644 index 000000000000..9b1d08df751a --- /dev/null +++ b/tests/facebookisms/mergeInto.js @@ -0,0 +1,34 @@ +// @flow + +let tests = [ + // global + function() { + (mergeInto()); // error, unknown global + }, + + // annotation + function(mergeInto: $Facebookism$MergeInto) { + let result = {}; + result.baz = false; + (mergeInto(result, { foo: 'a' }, { bar: 123 }): void); + (result: { foo: string, bar: number, baz: boolean }); + }, + + // module from lib + function() { + const mergeInto = require('mergeInto'); + let result: { foo?: string, bar?: number, baz: boolean } = { baz: false }; + (mergeInto(result, { foo: 'a' }, { bar: 123 }): void); + }, + + // too few args + function(mergeInto: $Facebookism$MergeInto) { + mergeInto(); + }, + + // passed as a function + function(mergeInto: $Facebookism$MergeInto) { + function x(cb: Function) {} + x(mergeInto); + } +]; diff --git a/tests/facebookisms/test.js b/tests/facebookisms/test.js new file mode 100644 index 000000000000..5d484ac096fa --- /dev/null +++ b/tests/facebookisms/test.js @@ -0,0 +1,9 @@ +var Bar = require('./Bar'); +var mixin = require('mixin'); + +class Foo extends mixin(Bar) { + m() { + var x: string = this.x; + this.y = ""; + } +} diff --git a/tests/fetch/__snapshots__/jsfmt.spec.js.snap b/tests/fetch/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..6c8225a8ddec --- /dev/null +++ b/tests/fetch/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,329 @@ +exports[`test fetch.js 1`] = ` +"/* @flow */ + +// most of the details are tested in the separate file +// here I test the basic usage + +const myRequest = new Request(\'http://google.com\'); + +const a: Promise = + fetch(myRequest) + .then(response => response.text()); + +const b: Promise = fetch(myRequest); // incorrect + +var myInit = { method: \'GET\', + headers: { + \'Content-Type\': \'image/jpeg\' + }, + mode: \'cors\', + cache: \'default\' }; + +const c: Promise = + fetch(\'image.png\') + .then(response => response.blob()); // correct + +const d: Promise = fetch(\'image.png\'); // incorrect +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test headers.js 1`] = ` +"/* @flow */ + +const a = new Headers(\"\'Content-Type\': \'image/jpeg\'\"); // not correct +const b = new Headers([\'Content-Type\', \'image/jpeg\']); // not correct +const c = new Headers({\'Content-Type\', \'image/jpeg\'}); // correct +const d = new Headers(c); // correct +const e: Headers = new Headers(); // correct +e.append(\'Content-Type\', \'image/jpeg\'); // correct +e.append(\'Content-Type\'); // not correct +e.append({\'Content-Type\', \'image/jpeg\'}); // not correct +e.set(\'Content-Type\', \'image/jpeg\'); // correct +e.set(\'Content-Type\'); // not correct +e.set({\'Content-Type\', \'image/jpeg\'}); // not correct + +const f: Headers = e.append(\'Content-Type\', \'image/jpeg\'); // not correct + +const g: string = e.get(\'Content-Type\'); // correct +const h: number = e.get(\'Content-Type\'); // not correct + +for (let v of e) { + const [i, j]: [string, string] = v; // correct +} + +for (let v of e.entries()) { + const [i, j]: [string, string] = v; // correct +} + +e.getAll(\'content-type\').forEach((v: string) => {}); // correct +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (5:37) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$3.parseObjPropValue (/node_modules/babylon/lib/index.js:3940:8) + at Parser.parseObjPropValue (/node_modules/babylon/lib/index.js:5537:13) + at Parser.pp$3.parseObj (/node_modules/babylon/lib/index.js:3871:10) + at Parser.pp$3.parseExprAtom (/node_modules/babylon/lib/index.js:3561:19) + at Parser.parseExprAtom (/node_modules/babylon/lib/index.js:6408:22) + at Parser.pp$3.parseExprSubscripts (/node_modules/babylon/lib/index.js:3337:19) + at Parser.pp$3.parseMaybeUnary (/node_modules/babylon/lib/index.js:3317:19) + at Parser.pp$3.parseExprOps (/node_modules/babylon/lib/index.js:3247:19) +" +`; + +exports[`test request.js 1`] = ` +"/* @flow */ +const a: Request = new Request(); // incorrect +const b: Request = new Request(\'http://example.org\'); // correct +const c: Request = new Request(b); // correct +const d: Request = new Request(c.clone()); // correct (doesn\'t make much sense though) +const e: Request = new Request(b, c); // incorrect + +const f: Request = new Request({}) // incorrect +const g: Request = new Request(\'http://example.org\', {}) // correct + +const h: Request = new Request(\'http://example.org\', { + method: \'GET\', + headers: { + \'Content-Type\': \'image/jpeg\' + }, + mode: \'cors\', + cache: \'default\' +}) // correct + +const i: Request = new Request(\'http://example.org\', { + method: \'POST\', + headers: { + \'Content-Type\': \'image/jpeg\' + }, + body: new URLSearchParams(\"key=value\"), + mode: \'cors\', + cache: \'default\' +}) // correct + +const j: Request = new Request(\'http://example.org\', { + method: \'GET\', + headers: \'Content-Type: image/jpeg\', + mode: \'cors\', + cache: \'default\' +}) // incorrect - headers is string + +const k: Request = new Request(\'http://example.org\', { + method: \'CONNECT\', + headers: { + \'Content-Type\': \'image/jpeg\' + }, + mode: \'cors\', + cache: \'default\' +}) // incorrect - CONNECT is forbidden + +var l: boolean = h.bodyUsed; + +h.text().then((t: string) => t); // correct +h.text().then((t: Buffer) => t); // incorrect +h.arrayBuffer().then((ab: ArrayBuffer) => ab); // correct +h.arrayBuffer().then((ab: Buffer) => ab); // incorrect +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +const a: Request = new Request();// incorrect +const b: Request = new Request(\"http://example.org\");// correct +const c: Request = new Request(b);// correct +const d: Request = new Request( + c.clone() +);// correct (doesn\'t make much sense though) +const e: Request = new Request(b, c);// incorrect +const f: Request = new Request({});// incorrect +const g: Request = new Request(\"http://example.org\", {});// correct +const h: Request = new Request( + \"http://example.org\", + { + method: \"GET\", + headers: { \"Content-Type\": \"image/jpeg\" }, + mode: \"cors\", + cache: \"default\" + } +);// correct +const i: Request = new Request( + \"http://example.org\", + { + method: \"POST\", + headers: { \"Content-Type\": \"image/jpeg\" }, + body: new URLSearchParams(\"key=value\"), + mode: \"cors\", + cache: \"default\" + } +);// correct +const j: Request = new Request( + \"http://example.org\", + { + method: \"GET\", + headers: \"Content-Type: image/jpeg\", + mode: \"cors\", + cache: \"default\" + } +);// incorrect - headers is string +const k: Request = new Request( + \"http://example.org\", + { + method: \"CONNECT\", + headers: { \"Content-Type\": \"image/jpeg\" }, + mode: \"cors\", + cache: \"default\" + } +);// incorrect - CONNECT is forbidden +var l: boolean = h.bodyUsed; +h.text().then((t: string) => t);// correct +h.text().then((t: Buffer) => t);// incorrect +h.arrayBuffer().then((ab: ArrayBuffer) => ab);// correct +h.arrayBuffer().then((ab: Buffer) => ab);// incorrect + +" +`; + +exports[`test response.js 1`] = ` +"/* @flow */ +const a: Response = new Response(); // correct +const b: Response = new Response(new Blob()); // correct +const c: Response = new Response(new FormData()); // correct + +const d: Response = new Response(new FormData(), { + status: 404 +}); // correct + +const e: Response = new Response(\"responsebody\", { + status: \"404\" +}); // incorrect + +const f: Response = new Response(\"responsebody\", { + status: 404, + headers: \"\'Content-Type\': \'image/jpeg\'\" +}); // incorrect + +const g: Response = new Response(\"responsebody\", { + status: 404, + headers: { + \'Content-Type\': \'image/jpeg\' + } +}); // correct + +const h: Response = new Response(\"responsebody\", { + status: 404, + headers: new Headers({ + \'Content-Type\': \'image/jpeg\' + }) +}); // correct, if verbose + +const i: Response = new Response({ + status: 404, + headers: new Headers({ + \'Content-Type\': \'image/jpeg\' + }) +}); // incorrect + +const ok: boolean = h.ok; +const status: number = h.status; + +h.text().then((t: string) => t); // correct +h.text().then((t: Buffer) => t); // incorrect +h.arrayBuffer().then((ab: ArrayBuffer) => ab); // correct +h.arrayBuffer().then((ab: Buffer) => ab); // incorrect +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +const a: Response = new Response();// correct +const b: Response = new Response(new Blob());// correct +const c: Response = new Response(new FormData());// correct +const d: Response = new Response(new FormData(), { status: 404 });// correct +const e: Response = new Response(\"responsebody\", { status: \"404\" });// incorrect +const f: Response = new Response( + \"responsebody\", + { status: 404, headers: \"\'Content-Type\': \'image/jpeg\'\" } +);// incorrect +const g: Response = new Response( + \"responsebody\", + { status: 404, headers: { \"Content-Type\": \"image/jpeg\" } } +);// correct +const h: Response = new Response( + \"responsebody\", + { status: 404, headers: new Headers({ \"Content-Type\": \"image/jpeg\" }) } +);// correct, if verbose +const i: Response = new Response({ + status: 404, + headers: new Headers({ \"Content-Type\": \"image/jpeg\" }) +});// incorrect +const ok: boolean = h.ok; +const status: number = h.status; +h.text().then((t: string) => t);// correct +h.text().then((t: Buffer) => t);// incorrect +h.arrayBuffer().then((ab: ArrayBuffer) => ab);// correct +h.arrayBuffer().then((ab: Buffer) => ab);// incorrect + +" +`; + +exports[`test urlsearchparams.js 1`] = ` +"/* @flow */ + +const a = new URLSearchParams(\"key1=value1\"); // correct +const b = new URLSearchParams([\'key1\', \'value1\']); // not correct +const c = new URLSearchParams({\'key1\', \'value1\'}); // not correct +const d = new URLSearchParams(c); // correct +const e: URLSearchParams = new URLSearchParams(); // correct +e.append(\'key1\', \'value1\'); // correct +e.append(\'key1\'); // not correct +e.append({\'key1\', \'value1\'}); // not correct +e.set(\'key1\', \'value1\'); // correct +e.set(\'key1\'); // not correct +e.set({\'key1\', \'value1\'}); // not correct + +const f: URLSearchParams = e.append(\'key1\', \'value1\'); // not correct + +const g: string = e.get(\'key1\'); // correct +const h: number = e.get(\'key1\'); // not correct + +for (let v of e) { + const [i, j]: [string, string] = v; // correct +} + +for (let v of e.entries()) { + const [i, j]: [string, string] = v; // correct +} + +e.getAll(\'key1\').forEach((v: string) => {}); // correct +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (5:37) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$3.parseObjPropValue (/node_modules/babylon/lib/index.js:3940:8) + at Parser.parseObjPropValue (/node_modules/babylon/lib/index.js:5537:13) + at Parser.pp$3.parseObj (/node_modules/babylon/lib/index.js:3871:10) + at Parser.pp$3.parseExprAtom (/node_modules/babylon/lib/index.js:3561:19) + at Parser.parseExprAtom (/node_modules/babylon/lib/index.js:6408:22) + at Parser.pp$3.parseExprSubscripts (/node_modules/babylon/lib/index.js:3337:19) + at Parser.pp$3.parseMaybeUnary (/node_modules/babylon/lib/index.js:3317:19) + at Parser.pp$3.parseExprOps (/node_modules/babylon/lib/index.js:3247:19) +" +`; diff --git a/tests/fetch/fetch.js b/tests/fetch/fetch.js new file mode 100644 index 000000000000..bd1f44a84333 --- /dev/null +++ b/tests/fetch/fetch.js @@ -0,0 +1,25 @@ +/* @flow */ + +// most of the details are tested in the separate file +// here I test the basic usage + +const myRequest = new Request('http://google.com'); + +const a: Promise = + fetch(myRequest) + .then(response => response.text()); + +const b: Promise = fetch(myRequest); // incorrect + +var myInit = { method: 'GET', + headers: { + 'Content-Type': 'image/jpeg' + }, + mode: 'cors', + cache: 'default' }; + +const c: Promise = + fetch('image.png') + .then(response => response.blob()); // correct + +const d: Promise = fetch('image.png'); // incorrect diff --git a/tests/fetch/headers.js b/tests/fetch/headers.js new file mode 100644 index 000000000000..9eab0d99635b --- /dev/null +++ b/tests/fetch/headers.js @@ -0,0 +1,28 @@ +/* @flow */ + +const a = new Headers("'Content-Type': 'image/jpeg'"); // not correct +const b = new Headers(['Content-Type', 'image/jpeg']); // not correct +const c = new Headers({'Content-Type', 'image/jpeg'}); // correct +const d = new Headers(c); // correct +const e: Headers = new Headers(); // correct +e.append('Content-Type', 'image/jpeg'); // correct +e.append('Content-Type'); // not correct +e.append({'Content-Type', 'image/jpeg'}); // not correct +e.set('Content-Type', 'image/jpeg'); // correct +e.set('Content-Type'); // not correct +e.set({'Content-Type', 'image/jpeg'}); // not correct + +const f: Headers = e.append('Content-Type', 'image/jpeg'); // not correct + +const g: string = e.get('Content-Type'); // correct +const h: number = e.get('Content-Type'); // not correct + +for (let v of e) { + const [i, j]: [string, string] = v; // correct +} + +for (let v of e.entries()) { + const [i, j]: [string, string] = v; // correct +} + +e.getAll('content-type').forEach((v: string) => {}); // correct diff --git a/tests/fetch/jsfmt.spec.js b/tests/fetch/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/fetch/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/fetch/request.js b/tests/fetch/request.js new file mode 100644 index 000000000000..1f8981e91f28 --- /dev/null +++ b/tests/fetch/request.js @@ -0,0 +1,51 @@ +/* @flow */ +const a: Request = new Request(); // incorrect +const b: Request = new Request('http://example.org'); // correct +const c: Request = new Request(b); // correct +const d: Request = new Request(c.clone()); // correct (doesn't make much sense though) +const e: Request = new Request(b, c); // incorrect + +const f: Request = new Request({}) // incorrect +const g: Request = new Request('http://example.org', {}) // correct + +const h: Request = new Request('http://example.org', { + method: 'GET', + headers: { + 'Content-Type': 'image/jpeg' + }, + mode: 'cors', + cache: 'default' +}) // correct + +const i: Request = new Request('http://example.org', { + method: 'POST', + headers: { + 'Content-Type': 'image/jpeg' + }, + body: new URLSearchParams("key=value"), + mode: 'cors', + cache: 'default' +}) // correct + +const j: Request = new Request('http://example.org', { + method: 'GET', + headers: 'Content-Type: image/jpeg', + mode: 'cors', + cache: 'default' +}) // incorrect - headers is string + +const k: Request = new Request('http://example.org', { + method: 'CONNECT', + headers: { + 'Content-Type': 'image/jpeg' + }, + mode: 'cors', + cache: 'default' +}) // incorrect - CONNECT is forbidden + +var l: boolean = h.bodyUsed; + +h.text().then((t: string) => t); // correct +h.text().then((t: Buffer) => t); // incorrect +h.arrayBuffer().then((ab: ArrayBuffer) => ab); // correct +h.arrayBuffer().then((ab: Buffer) => ab); // incorrect diff --git a/tests/fetch/response.js b/tests/fetch/response.js new file mode 100644 index 000000000000..f68b4d8b3493 --- /dev/null +++ b/tests/fetch/response.js @@ -0,0 +1,46 @@ +/* @flow */ +const a: Response = new Response(); // correct +const b: Response = new Response(new Blob()); // correct +const c: Response = new Response(new FormData()); // correct + +const d: Response = new Response(new FormData(), { + status: 404 +}); // correct + +const e: Response = new Response("responsebody", { + status: "404" +}); // incorrect + +const f: Response = new Response("responsebody", { + status: 404, + headers: "'Content-Type': 'image/jpeg'" +}); // incorrect + +const g: Response = new Response("responsebody", { + status: 404, + headers: { + 'Content-Type': 'image/jpeg' + } +}); // correct + +const h: Response = new Response("responsebody", { + status: 404, + headers: new Headers({ + 'Content-Type': 'image/jpeg' + }) +}); // correct, if verbose + +const i: Response = new Response({ + status: 404, + headers: new Headers({ + 'Content-Type': 'image/jpeg' + }) +}); // incorrect + +const ok: boolean = h.ok; +const status: number = h.status; + +h.text().then((t: string) => t); // correct +h.text().then((t: Buffer) => t); // incorrect +h.arrayBuffer().then((ab: ArrayBuffer) => ab); // correct +h.arrayBuffer().then((ab: Buffer) => ab); // incorrect diff --git a/tests/fetch/urlsearchparams.js b/tests/fetch/urlsearchparams.js new file mode 100644 index 000000000000..f40aae313f36 --- /dev/null +++ b/tests/fetch/urlsearchparams.js @@ -0,0 +1,28 @@ +/* @flow */ + +const a = new URLSearchParams("key1=value1"); // correct +const b = new URLSearchParams(['key1', 'value1']); // not correct +const c = new URLSearchParams({'key1', 'value1'}); // not correct +const d = new URLSearchParams(c); // correct +const e: URLSearchParams = new URLSearchParams(); // correct +e.append('key1', 'value1'); // correct +e.append('key1'); // not correct +e.append({'key1', 'value1'}); // not correct +e.set('key1', 'value1'); // correct +e.set('key1'); // not correct +e.set({'key1', 'value1'}); // not correct + +const f: URLSearchParams = e.append('key1', 'value1'); // not correct + +const g: string = e.get('key1'); // correct +const h: number = e.get('key1'); // not correct + +for (let v of e) { + const [i, j]: [string, string] = v; // correct +} + +for (let v of e.entries()) { + const [i, j]: [string, string] = v; // correct +} + +e.getAll('key1').forEach((v: string) => {}); // correct diff --git a/tests/find-module/__snapshots__/jsfmt.spec.js.snap b/tests/find-module/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..2855edc03840 --- /dev/null +++ b/tests/find-module/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,20 @@ +exports[`test req.js 1`] = ` +"module.exports = 0; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +module.exports = 0; + +" +`; + +exports[`test test.js 1`] = ` +"var x = require('./req'); +(x: string); + +import x from './req'; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var x = require("./req"); +(x: string); +import x from "./req"; + +" +`; diff --git a/tests/find-module/jsfmt.spec.js b/tests/find-module/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/find-module/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/find-module/req.js b/tests/find-module/req.js new file mode 100644 index 000000000000..d5b2e8bb3efb --- /dev/null +++ b/tests/find-module/req.js @@ -0,0 +1 @@ +module.exports = 0; diff --git a/tests/find-module/test.js b/tests/find-module/test.js new file mode 100644 index 000000000000..79de263f6960 --- /dev/null +++ b/tests/find-module/test.js @@ -0,0 +1,4 @@ +var x = require('./req'); +(x: string); + +import x from './req'; diff --git a/tests/find-refs/__snapshots__/jsfmt.spec.js.snap b/tests/find-refs/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..bd0b87986865 --- /dev/null +++ b/tests/find-refs/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,77 @@ +exports[`test locals.js 1`] = ` +"// @flow + +// Variable defs and uses +var foo = function() { }; +foo(); +foo = null; +foo(); + +// Nested functions +function bar() { + function bar() { + } + bar(); +} +bar(); + +// Classes +class C { } +new C; +class D extends C { } + +// Type aliases +type T = number; +type S = T; +((_: T) => {}); + +// Refinements +let nullable: ?string = \"\"; +if (nullable != null) { + console.log(nullable.length); + nullable = null; +} +(nullable: null); + +// Destructuring +let { x, y } = { x: 0, y: 0 }; +let { x: _x, y: _y } = { x, y }; +({ x: _x, y: _y }); + +// Not in scope +wut1; +wut1 = wut2; +wut2; + +// JSX +var React = require(\'react\'); +class Fancy extends React.Component { + props: { x: number }; +} +; + +// Imports +import { wut3 } from \'wutland\'; +import type { wut4 } from \'wutland\'; +(wut3: wut4); + +// Qualified types +(null: React.Component); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:5687 + throw jsxError || err; + ^ + +SyntaxError: JSX value should be either an expression or a quoted JSX text (50:9) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp$8.jsxParseAttributeValue (/node_modules/babylon/lib/index.js:6255:12) + at Parser.pp$8.jsxParseAttribute (/node_modules/babylon/lib/index.js:6306:42) + at Parser.pp$8.jsxParseOpeningElementAt (/node_modules/babylon/lib/index.js:6317:31) + at Parser.pp$8.jsxParseElementAt (/node_modules/babylon/lib/index.js:6339:29) + at Parser.pp$8.jsxParseElement (/node_modules/babylon/lib/index.js:6394:15) + at Parser.parseExprAtom (/node_modules/babylon/lib/index.js:6406:21) + at Parser.pp$3.parseExprSubscripts (/node_modules/babylon/lib/index.js:3337:19) + at Parser.pp$3.parseMaybeUnary (/node_modules/babylon/lib/index.js:3317:19) + at Parser.pp$3.parseExprOps (/node_modules/babylon/lib/index.js:3247:19) +" +`; diff --git a/tests/find-refs/jsfmt.spec.js b/tests/find-refs/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/find-refs/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/find-refs/locals.js b/tests/find-refs/locals.js new file mode 100644 index 000000000000..5651166c1f62 --- /dev/null +++ b/tests/find-refs/locals.js @@ -0,0 +1,58 @@ +// @flow + +// Variable defs and uses +var foo = function() { }; +foo(); +foo = null; +foo(); + +// Nested functions +function bar() { + function bar() { + } + bar(); +} +bar(); + +// Classes +class C { } +new C; +class D extends C { } + +// Type aliases +type T = number; +type S = T; +((_: T) => {}); + +// Refinements +let nullable: ?string = ""; +if (nullable != null) { + console.log(nullable.length); + nullable = null; +} +(nullable: null); + +// Destructuring +let { x, y } = { x: 0, y: 0 }; +let { x: _x, y: _y } = { x, y }; +({ x: _x, y: _y }); + +// Not in scope +wut1; +wut1 = wut2; +wut2; + +// JSX +var React = require('react'); +class Fancy extends React.Component { + props: { x: number }; +} +; + +// Imports +import { wut3 } from 'wutland'; +import type { wut4 } from 'wutland'; +(wut3: wut4); + +// Qualified types +(null: React.Component); diff --git a/tests/fixpoint/Fun.js b/tests/fixpoint/Fun.js new file mode 100644 index 000000000000..944a780d1260 --- /dev/null +++ b/tests/fixpoint/Fun.js @@ -0,0 +1,30 @@ + +/* @providesModule Fun */ + +function eq(x:number,y:number) { return true }; +function sub(x:number,y:number) { return 0; } +function mul(x:number,y:number) { return 0; } + +function fix(fold) { + var delta = function(delta) { + return fold( + function(x) { var eta = delta(delta); return eta(x); } + ); + }; + return delta(delta); +} + +function mk_factorial() { + return fix(function(factorial) { + return function(n) { + if (eq (n, 1)) { return 1; } + return mul (factorial (sub (n, 1)), n); + }; + }); +} + + +var factorial = mk_factorial(); +factorial("..."); + +module.exports = {fn: fix}; diff --git a/tests/fixpoint/Ycombinator.js b/tests/fixpoint/Ycombinator.js new file mode 100644 index 000000000000..d433603e5ca0 --- /dev/null +++ b/tests/fixpoint/Ycombinator.js @@ -0,0 +1,21 @@ + +/* @providesModule Ycombinator */ + +function Y(f) { + function g(x) { return f(x(x)); } + g(g); +} + +function func1(f) { + function fix_f(x:number):number { return f(x); } + return fix_f; +} +function func2(f) { + function fix_f(x:string):string { return f(x); } + return fix_f; +} + +Y(func1); +Y(func2); + +module.exports = Y; diff --git a/tests/fixpoint/__snapshots__/jsfmt.spec.js.snap b/tests/fixpoint/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..b397dc2a755f --- /dev/null +++ b/tests/fixpoint/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,120 @@ +exports[`test Fun.js 1`] = ` +" +/* @providesModule Fun */ + +function eq(x:number,y:number) { return true }; +function sub(x:number,y:number) { return 0; } +function mul(x:number,y:number) { return 0; } + +function fix(fold) { + var delta = function(delta) { + return fold( + function(x) { var eta = delta(delta); return eta(x); } + ); + }; + return delta(delta); +} + +function mk_factorial() { + return fix(function(factorial) { + return function(n) { + if (eq (n, 1)) { return 1; } + return mul (factorial (sub (n, 1)), n); + }; + }); +} + + +var factorial = mk_factorial(); +factorial("..."); + +module.exports = {fn: fix}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @providesModule Fun */ +function eq(x: number, y: number) { + return true; +} +function sub(x: number, y: number) { + return 0; +} +function mul(x: number, y: number) { + return 0; +} +function fix(fold) { + var delta = function(delta) { + return fold( + function(x) { + var eta = delta(delta); + return eta(x); + } + ); + }; + return delta(delta); +} +function mk_factorial() { + return fix( + function(factorial) { + return function(n) { + if (eq(n, 1)) { + return 1; + } + return mul(factorial(sub(n, 1)), n); + }; + } + ); +} +var factorial = mk_factorial(); +factorial("..."); +module.exports = { fn: fix }; + +" +`; + +exports[`test Ycombinator.js 1`] = ` +" +/* @providesModule Ycombinator */ + +function Y(f) { + function g(x) { return f(x(x)); } + g(g); +} + +function func1(f) { + function fix_f(x:number):number { return f(x); } + return fix_f; +} +function func2(f) { + function fix_f(x:string):string { return f(x); } + return fix_f; +} + +Y(func1); +Y(func2); + +module.exports = Y; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @providesModule Ycombinator */ +function Y(f) { + function g(x) { + return f(x(x)); + } + g(g); +} +function func1(f) { + function fix_f(x: number): number { + return f(x); + } + return fix_f; +} +function func2(f) { + function fix_f(x: string): string { + return f(x); + } + return fix_f; +} +Y(func1); +Y(func2); +module.exports = Y; + +" +`; diff --git a/tests/fixpoint/jsfmt.spec.js b/tests/fixpoint/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/fixpoint/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow_ast.template_strings/__snapshots__/jsfmt.spec.js.snap b/tests/flow_ast.template_strings/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..f3ef97d9d279 --- /dev/null +++ b/tests/flow_ast.template_strings/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,7 @@ +exports[`test foo.js 1`] = ` +"a\`foo \${bar} baz\` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +a\`foo \${bar} baz\`; + +" +`; diff --git a/tests/flow_ast.template_strings/foo.js b/tests/flow_ast.template_strings/foo.js new file mode 100644 index 000000000000..201691617f58 --- /dev/null +++ b/tests/flow_ast.template_strings/foo.js @@ -0,0 +1 @@ +a`foo ${bar} baz` diff --git a/tests/flow_ast.template_strings/jsfmt.spec.js b/tests/flow_ast.template_strings/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/flow_ast.template_strings/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/for/__snapshots__/jsfmt.spec.js.snap b/tests/for/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..84356a0db27e --- /dev/null +++ b/tests/for/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,272 @@ +exports[`test abnormal.js 1`] = ` +"/* @flow */ + +function foo(x: boolean) { + var max = 10; + for (var ii = 0; ii < max; ii++) { + if (x) { + continue; + } + return; + } + console.log('this is still reachable'); +} + +function bar(x: boolean) { + var max = 0; + for (var ii = 0; ii < max; ii++) { + return; + } + console.log('this is still reachable'); +} + +function baz(x: boolean) { + var max = 0; + for (var ii = 0; ii < max; ii++) { + continue; + } + console.log('this is still reachable'); +} + +function bliffl(x: boolean) { + var max = 10; + loop1: for (var ii = 0; ii < max; ii++) { + loop2: for (var jj = 0; jj < max; jj++) { + break loop1; + } + console.log('this is still reachable'); + } + console.log('this is still reachable'); +} + +function corge(x: boolean) { + var max = 0; + for (var ii = 0; ii < max; ii++) { + break; + } + console.log('this is still reachable'); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +function foo(x: boolean) { + var max = 10; + for (var ii = 0; ii < max; ii++) { + if (x) { + continue; + } + return; + } + console.log("this is still reachable"); +} +function bar(x: boolean) { + var max = 0; + for (var ii = 0; ii < max; ii++) { + return; + } + console.log("this is still reachable"); +} +function baz(x: boolean) { + var max = 0; + for (var ii = 0; ii < max; ii++) { + continue; + } + console.log("this is still reachable"); +} +function bliffl(x: boolean) { + var max = 10; + loop1: +for (var ii = 0; ii < max; ii++) { + loop2: +for (var jj = 0; jj < max; jj++) { + break loop1; + } + console.log("this is still reachable"); + } + console.log("this is still reachable"); +} +function corge(x: boolean) { + var max = 0; + for (var ii = 0; ii < max; ii++) { + break; + } + console.log("this is still reachable"); +} + +" +`; + +exports[`test abnormal_for_in.js 1`] = ` +"function foo(x: boolean) { + var obj = { a: 1, b: 2}; + for (var prop in obj) { + if (x) { + continue; + } + return; + } + console.log('this is still reachable'); +} + +function bar(x: boolean) { + for (var prop in {}) { + return; + } + console.log('this is still reachable'); +} + +function baz(x: boolean) { + for (var prop in {}) { + continue; + } + console.log('this is still reachable'); +} + +function bliffl(x: boolean) { + var obj = { a: 1, b: 2}; + loop1: for (var prop1 in obj) { + loop2: for (var prop2 in obj) { + break loop1; + } + console.log('this is still reachable'); + } + console.log('this is still reachable'); +} + +function corge(x: boolean) { + for (var prop in {}) { + break; + } + console.log('this is still reachable'); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +function foo(x: boolean) { + var obj = { a: 1, b: 2 }; + for (var prop in obj) { + if (x) { + continue; + } + return; + } + console.log("this is still reachable"); +} +function bar(x: boolean) { + for (var prop in {}) { + return; + } + console.log("this is still reachable"); +} +function baz(x: boolean) { + for (var prop in {}) { + continue; + } + console.log("this is still reachable"); +} +function bliffl(x: boolean) { + var obj = { a: 1, b: 2 }; + loop1: +for (var prop1 in obj) { + loop2: +for (var prop2 in obj) { + break loop1; + } + console.log("this is still reachable"); + } + console.log("this is still reachable"); +} +function corge(x: boolean) { + for (var prop in {}) { + break; + } + console.log("this is still reachable"); +} + +" +`; + +exports[`test abnormal_for_of.js 1`] = ` +"function foo(x: boolean) { + var arr = [1, 2, 3]; + for (var elem of arr) { + if (x) { + continue; + } + return; + } + console.log('this is still reachable'); +} + +function bar(x: boolean) { + for (var elem of []) { + return; + } + console.log('this is still reachable'); +} + +function baz(x: boolean) { + for (var elem of []) { + continue; + } + console.log('this is still reachable'); +} + +function bliffl(x: boolean) { + var arr = [1, 2, 3]; + loop1: for (var elem of arr) { + loop2: for (var elem of arr) { + break loop1; + } + console.log('this is still reachable'); + } + console.log('this is still reachable'); +} + +function corge(x: boolean) { + for (var elem of []) { + break; + } + console.log('this is still reachable'); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +function foo(x: boolean) { + var arr = [ 1, 2, 3 ]; + for (var elem of arr) { + if (x) { + continue; + } + return; + } + console.log("this is still reachable"); +} +function bar(x: boolean) { + for (var elem of [ ]) { + return; + } + console.log("this is still reachable"); +} +function baz(x: boolean) { + for (var elem of [ ]) { + continue; + } + console.log("this is still reachable"); +} +function bliffl(x: boolean) { + var arr = [ 1, 2, 3 ]; + loop1: +for (var elem of arr) { + loop2: +for (var elem of arr) { + break loop1; + } + console.log("this is still reachable"); + } + console.log("this is still reachable"); +} +function corge(x: boolean) { + for (var elem of [ ]) { + break; + } + console.log("this is still reachable"); +} + +" +`; diff --git a/tests/for/abnormal.js b/tests/for/abnormal.js new file mode 100644 index 000000000000..cc588841c2d6 --- /dev/null +++ b/tests/for/abnormal.js @@ -0,0 +1,47 @@ +/* @flow */ + +function foo(x: boolean) { + var max = 10; + for (var ii = 0; ii < max; ii++) { + if (x) { + continue; + } + return; + } + console.log('this is still reachable'); +} + +function bar(x: boolean) { + var max = 0; + for (var ii = 0; ii < max; ii++) { + return; + } + console.log('this is still reachable'); +} + +function baz(x: boolean) { + var max = 0; + for (var ii = 0; ii < max; ii++) { + continue; + } + console.log('this is still reachable'); +} + +function bliffl(x: boolean) { + var max = 10; + loop1: for (var ii = 0; ii < max; ii++) { + loop2: for (var jj = 0; jj < max; jj++) { + break loop1; + } + console.log('this is still reachable'); + } + console.log('this is still reachable'); +} + +function corge(x: boolean) { + var max = 0; + for (var ii = 0; ii < max; ii++) { + break; + } + console.log('this is still reachable'); +} diff --git a/tests/for/abnormal_for_in.js b/tests/for/abnormal_for_in.js new file mode 100644 index 000000000000..add7d98c52b2 --- /dev/null +++ b/tests/for/abnormal_for_in.js @@ -0,0 +1,42 @@ +function foo(x: boolean) { + var obj = { a: 1, b: 2}; + for (var prop in obj) { + if (x) { + continue; + } + return; + } + console.log('this is still reachable'); +} + +function bar(x: boolean) { + for (var prop in {}) { + return; + } + console.log('this is still reachable'); +} + +function baz(x: boolean) { + for (var prop in {}) { + continue; + } + console.log('this is still reachable'); +} + +function bliffl(x: boolean) { + var obj = { a: 1, b: 2}; + loop1: for (var prop1 in obj) { + loop2: for (var prop2 in obj) { + break loop1; + } + console.log('this is still reachable'); + } + console.log('this is still reachable'); +} + +function corge(x: boolean) { + for (var prop in {}) { + break; + } + console.log('this is still reachable'); +} diff --git a/tests/for/abnormal_for_of.js b/tests/for/abnormal_for_of.js new file mode 100644 index 000000000000..f6724018ed55 --- /dev/null +++ b/tests/for/abnormal_for_of.js @@ -0,0 +1,42 @@ +function foo(x: boolean) { + var arr = [1, 2, 3]; + for (var elem of arr) { + if (x) { + continue; + } + return; + } + console.log('this is still reachable'); +} + +function bar(x: boolean) { + for (var elem of []) { + return; + } + console.log('this is still reachable'); +} + +function baz(x: boolean) { + for (var elem of []) { + continue; + } + console.log('this is still reachable'); +} + +function bliffl(x: boolean) { + var arr = [1, 2, 3]; + loop1: for (var elem of arr) { + loop2: for (var elem of arr) { + break loop1; + } + console.log('this is still reachable'); + } + console.log('this is still reachable'); +} + +function corge(x: boolean) { + for (var elem of []) { + break; + } + console.log('this is still reachable'); +} diff --git a/tests/for/jsfmt.spec.js b/tests/for/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/for/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/forof/__snapshots__/jsfmt.spec.js.snap b/tests/forof/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..c772d3d905fc --- /dev/null +++ b/tests/forof/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,72 @@ +exports[`test forof.js 1`] = ` +"/** + * @flow + */ + +function testArray(arr: Array): void { + for (var x of arr) { + (x: string); // Error - number ~> string + } +} + +function testIterable1(iterable: Iterable): void { + for (var x of iterable) { + (x: string); // Error - number ~> string + } +} + +function testIterable2(iterable: Iterable<*>): void { + for (var x of iterable) { + (x: string); + } +} + +function testString(str: string): void { + for (var x of str) { + (x: number); // Error - string ~> number + } +} + +function testMap1(map: Map): void { + for (var elem of map) { + (elem: [string, number]); + (elem: number); // Error - tuple ~> number + } +} + +function testMap2(map: Map<*, *>): void { + for (var elem of map) { + (elem: [number, string]); // Any tuple is fine + (elem: number); // Error - tuple ~> number + } +} + +function testSet1(set: Set): void { + for (var x of set) { + (x: number); // Error - string ~> number + } +} + +function testSet2(set: Set<*>): void { + for (var x of set) { + (x: number); // Anything goes + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/forof/forof.js b/tests/forof/forof.js new file mode 100644 index 000000000000..ffcece1b14a0 --- /dev/null +++ b/tests/forof/forof.js @@ -0,0 +1,53 @@ +/** + * @flow + */ + +function testArray(arr: Array): void { + for (var x of arr) { + (x: string); // Error - number ~> string + } +} + +function testIterable1(iterable: Iterable): void { + for (var x of iterable) { + (x: string); // Error - number ~> string + } +} + +function testIterable2(iterable: Iterable<*>): void { + for (var x of iterable) { + (x: string); + } +} + +function testString(str: string): void { + for (var x of str) { + (x: number); // Error - string ~> number + } +} + +function testMap1(map: Map): void { + for (var elem of map) { + (elem: [string, number]); + (elem: number); // Error - tuple ~> number + } +} + +function testMap2(map: Map<*, *>): void { + for (var elem of map) { + (elem: [number, string]); // Any tuple is fine + (elem: number); // Error - tuple ~> number + } +} + +function testSet1(set: Set): void { + for (var x of set) { + (x: number); // Error - string ~> number + } +} + +function testSet2(set: Set<*>): void { + for (var x of set) { + (x: number); // Anything goes + } +} diff --git a/tests/forof/jsfmt.spec.js b/tests/forof/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/forof/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/function/__snapshots__/jsfmt.spec.js.snap b/tests/function/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..668c46396d01 --- /dev/null +++ b/tests/function/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,287 @@ +exports[`test apply.js 1`] = ` +"function test(a: string, b: number): number { + return this.length; // expect []/\"\" this +} + +// tuples flow correctly into params +test.apply(\"\", [\"\", 0]); + +// wrong this is an error +test.apply(0, [\"\", 0]); // error: lookup \`length\` on Number + +// not enough arguments is an error (via incompatible RestT) +test.apply(\"\", [\"\"]); // error: string ~> number + +// mistyped arguments is an error +test.apply(\"\", [\"\", \"\"]); // error: string ~> number (2nd arg) +test.apply(\"\", [0, 0]); // error: number ~> string (1st arg) + +// resolve args array from tvar +function f(args) { test.apply(\"\", args) } +f([\"\", 0]); // OK +f([\"\", \"\"]); // error: string ~> number (2nd arg) +f([0, 0]); // error: number ~> string (1st arg) + +// expect array +test.apply(\"\", \"not array\"); // error: expect array of args + +// expect 4 errors: +// - lookup length on Number (because 0 is used as \`this\`) +// - 123 is not a string +// - \'foo\' is not a number +// - return type (number) is not void +(test.call.apply(test, [0, 123, \'foo\']): void); + +// expect 2 errors: +// - lookup length on number (0 is used as \`this\`) +// - 123 is not a string +(test.bind.apply(test, [0, 123]): (b: number) => number); + +// args are optional +function test2(): number { return 0; } +(test2.apply(): number); +(test2.apply(\"\"): number); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test bind.js 1`] = ` +"// @flow + +let tests = [ + function(x: (a: string, b: string) => void) { + let y = x.bind(x, \'foo\'); + y(\'bar\'); // ok + y(123); // error, number !~> string + }, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test call.js 1`] = ` +"// @flow + +function test(a: string, b: number): number { + return this.length; // expect []/\"\" this +} + +// args flow correctly into params +test.call(\"\", \"\", 0); + +// wrong this is an error +test.call(0, \"\", 0); // error: lookup \`length\` on Number + +// not enough arguments is an error (via incompatible RestT) +test.call(\"\", \"\"); // error: string ~> number + +// mistyped arguments is an error +test.call(\"\", \"\", \"\"); // error: string ~> number (2nd arg) +test.call(\"\", 0, 0); // error: number ~> string (1st arg) + +// resolve args array from tvar +function f(args) { test.call(\"\", args[0], args[1]) } +f([\"\", 0]); // OK +f([\"\", \"\"]); // error: string ~> number (2nd arg) +f([0, 0]); // error: number ~> string (1st arg) + +// expect 3 errors: +// - lookup length on Number (0 used as \`this\`) +// - number !~> string (param a) +// - string !~> number (param b) +(test.apply.call(test, 0, [0, \'foo\']): number); + +// args are optional +function test2(): number { return 0; } +(test2.call(): number); +(test2.call(\"\"): number); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +function test(a: string, b: number): number { + return this.length;// expect []/\"\" this +} +// args flow correctly into params +test.call(\"\", \"\", 0); +// wrong this is an error +test.call(0, \"\", 0);// error: lookup \`length\` on Number +// not enough arguments is an error (via incompatible RestT) +test.call(\"\", \"\");// error: string ~> number +// mistyped arguments is an error +test.call(\"\", \"\", \"\");// error: string ~> number (2nd arg) +test.call(\"\", 0, 0);// error: number ~> string (1st arg) +// resolve args array from tvar +function f(args) { + test.call(\"\", args[0], args[1]); +} +f([ \"\", 0 ]);// OK +f([ \"\", \"\" ]);// error: string ~> number (2nd arg) +f([ 0, 0 ]);// error: number ~> string (1st arg) +// expect 3 errors: +// - lookup length on Number (0 used as \`this\`) +// - number !~> string (param a) +// - string !~> number (param b) +(test.apply.call(test, 0, [ 0, \"foo\" ]): number); +// args are optional +function test2(): number { + return 0; +} +(test2.call(): number); +(test2.call(\"\"): number); + +" +`; + +exports[`test function.js 1`] = ` +"/** + * @flow + */ + +// Previously we represented Function as (...rest: any) => any +// This means the following wouldn\'t pass, because that arrow function +// can only be called with 3 arguments. +var a: Function = (a, b, c) => 123; + +var b: Function = function(a: number, b: number): number { return a + b; }; + +class C {} + +var c: Function = C; + +function good(x: Function, MyThing: Function): number { + var o: Object = x; // Function is an Object + x.foo = 123; + x[\'foo\'] = 456; + x(); + ; + var {...something} = x; + Object.assign(x, {hi: \'there\'}); + Object.keys(x); + return x.bar + x[\'bar\'] + x.lala(); +} + +function bad(x: Function, y: Object): void { + var a: number = x; // Error + var b: string = x; // Error + var c: Function = y; // Object is not a Function +} + +let tests = [ + function(y: () => void, z: Function) { + function x() {} + (x.length: void); // error, it\'s a number + (y.length: void); // error, it\'s a number + (z.length: void); // error, it\'s a number + + (x.name: void); // error, it\'s a string + (y.name: void); // error, it\'s a string + (z.name: void); // error, it\'s a string + }, + + function(y: () => void, z: Function) { + function x() {} + x.length = \'foo\'; // error, it\'s a number + y.length = \'foo\'; // error, it\'s a number + z.length = \'foo\'; // error, it\'s a number + + x.name = 123; // error, it\'s a string + y.name = 123; // error, it\'s a string + z.name = 123; // error, it\'s a string + + // Non-(Function.prototype) properties on a \`Function\` type should be \`any\` + (z.foo: number); + (z.foo: string); + }, +]; + +// \`Function\` types can be bound (resulting in a \`Function\` type) +var d: Function = () => 1; +var e = (d.bind(1): Function)(); +(e: number); +(e: string); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test rest.js 1`] = ` +"/* regression tests */ + +function rest_array(...xs: Array): T { + return xs[0]; +} + +// Warn, singleton tuple types don\'t represent rest params +function rest_tuple(...xs: [T]): T { + return xs[0]; +} + +function rest_any(...xs: any): any { + return xs[0]; +} + +// Warn, arbitrary subtypes of an array type don\'t represent rest params +function rest_t>(...xs: T): U { + return xs[0]; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:340:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/function/apply.js b/tests/function/apply.js new file mode 100644 index 000000000000..527839d334c1 --- /dev/null +++ b/tests/function/apply.js @@ -0,0 +1,42 @@ +function test(a: string, b: number): number { + return this.length; // expect []/"" this +} + +// tuples flow correctly into params +test.apply("", ["", 0]); + +// wrong this is an error +test.apply(0, ["", 0]); // error: lookup `length` on Number + +// not enough arguments is an error (via incompatible RestT) +test.apply("", [""]); // error: string ~> number + +// mistyped arguments is an error +test.apply("", ["", ""]); // error: string ~> number (2nd arg) +test.apply("", [0, 0]); // error: number ~> string (1st arg) + +// resolve args array from tvar +function f(args) { test.apply("", args) } +f(["", 0]); // OK +f(["", ""]); // error: string ~> number (2nd arg) +f([0, 0]); // error: number ~> string (1st arg) + +// expect array +test.apply("", "not array"); // error: expect array of args + +// expect 4 errors: +// - lookup length on Number (because 0 is used as `this`) +// - 123 is not a string +// - 'foo' is not a number +// - return type (number) is not void +(test.call.apply(test, [0, 123, 'foo']): void); + +// expect 2 errors: +// - lookup length on number (0 is used as `this`) +// - 123 is not a string +(test.bind.apply(test, [0, 123]): (b: number) => number); + +// args are optional +function test2(): number { return 0; } +(test2.apply(): number); +(test2.apply(""): number); diff --git a/tests/function/bind.js b/tests/function/bind.js new file mode 100644 index 000000000000..1e29e8ee60eb --- /dev/null +++ b/tests/function/bind.js @@ -0,0 +1,9 @@ +// @flow + +let tests = [ + function(x: (a: string, b: string) => void) { + let y = x.bind(x, 'foo'); + y('bar'); // ok + y(123); // error, number !~> string + }, +]; diff --git a/tests/function/call.js b/tests/function/call.js new file mode 100644 index 000000000000..ebb6acd2e8a3 --- /dev/null +++ b/tests/function/call.js @@ -0,0 +1,35 @@ +// @flow + +function test(a: string, b: number): number { + return this.length; // expect []/"" this +} + +// args flow correctly into params +test.call("", "", 0); + +// wrong this is an error +test.call(0, "", 0); // error: lookup `length` on Number + +// not enough arguments is an error (via incompatible RestT) +test.call("", ""); // error: string ~> number + +// mistyped arguments is an error +test.call("", "", ""); // error: string ~> number (2nd arg) +test.call("", 0, 0); // error: number ~> string (1st arg) + +// resolve args array from tvar +function f(args) { test.call("", args[0], args[1]) } +f(["", 0]); // OK +f(["", ""]); // error: string ~> number (2nd arg) +f([0, 0]); // error: number ~> string (1st arg) + +// expect 3 errors: +// - lookup length on Number (0 used as `this`) +// - number !~> string (param a) +// - string !~> number (param b) +(test.apply.call(test, 0, [0, 'foo']): number); + +// args are optional +function test2(): number { return 0; } +(test2.call(): number); +(test2.call(""): number); diff --git a/tests/function/function.js b/tests/function/function.js new file mode 100644 index 000000000000..496b3031d645 --- /dev/null +++ b/tests/function/function.js @@ -0,0 +1,66 @@ +/** + * @flow + */ + +// Previously we represented Function as (...rest: any) => any +// This means the following wouldn't pass, because that arrow function +// can only be called with 3 arguments. +var a: Function = (a, b, c) => 123; + +var b: Function = function(a: number, b: number): number { return a + b; }; + +class C {} + +var c: Function = C; + +function good(x: Function, MyThing: Function): number { + var o: Object = x; // Function is an Object + x.foo = 123; + x['foo'] = 456; + x(); + ; + var {...something} = x; + Object.assign(x, {hi: 'there'}); + Object.keys(x); + return x.bar + x['bar'] + x.lala(); +} + +function bad(x: Function, y: Object): void { + var a: number = x; // Error + var b: string = x; // Error + var c: Function = y; // Object is not a Function +} + +let tests = [ + function(y: () => void, z: Function) { + function x() {} + (x.length: void); // error, it's a number + (y.length: void); // error, it's a number + (z.length: void); // error, it's a number + + (x.name: void); // error, it's a string + (y.name: void); // error, it's a string + (z.name: void); // error, it's a string + }, + + function(y: () => void, z: Function) { + function x() {} + x.length = 'foo'; // error, it's a number + y.length = 'foo'; // error, it's a number + z.length = 'foo'; // error, it's a number + + x.name = 123; // error, it's a string + y.name = 123; // error, it's a string + z.name = 123; // error, it's a string + + // Non-(Function.prototype) properties on a `Function` type should be `any` + (z.foo: number); + (z.foo: string); + }, +]; + +// `Function` types can be bound (resulting in a `Function` type) +var d: Function = () => 1; +var e = (d.bind(1): Function)(); +(e: number); +(e: string); diff --git a/tests/function/jsfmt.spec.js b/tests/function/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/function/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/function/rest.js b/tests/function/rest.js new file mode 100644 index 000000000000..78f52926f951 --- /dev/null +++ b/tests/function/rest.js @@ -0,0 +1,19 @@ +/* regression tests */ + +function rest_array(...xs: Array): T { + return xs[0]; +} + +// Warn, singleton tuple types don't represent rest params +function rest_tuple(...xs: [T]): T { + return xs[0]; +} + +function rest_any(...xs: any): any { + return xs[0]; +} + +// Warn, arbitrary subtypes of an array type don't represent rest params +function rest_t>(...xs: T): U { + return xs[0]; +} diff --git a/tests/funrec/__snapshots__/jsfmt.spec.js.snap b/tests/funrec/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..945fe504c59b --- /dev/null +++ b/tests/funrec/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,19 @@ +exports[`test funrec.js 1`] = ` +"function bar(x) { return x; } +function foo() { + return function bound() { + return bar(bound); + }; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +function bar(x) { + return x; +} +function foo() { + return function bound() { + return bar(bound); + }; +} + +" +`; diff --git a/tests/funrec/funrec.js b/tests/funrec/funrec.js new file mode 100644 index 000000000000..4f597e22d574 --- /dev/null +++ b/tests/funrec/funrec.js @@ -0,0 +1,6 @@ +function bar(x) { return x; } +function foo() { + return function bound() { + return bar(bound); + }; +} diff --git a/tests/funrec/jsfmt.spec.js b/tests/funrec/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/funrec/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/generators/__snapshots__/jsfmt.spec.js.snap b/tests/generators/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..67db9796bc48 --- /dev/null +++ b/tests/generators/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,454 @@ +exports[`test class.js 1`] = ` +"class GeneratorExamples { + *stmt_yield(): Generator { + yield 0; // ok + yield \"\"; // error: string ~> number + } + + *stmt_next(): Generator { + var a = yield; + if (a) { + (a : number); // ok + } + + var b = yield; + if (b) { + (b : string); // error: number ~> string + } + } + + *stmt_return_ok(): Generator { + return 0; // ok + } + + *stmt_return_err(): Generator { + return \"\"; // error: string ~> number + } + + *infer_stmt() { + var x: boolean = yield 0; // error: number ~> boolean + return \"\"; + } + + *widen_next() { + var x = yield 0; + if (typeof x === \"number\") { + } else if (typeof x === \"boolean\") { + } else { + (x : string) // ok, sherlock + } + } + + *widen_yield() { + yield 0; + yield \"\"; + yield true; + } + + *delegate_next_generator() { + function *inner() { + var x: number = yield; // error: string ~> number + } + yield *inner(); + } + + *delegate_yield_generator() { + function *inner() { + yield \"\"; + } + + yield *inner(); + } + + *delegate_return_generator() { + function *inner() { + return \"\"; + } + + var x: number = yield *inner(); // error: string ~> number + } + + // only generators can make use of a value passed to next + *delegate_next_iterable(xs: Array) { + yield *xs; + } + + *delegate_yield_iterable(xs: Array) { + yield *xs; + } + + *delegate_return_iterable(xs: Array) { + var x: void = yield *xs // ok: Iterator has no yield value + } + + *generic_yield(y: Y): Generator { + yield y; + } + + *generic_return(r: R): Generator { + return r; + } + + *generic_next(): Generator { + return yield undefined; + } +} + +var examples = new GeneratorExamples(); + +for (var x of examples.infer_stmt()) { (x : string) } // error: number ~> string + +var infer_stmt_next = examples.infer_stmt().next(0).value; // error: number ~> boolean +if (typeof infer_stmt_next === \"undefined\") { +} else if (typeof infer_stmt_next === \"number\") { +} else { + (infer_stmt_next : boolean) // error: string ~> boolean +} + +examples.widen_next().next(0) +examples.widen_next().next(\"\") +examples.widen_next().next(true) + +for (var x of examples.widen_yield()) { + if (typeof x === \"number\") { + } else if (typeof x === \"boolean\") { + } else { + (x : string) // ok, sherlock + } +} + +examples.delegate_next_generator().next(\"\"); + +for (var x of examples.delegate_yield_generator()) { + (x : number) // error: string ~> number +} + +examples.delegate_next_iterable([]).next(\"\"); // error: Iterator has no next value + +for (var x of examples.delegate_yield_iterable([])) { + (x : string) // error: number ~> string +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test class_failure.js 1`] = ` +"// generalization of failure in class.js + +class GeneratorExamples { + *infer_stmt() { + var x: boolean = yield 0; // error: number ~> boolean + return \"\"; + } +} + +var examples = new GeneratorExamples(); + +for (var x of examples.infer_stmt()) { (x : string) } // error: number ~> string + +var infer_stmt_next = examples.infer_stmt().next(0).value; // error: number ~> boolean + +if (typeof infer_stmt_next === \"undefined\") { +} else if (typeof infer_stmt_next === \"number\") { +} else { + (infer_stmt_next : boolean) // error: string ~> boolean +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1172:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test generators.js 1`] = ` +"function *stmt_yield(): Generator { + yield 0; // ok + yield \"\"; // error: string ~> number +} + +function *stmt_next(): Generator { + var a = yield; + if (a) { + (a : number); // ok + } + + var b = yield; + if (b) { + (b : string); // error: number ~> string + } +} + +function *stmt_return_ok(): Generator { + return 0; // ok +} + +function *stmt_return_err(): Generator { + return \"\"; // error: string ~> number +} + +function *infer_stmt() { + var x: boolean = yield 0; + return \"\"; +} +for (var x of infer_stmt()) { (x : string) } // error: number ~> string +var infer_stmt_next = infer_stmt().next(0).value; // error: number ~> boolean +if (typeof infer_stmt_next === \"undefined\") { +} else if (typeof infer_stmt_next === \"number\") { +} else { + (infer_stmt_next : boolean) // error: string ~> boolean +} + +function *widen_next() { + var x = yield 0; + if (typeof x === \"number\") { + } else if (typeof x === \"boolean\") { + } else { + (x : string) // ok, sherlock + } +} +widen_next().next(0) +widen_next().next(\"\") +widen_next().next(true) + +function *widen_yield() { + yield 0; + yield \"\"; + yield true; +} +for (var x of widen_yield()) { + if (typeof x === \"number\") { + } else if (typeof x === \"boolean\") { + } else { + (x : string) // ok, sherlock + } +} + +function *delegate_next_generator() { + function *inner() { + var x: number = yield; // error: string ~> number + } + yield *inner(); +} +delegate_next_generator().next(\"\"); + +function *delegate_yield_generator() { + function *inner() { + yield \"\"; + } + + yield *inner(); +} +for (var x of delegate_yield_generator()) { + (x : number) // error: string ~> number +} + +function *delegate_return_generator() { + function *inner() { + return \"\"; + } + + var x: number = yield *inner(); // error: string ~> number +} + +// only generators can make use of a value passed to next +function *delegate_next_iterable(xs: Array) { + yield *xs; +} +delegate_next_iterable([]).next(\"\"); // error: Iterator has no next value + +function *delegate_yield_iterable(xs: Array) { + yield *xs; +} +for (var x of delegate_yield_iterable([])) { + (x : string) // error: number ~> string +} + +function *delegate_return_iterable(xs: Array) { + var x: void = yield *xs // ok: Iterator has no yield value +} + +function *generic_yield(y: Y): Generator { + yield y; +} + +function *generic_return(r: R): Generator { + return r; +} + +function *generic_next(): Generator { + return yield undefined; +} + +function *multiple_return(b) { + if (b) { + return 0; + } else { + return \"foo\"; + } +} +let multiple_return_result = multiple_return().next(); +if (multiple_return_result.done) { + (multiple_return_result.value: void); // error: number|string ~> void +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test return.js 1`] = ` +"function test1(gen: Generator) { + // You can pass whatever you like to return, it doesn\'t need to be related to + // the Generator\'s return type + var ret = gen.return(0); + (ret.value: void); // error: string | number ~> void +} + +// However, a generator can \"refuse\" the return by catching an exception and +// yielding or returning internally. +function *refuse_return() { + try { + yield 1; + } finally { + return 0; + } +} +var refuse_return_gen = refuse_return(); +var refuse_return_result = refuse_return_gen.return(\"string\"); +if (refuse_return_result.done) { + (refuse_return_result.value: string); // error: number | void ~> string +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test throw.js 1`] = ` +"function *catch_return() { + try { + yield 0; + } catch (e) { + return e; + } +} + +var catch_return_value = catch_return().throw(\"\").value; +if (catch_return_value !== undefined) { + (catch_return_value : string); // error: number ~> string +} + +function *yield_return() { + try { + yield 0; + return; + } catch (e) { + yield e; + } +} +var yield_return_value = yield_return().throw(\"\").value; +if (yield_return_value !== undefined) { + (yield_return_value: string); // error: number ~> string +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +function* catch_return() { + try { + yield 0; + } catch (e) { + return e; + } +} +var catch_return_value = catch_return().throw(\"\").value; +if (catch_return_value !== undefined) { + (catch_return_value: string);// error: number ~> string +} +function* yield_return() { + try { + yield 0; + return; + } catch (e) { + yield e; + } +} +var yield_return_value = yield_return().throw(\"\").value; +if (yield_return_value !== undefined) { + (yield_return_value: string);// error: number ~> string +} + +" +`; + +exports[`test variance.js 1`] = ` +"declare var g1: Generator; +var g2: Generator = g1; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/generators/class.js b/tests/generators/class.js new file mode 100644 index 000000000000..8e88486f3831 --- /dev/null +++ b/tests/generators/class.js @@ -0,0 +1,129 @@ +class GeneratorExamples { + *stmt_yield(): Generator { + yield 0; // ok + yield ""; // error: string ~> number + } + + *stmt_next(): Generator { + var a = yield; + if (a) { + (a : number); // ok + } + + var b = yield; + if (b) { + (b : string); // error: number ~> string + } + } + + *stmt_return_ok(): Generator { + return 0; // ok + } + + *stmt_return_err(): Generator { + return ""; // error: string ~> number + } + + *infer_stmt() { + var x: boolean = yield 0; // error: number ~> boolean + return ""; + } + + *widen_next() { + var x = yield 0; + if (typeof x === "number") { + } else if (typeof x === "boolean") { + } else { + (x : string) // ok, sherlock + } + } + + *widen_yield() { + yield 0; + yield ""; + yield true; + } + + *delegate_next_generator() { + function *inner() { + var x: number = yield; // error: string ~> number + } + yield *inner(); + } + + *delegate_yield_generator() { + function *inner() { + yield ""; + } + + yield *inner(); + } + + *delegate_return_generator() { + function *inner() { + return ""; + } + + var x: number = yield *inner(); // error: string ~> number + } + + // only generators can make use of a value passed to next + *delegate_next_iterable(xs: Array) { + yield *xs; + } + + *delegate_yield_iterable(xs: Array) { + yield *xs; + } + + *delegate_return_iterable(xs: Array) { + var x: void = yield *xs // ok: Iterator has no yield value + } + + *generic_yield(y: Y): Generator { + yield y; + } + + *generic_return(r: R): Generator { + return r; + } + + *generic_next(): Generator { + return yield undefined; + } +} + +var examples = new GeneratorExamples(); + +for (var x of examples.infer_stmt()) { (x : string) } // error: number ~> string + +var infer_stmt_next = examples.infer_stmt().next(0).value; // error: number ~> boolean +if (typeof infer_stmt_next === "undefined") { +} else if (typeof infer_stmt_next === "number") { +} else { + (infer_stmt_next : boolean) // error: string ~> boolean +} + +examples.widen_next().next(0) +examples.widen_next().next("") +examples.widen_next().next(true) + +for (var x of examples.widen_yield()) { + if (typeof x === "number") { + } else if (typeof x === "boolean") { + } else { + (x : string) // ok, sherlock + } +} + +examples.delegate_next_generator().next(""); + +for (var x of examples.delegate_yield_generator()) { + (x : number) // error: string ~> number +} + +examples.delegate_next_iterable([]).next(""); // error: Iterator has no next value + +for (var x of examples.delegate_yield_iterable([])) { + (x : string) // error: number ~> string +} diff --git a/tests/generators/class_failure.js b/tests/generators/class_failure.js new file mode 100644 index 000000000000..42697f5de7ff --- /dev/null +++ b/tests/generators/class_failure.js @@ -0,0 +1,20 @@ +// generalization of failure in class.js + +class GeneratorExamples { + *infer_stmt() { + var x: boolean = yield 0; // error: number ~> boolean + return ""; + } +} + +var examples = new GeneratorExamples(); + +for (var x of examples.infer_stmt()) { (x : string) } // error: number ~> string + +var infer_stmt_next = examples.infer_stmt().next(0).value; // error: number ~> boolean + +if (typeof infer_stmt_next === "undefined") { +} else if (typeof infer_stmt_next === "number") { +} else { + (infer_stmt_next : boolean) // error: string ~> boolean +} diff --git a/tests/generators/generators.js b/tests/generators/generators.js new file mode 100644 index 000000000000..c59630bae767 --- /dev/null +++ b/tests/generators/generators.js @@ -0,0 +1,129 @@ +function *stmt_yield(): Generator { + yield 0; // ok + yield ""; // error: string ~> number +} + +function *stmt_next(): Generator { + var a = yield; + if (a) { + (a : number); // ok + } + + var b = yield; + if (b) { + (b : string); // error: number ~> string + } +} + +function *stmt_return_ok(): Generator { + return 0; // ok +} + +function *stmt_return_err(): Generator { + return ""; // error: string ~> number +} + +function *infer_stmt() { + var x: boolean = yield 0; + return ""; +} +for (var x of infer_stmt()) { (x : string) } // error: number ~> string +var infer_stmt_next = infer_stmt().next(0).value; // error: number ~> boolean +if (typeof infer_stmt_next === "undefined") { +} else if (typeof infer_stmt_next === "number") { +} else { + (infer_stmt_next : boolean) // error: string ~> boolean +} + +function *widen_next() { + var x = yield 0; + if (typeof x === "number") { + } else if (typeof x === "boolean") { + } else { + (x : string) // ok, sherlock + } +} +widen_next().next(0) +widen_next().next("") +widen_next().next(true) + +function *widen_yield() { + yield 0; + yield ""; + yield true; +} +for (var x of widen_yield()) { + if (typeof x === "number") { + } else if (typeof x === "boolean") { + } else { + (x : string) // ok, sherlock + } +} + +function *delegate_next_generator() { + function *inner() { + var x: number = yield; // error: string ~> number + } + yield *inner(); +} +delegate_next_generator().next(""); + +function *delegate_yield_generator() { + function *inner() { + yield ""; + } + + yield *inner(); +} +for (var x of delegate_yield_generator()) { + (x : number) // error: string ~> number +} + +function *delegate_return_generator() { + function *inner() { + return ""; + } + + var x: number = yield *inner(); // error: string ~> number +} + +// only generators can make use of a value passed to next +function *delegate_next_iterable(xs: Array) { + yield *xs; +} +delegate_next_iterable([]).next(""); // error: Iterator has no next value + +function *delegate_yield_iterable(xs: Array) { + yield *xs; +} +for (var x of delegate_yield_iterable([])) { + (x : string) // error: number ~> string +} + +function *delegate_return_iterable(xs: Array) { + var x: void = yield *xs // ok: Iterator has no yield value +} + +function *generic_yield(y: Y): Generator { + yield y; +} + +function *generic_return(r: R): Generator { + return r; +} + +function *generic_next(): Generator { + return yield undefined; +} + +function *multiple_return(b) { + if (b) { + return 0; + } else { + return "foo"; + } +} +let multiple_return_result = multiple_return().next(); +if (multiple_return_result.done) { + (multiple_return_result.value: void); // error: number|string ~> void +} diff --git a/tests/generators/jsfmt.spec.js b/tests/generators/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/generators/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/generators/return.js b/tests/generators/return.js new file mode 100644 index 000000000000..411d5ec17de0 --- /dev/null +++ b/tests/generators/return.js @@ -0,0 +1,21 @@ +function test1(gen: Generator) { + // You can pass whatever you like to return, it doesn't need to be related to + // the Generator's return type + var ret = gen.return(0); + (ret.value: void); // error: string | number ~> void +} + +// However, a generator can "refuse" the return by catching an exception and +// yielding or returning internally. +function *refuse_return() { + try { + yield 1; + } finally { + return 0; + } +} +var refuse_return_gen = refuse_return(); +var refuse_return_result = refuse_return_gen.return("string"); +if (refuse_return_result.done) { + (refuse_return_result.value: string); // error: number | void ~> string +} diff --git a/tests/generators/throw.js b/tests/generators/throw.js new file mode 100644 index 000000000000..de4ae94883d3 --- /dev/null +++ b/tests/generators/throw.js @@ -0,0 +1,25 @@ +function *catch_return() { + try { + yield 0; + } catch (e) { + return e; + } +} + +var catch_return_value = catch_return().throw("").value; +if (catch_return_value !== undefined) { + (catch_return_value : string); // error: number ~> string +} + +function *yield_return() { + try { + yield 0; + return; + } catch (e) { + yield e; + } +} +var yield_return_value = yield_return().throw("").value; +if (yield_return_value !== undefined) { + (yield_return_value: string); // error: number ~> string +} diff --git a/tests/generators/variance.js b/tests/generators/variance.js new file mode 100644 index 000000000000..021c3eddef17 --- /dev/null +++ b/tests/generators/variance.js @@ -0,0 +1,2 @@ +declare var g1: Generator; +var g2: Generator = g1; diff --git a/tests/generics/__snapshots__/jsfmt.spec.js.snap b/tests/generics/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..0edf6ac3fab0 --- /dev/null +++ b/tests/generics/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,62 @@ +exports[`test generics.js 1`] = ` +"class C { + x:X; + constructor(x:X) { this.x = x; } + get():X { return this.x; } +} + +class D { + x:T; + m(z:S,u:T,v):S { + this.x = u; + v.u = u; + return z; + } +} + +var d = new D(); +var o = {}; +var b = d.m(true,0,o); +var s:string = d.x; +var n:number = o.u; + +class E extends C { + //x:X; + set(x:X):X { /*return x;*/ this.x = x; return /*this.x; */this.get(); } +} + +var e = new E(); // error: too few arguments to inherited constructor +var x:string = e.set(0); + +class F { } +class G extends F> {} +class H extends G> { + x:Z; + foo(x:Z) { this.x = x; } +} + +var h1 = new H(); +h1.foo([\"...\"]); +var h2:F>>> = h1; + +var obj : Object = {} // error, arity 0 +var fn : Function = function() { return \'foo\'; } // error, arity 0 +var fn : function = function() { return \'foo\'; } // error, arity 0 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (43:9) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$7.flowParsePrimaryType (/node_modules/babylon/lib/index.js:5099:8) + at Parser.pp$7.flowParsePostfixType (/node_modules/babylon/lib/index.js:5105:19) + at Parser.pp$7.flowParsePrefixType (/node_modules/babylon/lib/index.js:5122:17) + at Parser.pp$7.flowParseAnonFunctionWithoutParens (/node_modules/babylon/lib/index.js:5127:20) + at Parser.pp$7.flowParseIntersectionType (/node_modules/babylon/lib/index.js:5141:19) + at Parser.pp$7.flowParseUnionType (/node_modules/babylon/lib/index.js:5151:19) + at Parser.pp$7.flowParseType (/node_modules/babylon/lib/index.js:5162:19) + at Parser.pp$7.flowParseTypeInitialiser (/node_modules/babylon/lib/index.js:4430:19) +" +`; diff --git a/tests/generics/generics.js b/tests/generics/generics.js new file mode 100644 index 000000000000..603f3aebb836 --- /dev/null +++ b/tests/generics/generics.js @@ -0,0 +1,43 @@ +class C { + x:X; + constructor(x:X) { this.x = x; } + get():X { return this.x; } +} + +class D { + x:T; + m(z:S,u:T,v):S { + this.x = u; + v.u = u; + return z; + } +} + +var d = new D(); +var o = {}; +var b = d.m(true,0,o); +var s:string = d.x; +var n:number = o.u; + +class E extends C { + //x:X; + set(x:X):X { /*return x;*/ this.x = x; return /*this.x; */this.get(); } +} + +var e = new E(); // error: too few arguments to inherited constructor +var x:string = e.set(0); + +class F { } +class G extends F> {} +class H extends G> { + x:Z; + foo(x:Z) { this.x = x; } +} + +var h1 = new H(); +h1.foo(["..."]); +var h2:F>>> = h1; + +var obj : Object = {} // error, arity 0 +var fn : Function = function() { return 'foo'; } // error, arity 0 +var fn : function = function() { return 'foo'; } // error, arity 0 diff --git a/tests/generics/jsfmt.spec.js b/tests/generics/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/generics/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/geolocation/__snapshots__/jsfmt.spec.js.snap b/tests/geolocation/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..989969569252 --- /dev/null +++ b/tests/geolocation/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,39 @@ +exports[`test a.js 1`] = ` +"/* @flow */ + +var geolocation = new Geolocation(); +var id = geolocation.watchPosition( + position => { + var coords: Coordinates = position.coords; + var accuracy: number = coords.accuracy; + }, + e => { + var message: string = e.message; + switch (e.code) { + case e.PERMISSION_DENIED: + case e.POSITION_UNAVAILABLE: + case e.TIMEOUT: + default: + break; + } + } +); +geolocation.clearWatch(id); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1020 + }, \"consequent\").indent(options.tabWidth)); + ^ + +TypeError: path.call(...).indent is not a function + at genericPrintNoParens (/src/printer.js:1020:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1005:36) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/geolocation/a.js b/tests/geolocation/a.js new file mode 100644 index 000000000000..21090c0c43b2 --- /dev/null +++ b/tests/geolocation/a.js @@ -0,0 +1,20 @@ +/* @flow */ + +var geolocation = new Geolocation(); +var id = geolocation.watchPosition( + position => { + var coords: Coordinates = position.coords; + var accuracy: number = coords.accuracy; + }, + e => { + var message: string = e.message; + switch (e.code) { + case e.PERMISSION_DENIED: + case e.POSITION_UNAVAILABLE: + case e.TIMEOUT: + default: + break; + } + } +); +geolocation.clearWatch(id); diff --git a/tests/geolocation/jsfmt.spec.js b/tests/geolocation/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/geolocation/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/get-def/__snapshots__/jsfmt.spec.js.snap b/tests/get-def/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..11081a6e424b --- /dev/null +++ b/tests/get-def/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,83 @@ +exports[`test example.js 1`] = ` +"/* @flow */ + +var lib = require('./library'); + +function add(a: number, b: number): number { + return a + b; +} + +var re = /^keynote (talk){2} (lightning){3,5} (talk){2} closing partytime!!!/ + +// t123456 +add(lib.iTakeAString(42), 7); + +// D123456 +lib.bar(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var lib = require("./library"); +function add(a: number, b: number): number { + return a + b; +} +var re = /^keynote (talk){2} (lightning){3,5} (talk){2} closing partytime!!!/; +// t123456 +add(lib.iTakeAString(42), 7); +// D123456 +lib.bar(); + +" +`; + +exports[`test imports.js 1`] = ` +"// @flow + +import thing from "./helpers/exports_default.js"; +thing; + +import {foo, bar as baz} from "./helpers/exports_named.js"; +foo; +baz; + +import * as things from "./helpers/exports_named.js"; +things; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +import thing from "./helpers/exports_default.js"; +thing; +import { foo, bar as baz } from "./helpers/exports_named.js"; +foo; +baz; +import * as things from "./helpers/exports_named.js"; +things; + +" +`; + +exports[`test library.js 1`] = ` +"/* @flow */ + +module.exports = { + + iTakeAString: function(name: string): number { + return 42; + }, + + bar: function(): number { + return 42; + }, + +}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +module.exports = { + iTakeAString: function(name: string): number { + return 42; + }, + bar: function(): number { + return 42; + } +}; + +" +`; diff --git a/tests/get-def/example.js b/tests/get-def/example.js new file mode 100644 index 000000000000..430af4dddd06 --- /dev/null +++ b/tests/get-def/example.js @@ -0,0 +1,15 @@ +/* @flow */ + +var lib = require('./library'); + +function add(a: number, b: number): number { + return a + b; +} + +var re = /^keynote (talk){2} (lightning){3,5} (talk){2} closing partytime!!!/ + +// t123456 +add(lib.iTakeAString(42), 7); + +// D123456 +lib.bar(); diff --git a/tests/get-def/helpers/__snapshots__/jsfmt.spec.js.snap b/tests/get-def/helpers/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..45914a5abd03 --- /dev/null +++ b/tests/get-def/helpers/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,26 @@ +exports[`test exports_default.js 1`] = ` +"// @flow + +const x = "foo"; + +export default x; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +const x = "foo"; +export default x + +" +`; + +exports[`test exports_named.js 1`] = ` +"// @flow + +export const foo = "foo"; +export const bar = "bar"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +export const foo = "foo"; +export const bar = "bar"; + +" +`; diff --git a/tests/get-def/helpers/exports_default.js b/tests/get-def/helpers/exports_default.js new file mode 100644 index 000000000000..35d13ab4a63c --- /dev/null +++ b/tests/get-def/helpers/exports_default.js @@ -0,0 +1,5 @@ +// @flow + +const x = "foo"; + +export default x; diff --git a/tests/get-def/helpers/exports_named.js b/tests/get-def/helpers/exports_named.js new file mode 100644 index 000000000000..1d89d4eb161c --- /dev/null +++ b/tests/get-def/helpers/exports_named.js @@ -0,0 +1,4 @@ +// @flow + +export const foo = "foo"; +export const bar = "bar"; diff --git a/tests/get-def/helpers/jsfmt.spec.js b/tests/get-def/helpers/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/get-def/helpers/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/get-def/imports.js b/tests/get-def/imports.js new file mode 100644 index 000000000000..34cf5bc9c3c6 --- /dev/null +++ b/tests/get-def/imports.js @@ -0,0 +1,11 @@ +// @flow + +import thing from "./helpers/exports_default.js"; +thing; + +import {foo, bar as baz} from "./helpers/exports_named.js"; +foo; +baz; + +import * as things from "./helpers/exports_named.js"; +things; diff --git a/tests/get-def/jsfmt.spec.js b/tests/get-def/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/get-def/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/get-def/library.js b/tests/get-def/library.js new file mode 100644 index 000000000000..fae7b9fc3770 --- /dev/null +++ b/tests/get-def/library.js @@ -0,0 +1,13 @@ +/* @flow */ + +module.exports = { + + iTakeAString: function(name: string): number { + return 42; + }, + + bar: function(): number { + return 42; + }, + +}; diff --git a/tests/get-def2/Parent.js b/tests/get-def2/Parent.js new file mode 100644 index 000000000000..830a1cd31ed6 --- /dev/null +++ b/tests/get-def2/Parent.js @@ -0,0 +1,4 @@ +// @flow + +var ParentFoo = {foo: 'bar'}; +module.exports = {ParentFoo}; diff --git a/tests/get-def2/__snapshots__/jsfmt.spec.js.snap b/tests/get-def2/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..bad7c5acd016 --- /dev/null +++ b/tests/get-def2/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,127 @@ +exports[`test Parent.js 1`] = ` +"// @flow + +var ParentFoo = {foo: 'bar'}; +module.exports = {ParentFoo}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +var ParentFoo = { foo: "bar" }; +module.exports = { ParentFoo }; + +" +`; + +exports[`test main.js 1`] = ` +"// @flow + +var Parent = require('./Parent'); + +// Hops through destructuring +let ParentFoo; +({ParentFoo} = Parent); +ParentFoo; // Points to lval in line above this + +// Follows assignment on simple/"non-destructuring" patterns +let ParentFoo2; +ParentFoo2 = Parent; +ParentFoo2; // Points to LHS of line above this + +// Follows assignment with declaration +let ParentFoo3 = Parent; +ParentFoo3; // Points to LHS of line above this + +// Follows non-destructured property access of \`require('Parent')\` +let foo = require('./Parent').ParentFoo.foo; +foo; + +import type {Foo} from './types'; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +var Parent = require("./Parent"); +// Hops through destructuring +let ParentFoo; +{ ParentFoo } = Parent; +ParentFoo;// Points to lval in line above this +// Follows assignment on simple/"non-destructuring" patterns +let ParentFoo2; +ParentFoo2 = Parent; +ParentFoo2;// Points to LHS of line above this +// Follows assignment with declaration +let ParentFoo3 = Parent; +ParentFoo3;// Points to LHS of line above this +// Follows non-destructured property access of \`require('Parent')\` +let foo = require("./Parent").ParentFoo.foo; +foo; +import type { Foo } from "./types"; + +" +`; + +exports[`test override.js 1`] = ` +"// @flow + +class C { + override() { } +} + +class D extends C { + foo() { this.override() } + bar() { this.override } + override() { } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +class C { + override() { + + } +} +class D extends C { + foo() { + this.override(); + } + bar() { + this.override; + } + override() { + + } +} + +" +`; + +exports[`test react.js 1`] = ` +"var React = require('react'); + +class C extends React.Component { + props: { x: string }; +} + +let msg = "hello"; + +(); + +(
); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var React = require("react"); +class C extends React.Component { + props: { x: string }; +} +let msg = "hello"; +; +
; + +" +`; + +exports[`test types.js 1`] = ` +"// @flow + +export type Foo = {}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +export type Foo = {}; + +" +`; diff --git a/tests/get-def2/jsfmt.spec.js b/tests/get-def2/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/get-def2/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/get-def2/lib/__snapshots__/jsfmt.spec.js.snap b/tests/get-def2/lib/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..1c2c02375402 --- /dev/null +++ b/tests/get-def2/lib/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,26 @@ +exports[`test jsx.js 1`] = ` +"declare var $React: $Exports<\'react\'>; // fake import +type $JSXIntrinsic = Class<$React.Component>; + +type $JSXIntrinsics = { + div: $JSXIntrinsic<{id: string}>, + span: $JSXIntrinsic<{id: string, class: string}>, +}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/get-def2/lib/jsfmt.spec.js b/tests/get-def2/lib/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/get-def2/lib/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/get-def2/lib/jsx.js b/tests/get-def2/lib/jsx.js new file mode 100644 index 000000000000..7de8608bc649 --- /dev/null +++ b/tests/get-def2/lib/jsx.js @@ -0,0 +1,7 @@ +declare var $React: $Exports<'react'>; // fake import +type $JSXIntrinsic = Class<$React.Component>; + +type $JSXIntrinsics = { + div: $JSXIntrinsic<{id: string}>, + span: $JSXIntrinsic<{id: string, class: string}>, +}; diff --git a/tests/get-def2/main.js b/tests/get-def2/main.js new file mode 100644 index 000000000000..63064a345a05 --- /dev/null +++ b/tests/get-def2/main.js @@ -0,0 +1,23 @@ +// @flow + +var Parent = require('./Parent'); + +// Hops through destructuring +let ParentFoo; +({ParentFoo} = Parent); +ParentFoo; // Points to lval in line above this + +// Follows assignment on simple/"non-destructuring" patterns +let ParentFoo2; +ParentFoo2 = Parent; +ParentFoo2; // Points to LHS of line above this + +// Follows assignment with declaration +let ParentFoo3 = Parent; +ParentFoo3; // Points to LHS of line above this + +// Follows non-destructured property access of `require('Parent')` +let foo = require('./Parent').ParentFoo.foo; +foo; + +import type {Foo} from './types'; diff --git a/tests/get-def2/override.js b/tests/get-def2/override.js new file mode 100644 index 000000000000..50de748aeb9c --- /dev/null +++ b/tests/get-def2/override.js @@ -0,0 +1,11 @@ +// @flow + +class C { + override() { } +} + +class D extends C { + foo() { this.override() } + bar() { this.override } + override() { } +} diff --git a/tests/get-def2/react.js b/tests/get-def2/react.js new file mode 100644 index 000000000000..1c633ef60d61 --- /dev/null +++ b/tests/get-def2/react.js @@ -0,0 +1,11 @@ +var React = require('react'); + +class C extends React.Component { + props: { x: string }; +} + +let msg = "hello"; + +(); + +(
); diff --git a/tests/get-def2/types.js b/tests/get-def2/types.js new file mode 100644 index 000000000000..2778d2acab1c --- /dev/null +++ b/tests/get-def2/types.js @@ -0,0 +1,3 @@ +// @flow + +export type Foo = {}; diff --git a/tests/get-imports-and-importers/__snapshots__/jsfmt.spec.js.snap b/tests/get-imports-and-importers/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..3a4850bffce2 --- /dev/null +++ b/tests/get-imports-and-importers/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,35 @@ +exports[`test a.js 1`] = ` +"// @flow +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; + +exports[`test b.js 1`] = ` +"/** + * @flow + * @providesModule b + */ +require('./a'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + * @providesModule b + */ +require("./a"); + +" +`; + +exports[`test c.js 1`] = ` +"// @flow +require('./a.js'); +require('b'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +require("./a.js"); +require("b"); + +" +`; diff --git a/tests/get-imports-and-importers/a.js b/tests/get-imports-and-importers/a.js new file mode 100644 index 000000000000..46e7f7c04567 --- /dev/null +++ b/tests/get-imports-and-importers/a.js @@ -0,0 +1 @@ +// @flow diff --git a/tests/get-imports-and-importers/b.js b/tests/get-imports-and-importers/b.js new file mode 100644 index 000000000000..6f8b57f3965d --- /dev/null +++ b/tests/get-imports-and-importers/b.js @@ -0,0 +1,5 @@ +/** + * @flow + * @providesModule b + */ +require('./a'); diff --git a/tests/get-imports-and-importers/c.js b/tests/get-imports-and-importers/c.js new file mode 100644 index 000000000000..31d5ef9657f4 --- /dev/null +++ b/tests/get-imports-and-importers/c.js @@ -0,0 +1,3 @@ +// @flow +require('./a.js'); +require('b'); diff --git a/tests/get-imports-and-importers/jsfmt.spec.js b/tests/get-imports-and-importers/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/get-imports-and-importers/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/getters_and_setters_disabled/__snapshots__/jsfmt.spec.js.snap b/tests/getters_and_setters_disabled/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..1c785f6ffd3c --- /dev/null +++ b/tests/getters_and_setters_disabled/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,26 @@ +exports[`test getters_and_setters.js 1`] = ` +"/** + * @flow + */ + +var f = { + get a() { return 4; }, + set b(x: number) { this.c = b; }, + c: 10, +}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +var f = { + get a() { + return 4; + }, + set b(x: number) { + this.c = b; + }, + c: 10 +}; + +" +`; diff --git a/tests/getters_and_setters_disabled/getters_and_setters.js b/tests/getters_and_setters_disabled/getters_and_setters.js new file mode 100644 index 000000000000..f20bb4867a5a --- /dev/null +++ b/tests/getters_and_setters_disabled/getters_and_setters.js @@ -0,0 +1,9 @@ +/** + * @flow + */ + +var f = { + get a() { return 4; }, + set b(x: number) { this.c = b; }, + c: 10, +}; diff --git a/tests/getters_and_setters_disabled/jsfmt.spec.js b/tests/getters_and_setters_disabled/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/getters_and_setters_disabled/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/getters_and_setters_enabled/__snapshots__/jsfmt.spec.js.snap b/tests/getters_and_setters_enabled/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..7bb852a92f5d --- /dev/null +++ b/tests/getters_and_setters_enabled/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,451 @@ +exports[`test class.js 1`] = ` +"/** + * @flow + */ + +var z: number = 123; + +class Foo { + get goodGetterNoAnnotation() { return 4; } + get goodGetterWithAnnotation(): number { return 4; } + + set goodSetterNoAnnotation(x) { z = x; } + set goodSetterWithAnnotation(x: number) { z = x; } + + get propWithMatchingGetterAndSetter(): number { return 4; } + set propWithMatchingGetterAndSetter(x: number) { } + + // The getter and setter need not have the same type - no error + get propWithSubtypingGetterAndSetter(): ?number { return 4; } + set propWithSubtypingGetterAndSetter(x: number) { } + + // The getter and setter need not have the same type - no error + set propWithSubtypingGetterAndSetterReordered(x: number) { } + get propWithSubtypingGetterAndSetterReordered(): ?number { return 4; } + + get propWithMismatchingGetterAndSetter(): number { return 4; } + set propWithMismatchingGetterAndSetter(x: string) { } // doesn't match getter (OK) + + propOverriddenWithGetter: number; + get propOverriddenWithGetter() { return "hello"; } + + propOverriddenWithSetter: number; + set propOverriddenWithSetter(x: string) { } +}; + +var foo = new Foo(); + +// Test getting properties with getters +var testGetterNoError1: number = foo.goodGetterNoAnnotation; +var testGetterNoError2: number = foo.goodGetterWithAnnotation; + +var testGetterWithError1: string = foo.goodGetterNoAnnotation; // Error number ~> string +var testGetterWithError2: string = foo.goodGetterWithAnnotation; // Error number ~> string + +// Test setting properties with getters +foo.goodSetterNoAnnotation = 123; +foo.goodSetterWithAnnotation = 123; + +// TODO: Why does no annotation mean no error? +foo.goodSetterNoAnnotation = "hello"; // Error string ~> number +foo.goodSetterWithAnnotation = "hello"; // Error string ~> number + +var testSubtypingGetterAndSetter: number = foo.propWithSubtypingGetterAndSetter; // Error ?number ~> number + +var testPropOverridenWithGetter: number = foo.propOverriddenWithGetter; // Error string ~> number +foo.propOverriddenWithSetter = 123; // Error number ~> string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +var z: number = 123; +class Foo { + get goodGetterNoAnnotation() { + return 4; + } + get goodGetterWithAnnotation(): number { + return 4; + } + set goodSetterNoAnnotation(x) { + z = x; + } + set goodSetterWithAnnotation(x: number) { + z = x; + } + get propWithMatchingGetterAndSetter(): number { + return 4; + } + set propWithMatchingGetterAndSetter(x: number) { + + } + // The getter and setter need not have the same type - no error + get propWithSubtypingGetterAndSetter(): ?number { + return 4; + } + set propWithSubtypingGetterAndSetter(x: number) { + + } + // The getter and setter need not have the same type - no error + set propWithSubtypingGetterAndSetterReordered(x: number) { + + } + get propWithSubtypingGetterAndSetterReordered(): ?number { + return 4; + } + get propWithMismatchingGetterAndSetter(): number { + return 4; + } + set propWithMismatchingGetterAndSetter(x: string) { + + }// doesn't match getter (OK) + propOverriddenWithGetter: number; + get propOverriddenWithGetter() { + return "hello"; + } + propOverriddenWithSetter: number; + set propOverriddenWithSetter(x: string) { + + } +} +var foo = new Foo(); +// Test getting properties with getters +var testGetterNoError1: number = foo.goodGetterNoAnnotation; +var testGetterNoError2: number = foo.goodGetterWithAnnotation; +var testGetterWithError1: string = foo.goodGetterNoAnnotation;// Error number ~> string +var testGetterWithError2: string = foo.goodGetterWithAnnotation;// Error number ~> string +// Test setting properties with getters +foo.goodSetterNoAnnotation = 123; +foo.goodSetterWithAnnotation = 123; +// TODO: Why does no annotation mean no error? +foo.goodSetterNoAnnotation = "hello";// Error string ~> number +foo.goodSetterWithAnnotation = "hello";// Error string ~> number +var testSubtypingGetterAndSetter: number = foo.propWithSubtypingGetterAndSetter;// Error ?number ~> number +var testPropOverridenWithGetter: number = foo.propOverriddenWithGetter;// Error string ~> number +foo.propOverriddenWithSetter = 123;// Error number ~> string + +" +`; + +exports[`test object.js 1`] = ` +"/** + * @flow + */ + +var z: number = 123; + +class A {} +class B extends A {} +class C extends A {} + +var obj = { + get goodGetterNoAnnotation() { return 4; }, + get goodGetterWithAnnotation(): number { return 4; }, + + set goodSetterNoAnnotation(x) { z = x; }, + set goodSetterWithAnnotation(x: number) { z = x; }, + + get propWithMatchingGetterAndSetter(): number { return 4; }, + set propWithMatchingGetterAndSetter(x: number) { }, + + // The getter and setter need not have the same type + get propWithSubtypingGetterAndSetter(): ?number { return 4; }, // OK + set propWithSubtypingGetterAndSetter(x: number) { }, + + set propWithSubtypingGetterAndSetterReordered(x: number) { }, // OK + get propWithSubtypingGetterAndSetterReordered(): ?number { return 4; }, + + get exampleOfOrderOfGetterAndSetter(): A { return new A(); }, + set exampleOfOrderOfGetterAndSetter(x: B) {}, + + set exampleOfOrderOfGetterAndSetterReordered(x: B) {}, + get exampleOfOrderOfGetterAndSetterReordered(): A { return new A(); }, +}; + + + +// Test getting properties with getters +var testGetterNoError1: number = obj.goodGetterNoAnnotation; +var testGetterNoError2: number = obj.goodGetterWithAnnotation; + +var testGetterWithError1: string = obj.goodGetterNoAnnotation; // Error number ~> string +var testGetterWithError2: string = obj.goodGetterWithAnnotation; // Error number ~> string + +// Test setting properties with getters +obj.goodSetterNoAnnotation = 123; +obj.goodSetterWithAnnotation = 123; + +obj.goodSetterNoAnnotation = "hello"; // Error string ~> number +obj.goodSetterWithAnnotation = "hello"; // Error string ~> number + +var testSubtypingGetterAndSetter: number = obj.propWithSubtypingGetterAndSetter; // Error ?number ~> number + +// When building this feature, it was tempting to flow the setter into the +// getter and then use either the getter or setter as the type of the property. +// This example shows the danger of using the getter's type +obj.exampleOfOrderOfGetterAndSetter = new C(); // Error C ~> B + +// And this example shows the danger of using the setter's type. +var testExampleOrOrderOfGetterAndSetterReordered: number = + obj.exampleOfOrderOfGetterAndSetterReordered; // Error A ~> B +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +var z: number = 123; +class A {} +class B extends A {} +class C extends A {} +var obj = { + get goodGetterNoAnnotation() { + return 4; + }, + get goodGetterWithAnnotation(): number { + return 4; + }, + set goodSetterNoAnnotation(x) { + z = x; + }, + set goodSetterWithAnnotation(x: number) { + z = x; + }, + get propWithMatchingGetterAndSetter(): number { + return 4; + }, + set propWithMatchingGetterAndSetter(x: number) { + + }, + // The getter and setter need not have the same type + // OK + get propWithSubtypingGetterAndSetter(): ?number { + return 4; + }, + set propWithSubtypingGetterAndSetter(x: number) { + + }, + // OK set propWithSubtypingGetterAndSetterReordered(x: number) { + + }, + get propWithSubtypingGetterAndSetterReordered(): ?number { + return 4; + }, + get exampleOfOrderOfGetterAndSetter(): A { + return new A(); + }, + set exampleOfOrderOfGetterAndSetter(x: B) { + + }, + set exampleOfOrderOfGetterAndSetterReordered(x: B) { + + }, + get exampleOfOrderOfGetterAndSetterReordered(): A { + return new A(); + } +}; +// Test getting properties with getters +var testGetterNoError1: number = obj.goodGetterNoAnnotation; +var testGetterNoError2: number = obj.goodGetterWithAnnotation; +var testGetterWithError1: string = obj.goodGetterNoAnnotation;// Error number ~> string +var testGetterWithError2: string = obj.goodGetterWithAnnotation;// Error number ~> string +// Test setting properties with getters +obj.goodSetterNoAnnotation = 123; +obj.goodSetterWithAnnotation = 123; +obj.goodSetterNoAnnotation = "hello";// Error string ~> number +obj.goodSetterWithAnnotation = "hello";// Error string ~> number +var testSubtypingGetterAndSetter: number = obj.propWithSubtypingGetterAndSetter;// Error ?number ~> number +// When building this feature, it was tempting to flow the setter into the +// getter and then use either the getter or setter as the type of the property. +// This example shows the danger of using the getter's type +obj.exampleOfOrderOfGetterAndSetter = new C();// Error C ~> B +// And this example shows the danger of using the setter's type. +var testExampleOrOrderOfGetterAndSetterReordered: number = obj.exampleOfOrderOfGetterAndSetterReordered;// Error A ~> B + +" +`; + +exports[`test react.js 1`] = ` +"/** + * @flow + */ + +React.createClass({ + propTypes: { + get a() { return 4; }, + set b(x: number) { this.c = x; }, + c: 10, + } +}); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +React.createClass({ + propTypes: { + get a() { + return 4; + }, + set b(x: number) { + this.c = x; + }, + c: 10 + } +}); + +" +`; + +exports[`test variance.js 1`] = ` +"/* @flow */ + +class A {} +class B extends A {} +class C extends B {} + +declare var a: A; +declare var b: B; +declare var c: C; + +class Base { + x: B; + +pos: B; + -neg: B; + get get(): B { return this.x }; + set set(value: B): void { this.x = value }; + get getset(): B { return this.x }; + set getset(value: B): void { this.x = value }; +} + +(class extends Base { + // error: getter incompatible with read/write property + get x(): B { return b } +}); + +(class extends Base { + // error: setter incompatible with read/write property + set x(value: B): void {} +}); + +(class extends Base { + // ok: get/set co/contra with read/write property, resp. + get x(): C { return c } + set x(value: A): void {} +}); + +(class extends Base { + // error: setter incompatible with read-only property + set pos(value: B): void {} +}); + +(class extends Base { + // ok: getter covariant with read-only property + get pos(): C { return c } +}); + +(class extends Base { + // error: getter incompatible with write-only property + get neg(): B { return b } +}); + +(class extends Base { + // ok: setter contravariant with write-only property + set neg(value: A): void {} +}); + +(class extends Base { + // ok: read/write covariant with getter + get: C; +}); + +(class extends Base { + // ok: read/write contravariant with setter + set: A; +}); + +(class extends Base { + // ok: read/write invariant with get/set + getset: B; +}); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +class A {} +class B extends A {} +class C extends B {} +declare var a: A; +declare var b: B; +declare var c: C; +class Base { + x: B; + +pos: B; + -neg: B; + get get(): B { + return this.x; + } + set set(value: B): void { + this.x = value; + } + get getset(): B { + return this.x; + } + set getset(value: B): void { + this.x = value; + } +} +class extends Base { + // error: getter incompatible with read/write property + get x(): B { + return b; + } +}; +class extends Base { + // error: setter incompatible with read/write property + set x(value: B): void { + + } +}; +class extends Base { + // ok: get/set co/contra with read/write property, resp. + get x(): C { + return c; + } + set x(value: A): void { + + } +}; +class extends Base { + // error: setter incompatible with read-only property + set pos(value: B): void { + + } +}; +class extends Base { + // ok: getter covariant with read-only property + get pos(): C { + return c; + } +}; +class extends Base { + // error: getter incompatible with write-only property + get neg(): B { + return b; + } +}; +class extends Base { + // ok: setter contravariant with write-only property + set neg(value: A): void { + + } +}; +class extends Base { + // ok: read/write covariant with getter + get: C; +}; +class extends Base { + // ok: read/write contravariant with setter + set: A; +}; +class extends Base { + // ok: read/write invariant with get/set + getset: B; +}; + +" +`; diff --git a/tests/getters_and_setters_enabled/class.js b/tests/getters_and_setters_enabled/class.js new file mode 100644 index 000000000000..c9a3707e28df --- /dev/null +++ b/tests/getters_and_setters_enabled/class.js @@ -0,0 +1,55 @@ +/** + * @flow + */ + +var z: number = 123; + +class Foo { + get goodGetterNoAnnotation() { return 4; } + get goodGetterWithAnnotation(): number { return 4; } + + set goodSetterNoAnnotation(x) { z = x; } + set goodSetterWithAnnotation(x: number) { z = x; } + + get propWithMatchingGetterAndSetter(): number { return 4; } + set propWithMatchingGetterAndSetter(x: number) { } + + // The getter and setter need not have the same type - no error + get propWithSubtypingGetterAndSetter(): ?number { return 4; } + set propWithSubtypingGetterAndSetter(x: number) { } + + // The getter and setter need not have the same type - no error + set propWithSubtypingGetterAndSetterReordered(x: number) { } + get propWithSubtypingGetterAndSetterReordered(): ?number { return 4; } + + get propWithMismatchingGetterAndSetter(): number { return 4; } + set propWithMismatchingGetterAndSetter(x: string) { } // doesn't match getter (OK) + + propOverriddenWithGetter: number; + get propOverriddenWithGetter() { return "hello"; } + + propOverriddenWithSetter: number; + set propOverriddenWithSetter(x: string) { } +}; + +var foo = new Foo(); + +// Test getting properties with getters +var testGetterNoError1: number = foo.goodGetterNoAnnotation; +var testGetterNoError2: number = foo.goodGetterWithAnnotation; + +var testGetterWithError1: string = foo.goodGetterNoAnnotation; // Error number ~> string +var testGetterWithError2: string = foo.goodGetterWithAnnotation; // Error number ~> string + +// Test setting properties with getters +foo.goodSetterNoAnnotation = 123; +foo.goodSetterWithAnnotation = 123; + +// TODO: Why does no annotation mean no error? +foo.goodSetterNoAnnotation = "hello"; // Error string ~> number +foo.goodSetterWithAnnotation = "hello"; // Error string ~> number + +var testSubtypingGetterAndSetter: number = foo.propWithSubtypingGetterAndSetter; // Error ?number ~> number + +var testPropOverridenWithGetter: number = foo.propOverriddenWithGetter; // Error string ~> number +foo.propOverriddenWithSetter = 123; // Error number ~> string diff --git a/tests/getters_and_setters_enabled/jsfmt.spec.js b/tests/getters_and_setters_enabled/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/getters_and_setters_enabled/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/getters_and_setters_enabled/object.js b/tests/getters_and_setters_enabled/object.js new file mode 100644 index 000000000000..5e54f25f40dc --- /dev/null +++ b/tests/getters_and_setters_enabled/object.js @@ -0,0 +1,60 @@ +/** + * @flow + */ + +var z: number = 123; + +class A {} +class B extends A {} +class C extends A {} + +var obj = { + get goodGetterNoAnnotation() { return 4; }, + get goodGetterWithAnnotation(): number { return 4; }, + + set goodSetterNoAnnotation(x) { z = x; }, + set goodSetterWithAnnotation(x: number) { z = x; }, + + get propWithMatchingGetterAndSetter(): number { return 4; }, + set propWithMatchingGetterAndSetter(x: number) { }, + + // The getter and setter need not have the same type + get propWithSubtypingGetterAndSetter(): ?number { return 4; }, // OK + set propWithSubtypingGetterAndSetter(x: number) { }, + + set propWithSubtypingGetterAndSetterReordered(x: number) { }, // OK + get propWithSubtypingGetterAndSetterReordered(): ?number { return 4; }, + + get exampleOfOrderOfGetterAndSetter(): A { return new A(); }, + set exampleOfOrderOfGetterAndSetter(x: B) {}, + + set exampleOfOrderOfGetterAndSetterReordered(x: B) {}, + get exampleOfOrderOfGetterAndSetterReordered(): A { return new A(); }, +}; + + + +// Test getting properties with getters +var testGetterNoError1: number = obj.goodGetterNoAnnotation; +var testGetterNoError2: number = obj.goodGetterWithAnnotation; + +var testGetterWithError1: string = obj.goodGetterNoAnnotation; // Error number ~> string +var testGetterWithError2: string = obj.goodGetterWithAnnotation; // Error number ~> string + +// Test setting properties with getters +obj.goodSetterNoAnnotation = 123; +obj.goodSetterWithAnnotation = 123; + +obj.goodSetterNoAnnotation = "hello"; // Error string ~> number +obj.goodSetterWithAnnotation = "hello"; // Error string ~> number + +var testSubtypingGetterAndSetter: number = obj.propWithSubtypingGetterAndSetter; // Error ?number ~> number + +// When building this feature, it was tempting to flow the setter into the +// getter and then use either the getter or setter as the type of the property. +// This example shows the danger of using the getter's type +obj.exampleOfOrderOfGetterAndSetter = new C(); // Error C ~> B + +// And this example shows the danger of using the setter's type. +var testExampleOrOrderOfGetterAndSetterReordered: number = + obj.exampleOfOrderOfGetterAndSetterReordered; // Error A ~> B diff --git a/tests/getters_and_setters_enabled/react.js b/tests/getters_and_setters_enabled/react.js new file mode 100644 index 000000000000..811acd9ec376 --- /dev/null +++ b/tests/getters_and_setters_enabled/react.js @@ -0,0 +1,11 @@ +/** + * @flow + */ + +React.createClass({ + propTypes: { + get a() { return 4; }, + set b(x: number) { this.c = x; }, + c: 10, + } +}); diff --git a/tests/getters_and_setters_enabled/variance.js b/tests/getters_and_setters_enabled/variance.js new file mode 100644 index 000000000000..8337453d57d9 --- /dev/null +++ b/tests/getters_and_setters_enabled/variance.js @@ -0,0 +1,70 @@ +/* @flow */ + +class A {} +class B extends A {} +class C extends B {} + +declare var a: A; +declare var b: B; +declare var c: C; + +class Base { + x: B; + +pos: B; + -neg: B; + get get(): B { return this.x }; + set set(value: B): void { this.x = value }; + get getset(): B { return this.x }; + set getset(value: B): void { this.x = value }; +} + +(class extends Base { + // error: getter incompatible with read/write property + get x(): B { return b } +}); + +(class extends Base { + // error: setter incompatible with read/write property + set x(value: B): void {} +}); + +(class extends Base { + // ok: get/set co/contra with read/write property, resp. + get x(): C { return c } + set x(value: A): void {} +}); + +(class extends Base { + // error: setter incompatible with read-only property + set pos(value: B): void {} +}); + +(class extends Base { + // ok: getter covariant with read-only property + get pos(): C { return c } +}); + +(class extends Base { + // error: getter incompatible with write-only property + get neg(): B { return b } +}); + +(class extends Base { + // ok: setter contravariant with write-only property + set neg(value: A): void {} +}); + +(class extends Base { + // ok: read/write covariant with getter + get: C; +}); + +(class extends Base { + // ok: read/write contravariant with setter + set: A; +}); + +(class extends Base { + // ok: read/write invariant with get/set + getset: B; +}); diff --git a/tests/haste_cycle/API.js b/tests/haste_cycle/API.js new file mode 100644 index 000000000000..9224269fc476 --- /dev/null +++ b/tests/haste_cycle/API.js @@ -0,0 +1,3 @@ +// @flow + +var OpenGraphObject = require('./models/OpenGraphObject.js'); diff --git a/tests/haste_cycle/__snapshots__/jsfmt.spec.js.snap b/tests/haste_cycle/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..a6433431534f --- /dev/null +++ b/tests/haste_cycle/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,10 @@ +exports[`test API.js 1`] = ` +"// @flow + +var OpenGraphObject = require('./models/OpenGraphObject.js'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +var OpenGraphObject = require("./models/OpenGraphObject.js"); + +" +`; diff --git a/tests/haste_cycle/jsfmt.spec.js b/tests/haste_cycle/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/haste_cycle/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/haste_cycle/models/OpenGraphAction.js b/tests/haste_cycle/models/OpenGraphAction.js new file mode 100644 index 000000000000..364e413bb22f --- /dev/null +++ b/tests/haste_cycle/models/OpenGraphAction.js @@ -0,0 +1,3 @@ +// @flow + +var OpenGraphValueContainer = require('./OpenGraphValueContainer.js'); diff --git a/tests/haste_cycle/models/OpenGraphObject.js b/tests/haste_cycle/models/OpenGraphObject.js new file mode 100644 index 000000000000..364e413bb22f --- /dev/null +++ b/tests/haste_cycle/models/OpenGraphObject.js @@ -0,0 +1,3 @@ +// @flow + +var OpenGraphValueContainer = require('./OpenGraphValueContainer.js'); diff --git a/tests/haste_cycle/models/OpenGraphValueContainer.js b/tests/haste_cycle/models/OpenGraphValueContainer.js new file mode 100644 index 000000000000..24d28abd4d28 --- /dev/null +++ b/tests/haste_cycle/models/OpenGraphValueContainer.js @@ -0,0 +1,3 @@ +// @flow + +var OpenGraphObject = require('./OpenGraphObject.js'); diff --git a/tests/haste_cycle/models/__snapshots__/jsfmt.spec.js.snap b/tests/haste_cycle/models/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..9b3493adb074 --- /dev/null +++ b/tests/haste_cycle/models/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,32 @@ +exports[`test OpenGraphAction.js 1`] = ` +"// @flow + +var OpenGraphValueContainer = require('./OpenGraphValueContainer.js'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +var OpenGraphValueContainer = require("./OpenGraphValueContainer.js"); + +" +`; + +exports[`test OpenGraphObject.js 1`] = ` +"// @flow + +var OpenGraphValueContainer = require('./OpenGraphValueContainer.js'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +var OpenGraphValueContainer = require("./OpenGraphValueContainer.js"); + +" +`; + +exports[`test OpenGraphValueContainer.js 1`] = ` +"// @flow + +var OpenGraphObject = require('./OpenGraphObject.js'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +var OpenGraphObject = require("./OpenGraphObject.js"); + +" +`; diff --git a/tests/haste_cycle/models/jsfmt.spec.js b/tests/haste_cycle/models/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/haste_cycle/models/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/haste_dupe/__snapshots__/jsfmt.spec.js.snap b/tests/haste_dupe/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..88db0d310415 --- /dev/null +++ b/tests/haste_dupe/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,51 @@ +exports[`test dupe1.js 1`] = ` +"/** + * Dupe provider 1/2 + * @providesModule Dupe + * @flow + */ +module.exports = "dupe1"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * Dupe provider 1/2 + * @providesModule Dupe + * @flow + */ +module.exports = "dupe1"; + +" +`; + +exports[`test dupe2.js 1`] = ` +"/** + * Dupe provider 2/2 + * @providesModule Dupe + * @flow + */ +module.exports = "dupe2"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * Dupe provider 2/2 + * @providesModule Dupe + * @flow + */ +module.exports = "dupe2"; + +" +`; + +exports[`test requires_dupe.js 1`] = ` +"/** + * depends on doubly-provided module + * @flow + */ +var dupe = require('Dupe'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * depends on doubly-provided module + * @flow + */ +var dupe = require("Dupe"); + +" +`; diff --git a/tests/haste_dupe/dupe1.js b/tests/haste_dupe/dupe1.js new file mode 100644 index 000000000000..1a298b67eae7 --- /dev/null +++ b/tests/haste_dupe/dupe1.js @@ -0,0 +1,6 @@ +/** + * Dupe provider 1/2 + * @providesModule Dupe + * @flow + */ +module.exports = "dupe1"; diff --git a/tests/haste_dupe/dupe2.js b/tests/haste_dupe/dupe2.js new file mode 100644 index 000000000000..aa64ae687dd8 --- /dev/null +++ b/tests/haste_dupe/dupe2.js @@ -0,0 +1,6 @@ +/** + * Dupe provider 2/2 + * @providesModule Dupe + * @flow + */ +module.exports = "dupe2"; diff --git a/tests/haste_dupe/jsfmt.spec.js b/tests/haste_dupe/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/haste_dupe/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/haste_dupe/requires_dupe.js b/tests/haste_dupe/requires_dupe.js new file mode 100644 index 000000000000..129992b7ab05 --- /dev/null +++ b/tests/haste_dupe/requires_dupe.js @@ -0,0 +1,5 @@ +/** + * depends on doubly-provided module + * @flow + */ +var dupe = require('Dupe'); diff --git a/tests/ignore_package/__snapshots__/jsfmt.spec.js.snap b/tests/ignore_package/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..365defe04de3 --- /dev/null +++ b/tests/ignore_package/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,31 @@ +exports[`test foo.js 1`] = ` +"/** @flow */ +/* this require routes to nothing, because + our node_modules/underscore directory + has been excluded. If package.json files + are being excluded properly, we'll get + 'Required module not found'. + */ +var _ = require('underscore'); + +function foo(): string { + return _.foo(); +} + +module.exports = foo; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** @flow */ +/* this require routes to nothing, because + our node_modules/underscore directory + has been excluded. If package.json files + are being excluded properly, we'll get + 'Required module not found'. + */ +var _ = require("underscore"); +function foo(): string { + return _.foo(); +} +module.exports = foo; + +" +`; diff --git a/tests/ignore_package/foo.js b/tests/ignore_package/foo.js new file mode 100644 index 000000000000..ff3a3366ac03 --- /dev/null +++ b/tests/ignore_package/foo.js @@ -0,0 +1,14 @@ +/** @flow */ +/* this require routes to nothing, because + our node_modules/underscore directory + has been excluded. If package.json files + are being excluded properly, we'll get + 'Required module not found'. + */ +var _ = require('underscore'); + +function foo(): string { + return _.foo(); +} + +module.exports = foo; diff --git a/tests/ignore_package/jsfmt.spec.js b/tests/ignore_package/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/ignore_package/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/immutable_methods/__snapshots__/jsfmt.spec.js.snap b/tests/immutable_methods/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..7365d8ee4758 --- /dev/null +++ b/tests/immutable_methods/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,29 @@ +exports[`test test.js 1`] = ` +"class A { + foo(): A { return this; } +} +class B extends A { + foo(): B { return this; } +} +class C extends A {} +var a: A = new B(); +a.foo = function(): C { return new C(); } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class A { + foo(): A { + return this; + } +} +class B extends A { + foo(): B { + return this; + } +} +class C extends A {} +var a: A = new B(); +a.foo = function(): C { + return new C(); +}; + +" +`; diff --git a/tests/immutable_methods/jsfmt.spec.js b/tests/immutable_methods/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/immutable_methods/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/immutable_methods/test.js b/tests/immutable_methods/test.js new file mode 100644 index 000000000000..2278239a27d9 --- /dev/null +++ b/tests/immutable_methods/test.js @@ -0,0 +1,9 @@ +class A { + foo(): A { return this; } +} +class B extends A { + foo(): B { return this; } +} +class C extends A {} +var a: A = new B(); +a.foo = function(): C { return new C(); } diff --git a/tests/import_type/ExportCJSDefault_Class.js b/tests/import_type/ExportCJSDefault_Class.js new file mode 100644 index 000000000000..dbbb5594a550 --- /dev/null +++ b/tests/import_type/ExportCJSDefault_Class.js @@ -0,0 +1,12 @@ +/** + * @flow + */ + +class ClassFoo3 { + givesANum(): number { return 42; } + static givesAFoo3(): ClassFoo3 { + return new ClassFoo3(); + } +} + +module.exports = ClassFoo3; diff --git a/tests/import_type/ExportCJSNamed_Class.js b/tests/import_type/ExportCJSNamed_Class.js new file mode 100644 index 000000000000..5a6ff405708a --- /dev/null +++ b/tests/import_type/ExportCJSNamed_Class.js @@ -0,0 +1,22 @@ +/** + * @flow + */ + +class ClassFoo4 { + returnsANumber(): number { return 42; } +} + +class ClassFoo5 {} + +function givesAFoo4(): ClassFoo4 { + return new ClassFoo4(); +} + +function givesAFoo5(): ClassFoo5 { + return new ClassFoo5(); +} + +exports.ClassFoo4 = ClassFoo4; +exports.ClassFoo5 = ClassFoo5 +exports.foo4Inst = new ClassFoo4(); +exports.foo5Inst = new ClassFoo5(); diff --git a/tests/import_type/ExportDefault_Class.js b/tests/import_type/ExportDefault_Class.js new file mode 100644 index 000000000000..9fe9fb3dc2e7 --- /dev/null +++ b/tests/import_type/ExportDefault_Class.js @@ -0,0 +1,10 @@ +/** + * @flow + */ + +class ClassFoo1 { + returnsANumber(): number { return 42; } +} + +export default ClassFoo1; +export var foo1Inst = new ClassFoo1(); diff --git a/tests/import_type/ExportNamed_Alias.js b/tests/import_type/ExportNamed_Alias.js new file mode 100644 index 000000000000..0f8c0f810195 --- /dev/null +++ b/tests/import_type/ExportNamed_Alias.js @@ -0,0 +1,12 @@ +/** + * @flow + */ + +export type AliasFoo3 = { + givesANum(): number +}; +export function givesAFoo3Obj(): AliasFoo3 { + return { + givesANum(): number { return 42; } + }; +}; diff --git a/tests/import_type/ExportNamed_Class.js b/tests/import_type/ExportNamed_Class.js new file mode 100644 index 000000000000..e5486eec5430 --- /dev/null +++ b/tests/import_type/ExportNamed_Class.js @@ -0,0 +1,10 @@ +/** + * @flow + */ + +class ClassFoo2 { + returnsANumber(): number { return 42; } +} + +export {ClassFoo2}; +export var foo2Inst = new ClassFoo2(); diff --git a/tests/import_type/ExportsANumber.js b/tests/import_type/ExportsANumber.js new file mode 100644 index 000000000000..bd4ab83a20e3 --- /dev/null +++ b/tests/import_type/ExportsANumber.js @@ -0,0 +1,3 @@ +/* @ flow */ + +export var numValue = 42; diff --git a/tests/import_type/__snapshots__/jsfmt.spec.js.snap b/tests/import_type/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..86121979ecbe --- /dev/null +++ b/tests/import_type/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,340 @@ +exports[`test ExportCJSDefault_Class.js 1`] = ` +"/** + * @flow + */ + +class ClassFoo3 { + givesANum(): number { return 42; } + static givesAFoo3(): ClassFoo3 { + return new ClassFoo3(); + } +} + +module.exports = ClassFoo3; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +class ClassFoo3 { + givesANum(): number { + return 42; + } + givesAFoo3(): ClassFoo3 { + return new ClassFoo3(); + } +} +module.exports = ClassFoo3; + +" +`; + +exports[`test ExportCJSNamed_Class.js 1`] = ` +"/** + * @flow + */ + +class ClassFoo4 { + returnsANumber(): number { return 42; } +} + +class ClassFoo5 {} + +function givesAFoo4(): ClassFoo4 { + return new ClassFoo4(); +} + +function givesAFoo5(): ClassFoo5 { + return new ClassFoo5(); +} + +exports.ClassFoo4 = ClassFoo4; +exports.ClassFoo5 = ClassFoo5 +exports.foo4Inst = new ClassFoo4(); +exports.foo5Inst = new ClassFoo5(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +class ClassFoo4 { + returnsANumber(): number { + return 42; + } +} +class ClassFoo5 {} +function givesAFoo4(): ClassFoo4 { + return new ClassFoo4(); +} +function givesAFoo5(): ClassFoo5 { + return new ClassFoo5(); +} +exports.ClassFoo4 = ClassFoo4; +exports.ClassFoo5 = ClassFoo5; +exports.foo4Inst = new ClassFoo4(); +exports.foo5Inst = new ClassFoo5(); + +" +`; + +exports[`test ExportDefault_Class.js 1`] = ` +"/** + * @flow + */ + +class ClassFoo1 { + returnsANumber(): number { return 42; } +} + +export default ClassFoo1; +export var foo1Inst = new ClassFoo1(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +class ClassFoo1 { + returnsANumber(): number { + return 42; + } +} +export default ClassFoo1 +export var foo1Inst = new ClassFoo1(); + +" +`; + +exports[`test ExportNamed_Alias.js 1`] = ` +"/** + * @flow + */ + +export type AliasFoo3 = { + givesANum(): number +}; +export function givesAFoo3Obj(): AliasFoo3 { + return { + givesANum(): number { return 42; } + }; +}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test ExportNamed_Class.js 1`] = ` +"/** + * @flow + */ + +class ClassFoo2 { + returnsANumber(): number { return 42; } +} + +export {ClassFoo2}; +export var foo2Inst = new ClassFoo2(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1785 + fromString(\", \").join(path.map(print, \"specifiers\")), + ^ + +TypeError: fromString(...).join is not a function + at printExportDeclaration (/src/printer.js:1785:26) + at genericPrintNoParens (/src/printer.js:488:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:238:16 +" +`; + +exports[`test ExportsANumber.js 1`] = ` +"/* @ flow */ + +export var numValue = 42; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @ flow */ +export var numValue = 42; + +" +`; + +exports[`test import_type.js 1`] = ` +"/** + * @flow + */ + +///////////////////////////////////////////////// +// == Importing Class Type (Default Export) == // +///////////////////////////////////////////////// + +import type ClassFoo1 from \"./ExportDefault_Class\"; +import {foo1Inst} from \"./ExportDefault_Class\"; + +var a1: ClassFoo1 = foo1Inst; +var a2: number = foo1Inst; // Error: ClassFoo1 ~> number +new ClassFoo1(); // Error: ClassFoo1 is not a value-identifier + +/////////////////////////////////////////////// +// == Importing Class Type (Named Export) == // +/////////////////////////////////////////////// + +import type {ClassFoo2} from \"./ExportNamed_Class\"; +import {foo2Inst} from \"./ExportNamed_Class\"; + +var b1: ClassFoo2 = foo2Inst; +var b2: number = foo2Inst; // Error: ClassFoo2 ~> number +new ClassFoo2(); // Error: ClassFoo2 is not a value-identifier + +///////////////////////////////////////////////////// +// == Importing Class Type (CJS Default Export) == // +///////////////////////////////////////////////////// + +import type ClassFoo3T from \"./ExportCJSDefault_Class\"; +import ClassFoo3 from \"./ExportCJSDefault_Class\"; +var c1: ClassFoo3T = new ClassFoo3(); +new ClassFoo3T(); // Error: ClassFoo3 is not a value-identifier + +/////////////////////////////////////////////////// +// == Importing Class Type (CJS Named Export) == // +/////////////////////////////////////////////////// + +import type {ClassFoo4, ClassFoo5} from \"./ExportCJSNamed_Class\"; +import {foo4Inst, foo5Inst} from \"./ExportCJSNamed_Class\"; + +var d1: ClassFoo4 = foo4Inst; +var d2: number = foo4Inst; // Error: ClassFoo4 ~> number +new ClassFoo4(); // Error: ClassFoo4 is not a value-identifier +// TODO: this errors correctly, but the message is just \'can\'t resolve name\' +var d3: typeof ClassFoo5 = foo5Inst; // Error: Can\'t typeof a type alias + +//////////////////////////////////////////// +// == Import Type Alias (Named Export) == // +//////////////////////////////////////////// + +import type {AliasFoo3} from \"./ExportNamed_Alias\"; +import {givesAFoo3Obj} from \"./ExportNamed_Alias\"; +var e1: AliasFoo3 = givesAFoo3Obj(); +var e2: number = givesAFoo3Obj(); // Error: AliasFoo3 ~> number +var e3: typeof AliasFoo3 = givesAFoo3Obj(); // Error: Can\'t typeof a type alias + +////////////////////////////////////////////// +// == Import Type Alias (Default Export) == // +////////////////////////////////////////////// + +// TODO: No support for this right now. It\'s most likely possible, but it\'s +// unclear how useful it is at the moment and it entails a little +// more work than named type exports, so I\'m punting on it for now. + +/////////////////////////////////////////////////////// +// == Import Type With Non-Alias Compatible Value == // +/////////////////////////////////////////////////////// + +import type {numValue} from \"./ExportsANumber\"; // Error: Cannot import-type a number value + +//////////////////////////////////////////////////////////////////////// +// == Regression Test: https://github.com/facebook/flow/issues/359 == // +// Ensure that type bindings stay type bindings across function body // +// env contexts. // +//////////////////////////////////////////////////////////////////////// + +import type ClassFoo6 from \"./issue-359\"; +function foo() { + ClassFoo6; // Error: Not a value binding +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +///////////////////////////////////////////////// +// == Importing Class Type (Default Export) == // +///////////////////////////////////////////////// +import type ClassFoo1 from \"./ExportDefault_Class\"; +import { foo1Inst } from \"./ExportDefault_Class\"; +var a1: ClassFoo1 = foo1Inst; +var a2: number = foo1Inst;// Error: ClassFoo1 ~> number +new ClassFoo1();// Error: ClassFoo1 is not a value-identifier +/////////////////////////////////////////////// +// == Importing Class Type (Named Export) == // +/////////////////////////////////////////////// +import type { ClassFoo2 } from \"./ExportNamed_Class\"; +import { foo2Inst } from \"./ExportNamed_Class\"; +var b1: ClassFoo2 = foo2Inst; +var b2: number = foo2Inst;// Error: ClassFoo2 ~> number +new ClassFoo2();// Error: ClassFoo2 is not a value-identifier +///////////////////////////////////////////////////// +// == Importing Class Type (CJS Default Export) == // +///////////////////////////////////////////////////// +import type ClassFoo3T from \"./ExportCJSDefault_Class\"; +import ClassFoo3 from \"./ExportCJSDefault_Class\"; +var c1: ClassFoo3T = new ClassFoo3(); +new ClassFoo3T();// Error: ClassFoo3 is not a value-identifier +/////////////////////////////////////////////////// +// == Importing Class Type (CJS Named Export) == // +/////////////////////////////////////////////////// +import type { ClassFoo4, ClassFoo5 } from \"./ExportCJSNamed_Class\"; +import { foo4Inst, foo5Inst } from \"./ExportCJSNamed_Class\"; +var d1: ClassFoo4 = foo4Inst; +var d2: number = foo4Inst;// Error: ClassFoo4 ~> number +new ClassFoo4();// Error: ClassFoo4 is not a value-identifier +// TODO: this errors correctly, but the message is just \'can\'t resolve name\' +var d3: typeof ClassFoo5 = foo5Inst;// Error: Can\'t typeof a type alias +//////////////////////////////////////////// +// == Import Type Alias (Named Export) == // +//////////////////////////////////////////// +import type { AliasFoo3 } from \"./ExportNamed_Alias\"; +import { givesAFoo3Obj } from \"./ExportNamed_Alias\"; +var e1: AliasFoo3 = givesAFoo3Obj(); +var e2: number = givesAFoo3Obj();// Error: AliasFoo3 ~> number +var e3: typeof AliasFoo3 = givesAFoo3Obj();// Error: Can\'t typeof a type alias +////////////////////////////////////////////// +// == Import Type Alias (Default Export) == // +////////////////////////////////////////////// +// TODO: No support for this right now. It\'s most likely possible, but it\'s +// unclear how useful it is at the moment and it entails a little +// more work than named type exports, so I\'m punting on it for now. +/////////////////////////////////////////////////////// +// == Import Type With Non-Alias Compatible Value == // +/////////////////////////////////////////////////////// +import type { numValue } from \"./ExportsANumber\";// Error: Cannot import-type a number value +//////////////////////////////////////////////////////////////////////// +// == Regression Test: https://github.com/facebook/flow/issues/359 == // +// Ensure that type bindings stay type bindings across function body // +// env contexts. // +//////////////////////////////////////////////////////////////////////// +import type ClassFoo6 from \"./issue-359\"; +function foo() { + ClassFoo6;// Error: Not a value binding +} + +" +`; + +exports[`test issue-359.js 1`] = ` +"/* @flow */ + +class ClassFoo6 {}; + +export default ClassFoo6; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +class ClassFoo6 {} +export default ClassFoo6 + +" +`; diff --git a/tests/import_type/import_type.js b/tests/import_type/import_type.js new file mode 100644 index 000000000000..96ec2be84ea4 --- /dev/null +++ b/tests/import_type/import_type.js @@ -0,0 +1,82 @@ +/** + * @flow + */ + +///////////////////////////////////////////////// +// == Importing Class Type (Default Export) == // +///////////////////////////////////////////////// + +import type ClassFoo1 from "./ExportDefault_Class"; +import {foo1Inst} from "./ExportDefault_Class"; + +var a1: ClassFoo1 = foo1Inst; +var a2: number = foo1Inst; // Error: ClassFoo1 ~> number +new ClassFoo1(); // Error: ClassFoo1 is not a value-identifier + +/////////////////////////////////////////////// +// == Importing Class Type (Named Export) == // +/////////////////////////////////////////////// + +import type {ClassFoo2} from "./ExportNamed_Class"; +import {foo2Inst} from "./ExportNamed_Class"; + +var b1: ClassFoo2 = foo2Inst; +var b2: number = foo2Inst; // Error: ClassFoo2 ~> number +new ClassFoo2(); // Error: ClassFoo2 is not a value-identifier + +///////////////////////////////////////////////////// +// == Importing Class Type (CJS Default Export) == // +///////////////////////////////////////////////////// + +import type ClassFoo3T from "./ExportCJSDefault_Class"; +import ClassFoo3 from "./ExportCJSDefault_Class"; +var c1: ClassFoo3T = new ClassFoo3(); +new ClassFoo3T(); // Error: ClassFoo3 is not a value-identifier + +/////////////////////////////////////////////////// +// == Importing Class Type (CJS Named Export) == // +/////////////////////////////////////////////////// + +import type {ClassFoo4, ClassFoo5} from "./ExportCJSNamed_Class"; +import {foo4Inst, foo5Inst} from "./ExportCJSNamed_Class"; + +var d1: ClassFoo4 = foo4Inst; +var d2: number = foo4Inst; // Error: ClassFoo4 ~> number +new ClassFoo4(); // Error: ClassFoo4 is not a value-identifier +// TODO: this errors correctly, but the message is just 'can't resolve name' +var d3: typeof ClassFoo5 = foo5Inst; // Error: Can't typeof a type alias + +//////////////////////////////////////////// +// == Import Type Alias (Named Export) == // +//////////////////////////////////////////// + +import type {AliasFoo3} from "./ExportNamed_Alias"; +import {givesAFoo3Obj} from "./ExportNamed_Alias"; +var e1: AliasFoo3 = givesAFoo3Obj(); +var e2: number = givesAFoo3Obj(); // Error: AliasFoo3 ~> number +var e3: typeof AliasFoo3 = givesAFoo3Obj(); // Error: Can't typeof a type alias + +////////////////////////////////////////////// +// == Import Type Alias (Default Export) == // +////////////////////////////////////////////// + +// TODO: No support for this right now. It's most likely possible, but it's +// unclear how useful it is at the moment and it entails a little +// more work than named type exports, so I'm punting on it for now. + +/////////////////////////////////////////////////////// +// == Import Type With Non-Alias Compatible Value == // +/////////////////////////////////////////////////////// + +import type {numValue} from "./ExportsANumber"; // Error: Cannot import-type a number value + +//////////////////////////////////////////////////////////////////////// +// == Regression Test: https://github.com/facebook/flow/issues/359 == // +// Ensure that type bindings stay type bindings across function body // +// env contexts. // +//////////////////////////////////////////////////////////////////////// + +import type ClassFoo6 from "./issue-359"; +function foo() { + ClassFoo6; // Error: Not a value binding +} diff --git a/tests/import_type/issue-359.js b/tests/import_type/issue-359.js new file mode 100644 index 000000000000..ee16df3468f4 --- /dev/null +++ b/tests/import_type/issue-359.js @@ -0,0 +1,5 @@ +/* @flow */ + +class ClassFoo6 {}; + +export default ClassFoo6; diff --git a/tests/import_type/jsfmt.spec.js b/tests/import_type/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/import_type/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/import_typeof/ExportCJSDefault_Class.js b/tests/import_typeof/ExportCJSDefault_Class.js new file mode 100644 index 000000000000..dbbb5594a550 --- /dev/null +++ b/tests/import_typeof/ExportCJSDefault_Class.js @@ -0,0 +1,12 @@ +/** + * @flow + */ + +class ClassFoo3 { + givesANum(): number { return 42; } + static givesAFoo3(): ClassFoo3 { + return new ClassFoo3(); + } +} + +module.exports = ClassFoo3; diff --git a/tests/import_typeof/ExportCJSDefault_Number.js b/tests/import_typeof/ExportCJSDefault_Number.js new file mode 100644 index 000000000000..a659089f56f5 --- /dev/null +++ b/tests/import_typeof/ExportCJSDefault_Number.js @@ -0,0 +1,3 @@ +/* @flow */ + +module.exports = 42; diff --git a/tests/import_typeof/ExportCJSNamed_Class.js b/tests/import_typeof/ExportCJSNamed_Class.js new file mode 100644 index 000000000000..d62df9161ea5 --- /dev/null +++ b/tests/import_typeof/ExportCJSNamed_Class.js @@ -0,0 +1,7 @@ +/** + * @flow + */ + +class ClassFoo4 {} + +exports.ClassFoo4 = ClassFoo4; diff --git a/tests/import_typeof/ExportCJSNamed_Number.js b/tests/import_typeof/ExportCJSNamed_Number.js new file mode 100644 index 000000000000..9341801928fd --- /dev/null +++ b/tests/import_typeof/ExportCJSNamed_Number.js @@ -0,0 +1,3 @@ +/* @flow */ + +exports.num = 42; diff --git a/tests/import_typeof/ExportDefault_Class.js b/tests/import_typeof/ExportDefault_Class.js new file mode 100644 index 000000000000..04777aa3fa28 --- /dev/null +++ b/tests/import_typeof/ExportDefault_Class.js @@ -0,0 +1,9 @@ +/** + * @flow + */ + +class ClassFoo1 { + returnsANumber(): number { return 42; } +} + +export default ClassFoo1; diff --git a/tests/import_typeof/ExportDefault_Number.js b/tests/import_typeof/ExportDefault_Number.js new file mode 100644 index 000000000000..4f63aba00b99 --- /dev/null +++ b/tests/import_typeof/ExportDefault_Number.js @@ -0,0 +1,3 @@ +/* @flow */ + +export default 42; diff --git a/tests/import_typeof/ExportNamed_Alias.js b/tests/import_typeof/ExportNamed_Alias.js new file mode 100644 index 000000000000..0f8c0f810195 --- /dev/null +++ b/tests/import_typeof/ExportNamed_Alias.js @@ -0,0 +1,12 @@ +/** + * @flow + */ + +export type AliasFoo3 = { + givesANum(): number +}; +export function givesAFoo3Obj(): AliasFoo3 { + return { + givesANum(): number { return 42; } + }; +}; diff --git a/tests/import_typeof/ExportNamed_Class.js b/tests/import_typeof/ExportNamed_Class.js new file mode 100644 index 000000000000..90ee78a0b440 --- /dev/null +++ b/tests/import_typeof/ExportNamed_Class.js @@ -0,0 +1,9 @@ +/** + * @flow + */ + +class ClassFoo2 { + returnsANumber(): number { return 42; } +} + +export {ClassFoo2}; diff --git a/tests/import_typeof/ExportNamed_Multi.js b/tests/import_typeof/ExportNamed_Multi.js new file mode 100644 index 000000000000..91f96c936d6f --- /dev/null +++ b/tests/import_typeof/ExportNamed_Multi.js @@ -0,0 +1,4 @@ +// @flow + +export var num = 42; +export var str = 'asdf'; diff --git a/tests/import_typeof/ExportNamed_Number.js b/tests/import_typeof/ExportNamed_Number.js new file mode 100644 index 000000000000..dc4b2ec97d0d --- /dev/null +++ b/tests/import_typeof/ExportNamed_Number.js @@ -0,0 +1,3 @@ +/* @flow */ + +export var num = 42; diff --git a/tests/import_typeof/__snapshots__/jsfmt.spec.js.snap b/tests/import_typeof/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..5eade817f7a9 --- /dev/null +++ b/tests/import_typeof/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,371 @@ +exports[`test ExportCJSDefault_Class.js 1`] = ` +"/** + * @flow + */ + +class ClassFoo3 { + givesANum(): number { return 42; } + static givesAFoo3(): ClassFoo3 { + return new ClassFoo3(); + } +} + +module.exports = ClassFoo3; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +class ClassFoo3 { + givesANum(): number { + return 42; + } + givesAFoo3(): ClassFoo3 { + return new ClassFoo3(); + } +} +module.exports = ClassFoo3; + +" +`; + +exports[`test ExportCJSDefault_Number.js 1`] = ` +"/* @flow */ + +module.exports = 42; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +module.exports = 42; + +" +`; + +exports[`test ExportCJSNamed_Class.js 1`] = ` +"/** + * @flow + */ + +class ClassFoo4 {} + +exports.ClassFoo4 = ClassFoo4; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +class ClassFoo4 {} +exports.ClassFoo4 = ClassFoo4; + +" +`; + +exports[`test ExportCJSNamed_Number.js 1`] = ` +"/* @flow */ + +exports.num = 42; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +exports.num = 42; + +" +`; + +exports[`test ExportDefault_Class.js 1`] = ` +"/** + * @flow + */ + +class ClassFoo1 { + returnsANumber(): number { return 42; } +} + +export default ClassFoo1; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +class ClassFoo1 { + returnsANumber(): number { + return 42; + } +} +export default ClassFoo1 + +" +`; + +exports[`test ExportDefault_Number.js 1`] = ` +"/* @flow */ + +export default 42; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +export default 42 + +" +`; + +exports[`test ExportNamed_Alias.js 1`] = ` +"/** + * @flow + */ + +export type AliasFoo3 = { + givesANum(): number +}; +export function givesAFoo3Obj(): AliasFoo3 { + return { + givesANum(): number { return 42; } + }; +}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test ExportNamed_Class.js 1`] = ` +"/** + * @flow + */ + +class ClassFoo2 { + returnsANumber(): number { return 42; } +} + +export {ClassFoo2}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1785 + fromString(\", \").join(path.map(print, \"specifiers\")), + ^ + +TypeError: fromString(...).join is not a function + at printExportDeclaration (/src/printer.js:1785:26) + at genericPrintNoParens (/src/printer.js:488:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:238:16 +" +`; + +exports[`test ExportNamed_Multi.js 1`] = ` +"// @flow + +export var num = 42; +export var str = \'asdf\'; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +export var num = 42; +export var str = \"asdf\"; + +" +`; + +exports[`test ExportNamed_Number.js 1`] = ` +"/* @flow */ + +export var num = 42; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +export var num = 42; + +" +`; + +exports[`test import_typeof.js 1`] = ` +"/** + * @flow + */ + +/////////////////////////////////////////////////// +// == Importing Class Typeof (Default Export) == // +/////////////////////////////////////////////////// + +import typeof ClassFoo1T from \"./ExportDefault_Class\"; +import ClassFoo1 from \"./ExportDefault_Class\"; + +var a1: ClassFoo1T = ClassFoo1; +var a2: ClassFoo1T = new ClassFoo1(); // Error: ClassFoo1 (inst) ~> ClassFoo1 (class) +new ClassFoo1T(); // Error: ClassFoo1T is not bound to a value + +///////////////////////////////////////////////// +// == Importing Class Typeof (Named Export) == // +///////////////////////////////////////////////// + +import typeof {ClassFoo2 as ClassFoo2T} from \"./ExportNamed_Class\"; +import {ClassFoo2} from \"./ExportNamed_Class\"; + +var b1: ClassFoo2T = ClassFoo2; +var b2: ClassFoo2T = new ClassFoo2(); // Error: ClassFoo2 (inst) ~> ClassFoo2 (class) +new ClassFoo2T(); // Error: ClassFoo2T is not bound to a value + +/////////////////////////////////////////////////////// +// == Importing Class Typeof (CJS Default Export) == // +/////////////////////////////////////////////////////// + +import typeof ClassFoo3T from \"./ExportCJSDefault_Class\"; +import ClassFoo3 from \"./ExportCJSDefault_Class\"; + +var c1: ClassFoo3T = ClassFoo3; +var c2: ClassFoo3T = new ClassFoo3(); // Error: ClassFoo3 (inst) ~> ClassFoo3 (class) + +///////////////////////////////////////////////////// +// == Importing Class Typeof (CJS Named Export) == // +///////////////////////////////////////////////////// + +import typeof {ClassFoo4 as ClassFoo4T} from \"./ExportCJSNamed_Class\"; +import {ClassFoo4} from \"./ExportCJSNamed_Class\"; + +var d1: ClassFoo4T = ClassFoo4; +var d2: ClassFoo4T = new ClassFoo4(); // Error: ClassFoo4 (inst) ~> ClassFoo4 (class) + +////////////////////////////////////////////// +// == Import Typeof Alias (Named Export) == // +////////////////////////////////////////////// + +import typeof {AliasFoo3} from \"./ExportNamed_Alias\"; // Error: Can\'t \`import typeof\` type aliases! + +//////////////////////////////////////////////// +// == Import Typeof Alias (Default Export) == // +//////////////////////////////////////////////// + +// TODO: No support for this right now. It\'s most likely possible, but it\'s +// unclear how useful it is at the moment and it entails a little +// more work than named type exports, so I\'m punting on it for now. + +/////////////////////////////////////////////////////////////// +// == Import Typeof With Non-Class Value (Default Export) == // +/////////////////////////////////////////////////////////////// + +import typeof num_default from \"./ExportDefault_Number\"; + +var f1: num_default = 42; +var f2: num_default = \'asdf\'; // Error: string ~> number + +///////////////////////////////////////////////////////////// +// == Import Typeof With Non-Class Value (Named Export) == // +///////////////////////////////////////////////////////////// + +import typeof {num as num_named} from \"./ExportNamed_Number\"; + +var g1: num_named = 42; +var g2: num_named = \'asdf\'; // Error: string ~> number + +/////////////////////////////////////////////////////////////////// +// == Import Typeof With Non-Class Value (CJS Default Export) == // +/////////////////////////////////////////////////////////////////// + +import typeof num_cjs_default from \"./ExportCJSDefault_Number\"; + +var h1: num_cjs_default = 42; +var h2: num_cjs_default = \'asdf\'; // Error: string ~> number + +///////////////////////////////////////////////////////////////// +// == Import Typeof With Non-Class Value (CJS Named Export) == // +///////////////////////////////////////////////////////////////// + +import typeof {num as num_cjs_named} from \"./ExportCJSNamed_Number\"; + +var i1: num_cjs_named = 42; +var i2: num_cjs_named = \'asdf\'; // Error: string ~> number + +/////////////////////////////////////////////// +// == Import Typeof ModuleNamespaceObject == // +/////////////////////////////////////////////// + +import typeof * as ModuleNSObjT from \"./ExportNamed_Multi\"; +var j1: ModuleNSObjT = {num: 42, str: \'asdf\'}; +var j2: ModuleNSObjT = {num: 42, str: 42}; // Error: number ~> string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +/////////////////////////////////////////////////// +// == Importing Class Typeof (Default Export) == // +/////////////////////////////////////////////////// +import typeof ClassFoo1T from \"./ExportDefault_Class\"; +import ClassFoo1 from \"./ExportDefault_Class\"; +var a1: ClassFoo1T = ClassFoo1; +var a2: ClassFoo1T = new ClassFoo1();// Error: ClassFoo1 (inst) ~> ClassFoo1 (class) +new ClassFoo1T();// Error: ClassFoo1T is not bound to a value +///////////////////////////////////////////////// +// == Importing Class Typeof (Named Export) == // +///////////////////////////////////////////////// +import typeof { ClassFoo2 as ClassFoo2T } from \"./ExportNamed_Class\"; +import { ClassFoo2 } from \"./ExportNamed_Class\"; +var b1: ClassFoo2T = ClassFoo2; +var b2: ClassFoo2T = new ClassFoo2();// Error: ClassFoo2 (inst) ~> ClassFoo2 (class) +new ClassFoo2T();// Error: ClassFoo2T is not bound to a value +/////////////////////////////////////////////////////// +// == Importing Class Typeof (CJS Default Export) == // +/////////////////////////////////////////////////////// +import typeof ClassFoo3T from \"./ExportCJSDefault_Class\"; +import ClassFoo3 from \"./ExportCJSDefault_Class\"; +var c1: ClassFoo3T = ClassFoo3; +var c2: ClassFoo3T = new ClassFoo3();// Error: ClassFoo3 (inst) ~> ClassFoo3 (class) +///////////////////////////////////////////////////// +// == Importing Class Typeof (CJS Named Export) == // +///////////////////////////////////////////////////// +import typeof { ClassFoo4 as ClassFoo4T } from \"./ExportCJSNamed_Class\"; +import { ClassFoo4 } from \"./ExportCJSNamed_Class\"; +var d1: ClassFoo4T = ClassFoo4; +var d2: ClassFoo4T = new ClassFoo4();// Error: ClassFoo4 (inst) ~> ClassFoo4 (class) +////////////////////////////////////////////// +// == Import Typeof Alias (Named Export) == // +////////////////////////////////////////////// +import typeof { AliasFoo3 } from \"./ExportNamed_Alias\";// Error: Can\'t \`import typeof\` type aliases! +//////////////////////////////////////////////// +// == Import Typeof Alias (Default Export) == // +//////////////////////////////////////////////// +// TODO: No support for this right now. It\'s most likely possible, but it\'s +// unclear how useful it is at the moment and it entails a little +// more work than named type exports, so I\'m punting on it for now. +/////////////////////////////////////////////////////////////// +// == Import Typeof With Non-Class Value (Default Export) == // +/////////////////////////////////////////////////////////////// +import typeof num_default from \"./ExportDefault_Number\"; +var f1: num_default = 42; +var f2: num_default = \"asdf\";// Error: string ~> number +///////////////////////////////////////////////////////////// +// == Import Typeof With Non-Class Value (Named Export) == // +///////////////////////////////////////////////////////////// +import typeof { num as num_named } from \"./ExportNamed_Number\"; +var g1: num_named = 42; +var g2: num_named = \"asdf\";// Error: string ~> number +/////////////////////////////////////////////////////////////////// +// == Import Typeof With Non-Class Value (CJS Default Export) == // +/////////////////////////////////////////////////////////////////// +import typeof num_cjs_default from \"./ExportCJSDefault_Number\"; +var h1: num_cjs_default = 42; +var h2: num_cjs_default = \"asdf\";// Error: string ~> number +///////////////////////////////////////////////////////////////// +// == Import Typeof With Non-Class Value (CJS Named Export) == // +///////////////////////////////////////////////////////////////// +import typeof { num as num_cjs_named } from \"./ExportCJSNamed_Number\"; +var i1: num_cjs_named = 42; +var i2: num_cjs_named = \"asdf\";// Error: string ~> number +/////////////////////////////////////////////// +// == Import Typeof ModuleNamespaceObject == // +/////////////////////////////////////////////// +import typeof * as ModuleNSObjT from \"./ExportNamed_Multi\"; +var j1: ModuleNSObjT = { num: 42, str: \"asdf\" }; +var j2: ModuleNSObjT = { num: 42, str: 42 };// Error: number ~> string + +" +`; diff --git a/tests/import_typeof/import_typeof.js b/tests/import_typeof/import_typeof.js new file mode 100644 index 000000000000..6916f800420c --- /dev/null +++ b/tests/import_typeof/import_typeof.js @@ -0,0 +1,103 @@ +/** + * @flow + */ + +/////////////////////////////////////////////////// +// == Importing Class Typeof (Default Export) == // +/////////////////////////////////////////////////// + +import typeof ClassFoo1T from "./ExportDefault_Class"; +import ClassFoo1 from "./ExportDefault_Class"; + +var a1: ClassFoo1T = ClassFoo1; +var a2: ClassFoo1T = new ClassFoo1(); // Error: ClassFoo1 (inst) ~> ClassFoo1 (class) +new ClassFoo1T(); // Error: ClassFoo1T is not bound to a value + +///////////////////////////////////////////////// +// == Importing Class Typeof (Named Export) == // +///////////////////////////////////////////////// + +import typeof {ClassFoo2 as ClassFoo2T} from "./ExportNamed_Class"; +import {ClassFoo2} from "./ExportNamed_Class"; + +var b1: ClassFoo2T = ClassFoo2; +var b2: ClassFoo2T = new ClassFoo2(); // Error: ClassFoo2 (inst) ~> ClassFoo2 (class) +new ClassFoo2T(); // Error: ClassFoo2T is not bound to a value + +/////////////////////////////////////////////////////// +// == Importing Class Typeof (CJS Default Export) == // +/////////////////////////////////////////////////////// + +import typeof ClassFoo3T from "./ExportCJSDefault_Class"; +import ClassFoo3 from "./ExportCJSDefault_Class"; + +var c1: ClassFoo3T = ClassFoo3; +var c2: ClassFoo3T = new ClassFoo3(); // Error: ClassFoo3 (inst) ~> ClassFoo3 (class) + +///////////////////////////////////////////////////// +// == Importing Class Typeof (CJS Named Export) == // +///////////////////////////////////////////////////// + +import typeof {ClassFoo4 as ClassFoo4T} from "./ExportCJSNamed_Class"; +import {ClassFoo4} from "./ExportCJSNamed_Class"; + +var d1: ClassFoo4T = ClassFoo4; +var d2: ClassFoo4T = new ClassFoo4(); // Error: ClassFoo4 (inst) ~> ClassFoo4 (class) + +////////////////////////////////////////////// +// == Import Typeof Alias (Named Export) == // +////////////////////////////////////////////// + +import typeof {AliasFoo3} from "./ExportNamed_Alias"; // Error: Can't `import typeof` type aliases! + +//////////////////////////////////////////////// +// == Import Typeof Alias (Default Export) == // +//////////////////////////////////////////////// + +// TODO: No support for this right now. It's most likely possible, but it's +// unclear how useful it is at the moment and it entails a little +// more work than named type exports, so I'm punting on it for now. + +/////////////////////////////////////////////////////////////// +// == Import Typeof With Non-Class Value (Default Export) == // +/////////////////////////////////////////////////////////////// + +import typeof num_default from "./ExportDefault_Number"; + +var f1: num_default = 42; +var f2: num_default = 'asdf'; // Error: string ~> number + +///////////////////////////////////////////////////////////// +// == Import Typeof With Non-Class Value (Named Export) == // +///////////////////////////////////////////////////////////// + +import typeof {num as num_named} from "./ExportNamed_Number"; + +var g1: num_named = 42; +var g2: num_named = 'asdf'; // Error: string ~> number + +/////////////////////////////////////////////////////////////////// +// == Import Typeof With Non-Class Value (CJS Default Export) == // +/////////////////////////////////////////////////////////////////// + +import typeof num_cjs_default from "./ExportCJSDefault_Number"; + +var h1: num_cjs_default = 42; +var h2: num_cjs_default = 'asdf'; // Error: string ~> number + +///////////////////////////////////////////////////////////////// +// == Import Typeof With Non-Class Value (CJS Named Export) == // +///////////////////////////////////////////////////////////////// + +import typeof {num as num_cjs_named} from "./ExportCJSNamed_Number"; + +var i1: num_cjs_named = 42; +var i2: num_cjs_named = 'asdf'; // Error: string ~> number + +/////////////////////////////////////////////// +// == Import Typeof ModuleNamespaceObject == // +/////////////////////////////////////////////// + +import typeof * as ModuleNSObjT from "./ExportNamed_Multi"; +var j1: ModuleNSObjT = {num: 42, str: 'asdf'}; +var j2: ModuleNSObjT = {num: 42, str: 42}; // Error: number ~> string diff --git a/tests/import_typeof/jsfmt.spec.js b/tests/import_typeof/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/import_typeof/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/include/foo/batman/__snapshots__/jsfmt.spec.js.snap b/tests/include/foo/batman/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..0716adec34e2 --- /dev/null +++ b/tests/include/foo/batman/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,10 @@ +exports[`test baz.js 1`] = ` +"/* @flow */ + +var x: number = "not a number" // Error string ~> number +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var x: number = "not a number";// Error string ~> number + +" +`; diff --git a/tests/include/foo/batman/baz.js b/tests/include/foo/batman/baz.js new file mode 100644 index 000000000000..d6b2eca30b2a --- /dev/null +++ b/tests/include/foo/batman/baz.js @@ -0,0 +1,3 @@ +/* @flow */ + +var x: number = "not a number" // Error string ~> number diff --git a/tests/include/foo/batman/jsfmt.spec.js b/tests/include/foo/batman/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/include/foo/batman/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/include/included/__snapshots__/jsfmt.spec.js.snap b/tests/include/included/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..0e05f7235a80 --- /dev/null +++ b/tests/include/included/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,7 @@ +exports[`test test.js 1`] = ` +"(123: string); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +(123: string); + +" +`; diff --git a/tests/include/included/jsfmt.spec.js b/tests/include/included/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/include/included/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/include/included/test.js b/tests/include/included/test.js new file mode 100644 index 000000000000..c41afeed67c2 --- /dev/null +++ b/tests/include/included/test.js @@ -0,0 +1 @@ +(123: string); diff --git a/tests/incremental/__snapshots__/jsfmt.spec.js.snap b/tests/incremental/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..51fa6eb132d4 --- /dev/null +++ b/tests/incremental/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,33 @@ +exports[`test a.js 1`] = ` +"/* @providesModule IncrModuleA */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; + +exports[`test b.js 1`] = ` +"/* @providesModule IncrModuleB + @flow +*/ + +var A = require('IncrModuleA'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @providesModule IncrModuleB + @flow +*/ +var A = require("IncrModuleA"); + +" +`; + +exports[`test dup_a.js 1`] = ` +"/* @providesModule IncrModuleA */ + +var x:string = 0; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @providesModule IncrModuleA */ +var x: string = 0; + +" +`; diff --git a/tests/incremental/a.js b/tests/incremental/a.js new file mode 100644 index 000000000000..02ef81a82995 --- /dev/null +++ b/tests/incremental/a.js @@ -0,0 +1 @@ +/* @providesModule IncrModuleA */ diff --git a/tests/incremental/b.js b/tests/incremental/b.js new file mode 100644 index 000000000000..221ffef4467a --- /dev/null +++ b/tests/incremental/b.js @@ -0,0 +1,5 @@ +/* @providesModule IncrModuleB + @flow +*/ + +var A = require('IncrModuleA'); diff --git a/tests/incremental/dup_a.js b/tests/incremental/dup_a.js new file mode 100644 index 000000000000..b996546f23a1 --- /dev/null +++ b/tests/incremental/dup_a.js @@ -0,0 +1,3 @@ +/* @providesModule IncrModuleA */ + +var x:string = 0; diff --git a/tests/incremental/jsfmt.spec.js b/tests/incremental/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/incremental/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/incremental_basic/__snapshots__/jsfmt.spec.js.snap b/tests/incremental_basic/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..c90d4376220c --- /dev/null +++ b/tests/incremental_basic/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,39 @@ +exports[`test a.js 1`] = ` +"// @flow +var a: string = 0; +module.exports = a; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +var a: string = 0; +module.exports = a; + +" +`; + +exports[`test b.js 1`] = ` +"// @flow +var a = require('./a'); +var b: number = a; +module.exports = b; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +var a = require("./a"); +var b: number = a; +module.exports = b; + +" +`; + +exports[`test c.js 1`] = ` +"// @flow +var b = require('./b'); +var c: string = b; +module.exports = c; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +var b = require("./b"); +var c: string = b; +module.exports = c; + +" +`; diff --git a/tests/incremental_basic/a.js b/tests/incremental_basic/a.js new file mode 100644 index 000000000000..f863b77254c3 --- /dev/null +++ b/tests/incremental_basic/a.js @@ -0,0 +1,3 @@ +// @flow +var a: string = 0; +module.exports = a; diff --git a/tests/incremental_basic/b.js b/tests/incremental_basic/b.js new file mode 100644 index 000000000000..8f3560a45c53 --- /dev/null +++ b/tests/incremental_basic/b.js @@ -0,0 +1,4 @@ +// @flow +var a = require('./a'); +var b: number = a; +module.exports = b; diff --git a/tests/incremental_basic/c.js b/tests/incremental_basic/c.js new file mode 100644 index 000000000000..3c6b3f56fe5d --- /dev/null +++ b/tests/incremental_basic/c.js @@ -0,0 +1,4 @@ +// @flow +var b = require('./b'); +var c: string = b; +module.exports = c; diff --git a/tests/incremental_basic/jsfmt.spec.js b/tests/incremental_basic/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/incremental_basic/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/incremental_basic/tmp1/__snapshots__/jsfmt.spec.js.snap b/tests/incremental_basic/tmp1/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..53f45bfe5ffa --- /dev/null +++ b/tests/incremental_basic/tmp1/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,13 @@ +exports[`test b.js 1`] = ` +"// @flow +var a = require('./a'); +var b = a; +module.exports = b; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +var a = require("./a"); +var b = a; +module.exports = b; + +" +`; diff --git a/tests/incremental_basic/tmp1/b.js b/tests/incremental_basic/tmp1/b.js new file mode 100644 index 000000000000..14c1a8efab19 --- /dev/null +++ b/tests/incremental_basic/tmp1/b.js @@ -0,0 +1,4 @@ +// @flow +var a = require('./a'); +var b = a; +module.exports = b; diff --git a/tests/incremental_basic/tmp1/jsfmt.spec.js b/tests/incremental_basic/tmp1/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/incremental_basic/tmp1/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/incremental_basic/tmp2/__snapshots__/jsfmt.spec.js.snap b/tests/incremental_basic/tmp2/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..2f9e0b72c72f --- /dev/null +++ b/tests/incremental_basic/tmp2/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,11 @@ +exports[`test a.js 1`] = ` +"// @flow +var a = 0; +module.exports = a; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +var a = 0; +module.exports = a; + +" +`; diff --git a/tests/incremental_basic/tmp2/a.js b/tests/incremental_basic/tmp2/a.js new file mode 100644 index 000000000000..6c8a55571b7a --- /dev/null +++ b/tests/incremental_basic/tmp2/a.js @@ -0,0 +1,3 @@ +// @flow +var a = 0; +module.exports = a; diff --git a/tests/incremental_basic/tmp2/jsfmt.spec.js b/tests/incremental_basic/tmp2/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/incremental_basic/tmp2/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/incremental_basic/tmp3/__snapshots__/jsfmt.spec.js.snap b/tests/incremental_basic/tmp3/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..9fc637bf3218 --- /dev/null +++ b/tests/incremental_basic/tmp3/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,13 @@ +exports[`test b.js 1`] = ` +"// @flow +var a = require('./a'); +var b: number = a; +module.exports = b; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +var a = require("./a"); +var b: number = a; +module.exports = b; + +" +`; diff --git a/tests/incremental_basic/tmp3/b.js b/tests/incremental_basic/tmp3/b.js new file mode 100644 index 000000000000..8f3560a45c53 --- /dev/null +++ b/tests/incremental_basic/tmp3/b.js @@ -0,0 +1,4 @@ +// @flow +var a = require('./a'); +var b: number = a; +module.exports = b; diff --git a/tests/incremental_basic/tmp3/jsfmt.spec.js b/tests/incremental_basic/tmp3/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/incremental_basic/tmp3/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/incremental_cycle/A.js b/tests/incremental_cycle/A.js new file mode 100644 index 000000000000..d745347242b6 --- /dev/null +++ b/tests/incremental_cycle/A.js @@ -0,0 +1,8 @@ +// @flow + +class A { + b: number; + c: string; +} + +module.exports = A; diff --git a/tests/incremental_cycle/B.js b/tests/incremental_cycle/B.js new file mode 100644 index 000000000000..2aebd1fc0cb8 --- /dev/null +++ b/tests/incremental_cycle/B.js @@ -0,0 +1,9 @@ +// @flow +var A = require ('./A'); +import type C from './C'; + +class B extends A { + c: C; +} + +module.exports = B; diff --git a/tests/incremental_cycle/C.js b/tests/incremental_cycle/C.js new file mode 100644 index 000000000000..3b7aed9f91d3 --- /dev/null +++ b/tests/incremental_cycle/C.js @@ -0,0 +1,9 @@ +// @flow +var A = require ('./A'); +import type B from './B'; + +class C extends A { + b: B; +} + +module.exports = C; diff --git a/tests/incremental_cycle/__snapshots__/jsfmt.spec.js.snap b/tests/incremental_cycle/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..48806a659072 --- /dev/null +++ b/tests/incremental_cycle/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,63 @@ +exports[`test A.js 1`] = ` +"// @flow + +class A { + b: number; + c: string; +} + +module.exports = A; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +class A { + b: number; + c: string; +} +module.exports = A; + +" +`; + +exports[`test B.js 1`] = ` +"// @flow +var A = require ('./A'); +import type C from './C'; + +class B extends A { + c: C; +} + +module.exports = B; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +var A = require("./A"); +import type C from "./C"; +class B extends A { + c: C; +} +module.exports = B; + +" +`; + +exports[`test C.js 1`] = ` +"// @flow +var A = require ('./A'); +import type B from './B'; + +class C extends A { + b: B; +} + +module.exports = C; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +var A = require("./A"); +import type B from "./B"; +class C extends A { + b: B; +} +module.exports = C; + +" +`; diff --git a/tests/incremental_cycle/jsfmt.spec.js b/tests/incremental_cycle/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/incremental_cycle/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/incremental_cycle/tmp1/B.js b/tests/incremental_cycle/tmp1/B.js new file mode 100644 index 000000000000..0beb2ae1ee93 --- /dev/null +++ b/tests/incremental_cycle/tmp1/B.js @@ -0,0 +1,5 @@ +// @flow +var A = require ('./A'); +import type C from './C'; + +export type B = string; diff --git a/tests/incremental_cycle/tmp1/C.js b/tests/incremental_cycle/tmp1/C.js new file mode 100644 index 000000000000..6e957cdd46d1 --- /dev/null +++ b/tests/incremental_cycle/tmp1/C.js @@ -0,0 +1,9 @@ +// @flow +var A = require ('./A'); +import type { B } from './B' + +class C extends A { + b: B; +} + +module.exports = C; diff --git a/tests/incremental_cycle/tmp1/__snapshots__/jsfmt.spec.js.snap b/tests/incremental_cycle/tmp1/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..2712efe33fbd --- /dev/null +++ b/tests/incremental_cycle/tmp1/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,36 @@ +exports[`test B.js 1`] = ` +"// @flow +var A = require ('./A'); +import type C from './C'; + +export type B = string; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +var A = require("./A"); +import type C from "./C"; +export type B = string; + +" +`; + +exports[`test C.js 1`] = ` +"// @flow +var A = require ('./A'); +import type { B } from './B' + +class C extends A { + b: B; +} + +module.exports = C; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +var A = require("./A"); +import type { B } from "./B"; +class C extends A { + b: B; +} +module.exports = C; + +" +`; diff --git a/tests/incremental_cycle/tmp1/jsfmt.spec.js b/tests/incremental_cycle/tmp1/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/incremental_cycle/tmp1/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/incremental_cycle/tmp2/B.js b/tests/incremental_cycle/tmp2/B.js new file mode 100644 index 000000000000..b9f7dd32dc37 --- /dev/null +++ b/tests/incremental_cycle/tmp2/B.js @@ -0,0 +1,5 @@ +// @flow +var A = require ('./A'); +//import type C from './C'; + +export type B = string; diff --git a/tests/incremental_cycle/tmp2/__snapshots__/jsfmt.spec.js.snap b/tests/incremental_cycle/tmp2/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..6781d9af80b0 --- /dev/null +++ b/tests/incremental_cycle/tmp2/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,14 @@ +exports[`test B.js 1`] = ` +"// @flow +var A = require ('./A'); +//import type C from './C'; + +export type B = string; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +var A = require("./A"); +//import type C from './C'; +export type B = string; + +" +`; diff --git a/tests/incremental_cycle/tmp2/jsfmt.spec.js b/tests/incremental_cycle/tmp2/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/incremental_cycle/tmp2/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/incremental_cycle/tmp3/B.js b/tests/incremental_cycle/tmp3/B.js new file mode 100644 index 000000000000..748fd837e9f8 --- /dev/null +++ b/tests/incremental_cycle/tmp3/B.js @@ -0,0 +1,6 @@ +// @flow +var A = require ('./A'); +import type C from './C'; + + +export type B = string; diff --git a/tests/incremental_cycle/tmp3/__snapshots__/jsfmt.spec.js.snap b/tests/incremental_cycle/tmp3/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..3e2ea4f503ac --- /dev/null +++ b/tests/incremental_cycle/tmp3/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,15 @@ +exports[`test B.js 1`] = ` +"// @flow +var A = require ('./A'); +import type C from './C'; + + +export type B = string; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +var A = require("./A"); +import type C from "./C"; +export type B = string; + +" +`; diff --git a/tests/incremental_cycle/tmp3/jsfmt.spec.js b/tests/incremental_cycle/tmp3/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/incremental_cycle/tmp3/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/incremental_delete/__snapshots__/jsfmt.spec.js.snap b/tests/incremental_delete/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..90424706cea3 --- /dev/null +++ b/tests/incremental_delete/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,123 @@ +exports[`test a.js 1`] = ` +"// @flow +var a: string = 0; +module.exports = a; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +var a: string = 0; +module.exports = a; + +" +`; + +exports[`test b.js 1`] = ` +"// @flow +var a = require('./a'); +var b: number = a; +module.exports = b; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +var a = require("./a"); +var b: number = a; +module.exports = b; + +" +`; + +exports[`test c.js 1`] = ` +"// @flow +var b = require('./b'); +var c: string = b; +module.exports = c; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +var b = require("./b"); +var c: string = b; +module.exports = c; + +" +`; + +exports[`test dupe1.js 1`] = ` +"/** + * Dupe provider 1/2 + * @providesModule Dupe + * @flow + */ +module.exports = "dupe1"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * Dupe provider 1/2 + * @providesModule Dupe + * @flow + */ +module.exports = "dupe1"; + +" +`; + +exports[`test dupe2.js 1`] = ` +"/** + * Dupe provider 2/2 + * @providesModule Dupe + * @flow + */ +module.exports = "dupe2"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * Dupe provider 2/2 + * @providesModule Dupe + * @flow + */ +module.exports = "dupe2"; + +" +`; + +exports[`test requires_dupe.js 1`] = ` +"/** + * depends on doubly-provided module + * @flow + */ +var dupe = require('Dupe'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * depends on doubly-provided module + * @flow + */ +var dupe = require("Dupe"); + +" +`; + +exports[`test requires_unchecked.js 1`] = ` +"/** + * depends on an unchecked module, which will be deleted + * @flow + */ +var unchecked = require('Unchecked'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * depends on an unchecked module, which will be deleted + * @flow + */ +var unchecked = require("Unchecked"); + +" +`; + +exports[`test unchecked.js 1`] = ` +"/** + * Not a flow module. + * @providesModule Unchecked + */ +module.exports = "unchecked"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * Not a flow module. + * @providesModule Unchecked + */ +module.exports = "unchecked"; + +" +`; diff --git a/tests/incremental_delete/a.js b/tests/incremental_delete/a.js new file mode 100644 index 000000000000..f863b77254c3 --- /dev/null +++ b/tests/incremental_delete/a.js @@ -0,0 +1,3 @@ +// @flow +var a: string = 0; +module.exports = a; diff --git a/tests/incremental_delete/b.js b/tests/incremental_delete/b.js new file mode 100644 index 000000000000..8f3560a45c53 --- /dev/null +++ b/tests/incremental_delete/b.js @@ -0,0 +1,4 @@ +// @flow +var a = require('./a'); +var b: number = a; +module.exports = b; diff --git a/tests/incremental_delete/c.js b/tests/incremental_delete/c.js new file mode 100644 index 000000000000..3c6b3f56fe5d --- /dev/null +++ b/tests/incremental_delete/c.js @@ -0,0 +1,4 @@ +// @flow +var b = require('./b'); +var c: string = b; +module.exports = c; diff --git a/tests/incremental_delete/dupe1.js b/tests/incremental_delete/dupe1.js new file mode 100644 index 000000000000..1a298b67eae7 --- /dev/null +++ b/tests/incremental_delete/dupe1.js @@ -0,0 +1,6 @@ +/** + * Dupe provider 1/2 + * @providesModule Dupe + * @flow + */ +module.exports = "dupe1"; diff --git a/tests/incremental_delete/dupe2.js b/tests/incremental_delete/dupe2.js new file mode 100644 index 000000000000..aa64ae687dd8 --- /dev/null +++ b/tests/incremental_delete/dupe2.js @@ -0,0 +1,6 @@ +/** + * Dupe provider 2/2 + * @providesModule Dupe + * @flow + */ +module.exports = "dupe2"; diff --git a/tests/incremental_delete/jsfmt.spec.js b/tests/incremental_delete/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/incremental_delete/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/incremental_delete/requires_dupe.js b/tests/incremental_delete/requires_dupe.js new file mode 100644 index 000000000000..129992b7ab05 --- /dev/null +++ b/tests/incremental_delete/requires_dupe.js @@ -0,0 +1,5 @@ +/** + * depends on doubly-provided module + * @flow + */ +var dupe = require('Dupe'); diff --git a/tests/incremental_delete/requires_unchecked.js b/tests/incremental_delete/requires_unchecked.js new file mode 100644 index 000000000000..1db1b9bc6a4c --- /dev/null +++ b/tests/incremental_delete/requires_unchecked.js @@ -0,0 +1,5 @@ +/** + * depends on an unchecked module, which will be deleted + * @flow + */ +var unchecked = require('Unchecked'); diff --git a/tests/incremental_delete/unchecked.js b/tests/incremental_delete/unchecked.js new file mode 100644 index 000000000000..96d63429283f --- /dev/null +++ b/tests/incremental_delete/unchecked.js @@ -0,0 +1,5 @@ +/** + * Not a flow module. + * @providesModule Unchecked + */ +module.exports = "unchecked"; diff --git a/tests/incremental_duplicate_delete/A.js b/tests/incremental_duplicate_delete/A.js new file mode 100644 index 000000000000..638d95f80d1d --- /dev/null +++ b/tests/incremental_duplicate_delete/A.js @@ -0,0 +1,4 @@ +/** + * @providesModule A + * @flow + */ diff --git a/tests/incremental_duplicate_delete/__snapshots__/jsfmt.spec.js.snap b/tests/incremental_duplicate_delete/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..519ef51f35cb --- /dev/null +++ b/tests/incremental_duplicate_delete/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,10 @@ +exports[`test A.js 1`] = ` +"/** + * @providesModule A + * @flow + */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; diff --git a/tests/incremental_duplicate_delete/jsfmt.spec.js b/tests/incremental_duplicate_delete/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/incremental_duplicate_delete/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/incremental_json/__snapshots__/jsfmt.spec.js.snap b/tests/incremental_json/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..435a7ef63301 --- /dev/null +++ b/tests/incremental_json/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,15 @@ +exports[`test test.js 1`] = ` +"/** + * @flow + */ +var data = require('./data'); +var x: number = data.x; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +var data = require("./data"); +var x: number = data.x; + +" +`; diff --git a/tests/incremental_json/jsfmt.spec.js b/tests/incremental_json/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/incremental_json/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/incremental_json/test.js b/tests/incremental_json/test.js new file mode 100644 index 000000000000..3eb8f377fcbe --- /dev/null +++ b/tests/incremental_json/test.js @@ -0,0 +1,5 @@ +/** + * @flow + */ +var data = require('./data'); +var x: number = data.x; diff --git a/tests/incremental_mixed_naming_cycle/__snapshots__/jsfmt.spec.js.snap b/tests/incremental_mixed_naming_cycle/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..6b272ab92e10 --- /dev/null +++ b/tests/incremental_mixed_naming_cycle/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,102 @@ +exports[`test a.js 1`] = ` +"/** + * @providesModule A + * @flow + */ + +(require('./b'): void); +(require('C'): void); + +module.exports = 'A'; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule A + * @flow + */ +(require("./b"): void); +(require("C"): void); +module.exports = "A"; + +" +`; + +exports[`test b.js 1`] = ` +"/** + * @providesModule B + * @flow + */ + +(require('A'): void); +(require('D'): void); + +module.exports = 'B'; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule B + * @flow + */ +(require("A"): void); +(require("D"): void); +module.exports = "B"; + +" +`; + +exports[`test c.js 1`] = ` +"/** + * @providesModule C + * @flow + */ + +require('Root'); +(require('./b'): void); + +module.exports = 'C'; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule C + * @flow + */ +require("Root"); +(require("./b"): void); +module.exports = "C"; + +" +`; + +exports[`test d.js 1`] = ` +"/** + * @providesModule D + * @flow + */ + +(require('./b'): void); + +module.exports = 'D'; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule D + * @flow + */ +(require("./b"): void); +module.exports = "D"; + +" +`; + +exports[`test root.js 1`] = ` +"/** + * @providesModule Root + * @flow + */ + +module.exports = 'Root'; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule Root + * @flow + */ +module.exports = "Root"; + +" +`; diff --git a/tests/incremental_mixed_naming_cycle/a.js b/tests/incremental_mixed_naming_cycle/a.js new file mode 100644 index 000000000000..e5effdb2d447 --- /dev/null +++ b/tests/incremental_mixed_naming_cycle/a.js @@ -0,0 +1,9 @@ +/** + * @providesModule A + * @flow + */ + +(require('./b'): void); +(require('C'): void); + +module.exports = 'A'; diff --git a/tests/incremental_mixed_naming_cycle/b.js b/tests/incremental_mixed_naming_cycle/b.js new file mode 100644 index 000000000000..be1f28147565 --- /dev/null +++ b/tests/incremental_mixed_naming_cycle/b.js @@ -0,0 +1,9 @@ +/** + * @providesModule B + * @flow + */ + +(require('A'): void); +(require('D'): void); + +module.exports = 'B'; diff --git a/tests/incremental_mixed_naming_cycle/c.js b/tests/incremental_mixed_naming_cycle/c.js new file mode 100644 index 000000000000..a9cf3abb6f6a --- /dev/null +++ b/tests/incremental_mixed_naming_cycle/c.js @@ -0,0 +1,9 @@ +/** + * @providesModule C + * @flow + */ + +require('Root'); +(require('./b'): void); + +module.exports = 'C'; diff --git a/tests/incremental_mixed_naming_cycle/d.js b/tests/incremental_mixed_naming_cycle/d.js new file mode 100644 index 000000000000..b95edb89b908 --- /dev/null +++ b/tests/incremental_mixed_naming_cycle/d.js @@ -0,0 +1,8 @@ +/** + * @providesModule D + * @flow + */ + +(require('./b'): void); + +module.exports = 'D'; diff --git a/tests/incremental_mixed_naming_cycle/jsfmt.spec.js b/tests/incremental_mixed_naming_cycle/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/incremental_mixed_naming_cycle/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/incremental_mixed_naming_cycle/root.js b/tests/incremental_mixed_naming_cycle/root.js new file mode 100644 index 000000000000..890d205a10e3 --- /dev/null +++ b/tests/incremental_mixed_naming_cycle/root.js @@ -0,0 +1,6 @@ +/** + * @providesModule Root + * @flow + */ + +module.exports = 'Root'; diff --git a/tests/incremental_mixed_naming_cycle/tmp1/__snapshots__/jsfmt.spec.js.snap b/tests/incremental_mixed_naming_cycle/tmp1/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..e441192b8fbe --- /dev/null +++ b/tests/incremental_mixed_naming_cycle/tmp1/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,18 @@ +exports[`test root.js 1`] = ` +"/** + * @providesModule Root + * @flow + */ + +// trivial edit (adding this comment) +module.exports = 'Root'; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule Root + * @flow + */ +// trivial edit (adding this comment) +module.exports = "Root"; + +" +`; diff --git a/tests/incremental_mixed_naming_cycle/tmp1/jsfmt.spec.js b/tests/incremental_mixed_naming_cycle/tmp1/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/incremental_mixed_naming_cycle/tmp1/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/incremental_mixed_naming_cycle/tmp1/root.js b/tests/incremental_mixed_naming_cycle/tmp1/root.js new file mode 100644 index 000000000000..d5d42fa36245 --- /dev/null +++ b/tests/incremental_mixed_naming_cycle/tmp1/root.js @@ -0,0 +1,7 @@ +/** + * @providesModule Root + * @flow + */ + +// trivial edit (adding this comment) +module.exports = 'Root'; diff --git a/tests/incremental_non_flow_move/__snapshots__/jsfmt.spec.js.snap b/tests/incremental_non_flow_move/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..51bc01bd2485 --- /dev/null +++ b/tests/incremental_non_flow_move/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,24 @@ +exports[`test foo.js 1`] = ` +"/* + * @providesModule Foo + */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; + +exports[`test test.js 1`] = ` +"/** + * @flow + */ + +require('Foo'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +require("Foo"); + +" +`; diff --git a/tests/incremental_non_flow_move/foo.js b/tests/incremental_non_flow_move/foo.js new file mode 100644 index 000000000000..ddb85456b207 --- /dev/null +++ b/tests/incremental_non_flow_move/foo.js @@ -0,0 +1,3 @@ +/* + * @providesModule Foo + */ diff --git a/tests/incremental_non_flow_move/jsfmt.spec.js b/tests/incremental_non_flow_move/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/incremental_non_flow_move/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/incremental_non_flow_move/test.js b/tests/incremental_non_flow_move/test.js new file mode 100644 index 000000000000..98297b3fb64b --- /dev/null +++ b/tests/incremental_non_flow_move/test.js @@ -0,0 +1,5 @@ +/** + * @flow + */ + +require('Foo'); diff --git a/tests/incremental_path/dir/__snapshots__/jsfmt.spec.js.snap b/tests/incremental_path/dir/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..4631bc37ffb5 --- /dev/null +++ b/tests/incremental_path/dir/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,9 @@ +exports[`test a.js 1`] = ` +"// @flow +(require('b'): boolean); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +(require("b"): boolean); + +" +`; diff --git a/tests/incremental_path/dir/a.js b/tests/incremental_path/dir/a.js new file mode 100644 index 000000000000..cb2264c959c3 --- /dev/null +++ b/tests/incremental_path/dir/a.js @@ -0,0 +1,2 @@ +// @flow +(require('b'): boolean); diff --git a/tests/incremental_path/dir/jsfmt.spec.js b/tests/incremental_path/dir/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/incremental_path/dir/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/indexer/A.js b/tests/indexer/A.js new file mode 100644 index 000000000000..3f14aa36f4fc --- /dev/null +++ b/tests/indexer/A.js @@ -0,0 +1,39 @@ +// No indexer should be fine +function foo0(): {} { + return { foo: "bar" } +} + +// Matching indexer should be fine +function foo1(): {[key: string]: string} { + return { foo: "bar" } +} + +// Indexer with different key type is an error when it matches +function foo2(): {[key: number]: string} { + return { foo: "bar" } +} + +// Matching indexer with different value type is an error +function foo3(): {[key: string]: number} { + return { foo: "bar" } +} + +// Indexer with different key type and different value type is twice an error +function foo4(): {[key: number]: number} { + return { foo: "bar" } +} + +// If key exists in object type then indexer is not matched +function foo5(): {[key: string]: number; foo: string} { + return { foo: "bar" } +} + +// If key exists in object type then indexer is not matched +function foo6(): {[key: number]: number; foo: string} { + return { foo: "bar" } +} + +// Should still complain about mistyped properties +function foo7(): {[key: string]: number; foo: number} { + return { foo: "bar" } +} diff --git a/tests/indexer/__snapshots__/jsfmt.spec.js.snap b/tests/indexer/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..205a700b09df --- /dev/null +++ b/tests/indexer/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,101 @@ +exports[`test A.js 1`] = ` +"// No indexer should be fine +function foo0(): {} { + return { foo: "bar" } +} + +// Matching indexer should be fine +function foo1(): {[key: string]: string} { + return { foo: "bar" } +} + +// Indexer with different key type is an error when it matches +function foo2(): {[key: number]: string} { + return { foo: "bar" } +} + +// Matching indexer with different value type is an error +function foo3(): {[key: string]: number} { + return { foo: "bar" } +} + +// Indexer with different key type and different value type is twice an error +function foo4(): {[key: number]: number} { + return { foo: "bar" } +} + +// If key exists in object type then indexer is not matched +function foo5(): {[key: string]: number; foo: string} { + return { foo: "bar" } +} + +// If key exists in object type then indexer is not matched +function foo6(): {[key: number]: number; foo: string} { + return { foo: "bar" } +} + +// Should still complain about mistyped properties +function foo7(): {[key: string]: number; foo: number} { + return { foo: "bar" } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// No indexer should be fine +function foo0(): {} { + return { foo: "bar" }; +} +// Matching indexer should be fine +function foo1(): { [key: string]: string } { + return { foo: "bar" }; +} +// Indexer with different key type is an error when it matches +function foo2(): { [key: number]: string } { + return { foo: "bar" }; +} +// Matching indexer with different value type is an error +function foo3(): { [key: string]: number } { + return { foo: "bar" }; +} +// Indexer with different key type and different value type is twice an error +function foo4(): { [key: number]: number } { + return { foo: "bar" }; +} +// If key exists in object type then indexer is not matched +function foo5(): { [key: string]: number, foo: string } { + return { foo: "bar" }; +} +// If key exists in object type then indexer is not matched +function foo6(): { [key: number]: number, foo: string } { + return { foo: "bar" }; +} +// Should still complain about mistyped properties +function foo7(): { [key: string]: number, foo: number } { + return { foo: "bar" }; +} + +" +`; + +exports[`test multiple.js 1`] = ` +"// @flow + +let tests = [ + function() { + ({}: { + [k1: string]: string, + [k2: number]: number, // error: not supported (yet) + }); + } +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +let tests = [ + function() { + ({}: { + [k1: string]: string, + // error: not supported (yet) [k2: number]: number + }); + } +]; + +" +`; diff --git a/tests/indexer/jsfmt.spec.js b/tests/indexer/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/indexer/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/indexer/multiple.js b/tests/indexer/multiple.js new file mode 100644 index 000000000000..2d6e931c9420 --- /dev/null +++ b/tests/indexer/multiple.js @@ -0,0 +1,10 @@ +// @flow + +let tests = [ + function() { + ({}: { + [k1: string]: string, + [k2: number]: number, // error: not supported (yet) + }); + } +]; diff --git a/tests/init/__snapshots__/jsfmt.spec.js.snap b/tests/init/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..460df9006972 --- /dev/null +++ b/tests/init/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,552 @@ +exports[`test hoisted.js 1`] = ` +"/** + * test initialization tracking in the presence of hoisting + * @flow + */ + +function _if(b: () => boolean) { + if (b()) { + var f = function () {}; + } + f(); // error, possibly undefined +} + +function _while(b: () => boolean) { + while (b()) { + var f = function () {}; + } + f(); // error, possibly undefined +} + +function _do_while(b: () => boolean) { + do { + var f = function () {}; + } while (b()); + f(); // ok +} + +function _for(n: number) { + for (var i = 0; i < n; i++) { + var f = function () {}; + } + f(); // error, possibly undefined +} + +function _for_in(obj: Object) { + for (var p in obj) { + var f = function () {}; + } + f(); // error, possibly undefined +} + +function _for_of(arr: Array) { + for (var x of arr) { + var f = function () {}; + } + f(); // error, possibly undefined +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test hoisted2.js 1`] = ` +"/** + * test initialization tracking for vars + * note: for try/catch/finally, see tests/try/init.js + * @flow + */ + +// deferred init on annotated vars is ok +function linear_deferred_init() { + var x:number; + x = 0; + var y:number = x; +} + +// ...but use of var before init gives undefined +function linear_pre_init() { + var x:number; + var y:number = x; // error +} + +// local use of annotated vars in an if is ok +function if_scoped_init(b) { + if (b) { + var x:number = 0; + var y:number = x; + } +} + +// but not across if/else +function if_else_partial_init(b) { + if (b) { + var x:number = 0; + } else { + var y:number = x; // error + } +} + +// use of var before if gives undefined +function if_pre_init(b) { + var y:number = x; // error + if (b) { + var x:number = 0; + } +} + +// ...and after +function if_partial_post_init(b) { + if (b) { + var x:number = 0; + } + var y:number = x; // error +} + +// ...unless both branches have initialized +function if_post_init(b) { + if (b) { + var x:number = 0; + } else { + var x:number = 1; + } + var y:number = x; +} + +// use of var after partial init (non-exhaustive if) gives undefined +function if_partial_post_init(b) { + var x:number; + if (b) { + x = 0; + } + var y:number = x; // error, possibly uninitialized +} + +// use of var after guaranteed init (exhaustive if) is ok +function if_post_init(b) { + var x:number; + if (b) { + x = 0; + } else { + x = 1; + } + var y:number = x; +} + +// use of var after partial init (non-exhaustive switch) gives undefined +function switch_partial_post_init(i) { + var x:number; + switch (i) { + case 0: + x = 0; + break; + case 1: + x = 1; + break; + } + var y:number = x; // error, possibly uninitialized +} + +// use of var after guaranteed init (exhaustive switch) is ok +function switch_post_init(i) { + var x:number; + switch (i) { + case 0: + x = 0; + break; + case 1: + x = 1; + break; + default: + x = 2; + } + var y:number = x; // no error, all cases covered +} + +// local use of annotated var in switch is ok +function switch_scoped_init_1(i) { + switch (i) { + case 0: + var x:number = 0; + var y:number = x; + } +} + +// ...but use of var before switch gives undefined +function switch_scoped_init_2(i) { + var y:number = x; // error + switch (i) { + case 0: + var x:number = 0; + } +} + +// ...and after +function switch_scoped_init_3(i) { + switch (i) { + case 0: + var x:number = 0; + } + var y:number = x; // error +} + +// ...and in a fallthrough case without initialization +function switch_scoped_init_4(i) { + switch (i) { + case 0: + var x:number = 0; + case 1: + var y:number = x; // error + } +} + +// local use of annotated var in while is ok +function while_scoped_init(b) { + while (b) { + var x:number = 0; + var y:number = x; + } +} + +// ...but use of var before while gives undefined +function while_pre_init(b) { + var y:number = x; // error + while (b) { + var x:number = 0; + } +} + +// ...and after +function while_post_init(b) { + while (b) { + var x:number = 0; + } + var y:number = x; // error +} + +// local use of annotated var in do-while is ok +function do_while_scoped_init(b) { + do { + var x:number = 0; + var y:number = x; + } while (b); +} + +// ...but use before do-while gives undefined +function do_while_pre_init(b) { + var y:number = x; // error + do { + var x:number = 0; + } while (b); +} + +// after is ok, because loop is guaranteed to run +function do_while_post_init(b) { + do { + var x:number = 0; + } while (b); + var y:number = x; +} + +// local use of annotated var in for is ok +function for_scoped_init(b) { + for (;b;) { + var x:number = 0; + var y:number = x; + } +} + +// ...but use before for gives undefined +function for_pre_init(b) { + var y:number = x; // error + for (;b;) { + var x:number = 0; + } +} + +// ...and after +function for_post_init(b) { + for (;b;) { + var x:number = 0; + } + var y:number = x; // error +} + +// local use of annotated var in for-in is ok +function for_in_scoped_init() { + for (var p in { a:1, b: 2 }) { + var x:number = 0; + var y:number = x; + } +} + +// ...but use before while gives undefined +function for_in_pre_init() { + var y:number = x; // error + for (var p in { a:1, b: 2 }) { + var x:number = 0; + } +} + +// ...and after +function for_in_post_init() { + for (var p in { a:1, b: 2 }) { + var x:number = 0; + } + var y:number = x; // error +} + +// local use of annotated var in for-of is ok +function for_of_scoped_init() { + for (var x of [1, 2, 3]) { + var x:number = 0; + var y:number = x; + } +} + +// ...but use before while gives undefined +function for_in_pre_init() { + var y:number = x; // error + for (var x of [1, 2, 3]) { + var x:number = 0; + } +} + +// ...and after +function for_in_post_init() { + for (var x of [1, 2, 3]) { + var x:number = 0; + } + var y:number = x; // error +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1020 + }, \"consequent\").indent(options.tabWidth)); + ^ + +TypeError: path.call(...).indent is not a function + at genericPrintNoParens (/src/printer.js:1020:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1005:36) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test let.js 1`] = ` +"/** + * test initialization tracking for lets + * @flow + */ + +// deferred init on annotated lets is ok +function linear_deferred_init() { + let x:number; + x = 0; + let y:number = x; +} + +// use of let before init gives undefined +function linear_pre_init() { + let x:number; + let y:?number = x; // ok + let z:number = x; // error + x = 0; + let w:number = x; // ok +} + +// self-references in let bindings are not ok +function self_init() { + let x = x; // \'x\' not initialized! +} + +// use of let after partial init (non-exhaustive if) gives undefined +function if_partial_post_init(b) { + let x:number; + if (b) { + x = 0; + } + var y:number = x; // error, possibly uninitialized +} + +// use of let after guaranteed init (exhaustive if) is ok +function if_post_init(b) { + let x:number; + if (b) { + x = 0; + } else { + x = 1; + } + var y:number = x; +} + +// use of let after partial init (non-exhaustive switch) gives undefined +function switch_partial_post_init(i) { + let x:number; + switch (i) { + case 0: + x = 0; + break; + case 1: + x = 1; + break; + } + var y:number = x; // error, possibly uninitialized +} + +// use of let after guaranteed init (exhaustive switch) is ok +function switch_post_init(i) { + let x:number; + switch (i) { + case 0: + x = 0; + break; + case 1: + x = 1; + break; + default: + x = 2; + } + var y:number = x; // no error, all cases covered +} + +// use in a switch after a skipped decl is an error +function switch_scoped_init_2(i) { + switch (i) { + case 0: + let x:number; + case 1: + let y:number = x; // error, skipped declaration + } +} + +// while leaves it possibly uninitialized +function while_post_init(b) { + let x:number; + while (b) { + x = 0; + } + var y:number = x; // error +} + +// do-while is ok, because loop is guaranteed to run at least once +function do_while_post_init(b) { + let x:number; + do { + x = 0; + } while (b); + var y:number = x; // ok +} + +// for-in leaves it possibly uninitialized +function for_in_post_init() { + var x:number; + for (var p in {}) { + x = 0; + } + var y:number = x; // error +} + +// for-of leaves it possibly uninitialized +function for_of_post_init() { + var x:number; + for (var x of []) { + x = 0; + } + var y:number = x; // error +} + +// use of let after guaranteed init (exhaustive switch + throw) is ok +function switch_post_init2(i): number { + let bar; + switch (i) { + case 1: + bar = 3; + break; + default: + throw new Error(\'Invalid state\'); + } + return bar; // ok, definitely initialized +} + +// use of let after guaranteed init (exhaustive switch + throw) is ok +function switch_post_init2(i): number { + let bar; + switch (i) { + case 1: + bar = 3; + break; + default: + throw new Error(\'Invalid state\'); + } + return bar; // ok, definitely initialized +} + +// reference of a let-binding is permitted in a sub-closure within the init expr +function sub_closure_init_reference() { + let x = function() { return x; }; + const y = function() { return y; }; + + // var-bindings can reference each other cyclically since they do not incur a + // TDZ (...even though this is weird...) + var z = z; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1020 + }, \"consequent\").indent(options.tabWidth)); + ^ + +TypeError: path.call(...).indent is not a function + at genericPrintNoParens (/src/printer.js:1020:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1005:36) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test nullable-init.js 1`] = ` +"var o: {x: ?number} = { x: null }; +var a: Array = [null,null]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/init/hoisted.js b/tests/init/hoisted.js new file mode 100644 index 000000000000..0a5458712f18 --- /dev/null +++ b/tests/init/hoisted.js @@ -0,0 +1,46 @@ +/** + * test initialization tracking in the presence of hoisting + * @flow + */ + +function _if(b: () => boolean) { + if (b()) { + var f = function () {}; + } + f(); // error, possibly undefined +} + +function _while(b: () => boolean) { + while (b()) { + var f = function () {}; + } + f(); // error, possibly undefined +} + +function _do_while(b: () => boolean) { + do { + var f = function () {}; + } while (b()); + f(); // ok +} + +function _for(n: number) { + for (var i = 0; i < n; i++) { + var f = function () {}; + } + f(); // error, possibly undefined +} + +function _for_in(obj: Object) { + for (var p in obj) { + var f = function () {}; + } + f(); // error, possibly undefined +} + +function _for_of(arr: Array) { + for (var x of arr) { + var f = function () {}; + } + f(); // error, possibly undefined +} diff --git a/tests/init/hoisted2.js b/tests/init/hoisted2.js new file mode 100644 index 000000000000..4258e0e46082 --- /dev/null +++ b/tests/init/hoisted2.js @@ -0,0 +1,268 @@ +/** + * test initialization tracking for vars + * note: for try/catch/finally, see tests/try/init.js + * @flow + */ + +// deferred init on annotated vars is ok +function linear_deferred_init() { + var x:number; + x = 0; + var y:number = x; +} + +// ...but use of var before init gives undefined +function linear_pre_init() { + var x:number; + var y:number = x; // error +} + +// local use of annotated vars in an if is ok +function if_scoped_init(b) { + if (b) { + var x:number = 0; + var y:number = x; + } +} + +// but not across if/else +function if_else_partial_init(b) { + if (b) { + var x:number = 0; + } else { + var y:number = x; // error + } +} + +// use of var before if gives undefined +function if_pre_init(b) { + var y:number = x; // error + if (b) { + var x:number = 0; + } +} + +// ...and after +function if_partial_post_init(b) { + if (b) { + var x:number = 0; + } + var y:number = x; // error +} + +// ...unless both branches have initialized +function if_post_init(b) { + if (b) { + var x:number = 0; + } else { + var x:number = 1; + } + var y:number = x; +} + +// use of var after partial init (non-exhaustive if) gives undefined +function if_partial_post_init(b) { + var x:number; + if (b) { + x = 0; + } + var y:number = x; // error, possibly uninitialized +} + +// use of var after guaranteed init (exhaustive if) is ok +function if_post_init(b) { + var x:number; + if (b) { + x = 0; + } else { + x = 1; + } + var y:number = x; +} + +// use of var after partial init (non-exhaustive switch) gives undefined +function switch_partial_post_init(i) { + var x:number; + switch (i) { + case 0: + x = 0; + break; + case 1: + x = 1; + break; + } + var y:number = x; // error, possibly uninitialized +} + +// use of var after guaranteed init (exhaustive switch) is ok +function switch_post_init(i) { + var x:number; + switch (i) { + case 0: + x = 0; + break; + case 1: + x = 1; + break; + default: + x = 2; + } + var y:number = x; // no error, all cases covered +} + +// local use of annotated var in switch is ok +function switch_scoped_init_1(i) { + switch (i) { + case 0: + var x:number = 0; + var y:number = x; + } +} + +// ...but use of var before switch gives undefined +function switch_scoped_init_2(i) { + var y:number = x; // error + switch (i) { + case 0: + var x:number = 0; + } +} + +// ...and after +function switch_scoped_init_3(i) { + switch (i) { + case 0: + var x:number = 0; + } + var y:number = x; // error +} + +// ...and in a fallthrough case without initialization +function switch_scoped_init_4(i) { + switch (i) { + case 0: + var x:number = 0; + case 1: + var y:number = x; // error + } +} + +// local use of annotated var in while is ok +function while_scoped_init(b) { + while (b) { + var x:number = 0; + var y:number = x; + } +} + +// ...but use of var before while gives undefined +function while_pre_init(b) { + var y:number = x; // error + while (b) { + var x:number = 0; + } +} + +// ...and after +function while_post_init(b) { + while (b) { + var x:number = 0; + } + var y:number = x; // error +} + +// local use of annotated var in do-while is ok +function do_while_scoped_init(b) { + do { + var x:number = 0; + var y:number = x; + } while (b); +} + +// ...but use before do-while gives undefined +function do_while_pre_init(b) { + var y:number = x; // error + do { + var x:number = 0; + } while (b); +} + +// after is ok, because loop is guaranteed to run +function do_while_post_init(b) { + do { + var x:number = 0; + } while (b); + var y:number = x; +} + +// local use of annotated var in for is ok +function for_scoped_init(b) { + for (;b;) { + var x:number = 0; + var y:number = x; + } +} + +// ...but use before for gives undefined +function for_pre_init(b) { + var y:number = x; // error + for (;b;) { + var x:number = 0; + } +} + +// ...and after +function for_post_init(b) { + for (;b;) { + var x:number = 0; + } + var y:number = x; // error +} + +// local use of annotated var in for-in is ok +function for_in_scoped_init() { + for (var p in { a:1, b: 2 }) { + var x:number = 0; + var y:number = x; + } +} + +// ...but use before while gives undefined +function for_in_pre_init() { + var y:number = x; // error + for (var p in { a:1, b: 2 }) { + var x:number = 0; + } +} + +// ...and after +function for_in_post_init() { + for (var p in { a:1, b: 2 }) { + var x:number = 0; + } + var y:number = x; // error +} + +// local use of annotated var in for-of is ok +function for_of_scoped_init() { + for (var x of [1, 2, 3]) { + var x:number = 0; + var y:number = x; + } +} + +// ...but use before while gives undefined +function for_in_pre_init() { + var y:number = x; // error + for (var x of [1, 2, 3]) { + var x:number = 0; + } +} + +// ...and after +function for_in_post_init() { + for (var x of [1, 2, 3]) { + var x:number = 0; + } + var y:number = x; // error +} diff --git a/tests/init/jsfmt.spec.js b/tests/init/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/init/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/init/let.js b/tests/init/let.js new file mode 100644 index 000000000000..f35e25f95fe9 --- /dev/null +++ b/tests/init/let.js @@ -0,0 +1,157 @@ +/** + * test initialization tracking for lets + * @flow + */ + +// deferred init on annotated lets is ok +function linear_deferred_init() { + let x:number; + x = 0; + let y:number = x; +} + +// use of let before init gives undefined +function linear_pre_init() { + let x:number; + let y:?number = x; // ok + let z:number = x; // error + x = 0; + let w:number = x; // ok +} + +// self-references in let bindings are not ok +function self_init() { + let x = x; // 'x' not initialized! +} + +// use of let after partial init (non-exhaustive if) gives undefined +function if_partial_post_init(b) { + let x:number; + if (b) { + x = 0; + } + var y:number = x; // error, possibly uninitialized +} + +// use of let after guaranteed init (exhaustive if) is ok +function if_post_init(b) { + let x:number; + if (b) { + x = 0; + } else { + x = 1; + } + var y:number = x; +} + +// use of let after partial init (non-exhaustive switch) gives undefined +function switch_partial_post_init(i) { + let x:number; + switch (i) { + case 0: + x = 0; + break; + case 1: + x = 1; + break; + } + var y:number = x; // error, possibly uninitialized +} + +// use of let after guaranteed init (exhaustive switch) is ok +function switch_post_init(i) { + let x:number; + switch (i) { + case 0: + x = 0; + break; + case 1: + x = 1; + break; + default: + x = 2; + } + var y:number = x; // no error, all cases covered +} + +// use in a switch after a skipped decl is an error +function switch_scoped_init_2(i) { + switch (i) { + case 0: + let x:number; + case 1: + let y:number = x; // error, skipped declaration + } +} + +// while leaves it possibly uninitialized +function while_post_init(b) { + let x:number; + while (b) { + x = 0; + } + var y:number = x; // error +} + +// do-while is ok, because loop is guaranteed to run at least once +function do_while_post_init(b) { + let x:number; + do { + x = 0; + } while (b); + var y:number = x; // ok +} + +// for-in leaves it possibly uninitialized +function for_in_post_init() { + var x:number; + for (var p in {}) { + x = 0; + } + var y:number = x; // error +} + +// for-of leaves it possibly uninitialized +function for_of_post_init() { + var x:number; + for (var x of []) { + x = 0; + } + var y:number = x; // error +} + +// use of let after guaranteed init (exhaustive switch + throw) is ok +function switch_post_init2(i): number { + let bar; + switch (i) { + case 1: + bar = 3; + break; + default: + throw new Error('Invalid state'); + } + return bar; // ok, definitely initialized +} + +// use of let after guaranteed init (exhaustive switch + throw) is ok +function switch_post_init2(i): number { + let bar; + switch (i) { + case 1: + bar = 3; + break; + default: + throw new Error('Invalid state'); + } + return bar; // ok, definitely initialized +} + +// reference of a let-binding is permitted in a sub-closure within the init expr +function sub_closure_init_reference() { + let x = function() { return x; }; + const y = function() { return y; }; + + // var-bindings can reference each other cyclically since they do not incur a + // TDZ (...even though this is weird...) + var z = z; +} diff --git a/tests/init/nullable-init.js b/tests/init/nullable-init.js new file mode 100644 index 000000000000..c5d32d0138a1 --- /dev/null +++ b/tests/init/nullable-init.js @@ -0,0 +1,2 @@ +var o: {x: ?number} = { x: null }; +var a: Array = [null,null]; diff --git a/tests/instanceof/__snapshots__/jsfmt.spec.js.snap b/tests/instanceof/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..48d9d5f61ccc --- /dev/null +++ b/tests/instanceof/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,106 @@ +exports[`test instanceof.js 1`] = ` +"/* @flow */ + +// x instancof t +class X1 { foo: number; }; +class X2 { foo: string; }; + +function x(b) { return b ? new X1 : new X2; } + +function consumer1(b) { + var g = x(b); + if (g instanceof X2) g.foo = \'1337\'; + else g.foo = 1337; +} + +function consumer2(b) { + var g = x(b); + if (g instanceof X1) g.foo = \'1337\'; // oops +} + +// x.y instanceof t +class Y1 { bar: X1; }; +class Y2 { bar: X2; }; + +function y(b) { return b ? new Y1 : new Y2; } + +function consumer3(b) { + var g = y(b); + if (g.bar instanceof X2) g.bar.foo = \'1337\'; + else g.bar.foo = 1337; +} + +function consumer4(b) { + var g = y(b); + if (g.bar instanceof X1) g.bar.foo = \'1337\'; // oops +} + +// x.y.z instance of t +class Z1 { baz: Y1; }; +class Z2 { baz: Y2; }; + +function z(b) { return b ? new Z1 : new Z2; } + +function consumer5(b) { + var g = z(b); + if (g.baz.bar instanceof X2) g.baz.bar.foo = \'1337\'; + else g.baz.bar.foo = 1337; +} + +function consumer6(b) { + var g = z(b); + if (g.baz.bar instanceof X1) g.baz.bar.foo = \'1337\'; // oops +} + +// this instanceof t +class C { + m() { + if (this instanceof D) + alert(this.s); + else + alert(\"nope\"); + } +} + +class D extends C { + s: string; + constructor() { + super(); + this.s = \"yup\"; + } +} + + +function foo0(x: Array | number) { + if (x instanceof Array) { + x[0] = 123; + } else { + x++; + } +} + +function foo1(x: Array | number) { + if (x instanceof Array) { + x++; // error + } else { + x[0] = 123; // error + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/instanceof/instanceof.js b/tests/instanceof/instanceof.js new file mode 100644 index 000000000000..bcbe7f6b8dc8 --- /dev/null +++ b/tests/instanceof/instanceof.js @@ -0,0 +1,87 @@ +/* @flow */ + +// x instancof t +class X1 { foo: number; }; +class X2 { foo: string; }; + +function x(b) { return b ? new X1 : new X2; } + +function consumer1(b) { + var g = x(b); + if (g instanceof X2) g.foo = '1337'; + else g.foo = 1337; +} + +function consumer2(b) { + var g = x(b); + if (g instanceof X1) g.foo = '1337'; // oops +} + +// x.y instanceof t +class Y1 { bar: X1; }; +class Y2 { bar: X2; }; + +function y(b) { return b ? new Y1 : new Y2; } + +function consumer3(b) { + var g = y(b); + if (g.bar instanceof X2) g.bar.foo = '1337'; + else g.bar.foo = 1337; +} + +function consumer4(b) { + var g = y(b); + if (g.bar instanceof X1) g.bar.foo = '1337'; // oops +} + +// x.y.z instance of t +class Z1 { baz: Y1; }; +class Z2 { baz: Y2; }; + +function z(b) { return b ? new Z1 : new Z2; } + +function consumer5(b) { + var g = z(b); + if (g.baz.bar instanceof X2) g.baz.bar.foo = '1337'; + else g.baz.bar.foo = 1337; +} + +function consumer6(b) { + var g = z(b); + if (g.baz.bar instanceof X1) g.baz.bar.foo = '1337'; // oops +} + +// this instanceof t +class C { + m() { + if (this instanceof D) + alert(this.s); + else + alert("nope"); + } +} + +class D extends C { + s: string; + constructor() { + super(); + this.s = "yup"; + } +} + + +function foo0(x: Array | number) { + if (x instanceof Array) { + x[0] = 123; + } else { + x++; + } +} + +function foo1(x: Array | number) { + if (x instanceof Array) { + x++; // error + } else { + x[0] = 123; // error + } +} diff --git a/tests/instanceof/jsfmt.spec.js b/tests/instanceof/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/instanceof/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/integration/__snapshots__/jsfmt.spec.js.snap b/tests/integration/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..4ca888585bc7 --- /dev/null +++ b/tests/integration/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,17 @@ +exports[`test bar.js 1`] = ` +"// @flow +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; + +exports[`test foo.js 1`] = ` +"// @flow +require('./bar'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +require("./bar"); + +" +`; diff --git a/tests/integration/bar.js b/tests/integration/bar.js new file mode 100644 index 000000000000..46e7f7c04567 --- /dev/null +++ b/tests/integration/bar.js @@ -0,0 +1 @@ +// @flow diff --git a/tests/integration/foo.js b/tests/integration/foo.js new file mode 100644 index 000000000000..69089d4e9298 --- /dev/null +++ b/tests/integration/foo.js @@ -0,0 +1,2 @@ +// @flow +require('./bar'); diff --git a/tests/integration/jsfmt.spec.js b/tests/integration/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/integration/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/interface/__snapshots__/jsfmt.spec.js.snap b/tests/interface/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..b612ebc1a585 --- /dev/null +++ b/tests/interface/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,211 @@ +exports[`test import.js 1`] = ` +"interface I { x: number } +export type J = I; // workaround for export interface +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1391 + fromString(\", \").join(path.map(print, \"extends\")) + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1391:28) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:238:16 + at FastPath.call (/src/fast-path.js:113:16) +" +`; + +exports[`test indexer.js 1`] = ` +"// @flow + +interface Ok { + [key: string]: string; +} + +interface Bad { + [k1: string]: string; + [k2: number]: number; // error: not supported (yet) +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1391 + fromString(\", \").join(path.map(print, \"extends\")) + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1391:28) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:238:16 + at FastPath.call (/src/fast-path.js:113:16) +" +`; + +exports[`test interface.js 1`] = ` +"declare class C { x: number; } + +var x: string = new C().x; + +interface I { x: number; } + +var i = new I(); // error + +function testInterfaceName(o: I) { + (o.name: string); // error, name is static + (o.constructor.name: string); // ok +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1391 + fromString(\", \").join(path.map(print, \"extends\")) + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1391:28) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:238:16 + at FastPath.call (/src/fast-path.js:113:16) +" +`; + +exports[`test test.js 1`] = ` +"interface I { y: string } +interface I_ { x: number } +interface J extends I, I_ { } +interface K extends J { } + +var k: K = { x: \"\", y: \"\" }; // error: x should be number +(k.x: string); // error: x is number +(k.y: string); + +declare class C { x: number } +declare class D extends C, Other { } // error: multiple extends +//declare class E implements I { } // parse error + +interface A { y: Y } +interface A_ { x: X } +interface B extends A, A_ { z: Z } +interface E extends B { } + +var e: E = { x: \"\", y: \"\", z: \"\" }; // error: x and z should be numbers +(e.x: string); // error: x is number +(e.y: string); +(e.z: string); // error: z is number +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1391 + fromString(\", \").join(path.map(print, \"extends\")) + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1391:28) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:238:16 + at FastPath.call (/src/fast-path.js:113:16) +" +`; + +exports[`test test2.js 1`] = ` +"import type { J } from \'./import\'; +interface K { } +interface L extends J, K { y: string } + +function foo(l: L) { l.x; l.y; l.z; } // error: z not found in L + +// interface + multiple inheritance is similar to object type + intersection +type M = { y: string } & J & { z: boolean } + +function bar(m: M) { m.x; m.y; m.z; } // OK +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1391 + fromString(\", \").join(path.map(print, \"extends\")) + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1391:28) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:238:16 + at FastPath.call (/src/fast-path.js:113:16) +" +`; + +exports[`test test3.js 1`] = ` +"interface I { x: number, y : string } +interface J { y : number } +interface K extends I, J { x: string } // error: x is number in I +function foo(k: K) { + (k.x: number); // error: x is string in K + (k.y: number); // error: y is string in I +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1391 + fromString(\", \").join(path.map(print, \"extends\")) + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1391:28) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:238:16 + at FastPath.call (/src/fast-path.js:113:16) +" +`; + +exports[`test test4.js 1`] = ` +"interface I { foo(x: number): void; } +(function foo(x: number) { }: I); // error, property \`foo\` not found function + +declare class C { + bar(i: I): void; + bar(f: (x: number) => void): void; +} + +new C().bar((x: string) => { }); // error, number ~/~> string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1391 + fromString(\", \").join(path.map(print, \"extends\")) + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1391:28) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:238:16 + at FastPath.call (/src/fast-path.js:113:16) +" +`; diff --git a/tests/interface/import.js b/tests/interface/import.js new file mode 100644 index 000000000000..1eba66eac7cb --- /dev/null +++ b/tests/interface/import.js @@ -0,0 +1,2 @@ +interface I { x: number } +export type J = I; // workaround for export interface diff --git a/tests/interface/indexer.js b/tests/interface/indexer.js new file mode 100644 index 000000000000..57d9204eabc5 --- /dev/null +++ b/tests/interface/indexer.js @@ -0,0 +1,10 @@ +// @flow + +interface Ok { + [key: string]: string; +} + +interface Bad { + [k1: string]: string; + [k2: number]: number; // error: not supported (yet) +} diff --git a/tests/interface/interface.js b/tests/interface/interface.js new file mode 100644 index 000000000000..40a01e3d02bd --- /dev/null +++ b/tests/interface/interface.js @@ -0,0 +1,12 @@ +declare class C { x: number; } + +var x: string = new C().x; + +interface I { x: number; } + +var i = new I(); // error + +function testInterfaceName(o: I) { + (o.name: string); // error, name is static + (o.constructor.name: string); // ok +} diff --git a/tests/interface/jsfmt.spec.js b/tests/interface/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/interface/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/interface/test.js b/tests/interface/test.js new file mode 100644 index 000000000000..1b071ece569f --- /dev/null +++ b/tests/interface/test.js @@ -0,0 +1,22 @@ +interface I { y: string } +interface I_ { x: number } +interface J extends I, I_ { } +interface K extends J { } + +var k: K = { x: "", y: "" }; // error: x should be number +(k.x: string); // error: x is number +(k.y: string); + +declare class C { x: number } +declare class D extends C, Other { } // error: multiple extends +//declare class E implements I { } // parse error + +interface A { y: Y } +interface A_ { x: X } +interface B extends A, A_ { z: Z } +interface E extends B { } + +var e: E = { x: "", y: "", z: "" }; // error: x and z should be numbers +(e.x: string); // error: x is number +(e.y: string); +(e.z: string); // error: z is number diff --git a/tests/interface/test2.js b/tests/interface/test2.js new file mode 100644 index 000000000000..0ef83b294e2e --- /dev/null +++ b/tests/interface/test2.js @@ -0,0 +1,10 @@ +import type { J } from './import'; +interface K { } +interface L extends J, K { y: string } + +function foo(l: L) { l.x; l.y; l.z; } // error: z not found in L + +// interface + multiple inheritance is similar to object type + intersection +type M = { y: string } & J & { z: boolean } + +function bar(m: M) { m.x; m.y; m.z; } // OK diff --git a/tests/interface/test3.js b/tests/interface/test3.js new file mode 100644 index 000000000000..a06a374a1dd9 --- /dev/null +++ b/tests/interface/test3.js @@ -0,0 +1,7 @@ +interface I { x: number, y : string } +interface J { y : number } +interface K extends I, J { x: string } // error: x is number in I +function foo(k: K) { + (k.x: number); // error: x is string in K + (k.y: number); // error: y is string in I +} diff --git a/tests/interface/test4.js b/tests/interface/test4.js new file mode 100644 index 000000000000..91246524d72a --- /dev/null +++ b/tests/interface/test4.js @@ -0,0 +1,9 @@ +interface I { foo(x: number): void; } +(function foo(x: number) { }: I); // error, property `foo` not found function + +declare class C { + bar(i: I): void; + bar(f: (x: number) => void): void; +} + +new C().bar((x: string) => { }); // error, number ~/~> string diff --git a/tests/intersection/__snapshots__/jsfmt.spec.js.snap b/tests/intersection/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..97de30df4f01 --- /dev/null +++ b/tests/intersection/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,207 @@ +exports[`test intersection.js 1`] = ` +"function foo(x: $All): number { + return x.type; +} + +function bar(x: Error & {type:number}): number { + return x.type; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test objassign.js 1`] = ` +"/** + * Test intersection of objects flowing to spread assignment. + * + * Definitions in lib/lib.js + * + * @noflow + */ + +declare var x: ObjAssignT; + +let y: ObjAssignT = { ...x }; // should be fine +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * Test intersection of objects flowing to spread assignment. + * + * Definitions in lib/lib.js + * + * @noflow + */ +declare var x: ObjAssignT; +let y: ObjAssignT = { ...x };// should be fine + +" +`; + +exports[`test pred.js 1`] = ` +"/** + * Test interaction of object intersections and predicates. + * Definitions in lib/lib.js + * + * @flow + */ + +type DuplexStreamOptions = ReadableStreamOptions & WritableStreamOptions & { + allowHalfOpen? : boolean, + readableObjectMode? : boolean, + writableObjectMode? : boolean +}; + +function hasObjectMode_bad(options: DuplexStreamOptions): boolean { + return options.objectMode + || options.readableObjectMode + || options.writableObjectMode; // error, undefined ~> boolean +} + +function hasObjectMode_ok(options: DuplexStreamOptions): boolean { + return !!(options.objectMode + || options.readableObjectMode + || options.writableObjectMode); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1407 + return fromString(\" & \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1407:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test_fun.js 1`] = ` +"/** + * Tests for intersections of function types. + * + * Note: Flow abuses intersection types to model + * function overloading, which precludes using a + * correct intersection of return types in the result. + * + * Here we test the special case where return types + * are equal. Tests of the overloading behavior can + * be found in tests/overload + * + * Definitions lin lib/lib.js + * + * @noflow + */ + +// intersection of function types satisfies union of param types + +type F = (_: ObjA) => void; +type G = (_: ObjB) => void; +type FG = (_: ObjA | ObjB) => void; + +declare var fun1 : F & G; + +(fun1 : FG); + +var fun2 : FG = fun1; + +// simpler variation +declare var f : ((_: number) => void) & ((_: string) => void); +var g: (_: number | string) => void = f; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test_obj.js 1`] = ` +"/** + * Tests for intersections of object types + * + * @noflow + */ + +// TODO we should give explicit errors for incompatibilities +// which make an intersection uninhabitable: +// - shared mutable properties with different types +// - different dictionary types +// +// Currently we give no such errors. Instead, we rely on +// the impossibility of providing a value for such a type +// to produce errors on value inflows. This is clearly +// suboptimal, since eg declared vars require no explicit +// provision of values. This leaves the impossible types +// free to flow downstream and satisfy impossible constraints. + +// intersection of object types satisfies union of properties +declare var a: A; +var b: B = a; + +// intersection of dictionary types: +declare var c: C; +var d: D = c; // ok + +// dict type mismatch +type E = { [key: string]: string }; +var e: E = c; // error +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * Tests for intersections of object types + * + * @noflow + */ +// TODO we should give explicit errors for incompatibilities +// which make an intersection uninhabitable: +// - shared mutable properties with different types +// - different dictionary types +// +// Currently we give no such errors. Instead, we rely on +// the impossibility of providing a value for such a type +// to produce errors on value inflows. This is clearly +// suboptimal, since eg declared vars require no explicit +// provision of values. This leaves the impossible types +// free to flow downstream and satisfy impossible constraints. +// intersection of object types satisfies union of properties +declare var a: A; +var b: B = a; +// intersection of dictionary types: +declare var c: C; +var d: D = c;// ok +// dict type mismatch +type E = { [key: string]: string }; +var e: E = c;// error + +" +`; diff --git a/tests/intersection/intersection.js b/tests/intersection/intersection.js new file mode 100644 index 000000000000..7508926e5902 --- /dev/null +++ b/tests/intersection/intersection.js @@ -0,0 +1,7 @@ +function foo(x: $All): number { + return x.type; +} + +function bar(x: Error & {type:number}): number { + return x.type; +} diff --git a/tests/intersection/jsfmt.spec.js b/tests/intersection/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/intersection/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/intersection/lib/__snapshots__/jsfmt.spec.js.snap b/tests/intersection/lib/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..17c299687982 --- /dev/null +++ b/tests/intersection/lib/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,53 @@ +exports[`test lib.js 1`] = ` +"// @flow + +// defs used in tests, here to stress tvar machinery + +// used in ObjAssign.js +// +type ObjAssignT = { foo: string } & { bar: string }; + +// used in pred.js +// +type ReadableStreamOptions = { + highWaterMark? : number, + encoding? : ?string, + objectMode? : boolean +}; + +type WritableStreamOptions = { + highWaterMark? : number, + decodeString? : boolean, + objectMode? : boolean +}; + +// used in test_fun.js +// +type ObjA = { foo: number, bar: string }; +type ObjB = { baz: bool }; + +// used in test_obj.js +// +type A = { a: string } & { b: string }; +type B = { a: string, b: string }; + +type C = { [key: string]: number } & { [key: string]: number }; +type D = { [key: string]: number }; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1407 + return fromString(\" & \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1407:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/intersection/lib/jsfmt.spec.js b/tests/intersection/lib/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/intersection/lib/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/intersection/lib/lib.js b/tests/intersection/lib/lib.js new file mode 100644 index 000000000000..df9d7d3f207e --- /dev/null +++ b/tests/intersection/lib/lib.js @@ -0,0 +1,34 @@ +// @flow + +// defs used in tests, here to stress tvar machinery + +// used in ObjAssign.js +// +type ObjAssignT = { foo: string } & { bar: string }; + +// used in pred.js +// +type ReadableStreamOptions = { + highWaterMark? : number, + encoding? : ?string, + objectMode? : boolean +}; + +type WritableStreamOptions = { + highWaterMark? : number, + decodeString? : boolean, + objectMode? : boolean +}; + +// used in test_fun.js +// +type ObjA = { foo: number, bar: string }; +type ObjB = { baz: bool }; + +// used in test_obj.js +// +type A = { a: string } & { b: string }; +type B = { a: string, b: string }; + +type C = { [key: string]: number } & { [key: string]: number }; +type D = { [key: string]: number }; diff --git a/tests/intersection/objassign.js b/tests/intersection/objassign.js new file mode 100644 index 000000000000..fc0436123c65 --- /dev/null +++ b/tests/intersection/objassign.js @@ -0,0 +1,11 @@ +/** + * Test intersection of objects flowing to spread assignment. + * + * Definitions in lib/lib.js + * + * @noflow + */ + +declare var x: ObjAssignT; + +let y: ObjAssignT = { ...x }; // should be fine diff --git a/tests/intersection/pred.js b/tests/intersection/pred.js new file mode 100644 index 000000000000..37a0d13beadf --- /dev/null +++ b/tests/intersection/pred.js @@ -0,0 +1,24 @@ +/** + * Test interaction of object intersections and predicates. + * Definitions in lib/lib.js + * + * @flow + */ + +type DuplexStreamOptions = ReadableStreamOptions & WritableStreamOptions & { + allowHalfOpen? : boolean, + readableObjectMode? : boolean, + writableObjectMode? : boolean +}; + +function hasObjectMode_bad(options: DuplexStreamOptions): boolean { + return options.objectMode + || options.readableObjectMode + || options.writableObjectMode; // error, undefined ~> boolean +} + +function hasObjectMode_ok(options: DuplexStreamOptions): boolean { + return !!(options.objectMode + || options.readableObjectMode + || options.writableObjectMode); +} diff --git a/tests/intersection/test_fun.js b/tests/intersection/test_fun.js new file mode 100644 index 000000000000..b9b2f81074bb --- /dev/null +++ b/tests/intersection/test_fun.js @@ -0,0 +1,31 @@ +/** + * Tests for intersections of function types. + * + * Note: Flow abuses intersection types to model + * function overloading, which precludes using a + * correct intersection of return types in the result. + * + * Here we test the special case where return types + * are equal. Tests of the overloading behavior can + * be found in tests/overload + * + * Definitions lin lib/lib.js + * + * @noflow + */ + +// intersection of function types satisfies union of param types + +type F = (_: ObjA) => void; +type G = (_: ObjB) => void; +type FG = (_: ObjA | ObjB) => void; + +declare var fun1 : F & G; + +(fun1 : FG); + +var fun2 : FG = fun1; + +// simpler variation +declare var f : ((_: number) => void) & ((_: string) => void); +var g: (_: number | string) => void = f; diff --git a/tests/intersection/test_obj.js b/tests/intersection/test_obj.js new file mode 100644 index 000000000000..6817584708ab --- /dev/null +++ b/tests/intersection/test_obj.js @@ -0,0 +1,29 @@ +/** + * Tests for intersections of object types + * + * @noflow + */ + +// TODO we should give explicit errors for incompatibilities +// which make an intersection uninhabitable: +// - shared mutable properties with different types +// - different dictionary types +// +// Currently we give no such errors. Instead, we rely on +// the impossibility of providing a value for such a type +// to produce errors on value inflows. This is clearly +// suboptimal, since eg declared vars require no explicit +// provision of values. This leaves the impossible types +// free to flow downstream and satisfy impossible constraints. + +// intersection of object types satisfies union of properties +declare var a: A; +var b: B = a; + +// intersection of dictionary types: +declare var c: C; +var d: D = c; // ok + +// dict type mismatch +type E = { [key: string]: string }; +var e: E = c; // error diff --git a/tests/issues-11/__snapshots__/jsfmt.spec.js.snap b/tests/issues-11/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..b9b2b0a3fefb --- /dev/null +++ b/tests/issues-11/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,25 @@ +exports[`test export.js 1`] = ` +"/* @flow */ +exports.x = 1; +exports.y = ""; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +exports.x = 1; +exports.y = ""; + +" +`; + +exports[`test import.js 1`] = ` +"/* @flow */ +var e = require('./export'); +var x: string = e.x; +var y: number = e.y; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var e = require("./export"); +var x: string = e.x; +var y: number = e.y; + +" +`; diff --git a/tests/issues-11/export.js b/tests/issues-11/export.js new file mode 100644 index 000000000000..0fe9283762c9 --- /dev/null +++ b/tests/issues-11/export.js @@ -0,0 +1,3 @@ +/* @flow */ +exports.x = 1; +exports.y = ""; diff --git a/tests/issues-11/import.js b/tests/issues-11/import.js new file mode 100644 index 000000000000..11d913911e42 --- /dev/null +++ b/tests/issues-11/import.js @@ -0,0 +1,4 @@ +/* @flow */ +var e = require('./export'); +var x: string = e.x; +var y: number = e.y; diff --git a/tests/issues-11/jsfmt.spec.js b/tests/issues-11/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/issues-11/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/iter/__snapshots__/jsfmt.spec.js.snap b/tests/iter/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..0695d0d737bb --- /dev/null +++ b/tests/iter/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,73 @@ +exports[`test iter.js 1`] = ` +"var a = [true,false]; +function foo(x) { } + +for (var i=0;i<3;i++) { + foo(a[i]); +} +for (var k in a) { + foo(a[k]); // k is a string, which shouldn't be used for array access +} + +var b = (null : ?{[key: string]: string}); +for (var j in b) { + foo(b[j]); +} + +var c; +for (var m in (c = b)) { + foo(c[m]); +} + +var d; +for (var n in (d = a)) { + foo(d[n]); // d is a string, which shouldn't be used for array access +} + +for (var x in undefined) { + foo(x); // unreachable +} + +for (var x in null) { + foo(x); // unreachable +} + +for (var y in this) { + // regression test to make sure \`in this\` doesn't fatal. it's currently + // allowed, even though we can't actually enumerate all the keys on \`this\`. +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var a = [ true, false ]; +function foo(x) { + +} +for (var i = 0; i < 3; i++) { + foo(a[i]); +} +for (var k in a) { + foo(a[k]);// k is a string, which shouldn't be used for array access +} +var b = (null: ?{ [key: string]: string }); +for (var j in b) { + foo(b[j]); +} +var c; +for (var m in c = b) { + foo(c[m]); +} +var d; +for (var n in d = a) { + foo(d[n]);// d is a string, which shouldn't be used for array access +} +for (var x in undefined) { + foo(x);// unreachable +} +for (var x in null) { + foo(x);// unreachable +} +for (var y in this) { + +} + +" +`; diff --git a/tests/iter/iter.js b/tests/iter/iter.js new file mode 100644 index 000000000000..62241fd8edcc --- /dev/null +++ b/tests/iter/iter.js @@ -0,0 +1,37 @@ +var a = [true,false]; +function foo(x) { } + +for (var i=0;i<3;i++) { + foo(a[i]); +} +for (var k in a) { + foo(a[k]); // k is a string, which shouldn't be used for array access +} + +var b = (null : ?{[key: string]: string}); +for (var j in b) { + foo(b[j]); +} + +var c; +for (var m in (c = b)) { + foo(c[m]); +} + +var d; +for (var n in (d = a)) { + foo(d[n]); // d is a string, which shouldn't be used for array access +} + +for (var x in undefined) { + foo(x); // unreachable +} + +for (var x in null) { + foo(x); // unreachable +} + +for (var y in this) { + // regression test to make sure `in this` doesn't fatal. it's currently + // allowed, even though we can't actually enumerate all the keys on `this`. +} diff --git a/tests/iter/jsfmt.spec.js b/tests/iter/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/iter/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/iterable/__snapshots__/jsfmt.spec.js.snap b/tests/iterable/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..c0ec883c959b --- /dev/null +++ b/tests/iterable/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,270 @@ +exports[`test array.js 1`] = ` +"/* @flow */ + +var arrayTest1: Iterable = ([1, 2]: Array); +var arrayTest2: Iterable = [1,2,\"hi\"]; +var arrayTest3: Iterable<*> = [1,2,3]; + +// Error string ~> number +var arrayTest4: Iterable = [\"hi\"]; +// Error string ~> number +var arrayTest5: Iterable = [\"hi\", 1]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test caching_bug.js 1`] = ` +"/* @flow */ + +/** + * I\'ve hit a bug with the caching in flow_js.ml. Avik is removing that caching + * so it should be fixed soon. The basic idea is I flow something like + * + * Array ~> Iterable + * + * then Flow won\'t notice when I try to flow + * + * Array ~> Iterable + * + * We shouldn\'t hit the cache because the union types are different, but we do + * anyway. I\'ve fixed this temporarily by bumping the \"meaningful\" param to + * Hashtbl.hash_param + */ + +function fill_the_cache(x: Array): Iterable { return x; } + +// Error: number ~> string +function miss_the_cache(x: Array): Iterable { return x; } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1497:36) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test iter.js 1`] = ` +"/* @flow */ + +function foo(strs: Iterable): void { + for (var s: string of strs) { + console.log(s); + } +} + +var m: Map = new Map(); + +foo(m.keys()); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test iterator_result.js 1`] = ` +"/* @flow */ + +function makeIterator(coin_flip: () => boolean ): Iterator { + return { + \"@@iterator\"() { return makeIterator(coin_flip); }, + next(): IteratorResult { + var done = coin_flip(); + if (!done) { + return { done, value: \"still going...\" }; + } else { + return { done }; + } + } + } +} + +function makeIterator(coin_flip: () => boolean ): Iterator { + return { + \"@@iterator\"() { return makeIterator(coin_flip); }, + next(): IteratorResult { + var done = coin_flip(); + if (done) { // Whoops, made a mistake and forgot to negate done + return { done, value: \"still going...\" }; // Error string ~> void + } else { + return { done }; // Error void ~> string + } + } + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test map.js 1`] = ` +"/* @flow */ + +function mapTest1(map: Map): Iterable<[string, number]> { + return map; +} +function mapTest2(map: Map): Iterable<[K, V]> { + return map; +}; +function mapTest3(map: Map): Iterable<*> { + return map; +} +// Error - Map is an Iterable<[K, V]> +function mapTest4(map: Map): Iterable { + return map; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test set.js 1`] = ` +"/* @flow */ + +function setTest1(set: Set): Iterable { + return set; +} +function setTest2(set: Set): Iterable { + return set; +}; +function setTest3(set: Set): Iterable<*> { + return set; +} +// Error string ~> number +function setTest4(set: Set): Iterable { + return set; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test string.js 1`] = ` +"/* @flow */ + +var stringTest1: Iterable = \"hi\"; +var stringTest3: Iterable<*> = \"hi\"; +var stringTest3: Iterable = \"hi\"; // Error - string is a Iterable +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test variance.js 1`] = ` +"/* @flow */ + +(([]: Array): Iterable); // ok, Iterable<+T> + +(([]: Array).values(): Iterable); // ok, Iterator<+T> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/iterable/array.js b/tests/iterable/array.js new file mode 100644 index 000000000000..c50c20f9352e --- /dev/null +++ b/tests/iterable/array.js @@ -0,0 +1,10 @@ +/* @flow */ + +var arrayTest1: Iterable = ([1, 2]: Array); +var arrayTest2: Iterable = [1,2,"hi"]; +var arrayTest3: Iterable<*> = [1,2,3]; + +// Error string ~> number +var arrayTest4: Iterable = ["hi"]; +// Error string ~> number +var arrayTest5: Iterable = ["hi", 1]; diff --git a/tests/iterable/caching_bug.js b/tests/iterable/caching_bug.js new file mode 100644 index 000000000000..6fffea9f95bb --- /dev/null +++ b/tests/iterable/caching_bug.js @@ -0,0 +1,21 @@ +/* @flow */ + +/** + * I've hit a bug with the caching in flow_js.ml. Avik is removing that caching + * so it should be fixed soon. The basic idea is I flow something like + * + * Array ~> Iterable + * + * then Flow won't notice when I try to flow + * + * Array ~> Iterable + * + * We shouldn't hit the cache because the union types are different, but we do + * anyway. I've fixed this temporarily by bumping the "meaningful" param to + * Hashtbl.hash_param + */ + +function fill_the_cache(x: Array): Iterable { return x; } + +// Error: number ~> string +function miss_the_cache(x: Array): Iterable { return x; } diff --git a/tests/iterable/iter.js b/tests/iterable/iter.js new file mode 100644 index 000000000000..b8c3d9cf1d9d --- /dev/null +++ b/tests/iterable/iter.js @@ -0,0 +1,11 @@ +/* @flow */ + +function foo(strs: Iterable): void { + for (var s: string of strs) { + console.log(s); + } +} + +var m: Map = new Map(); + +foo(m.keys()); diff --git a/tests/iterable/iterator_result.js b/tests/iterable/iterator_result.js new file mode 100644 index 000000000000..cb0b0b162c21 --- /dev/null +++ b/tests/iterable/iterator_result.js @@ -0,0 +1,29 @@ +/* @flow */ + +function makeIterator(coin_flip: () => boolean ): Iterator { + return { + "@@iterator"() { return makeIterator(coin_flip); }, + next(): IteratorResult { + var done = coin_flip(); + if (!done) { + return { done, value: "still going..." }; + } else { + return { done }; + } + } + } +} + +function makeIterator(coin_flip: () => boolean ): Iterator { + return { + "@@iterator"() { return makeIterator(coin_flip); }, + next(): IteratorResult { + var done = coin_flip(); + if (done) { // Whoops, made a mistake and forgot to negate done + return { done, value: "still going..." }; // Error string ~> void + } else { + return { done }; // Error void ~> string + } + } + } +} diff --git a/tests/iterable/jsfmt.spec.js b/tests/iterable/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/iterable/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/iterable/map.js b/tests/iterable/map.js new file mode 100644 index 000000000000..8125f7abf5c3 --- /dev/null +++ b/tests/iterable/map.js @@ -0,0 +1,15 @@ +/* @flow */ + +function mapTest1(map: Map): Iterable<[string, number]> { + return map; +} +function mapTest2(map: Map): Iterable<[K, V]> { + return map; +}; +function mapTest3(map: Map): Iterable<*> { + return map; +} +// Error - Map is an Iterable<[K, V]> +function mapTest4(map: Map): Iterable { + return map; +} diff --git a/tests/iterable/set.js b/tests/iterable/set.js new file mode 100644 index 000000000000..2251e52924e7 --- /dev/null +++ b/tests/iterable/set.js @@ -0,0 +1,15 @@ +/* @flow */ + +function setTest1(set: Set): Iterable { + return set; +} +function setTest2(set: Set): Iterable { + return set; +}; +function setTest3(set: Set): Iterable<*> { + return set; +} +// Error string ~> number +function setTest4(set: Set): Iterable { + return set; +} diff --git a/tests/iterable/string.js b/tests/iterable/string.js new file mode 100644 index 000000000000..bae6cec8e657 --- /dev/null +++ b/tests/iterable/string.js @@ -0,0 +1,5 @@ +/* @flow */ + +var stringTest1: Iterable = "hi"; +var stringTest3: Iterable<*> = "hi"; +var stringTest3: Iterable = "hi"; // Error - string is a Iterable diff --git a/tests/iterable/variance.js b/tests/iterable/variance.js new file mode 100644 index 000000000000..634c41db3c9f --- /dev/null +++ b/tests/iterable/variance.js @@ -0,0 +1,5 @@ +/* @flow */ + +(([]: Array): Iterable); // ok, Iterable<+T> + +(([]: Array).values(): Iterable); // ok, Iterator<+T> diff --git a/tests/jsx_intrinsics.builtin/__snapshots__/jsfmt.spec.js.snap b/tests/jsx_intrinsics.builtin/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..28d087d9ab26 --- /dev/null +++ b/tests/jsx_intrinsics.builtin/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,76 @@ +exports[`test main.js 1`] = ` +"// @flow + +var React = require(\'react\'); + +class CustomComponent extends React.Component { + props: { + prop: string + }; +} + +var a: React.Element<{prop: string}> = ; +var b: React.Element<{prop1: string}> = ; // Error: Props<{prop}> ~> Props<{prop1}> + +// Since intrinsics are typed as \`any\` out of the box, we can pass any +// attributes to intrinsics! +var c: React.Element =
; +// However, we don\'t allow such elements to be viewed as React elements with +// different attributes. +var d: React.Element<{doesntmatch: string}> =
; +// No error as long as expectations are consistent, though. +var e: React.Element<{not_a_real_attr: string}> =
; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test strings.js 1`] = ` +"/* @flow */ + +var React = require(\'react\'); + +// The builtin $JSXIntrinsics should allow any string + +var Div = \'div\'; +var Bad = \'bad\'; +var Str: string = \'str\'; + +
; // This is fine +; // This is fine +; // This is fine + +React.createElement(\'div\', {}); // This is fine +React.createElement(\'bad\', {}); // This is fine + +
; // This is fine +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var React = require(\"react\"); +// The builtin $JSXIntrinsics should allow any string +var Div = \"div\"; +var Bad = \"bad\"; +var Str: string = \"str\"; +
;// This is fine +;// This is fine +;// This is fine +React.createElement(\"div\", {});// This is fine +React.createElement(\"bad\", {});// This is fine +
;// This is fine + +" +`; diff --git a/tests/jsx_intrinsics.builtin/jsfmt.spec.js b/tests/jsx_intrinsics.builtin/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/jsx_intrinsics.builtin/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/jsx_intrinsics.builtin/main.js b/tests/jsx_intrinsics.builtin/main.js new file mode 100644 index 000000000000..4b7ee5a28510 --- /dev/null +++ b/tests/jsx_intrinsics.builtin/main.js @@ -0,0 +1,21 @@ +// @flow + +var React = require('react'); + +class CustomComponent extends React.Component { + props: { + prop: string + }; +} + +var a: React.Element<{prop: string}> = ; +var b: React.Element<{prop1: string}> = ; // Error: Props<{prop}> ~> Props<{prop1}> + +// Since intrinsics are typed as `any` out of the box, we can pass any +// attributes to intrinsics! +var c: React.Element =
; +// However, we don't allow such elements to be viewed as React elements with +// different attributes. +var d: React.Element<{doesntmatch: string}> =
; +// No error as long as expectations are consistent, though. +var e: React.Element<{not_a_real_attr: string}> =
; diff --git a/tests/jsx_intrinsics.builtin/strings.js b/tests/jsx_intrinsics.builtin/strings.js new file mode 100644 index 000000000000..f63649cec5f7 --- /dev/null +++ b/tests/jsx_intrinsics.builtin/strings.js @@ -0,0 +1,18 @@ +/* @flow */ + +var React = require('react'); + +// The builtin $JSXIntrinsics should allow any string + +var Div = 'div'; +var Bad = 'bad'; +var Str: string = 'str'; + +
; // This is fine +; // This is fine +; // This is fine + +React.createElement('div', {}); // This is fine +React.createElement('bad', {}); // This is fine + +
; // This is fine diff --git a/tests/jsx_intrinsics.custom/__snapshots__/jsfmt.spec.js.snap b/tests/jsx_intrinsics.custom/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..5034c898429f --- /dev/null +++ b/tests/jsx_intrinsics.custom/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,73 @@ +exports[`test main.js 1`] = ` +"// @flow + +var React = require(\'react\'); + +class CustomComponent extends React.Component { + props: { + prop: string + }; +} + +var a: React.Element<{prop: string}> = ; +var b: React.Element<{prop1: string}> = ; // Error: Props<{prop}> ~> Props<{prop1}> + +
; +
; // Error: (\`id\` prop) number ~> string +var c: React.Element<{id: string}> =
; +var d: React.Element<{id: number}> =
; // Error: Props<{id:string}> ~> Props<{id:number}> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test strings.js 1`] = ` +"/* @flow */ + +var React = require(\'react\'); + +var Div = \'div\'; +var Bad = \'bad\'; +var Str: string = \'str\'; + +
; // This is fine +; // Error: \'bad\' not in JSXIntrinsics +; // Error: string ~> keys of JSXIntrinsics + +React.createElement(\'div\', {}); // This is fine +React.createElement(\'bad\', {}); // Error: \'bad\' not in JSXIntrinsics +React.createElement(Str, {}); // Error: string ~> keys of JSXIntrinsics + +// TODO: Make this an error +
; // Not an error but should be eventually +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var React = require(\"react\"); +var Div = \"div\"; +var Bad = \"bad\"; +var Str: string = \"str\"; +
;// This is fine +;// Error: \'bad\' not in JSXIntrinsics +;// Error: string ~> keys of JSXIntrinsics +React.createElement(\"div\", {});// This is fine +React.createElement(\"bad\", {});// Error: \'bad\' not in JSXIntrinsics +React.createElement(Str, {});// Error: string ~> keys of JSXIntrinsics +// TODO: Make this an error +
;// Not an error but should be eventually + +" +`; diff --git a/tests/jsx_intrinsics.custom/jsfmt.spec.js b/tests/jsx_intrinsics.custom/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/jsx_intrinsics.custom/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/jsx_intrinsics.custom/lib/__snapshots__/jsfmt.spec.js.snap b/tests/jsx_intrinsics.custom/lib/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..1c2c02375402 --- /dev/null +++ b/tests/jsx_intrinsics.custom/lib/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,26 @@ +exports[`test jsx.js 1`] = ` +"declare var $React: $Exports<\'react\'>; // fake import +type $JSXIntrinsic = Class<$React.Component>; + +type $JSXIntrinsics = { + div: $JSXIntrinsic<{id: string}>, + span: $JSXIntrinsic<{id: string, class: string}>, +}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/jsx_intrinsics.custom/lib/jsfmt.spec.js b/tests/jsx_intrinsics.custom/lib/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/jsx_intrinsics.custom/lib/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/jsx_intrinsics.custom/lib/jsx.js b/tests/jsx_intrinsics.custom/lib/jsx.js new file mode 100644 index 000000000000..7de8608bc649 --- /dev/null +++ b/tests/jsx_intrinsics.custom/lib/jsx.js @@ -0,0 +1,7 @@ +declare var $React: $Exports<'react'>; // fake import +type $JSXIntrinsic = Class<$React.Component>; + +type $JSXIntrinsics = { + div: $JSXIntrinsic<{id: string}>, + span: $JSXIntrinsic<{id: string, class: string}>, +}; diff --git a/tests/jsx_intrinsics.custom/main.js b/tests/jsx_intrinsics.custom/main.js new file mode 100644 index 000000000000..b1f753a4c5c3 --- /dev/null +++ b/tests/jsx_intrinsics.custom/main.js @@ -0,0 +1,17 @@ +// @flow + +var React = require('react'); + +class CustomComponent extends React.Component { + props: { + prop: string + }; +} + +var a: React.Element<{prop: string}> = ; +var b: React.Element<{prop1: string}> = ; // Error: Props<{prop}> ~> Props<{prop1}> + +
; +
; // Error: (`id` prop) number ~> string +var c: React.Element<{id: string}> =
; +var d: React.Element<{id: number}> =
; // Error: Props<{id:string}> ~> Props<{id:number}> diff --git a/tests/jsx_intrinsics.custom/strings.js b/tests/jsx_intrinsics.custom/strings.js new file mode 100644 index 000000000000..b11fb78d66a3 --- /dev/null +++ b/tests/jsx_intrinsics.custom/strings.js @@ -0,0 +1,18 @@ +/* @flow */ + +var React = require('react'); + +var Div = 'div'; +var Bad = 'bad'; +var Str: string = 'str'; + +
; // This is fine +; // Error: 'bad' not in JSXIntrinsics +; // Error: string ~> keys of JSXIntrinsics + +React.createElement('div', {}); // This is fine +React.createElement('bad', {}); // Error: 'bad' not in JSXIntrinsics +React.createElement(Str, {}); // Error: string ~> keys of JSXIntrinsics + +// TODO: Make this an error +
; // Not an error but should be eventually diff --git a/tests/keys/__snapshots__/jsfmt.spec.js.snap b/tests/keys/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..7e35a42698d3 --- /dev/null +++ b/tests/keys/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,64 @@ +exports[`test keys.js 1`] = ` +"/* @flow */ + +function testKeysOfObject(str: string, lit: \'hi\') { + (str: $Keys); // Any string should be fine + if (str) { + (str: $Keys); // No error, truthy string should be fine + } + (\'hi\': $Keys); // String literal should be fine + + (123: $Keys); // Error: number -> keys of Object +} + +type StrDict = {[key: string]: mixed}; +function testKeysOfStrDict(str: string, lit: \'hi\') { + (str: $Keys); // Any string should be fine + if (str) { + (str: $Keys); // No error, truthy string should be fine + } + (\'hi\': $Keys); // String literal should be fine + + (123: $Keys); // Error: number -> keys of StrDict +} + +type StrLitDict = {[key: \'hi\']: mixed}; +function testKeysOfStrLitDict(str: string, lit: \'hi\') { + (str: $Keys); // Error: Not all strings are allowed + if (str) { + (str: $Keys); // Error: Not all truthy strings are allowed + } + (\'hi\': $Keys); // The right string literal is allowed + (\'bye\': $Keys); // Error: The wrong string literal is not allowed + + (123: $Keys); // Error: number -> keys of StrLitDict +} + +type ObjLit = {hi: mixed}; +function testKeysOfOtherObj(str: string, lit: \'hi\') { + (str: $Keys); // Error: string -> keys of ObjLit + if (str) { + (str: $Keys); // Error: truthy string -> keys of ObjLit + } + (\'hi\': $Keys); // String literal should be fine + + (123: $Keys); // Error: number -> keys of ObjLit +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/keys/jsfmt.spec.js b/tests/keys/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/keys/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/keys/keys.js b/tests/keys/keys.js new file mode 100644 index 000000000000..902bc52594ee --- /dev/null +++ b/tests/keys/keys.js @@ -0,0 +1,45 @@ +/* @flow */ + +function testKeysOfObject(str: string, lit: 'hi') { + (str: $Keys); // Any string should be fine + if (str) { + (str: $Keys); // No error, truthy string should be fine + } + ('hi': $Keys); // String literal should be fine + + (123: $Keys); // Error: number -> keys of Object +} + +type StrDict = {[key: string]: mixed}; +function testKeysOfStrDict(str: string, lit: 'hi') { + (str: $Keys); // Any string should be fine + if (str) { + (str: $Keys); // No error, truthy string should be fine + } + ('hi': $Keys); // String literal should be fine + + (123: $Keys); // Error: number -> keys of StrDict +} + +type StrLitDict = {[key: 'hi']: mixed}; +function testKeysOfStrLitDict(str: string, lit: 'hi') { + (str: $Keys); // Error: Not all strings are allowed + if (str) { + (str: $Keys); // Error: Not all truthy strings are allowed + } + ('hi': $Keys); // The right string literal is allowed + ('bye': $Keys); // Error: The wrong string literal is not allowed + + (123: $Keys); // Error: number -> keys of StrLitDict +} + +type ObjLit = {hi: mixed}; +function testKeysOfOtherObj(str: string, lit: 'hi') { + (str: $Keys); // Error: string -> keys of ObjLit + if (str) { + (str: $Keys); // Error: truthy string -> keys of ObjLit + } + ('hi': $Keys); // String literal should be fine + + (123: $Keys); // Error: number -> keys of ObjLit +} diff --git a/tests/keyvalue/__snapshots__/jsfmt.spec.js.snap b/tests/keyvalue/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..8505dac94a90 --- /dev/null +++ b/tests/keyvalue/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,18 @@ +exports[`test keyvalue.js 1`] = ` +"// @flow + +let tests = [ + function(x: { [key: number]: string }) { + (x[""]: number); + } +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +let tests = [ + function(x: { [key: number]: string }) { + (x[""]: number); + } +]; + +" +`; diff --git a/tests/keyvalue/jsfmt.spec.js b/tests/keyvalue/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/keyvalue/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/keyvalue/keyvalue.js b/tests/keyvalue/keyvalue.js new file mode 100644 index 000000000000..469501633e2c --- /dev/null +++ b/tests/keyvalue/keyvalue.js @@ -0,0 +1,7 @@ +// @flow + +let tests = [ + function(x: { [key: number]: string }) { + (x[""]: number); + } +]; diff --git a/tests/last_duplicate_property_wins/__snapshots__/jsfmt.spec.js.snap b/tests/last_duplicate_property_wins/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..4e569974d5d3 --- /dev/null +++ b/tests/last_duplicate_property_wins/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,95 @@ +exports[`test test.js 1`] = ` +"// @flow + +// Classes + +class C { + foo(): number { return 0; } + foo(): string { return "hello" } // last wins + x: number; + x: string; // last wins + bar(): number { return 0; } + bar: string; // field wins over method + qux: number; + qux(): string { return "hello" } // method loses to field! +} + +// check + +((new C).foo(): boolean); // last wins +((new C).x: boolean); // last wins +((new C).bar: boolean); // last wins +((new C).qux: boolean); // weird outlier where last doesn't win in classes + +// Objects + +const o = { + foo(): number { return 0; }, + foo(): string { return "hello" }, // last wins + x: 42, + x: "hello", // last wins + bar(): number { return 0; }, + bar: "hello", // last wins + qux: 42, + qux(): string { return "hello" }, // last wins +}; + +// check + +(o.foo(): boolean); // last wins +(o.x: boolean); // last wins +(o.bar: boolean); // last wins +(o.qux(): boolean); // last wins +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +// Classes +class C { + foo(): number { + return 0; + } + foo(): string { + return "hello"; + }// last wins + x: number; + x: string;// last wins + bar(): number { + return 0; + } + bar: string;// field wins over method + qux: number; + qux(): string { + return "hello"; + }// method loses to field! +} +// check +(new C().foo(): boolean);// last wins +(new C().x: boolean);// last wins +(new C().bar: boolean);// last wins +(new C().qux: boolean);// weird outlier where last doesn't win in classes +// Objects +const o = { + foo(): number { + return 0; + }, + // last wins foo(): string { + return "hello"; + }, + x: 42, + // last wins x: "hello", + bar(): number { + return 0; + }, + // last wins bar: "hello", + qux: 42, + // last wins qux(): string { + return "hello"; + } +}; +// check +(o.foo(): boolean);// last wins +(o.x: boolean);// last wins +(o.bar: boolean);// last wins +(o.qux(): boolean);// last wins + +" +`; diff --git a/tests/last_duplicate_property_wins/jsfmt.spec.js b/tests/last_duplicate_property_wins/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/last_duplicate_property_wins/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/last_duplicate_property_wins/test.js b/tests/last_duplicate_property_wins/test.js new file mode 100644 index 000000000000..8468262e3d2b --- /dev/null +++ b/tests/last_duplicate_property_wins/test.js @@ -0,0 +1,41 @@ +// @flow + +// Classes + +class C { + foo(): number { return 0; } + foo(): string { return "hello" } // last wins + x: number; + x: string; // last wins + bar(): number { return 0; } + bar: string; // field wins over method + qux: number; + qux(): string { return "hello" } // method loses to field! +} + +// check + +((new C).foo(): boolean); // last wins +((new C).x: boolean); // last wins +((new C).bar: boolean); // last wins +((new C).qux: boolean); // weird outlier where last doesn't win in classes + +// Objects + +const o = { + foo(): number { return 0; }, + foo(): string { return "hello" }, // last wins + x: 42, + x: "hello", // last wins + bar(): number { return 0; }, + bar: "hello", // last wins + qux: 42, + qux(): string { return "hello" }, // last wins +}; + +// check + +(o.foo(): boolean); // last wins +(o.x: boolean); // last wins +(o.bar: boolean); // last wins +(o.qux(): boolean); // last wins diff --git a/tests/lib/__snapshots__/jsfmt.spec.js.snap b/tests/lib/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..173f38aad2ce --- /dev/null +++ b/tests/lib/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,26 @@ +exports[`test libtest.js 1`] = ` +"var x:string = NaN +var y:string = Number.MAX_VALUE; +var z:number = new TypeError().name; +var w:string = parseInt("..."); + +var a = new Map(); +a.delete('foobar'); + +var b = undefined; +if (undefined) { +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var x: string = NaN; +var y: string = Number.MAX_VALUE; +var z: number = new TypeError().name; +var w: string = parseInt("..."); +var a = new Map(); +a.delete("foobar"); +var b = undefined; +if (undefined) { + +} + +" +`; diff --git a/tests/lib/jsfmt.spec.js b/tests/lib/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/lib/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/lib/libtest.js b/tests/lib/libtest.js new file mode 100644 index 000000000000..567ac09d40f4 --- /dev/null +++ b/tests/lib/libtest.js @@ -0,0 +1,11 @@ +var x:string = NaN +var y:string = Number.MAX_VALUE; +var z:number = new TypeError().name; +var w:string = parseInt("..."); + +var a = new Map(); +a.delete('foobar'); + +var b = undefined; +if (undefined) { +} diff --git a/tests/lib_interfaces/declarations/__snapshots__/jsfmt.spec.js.snap b/tests/lib_interfaces/declarations/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..07222343166a --- /dev/null +++ b/tests/lib_interfaces/declarations/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,26 @@ +exports[`test underscore.js 1`] = ` +"interface C { + foo(): CArrays; + bar(): C; +} +interface CArrays extends C> { + bar(): C; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1384:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/lib_interfaces/declarations/jsfmt.spec.js b/tests/lib_interfaces/declarations/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/lib_interfaces/declarations/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/lib_interfaces/declarations/underscore.js b/tests/lib_interfaces/declarations/underscore.js new file mode 100644 index 000000000000..305116bacbec --- /dev/null +++ b/tests/lib_interfaces/declarations/underscore.js @@ -0,0 +1,7 @@ +interface C { + foo(): CArrays; + bar(): C; +} +interface CArrays extends C> { + bar(): C; +} diff --git a/tests/libconfig/__snapshots__/jsfmt.spec.js.snap b/tests/libconfig/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..b941f86bc364 --- /dev/null +++ b/tests/libconfig/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,51 @@ +exports[`test libA.js 1`] = ` +"declare function foo(x: number): void; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test libB.js 1`] = ` +"declare function bar(x: string): void; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test libtest.js 1`] = ` +"foo(123); +bar(123); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +foo(123); +bar(123); + +" +`; diff --git a/tests/libconfig/jsfmt.spec.js b/tests/libconfig/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/libconfig/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/libconfig/libA.js b/tests/libconfig/libA.js new file mode 100644 index 000000000000..304501d2cc6f --- /dev/null +++ b/tests/libconfig/libA.js @@ -0,0 +1 @@ +declare function foo(x: number): void; diff --git a/tests/libconfig/libB.js b/tests/libconfig/libB.js new file mode 100644 index 000000000000..218601f8fa85 --- /dev/null +++ b/tests/libconfig/libB.js @@ -0,0 +1 @@ +declare function bar(x: string): void; diff --git a/tests/libconfig/libtest.js b/tests/libconfig/libtest.js new file mode 100644 index 000000000000..d78728d487b9 --- /dev/null +++ b/tests/libconfig/libtest.js @@ -0,0 +1,2 @@ +foo(123); +bar(123); diff --git a/tests/libdef_ignored_module/__snapshots__/jsfmt.spec.js.snap b/tests/libdef_ignored_module/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..8499e56a2b51 --- /dev/null +++ b/tests/libdef_ignored_module/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,13 @@ +exports[`test test.js 1`] = ` +"/* @flow */ + +import foo from "foo"; + +(foo.bar : string); // error number ~> string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +import foo from "foo"; +(foo.bar: string);// error number ~> string + +" +`; diff --git a/tests/libdef_ignored_module/jsfmt.spec.js b/tests/libdef_ignored_module/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/libdef_ignored_module/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/libdef_ignored_module/lib/__snapshots__/jsfmt.spec.js.snap b/tests/libdef_ignored_module/lib/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..5984291f7dc8 --- /dev/null +++ b/tests/libdef_ignored_module/lib/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,11 @@ +exports[`test foo.js 1`] = ` +"declare module foo { + declare var bar: number; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +declare module foo { + declare var bar: number; +} + +" +`; diff --git a/tests/libdef_ignored_module/lib/foo.js b/tests/libdef_ignored_module/lib/foo.js new file mode 100644 index 000000000000..955a7d7f6a79 --- /dev/null +++ b/tests/libdef_ignored_module/lib/foo.js @@ -0,0 +1,3 @@ +declare module foo { + declare var bar: number; +} diff --git a/tests/libdef_ignored_module/lib/jsfmt.spec.js b/tests/libdef_ignored_module/lib/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/libdef_ignored_module/lib/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/libdef_ignored_module/test.js b/tests/libdef_ignored_module/test.js new file mode 100644 index 000000000000..334e7e8c21bd --- /dev/null +++ b/tests/libdef_ignored_module/test.js @@ -0,0 +1,5 @@ +/* @flow */ + +import foo from "foo"; + +(foo.bar : string); // error number ~> string diff --git a/tests/liberr/__snapshots__/jsfmt.spec.js.snap b/tests/liberr/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..fb842df9ee45 --- /dev/null +++ b/tests/liberr/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,15 @@ +exports[`test a.js 1`] = ` +"/** + * @flow + */ +// one error here, to verify lib errors sort to top. +var x: string = 0; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +// one error here, to verify lib errors sort to top. +var x: string = 0; + +" +`; diff --git a/tests/liberr/a.js b/tests/liberr/a.js new file mode 100644 index 000000000000..f444c23835cd --- /dev/null +++ b/tests/liberr/a.js @@ -0,0 +1,5 @@ +/** + * @flow + */ +// one error here, to verify lib errors sort to top. +var x: string = 0; diff --git a/tests/liberr/jsfmt.spec.js b/tests/liberr/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/liberr/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/liberr/libs/__snapshots__/jsfmt.spec.js.snap b/tests/liberr/libs/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..8c596e46cf84 --- /dev/null +++ b/tests/liberr/libs/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,51 @@ +exports[`test parse_error.js 1`] = ` +"/** + * @flow + */ +declare class ExampleClass { + + methodA: () => any // parse error, missing comma + + methodB: () => any + +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (8:2) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$7.flowObjectTypeSemicolon (/node_modules/babylon/lib/index.js:4832:10) + at Parser.pp$7.flowParseObjectType (/node_modules/babylon/lib/index.js:4813:14) + at Parser.pp$7.flowParseInterfaceish (/node_modules/babylon/lib/index.js:4575:20) + at Parser.pp$7.flowParseDeclareClass (/node_modules/babylon/lib/index.js:4437:8) + at Parser.pp$7.flowParseDeclare (/node_modules/babylon/lib/index.js:4474:17) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5235:25) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) +" +`; + +exports[`test type_error.js 1`] = ` +"// @flow +declare function foo(x: number): Array; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/liberr/libs/jsfmt.spec.js b/tests/liberr/libs/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/liberr/libs/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/liberr/libs/parse_error.js b/tests/liberr/libs/parse_error.js new file mode 100644 index 000000000000..08cd2a15d3c0 --- /dev/null +++ b/tests/liberr/libs/parse_error.js @@ -0,0 +1,10 @@ +/** + * @flow + */ +declare class ExampleClass { + + methodA: () => any // parse error, missing comma + + methodB: () => any + +} diff --git a/tests/liberr/libs/type_error.js b/tests/liberr/libs/type_error.js new file mode 100644 index 000000000000..1c36d039ecc4 --- /dev/null +++ b/tests/liberr/libs/type_error.js @@ -0,0 +1,2 @@ +// @flow +declare function foo(x: number): Array; diff --git a/tests/libflow-typed/__snapshots__/jsfmt.spec.js.snap b/tests/libflow-typed/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..5a2c84cb0777 --- /dev/null +++ b/tests/libflow-typed/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,9 @@ +exports[`test libtest.js 1`] = ` +"/* @flow */ +const dino : Dinosaur = "Stegosaurus" +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +const dino: Dinosaur = "Stegosaurus"; + +" +`; diff --git a/tests/libflow-typed/flow-typed/__snapshots__/jsfmt.spec.js.snap b/tests/libflow-typed/flow-typed/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..fcc919445fd6 --- /dev/null +++ b/tests/libflow-typed/flow-typed/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,20 @@ +exports[`test dino.js 1`] = ` +"declare type Dinosaur = \"T-Rex\" | \"Apatosaurus\"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/libflow-typed/flow-typed/dino.js b/tests/libflow-typed/flow-typed/dino.js new file mode 100644 index 000000000000..283429db6f73 --- /dev/null +++ b/tests/libflow-typed/flow-typed/dino.js @@ -0,0 +1 @@ +declare type Dinosaur = "T-Rex" | "Apatosaurus"; diff --git a/tests/libflow-typed/flow-typed/jsfmt.spec.js b/tests/libflow-typed/flow-typed/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/libflow-typed/flow-typed/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/libflow-typed/jsfmt.spec.js b/tests/libflow-typed/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/libflow-typed/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/libflow-typed/libtest.js b/tests/libflow-typed/libtest.js new file mode 100644 index 000000000000..8d4b83401678 --- /dev/null +++ b/tests/libflow-typed/libtest.js @@ -0,0 +1,2 @@ +/* @flow */ +const dino : Dinosaur = "Stegosaurus" diff --git a/tests/librec/__snapshots__/jsfmt.spec.js.snap b/tests/librec/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..2921149267ff --- /dev/null +++ b/tests/librec/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,9 @@ +exports[`test libtest.js 1`] = ` +"foo(123); +bar(123); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +foo(123); +bar(123); + +" +`; diff --git a/tests/librec/jsfmt.spec.js b/tests/librec/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/librec/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/librec/lib/A/__snapshots__/jsfmt.spec.js.snap b/tests/librec/lib/A/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..b1e15472a048 --- /dev/null +++ b/tests/librec/lib/A/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,20 @@ +exports[`test libA.js 1`] = ` +"declare function foo(x: number): void; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/librec/lib/A/jsfmt.spec.js b/tests/librec/lib/A/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/librec/lib/A/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/librec/lib/A/libA.js b/tests/librec/lib/A/libA.js new file mode 100644 index 000000000000..304501d2cc6f --- /dev/null +++ b/tests/librec/lib/A/libA.js @@ -0,0 +1 @@ +declare function foo(x: number): void; diff --git a/tests/librec/lib/B/__snapshots__/jsfmt.spec.js.snap b/tests/librec/lib/B/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..09b44d83467e --- /dev/null +++ b/tests/librec/lib/B/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,20 @@ +exports[`test libB.js 1`] = ` +"declare function bar(x: string): void; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/librec/lib/B/jsfmt.spec.js b/tests/librec/lib/B/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/librec/lib/B/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/librec/lib/B/libB.js b/tests/librec/lib/B/libB.js new file mode 100644 index 000000000000..218601f8fa85 --- /dev/null +++ b/tests/librec/lib/B/libB.js @@ -0,0 +1 @@ +declare function bar(x: string): void; diff --git a/tests/librec/libtest.js b/tests/librec/libtest.js new file mode 100644 index 000000000000..d78728d487b9 --- /dev/null +++ b/tests/librec/libtest.js @@ -0,0 +1,2 @@ +foo(123); +bar(123); diff --git a/tests/literal/__snapshots__/jsfmt.spec.js.snap b/tests/literal/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..09237dbc394c --- /dev/null +++ b/tests/literal/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,104 @@ +exports[`test enum.js 1`] = ` +"var APIKeys = { + AGE: \'age\', + NAME: \'name\', +}; + +module.exports = APIKeys; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var APIKeys = { AGE: \"age\", NAME: \"name\" }; +module.exports = APIKeys; + +" +`; + +exports[`test enum_client.js 1`] = ` +"var APIKeys = require(\'./enum\'); +// object that maps \"AGE\" to \"age\", \"NAME\" to \"name\" + +function foo(x: $Keys) { } +foo(\"AGE\"); +foo(\"LOCATION\"); // error + +function bar(x: $Keys<{age: number}>) { } +bar(APIKeys.AGE); // not an error: APIKeys.AGE = \"age\" +bar(APIKeys.NAME); // error: since \"NAME\" is not in the smaller enum + +var object = {}; +object[APIKeys.AGE] = 123; // i.e., object.age = 123 +object[APIKeys.NAME] = \"FOO\"; // i.e., object.name = \"FOO\" + +var age:number = object[APIKeys.AGE]; +var name:number = object[APIKeys.NAME]; // error: object.name is a string + +var indices = { red: 0, green: 1, blue: 2 }; +var tuple = [42, \"hello\", false]; +var red:string = tuple[indices.red]; // error: tuple[0] is a number +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test number.js 1`] = ` +"function test1(x: number): number { + return -x; +} + +function test2(x: string): number { + return -x; +} + +// sanity checks to make sure merging envs doesn\'t keep creating new NumT\'s +// because of the UnaryMinusT\'s, causing nontermination +function test3(x: number, flip_times: number): number { + for (var i = 0; i < flip_times; i++) { + x = -x; + } + return x; +} +function test4(flip_times: number): number { + var x = 1; + for (var i = 0; i < flip_times; i++) { + x = -x; + } + return x; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +function test1(x: number): number { + return -x; +} +function test2(x: string): number { + return -x; +} +// sanity checks to make sure merging envs doesn\'t keep creating new NumT\'s +// because of the UnaryMinusT\'s, causing nontermination +function test3(x: number, flip_times: number): number { + for (var i = 0; i < flip_times; i++) { + x = -x; + } + return x; +} +function test4(flip_times: number): number { + var x = 1; + for (var i = 0; i < flip_times; i++) { + x = -x; + } + return x; +} + +" +`; diff --git a/tests/literal/enum.js b/tests/literal/enum.js new file mode 100644 index 000000000000..6449c41bd82c --- /dev/null +++ b/tests/literal/enum.js @@ -0,0 +1,6 @@ +var APIKeys = { + AGE: 'age', + NAME: 'name', +}; + +module.exports = APIKeys; diff --git a/tests/literal/enum_client.js b/tests/literal/enum_client.js new file mode 100644 index 000000000000..2259290ad4ef --- /dev/null +++ b/tests/literal/enum_client.js @@ -0,0 +1,21 @@ +var APIKeys = require('./enum'); +// object that maps "AGE" to "age", "NAME" to "name" + +function foo(x: $Keys) { } +foo("AGE"); +foo("LOCATION"); // error + +function bar(x: $Keys<{age: number}>) { } +bar(APIKeys.AGE); // not an error: APIKeys.AGE = "age" +bar(APIKeys.NAME); // error: since "NAME" is not in the smaller enum + +var object = {}; +object[APIKeys.AGE] = 123; // i.e., object.age = 123 +object[APIKeys.NAME] = "FOO"; // i.e., object.name = "FOO" + +var age:number = object[APIKeys.AGE]; +var name:number = object[APIKeys.NAME]; // error: object.name is a string + +var indices = { red: 0, green: 1, blue: 2 }; +var tuple = [42, "hello", false]; +var red:string = tuple[indices.red]; // error: tuple[0] is a number diff --git a/tests/literal/jsfmt.spec.js b/tests/literal/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/literal/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/literal/number.js b/tests/literal/number.js new file mode 100644 index 000000000000..1ec5299191b5 --- /dev/null +++ b/tests/literal/number.js @@ -0,0 +1,23 @@ +function test1(x: number): number { + return -x; +} + +function test2(x: string): number { + return -x; +} + +// sanity checks to make sure merging envs doesn't keep creating new NumT's +// because of the UnaryMinusT's, causing nontermination +function test3(x: number, flip_times: number): number { + for (var i = 0; i < flip_times; i++) { + x = -x; + } + return x; +} +function test4(flip_times: number): number { + var x = 1; + for (var i = 0; i < flip_times; i++) { + x = -x; + } + return x; +} diff --git a/tests/locals/__snapshots__/jsfmt.spec.js.snap b/tests/locals/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..6ed1110ebc93 --- /dev/null +++ b/tests/locals/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,122 @@ +exports[`test lex.js 1`] = ` +"function switch_scope(x: mixed) { + let a = \"\"; + let b = \"\"; + switch (x) { + case \"foo\": + let a; + a = 0; // doesn\'t add lower bound to outer a + b = 0; + } + (a : string); // OK + (b : string); // error: number ~> string +} + +function try_scope_finally() { + let a; + let b; + try { + a = \"\"; + b = \"\"; + } finally { + let a; + a = 0; // doesn\'t add lower bound to outer a + b = 0; + } + (a : string); // ok + (b : string); // error: number ~> string +} + +function for_scope() { + let a = \"\"; + let b = \"\"; + for (let a;;) { + a = 0; // doesn\'t add lower bound to outer a + b = 0; + } + (a : string); + (b : string); // error: number ~> string +} + +function for_in_scope(o: Object) { + let a = 0; + let b = 0; + for (let a in o) { + a = \"\"; // doesn\'t add lower bound to outer a + b = \"\"; + } + (a : number); + (b : number); // error: string ~> number +} + +function for_of_scope(xs: number[]) { + let a = \"\"; + let b = \"\"; + for (let a of xs) { + a = 0; // doesn\'t add lower bound to outer a + b = 0; + } + (a : string); + (b : string); // error: number ~> string +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1020 + }, \"consequent\").indent(options.tabWidth)); + ^ + +TypeError: path.call(...).indent is not a function + at genericPrintNoParens (/src/printer.js:1020:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1005:36) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test locals.js 1`] = ` +"var x:string = 0; +var x:number = 1; + +//declare var T: $Type>; + +function foo(p: bool) {} + +function sorry(really: bool) { + if (really) { + var x: number | string = 1337; + } else { + var x: bool = true; + } + foo(x); +} + +function foo0(b: bool): number { + var x = 0; + if (b) { + var x = \"\"; // error: string ~> number + } + return x; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/locals/jsfmt.spec.js b/tests/locals/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/locals/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/locals/lex.js b/tests/locals/lex.js new file mode 100644 index 000000000000..a97bd4604439 --- /dev/null +++ b/tests/locals/lex.js @@ -0,0 +1,60 @@ +function switch_scope(x: mixed) { + let a = ""; + let b = ""; + switch (x) { + case "foo": + let a; + a = 0; // doesn't add lower bound to outer a + b = 0; + } + (a : string); // OK + (b : string); // error: number ~> string +} + +function try_scope_finally() { + let a; + let b; + try { + a = ""; + b = ""; + } finally { + let a; + a = 0; // doesn't add lower bound to outer a + b = 0; + } + (a : string); // ok + (b : string); // error: number ~> string +} + +function for_scope() { + let a = ""; + let b = ""; + for (let a;;) { + a = 0; // doesn't add lower bound to outer a + b = 0; + } + (a : string); + (b : string); // error: number ~> string +} + +function for_in_scope(o: Object) { + let a = 0; + let b = 0; + for (let a in o) { + a = ""; // doesn't add lower bound to outer a + b = ""; + } + (a : number); + (b : number); // error: string ~> number +} + +function for_of_scope(xs: number[]) { + let a = ""; + let b = ""; + for (let a of xs) { + a = 0; // doesn't add lower bound to outer a + b = 0; + } + (a : string); + (b : string); // error: number ~> string +} diff --git a/tests/locals/locals.js b/tests/locals/locals.js new file mode 100644 index 000000000000..ebb8167bc4dd --- /dev/null +++ b/tests/locals/locals.js @@ -0,0 +1,23 @@ +var x:string = 0; +var x:number = 1; + +//declare var T: $Type>; + +function foo(p: bool) {} + +function sorry(really: bool) { + if (really) { + var x: number | string = 1337; + } else { + var x: bool = true; + } + foo(x); +} + +function foo0(b: bool): number { + var x = 0; + if (b) { + var x = ""; // error: string ~> number + } + return x; +} diff --git a/tests/logical/__snapshots__/jsfmt.spec.js.snap b/tests/logical/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..3970bd284909 --- /dev/null +++ b/tests/logical/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,533 @@ +exports[`test logical.js 1`] = ` +"/* @flow */ + +/** + * A falsy variable on the left side of && + */ +function logical1a(): number { // expected \`: boolean\` + var x = false; + return x && \'123\'; +} + +/** + * A truthy variable on the left side of && + */ +function logical1b(): string { + var x = true; + return x && \'123\'; +} + +/** + * A literal on the left side of && + */ +function logical2a(): number { // expected \`: boolean\` + return false && \'123\'; +} + +/** + * A literal on the left side of && + */ +function logical2b(): number { + return 0 && \'123\'; +} + +/** + * A literal on the left side of && + */ +function logical2c(): string { + return \"\" && 123; +} + +/** + * A literal on the left side of && + */ +function logical2d(): string { + return true && \'123\'; +} + +/** + * A literal on the left side of && + */ +function logical2e(): number { + return \'foo\' && 123; +} + +/** + * A literal on the left side of && + */ +function logical2f(): string { + return 123 && \'foo\'; +} + +/** + * A literal on the left side of && + */ +function logical2g(): string { + return [1,2,3] && \'foo\'; +} + +/** + * A literal on the left side of && + */ +function logical2h(x: {a: number}): string { + return x && \'foo\'; +} + +/** + * A literal on the left side of && + */ +function logical2i(x: Object): string { + return x && \'foo\'; +} + +/** + * A literal on the left side of && + */ +function logical2j(x: (a: number) => number): string { + return x && \'foo\'; +} + +/** + * A literal on the left side of && + */ +function logical2k(x: Function): string { + return x && \'foo\'; +} + +/** + * An expression on the left side of && + */ +function logical3a(): string { // expected \`: boolean\` + var x: ?number = null; + return x != null && x > 10; +} + +/** + * An expression on the left side of && + */ +function logical3b(): number { // expected \`: boolean | number\` + var x: ?number = null; + return x != null && x; +} + +/** + * An expression on the left side of && + */ +function logical3c(): ?number { // expected \`: boolean | ?number\` + var x: ?number = null; + return x != undefined && x; +} + +/** + * Maybe truthy returns both types + */ +function logical4(x: boolean): string { // expected \`: boolean | string\` + return x && \'123\'; +} + +/** + * A falsy variable on the left side of || + */ +function logical5a(): number { + var x = false; + return x || 0; +} + +/** + * A maybe variable on the left side of || + */ +function logical5b(): number { + var x: ?number = null; + return x || 0; +} + +/** + * A truthy variable on the left side of || + */ +function logical5c(): string { // expected \`: boolean\` + var x = true; + return x || 0; +} + +/** + * A literal on the left side of || + */ +function logical6a(): string { + return false || \'123\'; +} + +/** + * A literal on the left side of || + */ +function logical6b(): string { + return 0 || \'123\'; +} + +/** + * A literal on the left side of || + */ +function logical6c(): number { + return \"\" || 123; +} + +/** + * A literal on the left side of || + */ +function logical6d(): number { // expected \`: boolean\` + return true || \'123\'; +} + +/** + * A literal on the left side of || + */ +function logical6e(): string { + return \'foo\' || 123; +} + +/** + * A literal on the left side of || + */ +function logical6f(): number { + return 123 || \'foo\'; +} + +/** + * A composite && and || + */ +function logical7a(): number { + var x: ?number = null; + return x != null && x || 0; +} + +/** + * A composite && and || where the truthiness is unknown + */ +function logical7b(x: boolean, y: number): number { + return x && y || 0; +} + +/** + * A composite && and || + */ +function logical7c(x: string): number { + return x && 1 || 0; +} + +/** + * A composite && and || + */ +function logical7d(x: number): string { + return x && \'foo\' || \'bar\'; +} + +/** + * A composite && and || + */ +function logical7e(x: number): string { + return false && x || \'bar\'; +} + +/** + * A composite || and && + * + * \`x || 0\` always returns a number (never a boolean) and then short + * circuits the && because 0 is falsy, so this should just return number. + */ +function logical8a(): number { + var x = false; + return (x || 0) && \'foo\'; +} + +/** + * A composite || and && + * + * \`x || 1\` always returns something truthy, so this returns a string + */ +function logical8b(): string { + var x = false; + return (x || 1) && \'foo\'; +} + +/** + * A composite || and && + * + * \`x\` is always truthy, so this returns a string + */ +function logical8c(): string { + var x = true; + return (x || 1) && \'foo\'; +} + +/** + * A composite || and && + */ +function logical8d(): number { + var x = false; + return x || (0 && \'foo\'); +} + +/** + * A composite || and && + */ +function logical8e(): string { + var x = false; + return x || (1 && \'foo\'); +} + +/** + * A composite || and && + */ +function logical8f(): string { // expected \`: boolean\` + var x = true; + return x || (1 && \'foo\'); +} + +/** + * A composite || and || + */ +function logical9a( + x: number, + y: string +): number | string { // expected \`: number | string | boolean\` + return x || y || false; +} + +/** + * A composite || and || + */ +function logical9b(x: number, y: string): number | string { + return false || x || y; +} + +/** + * A composite || and || + */ +function logical9c(x: number, y: boolean): string { + return \'a\' || x || y; +} + +/** + * A composite && and && + */ +function logical10a( + x: number, + y: string +): number | string { // expected \`: number | string | boolean\` + return x && y && false; +} + +/** + * A composite && and && + */ +function logical10b(x: number, y: string): Array { // expected \`: boolean\` + return false && x && y; +} + +/** + * A composite && and && + */ +function logical10c(x: number, y: string): Array { // expected \`number | boolean\` + return x && false && y; +} + +/** + * || in a loop + */ +function logical11a(): number { + var y = 1; + for (var x = 0; x < 5; x++) { + y = y || true; + } + return y; +} + +/** + * || in a loop + */ +function logical11b(y: number): number { + for (var x = 0; x < 5; x++) { + y = y || true; // expected a number + } + return y; +} + +/** + * && in a loop + */ +function logical12a(): number { + var y = 1; + var z = true; + for (var x = 0; x < 5; x++) { + y = z && y; + z = false; + } + return y; +} + +/** + * && in a loop + */ +function logical12b(y: number): number { + for (var x = 0; x < 5; x++) { + y = y && true; // expected a number + } + return y; +} + +/** + * Complex && + */ +function logical13(x: number): Array<{x: string}> { + return [ + {x: x && \"bar\"}, + {x: true && \"bar\"}, + {x: true && false}, + {x: false && false}, + {x: 1 && \"bar\"}, + {x: \"foo\" && \"bar\"}, + {x: \"foo\" && \"bar\"}, + {x: \"foo\" && \"bar\"}, + ]; +} + +/** + * Complex || + */ +function logical14(x: number): Array<{x: string}> { + return [ + {x: x || \"bar\"}, + {x: false || \"bar\"}, + {x: false || true}, + {x: true || false}, + {x: 0 || \"bar\"}, + {x: \"foo\" || \"bar\"}, + {x: \"foo\" || \"bar\"}, + {x: \"foo\" || \"bar\"}, + ]; +} + +/** + * || in an addition + */ +function logical15a(x: number): number { + return 5 + (x || 7); +} + +/** + * || in an addition + */ +function logical15b(x: number): number { + return (x || 7) + 5; +} + +/** + * && in an addition + */ +function logical15c(x: number): number { + return 5 + (x && 7); +} + +/** + * && in an addition + */ +function logical15d(x: number): number { + return (x && 7) + 5; +} + +/** + * || in a comparison + */ +function logical16a(x: number): boolean { + return 5 < (x || 7); +} + +/** + * || in a comparison + */ +function logical16b(x: number): boolean { + return (x || 7) < 5; +} + +/** + * && in a comparison + */ +function logical16c(x: number): boolean { + return 5 < (x && 7); +} + +/** + * && in a comparison + */ +function logical16d(x: number): boolean { + return (x && 7) < 5; +} + +/** + * || in an equality + */ +function logical17a(x: number): boolean { + return 5 == (x || 7); +} + +/** + * || in an equality + */ +function logical17b(x: number): boolean { + return (x || 7) == 5; +} + +/** + * && in an equality + */ +function logical17c(x: number): boolean { + return 5 == (x && 7); +} + +/** + * && in an equality + */ +function logical17d(x: number): boolean { + return (x && 7) == 5; +} + +/** + * Expressions on each side that return truthy things + */ +function logical18a(x: number, y: number): number { + return x - 1 || y - 1; +} + +/** + * Sentinel properties should not interfere + */ +function logical18b(x: {a: number}, y: {b: number}): number { + return x.a - 1 || y.b - 1; +} + +/** + * Layer of indirection in the LHS (get prop) + */ +function logical19a(x: { y: string, z: boolean }): boolean { + return (x.y && x.z); // error: x.y is a string +} +function logical19b(x: { y: string, z: boolean }): boolean { + return (x.y || x.z); // error: x.y is a string +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/logical/jsfmt.spec.js b/tests/logical/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/logical/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/logical/logical.js b/tests/logical/logical.js new file mode 100644 index 000000000000..b4aa1dcf90aa --- /dev/null +++ b/tests/logical/logical.js @@ -0,0 +1,514 @@ +/* @flow */ + +/** + * A falsy variable on the left side of && + */ +function logical1a(): number { // expected `: boolean` + var x = false; + return x && '123'; +} + +/** + * A truthy variable on the left side of && + */ +function logical1b(): string { + var x = true; + return x && '123'; +} + +/** + * A literal on the left side of && + */ +function logical2a(): number { // expected `: boolean` + return false && '123'; +} + +/** + * A literal on the left side of && + */ +function logical2b(): number { + return 0 && '123'; +} + +/** + * A literal on the left side of && + */ +function logical2c(): string { + return "" && 123; +} + +/** + * A literal on the left side of && + */ +function logical2d(): string { + return true && '123'; +} + +/** + * A literal on the left side of && + */ +function logical2e(): number { + return 'foo' && 123; +} + +/** + * A literal on the left side of && + */ +function logical2f(): string { + return 123 && 'foo'; +} + +/** + * A literal on the left side of && + */ +function logical2g(): string { + return [1,2,3] && 'foo'; +} + +/** + * A literal on the left side of && + */ +function logical2h(x: {a: number}): string { + return x && 'foo'; +} + +/** + * A literal on the left side of && + */ +function logical2i(x: Object): string { + return x && 'foo'; +} + +/** + * A literal on the left side of && + */ +function logical2j(x: (a: number) => number): string { + return x && 'foo'; +} + +/** + * A literal on the left side of && + */ +function logical2k(x: Function): string { + return x && 'foo'; +} + +/** + * An expression on the left side of && + */ +function logical3a(): string { // expected `: boolean` + var x: ?number = null; + return x != null && x > 10; +} + +/** + * An expression on the left side of && + */ +function logical3b(): number { // expected `: boolean | number` + var x: ?number = null; + return x != null && x; +} + +/** + * An expression on the left side of && + */ +function logical3c(): ?number { // expected `: boolean | ?number` + var x: ?number = null; + return x != undefined && x; +} + +/** + * Maybe truthy returns both types + */ +function logical4(x: boolean): string { // expected `: boolean | string` + return x && '123'; +} + +/** + * A falsy variable on the left side of || + */ +function logical5a(): number { + var x = false; + return x || 0; +} + +/** + * A maybe variable on the left side of || + */ +function logical5b(): number { + var x: ?number = null; + return x || 0; +} + +/** + * A truthy variable on the left side of || + */ +function logical5c(): string { // expected `: boolean` + var x = true; + return x || 0; +} + +/** + * A literal on the left side of || + */ +function logical6a(): string { + return false || '123'; +} + +/** + * A literal on the left side of || + */ +function logical6b(): string { + return 0 || '123'; +} + +/** + * A literal on the left side of || + */ +function logical6c(): number { + return "" || 123; +} + +/** + * A literal on the left side of || + */ +function logical6d(): number { // expected `: boolean` + return true || '123'; +} + +/** + * A literal on the left side of || + */ +function logical6e(): string { + return 'foo' || 123; +} + +/** + * A literal on the left side of || + */ +function logical6f(): number { + return 123 || 'foo'; +} + +/** + * A composite && and || + */ +function logical7a(): number { + var x: ?number = null; + return x != null && x || 0; +} + +/** + * A composite && and || where the truthiness is unknown + */ +function logical7b(x: boolean, y: number): number { + return x && y || 0; +} + +/** + * A composite && and || + */ +function logical7c(x: string): number { + return x && 1 || 0; +} + +/** + * A composite && and || + */ +function logical7d(x: number): string { + return x && 'foo' || 'bar'; +} + +/** + * A composite && and || + */ +function logical7e(x: number): string { + return false && x || 'bar'; +} + +/** + * A composite || and && + * + * `x || 0` always returns a number (never a boolean) and then short + * circuits the && because 0 is falsy, so this should just return number. + */ +function logical8a(): number { + var x = false; + return (x || 0) && 'foo'; +} + +/** + * A composite || and && + * + * `x || 1` always returns something truthy, so this returns a string + */ +function logical8b(): string { + var x = false; + return (x || 1) && 'foo'; +} + +/** + * A composite || and && + * + * `x` is always truthy, so this returns a string + */ +function logical8c(): string { + var x = true; + return (x || 1) && 'foo'; +} + +/** + * A composite || and && + */ +function logical8d(): number { + var x = false; + return x || (0 && 'foo'); +} + +/** + * A composite || and && + */ +function logical8e(): string { + var x = false; + return x || (1 && 'foo'); +} + +/** + * A composite || and && + */ +function logical8f(): string { // expected `: boolean` + var x = true; + return x || (1 && 'foo'); +} + +/** + * A composite || and || + */ +function logical9a( + x: number, + y: string +): number | string { // expected `: number | string | boolean` + return x || y || false; +} + +/** + * A composite || and || + */ +function logical9b(x: number, y: string): number | string { + return false || x || y; +} + +/** + * A composite || and || + */ +function logical9c(x: number, y: boolean): string { + return 'a' || x || y; +} + +/** + * A composite && and && + */ +function logical10a( + x: number, + y: string +): number | string { // expected `: number | string | boolean` + return x && y && false; +} + +/** + * A composite && and && + */ +function logical10b(x: number, y: string): Array { // expected `: boolean` + return false && x && y; +} + +/** + * A composite && and && + */ +function logical10c(x: number, y: string): Array { // expected `number | boolean` + return x && false && y; +} + +/** + * || in a loop + */ +function logical11a(): number { + var y = 1; + for (var x = 0; x < 5; x++) { + y = y || true; + } + return y; +} + +/** + * || in a loop + */ +function logical11b(y: number): number { + for (var x = 0; x < 5; x++) { + y = y || true; // expected a number + } + return y; +} + +/** + * && in a loop + */ +function logical12a(): number { + var y = 1; + var z = true; + for (var x = 0; x < 5; x++) { + y = z && y; + z = false; + } + return y; +} + +/** + * && in a loop + */ +function logical12b(y: number): number { + for (var x = 0; x < 5; x++) { + y = y && true; // expected a number + } + return y; +} + +/** + * Complex && + */ +function logical13(x: number): Array<{x: string}> { + return [ + {x: x && "bar"}, + {x: true && "bar"}, + {x: true && false}, + {x: false && false}, + {x: 1 && "bar"}, + {x: "foo" && "bar"}, + {x: "foo" && "bar"}, + {x: "foo" && "bar"}, + ]; +} + +/** + * Complex || + */ +function logical14(x: number): Array<{x: string}> { + return [ + {x: x || "bar"}, + {x: false || "bar"}, + {x: false || true}, + {x: true || false}, + {x: 0 || "bar"}, + {x: "foo" || "bar"}, + {x: "foo" || "bar"}, + {x: "foo" || "bar"}, + ]; +} + +/** + * || in an addition + */ +function logical15a(x: number): number { + return 5 + (x || 7); +} + +/** + * || in an addition + */ +function logical15b(x: number): number { + return (x || 7) + 5; +} + +/** + * && in an addition + */ +function logical15c(x: number): number { + return 5 + (x && 7); +} + +/** + * && in an addition + */ +function logical15d(x: number): number { + return (x && 7) + 5; +} + +/** + * || in a comparison + */ +function logical16a(x: number): boolean { + return 5 < (x || 7); +} + +/** + * || in a comparison + */ +function logical16b(x: number): boolean { + return (x || 7) < 5; +} + +/** + * && in a comparison + */ +function logical16c(x: number): boolean { + return 5 < (x && 7); +} + +/** + * && in a comparison + */ +function logical16d(x: number): boolean { + return (x && 7) < 5; +} + +/** + * || in an equality + */ +function logical17a(x: number): boolean { + return 5 == (x || 7); +} + +/** + * || in an equality + */ +function logical17b(x: number): boolean { + return (x || 7) == 5; +} + +/** + * && in an equality + */ +function logical17c(x: number): boolean { + return 5 == (x && 7); +} + +/** + * && in an equality + */ +function logical17d(x: number): boolean { + return (x && 7) == 5; +} + +/** + * Expressions on each side that return truthy things + */ +function logical18a(x: number, y: number): number { + return x - 1 || y - 1; +} + +/** + * Sentinel properties should not interfere + */ +function logical18b(x: {a: number}, y: {b: number}): number { + return x.a - 1 || y.b - 1; +} + +/** + * Layer of indirection in the LHS (get prop) + */ +function logical19a(x: { y: string, z: boolean }): boolean { + return (x.y && x.z); // error: x.y is a string +} +function logical19b(x: { y: string, z: boolean }): boolean { + return (x.y || x.z); // error: x.y is a string +} diff --git a/tests/loners/__snapshots__/jsfmt.spec.js.snap b/tests/loners/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..515a6f93b424 --- /dev/null +++ b/tests/loners/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,28 @@ +exports[`test loners.js 1`] = ` +"var o = { x: 5, y: \"jello\" }; +var z = o.z; +var export_o: { x: number; } = o; + +function f(u,v?):number { return u; } +var export_f: (u: number) => number = f; + +//exports = export_o; +module.exports = export_f; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/loners/jsfmt.spec.js b/tests/loners/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/loners/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/loners/loners.js b/tests/loners/loners.js new file mode 100644 index 000000000000..291e2a8398a6 --- /dev/null +++ b/tests/loners/loners.js @@ -0,0 +1,9 @@ +var o = { x: 5, y: "jello" }; +var z = o.z; +var export_o: { x: number; } = o; + +function f(u,v?):number { return u; } +var export_f: (u: number) => number = f; + +//exports = export_o; +module.exports = export_f; diff --git a/tests/method_properties/__snapshots__/jsfmt.spec.js.snap b/tests/method_properties/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..a4401005ba39 --- /dev/null +++ b/tests/method_properties/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,74 @@ +exports[`test exports_optional_prop.js 1`] = ` +"// @flow + +declare class Foo { + bar?: () => string +} + +export {Foo}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test.js 1`] = ` +"class C { + C() { } + foo() { } + static bar() { } + qux() { this.constructor.x; } +} +C.x; +(new C).foo.x; +C.bar.x; + +import {Foo} from \'./exports_optional_prop\'; +const foo = new Foo(); +(foo.bar(): string); // error, could be undefined + +function f(x) { + (x.bar(): string); // error. caused by \`f(foo)\`; annotate x to track it down. +} +f(foo); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class C { + C() { + + } + foo() { + + } + bar() { + + } + qux() { + this.constructor.x; + } +} +C.x; +new C().foo.x; +C.bar.x; +import { Foo } from \"./exports_optional_prop\"; +const foo = new Foo(); +(foo.bar(): string);// error, could be undefined +function f(x) { + (x.bar(): string);// error. caused by \`f(foo)\`; annotate x to track it down. +} +f(foo); + +" +`; diff --git a/tests/method_properties/exports_optional_prop.js b/tests/method_properties/exports_optional_prop.js new file mode 100644 index 000000000000..b1eedca746eb --- /dev/null +++ b/tests/method_properties/exports_optional_prop.js @@ -0,0 +1,7 @@ +// @flow + +declare class Foo { + bar?: () => string +} + +export {Foo}; diff --git a/tests/method_properties/jsfmt.spec.js b/tests/method_properties/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/method_properties/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/method_properties/test.js b/tests/method_properties/test.js new file mode 100644 index 000000000000..85fa157985ee --- /dev/null +++ b/tests/method_properties/test.js @@ -0,0 +1,18 @@ +class C { + C() { } + foo() { } + static bar() { } + qux() { this.constructor.x; } +} +C.x; +(new C).foo.x; +C.bar.x; + +import {Foo} from './exports_optional_prop'; +const foo = new Foo(); +(foo.bar(): string); // error, could be undefined + +function f(x) { + (x.bar(): string); // error. caused by `f(foo)`; annotate x to track it down. +} +f(foo); diff --git a/tests/misc/A.js b/tests/misc/A.js new file mode 100644 index 000000000000..4e79aacb8bcf --- /dev/null +++ b/tests/misc/A.js @@ -0,0 +1,14 @@ + +/* @providesModule A */ + +module.exports = {}; + +var A = {x:true, ...{}}; +module.exports.cls = A; + +function f(x:boolean) { } +module.exports.fn = f; + +A.y = "?"; +A.x = A.y; +f(A.x); // A.x is now a string, by def assign diff --git a/tests/misc/B.js b/tests/misc/B.js new file mode 100644 index 000000000000..aa0fdbe1e8fe --- /dev/null +++ b/tests/misc/B.js @@ -0,0 +1,15 @@ + +/* @providesModule B */ + +var A = require('A').cls; + +function B() { + this.b = "..."; +} + +function f():number { return this.b; } + +B.prototype.s = 0; +B.prototype.fn = f; + +module.exports = B; diff --git a/tests/misc/C.js b/tests/misc/C.js new file mode 100644 index 000000000000..b882007e2382 --- /dev/null +++ b/tests/misc/C.js @@ -0,0 +1,14 @@ + +/* @providesModule C */ + +var B = require('B'); +var f = require('A').fn; + +function C() { + var o = new B(); + f(o.b); + f(o.s); + o.fn(); +} + +module.exports = C; diff --git a/tests/misc/D.js b/tests/misc/D.js new file mode 100644 index 000000000000..1caac58291a1 --- /dev/null +++ b/tests/misc/D.js @@ -0,0 +1,14 @@ + +/* @providesModule D */ + +var f = require('A').fn; + +function g():string { return this.i; } + +var o = {fn: g, ...{}}; +o.i = true; + +var i = o.fn(); +f(i); + +module.exports = "D for dummy"; diff --git a/tests/misc/E.js b/tests/misc/E.js new file mode 100644 index 000000000000..eddef9ab0577 --- /dev/null +++ b/tests/misc/E.js @@ -0,0 +1,10 @@ + +/* @providesModule E */ + +function h(x:number) { } +var proto = { fn: h } + +var o = Object.create(proto); +o.fn(false); + +module.exports = {obj: o}; diff --git a/tests/misc/F.js b/tests/misc/F.js new file mode 100644 index 000000000000..0e389f461181 --- /dev/null +++ b/tests/misc/F.js @@ -0,0 +1,6 @@ +function fn2(x) { return x.length * 4; } +fn2({length: 'hi'}); + +function foo(x: Array): string { + return x.length; +} diff --git a/tests/misc/G.js b/tests/misc/G.js new file mode 100644 index 000000000000..d27c2932240f --- /dev/null +++ b/tests/misc/G.js @@ -0,0 +1,7 @@ +var a = { length: "duck" }; +a.length = 123; +a.length(); + +var b = [ 123 ]; +b.length = "duck"; +b.length(); diff --git a/tests/misc/__snapshots__/jsfmt.spec.js.snap b/tests/misc/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..6e97a6f0692a --- /dev/null +++ b/tests/misc/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,191 @@ +exports[`test A.js 1`] = ` +" +/* @providesModule A */ + +module.exports = {}; + +var A = {x:true, ...{}}; +module.exports.cls = A; + +function f(x:boolean) { } +module.exports.fn = f; + +A.y = \"?\"; +A.x = A.y; +f(A.x); // A.x is now a string, by def assign +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @providesModule A */ +module.exports = {}; +var A = { x: true, ...{} }; +module.exports.cls = A; +function f(x: boolean) { + +} +module.exports.fn = f; +A.y = \"?\"; +A.x = A.y; +f(A.x);// A.x is now a string, by def assign + +" +`; + +exports[`test B.js 1`] = ` +" +/* @providesModule B */ + +var A = require(\'A\').cls; + +function B() { + this.b = \"...\"; +} + +function f():number { return this.b; } + +B.prototype.s = 0; +B.prototype.fn = f; + +module.exports = B; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @providesModule B */ +var A = require(\"A\").cls; +function B() { + this.b = \"...\"; +} +function f(): number { + return this.b; +} +B.prototype.s = 0; +B.prototype.fn = f; +module.exports = B; + +" +`; + +exports[`test C.js 1`] = ` +" +/* @providesModule C */ + +var B = require(\'B\'); +var f = require(\'A\').fn; + +function C() { + var o = new B(); + f(o.b); + f(o.s); + o.fn(); +} + +module.exports = C; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @providesModule C */ +var B = require(\"B\"); +var f = require(\"A\").fn; +function C() { + var o = new B(); + f(o.b); + f(o.s); + o.fn(); +} +module.exports = C; + +" +`; + +exports[`test D.js 1`] = ` +" +/* @providesModule D */ + +var f = require(\'A\').fn; + +function g():string { return this.i; } + +var o = {fn: g, ...{}}; +o.i = true; + +var i = o.fn(); +f(i); + +module.exports = \"D for dummy\"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @providesModule D */ +var f = require(\"A\").fn; +function g(): string { + return this.i; +} +var o = { fn: g, ...{} }; +o.i = true; +var i = o.fn(); +f(i); +module.exports = \"D for dummy\"; + +" +`; + +exports[`test E.js 1`] = ` +" +/* @providesModule E */ + +function h(x:number) { } +var proto = { fn: h } + +var o = Object.create(proto); +o.fn(false); + +module.exports = {obj: o}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @providesModule E */ +function h(x: number) { + +} +var proto = { fn: h }; +var o = Object.create(proto); +o.fn(false); +module.exports = { obj: o }; + +" +`; + +exports[`test F.js 1`] = ` +"function fn2(x) { return x.length * 4; } +fn2({length: \'hi\'}); + +function foo(x: Array): string { + return x.length; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test G.js 1`] = ` +"var a = { length: \"duck\" }; +a.length = 123; +a.length(); + +var b = [ 123 ]; +b.length = \"duck\"; +b.length(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var a = { length: \"duck\" }; +a.length = 123; +a.length(); +var b = [ 123 ]; +b.length = \"duck\"; +b.length(); + +" +`; diff --git a/tests/misc/jsfmt.spec.js b/tests/misc/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/misc/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/missing_annotation/__snapshots__/jsfmt.spec.js.snap b/tests/missing_annotation/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..9bb7d0b1529a --- /dev/null +++ b/tests/missing_annotation/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,107 @@ +exports[`test array.js 1`] = ` +"// @flow + +type Foo = {}; +var f: Foo = {}; +export var arr = [f]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +type Foo = {}; +var f: Foo = {}; +export var arr = [ f ]; + +" +`; + +exports[`test async_return.js 1`] = ` +"// @flow + +export async function foo() { + return 123; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +export async function foo() { + return 123; +} + +" +`; + +exports[`test infer.js 1`] = ` +"/* @flow */ + +var Foo = { + a: function(arg) { // missing arg annotation + return arg; + }, + + b: function(arg) { // missing arg annotation + return { + bar: arg + }; + }, + + c: function(arg: string) { // no return annotation required + return { + bar: arg + }; + }, + + d: function(arg: string): { + bar: string + } { + return { + bar: arg + }; + }, + + // return type annotation may be omitted, but if so, input positions on + // observed return type (e.g. param types in a function type) must come + // from annotations + e: function(arg: string) { + return function(x) { // missing param annotation + return x; + } + }, + + // ...if the return type is annotated explicitly, this is unnecessary + f: function(arg: string): (x:number) => number { + return function(x) { // no error + return x; + } + } + +}; + +var Bar = { + a: Foo.a(\'Foo\'), // no annotation required + + // object property types are inferred, so make sure that this doesn\'t cause + // us to also infer the parameter\'s type. + b: Foo.b(\'bar\'), // no annotation required + + c: Foo.c(\'bar\'), // no annotation required + + d: Foo.d(\'bar\'), // no annotation required +}; + +module.exports = Foo, Bar; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/missing_annotation/array.js b/tests/missing_annotation/array.js new file mode 100644 index 000000000000..4c90d239d23d --- /dev/null +++ b/tests/missing_annotation/array.js @@ -0,0 +1,5 @@ +// @flow + +type Foo = {}; +var f: Foo = {}; +export var arr = [f]; diff --git a/tests/missing_annotation/async_return.js b/tests/missing_annotation/async_return.js new file mode 100644 index 000000000000..1d533f773651 --- /dev/null +++ b/tests/missing_annotation/async_return.js @@ -0,0 +1,5 @@ +// @flow + +export async function foo() { + return 123; +} diff --git a/tests/missing_annotation/infer.js b/tests/missing_annotation/infer.js new file mode 100644 index 000000000000..d3827d4e03ff --- /dev/null +++ b/tests/missing_annotation/infer.js @@ -0,0 +1,58 @@ +/* @flow */ + +var Foo = { + a: function(arg) { // missing arg annotation + return arg; + }, + + b: function(arg) { // missing arg annotation + return { + bar: arg + }; + }, + + c: function(arg: string) { // no return annotation required + return { + bar: arg + }; + }, + + d: function(arg: string): { + bar: string + } { + return { + bar: arg + }; + }, + + // return type annotation may be omitted, but if so, input positions on + // observed return type (e.g. param types in a function type) must come + // from annotations + e: function(arg: string) { + return function(x) { // missing param annotation + return x; + } + }, + + // ...if the return type is annotated explicitly, this is unnecessary + f: function(arg: string): (x:number) => number { + return function(x) { // no error + return x; + } + } + +}; + +var Bar = { + a: Foo.a('Foo'), // no annotation required + + // object property types are inferred, so make sure that this doesn't cause + // us to also infer the parameter's type. + b: Foo.b('bar'), // no annotation required + + c: Foo.c('bar'), // no annotation required + + d: Foo.d('bar'), // no annotation required +}; + +module.exports = Foo, Bar; diff --git a/tests/missing_annotation/jsfmt.spec.js b/tests/missing_annotation/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/missing_annotation/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/modified_lib/__snapshots__/jsfmt.spec.js.snap b/tests/modified_lib/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..eabf387fb4aa --- /dev/null +++ b/tests/modified_lib/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,13 @@ +exports[`test test.js 1`] = ` +"// @flow + +import {bar} from 'foo'; + +bar(5); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +import { bar } from "foo"; +bar(5); + +" +`; diff --git a/tests/modified_lib/jsfmt.spec.js b/tests/modified_lib/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/modified_lib/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/modified_lib/lib/__snapshots__/jsfmt.spec.js.snap b/tests/modified_lib/lib/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..83927dd4b0ac --- /dev/null +++ b/tests/modified_lib/lib/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,22 @@ +exports[`test lib.js 1`] = ` +"declare module \'foo\' { + declare function bar(str: string): number; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/modified_lib/lib/jsfmt.spec.js b/tests/modified_lib/lib/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/modified_lib/lib/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/modified_lib/lib/lib.js b/tests/modified_lib/lib/lib.js new file mode 100644 index 000000000000..fcb3b4a27b45 --- /dev/null +++ b/tests/modified_lib/lib/lib.js @@ -0,0 +1,3 @@ +declare module 'foo' { + declare function bar(str: string): number; +} diff --git a/tests/modified_lib/test.js b/tests/modified_lib/test.js new file mode 100644 index 000000000000..d889dc83ca19 --- /dev/null +++ b/tests/modified_lib/test.js @@ -0,0 +1,5 @@ +// @flow + +import {bar} from 'foo'; + +bar(5); diff --git a/tests/module_not_found_errors/src/__snapshots__/jsfmt.spec.js.snap b/tests/module_not_found_errors/src/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..7ec34361073b --- /dev/null +++ b/tests/module_not_found_errors/src/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,17 @@ +exports[`test index.js 1`] = ` +"// @flow + +require('module_completely_absent'); + +// node_modules/module_outside_of_root/ sits outside of the Flow project root. +// Flow should give a descriptive error about this +require('module_outside_of_root'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +require("module_completely_absent"); +// node_modules/module_outside_of_root/ sits outside of the Flow project root. +// Flow should give a descriptive error about this +require("module_outside_of_root"); + +" +`; diff --git a/tests/module_not_found_errors/src/index.js b/tests/module_not_found_errors/src/index.js new file mode 100644 index 000000000000..f4e259cbbd87 --- /dev/null +++ b/tests/module_not_found_errors/src/index.js @@ -0,0 +1,7 @@ +// @flow + +require('module_completely_absent'); + +// node_modules/module_outside_of_root/ sits outside of the Flow project root. +// Flow should give a descriptive error about this +require('module_outside_of_root'); diff --git a/tests/module_not_found_errors/src/jsfmt.spec.js b/tests/module_not_found_errors/src/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/module_not_found_errors/src/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/module_redirect/A.js b/tests/module_redirect/A.js new file mode 100644 index 000000000000..d2f6a26c0d93 --- /dev/null +++ b/tests/module_redirect/A.js @@ -0,0 +1,3 @@ +declare module A { + declare function foo(): string; +} diff --git a/tests/module_redirect/B.js b/tests/module_redirect/B.js new file mode 100644 index 000000000000..02b47f2182c0 --- /dev/null +++ b/tests/module_redirect/B.js @@ -0,0 +1,6 @@ +/** + * @providesModule B + * @flow + */ + +module.exports = require('A'); diff --git a/tests/module_redirect/C.js b/tests/module_redirect/C.js new file mode 100644 index 000000000000..04871ce2a640 --- /dev/null +++ b/tests/module_redirect/C.js @@ -0,0 +1,6 @@ +/** + * @providesModule C + * @flow + */ + +module.exports = require('B'); diff --git a/tests/module_redirect/D.js b/tests/module_redirect/D.js new file mode 100644 index 000000000000..dc166b39d33c --- /dev/null +++ b/tests/module_redirect/D.js @@ -0,0 +1,8 @@ +/** + * @providesModule D + * @flow + */ + +var bar1: string = require('A'); +var bar2: string = require('B'); +var bar3: string = require('C'); diff --git a/tests/module_redirect/__snapshots__/jsfmt.spec.js.snap b/tests/module_redirect/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..5d49329b0caa --- /dev/null +++ b/tests/module_redirect/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,77 @@ +exports[`test A.js 1`] = ` +"declare module A { + declare function foo(): string; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test B.js 1`] = ` +"/** + * @providesModule B + * @flow + */ + +module.exports = require(\'A\'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule B + * @flow + */ +module.exports = require(\"A\"); + +" +`; + +exports[`test C.js 1`] = ` +"/** + * @providesModule C + * @flow + */ + +module.exports = require(\'B\'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule C + * @flow + */ +module.exports = require(\"B\"); + +" +`; + +exports[`test D.js 1`] = ` +"/** + * @providesModule D + * @flow + */ + +var bar1: string = require(\'A\'); +var bar2: string = require(\'B\'); +var bar3: string = require(\'C\'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule D + * @flow + */ +var bar1: string = require(\"A\"); +var bar2: string = require(\"B\"); +var bar3: string = require(\"C\"); + +" +`; diff --git a/tests/module_redirect/jsfmt.spec.js b/tests/module_redirect/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/module_redirect/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/module_use_strict/__snapshots__/jsfmt.spec.js.snap b/tests/module_use_strict/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..dbd713f36296 --- /dev/null +++ b/tests/module_use_strict/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,22 @@ +exports[`test test.js 1`] = ` +"// @flow + +(01: number); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Invalid number (3:1) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.readNumber (/node_modules/babylon/lib/index.js:1180:12) + at Parser.getTokenFromCode (/node_modules/babylon/lib/index.js:1020:21) + at Parser.readToken (/node_modules/babylon/lib/index.js:694:19) + at Parser. (/node_modules/babylon/lib/index.js:6441:20) + at Parser.readToken (/node_modules/babylon/lib/index.js:5376:22) + at Parser.nextToken (/node_modules/babylon/lib/index.js:684:19) + at Parser.next (/node_modules/babylon/lib/index.js:609:10) + at Parser.eat (/node_modules/babylon/lib/index.js:616:12) + at Parser.pp.expect (/node_modules/babylon/lib/index.js:1621:15) +" +`; diff --git a/tests/module_use_strict/jsfmt.spec.js b/tests/module_use_strict/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/module_use_strict/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/module_use_strict/test.js b/tests/module_use_strict/test.js new file mode 100644 index 000000000000..9fcda932e9f9 --- /dev/null +++ b/tests/module_use_strict/test.js @@ -0,0 +1,3 @@ +// @flow + +(01: number); diff --git a/tests/modules/__snapshots__/jsfmt.spec.js.snap b/tests/modules/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..748288f7203b --- /dev/null +++ b/tests/modules/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,53 @@ +exports[`test cli.js 1`] = ` +"/* @flow */ + +var f = require('./lib'); + +var y:number = f(0); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var f = require("./lib"); +var y: number = f(0); + +" +`; + +exports[`test cli2.js 1`] = ` +"/* @flow */ + +var f = require('./lib'); + +f("..."); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var f = require("./lib"); +f("..."); + +" +`; + +exports[`test lib.js 1`] = ` +"/* @flow */ + +function g(x:string) { } + +//function f(x) { g(x); return x; } +//function f(x:number) { g(x); return x; } +function f(x:number):number { g(x); return x; } + +module.exports = f; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +function g(x: string) { + +} +//function f(x) { g(x); return x; } +//function f(x:number) { g(x); return x; } +function f(x: number): number { + g(x); + return x; +} +module.exports = f; + +" +`; diff --git a/tests/modules/cli.js b/tests/modules/cli.js new file mode 100644 index 000000000000..5c3529d4b6d1 --- /dev/null +++ b/tests/modules/cli.js @@ -0,0 +1,5 @@ +/* @flow */ + +var f = require('./lib'); + +var y:number = f(0); diff --git a/tests/modules/cli2.js b/tests/modules/cli2.js new file mode 100644 index 000000000000..f69b2cbe901e --- /dev/null +++ b/tests/modules/cli2.js @@ -0,0 +1,5 @@ +/* @flow */ + +var f = require('./lib'); + +f("..."); diff --git a/tests/modules/jsfmt.spec.js b/tests/modules/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/modules/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/modules/lib.js b/tests/modules/lib.js new file mode 100644 index 000000000000..f37c923f472a --- /dev/null +++ b/tests/modules/lib.js @@ -0,0 +1,9 @@ +/* @flow */ + +function g(x:string) { } + +//function f(x) { g(x); return x; } +//function f(x:number) { g(x); return x; } +function f(x:number):number { g(x); return x; } + +module.exports = f; diff --git a/tests/more_annot/__snapshots__/jsfmt.spec.js.snap b/tests/more_annot/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..eeb440765656 --- /dev/null +++ b/tests/more_annot/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,81 @@ +exports[`test client_object.js 1`] = ` +"var o = require(\'./object\'); + +var a:number = o.w.z.y; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var o = require(\"./object\"); +var a: number = o.w.z.y; + +" +`; + +exports[`test object.js 1`] = ` +"var o1 = { x: 0, y: \"\" }; +var o2 = { z: o1 } + +var o3 = {}; +o3.w = o2; + +//declare var exports: { w: any }; + +module.exports = o3; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var o1 = { x: 0, y: \"\" }; +var o2 = { z: o1 }; +var o3 = {}; +o3.w = o2; +//declare var exports: { w: any }; +module.exports = o3; + +" +`; + +exports[`test proto.js 1`] = ` +"function Foo() { this.x = 0; } +Foo.prototype.m = function() { } + +var o1: { x: number; m(): void } = new Foo(); + +var o2: Foo = new Foo(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test super.js 1`] = ` +"class C { m() { } } +class D extends C { } + +var d: { +m: () => void } = new D(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/more_annot/client_object.js b/tests/more_annot/client_object.js new file mode 100644 index 000000000000..2a657fc3150a --- /dev/null +++ b/tests/more_annot/client_object.js @@ -0,0 +1,3 @@ +var o = require('./object'); + +var a:number = o.w.z.y; diff --git a/tests/more_annot/jsfmt.spec.js b/tests/more_annot/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/more_annot/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/more_annot/object.js b/tests/more_annot/object.js new file mode 100644 index 000000000000..db079d67aa3b --- /dev/null +++ b/tests/more_annot/object.js @@ -0,0 +1,9 @@ +var o1 = { x: 0, y: "" }; +var o2 = { z: o1 } + +var o3 = {}; +o3.w = o2; + +//declare var exports: { w: any }; + +module.exports = o3; diff --git a/tests/more_annot/proto.js b/tests/more_annot/proto.js new file mode 100644 index 000000000000..6baaa16ea640 --- /dev/null +++ b/tests/more_annot/proto.js @@ -0,0 +1,6 @@ +function Foo() { this.x = 0; } +Foo.prototype.m = function() { } + +var o1: { x: number; m(): void } = new Foo(); + +var o2: Foo = new Foo(); diff --git a/tests/more_annot/super.js b/tests/more_annot/super.js new file mode 100644 index 000000000000..de7d6a2b30e4 --- /dev/null +++ b/tests/more_annot/super.js @@ -0,0 +1,4 @@ +class C { m() { } } +class D extends C { } + +var d: { +m: () => void } = new D(); diff --git a/tests/more_classes/Bar.js b/tests/more_classes/Bar.js new file mode 100644 index 000000000000..dc757801ca4a --- /dev/null +++ b/tests/more_classes/Bar.js @@ -0,0 +1,20 @@ + +/* @providesModule Bar */ + +var Qux = require('Qux'); + +class Bar { + y:number; + self:Bar; + constructor(y:number) { + this.y = y; + this.self = this; + } + + bar(z:string,u:string):string { + new Qux().w = "?"; + return z; + } +} + +module.exports = Bar; diff --git a/tests/more_classes/Foo.js b/tests/more_classes/Foo.js new file mode 100644 index 000000000000..7def23ebcea1 --- /dev/null +++ b/tests/more_classes/Foo.js @@ -0,0 +1,26 @@ + +/* @providesModule Foo */ + +var Bar = require('Bar'); +var Qux = require('Qux'); + +class Foo extends Qux { + x:string; + constructor(x:string) { + this.x = x; + } + + foo(y:string,z):number { + this.x = y; + var u = new Foo("...").qux(); + var v = new Bar(y); + v.self = v; + return v.bar(z,u); + } + + fooqux(x:string) { + this.x; + } +} + +module.exports = Foo; diff --git a/tests/more_classes/Qux.js b/tests/more_classes/Qux.js new file mode 100644 index 000000000000..dae3dfeb102d --- /dev/null +++ b/tests/more_classes/Qux.js @@ -0,0 +1,12 @@ + +/* @providesModule Qux */ + +class Qux { + w:number; + + qux() { return this.w; } + + fooqux(x:number) { } +} + +module.exports = Qux; diff --git a/tests/more_classes/__snapshots__/jsfmt.spec.js.snap b/tests/more_classes/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..c5442e72127e --- /dev/null +++ b/tests/more_classes/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,121 @@ +exports[`test Bar.js 1`] = ` +" +/* @providesModule Bar */ + +var Qux = require('Qux'); + +class Bar { + y:number; + self:Bar; + constructor(y:number) { + this.y = y; + this.self = this; + } + + bar(z:string,u:string):string { + new Qux().w = "?"; + return z; + } +} + +module.exports = Bar; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @providesModule Bar */ +var Qux = require("Qux"); +class Bar { + y: number; + self: Bar; + constructor(y: number) { + this.y = y; + this.self = this; + } + bar(z: string, u: string): string { + new Qux().w = "?"; + return z; + } +} +module.exports = Bar; + +" +`; + +exports[`test Foo.js 1`] = ` +" +/* @providesModule Foo */ + +var Bar = require('Bar'); +var Qux = require('Qux'); + +class Foo extends Qux { + x:string; + constructor(x:string) { + this.x = x; + } + + foo(y:string,z):number { + this.x = y; + var u = new Foo("...").qux(); + var v = new Bar(y); + v.self = v; + return v.bar(z,u); + } + + fooqux(x:string) { + this.x; + } +} + +module.exports = Foo; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @providesModule Foo */ +var Bar = require("Bar"); +var Qux = require("Qux"); +class Foo extends Qux { + x: string; + constructor(x: string) { + this.x = x; + } + foo(y: string, z): number { + this.x = y; + var u = new Foo("...").qux(); + var v = new Bar(y); + v.self = v; + return v.bar(z, u); + } + fooqux(x: string) { + this.x; + } +} +module.exports = Foo; + +" +`; + +exports[`test Qux.js 1`] = ` +" +/* @providesModule Qux */ + +class Qux { + w:number; + + qux() { return this.w; } + + fooqux(x:number) { } +} + +module.exports = Qux; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @providesModule Qux */ +class Qux { + w: number; + qux() { + return this.w; + } + fooqux(x: number) { + + } +} +module.exports = Qux; + +" +`; diff --git a/tests/more_classes/jsfmt.spec.js b/tests/more_classes/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/more_classes/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/more_generics/__snapshots__/jsfmt.spec.js.snap b/tests/more_generics/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..6d6ddfc3331d --- /dev/null +++ b/tests/more_generics/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,48 @@ +exports[`test poly.js 1`] = ` +"var foo1 = function(x:T):T { return x; } + +function foo2(x:T):S { return x; } + +var foo3 = function (x:T):T { return foo3(x); } + +function foo4(x:T):S { return foo4(x); } + +var x = []; +function foo5():Array { return x; } +/* + var a = foo5(); + a[0] = 0; + var b = foo5(); + var y: string = b[0]; +*/ + +var foo6 = function(x:R):R { return foo1(x); } + +function foo7(x:R):R { return foo5(); } + +function foo8(x:U,y):U { + var z = foo8(x,x); + y(); + return x; +} +/* + foo8(0,void 0); +*/ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:340:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/more_generics/jsfmt.spec.js b/tests/more_generics/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/more_generics/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/more_generics/poly.js b/tests/more_generics/poly.js new file mode 100644 index 000000000000..dc0e5465bc93 --- /dev/null +++ b/tests/more_generics/poly.js @@ -0,0 +1,29 @@ +var foo1 = function(x:T):T { return x; } + +function foo2(x:T):S { return x; } + +var foo3 = function (x:T):T { return foo3(x); } + +function foo4(x:T):S { return foo4(x); } + +var x = []; +function foo5():Array { return x; } +/* + var a = foo5(); + a[0] = 0; + var b = foo5(); + var y: string = b[0]; +*/ + +var foo6 = function(x:R):R { return foo1(x); } + +function foo7(x:R):R { return foo5(); } + +function foo8(x:U,y):U { + var z = foo8(x,x); + y(); + return x; +} +/* + foo8(0,void 0); +*/ diff --git a/tests/more_path/Condition.js b/tests/more_path/Condition.js new file mode 100644 index 000000000000..3fbf99d6f1ff --- /dev/null +++ b/tests/more_path/Condition.js @@ -0,0 +1,57 @@ +/* @providesModule Condition */ + +function f(x:number) { } +function g() { return (42 || "hello"); } + +var x = g(); +if (typeof x === "string") { + x = 0; +} +f(x); + +class A {} +function h() { return (42 || new A()); } + +var y = h(); +if (y instanceof A) { + y = 0; +} +//f(y); + +function bar() { return true; } + +class C { qux() { } } + +function foo() { + + var c = "..."; + c = new C(); + if (bar()) { + c.qux(); + } + +} + +function goofy() { + var x = g(); + if (typeof x == 'function') { + x(); + } else { // if (typeof x == 'number') { + //f(x); + } +} + +function goofy2() { + var o = {x : 0} + if (typeof o.x == 'function') { + o.x(); + } + var y = o.x; + if (typeof y == 'function') { + y(); + } else { + //f(y); + } +} + +module.exports = true; diff --git a/tests/more_path/FlowSA.js b/tests/more_path/FlowSA.js new file mode 100644 index 000000000000..51aa51ff367a --- /dev/null +++ b/tests/more_path/FlowSA.js @@ -0,0 +1,12 @@ + +/* @providesModule FlowSA */ + +function check(x:string) { } + +function FlowSA() { + var x = 0; + x = "..."; + check(x); +} + +module.exports = FlowSA; diff --git a/tests/more_path/Sigma.js b/tests/more_path/Sigma.js new file mode 100644 index 000000000000..c5c871de1e29 --- /dev/null +++ b/tests/more_path/Sigma.js @@ -0,0 +1,40 @@ + +/* @providesModule Sigma */ + +class A { a() {} } + +class B extends A { b() {} } + +class C extends B { c() {} } + +function bar(x:B) { + if (x instanceof A) { + x.a(); + x.c(); // error + } else { + x++; // TODO no error? since unreachable (x: B implies x: A) + } +} + +function foo(x:A) { + if (x instanceof C) { + x.a(); + x.b(); + x.c(); + x.d(); // error + } else { + x.a(); + x.c(); // error + } +} + + +class D { d() {} } + +function baz(x:D) { + if (x instanceof A) { + // unreachable, TODO: this shouldn't throw + } +} + +module.exports = "sigma"; diff --git a/tests/more_path/__snapshots__/jsfmt.spec.js.snap b/tests/more_path/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..882f81ebf68b --- /dev/null +++ b/tests/more_path/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,271 @@ +exports[`test Condition.js 1`] = ` +"/* @providesModule Condition */ + +function f(x:number) { } +function g() { return (42 || "hello"); } + +var x = g(); +if (typeof x === "string") { + x = 0; +} +f(x); + +class A {} +function h() { return (42 || new A()); } + +var y = h(); +if (y instanceof A) { + y = 0; +} +//f(y); + +function bar() { return true; } + +class C { qux() { } } + +function foo() { + + var c = "..."; + c = new C(); + if (bar()) { + c.qux(); + } + +} + +function goofy() { + var x = g(); + if (typeof x == 'function') { + x(); + } else { // if (typeof x == 'number') { + //f(x); + } +} + +function goofy2() { + var o = {x : 0} + if (typeof o.x == 'function') { + o.x(); + } + var y = o.x; + if (typeof y == 'function') { + y(); + } else { + //f(y); + } +} + +module.exports = true; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @providesModule Condition */ +function f(x: number) { + +} +function g() { + return 42 || "hello"; +} +var x = g(); +if (typeof x === "string") { + x = 0; +} +f(x); +class A {} +function h() { + return 42 || new A(); +} +var y = h(); +if (y instanceof A) { + y = 0; +} +//f(y); +function bar() { + return true; +} +class C { + qux() { + + } +} +function foo() { + var c = "..."; + c = new C(); + if (bar()) { + c.qux(); + } +} +function goofy() { + var x = g(); + if (typeof x == "function") { + x(); + } else { + + } +} +function goofy2() { + var o = { x: 0 }; + if (typeof o.x == "function") { + o.x(); + } + var y = o.x; + if (typeof y == "function") { + y(); + } else { + + } +} +module.exports = true; + +" +`; + +exports[`test FlowSA.js 1`] = ` +" +/* @providesModule FlowSA */ + +function check(x:string) { } + +function FlowSA() { + var x = 0; + x = "..."; + check(x); +} + +module.exports = FlowSA; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @providesModule FlowSA */ +function check(x: string) { + +} +function FlowSA() { + var x = 0; + x = "..."; + check(x); +} +module.exports = FlowSA; + +" +`; + +exports[`test Sigma.js 1`] = ` +" +/* @providesModule Sigma */ + +class A { a() {} } + +class B extends A { b() {} } + +class C extends B { c() {} } + +function bar(x:B) { + if (x instanceof A) { + x.a(); + x.c(); // error + } else { + x++; // TODO no error? since unreachable (x: B implies x: A) + } +} + +function foo(x:A) { + if (x instanceof C) { + x.a(); + x.b(); + x.c(); + x.d(); // error + } else { + x.a(); + x.c(); // error + } +} + + +class D { d() {} } + +function baz(x:D) { + if (x instanceof A) { + // unreachable, TODO: this shouldn't throw + } +} + +module.exports = "sigma"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @providesModule Sigma */ +class A { + a() { + + } +} +class B extends A { + b() { + + } +} +class C extends B { + c() { + + } +} +function bar(x: B) { + if (x instanceof A) { + x.a(); + x.c();// error + } else { + x++;// TODO no error? since unreachable (x: B implies x: A) + } +} +function foo(x: A) { + if (x instanceof C) { + x.a(); + x.b(); + x.c(); + x.d();// error + } else { + x.a(); + x.c();// error + } +} +class D { + d() { + + } +} +function baz(x: D) { + if (x instanceof A) { + + } +} +module.exports = "sigma"; + +" +`; + +exports[`test test.js 1`] = ` +"class BaseClass { + baseProp: string; +} + +class ChildClass extends BaseClass { + childProp: string; +} + +function test(obj: BaseClass): string { + if (obj instanceof ChildClass) { + return obj.childProp_TYPO; // error (obj: ChildClass) + } + return obj.baseProp; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class BaseClass { + baseProp: string; +} +class ChildClass extends BaseClass { + childProp: string; +} +function test(obj: BaseClass): string { + if (obj instanceof ChildClass) { + return obj.childProp_TYPO;// error (obj: ChildClass) + } + return obj.baseProp; +} + +" +`; diff --git a/tests/more_path/jsfmt.spec.js b/tests/more_path/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/more_path/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/more_path/test.js b/tests/more_path/test.js new file mode 100644 index 000000000000..e54e1def4d8d --- /dev/null +++ b/tests/more_path/test.js @@ -0,0 +1,14 @@ +class BaseClass { + baseProp: string; +} + +class ChildClass extends BaseClass { + childProp: string; +} + +function test(obj: BaseClass): string { + if (obj instanceof ChildClass) { + return obj.childProp_TYPO; // error (obj: ChildClass) + } + return obj.baseProp; +} diff --git a/tests/more_react/API.react.js b/tests/more_react/API.react.js new file mode 100644 index 000000000000..1cb0cafd8bcd --- /dev/null +++ b/tests/more_react/API.react.js @@ -0,0 +1,8 @@ + +var app = require('JSX'); + +app.setProps({y:42}); // error, y:number but foo expects string in App.react +app.setState({z:42}); // error, z:number but foo expects string in App.react + +function bar(x:number) { } +bar(app.props.children); // No error, App doesn't specify propTypes so anything goes diff --git a/tests/more_react/App.react.js b/tests/more_react/App.react.js new file mode 100644 index 000000000000..9121b661cd2b --- /dev/null +++ b/tests/more_react/App.react.js @@ -0,0 +1,43 @@ + +/** + * @providesModule App.react + * @jsx React.DOM + */ + +var React = require('react'); + +// expect args to be strings +function foo(p:string,q:string):string { return p+q; } + +var App = React.createClass({ + + getDefaultProps: function(): { y: string } { + return {y:""}; // infer props.y: string + }, + + getInitialState: function() { + return {z:0}; // infer state.z: number + }, + + handler: function() { + this.setState({z:42}); // ok + }, + + render: function() { + var x = this.props.x; + var y = this.props.y; + var z = this.state.z; + + //this.state; + + return ( +
+ {foo(x,y)} + {foo(z,x)} // error, since z: number +
+ ); + } + +}); + +module.exports = App; diff --git a/tests/more_react/JSX.js b/tests/more_react/JSX.js new file mode 100644 index 000000000000..68a256f9c3e7 --- /dev/null +++ b/tests/more_react/JSX.js @@ -0,0 +1,12 @@ + +/* @providesModule JSX */ + +var React = require('react'); +var App = require('App.react'); + +var app = + // error, y: number but foo expects string in App.react + Some text. + ; + +module.exports = app; diff --git a/tests/more_react/__snapshots__/jsfmt.spec.js.snap b/tests/more_react/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..d749aeba2738 --- /dev/null +++ b/tests/more_react/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,160 @@ +exports[`test API.react.js 1`] = ` +" +var app = require('JSX'); + +app.setProps({y:42}); // error, y:number but foo expects string in App.react +app.setState({z:42}); // error, z:number but foo expects string in App.react + +function bar(x:number) { } +bar(app.props.children); // No error, App doesn't specify propTypes so anything goes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var app = require("JSX"); +app.setProps({ y: 42 });// error, y:number but foo expects string in App.react +app.setState({ z: 42 });// error, z:number but foo expects string in App.react +function bar(x: number) { + +} +bar( + app.props.children +);// No error, App doesn't specify propTypes so anything goes + +" +`; + +exports[`test App.react.js 1`] = ` +" +/** + * @providesModule App.react + * @jsx React.DOM + */ + +var React = require('react'); + +// expect args to be strings +function foo(p:string,q:string):string { return p+q; } + +var App = React.createClass({ + + getDefaultProps: function(): { y: string } { + return {y:""}; // infer props.y: string + }, + + getInitialState: function() { + return {z:0}; // infer state.z: number + }, + + handler: function() { + this.setState({z:42}); // ok + }, + + render: function() { + var x = this.props.x; + var y = this.props.y; + var z = this.state.z; + + //this.state; + + return ( +
+ {foo(x,y)} + {foo(z,x)} // error, since z: number +
+ ); + } + +}); + +module.exports = App; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule App.react + * @jsx React.DOM + */ +var React = require("react"); +// expect args to be strings +function foo(p: string, q: string): string { + return p + q; +} +var App = React.createClass({ + getDefaultProps: function(): { y: string } { + return { y: "" };// infer props.y: string + }, + getInitialState: function() { + return { z: 0 };// infer state.z: number + }, + handler: function() { + this.setState({ z: 42 });// ok + }, + render: function() { + var x = this.props.x; + var y = this.props.y; + var z = this.state.z; + //this.state; + return ( +
+ {foo(x, y)} + {foo(z, x)}// error, since z: number
+ ); + } +}); +module.exports = App; + +" +`; + +exports[`test JSX.js 1`] = ` +" +/* @providesModule JSX */ + +var React = require('react'); +var App = require('App.react'); + +var app = + // error, y: number but foo expects string in App.react + Some text. + ; + +module.exports = app; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @providesModule JSX */ +var React = require("react"); +var App = require("App.react"); +var app = // error, y: number but foo expects string in App.react[object Object] Some text.; +module.exports = app; + +" +`; + +exports[`test propTypes.js 1`] = ` +"var React = require('React'); + +var C = React.createClass({ + propTypes: { + title: React.PropTypes.string.isRequired, + } +}); +var D = React.createClass({ + propTypes: { + name: React.PropTypes.string.isRequired, + ...C.propTypes, + } +}); + +; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var React = require("React"); +var C = React.createClass({ + propTypes: { title: React.PropTypes.string.isRequired } +}); +var D = React.createClass({ + propTypes: { name: React.PropTypes.string.isRequired, ...C.propTypes } +}); +; + +" +`; diff --git a/tests/more_react/jsfmt.spec.js b/tests/more_react/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/more_react/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/more_react/propTypes.js b/tests/more_react/propTypes.js new file mode 100644 index 000000000000..3bb8a2a05f60 --- /dev/null +++ b/tests/more_react/propTypes.js @@ -0,0 +1,18 @@ +var React = require('React'); + +var C = React.createClass({ + propTypes: { + title: React.PropTypes.string.isRequired, + } +}); +var D = React.createClass({ + propTypes: { + name: React.PropTypes.string.isRequired, + ...C.propTypes, + } +}); + +; diff --git a/tests/more_statics/__snapshots__/jsfmt.spec.js.snap b/tests/more_statics/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..2496ab6a3ed1 --- /dev/null +++ b/tests/more_statics/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,27 @@ +exports[`test class_static.js 1`] = ` +"class B { + static foo(): string { return ""; } +} + +class C extends B { + static bar(): string { return ""; } +} + +var x: number = C.bar(); +var y: number = C.foo(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class B { + foo(): string { + return ""; + } +} +class C extends B { + bar(): string { + return ""; + } +} +var x: number = C.bar(); +var y: number = C.foo(); + +" +`; diff --git a/tests/more_statics/class_static.js b/tests/more_statics/class_static.js new file mode 100644 index 000000000000..b66fb6dd07d5 --- /dev/null +++ b/tests/more_statics/class_static.js @@ -0,0 +1,10 @@ +class B { + static foo(): string { return ""; } +} + +class C extends B { + static bar(): string { return ""; } +} + +var x: number = C.bar(); +var y: number = C.foo(); diff --git a/tests/more_statics/jsfmt.spec.js b/tests/more_statics/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/more_statics/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/name_prop/__snapshots__/jsfmt.spec.js.snap b/tests/name_prop/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..87c5c721291c --- /dev/null +++ b/tests/name_prop/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,34 @@ +exports[`test class.js 1`] = ` +"class A {} + +var test1 = A.bar; // Error bar doesn't exist +var test2: string = A.name; +var test3: number = A.name; // Error string ~> number + +var a = new A(); +var test4 = a.constructor.bar; // Error bar doesn't exist +var test5: string = a.constructor.name; +var test6: number = a.constructor.name; // Error string ~> number +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class A {} +var test1 = A.bar;// Error bar doesn't exist +var test2: string = A.name; +var test3: number = A.name;// Error string ~> number +var a = new A(); +var test4 = a.constructor.bar;// Error bar doesn't exist +var test5: string = a.constructor.name; +var test6: number = a.constructor.name;// Error string ~> number + +" +`; + +exports[`test function.js 1`] = ` +"/* TODO - we currently say that a function's statics are an AnyObjT and + * anything goes. When we start enforcing the statics properly, we'll need to + * know that .name exists + */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; diff --git a/tests/name_prop/class.js b/tests/name_prop/class.js new file mode 100644 index 000000000000..0076d3e7af4c --- /dev/null +++ b/tests/name_prop/class.js @@ -0,0 +1,10 @@ +class A {} + +var test1 = A.bar; // Error bar doesn't exist +var test2: string = A.name; +var test3: number = A.name; // Error string ~> number + +var a = new A(); +var test4 = a.constructor.bar; // Error bar doesn't exist +var test5: string = a.constructor.name; +var test6: number = a.constructor.name; // Error string ~> number diff --git a/tests/name_prop/function.js b/tests/name_prop/function.js new file mode 100644 index 000000000000..304356168f1b --- /dev/null +++ b/tests/name_prop/function.js @@ -0,0 +1,4 @@ +/* TODO - we currently say that a function's statics are an AnyObjT and + * anything goes. When we start enforcing the statics properly, we'll need to + * know that .name exists + */ diff --git a/tests/name_prop/jsfmt.spec.js b/tests/name_prop/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/name_prop/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/namespace/__snapshots__/jsfmt.spec.js.snap b/tests/namespace/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..6dc7580c8a6a --- /dev/null +++ b/tests/namespace/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,47 @@ +exports[`test client.js 1`] = ` +"var ns = require(\'./namespace\') + +var bar: string = ns.foo +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var ns = require(\"./namespace\"); +var bar: string = ns.foo; + +" +`; + +exports[`test namespace.js 1`] = ` +"/*@flow*/// import type { T } from \'...\' +type T = (x:number) => void; +var f: T = function(x:string): void { } + +type Map = (x:X) => Y; + +function bar(x:U, f:Map): V { + return f(x); +} + +var y:number = bar(0, x => \"\"); + +type Seq = number | Array; +var s1:Seq = [0,[0]]; +var s2:Seq = [[\"\"]]; + +module.exports = { foo: (\"\": number) }; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/namespace/client.js b/tests/namespace/client.js new file mode 100644 index 000000000000..9ce64259134b --- /dev/null +++ b/tests/namespace/client.js @@ -0,0 +1,3 @@ +var ns = require('./namespace') + +var bar: string = ns.foo diff --git a/tests/namespace/jsfmt.spec.js b/tests/namespace/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/namespace/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/namespace/namespace.js b/tests/namespace/namespace.js new file mode 100644 index 000000000000..ab51ee36eae3 --- /dev/null +++ b/tests/namespace/namespace.js @@ -0,0 +1,17 @@ +/*@flow*/// import type { T } from '...' +type T = (x:number) => void; +var f: T = function(x:string): void { } + +type Map = (x:X) => Y; + +function bar(x:U, f:Map): V { + return f(x); +} + +var y:number = bar(0, x => ""); + +type Seq = number | Array; +var s1:Seq = [0,[0]]; +var s2:Seq = [[""]]; + +module.exports = { foo: ("": number) }; diff --git a/tests/new_react/FeedUFI.react.js b/tests/new_react/FeedUFI.react.js new file mode 100644 index 000000000000..3566747e36ce --- /dev/null +++ b/tests/new_react/FeedUFI.react.js @@ -0,0 +1,42 @@ +/** + * Copyright 2004-present Facebook. All Rights Reserved. + * + * @providesModule FeedUFI.react + * @flow + */ + +'use strict'; + +var UFILikeCount = require('UFILikeCount.react'); +var React = require('react'); + +var FeedUFI = React.createClass({ + _renderLikeCount: function( + feedback: any + ) { + var props = { + className: "", + key: "", + feedback: {feedback}, + permalink: "", + }; + var ignored = ; + return ( + + ); + }, + + render: function(): ?React.Element { + return ( +
+ ); + } + +}); + +module.exports = FeedUFI; diff --git a/tests/new_react/Mixin.js b/tests/new_react/Mixin.js new file mode 100644 index 000000000000..8a3aebf5bbba --- /dev/null +++ b/tests/new_react/Mixin.js @@ -0,0 +1,4 @@ +/* @providesModule Mixin */ +module.exports = { + success: function() { } +}; diff --git a/tests/new_react/UFILikeCount.react.js b/tests/new_react/UFILikeCount.react.js new file mode 100644 index 000000000000..75cc89f20983 --- /dev/null +++ b/tests/new_react/UFILikeCount.react.js @@ -0,0 +1,23 @@ +/** + * Copyright 2004-present Facebook. All Rights Reserved. + * + * @providesModule UFILikeCount.react + * @flow + */ + +'use strict'; + +var React = require('react'); + +var UFILikeCount = React.createClass({ + propTypes: { + permalink: React.PropTypes.string, + feedback: React.PropTypes.object.isRequired + }, + + render: function(): ?React.Element { + return
; + } +}); + +module.exports = UFILikeCount; diff --git a/tests/new_react/__snapshots__/jsfmt.spec.js.snap b/tests/new_react/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..4db6b5d1e26e --- /dev/null +++ b/tests/new_react/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,845 @@ +exports[`test FeedUFI.react.js 1`] = ` +"/** + * Copyright 2004-present Facebook. All Rights Reserved. + * + * @providesModule FeedUFI.react + * @flow + */ + +\'use strict\'; + +var UFILikeCount = require(\'UFILikeCount.react\'); +var React = require(\'react\'); + +var FeedUFI = React.createClass({ + _renderLikeCount: function( + feedback: any + ) { + var props = { + className: \"\", + key: \"\", + feedback: {feedback}, + permalink: \"\", + }; + var ignored = ; + return ( + + ); + }, + + render: function(): ?React.Element { + return ( +
+ ); + } + +}); + +module.exports = FeedUFI; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test Mixin.js 1`] = ` +"/* @providesModule Mixin */ +module.exports = { + success: function() { } +}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @providesModule Mixin */ +module.exports = { + success: function() { + + } +}; + +" +`; + +exports[`test UFILikeCount.react.js 1`] = ` +"/** + * Copyright 2004-present Facebook. All Rights Reserved. + * + * @providesModule UFILikeCount.react + * @flow + */ + +\'use strict\'; + +var React = require(\'react\'); + +var UFILikeCount = React.createClass({ + propTypes: { + permalink: React.PropTypes.string, + feedback: React.PropTypes.object.isRequired + }, + + render: function(): ?React.Element { + return
; + } +}); + +module.exports = UFILikeCount; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test bad_default_props.js 1`] = ` +"var React = require(\'React\'); + +type T1 = { } +type T2 = { x: number } +type T3 = { x: number, y: number } + +class C1 extends React.Component { // error +} + +class C2 extends React.Component { // OK +} + +// no need to add type arguments to React.Component +class C3 extends React.Component { // OK + static defaultProps: T1; + props: T2; +} + +class C4 extends React.Component { // OK, recommended + // no need to declare defaultProps unless necessary + props: T2; +} + +class C5 extends React.Component { // error +} + +class C6 extends React.Component { // OK, recommended + static defaultProps: T2; + props: T3; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1180:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test classes.js 1`] = ` +"var React = require(\'React\'); + +type DefaultProps = { }; +type Props = { x: number }; +type State = { y: number }; + +class Foo extends React.Component { + props: Props; + state: State; + static defaultProps: DefaultProps; + + is_mounted: boolean; + + static bar(): void {} + + qux(): void { + var _: string = this.props.x; + } + + constructor(props) { + super(props); + this.state = { y: \"\" }; + } + + setState(o: { y_: string }): void { } + + componentDidMount(): void { + this.is_mounted = true; + } + + componentWillReceiveProps( + nextProps: Object, + nextContext: any + ): void { + this.qux(); + } + +} + +Foo.defaultProps = 0; +var foo: $jsx = ; + +Foo.bar(); + +var FooLegacy = React.createClass({ + is_mounted: (undefined: ?boolean), + + propTypes: { + x: React.PropTypes.number.isRequired + }, + + getDefaultProps(): DefaultProps { return {} }, + + statics: { + bar(): void {} + }, + + qux(): void { + var _: string = this.props.x; + }, + + getInitialState(): { y: string } { + return { y: \"\" }; + }, + + setState(o: { y_: string }): void { }, + + componentDidMount(): void { + this.is_mounted = true; + }, + + componentWillReceiveProps( + nextProps: Object, + nextContext: any + ): void { + this.qux(); + }, +}); + +FooLegacy.defaultProps = 0; // TODO: should be error +var foo_legacy: $jsx = ; + +FooLegacy.bar(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test import-react.js 1`] = ` +"/* @flow */ + +// Testing local binding of React in all kinds of ways. The only reason this +// might even be an issue is that internally, the use of JSX triggers an +// implicit require(\'react\'), so any bugs in (1) interop of CJS require and ES6 +// import (2) module re-export, as used to redirect the module name \'React\' to +// \'react\' might show up here. + +import React from \"react\"; +//import React from \"React\"; +//var React = require(\"react\"); +//var React = require(\"React\"); + +class HelloMessage extends React.Component { + props: { name: string }; +} + +; // number ~/~> string error +; // ok +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:5687 + throw jsxError || err; + ^ + +SyntaxError: Invalid number (18:20) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.readNumber (/node_modules/babylon/lib/index.js:1180:12) + at Parser.getTokenFromCode (/node_modules/babylon/lib/index.js:1020:21) + at Parser.readToken (/node_modules/babylon/lib/index.js:694:19) + at Parser. (/node_modules/babylon/lib/index.js:6441:20) + at Parser.readToken (/node_modules/babylon/lib/index.js:5376:22) + at Parser.nextToken (/node_modules/babylon/lib/index.js:684:19) + at Parser.next (/node_modules/babylon/lib/index.js:609:10) + at Parser.pp$8.jsxParseExpressionContainer (/node_modules/babylon/lib/index.js:6285:8) + at Parser.pp$8.jsxParseAttributeValue (/node_modules/babylon/lib/index.js:6241:19) +" +`; + +exports[`test new_react.js 1`] = ` +"var React = require(\'react\'); +var Mixin = require(\'Mixin\'); +var C = React.createClass({ + mixins: [Mixin], + propTypes: { + x: React.PropTypes.string.isRequired, + y: React.PropTypes.array, + z: React.PropTypes.number + }, + replaceProps(props: { }) { }, + + getDefaultProps(): { z: number } { + return { z: 0 }; + }, + getInitialState() { return 4; }, + render() { + var foo: string = this.state; + var bar: string = this.props; + var qux: string = this.props.z; + var w:number = this.props.x; + this.props.y[0]; + var len:number = this.props.x.length; + this.success(); + return
; + } + +}) + +var element = ; +var element_ = ; + +var x: number = C.displayName; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var React = require(\"react\"); +var Mixin = require(\"Mixin\"); +var C = React.createClass({ + mixins: [ Mixin ], + propTypes: { + x: React.PropTypes.string.isRequired, + y: React.PropTypes.array, + z: React.PropTypes.number + }, + replaceProps(props: {}) { + + }, + getDefaultProps(): { z: number } { + return { z: 0 }; + }, + getInitialState() { + return 4; + }, + render() { + var foo: string = this.state; + var bar: string = this.props; + var qux: string = this.props.z; + var w: number = this.props.x; + this.props.y[0]; + var len: number = this.props.x.length; + this.success(); + return
; + } +}); +var element = ; +var element_ = ; +var x: number = C.displayName; + +" +`; + +exports[`test propTypes.js 1`] = ` +"var React = require(\'react\'); +var PropTypes = React.PropTypes; + +var C = React.createClass({ + propTypes: { + statistics: PropTypes.arrayOf(PropTypes.shape({ + label: PropTypes.string.isRequired, + value: PropTypes.number, + })).isRequired, + } +}); + +; // error (label is required, value not required) + +var props: Array<{label: string, value?: number}> = [ + {}, + {label:\"\",value:undefined}, +]; // error (same as ^) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test props.js 1`] = ` +"var React = require(\'react\'); +var TestProps = React.createClass({ + + propTypes: { + x: React.PropTypes.string, + z: React.PropTypes.number + }, + + getDefaultProps: function() { + return {x: \'\', y: 0} + }, + + test: function() { + var a: number = this.props.x; // error + var b: string = this.props.y; // error + var c: string = this.props.z; // error + } +}); + +var element = ; // 3 errors + +(element: $jsx<*>); +(element: $jsx); +var FooProps = React.createClass({ + propTypes: { w: React.PropTypes.string.isRequired } +}); +(element: $jsx); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test props2.js 1`] = ` +"var React = require(\'react\'); +var C = React.createClass({ + propTypes: { + foo: React.PropTypes.string.isRequired, + bar: React.PropTypes.string.isRequired, + } +}); +var D = React.createClass({ + getInitialState: function(): { bar: number } { + return { bar: 0 }; + }, + render: function() { + var obj = { bar: 0 }; + var s: string = this.state.bar; + return ; + } +}); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var React = require(\"react\"); +var C = React.createClass({ + propTypes: { + foo: React.PropTypes.string.isRequired, + bar: React.PropTypes.string.isRequired + } +}); +var D = React.createClass({ + getInitialState: function(): { bar: number } { + return { bar: 0 }; + }, + render: function() { + var obj = { bar: 0 }; + var s: string = this.state.bar; + return ; + } +}); + +" +`; + +exports[`test props3.js 1`] = ` +"var React = require(\'react\'); +var TestProps = React.createClass({ + // Do something illegal inside of propTypes and make sure Flow notices + propTypes: { + arr: React.PropTypes.array, + arr_rec: React.PropTypes.array.isRequired, + bool: React.PropTypes.bool, + bool_rec: React.PropTypes.bool.isRequired, + func: React.PropTypes.func, + func_rec: React.PropTypes.func.isRequired, + number: React.PropTypes.number, + number_rec: React.PropTypes.number.isRequired, + object: React.PropTypes.object, + object_rec: React.PropTypes.object.isRequired, + string: React.PropTypes.string, + string_rec: React.PropTypes.string.isRequired, + + any: React.PropTypes.any, + any_rec: React.PropTypes.any.isRequired, + element: React.PropTypes.element, + element_rec: React.PropTypes.element.isRequired, + node: React.PropTypes.node, + node_rec: React.PropTypes.node.isRequired, + + arrayOf: React.PropTypes.arrayOf(React.PropTypes.string), + arrayOf_rec: React.PropTypes.arrayOf(React.PropTypes.string).isRequired, + instanceOf: React.PropTypes.instanceOf(Object), + instanceOf_rec: React.PropTypes.instanceOf(Object).isRequired, + objectOf: React.PropTypes.objectOf(React.PropTypes.string), + objectOf_rec: React.PropTypes.objectOf(React.PropTypes.string).isRequired, + oneOf: React.PropTypes.oneOf([\"yes\", \"no\"]), + oneOf_rec: React.PropTypes.oneOf([\"yes\", \"no\"]).isRequired, + oneOfType: React.PropTypes.oneOfType( + [React.PropTypes.string, React.PropTypes.number] + ), + oneOfType_rec: React.PropTypes.oneOfType( + [React.PropTypes.string, React.PropTypes.number] + ).isRequired, + shape: React.PropTypes.shape({ + foo: React.PropTypes.string, + bar: React.PropTypes.number, + }), + shape_rec: React.PropTypes.shape({ + foo: React.PropTypes.string, + bar: React.PropTypes.number, + }).isRequired, + + // And do something bad here + bad_one: React.PropTypes.imaginaryType, + bad_two: React.PropTypes.string.inRequired, + }, +}); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var React = require(\"react\"); +var TestProps = React.createClass({ + // Do something illegal inside of propTypes and make sure Flow notices + propTypes: { + arr: React.PropTypes.array, + arr_rec: React.PropTypes.array.isRequired, + bool: React.PropTypes.bool, + bool_rec: React.PropTypes.bool.isRequired, + func: React.PropTypes.func, + func_rec: React.PropTypes.func.isRequired, + number: React.PropTypes.number, + number_rec: React.PropTypes.number.isRequired, + object: React.PropTypes.object, + object_rec: React.PropTypes.object.isRequired, + string: React.PropTypes.string, + string_rec: React.PropTypes.string.isRequired, + any: React.PropTypes.any, + any_rec: React.PropTypes.any.isRequired, + element: React.PropTypes.element, + element_rec: React.PropTypes.element.isRequired, + node: React.PropTypes.node, + node_rec: React.PropTypes.node.isRequired, + arrayOf: React.PropTypes.arrayOf(React.PropTypes.string), + arrayOf_rec: React.PropTypes.arrayOf(React.PropTypes.string).isRequired, + instanceOf: React.PropTypes.instanceOf(Object), + instanceOf_rec: React.PropTypes.instanceOf(Object).isRequired, + objectOf: React.PropTypes.objectOf(React.PropTypes.string), + objectOf_rec: React.PropTypes.objectOf(React.PropTypes.string).isRequired, + oneOf: React.PropTypes.oneOf([ \"yes\", \"no\" ]), + oneOf_rec: React.PropTypes.oneOf([ \"yes\", \"no\" ]).isRequired, + oneOfType: React.PropTypes.oneOfType( + [ React.PropTypes.string, React.PropTypes.number ] + ), + oneOfType_rec: React.PropTypes.oneOfType( + [ React.PropTypes.string, React.PropTypes.number ] + ).isRequired, + shape: React.PropTypes.shape({ + foo: React.PropTypes.string, + bar: React.PropTypes.number + }), + shape_rec: React.PropTypes.shape({ + foo: React.PropTypes.string, + bar: React.PropTypes.number + }).isRequired, + // And do something bad here bad_one: React.PropTypes.imaginaryType, + bad_two: React.PropTypes.string.inRequired + } +}); + +" +`; + +exports[`test props4.js 1`] = ` +"// @flow + +import React from \"React\"; + +class JDiv extends React.Component { + // static defaultProps: { }; + props: { + id: string + }; +} + +// Should be a type error (\'id\' takes a string, not a number..) +; + +class Example extends React.Component { + props: { bar: string }; + + render() { + return
{this.props.bar}
+ } +} + +React.render( + , + document.body +); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +import React from \"React\"; +class JDiv extends React.Component { + // static defaultProps: { }; + props: { id: string }; +} +// Should be a type error (\'id\' takes a string, not a number..) +; +class Example extends React.Component { + props: { bar: string }; + render() { + return
{this.props.bar}
; + } +} +React.render(, document.body); + +" +`; + +exports[`test props5.js 1`] = ` +"var React = require(\'React\'); + +var C = React.createClass({ + getDefaultProps: function() { + return { x: 0 }; + } +}); + +module.exports = C; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var React = require(\"React\"); +var C = React.createClass({ + getDefaultProps: function() { + return { x: 0 }; + } +}); +module.exports = C; + +" +`; + +exports[`test state.js 1`] = ` +"/* @flow */ + +var React = require(\'react\'); + +type State = { + bar: ?{ qux: string; }; +}; + +var ReactClass = React.createClass({ + getInitialState: function():State { + return { bar: null }; + }, + + render: function(): any { + // Any state access here seems to make state any + this.state; + return ( +
+ {this.state.bar.qux} +
+ ); + } +}); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var React = require(\"react\"); +type State = { bar: ?{ qux: string } }; +var ReactClass = React.createClass({ + getInitialState: function(): State { + return { bar: null }; + }, + render: function(): any { + // Any state access here seems to make state any + this.state; + return ( +
+ {this.state.bar.qux} +
+ ); + } +}); + +" +`; + +exports[`test state2.js 1`] = ` +"// @flow + +var React = require(\'react\'); + +type FooState = { + key: ?Object; +}; + +var Comp = React.createClass({ + getInitialState: function(): FooState { + return { + key: null, // this used to cause a missing annotation error + }; + } +}); + +module.exports = Comp; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +var React = require(\"react\"); +type FooState = { key: ?Object }; +var Comp = React.createClass({ + getInitialState: function(): FooState { + return { // this used to cause a missing annotation error key: null }; + } +}); +module.exports = Comp; + +" +`; + +exports[`test state3.js 1`] = ` +"var React = require(\'react\'); +var TestState = React.createClass({ + + getInitialState: function(): { x: string; } { + return { + x: \'\' + } + }, + + test: function() { + var a: number = this.state.x; // error + + this.setState({ + x: false // error + }) + } + +}); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var React = require(\"react\"); +var TestState = React.createClass({ + getInitialState: function(): { x: string } { + return { x: \"\" }; + }, + test: function() { + var a: number = this.state.x;// error + this.setState({ // error x: false }); + } +}); + +" +`; + +exports[`test state4.js 1`] = ` +"var React = require(\'React\'); + +var C = React.createClass({ + getInitialState: function() { + return { x: 0 }; + }, + + render() { + this.setState({ y: 0 }); + return
{this.state.z}
+ } + +}); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var React = require(\"React\"); +var C = React.createClass({ + getInitialState: function() { + return { x: 0 }; + }, + render() { + this.setState({ y: 0 }); + return
{this.state.z}
; + } +}); + +" +`; + +exports[`test state5.js 1`] = ` +"var React = require(\'React\'); + +class C extends React.Component { + foo(): number { + return this.state.x; // error: need to declare type of state + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var React = require(\"React\"); +class C extends React.Component { + foo(): number { + return this.state.x;// error: need to declare type of state + } +} + +" +`; diff --git a/tests/new_react/bad_default_props.js b/tests/new_react/bad_default_props.js new file mode 100644 index 000000000000..9dd3159f431a --- /dev/null +++ b/tests/new_react/bad_default_props.js @@ -0,0 +1,30 @@ +var React = require('React'); + +type T1 = { } +type T2 = { x: number } +type T3 = { x: number, y: number } + +class C1 extends React.Component { // error +} + +class C2 extends React.Component { // OK +} + +// no need to add type arguments to React.Component +class C3 extends React.Component { // OK + static defaultProps: T1; + props: T2; +} + +class C4 extends React.Component { // OK, recommended + // no need to declare defaultProps unless necessary + props: T2; +} + +class C5 extends React.Component { // error +} + +class C6 extends React.Component { // OK, recommended + static defaultProps: T2; + props: T3; +} diff --git a/tests/new_react/classes.js b/tests/new_react/classes.js new file mode 100644 index 000000000000..7574a00af3b7 --- /dev/null +++ b/tests/new_react/classes.js @@ -0,0 +1,83 @@ +var React = require('React'); + +type DefaultProps = { }; +type Props = { x: number }; +type State = { y: number }; + +class Foo extends React.Component { + props: Props; + state: State; + static defaultProps: DefaultProps; + + is_mounted: boolean; + + static bar(): void {} + + qux(): void { + var _: string = this.props.x; + } + + constructor(props) { + super(props); + this.state = { y: "" }; + } + + setState(o: { y_: string }): void { } + + componentDidMount(): void { + this.is_mounted = true; + } + + componentWillReceiveProps( + nextProps: Object, + nextContext: any + ): void { + this.qux(); + } + +} + +Foo.defaultProps = 0; +var foo: $jsx = ; + +Foo.bar(); + +var FooLegacy = React.createClass({ + is_mounted: (undefined: ?boolean), + + propTypes: { + x: React.PropTypes.number.isRequired + }, + + getDefaultProps(): DefaultProps { return {} }, + + statics: { + bar(): void {} + }, + + qux(): void { + var _: string = this.props.x; + }, + + getInitialState(): { y: string } { + return { y: "" }; + }, + + setState(o: { y_: string }): void { }, + + componentDidMount(): void { + this.is_mounted = true; + }, + + componentWillReceiveProps( + nextProps: Object, + nextContext: any + ): void { + this.qux(); + }, +}); + +FooLegacy.defaultProps = 0; // TODO: should be error +var foo_legacy: $jsx = ; + +FooLegacy.bar(); diff --git a/tests/new_react/fakelib/__snapshots__/jsfmt.spec.js.snap b/tests/new_react/fakelib/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..4a9d5f412fd7 --- /dev/null +++ b/tests/new_react/fakelib/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,25 @@ +exports[`test type_aliases.js 1`] = ` +"declare var $React: $Exports<\'react\'>; // fake import +// Strawman: revised definition of $jsx (alternatively, React.Element). +// Using bounded poly to specify a constraint on a type parameter, and +// existentials to elide type arguments. +type _ReactElement, C: $React.Component> = $React.Element; +type $jsx = _ReactElement<*, *, *, C>; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/new_react/fakelib/jsfmt.spec.js b/tests/new_react/fakelib/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/new_react/fakelib/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/new_react/fakelib/type_aliases.js b/tests/new_react/fakelib/type_aliases.js new file mode 100644 index 000000000000..864772039b5c --- /dev/null +++ b/tests/new_react/fakelib/type_aliases.js @@ -0,0 +1,6 @@ +declare var $React: $Exports<'react'>; // fake import +// Strawman: revised definition of $jsx (alternatively, React.Element). +// Using bounded poly to specify a constraint on a type parameter, and +// existentials to elide type arguments. +type _ReactElement, C: $React.Component> = $React.Element; +type $jsx = _ReactElement<*, *, *, C>; diff --git a/tests/new_react/import-react.js b/tests/new_react/import-react.js new file mode 100644 index 000000000000..f4fda4d77d95 --- /dev/null +++ b/tests/new_react/import-react.js @@ -0,0 +1,19 @@ +/* @flow */ + +// Testing local binding of React in all kinds of ways. The only reason this +// might even be an issue is that internally, the use of JSX triggers an +// implicit require('react'), so any bugs in (1) interop of CJS require and ES6 +// import (2) module re-export, as used to redirect the module name 'React' to +// 'react' might show up here. + +import React from "react"; +//import React from "React"; +//var React = require("react"); +//var React = require("React"); + +class HelloMessage extends React.Component { + props: { name: string }; +} + +; // number ~/~> string error +; // ok diff --git a/tests/new_react/jsfmt.spec.js b/tests/new_react/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/new_react/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/new_react/new_react.js b/tests/new_react/new_react.js new file mode 100644 index 000000000000..70444d1cf288 --- /dev/null +++ b/tests/new_react/new_react.js @@ -0,0 +1,32 @@ +var React = require('react'); +var Mixin = require('Mixin'); +var C = React.createClass({ + mixins: [Mixin], + propTypes: { + x: React.PropTypes.string.isRequired, + y: React.PropTypes.array, + z: React.PropTypes.number + }, + replaceProps(props: { }) { }, + + getDefaultProps(): { z: number } { + return { z: 0 }; + }, + getInitialState() { return 4; }, + render() { + var foo: string = this.state; + var bar: string = this.props; + var qux: string = this.props.z; + var w:number = this.props.x; + this.props.y[0]; + var len:number = this.props.x.length; + this.success(); + return
; + } + +}) + +var element = ; +var element_ = ; + +var x: number = C.displayName; diff --git a/tests/new_react/propTypes.js b/tests/new_react/propTypes.js new file mode 100644 index 000000000000..2acaef6fafcf --- /dev/null +++ b/tests/new_react/propTypes.js @@ -0,0 +1,21 @@ +var React = require('react'); +var PropTypes = React.PropTypes; + +var C = React.createClass({ + propTypes: { + statistics: PropTypes.arrayOf(PropTypes.shape({ + label: PropTypes.string.isRequired, + value: PropTypes.number, + })).isRequired, + } +}); + +; // error (label is required, value not required) + +var props: Array<{label: string, value?: number}> = [ + {}, + {label:"",value:undefined}, +]; // error (same as ^) diff --git a/tests/new_react/props.js b/tests/new_react/props.js new file mode 100644 index 000000000000..055d608ef2f3 --- /dev/null +++ b/tests/new_react/props.js @@ -0,0 +1,27 @@ +var React = require('react'); +var TestProps = React.createClass({ + + propTypes: { + x: React.PropTypes.string, + z: React.PropTypes.number + }, + + getDefaultProps: function() { + return {x: '', y: 0} + }, + + test: function() { + var a: number = this.props.x; // error + var b: string = this.props.y; // error + var c: string = this.props.z; // error + } +}); + +var element = ; // 3 errors + +(element: $jsx<*>); +(element: $jsx); +var FooProps = React.createClass({ + propTypes: { w: React.PropTypes.string.isRequired } +}); +(element: $jsx); diff --git a/tests/new_react/props2.js b/tests/new_react/props2.js new file mode 100644 index 000000000000..6f8fd76c440f --- /dev/null +++ b/tests/new_react/props2.js @@ -0,0 +1,17 @@ +var React = require('react'); +var C = React.createClass({ + propTypes: { + foo: React.PropTypes.string.isRequired, + bar: React.PropTypes.string.isRequired, + } +}); +var D = React.createClass({ + getInitialState: function(): { bar: number } { + return { bar: 0 }; + }, + render: function() { + var obj = { bar: 0 }; + var s: string = this.state.bar; + return ; + } +}); diff --git a/tests/new_react/props3.js b/tests/new_react/props3.js new file mode 100644 index 000000000000..169b85c9afc4 --- /dev/null +++ b/tests/new_react/props3.js @@ -0,0 +1,52 @@ +var React = require('react'); +var TestProps = React.createClass({ + // Do something illegal inside of propTypes and make sure Flow notices + propTypes: { + arr: React.PropTypes.array, + arr_rec: React.PropTypes.array.isRequired, + bool: React.PropTypes.bool, + bool_rec: React.PropTypes.bool.isRequired, + func: React.PropTypes.func, + func_rec: React.PropTypes.func.isRequired, + number: React.PropTypes.number, + number_rec: React.PropTypes.number.isRequired, + object: React.PropTypes.object, + object_rec: React.PropTypes.object.isRequired, + string: React.PropTypes.string, + string_rec: React.PropTypes.string.isRequired, + + any: React.PropTypes.any, + any_rec: React.PropTypes.any.isRequired, + element: React.PropTypes.element, + element_rec: React.PropTypes.element.isRequired, + node: React.PropTypes.node, + node_rec: React.PropTypes.node.isRequired, + + arrayOf: React.PropTypes.arrayOf(React.PropTypes.string), + arrayOf_rec: React.PropTypes.arrayOf(React.PropTypes.string).isRequired, + instanceOf: React.PropTypes.instanceOf(Object), + instanceOf_rec: React.PropTypes.instanceOf(Object).isRequired, + objectOf: React.PropTypes.objectOf(React.PropTypes.string), + objectOf_rec: React.PropTypes.objectOf(React.PropTypes.string).isRequired, + oneOf: React.PropTypes.oneOf(["yes", "no"]), + oneOf_rec: React.PropTypes.oneOf(["yes", "no"]).isRequired, + oneOfType: React.PropTypes.oneOfType( + [React.PropTypes.string, React.PropTypes.number] + ), + oneOfType_rec: React.PropTypes.oneOfType( + [React.PropTypes.string, React.PropTypes.number] + ).isRequired, + shape: React.PropTypes.shape({ + foo: React.PropTypes.string, + bar: React.PropTypes.number, + }), + shape_rec: React.PropTypes.shape({ + foo: React.PropTypes.string, + bar: React.PropTypes.number, + }).isRequired, + + // And do something bad here + bad_one: React.PropTypes.imaginaryType, + bad_two: React.PropTypes.string.inRequired, + }, +}); diff --git a/tests/new_react/props4.js b/tests/new_react/props4.js new file mode 100644 index 000000000000..ba428355b20a --- /dev/null +++ b/tests/new_react/props4.js @@ -0,0 +1,26 @@ +// @flow + +import React from "React"; + +class JDiv extends React.Component { + // static defaultProps: { }; + props: { + id: string + }; +} + +// Should be a type error ('id' takes a string, not a number..) +; + +class Example extends React.Component { + props: { bar: string }; + + render() { + return
{this.props.bar}
+ } +} + +React.render( + , + document.body +); diff --git a/tests/new_react/props5.js b/tests/new_react/props5.js new file mode 100644 index 000000000000..0e236158260a --- /dev/null +++ b/tests/new_react/props5.js @@ -0,0 +1,9 @@ +var React = require('React'); + +var C = React.createClass({ + getDefaultProps: function() { + return { x: 0 }; + } +}); + +module.exports = C; diff --git a/tests/new_react/state.js b/tests/new_react/state.js new file mode 100644 index 000000000000..aa3bac629751 --- /dev/null +++ b/tests/new_react/state.js @@ -0,0 +1,23 @@ +/* @flow */ + +var React = require('react'); + +type State = { + bar: ?{ qux: string; }; +}; + +var ReactClass = React.createClass({ + getInitialState: function():State { + return { bar: null }; + }, + + render: function(): any { + // Any state access here seems to make state any + this.state; + return ( +
+ {this.state.bar.qux} +
+ ); + } +}); diff --git a/tests/new_react/state2.js b/tests/new_react/state2.js new file mode 100644 index 000000000000..213d7679f51e --- /dev/null +++ b/tests/new_react/state2.js @@ -0,0 +1,17 @@ +// @flow + +var React = require('react'); + +type FooState = { + key: ?Object; +}; + +var Comp = React.createClass({ + getInitialState: function(): FooState { + return { + key: null, // this used to cause a missing annotation error + }; + } +}); + +module.exports = Comp; diff --git a/tests/new_react/state3.js b/tests/new_react/state3.js new file mode 100644 index 000000000000..4b7541d604d4 --- /dev/null +++ b/tests/new_react/state3.js @@ -0,0 +1,18 @@ +var React = require('react'); +var TestState = React.createClass({ + + getInitialState: function(): { x: string; } { + return { + x: '' + } + }, + + test: function() { + var a: number = this.state.x; // error + + this.setState({ + x: false // error + }) + } + +}); diff --git a/tests/new_react/state4.js b/tests/new_react/state4.js new file mode 100644 index 000000000000..9b1ea8251736 --- /dev/null +++ b/tests/new_react/state4.js @@ -0,0 +1,13 @@ +var React = require('React'); + +var C = React.createClass({ + getInitialState: function() { + return { x: 0 }; + }, + + render() { + this.setState({ y: 0 }); + return
{this.state.z}
+ } + +}); diff --git a/tests/new_react/state5.js b/tests/new_react/state5.js new file mode 100644 index 000000000000..f14b3db25a1e --- /dev/null +++ b/tests/new_react/state5.js @@ -0,0 +1,7 @@ +var React = require('React'); + +class C extends React.Component { + foo(): number { + return this.state.x; // error: need to declare type of state + } +} diff --git a/tests/node_haste/__snapshots__/jsfmt.spec.js.snap b/tests/node_haste/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..2794c8648aa9 --- /dev/null +++ b/tests/node_haste/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,15 @@ +exports[`test client.js 1`] = ` +"var md5 = require('./md5'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var md5 = require("./md5"); + +" +`; + +exports[`test md5.js 1`] = ` +"/* @providesModule md5 */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; diff --git a/tests/node_haste/client.js b/tests/node_haste/client.js new file mode 100644 index 000000000000..7c4f789d466a --- /dev/null +++ b/tests/node_haste/client.js @@ -0,0 +1 @@ +var md5 = require('./md5'); diff --git a/tests/node_haste/external/_d3/__snapshots__/jsfmt.spec.js.snap b/tests/node_haste/external/_d3/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..af2f32ce6008 --- /dev/null +++ b/tests/node_haste/external/_d3/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,6 @@ +exports[`test min.js 1`] = ` +"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; diff --git a/tests/node_haste/external/_d3/jsfmt.spec.js b/tests/node_haste/external/_d3/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_haste/external/_d3/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_haste/external/_d3/min.js b/tests/node_haste/external/_d3/min.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/node_haste/foo/bar/__snapshots__/jsfmt.spec.js.snap b/tests/node_haste/foo/bar/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..86fe21ff2a9b --- /dev/null +++ b/tests/node_haste/foo/bar/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,18 @@ +exports[`test client.js 1`] = ` +"var docblock = require('qux/docblock'); +var min = require('d3/min.js'); +var corge = require('qux/corge'); + +// make sure we don't pick up non-header @providesModule +// annotations - see node_modules/qux/docblock.js +var unreachable = require('annotation'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var docblock = require("qux/docblock"); +var min = require("d3/min.js"); +var corge = require("qux/corge"); +// make sure we don't pick up non-header @providesModule +// annotations - see node_modules/qux/docblock.js +var unreachable = require("annotation"); + +" +`; diff --git a/tests/node_haste/foo/bar/client.js b/tests/node_haste/foo/bar/client.js new file mode 100644 index 000000000000..f77729c44c56 --- /dev/null +++ b/tests/node_haste/foo/bar/client.js @@ -0,0 +1,7 @@ +var docblock = require('qux/docblock'); +var min = require('d3/min.js'); +var corge = require('qux/corge'); + +// make sure we don't pick up non-header @providesModule +// annotations - see node_modules/qux/docblock.js +var unreachable = require('annotation'); diff --git a/tests/node_haste/foo/bar/jsfmt.spec.js b/tests/node_haste/foo/bar/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_haste/foo/bar/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_haste/jsfmt.spec.js b/tests/node_haste/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_haste/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_haste/md5.js b/tests/node_haste/md5.js new file mode 100644 index 000000000000..c358d72168a6 --- /dev/null +++ b/tests/node_haste/md5.js @@ -0,0 +1 @@ +/* @providesModule md5 */ diff --git a/tests/node_haste/ws/__snapshots__/jsfmt.spec.js.snap b/tests/node_haste/ws/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..72caaf084cb1 --- /dev/null +++ b/tests/node_haste/ws/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,6 @@ +exports[`test index.js 1`] = ` +"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; diff --git a/tests/node_haste/ws/index.js b/tests/node_haste/ws/index.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/node_haste/ws/jsfmt.spec.js b/tests/node_haste/ws/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_haste/ws/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_haste/ws/test/__snapshots__/jsfmt.spec.js.snap b/tests/node_haste/ws/test/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..f54d0216ceb7 --- /dev/null +++ b/tests/node_haste/ws/test/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,7 @@ +exports[`test client.js 1`] = ` +"var ws = require('../'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var ws = require("../"); + +" +`; diff --git a/tests/node_haste/ws/test/client.js b/tests/node_haste/ws/test/client.js new file mode 100644 index 000000000000..2fb0ed6d4dfd --- /dev/null +++ b/tests/node_haste/ws/test/client.js @@ -0,0 +1 @@ +var ws = require('../'); diff --git a/tests/node_haste/ws/test/jsfmt.spec.js b/tests/node_haste/ws/test/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_haste/ws/test/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_modules_with_symlinks/root/__snapshots__/jsfmt.spec.js.snap b/tests/node_modules_with_symlinks/root/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..b2745f3910e8 --- /dev/null +++ b/tests/node_modules_with_symlinks/root/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,13 @@ +exports[`test foo.js 1`] = ` +"var x = require('symlink_lib'); +var y = require('symlink_lib_outside_root'); +console.log(x); +console.log(y); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var x = require("symlink_lib"); +var y = require("symlink_lib_outside_root"); +console.log(x); +console.log(y); + +" +`; diff --git a/tests/node_modules_with_symlinks/root/foo.js b/tests/node_modules_with_symlinks/root/foo.js new file mode 100644 index 000000000000..a281d0aade1d --- /dev/null +++ b/tests/node_modules_with_symlinks/root/foo.js @@ -0,0 +1,4 @@ +var x = require('symlink_lib'); +var y = require('symlink_lib_outside_root'); +console.log(x); +console.log(y); diff --git a/tests/node_modules_with_symlinks/root/jsfmt.spec.js b/tests/node_modules_with_symlinks/root/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_modules_with_symlinks/root/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_modules_with_symlinks/root/symlink_lib/__snapshots__/jsfmt.spec.js.snap b/tests/node_modules_with_symlinks/root/symlink_lib/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..9ccacc6e6df6 --- /dev/null +++ b/tests/node_modules_with_symlinks/root/symlink_lib/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,7 @@ +exports[`test index.js 1`] = ` +"module.exports = { bar: "bar" }; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +module.exports = { bar: "bar" }; + +" +`; diff --git a/tests/node_modules_with_symlinks/root/symlink_lib/index.js b/tests/node_modules_with_symlinks/root/symlink_lib/index.js new file mode 100644 index 000000000000..206c5837e74d --- /dev/null +++ b/tests/node_modules_with_symlinks/root/symlink_lib/index.js @@ -0,0 +1 @@ +module.exports = { bar: "bar" }; diff --git a/tests/node_modules_with_symlinks/root/symlink_lib/jsfmt.spec.js b/tests/node_modules_with_symlinks/root/symlink_lib/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_modules_with_symlinks/root/symlink_lib/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_modules_with_symlinks/symlink_lib_outside_root/__snapshots__/jsfmt.spec.js.snap b/tests/node_modules_with_symlinks/symlink_lib_outside_root/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..9ccacc6e6df6 --- /dev/null +++ b/tests/node_modules_with_symlinks/symlink_lib_outside_root/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,7 @@ +exports[`test index.js 1`] = ` +"module.exports = { bar: "bar" }; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +module.exports = { bar: "bar" }; + +" +`; diff --git a/tests/node_modules_with_symlinks/symlink_lib_outside_root/index.js b/tests/node_modules_with_symlinks/symlink_lib_outside_root/index.js new file mode 100644 index 000000000000..206c5837e74d --- /dev/null +++ b/tests/node_modules_with_symlinks/symlink_lib_outside_root/index.js @@ -0,0 +1 @@ +module.exports = { bar: "bar" }; diff --git a/tests/node_modules_with_symlinks/symlink_lib_outside_root/jsfmt.spec.js b/tests/node_modules_with_symlinks/symlink_lib_outside_root/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_modules_with_symlinks/symlink_lib_outside_root/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_modules_without_json/__snapshots__/jsfmt.spec.js.snap b/tests/node_modules_without_json/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..9b9f9ace19db --- /dev/null +++ b/tests/node_modules_without_json/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,10 @@ +exports[`test test.js 1`] = ` +"// @flow + +let foo = require('foo'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +let foo = require("foo"); + +" +`; diff --git a/tests/node_modules_without_json/jsfmt.spec.js b/tests/node_modules_without_json/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_modules_without_json/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_modules_without_json/test.js b/tests/node_modules_without_json/test.js new file mode 100644 index 000000000000..0929d36bbcdb --- /dev/null +++ b/tests/node_modules_without_json/test.js @@ -0,0 +1,3 @@ +// @flow + +let foo = require('foo'); diff --git a/tests/node_tests/assert/__snapshots__/jsfmt.spec.js.snap b/tests/node_tests/assert/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..917ce8adb7d3 --- /dev/null +++ b/tests/node_tests/assert/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,14 @@ +exports[`test assert.js 1`] = ` +"/* @flow */ + +var assert = require("assert") +assert(true) // ok +assert.ok(true) // ok +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var assert = require("assert"); +assert(true);// ok +assert.ok(true);// ok + +" +`; diff --git a/tests/node_tests/assert/assert.js b/tests/node_tests/assert/assert.js new file mode 100644 index 000000000000..cc8f9feeffdd --- /dev/null +++ b/tests/node_tests/assert/assert.js @@ -0,0 +1,5 @@ +/* @flow */ + +var assert = require("assert") +assert(true) // ok +assert.ok(true) // ok diff --git a/tests/node_tests/assert/jsfmt.spec.js b/tests/node_tests/assert/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_tests/assert/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_tests/basic/__snapshots__/jsfmt.spec.js.snap b/tests/node_tests/basic/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..cd7e6db3e4d9 --- /dev/null +++ b/tests/node_tests/basic/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,17 @@ +exports[`test bar.js 1`] = ` +"module.exports = "bar"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +module.exports = "bar"; + +" +`; + +exports[`test foo.js 1`] = ` +"var x = require('./bar.js'); +console.log(x); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var x = require("./bar.js"); +console.log(x); + +" +`; diff --git a/tests/node_tests/basic/bar.js b/tests/node_tests/basic/bar.js new file mode 100644 index 000000000000..a7f281456cc7 --- /dev/null +++ b/tests/node_tests/basic/bar.js @@ -0,0 +1 @@ +module.exports = "bar"; diff --git a/tests/node_tests/basic/foo.js b/tests/node_tests/basic/foo.js new file mode 100644 index 000000000000..cebd25bedbde --- /dev/null +++ b/tests/node_tests/basic/foo.js @@ -0,0 +1,2 @@ +var x = require('./bar.js'); +console.log(x); diff --git a/tests/node_tests/basic/jsfmt.spec.js b/tests/node_tests/basic/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_tests/basic/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_tests/basic_file/__snapshots__/jsfmt.spec.js.snap b/tests/node_tests/basic_file/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..b4a5902fad5e --- /dev/null +++ b/tests/node_tests/basic_file/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,17 @@ +exports[`test bar.js 1`] = ` +"module.exports = "bar"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +module.exports = "bar"; + +" +`; + +exports[`test foo.js 1`] = ` +"var x = require('./bar'); // bar.js does not work +console.log(x); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var x = require("./bar");// bar.js does not work +console.log(x); + +" +`; diff --git a/tests/node_tests/basic_file/bar.js b/tests/node_tests/basic_file/bar.js new file mode 100644 index 000000000000..a7f281456cc7 --- /dev/null +++ b/tests/node_tests/basic_file/bar.js @@ -0,0 +1 @@ +module.exports = "bar"; diff --git a/tests/node_tests/basic_file/foo.js b/tests/node_tests/basic_file/foo.js new file mode 100644 index 000000000000..93cbf0128dbf --- /dev/null +++ b/tests/node_tests/basic_file/foo.js @@ -0,0 +1,2 @@ +var x = require('./bar'); // bar.js does not work +console.log(x); diff --git a/tests/node_tests/basic_file/jsfmt.spec.js b/tests/node_tests/basic_file/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_tests/basic_file/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_tests/basic_node_modules/__snapshots__/jsfmt.spec.js.snap b/tests/node_tests/basic_node_modules/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..677c14aba703 --- /dev/null +++ b/tests/node_tests/basic_node_modules/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,9 @@ +exports[`test foo.js 1`] = ` +"var x = require('bar.js'); +console.log(x); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var x = require("bar.js"); +console.log(x); + +" +`; diff --git a/tests/node_tests/basic_node_modules/foo.js b/tests/node_tests/basic_node_modules/foo.js new file mode 100644 index 000000000000..8f06577c8d27 --- /dev/null +++ b/tests/node_tests/basic_node_modules/foo.js @@ -0,0 +1,2 @@ +var x = require('bar.js'); +console.log(x); diff --git a/tests/node_tests/basic_node_modules/jsfmt.spec.js b/tests/node_tests/basic_node_modules/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_tests/basic_node_modules/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_tests/basic_node_modules_with_path/__snapshots__/jsfmt.spec.js.snap b/tests/node_tests/basic_node_modules_with_path/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..a17a80ce4244 --- /dev/null +++ b/tests/node_tests/basic_node_modules_with_path/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,9 @@ +exports[`test foo.js 1`] = ` +"var x = require('bar_lib/bar'); +console.log(x); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var x = require("bar_lib/bar"); +console.log(x); + +" +`; diff --git a/tests/node_tests/basic_node_modules_with_path/foo.js b/tests/node_tests/basic_node_modules_with_path/foo.js new file mode 100644 index 000000000000..ba511e6ca239 --- /dev/null +++ b/tests/node_tests/basic_node_modules_with_path/foo.js @@ -0,0 +1,2 @@ +var x = require('bar_lib/bar'); +console.log(x); diff --git a/tests/node_tests/basic_node_modules_with_path/jsfmt.spec.js b/tests/node_tests/basic_node_modules_with_path/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_tests/basic_node_modules_with_path/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_tests/basic_package/__snapshots__/jsfmt.spec.js.snap b/tests/node_tests/basic_package/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..02758dca30b4 --- /dev/null +++ b/tests/node_tests/basic_package/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,9 @@ +exports[`test foo.js 1`] = ` +"var x = require('./bar_lib'); // 'bar_lib' does not work! +console.log(x); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var x = require("./bar_lib");// 'bar_lib' does not work! +console.log(x); + +" +`; diff --git a/tests/node_tests/basic_package/bar_lib/__snapshots__/jsfmt.spec.js.snap b/tests/node_tests/basic_package/bar_lib/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..23901fc4ada1 --- /dev/null +++ b/tests/node_tests/basic_package/bar_lib/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,7 @@ +exports[`test bar.js 1`] = ` +"module.exports = "bar"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +module.exports = "bar"; + +" +`; diff --git a/tests/node_tests/basic_package/bar_lib/bar.js b/tests/node_tests/basic_package/bar_lib/bar.js new file mode 100644 index 000000000000..a7f281456cc7 --- /dev/null +++ b/tests/node_tests/basic_package/bar_lib/bar.js @@ -0,0 +1 @@ +module.exports = "bar"; diff --git a/tests/node_tests/basic_package/bar_lib/jsfmt.spec.js b/tests/node_tests/basic_package/bar_lib/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_tests/basic_package/bar_lib/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_tests/basic_package/foo.js b/tests/node_tests/basic_package/foo.js new file mode 100644 index 000000000000..11c0d1d81528 --- /dev/null +++ b/tests/node_tests/basic_package/foo.js @@ -0,0 +1,2 @@ +var x = require('./bar_lib'); // 'bar_lib' does not work! +console.log(x); diff --git a/tests/node_tests/basic_package/jsfmt.spec.js b/tests/node_tests/basic_package/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_tests/basic_package/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_tests/buffer/__snapshots__/jsfmt.spec.js.snap b/tests/node_tests/buffer/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..a5d8d12edbc7 --- /dev/null +++ b/tests/node_tests/buffer/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,76 @@ +exports[`test buffer.js 1`] = ` +"/* @flow */ + +let bool: boolean = false; +let buffer: Buffer = new Buffer(0); +let num: number = 0; +let maybeNum: ?number; + +// Uint8Array properties. All of these should type check ok. +buffer.length; +buffer.buffer; +buffer.byteOffset; +buffer.byteLength; +buffer[1]; + +// A few Uint8Array instance methods. All of these should type check ok. +buffer.copyWithin(0, 0); +buffer.copyWithin(0, 0, 0); + +const it1: Iterator<[number, number]> = buffer.entries(); + +bool = buffer.every((element: number) => false); +bool = buffer.every((element: number) => false, buffer); + +buffer = buffer.fill(1); +buffer = buffer.fill(1, 0, 0); +buffer = buffer.fill(\"a\"); +buffer = buffer.fill(\"a\", 0, 0); +buffer = buffer.fill(\"a\", 0, 0, \"utf8\"); +buffer = buffer.fill(\"a\", \"utf8\"); + +maybeNum = buffer.find((element: number, index: number, array:Uint8Array) => false); +maybeNum = buffer.find((element: number, index: number, array:Uint8Array) => false, buffer); + +num = buffer.findIndex((element: number, index: number, array:Uint8Array) => false); +num = buffer.findIndex((element: number, index: number, array:Uint8Array) => false, buffer); + +buffer.forEach((value: number) => console.log(value), buffer); +buffer.forEach((value: number, index:number, array:Uint8Array) => console.log(value), buffer); + +bool = buffer.includes(3); +bool = buffer.includes(3, 4); + +num = buffer.indexOf(3); +num = buffer.indexOf(3, 4); + +// static methods +buffer = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]); +const typedArray = new Uint8Array([0x34]); +buffer = Buffer.from(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength); +buffer = Buffer.from(new Buffer(0)); +buffer = Buffer.from(\"foo\", \"utf8\"); + +// This call to from() does type check ok, but should not. Unfortunately, Buffer +// extends Uint8Array but gets rid of this signature to .from(). Understandably, +// flow doesn\'t support a subclass hiding a superclass member, so this checks out as +// ok, even though it is not correct. +buffer = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72], (a:number) => a + 1, {}); // should error but doesn\'t +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1237 + throw new Error(\"unprintable type: \" + JSON.stringify(n.type)); + ^ + +Error: unprintable type: \"TupleTypeAnnotation\" + at genericPrintNoParens (/src/printer.js:1237:13) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1497:36) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/node_tests/buffer/buffer.js b/tests/node_tests/buffer/buffer.js new file mode 100644 index 000000000000..9c53197948c6 --- /dev/null +++ b/tests/node_tests/buffer/buffer.js @@ -0,0 +1,57 @@ +/* @flow */ + +let bool: boolean = false; +let buffer: Buffer = new Buffer(0); +let num: number = 0; +let maybeNum: ?number; + +// Uint8Array properties. All of these should type check ok. +buffer.length; +buffer.buffer; +buffer.byteOffset; +buffer.byteLength; +buffer[1]; + +// A few Uint8Array instance methods. All of these should type check ok. +buffer.copyWithin(0, 0); +buffer.copyWithin(0, 0, 0); + +const it1: Iterator<[number, number]> = buffer.entries(); + +bool = buffer.every((element: number) => false); +bool = buffer.every((element: number) => false, buffer); + +buffer = buffer.fill(1); +buffer = buffer.fill(1, 0, 0); +buffer = buffer.fill("a"); +buffer = buffer.fill("a", 0, 0); +buffer = buffer.fill("a", 0, 0, "utf8"); +buffer = buffer.fill("a", "utf8"); + +maybeNum = buffer.find((element: number, index: number, array:Uint8Array) => false); +maybeNum = buffer.find((element: number, index: number, array:Uint8Array) => false, buffer); + +num = buffer.findIndex((element: number, index: number, array:Uint8Array) => false); +num = buffer.findIndex((element: number, index: number, array:Uint8Array) => false, buffer); + +buffer.forEach((value: number) => console.log(value), buffer); +buffer.forEach((value: number, index:number, array:Uint8Array) => console.log(value), buffer); + +bool = buffer.includes(3); +bool = buffer.includes(3, 4); + +num = buffer.indexOf(3); +num = buffer.indexOf(3, 4); + +// static methods +buffer = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]); +const typedArray = new Uint8Array([0x34]); +buffer = Buffer.from(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength); +buffer = Buffer.from(new Buffer(0)); +buffer = Buffer.from("foo", "utf8"); + +// This call to from() does type check ok, but should not. Unfortunately, Buffer +// extends Uint8Array but gets rid of this signature to .from(). Understandably, +// flow doesn't support a subclass hiding a superclass member, so this checks out as +// ok, even though it is not correct. +buffer = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72], (a:number) => a + 1, {}); // should error but doesn't diff --git a/tests/node_tests/buffer/jsfmt.spec.js b/tests/node_tests/buffer/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_tests/buffer/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_tests/child_process/__snapshots__/jsfmt.spec.js.snap b/tests/node_tests/child_process/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..ad8791456df2 --- /dev/null +++ b/tests/node_tests/child_process/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,200 @@ +exports[`test exec.js 1`] = ` +"/* @flow */ + +var exec = require(\'child_process\').exec; + +// callback only. +exec(\'ls\', function(error, stdout, stderr) { + console.info(stdout); +}); + +// options only. +exec(\'ls\', {timeout: 250}); + +// options + callback. +exec(\'ls\', {maxBuffer: 100}, function(error, stdout, stderr) { + console.info(stdout); +}); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var exec = require(\"child_process\").exec; +// callback only. +exec( + \"ls\", + function(error, stdout, stderr) { + console.info(stdout); + } +); +// options only. +exec(\"ls\", { timeout: 250 }); +// options + callback. +exec( + \"ls\", + { maxBuffer: 100 }, + function(error, stdout, stderr) { + console.info(stdout); + } +); + +" +`; + +exports[`test execFile.js 1`] = ` +"/* @flow */ + +var execFile = require(\'child_process\').execFile; + +// args only. +execFile(\'ls\', [\'-lh\']); + +// callback only. +execFile(\'ls\', function(error, stdout, stderr) { + console.info(stdout); +}); + +// options only. +execFile(\'wc\', {timeout: 250}); + +// args + callback. +execFile(\'ls\', [\'-l\'], function(error, stdout, stderr) { + console.info(stdout); +}); + +// args + options. +execFile(\'ls\', [\'-l\'], {timeout: 250}); + +// Put it all together. +execFile(\'ls\', [\'-l\'], {timeout: 250}, function(error, stdout, stderr) { + console.info(stdout); +}); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var execFile = require(\"child_process\").execFile; +// args only. +execFile(\"ls\", [ \"-lh\" ]); +// callback only. +execFile( + \"ls\", + function(error, stdout, stderr) { + console.info(stdout); + } +); +// options only. +execFile(\"wc\", { timeout: 250 }); +// args + callback. +execFile( + \"ls\", + [ \"-l\" ], + function(error, stdout, stderr) { + console.info(stdout); + } +); +// args + options. +execFile(\"ls\", [ \"-l\" ], { timeout: 250 }); +// Put it all together. +execFile( + \"ls\", + [ \"-l\" ], + { timeout: 250 }, + function(error, stdout, stderr) { + console.info(stdout); + } +); + +" +`; + +exports[`test execSync.js 1`] = ` +"/* @flow */ + +var execSync = require(\'child_process\').execSync; + +(execSync(\'ls\'): Buffer); // returns Buffer +(execSync(\'ls\', {encoding: \'buffer\'}): Buffer); // returns Buffer +(execSync(\'ls\', {encoding: \'utf8\'}): string); // returns string +(execSync(\'ls\', {timeout: \'250\'})); // error, no signatures match +(execSync(\'ls\', {stdio: \'inherit\'})); // error, no signatures match +(execSync(\'ls\', {stdio: [\'inherit\']})); // error, no signatures match +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var execSync = require(\"child_process\").execSync; +(execSync(\"ls\"): Buffer);// returns Buffer +(execSync(\"ls\", { encoding: \"buffer\" }): Buffer);// returns Buffer +(execSync(\"ls\", { encoding: \"utf8\" }): string);// returns string +execSync(\"ls\", { timeout: \"250\" });// error, no signatures match +execSync(\"ls\", { stdio: \"inherit\" });// error, no signatures match +execSync(\"ls\", { stdio: [ \"inherit\" ] });// error, no signatures match + +" +`; + +exports[`test spawn.js 1`] = ` +"/* @flow */ + +var child_process = require(\'child_process\'); + +var ls = child_process.spawn(\'ls\'); +var wc = child_process.spawn(\'wc\', [\'-l\']); + +// args + options. +child_process.spawn(\'echo\', [\'-n\', \'\"Testing...\"\'], {env: {TEST: \'foo\'}}); + +// options only. +child_process.spawn(\'echo\', {env: {FOO: 2}}); + +ls.stdout.on(\'data\', function(data) { + wc.stdin.write(data); +}); + +ls.stderr.on(\'data\', function(data) { + console.warn(data); +}); + +ls.on(\'close\', function(code) { + if (code !== 0) { + console.warn(\'\`ls\` exited with code %s\', code); + } + wc.stdin.end(); +}); + +wc.stdout.pipe(process.stdout); +wc.stderr.pipe(process.stderr); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var child_process = require(\"child_process\"); +var ls = child_process.spawn(\"ls\"); +var wc = child_process.spawn(\"wc\", [ \"-l\" ]); +// args + options. +child_process.spawn( + \"echo\", + [ \"-n\", \"\\\"Testing...\\\"\" ], + { env: { TEST: \"foo\" } } +); +// options only. +child_process.spawn(\"echo\", { env: { FOO: 2 } }); +ls.stdout.on( + \"data\", + function(data) { + wc.stdin.write(data); + } +); +ls.stderr.on( + \"data\", + function(data) { + console.warn(data); + } +); +ls.on( + \"close\", + function(code) { + if (code !== 0) { + console.warn(\"\`ls\` exited with code %s\", code); + } + wc.stdin.end(); + } +); +wc.stdout.pipe(process.stdout); +wc.stderr.pipe(process.stderr); + +" +`; diff --git a/tests/node_tests/child_process/exec.js b/tests/node_tests/child_process/exec.js new file mode 100644 index 000000000000..395a75b59946 --- /dev/null +++ b/tests/node_tests/child_process/exec.js @@ -0,0 +1,16 @@ +/* @flow */ + +var exec = require('child_process').exec; + +// callback only. +exec('ls', function(error, stdout, stderr) { + console.info(stdout); +}); + +// options only. +exec('ls', {timeout: 250}); + +// options + callback. +exec('ls', {maxBuffer: 100}, function(error, stdout, stderr) { + console.info(stdout); +}); diff --git a/tests/node_tests/child_process/execFile.js b/tests/node_tests/child_process/execFile.js new file mode 100644 index 000000000000..65963c46413e --- /dev/null +++ b/tests/node_tests/child_process/execFile.js @@ -0,0 +1,27 @@ +/* @flow */ + +var execFile = require('child_process').execFile; + +// args only. +execFile('ls', ['-lh']); + +// callback only. +execFile('ls', function(error, stdout, stderr) { + console.info(stdout); +}); + +// options only. +execFile('wc', {timeout: 250}); + +// args + callback. +execFile('ls', ['-l'], function(error, stdout, stderr) { + console.info(stdout); +}); + +// args + options. +execFile('ls', ['-l'], {timeout: 250}); + +// Put it all together. +execFile('ls', ['-l'], {timeout: 250}, function(error, stdout, stderr) { + console.info(stdout); +}); diff --git a/tests/node_tests/child_process/execSync.js b/tests/node_tests/child_process/execSync.js new file mode 100644 index 000000000000..171fd1748834 --- /dev/null +++ b/tests/node_tests/child_process/execSync.js @@ -0,0 +1,10 @@ +/* @flow */ + +var execSync = require('child_process').execSync; + +(execSync('ls'): Buffer); // returns Buffer +(execSync('ls', {encoding: 'buffer'}): Buffer); // returns Buffer +(execSync('ls', {encoding: 'utf8'}): string); // returns string +(execSync('ls', {timeout: '250'})); // error, no signatures match +(execSync('ls', {stdio: 'inherit'})); // error, no signatures match +(execSync('ls', {stdio: ['inherit']})); // error, no signatures match diff --git a/tests/node_tests/child_process/jsfmt.spec.js b/tests/node_tests/child_process/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_tests/child_process/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_tests/child_process/spawn.js b/tests/node_tests/child_process/spawn.js new file mode 100644 index 000000000000..760c3cf25c9d --- /dev/null +++ b/tests/node_tests/child_process/spawn.js @@ -0,0 +1,30 @@ +/* @flow */ + +var child_process = require('child_process'); + +var ls = child_process.spawn('ls'); +var wc = child_process.spawn('wc', ['-l']); + +// args + options. +child_process.spawn('echo', ['-n', '"Testing..."'], {env: {TEST: 'foo'}}); + +// options only. +child_process.spawn('echo', {env: {FOO: 2}}); + +ls.stdout.on('data', function(data) { + wc.stdin.write(data); +}); + +ls.stderr.on('data', function(data) { + console.warn(data); +}); + +ls.on('close', function(code) { + if (code !== 0) { + console.warn('`ls` exited with code %s', code); + } + wc.stdin.end(); +}); + +wc.stdout.pipe(process.stdout); +wc.stderr.pipe(process.stderr); diff --git a/tests/node_tests/crypto/__snapshots__/jsfmt.spec.js.snap b/tests/node_tests/crypto/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..34bdcbc30cc0 --- /dev/null +++ b/tests/node_tests/crypto/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,57 @@ +exports[`test crypto.js 1`] = ` +"/* @flow */ + +const crypto = require(\'crypto\'); + +let tests = [ + // Hmac is a duplex stream + function() { + const hmac = crypto.createHmac(\'sha256\', \'a secret\'); + + hmac.on(\'readable\', () => { + (hmac.read(): ?(string | Buffer)); + (hmac.read(): number); // 4 errors: null, void, string, Buffer + }); + + hmac.write(\'some data to hash\'); + hmac.write(123); // 2 errors: not a string or a Buffer + hmac.end(); + }, + + // Hmac supports update and digest functions too + function(buf: Buffer) { + const hmac = crypto.createHmac(\'sha256\', \'a secret\'); + + hmac.update(\'some data to hash\'); + hmac.update(\'foo\', \'utf8\'); + hmac.update(\'foo\', \'bogus\'); // 1 error + hmac.update(buf); + hmac.update(buf, \'utf8\'); // 1 error: no encoding when passing a buffer + + // it\'s also chainable + (hmac.update(\'some data to hash\').update(buf).digest(): Buffer); + + (hmac.digest(\'hex\'): string); + (hmac.digest(): Buffer); + (hmac.digest(\'hex\'): void); // 1 error + (hmac.digest(): void); // 1 error + } +] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1412:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/node_tests/crypto/crypto.js b/tests/node_tests/crypto/crypto.js new file mode 100644 index 000000000000..a8544654e399 --- /dev/null +++ b/tests/node_tests/crypto/crypto.js @@ -0,0 +1,38 @@ +/* @flow */ + +const crypto = require('crypto'); + +let tests = [ + // Hmac is a duplex stream + function() { + const hmac = crypto.createHmac('sha256', 'a secret'); + + hmac.on('readable', () => { + (hmac.read(): ?(string | Buffer)); + (hmac.read(): number); // 4 errors: null, void, string, Buffer + }); + + hmac.write('some data to hash'); + hmac.write(123); // 2 errors: not a string or a Buffer + hmac.end(); + }, + + // Hmac supports update and digest functions too + function(buf: Buffer) { + const hmac = crypto.createHmac('sha256', 'a secret'); + + hmac.update('some data to hash'); + hmac.update('foo', 'utf8'); + hmac.update('foo', 'bogus'); // 1 error + hmac.update(buf); + hmac.update(buf, 'utf8'); // 1 error: no encoding when passing a buffer + + // it's also chainable + (hmac.update('some data to hash').update(buf).digest(): Buffer); + + (hmac.digest('hex'): string); + (hmac.digest(): Buffer); + (hmac.digest('hex'): void); // 1 error + (hmac.digest(): void); // 1 error + } +] diff --git a/tests/node_tests/crypto/jsfmt.spec.js b/tests/node_tests/crypto/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_tests/crypto/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_tests/fs/__snapshots__/jsfmt.spec.js.snap b/tests/node_tests/fs/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..76ebd2adc28a --- /dev/null +++ b/tests/node_tests/fs/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,76 @@ +exports[`test fs.js 1`] = ` +"var fs = require("fs"); + +/* readFile */ + +fs.readFile("file.exp", (_, data) => { + (data : Buffer); +}); + +fs.readFile("file.exp", "blah", (_, data) => { + (data : string); +}); + +fs.readFile("file.exp", { encoding: "blah" }, (_, data) => { + (data : string); +}); + +fs.readFile("file.exp", {}, (_, data) => { + (data : Buffer); +}); + +/* readFileSync */ + +(fs.readFileSync("file.exp") : Buffer); +(fs.readFileSync("file.exp") : string); // error + +(fs.readFileSync("file.exp", "blah") : string); +(fs.readFileSync("file.exp", "blah") : Buffer); // error + +(fs.readFileSync("file.exp", { encoding: "blah" }) : string); +(fs.readFileSync("file.exp", { encoding: "blah" }) : Buffer); // error + +(fs.readFileSync("file.exp", {}) : Buffer); +(fs.readFileSync("file.exp", {}) : string); // error +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var fs = require("fs"); +/* readFile */ +fs.readFile( + "file.exp", + (_, data) => { + (data: Buffer); + } +); +fs.readFile( + "file.exp", + "blah", + (_, data) => { + (data: string); + } +); +fs.readFile( + "file.exp", + { encoding: "blah" }, + (_, data) => { + (data: string); + } +); +fs.readFile( + "file.exp", + {}, + (_, data) => { + (data: Buffer); + } +); +/* readFileSync */ +(fs.readFileSync("file.exp"): Buffer); +(fs.readFileSync("file.exp"): string);// error +(fs.readFileSync("file.exp", "blah"): string); +(fs.readFileSync("file.exp", "blah"): Buffer);// error +(fs.readFileSync("file.exp", { encoding: "blah" }): string); +(fs.readFileSync("file.exp", { encoding: "blah" }): Buffer);// error +(fs.readFileSync("file.exp", {}): Buffer); +(fs.readFileSync("file.exp", {}): string);// error + +" +`; diff --git a/tests/node_tests/fs/fs.js b/tests/node_tests/fs/fs.js new file mode 100644 index 000000000000..8a05bd98a384 --- /dev/null +++ b/tests/node_tests/fs/fs.js @@ -0,0 +1,33 @@ +var fs = require("fs"); + +/* readFile */ + +fs.readFile("file.exp", (_, data) => { + (data : Buffer); +}); + +fs.readFile("file.exp", "blah", (_, data) => { + (data : string); +}); + +fs.readFile("file.exp", { encoding: "blah" }, (_, data) => { + (data : string); +}); + +fs.readFile("file.exp", {}, (_, data) => { + (data : Buffer); +}); + +/* readFileSync */ + +(fs.readFileSync("file.exp") : Buffer); +(fs.readFileSync("file.exp") : string); // error + +(fs.readFileSync("file.exp", "blah") : string); +(fs.readFileSync("file.exp", "blah") : Buffer); // error + +(fs.readFileSync("file.exp", { encoding: "blah" }) : string); +(fs.readFileSync("file.exp", { encoding: "blah" }) : Buffer); // error + +(fs.readFileSync("file.exp", {}) : Buffer); +(fs.readFileSync("file.exp", {}) : string); // error diff --git a/tests/node_tests/fs/jsfmt.spec.js b/tests/node_tests/fs/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_tests/fs/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_tests/json_file/__snapshots__/jsfmt.spec.js.snap b/tests/node_tests/json_file/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..5d8a9dd3ad97 --- /dev/null +++ b/tests/node_tests/json_file/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,42 @@ +exports[`test test.js 1`] = ` +"// @flow + +let data = require(\'./package/index.json\'); +(data.foo: void); // error, should be object literal +(data.foo.bar: void); // error, should be boolean +(data.abc: boolean); // error, should be ?string + +let data2 = require(\'./package\'); +(data2.baz: void); // error, should be string + +let data3 = require(\'./package2\'); +(data3.foo: void); // error, should be number (not string! index.js wins) + +let data4 = require(\'./json_array\'); +(data4: Array); +(data4: void); // error, should be Array + +(require(\'./json_string\'): void); // error, should be string +(require(\'./json_number\'): void); // error, should be number +(require(\'./json_true\'): void); // error, should be true +(require(\'./json_false\'): void); // error, should be false +(require(\'./json_null\'): void); // error, should be null +(require(\'./json_negative_number\'): -1); // ok +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/node_tests/json_file/jsfmt.spec.js b/tests/node_tests/json_file/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_tests/json_file/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_tests/json_file/package2/__snapshots__/jsfmt.spec.js.snap b/tests/node_tests/json_file/package2/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..fd6af48c9dd6 --- /dev/null +++ b/tests/node_tests/json_file/package2/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,12 @@ +exports[`test index.js 1`] = ` +"// @flow + +module.exports = { + foo: 123 +}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +module.exports = { foo: 123 }; + +" +`; diff --git a/tests/node_tests/json_file/package2/index.js b/tests/node_tests/json_file/package2/index.js new file mode 100644 index 000000000000..30453d22029a --- /dev/null +++ b/tests/node_tests/json_file/package2/index.js @@ -0,0 +1,5 @@ +// @flow + +module.exports = { + foo: 123 +}; diff --git a/tests/node_tests/json_file/package2/jsfmt.spec.js b/tests/node_tests/json_file/package2/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_tests/json_file/package2/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_tests/json_file/test.js b/tests/node_tests/json_file/test.js new file mode 100644 index 000000000000..c18576cfc6cf --- /dev/null +++ b/tests/node_tests/json_file/test.js @@ -0,0 +1,23 @@ +// @flow + +let data = require('./package/index.json'); +(data.foo: void); // error, should be object literal +(data.foo.bar: void); // error, should be boolean +(data.abc: boolean); // error, should be ?string + +let data2 = require('./package'); +(data2.baz: void); // error, should be string + +let data3 = require('./package2'); +(data3.foo: void); // error, should be number (not string! index.js wins) + +let data4 = require('./json_array'); +(data4: Array); +(data4: void); // error, should be Array + +(require('./json_string'): void); // error, should be string +(require('./json_number'): void); // error, should be number +(require('./json_true'): void); // error, should be true +(require('./json_false'): void); // error, should be false +(require('./json_null'): void); // error, should be null +(require('./json_negative_number'): -1); // ok diff --git a/tests/node_tests/os/__snapshots__/jsfmt.spec.js.snap b/tests/node_tests/os/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..fb845385abea --- /dev/null +++ b/tests/node_tests/os/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,31 @@ +exports[`test userInfo.js 1`] = ` +"/* @flow */ + +var os = require('os'); + +var u1 = os.userInfo(); +(u1.username: string); +(u1.username: Buffer); // error + +var u2 = os.userInfo({encoding: 'utf8'}); +(u2.username: string); +(u2.username: Buffer); // error + +var u3 = os.userInfo({encoding: 'buffer'}); +(u3.username: string); // error +(u3.username: Buffer); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var os = require("os"); +var u1 = os.userInfo(); +(u1.username: string); +(u1.username: Buffer);// error +var u2 = os.userInfo({ encoding: "utf8" }); +(u2.username: string); +(u2.username: Buffer);// error +var u3 = os.userInfo({ encoding: "buffer" }); +(u3.username: string);// error +(u3.username: Buffer); + +" +`; diff --git a/tests/node_tests/os/jsfmt.spec.js b/tests/node_tests/os/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_tests/os/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_tests/os/userInfo.js b/tests/node_tests/os/userInfo.js new file mode 100644 index 000000000000..d2644f249bc6 --- /dev/null +++ b/tests/node_tests/os/userInfo.js @@ -0,0 +1,15 @@ +/* @flow */ + +var os = require('os'); + +var u1 = os.userInfo(); +(u1.username: string); +(u1.username: Buffer); // error + +var u2 = os.userInfo({encoding: 'utf8'}); +(u2.username: string); +(u2.username: Buffer); // error + +var u3 = os.userInfo({encoding: 'buffer'}); +(u3.username: string); // error +(u3.username: Buffer); diff --git a/tests/node_tests/package_file/__snapshots__/jsfmt.spec.js.snap b/tests/node_tests/package_file/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..addfa1957571 --- /dev/null +++ b/tests/node_tests/package_file/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,17 @@ +exports[`test bar_lib.js 1`] = ` +"module.exports = "bar_lib"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +module.exports = "bar_lib"; + +" +`; + +exports[`test foo.js 1`] = ` +"var x: string = require('./bar_lib'); // 'bar_lib' does not work! +console.log(x); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var x: string = require("./bar_lib");// 'bar_lib' does not work! +console.log(x); + +" +`; diff --git a/tests/node_tests/package_file/bar_lib.js b/tests/node_tests/package_file/bar_lib.js new file mode 100644 index 000000000000..411baf0101f2 --- /dev/null +++ b/tests/node_tests/package_file/bar_lib.js @@ -0,0 +1 @@ +module.exports = "bar_lib"; diff --git a/tests/node_tests/package_file/bar_lib/__snapshots__/jsfmt.spec.js.snap b/tests/node_tests/package_file/bar_lib/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..e0181c12d007 --- /dev/null +++ b/tests/node_tests/package_file/bar_lib/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,7 @@ +exports[`test bar.js 1`] = ` +"module.exports = 0; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +module.exports = 0; + +" +`; diff --git a/tests/node_tests/package_file/bar_lib/bar.js b/tests/node_tests/package_file/bar_lib/bar.js new file mode 100644 index 000000000000..d5b2e8bb3efb --- /dev/null +++ b/tests/node_tests/package_file/bar_lib/bar.js @@ -0,0 +1 @@ +module.exports = 0; diff --git a/tests/node_tests/package_file/bar_lib/jsfmt.spec.js b/tests/node_tests/package_file/bar_lib/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_tests/package_file/bar_lib/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_tests/package_file/foo.js b/tests/node_tests/package_file/foo.js new file mode 100644 index 000000000000..24c3a1e4a93e --- /dev/null +++ b/tests/node_tests/package_file/foo.js @@ -0,0 +1,2 @@ +var x: string = require('./bar_lib'); // 'bar_lib' does not work! +console.log(x); diff --git a/tests/node_tests/package_file/jsfmt.spec.js b/tests/node_tests/package_file/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_tests/package_file/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_tests/package_file_node_modules/foo/__snapshots__/jsfmt.spec.js.snap b/tests/node_tests/package_file_node_modules/foo/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..d1860d5e0c67 --- /dev/null +++ b/tests/node_tests/package_file_node_modules/foo/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,9 @@ +exports[`test foo.js 1`] = ` +"var x: string = require('bar_lib'); // 'bar_lib' does not work! +console.log(x); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var x: string = require("bar_lib");// 'bar_lib' does not work! +console.log(x); + +" +`; diff --git a/tests/node_tests/package_file_node_modules/foo/foo.js b/tests/node_tests/package_file_node_modules/foo/foo.js new file mode 100644 index 000000000000..680a16b26253 --- /dev/null +++ b/tests/node_tests/package_file_node_modules/foo/foo.js @@ -0,0 +1,2 @@ +var x: string = require('bar_lib'); // 'bar_lib' does not work! +console.log(x); diff --git a/tests/node_tests/package_file_node_modules/foo/jsfmt.spec.js b/tests/node_tests/package_file_node_modules/foo/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_tests/package_file_node_modules/foo/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_tests/path_node_modules/__snapshots__/jsfmt.spec.js.snap b/tests/node_tests/path_node_modules/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..c35d8d2fd0f5 --- /dev/null +++ b/tests/node_tests/path_node_modules/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,9 @@ +exports[`test foo.js 1`] = ` +"var x = require('bar_lib/src/lib/bar'); +console.log(x); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var x = require("bar_lib/src/lib/bar"); +console.log(x); + +" +`; diff --git a/tests/node_tests/path_node_modules/foo.js b/tests/node_tests/path_node_modules/foo.js new file mode 100644 index 000000000000..ff7947a598eb --- /dev/null +++ b/tests/node_tests/path_node_modules/foo.js @@ -0,0 +1,2 @@ +var x = require('bar_lib/src/lib/bar'); +console.log(x); diff --git a/tests/node_tests/path_node_modules/jsfmt.spec.js b/tests/node_tests/path_node_modules/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_tests/path_node_modules/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_tests/path_node_modules_with_short_main/__snapshots__/jsfmt.spec.js.snap b/tests/node_tests/path_node_modules_with_short_main/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..f63b520200fb --- /dev/null +++ b/tests/node_tests/path_node_modules_with_short_main/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,9 @@ +exports[`test foo.js 1`] = ` +"var x = require('bar_lib'); +console.log(x); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var x = require("bar_lib"); +console.log(x); + +" +`; diff --git a/tests/node_tests/path_node_modules_with_short_main/foo.js b/tests/node_tests/path_node_modules_with_short_main/foo.js new file mode 100644 index 000000000000..bb32aaa21c20 --- /dev/null +++ b/tests/node_tests/path_node_modules_with_short_main/foo.js @@ -0,0 +1,2 @@ +var x = require('bar_lib'); +console.log(x); diff --git a/tests/node_tests/path_node_modules_with_short_main/jsfmt.spec.js b/tests/node_tests/path_node_modules_with_short_main/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_tests/path_node_modules_with_short_main/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_tests/path_node_modules_without_main/__snapshots__/jsfmt.spec.js.snap b/tests/node_tests/path_node_modules_without_main/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..f63b520200fb --- /dev/null +++ b/tests/node_tests/path_node_modules_without_main/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,9 @@ +exports[`test foo.js 1`] = ` +"var x = require('bar_lib'); +console.log(x); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var x = require("bar_lib"); +console.log(x); + +" +`; diff --git a/tests/node_tests/path_node_modules_without_main/foo.js b/tests/node_tests/path_node_modules_without_main/foo.js new file mode 100644 index 000000000000..bb32aaa21c20 --- /dev/null +++ b/tests/node_tests/path_node_modules_without_main/foo.js @@ -0,0 +1,2 @@ +var x = require('bar_lib'); +console.log(x); diff --git a/tests/node_tests/path_node_modules_without_main/jsfmt.spec.js b/tests/node_tests/path_node_modules_without_main/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_tests/path_node_modules_without_main/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_tests/path_package/__snapshots__/jsfmt.spec.js.snap b/tests/node_tests/path_package/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..bfaab3d36a4f --- /dev/null +++ b/tests/node_tests/path_package/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,9 @@ +exports[`test foo.js 1`] = ` +"var x = require('bar_lib/src/lib'); +console.log(x); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var x = require("bar_lib/src/lib"); +console.log(x); + +" +`; diff --git a/tests/node_tests/path_package/foo.js b/tests/node_tests/path_package/foo.js new file mode 100644 index 000000000000..a063eb60223b --- /dev/null +++ b/tests/node_tests/path_package/foo.js @@ -0,0 +1,2 @@ +var x = require('bar_lib/src/lib'); +console.log(x); diff --git a/tests/node_tests/path_package/jsfmt.spec.js b/tests/node_tests/path_package/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_tests/path_package/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_tests/stream/__snapshots__/jsfmt.spec.js.snap b/tests/node_tests/stream/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..3c48fa7d08e2 --- /dev/null +++ b/tests/node_tests/stream/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,84 @@ +exports[`test stream.js 1`] = ` +"/* @flow */ + +var child_process = require('child_process'); +var fs = require('fs'); +var stream = require('stream'); +var ls = child_process.spawn('ls'); + +var data = "foo"; + +ls.stdin.write(data); +ls.stdin.write(data, "utf-8"); +ls.stdin.write(data, () => {}); +ls.stdin.write(data, "utf-8", () => {}); + +ls.stdin.end(); +ls.stdin.end(data); +ls.stdin.end(data, "utf-8"); +ls.stdin.end(data, () => {}); +ls.stdin.end(data, "utf-8", () => {}); + +var ws = fs.createWriteStream('/dev/null'); +ls.stdout.pipe(ws).end(); + +class MyReadStream extends stream.Readable {} +class MyWriteStream extends stream.Writable {} +class MyDuplex extends stream.Duplex {} +class MyTransform extends stream.Duplex {} + +new MyReadStream() + .pipe(new MyDuplex()) + .pipe(new MyTransform()) + .pipe(new MyWriteStream()); + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var child_process = require("child_process"); +var fs = require("fs"); +var stream = require("stream"); +var ls = child_process.spawn("ls"); +var data = "foo"; +ls.stdin.write(data); +ls.stdin.write(data, "utf-8"); +ls.stdin.write( + data, + () => { + + } +); +ls.stdin.write( + data, + "utf-8", + () => { + + } +); +ls.stdin.end(); +ls.stdin.end(data); +ls.stdin.end(data, "utf-8"); +ls.stdin.end( + data, + () => { + + } +); +ls.stdin.end( + data, + "utf-8", + () => { + + } +); +var ws = fs.createWriteStream("/dev/null"); +ls.stdout.pipe(ws).end(); +class MyReadStream extends stream.Readable {} +class MyWriteStream extends stream.Writable {} +class MyDuplex extends stream.Duplex {} +class MyTransform extends stream.Duplex {} +new MyReadStream().pipe(new MyDuplex()).pipe(new MyTransform()).pipe( + new MyWriteStream() +); + +" +`; diff --git a/tests/node_tests/stream/jsfmt.spec.js b/tests/node_tests/stream/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_tests/stream/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_tests/stream/stream.js b/tests/node_tests/stream/stream.js new file mode 100644 index 000000000000..61cd149e3f64 --- /dev/null +++ b/tests/node_tests/stream/stream.js @@ -0,0 +1,33 @@ +/* @flow */ + +var child_process = require('child_process'); +var fs = require('fs'); +var stream = require('stream'); +var ls = child_process.spawn('ls'); + +var data = "foo"; + +ls.stdin.write(data); +ls.stdin.write(data, "utf-8"); +ls.stdin.write(data, () => {}); +ls.stdin.write(data, "utf-8", () => {}); + +ls.stdin.end(); +ls.stdin.end(data); +ls.stdin.end(data, "utf-8"); +ls.stdin.end(data, () => {}); +ls.stdin.end(data, "utf-8", () => {}); + +var ws = fs.createWriteStream('/dev/null'); +ls.stdout.pipe(ws).end(); + +class MyReadStream extends stream.Readable {} +class MyWriteStream extends stream.Writable {} +class MyDuplex extends stream.Duplex {} +class MyTransform extends stream.Duplex {} + +new MyReadStream() + .pipe(new MyDuplex()) + .pipe(new MyTransform()) + .pipe(new MyWriteStream()); + diff --git a/tests/node_tests/timers/__snapshots__/jsfmt.spec.js.snap b/tests/node_tests/timers/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..309a91c9044f --- /dev/null +++ b/tests/node_tests/timers/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,17 @@ +exports[`test timers.js 1`] = ` +"// @flow + +function setImmediateCallback(): number { + return 0; +} + +setImmediate(setImmediateCallback); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +function setImmediateCallback(): number { + return 0; +} +setImmediate(setImmediateCallback); + +" +`; diff --git a/tests/node_tests/timers/jsfmt.spec.js b/tests/node_tests/timers/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_tests/timers/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_tests/timers/timers.js b/tests/node_tests/timers/timers.js new file mode 100644 index 000000000000..28f9462aea1b --- /dev/null +++ b/tests/node_tests/timers/timers.js @@ -0,0 +1,7 @@ +// @flow + +function setImmediateCallback(): number { + return 0; +} + +setImmediate(setImmediateCallback); diff --git a/tests/node_tests/url/__snapshots__/jsfmt.spec.js.snap b/tests/node_tests/url/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..786180dca476 --- /dev/null +++ b/tests/node_tests/url/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,9 @@ +exports[`test url.js 1`] = ` +"const url = require('url'); +url.format(url.parse('https://example.com/foo')); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +const url = require("url"); +url.format(url.parse("https://example.com/foo")); + +" +`; diff --git a/tests/node_tests/url/jsfmt.spec.js b/tests/node_tests/url/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/node_tests/url/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/node_tests/url/url.js b/tests/node_tests/url/url.js new file mode 100644 index 000000000000..45b9072eda4b --- /dev/null +++ b/tests/node_tests/url/url.js @@ -0,0 +1,2 @@ +const url = require('url'); +url.format(url.parse('https://example.com/foo')); diff --git a/tests/nullable/__snapshots__/jsfmt.spec.js.snap b/tests/nullable/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..3beb79eaa81c --- /dev/null +++ b/tests/nullable/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,77 @@ +exports[`test maybe.js 1`] = ` +"// @flow + +// unwrapping nested maybes should work +((\'foo\': ?(?string)): ?string); // ok +((123: ?(?number)): ?string); // error (only num ~> string) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +// unwrapping nested maybes should work +((\"foo\": ??string): ?string);// ok +((123: ??number): ?string);// error (only num ~> string) + +" +`; + +exports[`test nullable.js 1`] = ` +"function foo():string { return null; } + +function bar():?string { return null; } + +function qux(x:string) { } + +function corge(x:number) { } + +var x = bar(); // x: ?string +if (x != null) qux(x); // x: ?string | null +if (x != null) corge(x); // x: ?string | null + +function grault() { x = null; } +if (x != null) { + grault(); qux(x); +} + +var array_of_nullable: Array = [null, 3]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test simple_nullable.js 1`] = ` +"function foo(x:?string) {} +function bar(x:?number) {} +foo(\'hmm\'); +bar(\'hmm\'); + +function fn(data: ?{}) {} +fn({some: \'literal\'}); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +function foo(x: ?string) { + +} +function bar(x: ?number) { + +} +foo(\"hmm\"); +bar(\"hmm\"); +function fn(data: ?{}) { + +} +fn({ some: \"literal\" }); + +" +`; diff --git a/tests/nullable/jsfmt.spec.js b/tests/nullable/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/nullable/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/nullable/maybe.js b/tests/nullable/maybe.js new file mode 100644 index 000000000000..f6bb901ab3ef --- /dev/null +++ b/tests/nullable/maybe.js @@ -0,0 +1,5 @@ +// @flow + +// unwrapping nested maybes should work +(('foo': ?(?string)): ?string); // ok +((123: ?(?number)): ?string); // error (only num ~> string) diff --git a/tests/nullable/nullable.js b/tests/nullable/nullable.js new file mode 100644 index 000000000000..642e8a55b0f1 --- /dev/null +++ b/tests/nullable/nullable.js @@ -0,0 +1,18 @@ +function foo():string { return null; } + +function bar():?string { return null; } + +function qux(x:string) { } + +function corge(x:number) { } + +var x = bar(); // x: ?string +if (x != null) qux(x); // x: ?string | null +if (x != null) corge(x); // x: ?string | null + +function grault() { x = null; } +if (x != null) { + grault(); qux(x); +} + +var array_of_nullable: Array = [null, 3]; diff --git a/tests/nullable/simple_nullable.js b/tests/nullable/simple_nullable.js new file mode 100644 index 000000000000..d537956c14e5 --- /dev/null +++ b/tests/nullable/simple_nullable.js @@ -0,0 +1,7 @@ +function foo(x:?string) {} +function bar(x:?number) {} +foo('hmm'); +bar('hmm'); + +function fn(data: ?{}) {} +fn({some: 'literal'}); diff --git a/tests/number_constants/__snapshots__/jsfmt.spec.js.snap b/tests/number_constants/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..1c8a6bf8971a --- /dev/null +++ b/tests/number_constants/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,29 @@ +exports[`test number_constants.js 1`] = ` +"var a: number = Number.MAX_SAFE_INTEGER; +var b: string = Number.MAX_SAFE_INTEGER; +var c: number = Number.MIN_SAFE_INTEGER; +var d: string = Number.MIN_SAFE_INTEGER; +var e: number = Number.MAX_VALUE; +var f: string = Number.MAX_VALUE; +var g: number = Number.MIN_VALUE; +var h: string = Number.MIN_VALUE; +var i: number = Number.NaN; +var j: string = Number.NaN; +var k: number = Number.EPSILON; +var l: string = Number.EPSILON; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var a: number = Number.MAX_SAFE_INTEGER; +var b: string = Number.MAX_SAFE_INTEGER; +var c: number = Number.MIN_SAFE_INTEGER; +var d: string = Number.MIN_SAFE_INTEGER; +var e: number = Number.MAX_VALUE; +var f: string = Number.MAX_VALUE; +var g: number = Number.MIN_VALUE; +var h: string = Number.MIN_VALUE; +var i: number = Number.NaN; +var j: string = Number.NaN; +var k: number = Number.EPSILON; +var l: string = Number.EPSILON; + +" +`; diff --git a/tests/number_constants/jsfmt.spec.js b/tests/number_constants/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/number_constants/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/number_constants/number_constants.js b/tests/number_constants/number_constants.js new file mode 100644 index 000000000000..51f89d21e00f --- /dev/null +++ b/tests/number_constants/number_constants.js @@ -0,0 +1,12 @@ +var a: number = Number.MAX_SAFE_INTEGER; +var b: string = Number.MAX_SAFE_INTEGER; +var c: number = Number.MIN_SAFE_INTEGER; +var d: string = Number.MIN_SAFE_INTEGER; +var e: number = Number.MAX_VALUE; +var f: string = Number.MAX_VALUE; +var g: number = Number.MIN_VALUE; +var h: string = Number.MIN_VALUE; +var i: number = Number.NaN; +var j: string = Number.NaN; +var k: number = Number.EPSILON; +var l: string = Number.EPSILON; diff --git a/tests/object-method/__snapshots__/jsfmt.spec.js.snap b/tests/object-method/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..96b4ef5f3b1e --- /dev/null +++ b/tests/object-method/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,150 @@ +exports[`test id.js 1`] = ` +"declare function id(_: X): X; + +module.exports = id; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test subtype.js 1`] = ` +"interface Interface { + m(): void; +} +import type { ObjectType } from \'./test\'; + +function subtypeCheck(x: Interface): ObjectType { return x; } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1391 + fromString(\", \").join(path.map(print, \"extends\")) + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1391:28) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:238:16 + at FastPath.call (/src/fast-path.js:113:16) +" +`; + +exports[`test test.js 1`] = ` +"const id = require(\'./id\'); + +export type ObjectType = { + +m: () => void, +}; + +function methodCaller(x: ObjectType) { + x.m(); +}; + +module.exports = id( + methodCaller +); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test2.js 1`] = ` +"/* @flow */ + +function f() { + return this.p; +} + +var a = { + p: 0, + f +} + +var b = { + f +} + +a.f(); // okey-dokie +b.f(); // error, property \`p\` not found +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +function f() { + return this.p; +} +var a = { p: 0, f }; +var b = { f }; +a.f();// okey-dokie +b.f();// error, property \`p\` not found + +" +`; + +exports[`test test3.js 1`] = ` +"/* @flow */ + +function foo() { + this.m(); +} + +function bar(f: () => void) { + f(); // passing global object as \`this\` + ({ f }).f(); // passing container object as \`this\` +} + +bar(foo); // error, since \`this\` is used non-trivially in \`foo\` + +function qux(o: { f: () => void }) { + o.f(); // passing o as \`this\` +} + +qux({ f: foo }); // error, since \`this\` is used non-trivially in \`foo\` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/object-method/id.js b/tests/object-method/id.js new file mode 100644 index 000000000000..73650e97515d --- /dev/null +++ b/tests/object-method/id.js @@ -0,0 +1,3 @@ +declare function id(_: X): X; + +module.exports = id; diff --git a/tests/object-method/jsfmt.spec.js b/tests/object-method/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/object-method/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/object-method/subtype.js b/tests/object-method/subtype.js new file mode 100644 index 000000000000..810b2c8d0ffe --- /dev/null +++ b/tests/object-method/subtype.js @@ -0,0 +1,6 @@ +interface Interface { + m(): void; +} +import type { ObjectType } from './test'; + +function subtypeCheck(x: Interface): ObjectType { return x; } diff --git a/tests/object-method/test.js b/tests/object-method/test.js new file mode 100644 index 000000000000..033b1abf3229 --- /dev/null +++ b/tests/object-method/test.js @@ -0,0 +1,13 @@ +const id = require('./id'); + +export type ObjectType = { + +m: () => void, +}; + +function methodCaller(x: ObjectType) { + x.m(); +}; + +module.exports = id( + methodCaller +); diff --git a/tests/object-method/test2.js b/tests/object-method/test2.js new file mode 100644 index 000000000000..652c93c8735f --- /dev/null +++ b/tests/object-method/test2.js @@ -0,0 +1,17 @@ +/* @flow */ + +function f() { + return this.p; +} + +var a = { + p: 0, + f +} + +var b = { + f +} + +a.f(); // okey-dokie +b.f(); // error, property `p` not found diff --git a/tests/object-method/test3.js b/tests/object-method/test3.js new file mode 100644 index 000000000000..365e8e460805 --- /dev/null +++ b/tests/object-method/test3.js @@ -0,0 +1,18 @@ +/* @flow */ + +function foo() { + this.m(); +} + +function bar(f: () => void) { + f(); // passing global object as `this` + ({ f }).f(); // passing container object as `this` +} + +bar(foo); // error, since `this` is used non-trivially in `foo` + +function qux(o: { f: () => void }) { + o.f(); // passing o as `this` +} + +qux({ f: foo }); // error, since `this` is used non-trivially in `foo` diff --git a/tests/object_annot/__snapshots__/jsfmt.spec.js.snap b/tests/object_annot/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..83a6c2b5f013 --- /dev/null +++ b/tests/object_annot/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,27 @@ +exports[`test test.js 1`] = ` +"function foo(x: Array): Array { + return x.sort((a, b) => a.foo - b.foo); +} + +// Make sure Object works with Object.keys() +function bar(x: Object): Array { + return Object.keys(x); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/object_annot/jsfmt.spec.js b/tests/object_annot/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/object_annot/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/object_annot/test.js b/tests/object_annot/test.js new file mode 100644 index 000000000000..88565b11935c --- /dev/null +++ b/tests/object_annot/test.js @@ -0,0 +1,8 @@ +function foo(x: Array): Array { + return x.sort((a, b) => a.foo - b.foo); +} + +// Make sure Object works with Object.keys() +function bar(x: Object): Array { + return Object.keys(x); +} diff --git a/tests/object_api/__snapshots__/jsfmt.spec.js.snap b/tests/object_api/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..53a9dd7b2de1 --- /dev/null +++ b/tests/object_api/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,401 @@ +exports[`test a.js 1`] = ` +"/* @flow */ + +module.exports = { a() {} };~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +module.exports = { + a() { + + } +}; + +" +`; + +exports[`test b.js 1`] = ` +"/* @flow */ + +var a = require(\'./a\'); +var b = Object.assign({ bar() {}, ...{} }, a); +b.a(); // works here +module.exports = b; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var a = require(\"./a\"); +var b = Object.assign( + { + bar() { + + }, + ...{} + }, + a +); +b.a();// works here +module.exports = b; + +" +`; + +exports[`test c.js 1`] = ` +"/* @flow */ + +var c = require(\'./b\'); +c.a(); +c.foo();~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var c = require(\"./b\"); +c.a(); +c.foo(); + +" +`; + +exports[`test object_assign.js 1`] = ` +"/* @flow */ + +var export_ = Object.assign({}, { + foo: function(param) { return param; } +}); + +var decl_export_: { foo: any; bar: any } = Object.assign({}, export_); + +let anyObj: Object = {}; +Object.assign(anyObj, anyObj); // makes sure this terminates + +module.exports = export_; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var export_ = Object.assign( + {}, + { + foo: function(param) { + return param; + } + } +); +var decl_export_: { foo: any, bar: any } = Object.assign({}, export_); +let anyObj: Object = {}; +Object.assign(anyObj, anyObj);// makes sure this terminates +module.exports = export_; + +" +`; + +exports[`test object_create.js 1`] = ` +"/* @flow */ + +class C { foo: string; } + +// OK, \`instanceof C\` would be true +(Object.create(C.prototype): C); + +// OK, \`instanceof C\` would be true +(Object.create(new C): C); + +// error, object literals don\'t structurally match instances +({ foo: \"foo\" }: C); + +// error, object types don\'t structurally match instances +type O = { foo: string; } +declare var o: O; +(o: C); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +class C { + foo: string; +} +// OK, \`instanceof C\` would be true +(Object.create(C.prototype): C); +// OK, \`instanceof C\` would be true +(Object.create(new C()): C); +// error, object literals don\'t structurally match instances +({ foo: \"foo\" }: C); +// error, object types don\'t structurally match instances +type O = { foo: string }; +declare var o: O; +(o: C); + +" +`; + +exports[`test object_getprototypeof.js 1`] = ` +"// @flow + +class Foo {} +class Bar extends Foo {} + +let tests = [ + function() { + const x = new Bar(); + (Object.getPrototypeOf(x): Foo); + }, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +class Foo {} +class Bar extends Foo {} +let tests = [ + function() { + const x = new Bar(); + (Object.getPrototypeOf(x): Foo); + } +]; + +" +`; + +exports[`test object_keys.js 1`] = ` +"/* @flow */ + +var sealed = {one: \'one\', two: \'two\'}; +(Object.keys(sealed): Array<\'one\'|\'two\'>); +(Object.keys(sealed): void); // error, Array + +var unsealed = {}; +Object.keys(unsealed).forEach(k => { + (k : number) // error: number ~> string +}); + +var dict: { [k: number]: string } = {}; +Object.keys(dict).forEach(k => { + (k : number) // error: number ~> string +}); + +var any: Object = {}; +(Object.keys(any): Array); // error, Array + +class Foo { + prop: string; + foo() {} +} +// constructor and foo not enumerable +(Object.keys(new Foo()): Array<\'error\'>); // error: prop ~> error + +class Bar extends Foo { + bar_prop: string; + bar() {} +} +// only own enumerable props +(Object.keys(new Bar()): Array<\'error\'>); // error: bar_prop ~> error +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1497:36) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test object_missing.js 1`] = ` +"// @flow + +let tests = [ + function() { + Object.doesNotExist(); + }, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +let tests = [ + function() { + Object.doesNotExist(); + } +]; + +" +`; + +exports[`test object_prototype.js 1`] = ` +"/* @flow */ + +function takesABool(x: boolean) {} +function takesAString(x: string) {} +function takesANumber(x: number) {} +function takesAnObject(x: Object) {} + +class Foo {} + +var a = { foo: \'bar\' }; +var b = { foo: \'bar\', ...{}}; +var c = { foo: \'bar\', toString: function(): number { return 123; }}; +var d : { [key: string]: string } = { foo: \'bar\' }; +var x = new Date(); +var y = new Foo(); + +// +// toString +// + +// call +takesAString(a.toString()); +d.toString(); // ok, even though dict specifies strings, this is a function + +// get +var aToString : () => string = a.toString; +var aToString2 = a.toString; +takesAString(aToString2()); + +// set +b.toString = function(): string { return \'foo\'; }; +c.toString = function(): number { return 123; }; + +// override +var cToString : () => number = c.toString; + +// ... on a built-in instance +var xToString : number = x.toString; // error +var xToString2 : () => number = x.toString; // error +takesAString(x.toString()); + +// ... on an instance +var yToString : number = y.toString; // error +takesAString(y.toString()); + +// ... on a primitive +(123).toString(); +(123).toString; +(123).toString = function() {}; // error +(123).toString(2); +(123).toString(\'foo\'); // error +(123).toString(null); // error + + +// +// hasOwnProperty +// + +// call +takesABool(a.hasOwnProperty(\'foo\')); + +// get +var aHasOwnProperty : (prop: string) => boolean = a.hasOwnProperty; +var aHasOwnProperty2 = a.hasOwnProperty; +takesABool(aHasOwnProperty2(\'bar\')); + +// set +b.hasOwnProperty = function() { return false; }; + +// ... on a built-in instance +var xHasOwnProperty : number = x.hasOwnProperty; // error +var xHasOwnProperty2 : (prop: string) => number = x.hasOwnProperty; // error +takesABool(x.hasOwnProperty(\'foo\')); + +// ... on an instance +var yHasOwnProperty : number = y.hasOwnProperty; // error +takesABool(y.hasOwnProperty(\'foo\')); + + +// +// propertyIsEnumerable +// + +// call +takesABool(a.propertyIsEnumerable(\'foo\')); + +// get +var aPropertyIsEnumerable : (prop: string) => boolean = a.propertyIsEnumerable; +var aPropertyIsEnumerable2 = a.propertyIsEnumerable; +takesABool(aPropertyIsEnumerable2(\'bar\')); + +// set +b.propertyIsEnumerable = function() { return false; }; + +// ... on a built-in instance +var xPropertyIsEnumerable : number = x.propertyIsEnumerable; // error +var xPropertyIsEnumerable2 : (prop: string) => number = + x.propertyIsEnumerable; // error +takesABool(x.propertyIsEnumerable(\'foo\')); + +// ... on an instance +var yPropertyIsEnumerable : number = y.propertyIsEnumerable; // error +takesABool(y.propertyIsEnumerable(\'foo\')); + + +// +// valueOf +// + +// call +takesAnObject(a.valueOf()); + +// get +var aValueOf : () => Object = a.valueOf; +var aValueOf2 = a.valueOf; +takesAnObject(aValueOf2()); + +// set +b.valueOf = function() { return {}; }; + +// ... on a built-in instance +var xValueOf : number = x.valueOf; // error +takesANumber(x.valueOf()); + +// ... on an instance +var yValueOf : number = y.valueOf; // error +takesAnObject(y.valueOf()); + +// ... on a literal +var strValueOf : string = (\"foo\").valueOf(); +var numValueOf : number = (123).valueOf(); +var boolValueOf : boolean = (true).valueOf(); + +// +// toLocaleString +// + +// call +takesAString(a.toLocaleString()); + +// get +var aToLocaleString : () => string = a.toLocaleString; +var aToLocaleString2 = a.toLocaleString; +takesAString(aToLocaleString2()); + +// set +b.toLocaleString = function() { return \'derp\'; }; + +// ... on a built-in instance +var xToLocaleString : number = x.toLocaleString; // error +var xToLocaleString2 : () => number = x.toLocaleString; // error +takesAString(x.toLocaleString()); + +// ... on an instance +var yToLocaleString : number = y.toLocaleString; // error +takesAString(y.toLocaleString()); + + +// +// constructor +// + +var k : Object = a.constructor; +(123).constructor; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/object_api/a.js b/tests/object_api/a.js new file mode 100644 index 000000000000..934ca8eb8e02 --- /dev/null +++ b/tests/object_api/a.js @@ -0,0 +1,3 @@ +/* @flow */ + +module.exports = { a() {} }; \ No newline at end of file diff --git a/tests/object_api/b.js b/tests/object_api/b.js new file mode 100644 index 000000000000..8b324fb26c73 --- /dev/null +++ b/tests/object_api/b.js @@ -0,0 +1,6 @@ +/* @flow */ + +var a = require('./a'); +var b = Object.assign({ bar() {}, ...{} }, a); +b.a(); // works here +module.exports = b; diff --git a/tests/object_api/c.js b/tests/object_api/c.js new file mode 100644 index 000000000000..69a6c4b46ea1 --- /dev/null +++ b/tests/object_api/c.js @@ -0,0 +1,5 @@ +/* @flow */ + +var c = require('./b'); +c.a(); +c.foo(); \ No newline at end of file diff --git a/tests/object_api/jsfmt.spec.js b/tests/object_api/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/object_api/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/object_api/object_assign.js b/tests/object_api/object_assign.js new file mode 100644 index 000000000000..16e86b66e88c --- /dev/null +++ b/tests/object_api/object_assign.js @@ -0,0 +1,12 @@ +/* @flow */ + +var export_ = Object.assign({}, { + foo: function(param) { return param; } +}); + +var decl_export_: { foo: any; bar: any } = Object.assign({}, export_); + +let anyObj: Object = {}; +Object.assign(anyObj, anyObj); // makes sure this terminates + +module.exports = export_; diff --git a/tests/object_api/object_create.js b/tests/object_api/object_create.js new file mode 100644 index 000000000000..b73c200590db --- /dev/null +++ b/tests/object_api/object_create.js @@ -0,0 +1,17 @@ +/* @flow */ + +class C { foo: string; } + +// OK, `instanceof C` would be true +(Object.create(C.prototype): C); + +// OK, `instanceof C` would be true +(Object.create(new C): C); + +// error, object literals don't structurally match instances +({ foo: "foo" }: C); + +// error, object types don't structurally match instances +type O = { foo: string; } +declare var o: O; +(o: C); diff --git a/tests/object_api/object_getprototypeof.js b/tests/object_api/object_getprototypeof.js new file mode 100644 index 000000000000..74d5355376f2 --- /dev/null +++ b/tests/object_api/object_getprototypeof.js @@ -0,0 +1,11 @@ +// @flow + +class Foo {} +class Bar extends Foo {} + +let tests = [ + function() { + const x = new Bar(); + (Object.getPrototypeOf(x): Foo); + }, +]; diff --git a/tests/object_api/object_keys.js b/tests/object_api/object_keys.js new file mode 100644 index 000000000000..2af5c7fc21d6 --- /dev/null +++ b/tests/object_api/object_keys.js @@ -0,0 +1,32 @@ +/* @flow */ + +var sealed = {one: 'one', two: 'two'}; +(Object.keys(sealed): Array<'one'|'two'>); +(Object.keys(sealed): void); // error, Array + +var unsealed = {}; +Object.keys(unsealed).forEach(k => { + (k : number) // error: number ~> string +}); + +var dict: { [k: number]: string } = {}; +Object.keys(dict).forEach(k => { + (k : number) // error: number ~> string +}); + +var any: Object = {}; +(Object.keys(any): Array); // error, Array + +class Foo { + prop: string; + foo() {} +} +// constructor and foo not enumerable +(Object.keys(new Foo()): Array<'error'>); // error: prop ~> error + +class Bar extends Foo { + bar_prop: string; + bar() {} +} +// only own enumerable props +(Object.keys(new Bar()): Array<'error'>); // error: bar_prop ~> error diff --git a/tests/object_api/object_missing.js b/tests/object_api/object_missing.js new file mode 100644 index 000000000000..8c33c2d94029 --- /dev/null +++ b/tests/object_api/object_missing.js @@ -0,0 +1,7 @@ +// @flow + +let tests = [ + function() { + Object.doesNotExist(); + }, +]; diff --git a/tests/object_api/object_prototype.js b/tests/object_api/object_prototype.js new file mode 100644 index 000000000000..233028a7e6c0 --- /dev/null +++ b/tests/object_api/object_prototype.js @@ -0,0 +1,164 @@ +/* @flow */ + +function takesABool(x: boolean) {} +function takesAString(x: string) {} +function takesANumber(x: number) {} +function takesAnObject(x: Object) {} + +class Foo {} + +var a = { foo: 'bar' }; +var b = { foo: 'bar', ...{}}; +var c = { foo: 'bar', toString: function(): number { return 123; }}; +var d : { [key: string]: string } = { foo: 'bar' }; +var x = new Date(); +var y = new Foo(); + +// +// toString +// + +// call +takesAString(a.toString()); +d.toString(); // ok, even though dict specifies strings, this is a function + +// get +var aToString : () => string = a.toString; +var aToString2 = a.toString; +takesAString(aToString2()); + +// set +b.toString = function(): string { return 'foo'; }; +c.toString = function(): number { return 123; }; + +// override +var cToString : () => number = c.toString; + +// ... on a built-in instance +var xToString : number = x.toString; // error +var xToString2 : () => number = x.toString; // error +takesAString(x.toString()); + +// ... on an instance +var yToString : number = y.toString; // error +takesAString(y.toString()); + +// ... on a primitive +(123).toString(); +(123).toString; +(123).toString = function() {}; // error +(123).toString(2); +(123).toString('foo'); // error +(123).toString(null); // error + + +// +// hasOwnProperty +// + +// call +takesABool(a.hasOwnProperty('foo')); + +// get +var aHasOwnProperty : (prop: string) => boolean = a.hasOwnProperty; +var aHasOwnProperty2 = a.hasOwnProperty; +takesABool(aHasOwnProperty2('bar')); + +// set +b.hasOwnProperty = function() { return false; }; + +// ... on a built-in instance +var xHasOwnProperty : number = x.hasOwnProperty; // error +var xHasOwnProperty2 : (prop: string) => number = x.hasOwnProperty; // error +takesABool(x.hasOwnProperty('foo')); + +// ... on an instance +var yHasOwnProperty : number = y.hasOwnProperty; // error +takesABool(y.hasOwnProperty('foo')); + + +// +// propertyIsEnumerable +// + +// call +takesABool(a.propertyIsEnumerable('foo')); + +// get +var aPropertyIsEnumerable : (prop: string) => boolean = a.propertyIsEnumerable; +var aPropertyIsEnumerable2 = a.propertyIsEnumerable; +takesABool(aPropertyIsEnumerable2('bar')); + +// set +b.propertyIsEnumerable = function() { return false; }; + +// ... on a built-in instance +var xPropertyIsEnumerable : number = x.propertyIsEnumerable; // error +var xPropertyIsEnumerable2 : (prop: string) => number = + x.propertyIsEnumerable; // error +takesABool(x.propertyIsEnumerable('foo')); + +// ... on an instance +var yPropertyIsEnumerable : number = y.propertyIsEnumerable; // error +takesABool(y.propertyIsEnumerable('foo')); + + +// +// valueOf +// + +// call +takesAnObject(a.valueOf()); + +// get +var aValueOf : () => Object = a.valueOf; +var aValueOf2 = a.valueOf; +takesAnObject(aValueOf2()); + +// set +b.valueOf = function() { return {}; }; + +// ... on a built-in instance +var xValueOf : number = x.valueOf; // error +takesANumber(x.valueOf()); + +// ... on an instance +var yValueOf : number = y.valueOf; // error +takesAnObject(y.valueOf()); + +// ... on a literal +var strValueOf : string = ("foo").valueOf(); +var numValueOf : number = (123).valueOf(); +var boolValueOf : boolean = (true).valueOf(); + +// +// toLocaleString +// + +// call +takesAString(a.toLocaleString()); + +// get +var aToLocaleString : () => string = a.toLocaleString; +var aToLocaleString2 = a.toLocaleString; +takesAString(aToLocaleString2()); + +// set +b.toLocaleString = function() { return 'derp'; }; + +// ... on a built-in instance +var xToLocaleString : number = x.toLocaleString; // error +var xToLocaleString2 : () => number = x.toLocaleString; // error +takesAString(x.toLocaleString()); + +// ... on an instance +var yToLocaleString : number = y.toLocaleString; // error +takesAString(y.toLocaleString()); + + +// +// constructor +// + +var k : Object = a.constructor; +(123).constructor; diff --git a/tests/object_assign/A.js b/tests/object_assign/A.js new file mode 100644 index 000000000000..8a7fa79901e7 --- /dev/null +++ b/tests/object_assign/A.js @@ -0,0 +1,26 @@ +/** + * @flow + */ + +var EventEmitter = require('events').EventEmitter; + +// This pattern seems to cause the trouble. +var Bad = Object.assign({}, EventEmitter.prototype, { + foo: function(): string { return 'hi'; } +}); + +// Calling Bad.foo() in the same file doesn't error +var bad: number = Bad.foo(); + +// Doesn't repro if I extend the class myself +class MyEventEmitter extends events$EventEmitter {} +var Good = Object.assign({}, MyEventEmitter.prototype, { + foo: function(): string { return 'hi'; } +}); +// Calling Good.foo() in the same file doesn't error +var good: number = Good.foo(); + +module.exports = { + Bad: Bad, + Good: Good, +}; diff --git a/tests/object_assign/B.js b/tests/object_assign/B.js new file mode 100644 index 000000000000..40278d660844 --- /dev/null +++ b/tests/object_assign/B.js @@ -0,0 +1,17 @@ +/** + * @flow + */ + +var A = require('./A.js'); + +var good: number = A.Good.foo(); + +var f = A.Bad.foo; // Property access is fine +var bad_: number = f(); // Calling the function is fine + +var bad: number = A.Bad.foo(); // Method call is not fine +/* +B.js|12 col 1 error| call of method foo +|| Property not found in +A.js|8 col 23 error| object literal +*/ diff --git a/tests/object_assign/__snapshots__/jsfmt.spec.js.snap b/tests/object_assign/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..47c18dc07b7e --- /dev/null +++ b/tests/object_assign/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,166 @@ +exports[`test A.js 1`] = ` +"/** + * @flow + */ + +var EventEmitter = require('events').EventEmitter; + +// This pattern seems to cause the trouble. +var Bad = Object.assign({}, EventEmitter.prototype, { + foo: function(): string { return 'hi'; } +}); + +// Calling Bad.foo() in the same file doesn't error +var bad: number = Bad.foo(); + +// Doesn't repro if I extend the class myself +class MyEventEmitter extends events$EventEmitter {} +var Good = Object.assign({}, MyEventEmitter.prototype, { + foo: function(): string { return 'hi'; } +}); +// Calling Good.foo() in the same file doesn't error +var good: number = Good.foo(); + +module.exports = { + Bad: Bad, + Good: Good, +}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +var EventEmitter = require("events").EventEmitter; +// This pattern seems to cause the trouble. +var Bad = Object.assign( + {}, + EventEmitter.prototype, + { + foo: function(): string { + return "hi"; + } + } +); +// Calling Bad.foo() in the same file doesn't error +var bad: number = Bad.foo(); +// Doesn't repro if I extend the class myself +class MyEventEmitter extends events$EventEmitter {} +var Good = Object.assign( + {}, + MyEventEmitter.prototype, + { + foo: function(): string { + return "hi"; + } + } +); +// Calling Good.foo() in the same file doesn't error +var good: number = Good.foo(); +module.exports = { Bad: Bad, Good: Good }; + +" +`; + +exports[`test B.js 1`] = ` +"/** + * @flow + */ + +var A = require('./A.js'); + +var good: number = A.Good.foo(); + +var f = A.Bad.foo; // Property access is fine +var bad_: number = f(); // Calling the function is fine + +var bad: number = A.Bad.foo(); // Method call is not fine +/* +B.js|12 col 1 error| call of method foo +|| Property not found in +A.js|8 col 23 error| object literal +*/ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +var A = require("./A.js"); +var good: number = A.Good.foo(); +var f = A.Bad.foo;// Property access is fine +var bad_: number = f();// Calling the function is fine +var bad: number = A.Bad.foo();// Method call is not fine/* +B.js|12 col 1 error| call of method foo +|| Property not found in +A.js|8 col 23 error| object literal +*/ + +" +`; + +exports[`test apply.js 1`] = ` +"// @flow + +(Object.assign.apply(null, [{}, {a: 1}, {b: 'foo'}]): {a: number, b: string}); +(Object.assign({}, ...[{a: 1}, {b: 'foo'}]): {a: number, b: string}); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +(Object.assign.apply(null, [ {}, { a: 1 }, { b: "foo" } ]): { + a: number, + b: string +}); +(Object.assign({}, ...[ { a: 1 }, { b: "foo" } ]): { a: number, b: string }); + +" +`; + +exports[`test non_objects.js 1`] = ` +"/* @flow */ + +Object.assign("123", {a: "foo"}); +Object.assign(123, {a: "foo"}); +Object.assign({a: "foo"}, 123); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +Object.assign("123", { a: "foo" }); +Object.assign(123, { a: "foo" }); +Object.assign({ a: "foo" }, 123); + +" +`; + +exports[`test undefined.js 1`] = ` +"/* @flow */ + +var React = require('react'); + +type DefaultProps = { + foo: number, +} + +type Props = { + foo: number, +} + +class MyReactThing extends React.Component { + props: Props; + static defaultProps: DefaultProps; + getFoo(): number { return this.props.foo; } +} + +; // works +; // also works +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var React = require("react"); +type DefaultProps = { foo: number }; +type Props = { foo: number }; +class MyReactThing extends React.Component { + props: Props; + static defaultProps: DefaultProps; + getFoo(): number { + return this.props.foo; + } +} +;// works +;// also works + +" +`; diff --git a/tests/object_assign/apply.js b/tests/object_assign/apply.js new file mode 100644 index 000000000000..6bb4c551eab8 --- /dev/null +++ b/tests/object_assign/apply.js @@ -0,0 +1,4 @@ +// @flow + +(Object.assign.apply(null, [{}, {a: 1}, {b: 'foo'}]): {a: number, b: string}); +(Object.assign({}, ...[{a: 1}, {b: 'foo'}]): {a: number, b: string}); diff --git a/tests/object_assign/jsfmt.spec.js b/tests/object_assign/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/object_assign/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/object_assign/non_objects.js b/tests/object_assign/non_objects.js new file mode 100644 index 000000000000..8cb9b564d6ee --- /dev/null +++ b/tests/object_assign/non_objects.js @@ -0,0 +1,5 @@ +/* @flow */ + +Object.assign("123", {a: "foo"}); +Object.assign(123, {a: "foo"}); +Object.assign({a: "foo"}, 123); diff --git a/tests/object_assign/undefined.js b/tests/object_assign/undefined.js new file mode 100644 index 000000000000..27b1d0b9f44d --- /dev/null +++ b/tests/object_assign/undefined.js @@ -0,0 +1,20 @@ +/* @flow */ + +var React = require('react'); + +type DefaultProps = { + foo: number, +} + +type Props = { + foo: number, +} + +class MyReactThing extends React.Component { + props: Props; + static defaultProps: DefaultProps; + getFoo(): number { return this.props.foo; } +} + +; // works +; // also works diff --git a/tests/object_freeze/__snapshots__/jsfmt.spec.js.snap b/tests/object_freeze/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..2eed145c9e9b --- /dev/null +++ b/tests/object_freeze/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,41 @@ +exports[`test object_freeze.js 1`] = ` +"/* @flow */ + +var foo = Object.freeze({bar: '12345'}); +foo.bar = '23456'; // error + +Object.assign(foo, {bar: '12345'}); // error + +var baz = {baz: 12345}; +var bliffl = Object.freeze({bar: '12345', ...baz}); +bliffl.bar = '23456'; // error +bliffl.baz = 3456; // error +bliffl.corge; // error +bliffl.constructor = baz; // error +bliffl.toString = function() {}; // error + +baz.baz = 0; + +var x : number = Object.freeze(123); + +var xx : { x: number } = Object.freeze({ x: "error" }) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var foo = Object.freeze({ bar: "12345" }); +foo.bar = "23456";// error +Object.assign(foo, { bar: "12345" });// error +var baz = { baz: 12345 }; +var bliffl = Object.freeze({ bar: "12345", ...baz }); +bliffl.bar = "23456";// error +bliffl.baz = 3456;// error +bliffl.corge;// error +bliffl.constructor = baz;// error +bliffl.toString = function() { + +};// error +baz.baz = 0; +var x: number = Object.freeze(123); +var xx: { x: number } = Object.freeze({ x: "error" }); + +" +`; diff --git a/tests/object_freeze/jsfmt.spec.js b/tests/object_freeze/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/object_freeze/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/object_freeze/object_freeze.js b/tests/object_freeze/object_freeze.js new file mode 100644 index 000000000000..cc192b2115aa --- /dev/null +++ b/tests/object_freeze/object_freeze.js @@ -0,0 +1,20 @@ +/* @flow */ + +var foo = Object.freeze({bar: '12345'}); +foo.bar = '23456'; // error + +Object.assign(foo, {bar: '12345'}); // error + +var baz = {baz: 12345}; +var bliffl = Object.freeze({bar: '12345', ...baz}); +bliffl.bar = '23456'; // error +bliffl.baz = 3456; // error +bliffl.corge; // error +bliffl.constructor = baz; // error +bliffl.toString = function() {}; // error + +baz.baz = 0; + +var x : number = Object.freeze(123); + +var xx : { x: number } = Object.freeze({ x: "error" }) diff --git a/tests/object_is/__snapshots__/jsfmt.spec.js.snap b/tests/object_is/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..712ce14a0089 --- /dev/null +++ b/tests/object_is/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,46 @@ +exports[`test object_is.js 1`] = ` +"Object.is(1, 1); +Object.is(1, 2); +Object.is(1, {}); +Object.is(1, NaN); +Object.is(0, 0); +Object.is(0, -0); +Object.is(NaN, NaN); +Object.is({}, {}); + +var emptyObject = {}; +var emptyArray = []; +Object.is(emptyObject, emptyObject); +Object.is(emptyArray, emptyArray); +Object.is(emptyObject, emptyArray); + +var squared = x => x * x; +Object.is(squared, squared); + +var a: boolean = Object.is('a', 'a'); +var b: string = Object.is('a', 'a'); +var c: boolean = Object.is('a'); +var d: boolean = Object.is('a', 'b', 'c'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Object.is(1, 1); +Object.is(1, 2); +Object.is(1, {}); +Object.is(1, NaN); +Object.is(0, 0); +Object.is(0, -0); +Object.is(NaN, NaN); +Object.is({}, {}); +var emptyObject = {}; +var emptyArray = [ ]; +Object.is(emptyObject, emptyObject); +Object.is(emptyArray, emptyArray); +Object.is(emptyObject, emptyArray); +var squared = x => x * x; +Object.is(squared, squared); +var a: boolean = Object.is("a", "a"); +var b: string = Object.is("a", "a"); +var c: boolean = Object.is("a"); +var d: boolean = Object.is("a", "b", "c"); + +" +`; diff --git a/tests/object_is/jsfmt.spec.js b/tests/object_is/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/object_is/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/object_is/object_is.js b/tests/object_is/object_is.js new file mode 100644 index 000000000000..a13f2e3baed7 --- /dev/null +++ b/tests/object_is/object_is.js @@ -0,0 +1,22 @@ +Object.is(1, 1); +Object.is(1, 2); +Object.is(1, {}); +Object.is(1, NaN); +Object.is(0, 0); +Object.is(0, -0); +Object.is(NaN, NaN); +Object.is({}, {}); + +var emptyObject = {}; +var emptyArray = []; +Object.is(emptyObject, emptyObject); +Object.is(emptyArray, emptyArray); +Object.is(emptyObject, emptyArray); + +var squared = x => x * x; +Object.is(squared, squared); + +var a: boolean = Object.is('a', 'a'); +var b: string = Object.is('a', 'a'); +var c: boolean = Object.is('a'); +var d: boolean = Object.is('a', 'b', 'c'); diff --git a/tests/objects/__snapshots__/jsfmt.spec.js.snap b/tests/objects/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..daae00078b05 --- /dev/null +++ b/tests/objects/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,125 @@ +exports[`test conversion.js 1`] = ` +"/* @flow */ + +(Object({foo: \'bar\'}): {foo: string}); +(Object(\"123\"): String); +(Object(123): Number); +(Object(true): Boolean); +(Object(null): {}); +(Object(undefined): {}); +(Object(void(0)): {}); +(Object(undefined): Number); // error + +var x = Object(null); +x.foo = \"bar\"; + +var y = Object(\"123\"); +(y.charAt(0): string); + +var z = Object(123); // error (next line makes this not match any signatures) +(z.charAt(0): string); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +(Object({ foo: \"bar\" }): { foo: string }); +(Object(\"123\"): String); +(Object(123): Number); +(Object(true): Boolean); +(Object(null): {}); +(Object(undefined): {}); +(Object(void 0): {}); +(Object(undefined): Number);// error +var x = Object(null); +x.foo = \"bar\"; +var y = Object(\"123\"); +(y.charAt(0): string); +var z = Object(123);// error (next line makes this not match any signatures) +(z.charAt(0): string); + +" +`; + +exports[`test objects.js 1`] = ` +"/* @flow */ + +var x : {\'123\': string, bar: string} = {\'123\': \'val\', bar: \'bar\'}; +(x.foo : string); // error, key doesn\'t exist +(x[\'foo\'] : string); // error, key doesn\'t exist +(x[123] : boolean); // TODO: use the number\'s value to error here +(x.bar: boolean); // error, string !~> boolean +(x[\'123\'] : boolean); // error, string !~> boolean +x[\'123\'] = false; // error, boolean !~> string +x[123] = false; // TODO: use the number\'s value to error here +x[\'foo\'+\'bar\'] = \'derp\'; // ok since we can\'t tell +(x[\`foo\`]: string); // error, key doesn\'t exist + +var y : {foo: string} = {foo: \'bar\'}; +y[\'foo\'] = 123; // error, number !~> string +y[\'bar\'] = \'abc\'; // error, property not found + +(y[\'hasOwnProperty\']: string); // error, prototype method is not a string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var x: { \"123\": string, bar: string } = { \"123\": \"val\", bar: \"bar\" }; +(x.foo: string);// error, key doesn\'t exist +(x[\"foo\"]: string);// error, key doesn\'t exist +(x[123]: boolean);// TODO: use the number\'s value to error here +(x.bar: boolean);// error, string !~> boolean +(x[\"123\"]: boolean);// error, string !~> boolean +x[\"123\"] = false;// error, boolean !~> string +x[123] = false;// TODO: use the number\'s value to error here +x[\"foo\" + \"bar\"] = \"derp\";// ok since we can\'t tell +(x[\`foo\`]: string);// error, key doesn\'t exist +var y: { foo: string } = { foo: \"bar\" }; +y[\"foo\"] = 123;// error, number !~> string +y[\"bar\"] = \"abc\";// error, property not found +(y[\"hasOwnProperty\"]: string);// error, prototype method is not a string + +" +`; + +exports[`test unaliased_assign.js 1`] = ` +"/** + * test handling of unaliased value assignment. + * + * An unaliased object rvalue may be assigned to a supertype lvalue, + * because later widening mutations on the rvalue can\'t break assumptions + * made by other lvalues. + * + * However, upon assignment the rvalue must take on the type of the + * lvalue, to avoid both false positives and false negatives + * (unsoundness), as shown below. + * + * @flow + */ + +var glob: { x: string } = { x: \"hey\" }; + +function assign_then_alias() { + var obj: { x: string | number }; + obj = { x: \"hey\" }; + glob = obj; // error: subsequent assignment might make glob.x a number +} + +function assign_then_widen() { + var obj: { x: string | number }; + obj = { x: \"hey\" }; + obj.x = 10; // ok, by lvalue\'s given type +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/objects/conversion.js b/tests/objects/conversion.js new file mode 100644 index 000000000000..48cc52980743 --- /dev/null +++ b/tests/objects/conversion.js @@ -0,0 +1,19 @@ +/* @flow */ + +(Object({foo: 'bar'}): {foo: string}); +(Object("123"): String); +(Object(123): Number); +(Object(true): Boolean); +(Object(null): {}); +(Object(undefined): {}); +(Object(void(0)): {}); +(Object(undefined): Number); // error + +var x = Object(null); +x.foo = "bar"; + +var y = Object("123"); +(y.charAt(0): string); + +var z = Object(123); // error (next line makes this not match any signatures) +(z.charAt(0): string); diff --git a/tests/objects/jsfmt.spec.js b/tests/objects/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/objects/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/objects/objects.js b/tests/objects/objects.js new file mode 100644 index 000000000000..5eea185f7041 --- /dev/null +++ b/tests/objects/objects.js @@ -0,0 +1,18 @@ +/* @flow */ + +var x : {'123': string, bar: string} = {'123': 'val', bar: 'bar'}; +(x.foo : string); // error, key doesn't exist +(x['foo'] : string); // error, key doesn't exist +(x[123] : boolean); // TODO: use the number's value to error here +(x.bar: boolean); // error, string !~> boolean +(x['123'] : boolean); // error, string !~> boolean +x['123'] = false; // error, boolean !~> string +x[123] = false; // TODO: use the number's value to error here +x['foo'+'bar'] = 'derp'; // ok since we can't tell +(x[`foo`]: string); // error, key doesn't exist + +var y : {foo: string} = {foo: 'bar'}; +y['foo'] = 123; // error, number !~> string +y['bar'] = 'abc'; // error, property not found + +(y['hasOwnProperty']: string); // error, prototype method is not a string diff --git a/tests/objects/unaliased_assign.js b/tests/objects/unaliased_assign.js new file mode 100644 index 000000000000..c1cebeea5260 --- /dev/null +++ b/tests/objects/unaliased_assign.js @@ -0,0 +1,27 @@ +/** + * test handling of unaliased value assignment. + * + * An unaliased object rvalue may be assigned to a supertype lvalue, + * because later widening mutations on the rvalue can't break assumptions + * made by other lvalues. + * + * However, upon assignment the rvalue must take on the type of the + * lvalue, to avoid both false positives and false negatives + * (unsoundness), as shown below. + * + * @flow + */ + +var glob: { x: string } = { x: "hey" }; + +function assign_then_alias() { + var obj: { x: string | number }; + obj = { x: "hey" }; + glob = obj; // error: subsequent assignment might make glob.x a number +} + +function assign_then_widen() { + var obj: { x: string | number }; + obj = { x: "hey" }; + obj.x = 10; // ok, by lvalue's given type +} diff --git a/tests/objmap/__snapshots__/jsfmt.spec.js.snap b/tests/objmap/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..59efee2952c0 --- /dev/null +++ b/tests/objmap/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,37 @@ +exports[`test objmap.js 1`] = ` +"declare function promiseAllByKey(o: O): Promise<$ObjMap>; +declare function keyMirror(o: O): $ObjMapi(k:K) => K>; + +var o = keyMirror({ + FOO: null, + BAR: null, +}); + +(o.FOO : \'FOO\'); // ok +(o.FOO : \'BAR\'); // error, \'FOO\' incompatible with \'BAR\' + +promiseAllByKey({ + foo: Promise.resolve(0), + bar: \'bar\', +}).then(o => { + (o.foo: string); // error, number ~> string + (o.bar: \'bar\'); // ok +}); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/objmap/jsfmt.spec.js b/tests/objmap/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/objmap/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/objmap/objmap.js b/tests/objmap/objmap.js new file mode 100644 index 000000000000..2bdd9fd2ddc9 --- /dev/null +++ b/tests/objmap/objmap.js @@ -0,0 +1,18 @@ +declare function promiseAllByKey(o: O): Promise<$ObjMap>; +declare function keyMirror(o: O): $ObjMapi(k:K) => K>; + +var o = keyMirror({ + FOO: null, + BAR: null, +}); + +(o.FOO : 'FOO'); // ok +(o.FOO : 'BAR'); // error, 'FOO' incompatible with 'BAR' + +promiseAllByKey({ + foo: Promise.resolve(0), + bar: 'bar', +}).then(o => { + (o.foo: string); // error, number ~> string + (o.bar: 'bar'); // ok +}); diff --git a/tests/optional/__snapshots__/jsfmt.spec.js.snap b/tests/optional/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..df6981ed082c --- /dev/null +++ b/tests/optional/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,407 @@ +exports[`test client_optional.js 1`] = ` +"var qux = require(\'./optional\'); + +qux(0); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var qux = require(\"./optional\"); +qux(0); + +" +`; + +exports[`test default.js 1`] = ` +"function f(foo, bar = foo): [T, T] { + return [foo, bar]; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:340:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test generic.js 1`] = ` +"function x(x: T = 0) {} + +class C { + x(x: T = 0) {} +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:340:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test maybe.js 1`] = ` +"function foo(x?: string): ?string { + return x; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +function foo(x: string): ?string { + return x; +} + +" +`; + +exports[`test nullable.js 1`] = ` +"/* @flow */ + +function optionalNullable1(x: {y?: ?number}) { + if (x.y !== null && x.y !== undefined) { + x.y++; + } +} + +function optionalNullable2(x: {y?: ?number}) { + if (x.y !== undefined && x.y !== null) { + x.y++; + } +} + +function optionalNullable3(x: {y?: ?number}) { + if (!(x.y !== null && x.y !== undefined)) { + x.y++; // should error + } +} + +function optionalNullable4(x: {y?: ?number}) { + if (!(x.y !== undefined && x.y !== null)) { + x.y++; // should error + } +} + +function optionalNullable5(x: {y?: ?number}) { + if (x.y === null || x.y === undefined) { + x.y++; // should error + } +} + +function optionalNullable6(x: {y?: ?number}) { + if (x.y === undefined || x.y === null) { + x.y++; // should error + } +} + +function optionalNullable7(x: {y?: ?number}) { + if (!(x.y === null || x.y === undefined)) { + x.y++; + } +} + +function optionalNullable8(x: {y?: ?number}) { + if (!(x.y === undefined || x.y === null)) { + x.y++; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +function optionalNullable1(x: { y?: ?number }) { + if (x.y !== null && x.y !== undefined) { + x.y++; + } +} +function optionalNullable2(x: { y?: ?number }) { + if (x.y !== undefined && x.y !== null) { + x.y++; + } +} +function optionalNullable3(x: { y?: ?number }) { + if (!(x.y !== null && x.y !== undefined)) { + x.y++;// should error + } +} +function optionalNullable4(x: { y?: ?number }) { + if (!(x.y !== undefined && x.y !== null)) { + x.y++;// should error + } +} +function optionalNullable5(x: { y?: ?number }) { + if (x.y === null || x.y === undefined) { + x.y++;// should error + } +} +function optionalNullable6(x: { y?: ?number }) { + if (x.y === undefined || x.y === null) { + x.y++;// should error + } +} +function optionalNullable7(x: { y?: ?number }) { + if (!(x.y === null || x.y === undefined)) { + x.y++; + } +} +function optionalNullable8(x: { y?: ?number }) { + if (!(x.y === undefined || x.y === null)) { + x.y++; + } +} + +" +`; + +exports[`test optional.js 1`] = ` +"function bar(x?,y?) { x * 0; } +bar(0); + +var foo:(x?:number)=>void = bar; +foo(); + +function qux(x=\"hello\",...y):string { foo(x); return y[0]; } + +qux(0,0); +qux(0,...[\"\",42]); + +module.exports = qux; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test optional_param.js 1`] = ` +"/* @flow */ +function foo(x?: string): string { + if (x == null) { return \'foo\'; } + return x; +} + +function bar(obj: {x?: string}): string { + if (obj.x == null) { return \'foo\'; } + return obj.x; +} + +function baz(bar?) { + if (!bar) { return 1; } + return bar.duck +} + +function testOptionalNullable(x?: ?string): string { + if (x == null) { return \'foo\'; } + return x; +} + +function testOptionalNullableDefault(x?: ?string = \"hi\"): string { + if (x == null) { return \'foo\'; } + return x; +} + +function testOptionalNullableProperty(obj: {x?: ?string}): string { + if (obj.x == null) { return \'foo\'; } + return obj.x; +} + +function testOptionalNullableFlowingToNullable(x?: ?string): ?string { + var f = function(y: ?string) {}; + f(x); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +function foo(x: string): string { + if (x == null) { + return \"foo\"; + } + return x; +} +function bar(obj: { x?: string }): string { + if (obj.x == null) { + return \"foo\"; + } + return obj.x; +} +function baz(bar) { + if (!bar) { + return 1; + } + return bar.duck; +} +function testOptionalNullable(x: ?string): string { + if (x == null) { + return \"foo\"; + } + return x; +} +function testOptionalNullableDefault(x: ?string = \"hi\"): string { + if (x == null) { + return \"foo\"; + } + return x; +} +function testOptionalNullableProperty(obj: { x?: ?string }): string { + if (obj.x == null) { + return \"foo\"; + } + return obj.x; +} +function testOptionalNullableFlowingToNullable(x: ?string): ?string { + var f = function(y: ?string) { + + }; + f(x); +} + +" +`; + +exports[`test optional_param2.js 1`] = ` +"declare class I { + map( + mapper: (value?: V) => M + ): I; +} +var i:I = new I(); +var j:I = i.map(id => id); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1368:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test optional_param3.js 1`] = ` +"function foo(x?: number) {} +foo(undefined); // ok + +function bar(x = \"bar\"): string { + return x; +} +bar(undefined); // ok +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +function foo(x: number) { + +} +foo(undefined);// ok +function bar(x = \"bar\"): string { + return x; +} +bar(undefined);// ok + +" +`; + +exports[`test optional_param4.js 1`] = ` +"/* @flow */ + +function foo(x?: number, ...y: Array): [?number, Array] { + return [x, y]; +} + +foo(); // OK +foo(123), // OK +foo(123, \'hello\'); // OK + + +foo(true); // ERROR boolean ~> number +foo(123, true); // ERROR boolean ~> string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1237 + throw new Error(\"unprintable type: \" + JSON.stringify(n.type)); + ^ + +Error: unprintable type: \"TupleTypeAnnotation\" + at genericPrintNoParens (/src/printer.js:1237:13) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test undefined.js 1`] = ` +"var x; + +function foo(bar? = undefined) { + x = bar; +} + +function bar() { + return x.duck; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var x; +function foo(bar = undefined) { + x = bar; +} +function bar() { + return x.duck; +} + +" +`; + +exports[`test undefined2.js 1`] = ` +"var x; + +function foo(bar?) { + x = bar; +} + +function bar() { + return x.duck; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var x; +function foo(bar) { + x = bar; +} +function bar() { + return x.duck; +} + +" +`; diff --git a/tests/optional/client_optional.js b/tests/optional/client_optional.js new file mode 100644 index 000000000000..1f7d6b7bb0b1 --- /dev/null +++ b/tests/optional/client_optional.js @@ -0,0 +1,3 @@ +var qux = require('./optional'); + +qux(0); diff --git a/tests/optional/default.js b/tests/optional/default.js new file mode 100644 index 000000000000..48d31c63ba77 --- /dev/null +++ b/tests/optional/default.js @@ -0,0 +1,3 @@ +function f(foo, bar = foo): [T, T] { + return [foo, bar]; +} diff --git a/tests/optional/generic.js b/tests/optional/generic.js new file mode 100644 index 000000000000..3fb2a4d6c460 --- /dev/null +++ b/tests/optional/generic.js @@ -0,0 +1,5 @@ +function x(x: T = 0) {} + +class C { + x(x: T = 0) {} +} diff --git a/tests/optional/jsfmt.spec.js b/tests/optional/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/optional/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/optional/maybe.js b/tests/optional/maybe.js new file mode 100644 index 000000000000..d2266e6d22a4 --- /dev/null +++ b/tests/optional/maybe.js @@ -0,0 +1,3 @@ +function foo(x?: string): ?string { + return x; +} diff --git a/tests/optional/nullable.js b/tests/optional/nullable.js new file mode 100644 index 000000000000..509397894097 --- /dev/null +++ b/tests/optional/nullable.js @@ -0,0 +1,49 @@ +/* @flow */ + +function optionalNullable1(x: {y?: ?number}) { + if (x.y !== null && x.y !== undefined) { + x.y++; + } +} + +function optionalNullable2(x: {y?: ?number}) { + if (x.y !== undefined && x.y !== null) { + x.y++; + } +} + +function optionalNullable3(x: {y?: ?number}) { + if (!(x.y !== null && x.y !== undefined)) { + x.y++; // should error + } +} + +function optionalNullable4(x: {y?: ?number}) { + if (!(x.y !== undefined && x.y !== null)) { + x.y++; // should error + } +} + +function optionalNullable5(x: {y?: ?number}) { + if (x.y === null || x.y === undefined) { + x.y++; // should error + } +} + +function optionalNullable6(x: {y?: ?number}) { + if (x.y === undefined || x.y === null) { + x.y++; // should error + } +} + +function optionalNullable7(x: {y?: ?number}) { + if (!(x.y === null || x.y === undefined)) { + x.y++; + } +} + +function optionalNullable8(x: {y?: ?number}) { + if (!(x.y === undefined || x.y === null)) { + x.y++; + } +} diff --git a/tests/optional/optional.js b/tests/optional/optional.js new file mode 100644 index 000000000000..79fa1d5317b9 --- /dev/null +++ b/tests/optional/optional.js @@ -0,0 +1,12 @@ +function bar(x?,y?) { x * 0; } +bar(0); + +var foo:(x?:number)=>void = bar; +foo(); + +function qux(x="hello",...y):string { foo(x); return y[0]; } + +qux(0,0); +qux(0,...["",42]); + +module.exports = qux; diff --git a/tests/optional/optional_param.js b/tests/optional/optional_param.js new file mode 100644 index 000000000000..5ba9b9a660ca --- /dev/null +++ b/tests/optional/optional_param.js @@ -0,0 +1,35 @@ +/* @flow */ +function foo(x?: string): string { + if (x == null) { return 'foo'; } + return x; +} + +function bar(obj: {x?: string}): string { + if (obj.x == null) { return 'foo'; } + return obj.x; +} + +function baz(bar?) { + if (!bar) { return 1; } + return bar.duck +} + +function testOptionalNullable(x?: ?string): string { + if (x == null) { return 'foo'; } + return x; +} + +function testOptionalNullableDefault(x?: ?string = "hi"): string { + if (x == null) { return 'foo'; } + return x; +} + +function testOptionalNullableProperty(obj: {x?: ?string}): string { + if (obj.x == null) { return 'foo'; } + return obj.x; +} + +function testOptionalNullableFlowingToNullable(x?: ?string): ?string { + var f = function(y: ?string) {}; + f(x); +} diff --git a/tests/optional/optional_param2.js b/tests/optional/optional_param2.js new file mode 100644 index 000000000000..c26f546d3e84 --- /dev/null +++ b/tests/optional/optional_param2.js @@ -0,0 +1,7 @@ +declare class I { + map( + mapper: (value?: V) => M + ): I; +} +var i:I = new I(); +var j:I = i.map(id => id); diff --git a/tests/optional/optional_param3.js b/tests/optional/optional_param3.js new file mode 100644 index 000000000000..1171945a726d --- /dev/null +++ b/tests/optional/optional_param3.js @@ -0,0 +1,7 @@ +function foo(x?: number) {} +foo(undefined); // ok + +function bar(x = "bar"): string { + return x; +} +bar(undefined); // ok diff --git a/tests/optional/optional_param4.js b/tests/optional/optional_param4.js new file mode 100644 index 000000000000..c880d64e6fef --- /dev/null +++ b/tests/optional/optional_param4.js @@ -0,0 +1,13 @@ +/* @flow */ + +function foo(x?: number, ...y: Array): [?number, Array] { + return [x, y]; +} + +foo(); // OK +foo(123), // OK +foo(123, 'hello'); // OK + + +foo(true); // ERROR boolean ~> number +foo(123, true); // ERROR boolean ~> string diff --git a/tests/optional/undefined.js b/tests/optional/undefined.js new file mode 100644 index 000000000000..5a23002c0cab --- /dev/null +++ b/tests/optional/undefined.js @@ -0,0 +1,9 @@ +var x; + +function foo(bar? = undefined) { + x = bar; +} + +function bar() { + return x.duck; +} diff --git a/tests/optional/undefined2.js b/tests/optional/undefined2.js new file mode 100644 index 000000000000..921c7383c757 --- /dev/null +++ b/tests/optional/undefined2.js @@ -0,0 +1,9 @@ +var x; + +function foo(bar?) { + x = bar; +} + +function bar() { + return x.duck; +} diff --git a/tests/optional_props/__snapshots__/jsfmt.spec.js.snap b/tests/optional_props/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..0c27bae9581e --- /dev/null +++ b/tests/optional_props/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,199 @@ +exports[`test test.js 1`] = ` +"var x: { } = { foo: 0 }; +var y: { foo?: string } = x; // OK in TypeScript, not OK in Flow + +var z: string = y.foo || \"\"; + +var o = { }; +y = o; // OK; we know that narrowing could not have happened +o.foo = 0; // future widening is constrained + +function bar(config: { foo?: number }) {} +bar({}); +bar({foo: \"\"}); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var x: {} = { foo: 0 }; +var y: { foo?: string } = x;// OK in TypeScript, not OK in Flow +var z: string = y.foo || \"\"; +var o = {}; +y = o;// OK; we know that narrowing could not have happened +o.foo = 0;// future widening is constrained +function bar(config: { foo?: number }) { + +} +bar({}); +bar({ foo: \"\" }); + +" +`; + +exports[`test test2.js 1`] = ` +"var a: { foo?: string } = {}; +a.foo = undefined; // This is not an error +a.foo = null; // But this is an error + +var b: { foo?: ?string } = {}; +b.foo = undefined; // This is fine +b.foo = null; // Also fine + +var c: { foo?: string } = { foo: undefined }; // This is not an error +var d: { foo?: string } = { foo: null }; // But this is an error + +var e: { foo?: ?string } = { foo: undefined }; // This is fine +var f: { foo?: ?string } = { foo: null }; // Also fine +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var a: { foo?: string } = {}; +a.foo = undefined;// This is not an error +a.foo = null;// But this is an error +var b: { foo?: ?string } = {}; +b.foo = undefined;// This is fine +b.foo = null;// Also fine +var c: { foo?: string } = { foo: undefined };// This is not an error +var d: { foo?: string } = { foo: null };// But this is an error +var e: { foo?: ?string } = { foo: undefined };// This is fine +var f: { foo?: ?string } = { foo: null };// Also fine + +" +`; + +exports[`test test3.js 1`] = ` +"// @flow + +/* + object literals are sealed. this is simply a heuristic + decision: most of the time, the rule gives the \'right\' + errors. + + an exception is when a literal is used as an initializer + for an lvalue whose type specifies optional properties + missing from the literal, as below. + + the problem becomes visible when a property assignment + is then used to (legitimately) extend the object with an + optional property - the variable\'s specific (path- + dependent) type has become that of the literal which. + without adjustment, will reject the property addition. + + the solution in cases where a sealed object type (as from + an object literal) flows to an object type with optional + properties, is to have the sealed type acquire the optional + properties. + */ + +// x has optional property b. +// (note that the initializer here does not play into +// the problem, it\'s just a placeholder. initializers +// do not narrow the types of annotated variables as do +// subsequent assignments.) +// +var x: { a: number, b?: number } = { a: 0 }; + +// now assign an object literal lacking property b. +// the literal\'s type is sealed and has only a at creation. +// but it then flows, specific ~> general, to x\'s annotation +// type. at that point, it acquires b as an optional property. +// +x = { a: 0 }; + +// ...which allows this assignment to take place. +x.b = 1; + +// T7810506 +class A { + x: { a: number, b?: string }; + foo() { + // Something similar should happen here, but doesn\'t: the problem is + // made explicit by adding generics (see test3_failure.js introduced by + // D2747512). There is a race between writing b on the object literal + // type and adding b as an optional property to it, since in general we + // cannot guarantee that the flow from the object literal to the + // annotation will be processed before the flow involving the + // access. Here we lose the race and get an error on the write. + this.x = { a: 123 }; + this.x.b = \'hello\'; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +/* + object literals are sealed. this is simply a heuristic + decision: most of the time, the rule gives the \'right\' + errors. + + an exception is when a literal is used as an initializer + for an lvalue whose type specifies optional properties + missing from the literal, as below. + + the problem becomes visible when a property assignment + is then used to (legitimately) extend the object with an + optional property - the variable\'s specific (path- + dependent) type has become that of the literal which. + without adjustment, will reject the property addition. + + the solution in cases where a sealed object type (as from + an object literal) flows to an object type with optional + properties, is to have the sealed type acquire the optional + properties. + */ +// x has optional property b. +// (note that the initializer here does not play into +// the problem, it\'s just a placeholder. initializers +// do not narrow the types of annotated variables as do +// subsequent assignments.) +// +var x: { a: number, b?: number } = { a: 0 }; +// now assign an object literal lacking property b. +// the literal\'s type is sealed and has only a at creation. +// but it then flows, specific ~> general, to x\'s annotation +// type. at that point, it acquires b as an optional property. +// +x = { a: 0 }; +// ...which allows this assignment to take place. +x.b = 1; +// T7810506 +class A { + x: { a: number, b?: string }; + foo() { + // Something similar should happen here, but doesn\'t: the problem is + // made explicit by adding generics (see test3_failure.js introduced by + // D2747512). There is a race between writing b on the object literal + // type and adding b as an optional property to it, since in general we + // cannot guarantee that the flow from the object literal to the + // annotation will be processed before the flow involving the + // access. Here we lose the race and get an error on the write. + this.x = { a: 123 }; + this.x.b = \"hello\"; + } +} + +" +`; + +exports[`test test3_failure.js 1`] = ` +"// generalization of failure in test3.js + +class A { + o: O; + foo() { + this.o.x = { a: 123 }; + this.o.x.b = \'hello\'; // this is a spurious error (see test3.js for details) + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1172:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/optional_props/jsfmt.spec.js b/tests/optional_props/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/optional_props/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/optional_props/test.js b/tests/optional_props/test.js new file mode 100644 index 000000000000..ecb039485694 --- /dev/null +++ b/tests/optional_props/test.js @@ -0,0 +1,12 @@ +var x: { } = { foo: 0 }; +var y: { foo?: string } = x; // OK in TypeScript, not OK in Flow + +var z: string = y.foo || ""; + +var o = { }; +y = o; // OK; we know that narrowing could not have happened +o.foo = 0; // future widening is constrained + +function bar(config: { foo?: number }) {} +bar({}); +bar({foo: ""}); diff --git a/tests/optional_props/test2.js b/tests/optional_props/test2.js new file mode 100644 index 000000000000..827c6f3776ce --- /dev/null +++ b/tests/optional_props/test2.js @@ -0,0 +1,13 @@ +var a: { foo?: string } = {}; +a.foo = undefined; // This is not an error +a.foo = null; // But this is an error + +var b: { foo?: ?string } = {}; +b.foo = undefined; // This is fine +b.foo = null; // Also fine + +var c: { foo?: string } = { foo: undefined }; // This is not an error +var d: { foo?: string } = { foo: null }; // But this is an error + +var e: { foo?: ?string } = { foo: undefined }; // This is fine +var f: { foo?: ?string } = { foo: null }; // Also fine diff --git a/tests/optional_props/test3.js b/tests/optional_props/test3.js new file mode 100644 index 000000000000..2b737ace63fa --- /dev/null +++ b/tests/optional_props/test3.js @@ -0,0 +1,56 @@ +// @flow + +/* + object literals are sealed. this is simply a heuristic + decision: most of the time, the rule gives the 'right' + errors. + + an exception is when a literal is used as an initializer + for an lvalue whose type specifies optional properties + missing from the literal, as below. + + the problem becomes visible when a property assignment + is then used to (legitimately) extend the object with an + optional property - the variable's specific (path- + dependent) type has become that of the literal which. + without adjustment, will reject the property addition. + + the solution in cases where a sealed object type (as from + an object literal) flows to an object type with optional + properties, is to have the sealed type acquire the optional + properties. + */ + +// x has optional property b. +// (note that the initializer here does not play into +// the problem, it's just a placeholder. initializers +// do not narrow the types of annotated variables as do +// subsequent assignments.) +// +var x: { a: number, b?: number } = { a: 0 }; + +// now assign an object literal lacking property b. +// the literal's type is sealed and has only a at creation. +// but it then flows, specific ~> general, to x's annotation +// type. at that point, it acquires b as an optional property. +// +x = { a: 0 }; + +// ...which allows this assignment to take place. +x.b = 1; + +// T7810506 +class A { + x: { a: number, b?: string }; + foo() { + // Something similar should happen here, but doesn't: the problem is + // made explicit by adding generics (see test3_failure.js introduced by + // D2747512). There is a race between writing b on the object literal + // type and adding b as an optional property to it, since in general we + // cannot guarantee that the flow from the object literal to the + // annotation will be processed before the flow involving the + // access. Here we lose the race and get an error on the write. + this.x = { a: 123 }; + this.x.b = 'hello'; + } +} diff --git a/tests/optional_props/test3_failure.js b/tests/optional_props/test3_failure.js new file mode 100644 index 000000000000..f6478197da18 --- /dev/null +++ b/tests/optional_props/test3_failure.js @@ -0,0 +1,9 @@ +// generalization of failure in test3.js + +class A { + o: O; + foo() { + this.o.x = { a: 123 }; + this.o.x.b = 'hello'; // this is a spurious error (see test3.js for details) + } +} diff --git a/tests/overload/__snapshots__/jsfmt.spec.js.snap b/tests/overload/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..4998e29a8cec --- /dev/null +++ b/tests/overload/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,176 @@ +exports[`test overload.js 1`] = ` +"/** + * tests of overload selection + * + * @flow + */ + +var x1: number = \"\".match(0)[0]; +var x2: number = \"\".match(/pattern/)[0]; +var x3: string = \"\".replace(/pattern/,\"...\"); +var x4: number = \"\".split(/pattern/)[0]; + +declare class C { + foo(x:number): number; + foo(x:string): string; + + bar(x: { a: number }): number; + bar(x: { a: string }): string; +} + +var a = new C(); + +a.foo(0); // ok +a.foo(\"hey\"); // ok +a.foo(true); // error, function cannot be called on intersection type + +a.bar({ a: 0 }); // ok +a.bar({ a: \"hey\" }); // ok +a.bar({ a: true }); // error, function cannot be called on intersection type + +declare var x: { a: boolean; } & { b: string }; + +a.bar(x); // error with nested intersection info (outer for bar, inner for x) + +/********** tests ************** +interface Dummy { + dumb(foo: (x:number) => number):number; + dumb(foo: (x:string) => string):string; + + dumber(bar: (x:T) => Array):U; + dumber(bar: (x:T) => U):Array; +} + +function foo(x:string):string { return x; } +var y:number = new Dummy().dumb(foo); + +function bar1(x:number):Array { return []; } +var z1:number = new Dummy().dumber(bar1); + +function bar2(x:number):string { return \"...\"; } +var z2:Array = new Dummy().dumber(bar2); +*/ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test.js 1`] = ` +"function foo() { + var output = new FakeUint8Array(); + output.set(new FakeUint8Array(), 0); // matches one of the overloads of set +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +function foo() { + var output = new FakeUint8Array(); + output.set(new FakeUint8Array(), 0);// matches one of the overloads of set +} + +" +`; + +exports[`test test2.js 1`] = ` +"declare class Foo { + bar(x: \'hmm\'): number; + bar(x: string): string; +} +var foo = new Foo; +(foo.bar(\'hmm\'): number); // OK +(foo.bar(\'hmmm\'): number); // error +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test3.js 1`] = ` +"// passing a union-like thing into an overload is ok +// if overload handles each branch of union-like thing + +// unions +declare function f(x: string): void; +declare function f(x: number): void; +declare var x_f: string | number; +f(x_f); // ok + +// maybe +declare function g(x: null): void; +declare function g(x: void): void; +declare function g(x: string): void; +declare var x_g: ?string; +g(x_g); // ok + +// optional +declare function h(x: void): void; +declare function h(x: string): void; +declare var x_h: {p?: string}; +h(x_h.p); // ok +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test union.js 1`] = ` +"function foo (x: $Either,U>): Array { return []; } + +var x1:number = foo(0)[0]; +var x2:string = foo([\"\"])[0]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:340:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/overload/jsfmt.spec.js b/tests/overload/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/overload/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/overload/lib/__snapshots__/jsfmt.spec.js.snap b/tests/overload/lib/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..b85798c12da2 --- /dev/null +++ b/tests/overload/lib/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,23 @@ +exports[`test lib.js 1`] = ` +"declare class FakeUint8Array { + set(index: number, value: number): void; + set(array: FakeUint8Array | Array, offset?: number): void; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/overload/lib/jsfmt.spec.js b/tests/overload/lib/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/overload/lib/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/overload/lib/lib.js b/tests/overload/lib/lib.js new file mode 100644 index 000000000000..616d065acbe6 --- /dev/null +++ b/tests/overload/lib/lib.js @@ -0,0 +1,4 @@ +declare class FakeUint8Array { + set(index: number, value: number): void; + set(array: FakeUint8Array | Array, offset?: number): void; +} diff --git a/tests/overload/overload.js b/tests/overload/overload.js new file mode 100644 index 000000000000..ca2d8848bba4 --- /dev/null +++ b/tests/overload/overload.js @@ -0,0 +1,51 @@ +/** + * tests of overload selection + * + * @flow + */ + +var x1: number = "".match(0)[0]; +var x2: number = "".match(/pattern/)[0]; +var x3: string = "".replace(/pattern/,"..."); +var x4: number = "".split(/pattern/)[0]; + +declare class C { + foo(x:number): number; + foo(x:string): string; + + bar(x: { a: number }): number; + bar(x: { a: string }): string; +} + +var a = new C(); + +a.foo(0); // ok +a.foo("hey"); // ok +a.foo(true); // error, function cannot be called on intersection type + +a.bar({ a: 0 }); // ok +a.bar({ a: "hey" }); // ok +a.bar({ a: true }); // error, function cannot be called on intersection type + +declare var x: { a: boolean; } & { b: string }; + +a.bar(x); // error with nested intersection info (outer for bar, inner for x) + +/********** tests ************** +interface Dummy { + dumb(foo: (x:number) => number):number; + dumb(foo: (x:string) => string):string; + + dumber(bar: (x:T) => Array):U; + dumber(bar: (x:T) => U):Array; +} + +function foo(x:string):string { return x; } +var y:number = new Dummy().dumb(foo); + +function bar1(x:number):Array { return []; } +var z1:number = new Dummy().dumber(bar1); + +function bar2(x:number):string { return "..."; } +var z2:Array = new Dummy().dumber(bar2); +*/ diff --git a/tests/overload/test.js b/tests/overload/test.js new file mode 100644 index 000000000000..1dc44ef28277 --- /dev/null +++ b/tests/overload/test.js @@ -0,0 +1,4 @@ +function foo() { + var output = new FakeUint8Array(); + output.set(new FakeUint8Array(), 0); // matches one of the overloads of set +} diff --git a/tests/overload/test2.js b/tests/overload/test2.js new file mode 100644 index 000000000000..d3d1fb7f5ba6 --- /dev/null +++ b/tests/overload/test2.js @@ -0,0 +1,7 @@ +declare class Foo { + bar(x: 'hmm'): number; + bar(x: string): string; +} +var foo = new Foo; +(foo.bar('hmm'): number); // OK +(foo.bar('hmmm'): number); // error diff --git a/tests/overload/test3.js b/tests/overload/test3.js new file mode 100644 index 000000000000..4c4fefe6e609 --- /dev/null +++ b/tests/overload/test3.js @@ -0,0 +1,21 @@ +// passing a union-like thing into an overload is ok +// if overload handles each branch of union-like thing + +// unions +declare function f(x: string): void; +declare function f(x: number): void; +declare var x_f: string | number; +f(x_f); // ok + +// maybe +declare function g(x: null): void; +declare function g(x: void): void; +declare function g(x: string): void; +declare var x_g: ?string; +g(x_g); // ok + +// optional +declare function h(x: void): void; +declare function h(x: string): void; +declare var x_h: {p?: string}; +h(x_h.p); // ok diff --git a/tests/overload/union.js b/tests/overload/union.js new file mode 100644 index 000000000000..4e707c3feb53 --- /dev/null +++ b/tests/overload/union.js @@ -0,0 +1,4 @@ +function foo (x: $Either,U>): Array { return []; } + +var x1:number = foo(0)[0]; +var x2:string = foo([""])[0]; diff --git a/tests/parse/__snapshots__/jsfmt.spec.js.snap b/tests/parse/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..9a0838ba525a --- /dev/null +++ b/tests/parse/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,90 @@ +exports[`test fail.js 1`] = ` +". +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (1:0) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$3.parseExprAtom (/node_modules/babylon/lib/index.js:3592:12) + at Parser.parseExprAtom (/node_modules/babylon/lib/index.js:6408:22) + at Parser.pp$3.parseExprSubscripts (/node_modules/babylon/lib/index.js:3337:19) + at Parser.pp$3.parseMaybeUnary (/node_modules/babylon/lib/index.js:3317:19) + at Parser.pp$3.parseExprOps (/node_modules/babylon/lib/index.js:3247:19) + at Parser.pp$3.parseMaybeConditional (/node_modules/babylon/lib/index.js:3224:19) + at Parser.pp$3.parseMaybeAssign (/node_modules/babylon/lib/index.js:3187:19) + at Parser.parseMaybeAssign (/node_modules/babylon/lib/index.js:5700:20) +" +`; + +exports[`test fail-flow.js 1`] = ` +"/* @flow */ + +. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (3:0) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$3.parseExprAtom (/node_modules/babylon/lib/index.js:3592:12) + at Parser.parseExprAtom (/node_modules/babylon/lib/index.js:6408:22) + at Parser.pp$3.parseExprSubscripts (/node_modules/babylon/lib/index.js:3337:19) + at Parser.pp$3.parseMaybeUnary (/node_modules/babylon/lib/index.js:3317:19) + at Parser.pp$3.parseExprOps (/node_modules/babylon/lib/index.js:3247:19) + at Parser.pp$3.parseMaybeConditional (/node_modules/babylon/lib/index.js:3224:19) + at Parser.pp$3.parseMaybeAssign (/node_modules/babylon/lib/index.js:3187:19) + at Parser.parseMaybeAssign (/node_modules/babylon/lib/index.js:5700:20) +" +`; + +exports[`test fail-flow-2.js 1`] = ` +"/** + * @flow */ + +. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (4:0) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$3.parseExprAtom (/node_modules/babylon/lib/index.js:3592:12) + at Parser.parseExprAtom (/node_modules/babylon/lib/index.js:6408:22) + at Parser.pp$3.parseExprSubscripts (/node_modules/babylon/lib/index.js:3337:19) + at Parser.pp$3.parseMaybeUnary (/node_modules/babylon/lib/index.js:3317:19) + at Parser.pp$3.parseExprOps (/node_modules/babylon/lib/index.js:3247:19) + at Parser.pp$3.parseMaybeConditional (/node_modules/babylon/lib/index.js:3224:19) + at Parser.pp$3.parseMaybeAssign (/node_modules/babylon/lib/index.js:3187:19) + at Parser.parseMaybeAssign (/node_modules/babylon/lib/index.js:5700:20) +" +`; + +exports[`test no_parse_error.js 1`] = ` +"/* +@flow +*/ + +var x = \'Test\'; +var y = 5 / x; + +var z: { + type: number, + y: string +} = {type: 1, y: \'hey\'}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* +@flow +*/ +var x = \"Test\"; +var y = 5 / x; +var z: { type: number, y: string } = { type: 1, y: \"hey\" }; + +" +`; diff --git a/tests/parse/fail-flow-2.js b/tests/parse/fail-flow-2.js new file mode 100644 index 000000000000..97dfd87f73a3 --- /dev/null +++ b/tests/parse/fail-flow-2.js @@ -0,0 +1,4 @@ +/** + * @flow */ + +. diff --git a/tests/parse/fail-flow.js b/tests/parse/fail-flow.js new file mode 100644 index 000000000000..762b3652bbfe --- /dev/null +++ b/tests/parse/fail-flow.js @@ -0,0 +1,3 @@ +/* @flow */ + +. diff --git a/tests/parse/fail.js b/tests/parse/fail.js new file mode 100644 index 000000000000..9c558e357c41 --- /dev/null +++ b/tests/parse/fail.js @@ -0,0 +1 @@ +. diff --git a/tests/parse/jsfmt.spec.js b/tests/parse/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/parse/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/parse/no_parse_error.js b/tests/parse/no_parse_error.js new file mode 100644 index 000000000000..51fd29947373 --- /dev/null +++ b/tests/parse/no_parse_error.js @@ -0,0 +1,11 @@ +/* +@flow +*/ + +var x = 'Test'; +var y = 5 / x; + +var z: { + type: number, + y: string +} = {type: 1, y: 'hey'}; diff --git a/tests/parse_error_haste/Client.js b/tests/parse_error_haste/Client.js new file mode 100644 index 000000000000..b3827baaa074 --- /dev/null +++ b/tests/parse_error_haste/Client.js @@ -0,0 +1,11 @@ +/** + * Client imports some but not all modules, + * triggering/suppressing parse errors. + * @flow + */ + +// non-flow files should not show parse errors +var A = require("Foo"); // non-Flow file @providesModule Foo +var B = require("./NoProvides"); // non-Flow file + +var C = require("./ParseError"); // Flow file diff --git a/tests/parse_error_haste/NoProvides.js b/tests/parse_error_haste/NoProvides.js new file mode 100644 index 000000000000..16817984e83b --- /dev/null +++ b/tests/parse_error_haste/NoProvides.js @@ -0,0 +1,9 @@ +/** + * Parse errors, imported, not in flow, no provides module. + * No parse errors are raised because it is ignored by flow. + */ +function f(s) { ### // illegal token + return s; +} + +module.exports = { f: f } diff --git a/tests/parse_error_haste/ParseError.js b/tests/parse_error_haste/ParseError.js new file mode 100644 index 000000000000..6766fa50ec34 --- /dev/null +++ b/tests/parse_error_haste/ParseError.js @@ -0,0 +1,6 @@ +// @flow + +function foo() { ### // invalid token +} + +module.exports = foo; diff --git a/tests/parse_error_haste/Provides.js b/tests/parse_error_haste/Provides.js new file mode 100644 index 000000000000..da8e04dcaba6 --- /dev/null +++ b/tests/parse_error_haste/Provides.js @@ -0,0 +1,12 @@ +/** + * Parse errors, imported, not in flow, provides module. + * Should see a parse error in this file, and module + * not found in client. + * @providesModule Foo + * @noflow + */ +function f(s: string): string { ### // illegal token + return s; +} + +module.exports = { f: f } diff --git a/tests/parse_error_haste/Unimported.js b/tests/parse_error_haste/Unimported.js new file mode 100644 index 000000000000..177d59e6ab69 --- /dev/null +++ b/tests/parse_error_haste/Unimported.js @@ -0,0 +1,10 @@ +/** + * Parse errors but not in flow and not imported. + * Should see no parse errors for this file. + * @providesModule Bar + */ +function f(s:string):string { ### // illegal token + return s; +} + +module.exports = { f: f } diff --git a/tests/parse_error_haste/__snapshots__/jsfmt.spec.js.snap b/tests/parse_error_haste/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..0fd5bcf9e3fe --- /dev/null +++ b/tests/parse_error_haste/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,142 @@ +exports[`test Client.js 1`] = ` +"/** + * Client imports some but not all modules, + * triggering/suppressing parse errors. + * @flow + */ + +// non-flow files should not show parse errors +var A = require(\"Foo\"); // non-Flow file @providesModule Foo +var B = require(\"./NoProvides\"); // non-Flow file + +var C = require(\"./ParseError\"); // Flow file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * Client imports some but not all modules, + * triggering/suppressing parse errors. + * @flow + */ +// non-flow files should not show parse errors +var A = require(\"Foo\");// non-Flow file @providesModule Foo +var B = require(\"./NoProvides\");// non-Flow file +var C = require(\"./ParseError\");// Flow file + +" +`; + +exports[`test NoProvides.js 1`] = ` +"/** + * Parse errors, imported, not in flow, no provides module. + * No parse errors are raised because it is ignored by flow. + */ +function f(s) { ### // illegal token + return s; +} + +module.exports = { f: f } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected character \'#\' (5:16) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.getTokenFromCode (/node_modules/babylon/lib/index.js:1065:10) + at Parser.readToken (/node_modules/babylon/lib/index.js:694:19) + at Parser. (/node_modules/babylon/lib/index.js:6441:20) + at Parser.readToken (/node_modules/babylon/lib/index.js:5376:22) + at Parser.nextToken (/node_modules/babylon/lib/index.js:684:19) + at Parser.next (/node_modules/babylon/lib/index.js:609:10) + at Parser.eat (/node_modules/babylon/lib/index.js:616:12) + at Parser.pp.expect (/node_modules/babylon/lib/index.js:1621:15) + at Parser.pp$1.parseBlock (/node_modules/babylon/lib/index.js:2119:8) +" +`; + +exports[`test ParseError.js 1`] = ` +"// @flow + +function foo() { ### // invalid token +} + +module.exports = foo; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected character \'#\' (3:17) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.getTokenFromCode (/node_modules/babylon/lib/index.js:1065:10) + at Parser.readToken (/node_modules/babylon/lib/index.js:694:19) + at Parser. (/node_modules/babylon/lib/index.js:6441:20) + at Parser.readToken (/node_modules/babylon/lib/index.js:5376:22) + at Parser.nextToken (/node_modules/babylon/lib/index.js:684:19) + at Parser.next (/node_modules/babylon/lib/index.js:609:10) + at Parser.eat (/node_modules/babylon/lib/index.js:616:12) + at Parser.pp.expect (/node_modules/babylon/lib/index.js:1621:15) + at Parser.pp$1.parseBlock (/node_modules/babylon/lib/index.js:2119:8) +" +`; + +exports[`test Provides.js 1`] = ` +"/** + * Parse errors, imported, not in flow, provides module. + * Should see a parse error in this file, and module + * not found in client. + * @providesModule Foo + * @noflow + */ +function f(s: string): string { ### // illegal token + return s; +} + +module.exports = { f: f } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected character \'#\' (8:32) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.getTokenFromCode (/node_modules/babylon/lib/index.js:1065:10) + at Parser.readToken (/node_modules/babylon/lib/index.js:694:19) + at Parser. (/node_modules/babylon/lib/index.js:6441:20) + at Parser.readToken (/node_modules/babylon/lib/index.js:5376:22) + at Parser.nextToken (/node_modules/babylon/lib/index.js:684:19) + at Parser.next (/node_modules/babylon/lib/index.js:609:10) + at Parser.eat (/node_modules/babylon/lib/index.js:616:12) + at Parser.pp.expect (/node_modules/babylon/lib/index.js:1621:15) + at Parser.pp$1.parseBlock (/node_modules/babylon/lib/index.js:2119:8) +" +`; + +exports[`test Unimported.js 1`] = ` +"/** + * Parse errors but not in flow and not imported. + * Should see no parse errors for this file. + * @providesModule Bar + */ +function f(s:string):string { ### // illegal token + return s; +} + +module.exports = { f: f } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected character \'#\' (6:30) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.getTokenFromCode (/node_modules/babylon/lib/index.js:1065:10) + at Parser.readToken (/node_modules/babylon/lib/index.js:694:19) + at Parser. (/node_modules/babylon/lib/index.js:6441:20) + at Parser.readToken (/node_modules/babylon/lib/index.js:5376:22) + at Parser.nextToken (/node_modules/babylon/lib/index.js:684:19) + at Parser.next (/node_modules/babylon/lib/index.js:609:10) + at Parser.eat (/node_modules/babylon/lib/index.js:616:12) + at Parser.pp.expect (/node_modules/babylon/lib/index.js:1621:15) + at Parser.pp$1.parseBlock (/node_modules/babylon/lib/index.js:2119:8) +" +`; diff --git a/tests/parse_error_haste/jsfmt.spec.js b/tests/parse_error_haste/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/parse_error_haste/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/parse_error_node/Client.js b/tests/parse_error_node/Client.js new file mode 100644 index 000000000000..45ea11195d28 --- /dev/null +++ b/tests/parse_error_node/Client.js @@ -0,0 +1,10 @@ +/** + * Client imports some but not all modules, + * triggering/suppressing parse errors. + * @flow + */ + +// non-flow files should not give parse errors +var A = require("./Imported"); // non-Flow file @providesModule Foo + +var B = require("./ParseError"); // Flow file diff --git a/tests/parse_error_node/Imported.js b/tests/parse_error_node/Imported.js new file mode 100644 index 000000000000..16817984e83b --- /dev/null +++ b/tests/parse_error_node/Imported.js @@ -0,0 +1,9 @@ +/** + * Parse errors, imported, not in flow, no provides module. + * No parse errors are raised because it is ignored by flow. + */ +function f(s) { ### // illegal token + return s; +} + +module.exports = { f: f } diff --git a/tests/parse_error_node/ParseError.js b/tests/parse_error_node/ParseError.js new file mode 100644 index 000000000000..6766fa50ec34 --- /dev/null +++ b/tests/parse_error_node/ParseError.js @@ -0,0 +1,6 @@ +// @flow + +function foo() { ### // invalid token +} + +module.exports = foo; diff --git a/tests/parse_error_node/Unimported.js b/tests/parse_error_node/Unimported.js new file mode 100644 index 000000000000..177d59e6ab69 --- /dev/null +++ b/tests/parse_error_node/Unimported.js @@ -0,0 +1,10 @@ +/** + * Parse errors but not in flow and not imported. + * Should see no parse errors for this file. + * @providesModule Bar + */ +function f(s:string):string { ### // illegal token + return s; +} + +module.exports = { f: f } diff --git a/tests/parse_error_node/__snapshots__/jsfmt.spec.js.snap b/tests/parse_error_node/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..bb6c5eb1fac5 --- /dev/null +++ b/tests/parse_error_node/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,108 @@ +exports[`test Client.js 1`] = ` +"/** + * Client imports some but not all modules, + * triggering/suppressing parse errors. + * @flow + */ + +// non-flow files should not give parse errors +var A = require(\"./Imported\"); // non-Flow file @providesModule Foo + +var B = require(\"./ParseError\"); // Flow file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * Client imports some but not all modules, + * triggering/suppressing parse errors. + * @flow + */ +// non-flow files should not give parse errors +var A = require(\"./Imported\");// non-Flow file @providesModule Foo +var B = require(\"./ParseError\");// Flow file + +" +`; + +exports[`test Imported.js 1`] = ` +"/** + * Parse errors, imported, not in flow, no provides module. + * No parse errors are raised because it is ignored by flow. + */ +function f(s) { ### // illegal token + return s; +} + +module.exports = { f: f } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected character \'#\' (5:16) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.getTokenFromCode (/node_modules/babylon/lib/index.js:1065:10) + at Parser.readToken (/node_modules/babylon/lib/index.js:694:19) + at Parser. (/node_modules/babylon/lib/index.js:6441:20) + at Parser.readToken (/node_modules/babylon/lib/index.js:5376:22) + at Parser.nextToken (/node_modules/babylon/lib/index.js:684:19) + at Parser.next (/node_modules/babylon/lib/index.js:609:10) + at Parser.eat (/node_modules/babylon/lib/index.js:616:12) + at Parser.pp.expect (/node_modules/babylon/lib/index.js:1621:15) + at Parser.pp$1.parseBlock (/node_modules/babylon/lib/index.js:2119:8) +" +`; + +exports[`test ParseError.js 1`] = ` +"// @flow + +function foo() { ### // invalid token +} + +module.exports = foo; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected character \'#\' (3:17) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.getTokenFromCode (/node_modules/babylon/lib/index.js:1065:10) + at Parser.readToken (/node_modules/babylon/lib/index.js:694:19) + at Parser. (/node_modules/babylon/lib/index.js:6441:20) + at Parser.readToken (/node_modules/babylon/lib/index.js:5376:22) + at Parser.nextToken (/node_modules/babylon/lib/index.js:684:19) + at Parser.next (/node_modules/babylon/lib/index.js:609:10) + at Parser.eat (/node_modules/babylon/lib/index.js:616:12) + at Parser.pp.expect (/node_modules/babylon/lib/index.js:1621:15) + at Parser.pp$1.parseBlock (/node_modules/babylon/lib/index.js:2119:8) +" +`; + +exports[`test Unimported.js 1`] = ` +"/** + * Parse errors but not in flow and not imported. + * Should see no parse errors for this file. + * @providesModule Bar + */ +function f(s:string):string { ### // illegal token + return s; +} + +module.exports = { f: f } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected character \'#\' (6:30) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.getTokenFromCode (/node_modules/babylon/lib/index.js:1065:10) + at Parser.readToken (/node_modules/babylon/lib/index.js:694:19) + at Parser. (/node_modules/babylon/lib/index.js:6441:20) + at Parser.readToken (/node_modules/babylon/lib/index.js:5376:22) + at Parser.nextToken (/node_modules/babylon/lib/index.js:684:19) + at Parser.next (/node_modules/babylon/lib/index.js:609:10) + at Parser.eat (/node_modules/babylon/lib/index.js:616:12) + at Parser.pp.expect (/node_modules/babylon/lib/index.js:1621:15) + at Parser.pp$1.parseBlock (/node_modules/babylon/lib/index.js:2119:8) +" +`; diff --git a/tests/parse_error_node/jsfmt.spec.js b/tests/parse_error_node/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/parse_error_node/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/path/__snapshots__/jsfmt.spec.js.snap b/tests/path/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..a2a3fe378afd --- /dev/null +++ b/tests/path/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,18 @@ +exports[`test while.js 1`] = ` +"var x = 1; +while(typeof x == "number" || typeof x == "string") { + x = x + 1; + if (true) x = ""; +} +var z:number = x; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var x = 1; +while (typeof x == "number" || typeof x == "string") { + x = x + 1; + if (true) + x = ""; +} +var z: number = x; + +" +`; diff --git a/tests/path/jsfmt.spec.js b/tests/path/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/path/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/path/while.js b/tests/path/while.js new file mode 100644 index 000000000000..83c943b5faf5 --- /dev/null +++ b/tests/path/while.js @@ -0,0 +1,6 @@ +var x = 1; +while(typeof x == "number" || typeof x == "string") { + x = x + 1; + if (true) x = ""; +} +var z:number = x; diff --git a/tests/plsummit/__snapshots__/jsfmt.spec.js.snap b/tests/plsummit/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..ffd3dd6c2c29 --- /dev/null +++ b/tests/plsummit/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,132 @@ +exports[`test arrays.js 1`] = ` +"function foo(x) { return [x, x > 0, \"number \" + x]; } + +var [n, b, s] = foo(42); +n * s.length; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +function foo(x) { + return [ x, x > 0, \"number \" + x ]; +} +var [ n, b, s ] = foo(42); +n * s.length; + +" +`; + +exports[`test export_class.js 1`] = ` +"class C { + x: number; + constructor(x: number) { this.x = x; } +} + +module.exports = C; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class C { + x: number; + constructor(x: number) { + this.x = x; + } +} +module.exports = C; + +" +`; + +exports[`test generics.js 1`] = ` +"/* @flow */ + +var r: number = 0; +function foo(x: X): X { r = x; return x; } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:340:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test import_class.js 1`] = ` +"var C = require(\'./export_class\'); + +var c = new C(\"\"); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var C = require(\"./export_class\"); +var c = new C(\"\"); + +" +`; + +exports[`test locals.js 1`] = ` +"/* @flow */ + +function foo() { + var x = 0; + var y = x; +} + +function bar(x: ?string): number { + if (x == null) x = \"\"; + return x.length; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +function foo() { + var x = 0; + var y = x; +} +function bar(x: ?string): number { + if (x == null) + x = \"\"; + return x.length; +} + +" +`; + +exports[`test objects.js 1`] = ` +"function C() { this.x = 0; } +C.prototype.foo = function() { return this.x; } + +var c = new C(); +var x: string = c.foo(); + +function foo() { return this.y; } +function bar() { return this.foo(); } +var o = { y: \"\", foo: foo, bar: bar }; +var o2 = { y: 0, foo: foo, bar: bar }; + +o.bar(); +var y: number = o2.bar(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +function C() { + this.x = 0; +} +C.prototype.foo = function() { + return this.x; +}; +var c = new C(); +var x: string = c.foo(); +function foo() { + return this.y; +} +function bar() { + return this.foo(); +} +var o = { y: \"\", foo: foo, bar: bar }; +var o2 = { y: 0, foo: foo, bar: bar }; +o.bar(); +var y: number = o2.bar(); + +" +`; diff --git a/tests/plsummit/arrays.js b/tests/plsummit/arrays.js new file mode 100644 index 000000000000..9ed4c7b22f49 --- /dev/null +++ b/tests/plsummit/arrays.js @@ -0,0 +1,4 @@ +function foo(x) { return [x, x > 0, "number " + x]; } + +var [n, b, s] = foo(42); +n * s.length; diff --git a/tests/plsummit/export_class.js b/tests/plsummit/export_class.js new file mode 100644 index 000000000000..8f796fb05385 --- /dev/null +++ b/tests/plsummit/export_class.js @@ -0,0 +1,6 @@ +class C { + x: number; + constructor(x: number) { this.x = x; } +} + +module.exports = C; diff --git a/tests/plsummit/generics.js b/tests/plsummit/generics.js new file mode 100644 index 000000000000..ec4d476134da --- /dev/null +++ b/tests/plsummit/generics.js @@ -0,0 +1,4 @@ +/* @flow */ + +var r: number = 0; +function foo(x: X): X { r = x; return x; } diff --git a/tests/plsummit/import_class.js b/tests/plsummit/import_class.js new file mode 100644 index 000000000000..4a361b1515cd --- /dev/null +++ b/tests/plsummit/import_class.js @@ -0,0 +1,3 @@ +var C = require('./export_class'); + +var c = new C(""); diff --git a/tests/plsummit/jsfmt.spec.js b/tests/plsummit/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/plsummit/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/plsummit/locals.js b/tests/plsummit/locals.js new file mode 100644 index 000000000000..08f150b95c56 --- /dev/null +++ b/tests/plsummit/locals.js @@ -0,0 +1,11 @@ +/* @flow */ + +function foo() { + var x = 0; + var y = x; +} + +function bar(x: ?string): number { + if (x == null) x = ""; + return x.length; +} diff --git a/tests/plsummit/objects.js b/tests/plsummit/objects.js new file mode 100644 index 000000000000..29af3f72b10f --- /dev/null +++ b/tests/plsummit/objects.js @@ -0,0 +1,13 @@ +function C() { this.x = 0; } +C.prototype.foo = function() { return this.x; } + +var c = new C(); +var x: string = c.foo(); + +function foo() { return this.y; } +function bar() { return this.foo(); } +var o = { y: "", foo: foo, bar: bar }; +var o2 = { y: 0, foo: foo, bar: bar }; + +o.bar(); +var y: number = o2.bar(); diff --git a/tests/poly/__snapshots__/jsfmt.spec.js.snap b/tests/poly/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..4a4d1b086938 --- /dev/null +++ b/tests/poly/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,192 @@ +exports[`test annot.js 1`] = ` +"class A { } +new A; // OK, implicitly inferred type args +class B extends A { } // OK, same as above + +function foo(b): A { // ok but unsafe, caller may assume any type arg + return b ? (new A: A): (new A: A); +} + +function bar(): A<*> { // error, * can\'t be {} and {x: string} at the same time + return (new A: A<{}>) || (new A: A<{x: string}>); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1172:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test implicit_bounded_instantiation.js 1`] = ` +"// @flow + +class Base {} +class Middle extends Base {} +class Child extends Middle {} + +class C { + meth(a: T): T { + return a; + } +} + +// T is implicitly (bounded by) Middle in constructor call if not provided. +// Explicit type arg is required in annotation - here a wildcard captures it. +var a: C<*> = new C(); + +a.meth(new Middle()); +a.meth(new Child()); +a.meth(42); // Error: number ~> Middle +a.meth(new Base()); // Error: Base ~> Middle +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1172:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test issue-1029.js 1`] = ` +"// @flow + +// naive unification causes combinatorial explosion here, +// effectively hangs + +declare type Box = { + map1(f: (x: T) => U): Box; + map2(f: (x: T) => U): Box; + map3(f: (x: T) => U): Box; + map4(f: (x: T) => U): Box; + map5(f: (x: T) => U): Box; +} + +declare var bool: Box; + +declare function unbox(box: Box): A + +unbox(bool); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1479:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test poly.js 1`] = ` +"class Foo { + x:T; + constructor(x:T) { this.x = x; } +} + +function bar(foo:Foo,y:S):Foo { return new Foo(y); } + +var P = { + bar: bar +} + +declare var Q: { + bar(foo:Foo,y:S):Foo; +} + +var foo = new Foo(0); +var x:string = foo.x; +var z:Foo = Q.bar(foo,\"\"); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1172:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test.js 1`] = ` +"class C { + foo(x: X): X { return x; } + foo_(x: X): number { return x; } + bar(x: X): X { return x; } + qux(x: number): number { return x; } +} +class D extends C { + foo(x: number): number { return x; } // error (specialization, see below) + foo_(x: number): number { return x; } // OK, but only because the overridden foo accepts no more than number and returns exactly number + bar(x: X): X { return x; } // OK + qux(x: X): X { return x; } // OK (generalization) +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class C { + foo(x: X): X { + return x; + } + foo_(x: X): number { + return x; + } + bar(x: X): X { + return x; + } + qux(x: number): number { + return x; + } +} +class D extends C { + foo(x: number): number { + return x; + }// error (specialization, see below) + foo_(x: number): number { + return x; + }// OK, but only because the overridden foo accepts no more than number and returns exactly number + bar(x: X): X { + return x; + }// OK + qux(x: X): X { + return x; + }// OK (generalization) +} + +" +`; diff --git a/tests/poly/annot.js b/tests/poly/annot.js new file mode 100644 index 000000000000..f5feef466989 --- /dev/null +++ b/tests/poly/annot.js @@ -0,0 +1,11 @@ +class A { } +new A; // OK, implicitly inferred type args +class B extends A { } // OK, same as above + +function foo(b): A { // ok but unsafe, caller may assume any type arg + return b ? (new A: A): (new A: A); +} + +function bar(): A<*> { // error, * can't be {} and {x: string} at the same time + return (new A: A<{}>) || (new A: A<{x: string}>); +} diff --git a/tests/poly/implicit_bounded_instantiation.js b/tests/poly/implicit_bounded_instantiation.js new file mode 100644 index 000000000000..e684afff4d6b --- /dev/null +++ b/tests/poly/implicit_bounded_instantiation.js @@ -0,0 +1,20 @@ +// @flow + +class Base {} +class Middle extends Base {} +class Child extends Middle {} + +class C { + meth(a: T): T { + return a; + } +} + +// T is implicitly (bounded by) Middle in constructor call if not provided. +// Explicit type arg is required in annotation - here a wildcard captures it. +var a: C<*> = new C(); + +a.meth(new Middle()); +a.meth(new Child()); +a.meth(42); // Error: number ~> Middle +a.meth(new Base()); // Error: Base ~> Middle diff --git a/tests/poly/issue-1029.js b/tests/poly/issue-1029.js new file mode 100644 index 000000000000..5b7ac33f3823 --- /dev/null +++ b/tests/poly/issue-1029.js @@ -0,0 +1,18 @@ +// @flow + +// naive unification causes combinatorial explosion here, +// effectively hangs + +declare type Box = { + map1(f: (x: T) => U): Box; + map2(f: (x: T) => U): Box; + map3(f: (x: T) => U): Box; + map4(f: (x: T) => U): Box; + map5(f: (x: T) => U): Box; +} + +declare var bool: Box; + +declare function unbox(box: Box): A + +unbox(bool); diff --git a/tests/poly/jsfmt.spec.js b/tests/poly/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/poly/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/poly/poly.js b/tests/poly/poly.js new file mode 100644 index 000000000000..be8b8014784e --- /dev/null +++ b/tests/poly/poly.js @@ -0,0 +1,18 @@ +class Foo { + x:T; + constructor(x:T) { this.x = x; } +} + +function bar(foo:Foo,y:S):Foo { return new Foo(y); } + +var P = { + bar: bar +} + +declare var Q: { + bar(foo:Foo,y:S):Foo; +} + +var foo = new Foo(0); +var x:string = foo.x; +var z:Foo = Q.bar(foo,""); diff --git a/tests/poly/test.js b/tests/poly/test.js new file mode 100644 index 000000000000..9e72bab6ada9 --- /dev/null +++ b/tests/poly/test.js @@ -0,0 +1,12 @@ +class C { + foo(x: X): X { return x; } + foo_(x: X): number { return x; } + bar(x: X): X { return x; } + qux(x: number): number { return x; } +} +class D extends C { + foo(x: number): number { return x; } // error (specialization, see below) + foo_(x: number): number { return x; } // OK, but only because the overridden foo accepts no more than number and returns exactly number + bar(x: X): X { return x; } // OK + qux(x: X): X { return x; } // OK (generalization) +} diff --git a/tests/poly_class_export/A.js b/tests/poly_class_export/A.js new file mode 100644 index 000000000000..3a6aecfd9ff5 --- /dev/null +++ b/tests/poly_class_export/A.js @@ -0,0 +1,7 @@ +// @flow + +class A { + x: T +} + +module.exports = A; diff --git a/tests/poly_class_export/B.js b/tests/poly_class_export/B.js new file mode 100644 index 000000000000..d4282e925827 --- /dev/null +++ b/tests/poly_class_export/B.js @@ -0,0 +1,11 @@ +// @flow + +let A = require('./A'); + +class B extends A { + constructor() { + super(); + } +} + +module.exports = new B(); diff --git a/tests/poly_class_export/C.js b/tests/poly_class_export/C.js new file mode 100644 index 000000000000..e017e2c83c91 --- /dev/null +++ b/tests/poly_class_export/C.js @@ -0,0 +1,14 @@ +// @flow + +// This test exports a function whose return type is the class's `this` type. +// It should be inferred (no annotation required). + +class Foo { + foo(): this { + return this; + } +} + +export function f(x: Foo) { + return x.foo(); +} diff --git a/tests/poly_class_export/__snapshots__/jsfmt.spec.js.snap b/tests/poly_class_export/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..038cb4d4f12d --- /dev/null +++ b/tests/poly_class_export/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,88 @@ +exports[`test A.js 1`] = ` +"// @flow + +class A { + x: T +} + +module.exports = A; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1172:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test B.js 1`] = ` +"// @flow + +let A = require(\'./A\'); + +class B extends A { + constructor() { + super(); + } +} + +module.exports = new B(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1180:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test C.js 1`] = ` +"// @flow + +// This test exports a function whose return type is the class\'s \`this\` type. +// It should be inferred (no annotation required). + +class Foo { + foo(): this { + return this; + } +} + +export function f(x: Foo) { + return x.foo(); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +// This test exports a function whose return type is the class\'s \`this\` type. +// It should be inferred (no annotation required). +class Foo { + foo(): this { + return this; + } +} +export function f(x: Foo) { + return x.foo(); +} + +" +`; diff --git a/tests/poly_class_export/jsfmt.spec.js b/tests/poly_class_export/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/poly_class_export/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/poly_overload/decls/__snapshots__/jsfmt.spec.js.snap b/tests/poly_overload/decls/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..ada571d251c5 --- /dev/null +++ b/tests/poly_overload/decls/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,35 @@ +exports[`test typescript-deferred.js 1`] = ` +"interface Some {} +interface Other { x: X; } +interface None {} +interface Nada { y: Y } +interface A { + foo(s: Some, e: None): A; + foo(s: Some, e: Nada): A; + foo(s: Other, e: None): A; + foo(s: Other, e: Nada): A; +} +interface B extends A { + foo(s: Some, e: None): B; + foo(s: Some, e: Nada): B; + foo(s: Other, e: None): B; + foo(s: Other, e: Nada): B; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1384:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/poly_overload/decls/jsfmt.spec.js b/tests/poly_overload/decls/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/poly_overload/decls/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/poly_overload/decls/typescript-deferred.js b/tests/poly_overload/decls/typescript-deferred.js new file mode 100644 index 000000000000..cf6fe780dc37 --- /dev/null +++ b/tests/poly_overload/decls/typescript-deferred.js @@ -0,0 +1,16 @@ +interface Some {} +interface Other { x: X; } +interface None {} +interface Nada { y: Y } +interface A { + foo(s: Some, e: None): A; + foo(s: Some, e: Nada): A; + foo(s: Other, e: None): A; + foo(s: Other, e: Nada): A; +} +interface B extends A { + foo(s: Some, e: None): B; + foo(s: Some, e: Nada): B; + foo(s: Other, e: None): B; + foo(s: Other, e: Nada): B; +} diff --git a/tests/predicates-abstract/__snapshots__/jsfmt.spec.js.snap b/tests/predicates-abstract/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..9bb9fc127f51 --- /dev/null +++ b/tests/predicates-abstract/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,278 @@ +exports[`test filter.js 1`] = ` +"// @flow + +// Filter the contents of an array + +declare function my_filter>(v: Array, cb: P): Array<$Refine>; + +declare var arr: Array; +const barr = my_filter(arr, is_string); +(barr: Array); + +function is_string(x): %checks { + return typeof x === \"string\"; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (11:23) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$7.flowParsePrimaryType (/node_modules/babylon/lib/index.js:5099:8) + at Parser.pp$7.flowParsePostfixType (/node_modules/babylon/lib/index.js:5105:19) + at Parser.pp$7.flowParsePrefixType (/node_modules/babylon/lib/index.js:5122:17) + at Parser.pp$7.flowParseAnonFunctionWithoutParens (/node_modules/babylon/lib/index.js:5127:20) + at Parser.pp$7.flowParseIntersectionType (/node_modules/babylon/lib/index.js:5141:19) + at Parser.pp$7.flowParseUnionType (/node_modules/babylon/lib/index.js:5151:19) + at Parser.pp$7.flowParseType (/node_modules/babylon/lib/index.js:5162:19) + at Parser.pp$7.flowParseTypeInitialiser (/node_modules/babylon/lib/index.js:4430:19) +" +`; + +exports[`test filter-union.js 1`] = ` +"// @flow + +// Filter the contents of an array + + +declare function my_filter>(v: Array, cb: P): Array<$Refine>; + +type A = { kind: \'A\', u: number } +type B = { kind: \'B\', v: string } +type C = { kind: \'C\', y: boolean } +type D = { kind: \'D\', x: boolean } +type E = { kind: \'E\', y: boolean } + +declare var ab: Array; + +(my_filter(ab, (x): %checks => x.kind === \'A\'): Array); // OK +(my_filter(ab, (x): %checks => x.kind !== \'A\'): Array); // OK +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (16:20) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$7.flowParsePrimaryType (/node_modules/babylon/lib/index.js:5099:8) + at Parser.pp$7.flowParsePostfixType (/node_modules/babylon/lib/index.js:5105:19) + at Parser.pp$7.flowParsePrefixType (/node_modules/babylon/lib/index.js:5122:17) + at Parser.pp$7.flowParseAnonFunctionWithoutParens (/node_modules/babylon/lib/index.js:5127:20) + at Parser.pp$7.flowParseIntersectionType (/node_modules/babylon/lib/index.js:5141:19) + at Parser.pp$7.flowParseUnionType (/node_modules/babylon/lib/index.js:5151:19) + at Parser.pp$7.flowParseType (/node_modules/babylon/lib/index.js:5162:19) + at Parser.pp$7.flowParseTypeInitialiser (/node_modules/babylon/lib/index.js:4430:19) +" +`; + +exports[`test refine.js 1`] = ` +"// @flow + +/* + $Pred is an \"abstract predicate type\", i.e. denotes a (function) type that + refines N variables. So if \`cb\` is a function, then it should be refining + exactly N argument. It is abstract in that we do not need to specify: + (a) which variables are going to be refined (just the number), or (b) what + exactly the refinement (predicate) is going to be. + + $Refine is a refinement type, that refines type T with the k-th + argument that gets refined by an abstract preficate type P. +*/ +declare function refine>(v: T, cb: P): $Refine; +// function refine(v, cb) +// { if (cb(v)) { return v; } else { throw new Error(); } } + +/* + Use case +*/ +declare var a: mixed; +var b = refine(a, is_string); +(b: string); + +declare function refine_fst>(v: T, w: T, cb: P): $Refine; +// function refine_fst(v, w, cb) +// { if (cb(v, w)) { return v; } else { throw new Error(); } } + +declare var c: mixed; +declare var d: mixed; + +var e = refine2(c, d, is_string_and_number); +(e: string); + + +declare function refine2>(v: T, w: T, cb: P): $Refine; + +// function refine_fst(v, w, cb) +// { if (cb(v, w)) { return w; } else { throw new Error(); } } + +function is_string(x): boolean %checks { + return typeof x === \"string\"; +} + +function is_string_and_number(x, y): %checks { + return typeof x === \"string\" && typeof y === \"number\"; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected { (40:31) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.expect (/node_modules/babylon/lib/index.js:1621:33) + at Parser.pp$1.parseBlock (/node_modules/babylon/lib/index.js:2119:8) + at Parser.pp$3.parseFunctionBody (/node_modules/babylon/lib/index.js:4004:22) + at Parser.parseFunctionBody (/node_modules/babylon/lib/index.js:5211:20) + at Parser.pp$1.parseFunction (/node_modules/babylon/lib/index.js:2257:8) + at Parser.pp$1.parseFunctionStatement (/node_modules/babylon/lib/index.js:1926:15) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1712:19) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) +" +`; + +exports[`test sanity-filter.js 1`] = ` +"// @flow + +declare function my_filter>(v: Array, cb: P): Array<$Refine>; + +// Sanity check A: filtering the wrong type +declare var a: Array; +const b = my_filter(a, is_string); +(b: Array); + + +// Sanity check B: Passing non-predicate function to filter +declare var c: Array; +const d = my_filter(c, is_string_regular); +(d: Array); + +function is_string(x): boolean %checks { + return typeof x === \"string\"; +} + +function is_string_regular(x): boolean { + return typeof x === \"string\"; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected { (16:31) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.expect (/node_modules/babylon/lib/index.js:1621:33) + at Parser.pp$1.parseBlock (/node_modules/babylon/lib/index.js:2119:8) + at Parser.pp$3.parseFunctionBody (/node_modules/babylon/lib/index.js:4004:22) + at Parser.parseFunctionBody (/node_modules/babylon/lib/index.js:5211:20) + at Parser.pp$1.parseFunction (/node_modules/babylon/lib/index.js:2257:8) + at Parser.pp$1.parseFunctionStatement (/node_modules/babylon/lib/index.js:1926:15) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1712:19) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) +" +`; + +exports[`test sanity-filter-union.js 1`] = ` +"// @flow + +// Filter the contents of an array + + +declare function my_filter>(v: Array, cb: P): Array<$Refine>; + +type A = { kind: \'A\', u: number } +type B = { kind: \'B\', v: string } +type C = { kind: \'C\', y: boolean } +type D = { kind: \'D\', x: boolean } +type E = { kind: \'E\', y: boolean } + +declare var ab: Array; + +(my_filter(ab, (x): %checks => x.kind === \'A\'): Array); // ERROR +(my_filter(ab, (x): %checks => x.kind !== \'A\'): Array); // ERROR +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (16:20) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$7.flowParsePrimaryType (/node_modules/babylon/lib/index.js:5099:8) + at Parser.pp$7.flowParsePostfixType (/node_modules/babylon/lib/index.js:5105:19) + at Parser.pp$7.flowParsePrefixType (/node_modules/babylon/lib/index.js:5122:17) + at Parser.pp$7.flowParseAnonFunctionWithoutParens (/node_modules/babylon/lib/index.js:5127:20) + at Parser.pp$7.flowParseIntersectionType (/node_modules/babylon/lib/index.js:5141:19) + at Parser.pp$7.flowParseUnionType (/node_modules/babylon/lib/index.js:5151:19) + at Parser.pp$7.flowParseType (/node_modules/babylon/lib/index.js:5162:19) + at Parser.pp$7.flowParseTypeInitialiser (/node_modules/babylon/lib/index.js:4430:19) +" +`; + +exports[`test sanity-refine.js 1`] = ` +"// @flow + +// Sanity check A: the refinment position index is outside of the allowed range +declare function refine>(v: T, cb: P): $Refine; + +declare var a: mixed; +var b = refine(a, is_string); // ERROR: index out of bounds +(b: string); + + +// Sanity check B: refine2 expects a function that accepts 3 arguments but +// it is called with a function that takes 2 +declare var c: mixed; +declare var d: mixed; +declare var e: mixed; + +declare function refine3>(u: T, v: T, w: T, cb: P): $Refine; + +var e = refine3(c, d, e, is_string_and_number); +(e: string); + +function is_string_and_number(x, y): %checks { + return typeof x === \"string\" && typeof y === \"number\"; +} + + +// Sanity check C: expecting a predicate function but passed a non-predicate one +var e = refine(a, is_string_regular); // ERROR: is_string_regular is not a + // predicate function +(e: number); + +//////////////////////////////////////////////////////////////////////////////// + +function is_string(x): %checks { + return typeof x === \"string\"; +} + +function is_string_regular(x) { + return typeof x === \"string\"; +} + +function is_string_and_number(x, y): %checks { + return typeof x === \"string\" && typeof y === \"number\"; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (22:37) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$7.flowParsePrimaryType (/node_modules/babylon/lib/index.js:5099:8) + at Parser.pp$7.flowParsePostfixType (/node_modules/babylon/lib/index.js:5105:19) + at Parser.pp$7.flowParsePrefixType (/node_modules/babylon/lib/index.js:5122:17) + at Parser.pp$7.flowParseAnonFunctionWithoutParens (/node_modules/babylon/lib/index.js:5127:20) + at Parser.pp$7.flowParseIntersectionType (/node_modules/babylon/lib/index.js:5141:19) + at Parser.pp$7.flowParseUnionType (/node_modules/babylon/lib/index.js:5151:19) + at Parser.pp$7.flowParseType (/node_modules/babylon/lib/index.js:5162:19) + at Parser.pp$7.flowParseTypeInitialiser (/node_modules/babylon/lib/index.js:4430:19) +" +`; diff --git a/tests/predicates-abstract/filter-union.js b/tests/predicates-abstract/filter-union.js new file mode 100644 index 000000000000..081cd6ebc161 --- /dev/null +++ b/tests/predicates-abstract/filter-union.js @@ -0,0 +1,17 @@ +// @flow + +// Filter the contents of an array + + +declare function my_filter>(v: Array, cb: P): Array<$Refine>; + +type A = { kind: 'A', u: number } +type B = { kind: 'B', v: string } +type C = { kind: 'C', y: boolean } +type D = { kind: 'D', x: boolean } +type E = { kind: 'E', y: boolean } + +declare var ab: Array; + +(my_filter(ab, (x): %checks => x.kind === 'A'): Array); // OK +(my_filter(ab, (x): %checks => x.kind !== 'A'): Array); // OK diff --git a/tests/predicates-abstract/filter.js b/tests/predicates-abstract/filter.js new file mode 100644 index 000000000000..c14d10737b62 --- /dev/null +++ b/tests/predicates-abstract/filter.js @@ -0,0 +1,13 @@ +// @flow + +// Filter the contents of an array + +declare function my_filter>(v: Array, cb: P): Array<$Refine>; + +declare var arr: Array; +const barr = my_filter(arr, is_string); +(barr: Array); + +function is_string(x): %checks { + return typeof x === "string"; +} diff --git a/tests/predicates-abstract/jsfmt.spec.js b/tests/predicates-abstract/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/predicates-abstract/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/predicates-abstract/refine.js b/tests/predicates-abstract/refine.js new file mode 100644 index 000000000000..bde9c92f3e60 --- /dev/null +++ b/tests/predicates-abstract/refine.js @@ -0,0 +1,46 @@ +// @flow + +/* + $Pred is an "abstract predicate type", i.e. denotes a (function) type that + refines N variables. So if `cb` is a function, then it should be refining + exactly N argument. It is abstract in that we do not need to specify: + (a) which variables are going to be refined (just the number), or (b) what + exactly the refinement (predicate) is going to be. + + $Refine is a refinement type, that refines type T with the k-th + argument that gets refined by an abstract preficate type P. +*/ +declare function refine>(v: T, cb: P): $Refine; +// function refine(v, cb) +// { if (cb(v)) { return v; } else { throw new Error(); } } + +/* + Use case +*/ +declare var a: mixed; +var b = refine(a, is_string); +(b: string); + +declare function refine_fst>(v: T, w: T, cb: P): $Refine; +// function refine_fst(v, w, cb) +// { if (cb(v, w)) { return v; } else { throw new Error(); } } + +declare var c: mixed; +declare var d: mixed; + +var e = refine2(c, d, is_string_and_number); +(e: string); + + +declare function refine2>(v: T, w: T, cb: P): $Refine; + +// function refine_fst(v, w, cb) +// { if (cb(v, w)) { return w; } else { throw new Error(); } } + +function is_string(x): boolean %checks { + return typeof x === "string"; +} + +function is_string_and_number(x, y): %checks { + return typeof x === "string" && typeof y === "number"; +} diff --git a/tests/predicates-abstract/sanity-filter-union.js b/tests/predicates-abstract/sanity-filter-union.js new file mode 100644 index 000000000000..a3eb6d03be69 --- /dev/null +++ b/tests/predicates-abstract/sanity-filter-union.js @@ -0,0 +1,17 @@ +// @flow + +// Filter the contents of an array + + +declare function my_filter>(v: Array, cb: P): Array<$Refine>; + +type A = { kind: 'A', u: number } +type B = { kind: 'B', v: string } +type C = { kind: 'C', y: boolean } +type D = { kind: 'D', x: boolean } +type E = { kind: 'E', y: boolean } + +declare var ab: Array; + +(my_filter(ab, (x): %checks => x.kind === 'A'): Array); // ERROR +(my_filter(ab, (x): %checks => x.kind !== 'A'): Array); // ERROR diff --git a/tests/predicates-abstract/sanity-filter.js b/tests/predicates-abstract/sanity-filter.js new file mode 100644 index 000000000000..52ea0aaf6153 --- /dev/null +++ b/tests/predicates-abstract/sanity-filter.js @@ -0,0 +1,22 @@ +// @flow + +declare function my_filter>(v: Array, cb: P): Array<$Refine>; + +// Sanity check A: filtering the wrong type +declare var a: Array; +const b = my_filter(a, is_string); +(b: Array); + + +// Sanity check B: Passing non-predicate function to filter +declare var c: Array; +const d = my_filter(c, is_string_regular); +(d: Array); + +function is_string(x): boolean %checks { + return typeof x === "string"; +} + +function is_string_regular(x): boolean { + return typeof x === "string"; +} diff --git a/tests/predicates-abstract/sanity-refine.js b/tests/predicates-abstract/sanity-refine.js new file mode 100644 index 000000000000..2edc62b47c8d --- /dev/null +++ b/tests/predicates-abstract/sanity-refine.js @@ -0,0 +1,44 @@ +// @flow + +// Sanity check A: the refinment position index is outside of the allowed range +declare function refine>(v: T, cb: P): $Refine; + +declare var a: mixed; +var b = refine(a, is_string); // ERROR: index out of bounds +(b: string); + + +// Sanity check B: refine2 expects a function that accepts 3 arguments but +// it is called with a function that takes 2 +declare var c: mixed; +declare var d: mixed; +declare var e: mixed; + +declare function refine3>(u: T, v: T, w: T, cb: P): $Refine; + +var e = refine3(c, d, e, is_string_and_number); +(e: string); + +function is_string_and_number(x, y): %checks { + return typeof x === "string" && typeof y === "number"; +} + + +// Sanity check C: expecting a predicate function but passed a non-predicate one +var e = refine(a, is_string_regular); // ERROR: is_string_regular is not a + // predicate function +(e: number); + +//////////////////////////////////////////////////////////////////////////////// + +function is_string(x): %checks { + return typeof x === "string"; +} + +function is_string_regular(x) { + return typeof x === "string"; +} + +function is_string_and_number(x, y): %checks { + return typeof x === "string" && typeof y === "number"; +} diff --git a/tests/predicates-declared/__snapshots__/jsfmt.spec.js.snap b/tests/predicates-declared/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..5b38e142c50a --- /dev/null +++ b/tests/predicates-declared/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,473 @@ +exports[`test function-bind.js 1`] = ` +"// @flow + +// Sanity checks: +// - use of bind in a position of a function predicate. +// (This case should fall through, as method calls +// are currently not supported.) The original behavior +// (including \`havoc\`) should be retained. + +class C { + m() { + return true; + } + a: 1; + + n() { + if(this.m.bind(this)) { + this.a; + } + } +} + +declare var m: Function; +const o = { a: 1 }; + +if (m.bind(o)) { + o.a; +} + + +class D { + m: Function; + + n() { + if(this.m({})) { } + } +} + +declare var m: Function; +const x = \"\"; +if (m.bind(this)(x)) { } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/ast-types/lib/types.js:60 + throw new Error(str + \" does not match type \" + this); + ^ + +Error: {type: NumericLiteralTypeAnnotation, start: 288, end: 289, loc: [object Object], value: 1, extra: [object Object]} does not match type Printable + at Type.Tp.assert (/node_modules/ast-types/lib/types.js:60:19) + at genericPrintNoParens (/src/printer.js:221:24) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) +" +`; + +exports[`test function-union.js 1`] = ` +"// @flow + +declare function f1(x: mixed): boolean %checks(typeof x === \"string\"); +declare function f2(x: mixed): boolean %checks(Array.isArray(x)); + +declare var cond: boolean; + +// Feature check: +function foo(x: number | string | Array): number { + + var f = (cond) ? f1 : f2; + + if (f(x)) { + return x.length; + } else { + return 1; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (3:39) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$7.flowParseDeclareFunction (/node_modules/babylon/lib/index.js:4467:8) + at Parser.pp$7.flowParseDeclare (/node_modules/babylon/lib/index.js:4476:17) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5235:25) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) +" +`; + +exports[`test is-string-decl.js 1`] = ` +"// @flow + +declare function is_string(x: mixed): boolean %checks(typeof x === \"string\"); +declare function is_number(x: mixed): boolean %checks(typeof x === \"number\"); + +// Feature check: +function foo(x: string | Array): string { + if (is_string(x)) { + // The use of \`is_string\` as a conditional check + // should guarantee the narrowing of the type of \`x\` + // to string. + return x; + } else { + // Accordingly the negation of the above check + // guarantees that \`x\` here is an Array + return x.join(); + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (3:46) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$7.flowParseDeclareFunction (/node_modules/babylon/lib/index.js:4467:8) + at Parser.pp$7.flowParseDeclare (/node_modules/babylon/lib/index.js:4476:17) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5235:25) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) +" +`; + +exports[`test logical-or.js 1`] = ` +"// @flow + +// Sanity check: +// - conditional functions do not affect behavior of conditional +// expressions (e.g. \`||\`) + +declare function r(x: string): number; +var s = \'a\'; +var n = r(s) || 1; +(n: number); + +var x = \"\"; +if (x = r(s) || 1) { + (x: number); +} + +declare var dollars: mixed; + +function foo(x: mixed) { return 1; } +(foo(dollars) || 0); + +(Number(dollars) || 0); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test object-invariant.js 1`] = ` +"// @flow + +// Sanity check: +// - preserving \`havoc\` semantics + +type Meeting = { + organizer: ?Invitee, + es: Array +} + +type Invitee = { + fbid: number +} + +function f(_this: { m: ?Meeting }): string { + if (!_this.m) { + return \"0\"; + } + + if (_this.m.es.some((a) => a.fbid === 0)) { + + } + return \"3\"; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test orig-string-tag-check.js 1`] = ` +"// @flow + +// The original first-order case + +function foo(x: string | Array): string { + if (typeof x === \"string\") { + return x; // [ERROR] x: Array doesn\'t match return type + } + else { + return x.join(); // [ERROR] x: string doesn\'t have .join method + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test sanity-conditional.js 1`] = ` +"// @flow + +// ERROR: only allow conditional expressions in \`%checks\` + +declare function foo(x: string): mixed %checks(x = \"1\"); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (5:39) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$7.flowParseDeclareFunction (/node_modules/babylon/lib/index.js:4467:8) + at Parser.pp$7.flowParseDeclare (/node_modules/babylon/lib/index.js:4476:17) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5235:25) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) +" +`; + +exports[`test sanity-fall-through.js 1`] = ` +"// @flow + +// Sanity check: +// - we should still be getting an error at the second return statement + +declare function pred(x: T): boolean; + +function foo(s: Array): string { + if (pred(s)) { + return \"1\"; + } + return 1; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test sanity-invalid-calls.js 1`] = ` +"// @flow + +// Sanity check: +// - invalid calls at predicate positions + +declare function pred(x: T): boolean; + +function foo(s: Array): string { + + if ((1)(s)) { + return \"1\"; + } + + if ((pred + 1)(\"s\")) { + return \"1\"; + } + + return \"1\" +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test sanity-is-string-bug.js 1`] = ` +"// @flow + +declare function is_string(x: mixed): boolean %checks(typeof x === \"string\"); +declare function is_number(x: mixed): boolean %checks(typeof x === \"number\"); + +// Sanity check: +// - Erroneous logic + +function bar(x: string | Array): string { + if (is_number(x)) { + return x; + } else { + return x.join(); // error: both string and Array can flow to x + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (3:46) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$7.flowParseDeclareFunction (/node_modules/babylon/lib/index.js:4467:8) + at Parser.pp$7.flowParseDeclare (/node_modules/babylon/lib/index.js:4476:17) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5235:25) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) +" +`; + +exports[`test sanity-parameter-mismatch.js 1`] = ` +"// @flow + +// Sanity check: make sure the parameters are checked as usual + +declare function foo( + input: mixed, + types: string | Array +): boolean %checks(typeof input === \"string\" || Array.isArray(input)); + +foo(3, 3); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (8:11) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$7.flowParseDeclareFunction (/node_modules/babylon/lib/index.js:4467:8) + at Parser.pp$7.flowParseDeclare (/node_modules/babylon/lib/index.js:4476:17) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5235:25) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) +" +`; + +exports[`test sanity-pred-with-body.js 1`] = ` +"// @flow + +// Sanity check: +// - predicate functions cannot have bodies (can only be declarations) + +function pred(x: mixed): boolean %checks(typeof x === \"string\") { // error: cannot use pred type here + return typeof x === \"string\"; +} + +function foo(x: string | Array): string { + if (pred(x)) { + return x; + } + return \"1\" +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected { (6:33) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.expect (/node_modules/babylon/lib/index.js:1621:33) + at Parser.pp$1.parseBlock (/node_modules/babylon/lib/index.js:2119:8) + at Parser.pp$3.parseFunctionBody (/node_modules/babylon/lib/index.js:4004:22) + at Parser.parseFunctionBody (/node_modules/babylon/lib/index.js:5211:20) + at Parser.pp$1.parseFunction (/node_modules/babylon/lib/index.js:2257:8) + at Parser.pp$1.parseFunctionStatement (/node_modules/babylon/lib/index.js:1926:15) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1712:19) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) +" +`; + +exports[`test sanity-return-type.js 1`] = ` +"// @flow + +declare function f2(x: mixed): string %checks(Array.isArray(x)); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (3:38) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$7.flowParseDeclareFunction (/node_modules/babylon/lib/index.js:4467:8) + at Parser.pp$7.flowParseDeclare (/node_modules/babylon/lib/index.js:4476:17) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5235:25) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) +" +`; diff --git a/tests/predicates-declared/function-bind.js b/tests/predicates-declared/function-bind.js new file mode 100644 index 000000000000..ac501bb17518 --- /dev/null +++ b/tests/predicates-declared/function-bind.js @@ -0,0 +1,40 @@ +// @flow + +// Sanity checks: +// - use of bind in a position of a function predicate. +// (This case should fall through, as method calls +// are currently not supported.) The original behavior +// (including `havoc`) should be retained. + +class C { + m() { + return true; + } + a: 1; + + n() { + if(this.m.bind(this)) { + this.a; + } + } +} + +declare var m: Function; +const o = { a: 1 }; + +if (m.bind(o)) { + o.a; +} + + +class D { + m: Function; + + n() { + if(this.m({})) { } + } +} + +declare var m: Function; +const x = ""; +if (m.bind(this)(x)) { } diff --git a/tests/predicates-declared/function-union.js b/tests/predicates-declared/function-union.js new file mode 100644 index 000000000000..561c9d1eb8ce --- /dev/null +++ b/tests/predicates-declared/function-union.js @@ -0,0 +1,18 @@ +// @flow + +declare function f1(x: mixed): boolean %checks(typeof x === "string"); +declare function f2(x: mixed): boolean %checks(Array.isArray(x)); + +declare var cond: boolean; + +// Feature check: +function foo(x: number | string | Array): number { + + var f = (cond) ? f1 : f2; + + if (f(x)) { + return x.length; + } else { + return 1; + } +} diff --git a/tests/predicates-declared/is-string-decl.js b/tests/predicates-declared/is-string-decl.js new file mode 100644 index 000000000000..11c94296c89f --- /dev/null +++ b/tests/predicates-declared/is-string-decl.js @@ -0,0 +1,18 @@ +// @flow + +declare function is_string(x: mixed): boolean %checks(typeof x === "string"); +declare function is_number(x: mixed): boolean %checks(typeof x === "number"); + +// Feature check: +function foo(x: string | Array): string { + if (is_string(x)) { + // The use of `is_string` as a conditional check + // should guarantee the narrowing of the type of `x` + // to string. + return x; + } else { + // Accordingly the negation of the above check + // guarantees that `x` here is an Array + return x.join(); + } +} diff --git a/tests/predicates-declared/jsfmt.spec.js b/tests/predicates-declared/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/predicates-declared/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/predicates-declared/logical-or.js b/tests/predicates-declared/logical-or.js new file mode 100644 index 000000000000..babe5b660fc1 --- /dev/null +++ b/tests/predicates-declared/logical-or.js @@ -0,0 +1,22 @@ +// @flow + +// Sanity check: +// - conditional functions do not affect behavior of conditional +// expressions (e.g. `||`) + +declare function r(x: string): number; +var s = 'a'; +var n = r(s) || 1; +(n: number); + +var x = ""; +if (x = r(s) || 1) { + (x: number); +} + +declare var dollars: mixed; + +function foo(x: mixed) { return 1; } +(foo(dollars) || 0); + +(Number(dollars) || 0); diff --git a/tests/predicates-declared/object-invariant.js b/tests/predicates-declared/object-invariant.js new file mode 100644 index 000000000000..916f7ead2fdc --- /dev/null +++ b/tests/predicates-declared/object-invariant.js @@ -0,0 +1,24 @@ +// @flow + +// Sanity check: +// - preserving `havoc` semantics + +type Meeting = { + organizer: ?Invitee, + es: Array +} + +type Invitee = { + fbid: number +} + +function f(_this: { m: ?Meeting }): string { + if (!_this.m) { + return "0"; + } + + if (_this.m.es.some((a) => a.fbid === 0)) { + + } + return "3"; +} diff --git a/tests/predicates-declared/orig-string-tag-check.js b/tests/predicates-declared/orig-string-tag-check.js new file mode 100644 index 000000000000..3930027f602f --- /dev/null +++ b/tests/predicates-declared/orig-string-tag-check.js @@ -0,0 +1,12 @@ +// @flow + +// The original first-order case + +function foo(x: string | Array): string { + if (typeof x === "string") { + return x; // [ERROR] x: Array doesn't match return type + } + else { + return x.join(); // [ERROR] x: string doesn't have .join method + } +} diff --git a/tests/predicates-declared/sanity-conditional.js b/tests/predicates-declared/sanity-conditional.js new file mode 100644 index 000000000000..d3d4ced873ad --- /dev/null +++ b/tests/predicates-declared/sanity-conditional.js @@ -0,0 +1,5 @@ +// @flow + +// ERROR: only allow conditional expressions in `%checks` + +declare function foo(x: string): mixed %checks(x = "1"); diff --git a/tests/predicates-declared/sanity-fall-through.js b/tests/predicates-declared/sanity-fall-through.js new file mode 100644 index 000000000000..2a4df3867533 --- /dev/null +++ b/tests/predicates-declared/sanity-fall-through.js @@ -0,0 +1,13 @@ +// @flow + +// Sanity check: +// - we should still be getting an error at the second return statement + +declare function pred(x: T): boolean; + +function foo(s: Array): string { + if (pred(s)) { + return "1"; + } + return 1; +} diff --git a/tests/predicates-declared/sanity-invalid-calls.js b/tests/predicates-declared/sanity-invalid-calls.js new file mode 100644 index 000000000000..1ccb68edb9af --- /dev/null +++ b/tests/predicates-declared/sanity-invalid-calls.js @@ -0,0 +1,19 @@ +// @flow + +// Sanity check: +// - invalid calls at predicate positions + +declare function pred(x: T): boolean; + +function foo(s: Array): string { + + if ((1)(s)) { + return "1"; + } + + if ((pred + 1)("s")) { + return "1"; + } + + return "1" +} diff --git a/tests/predicates-declared/sanity-is-string-bug.js b/tests/predicates-declared/sanity-is-string-bug.js new file mode 100644 index 000000000000..f96d550bb857 --- /dev/null +++ b/tests/predicates-declared/sanity-is-string-bug.js @@ -0,0 +1,15 @@ +// @flow + +declare function is_string(x: mixed): boolean %checks(typeof x === "string"); +declare function is_number(x: mixed): boolean %checks(typeof x === "number"); + +// Sanity check: +// - Erroneous logic + +function bar(x: string | Array): string { + if (is_number(x)) { + return x; + } else { + return x.join(); // error: both string and Array can flow to x + } +} diff --git a/tests/predicates-declared/sanity-parameter-mismatch.js b/tests/predicates-declared/sanity-parameter-mismatch.js new file mode 100644 index 000000000000..5432bc826ac8 --- /dev/null +++ b/tests/predicates-declared/sanity-parameter-mismatch.js @@ -0,0 +1,10 @@ +// @flow + +// Sanity check: make sure the parameters are checked as usual + +declare function foo( + input: mixed, + types: string | Array +): boolean %checks(typeof input === "string" || Array.isArray(input)); + +foo(3, 3); diff --git a/tests/predicates-declared/sanity-pred-with-body.js b/tests/predicates-declared/sanity-pred-with-body.js new file mode 100644 index 000000000000..1a60d83869d4 --- /dev/null +++ b/tests/predicates-declared/sanity-pred-with-body.js @@ -0,0 +1,15 @@ +// @flow + +// Sanity check: +// - predicate functions cannot have bodies (can only be declarations) + +function pred(x: mixed): boolean %checks(typeof x === "string") { // error: cannot use pred type here + return typeof x === "string"; +} + +function foo(x: string | Array): string { + if (pred(x)) { + return x; + } + return "1" +} diff --git a/tests/predicates-declared/sanity-return-type.js b/tests/predicates-declared/sanity-return-type.js new file mode 100644 index 000000000000..7ee0f6a11980 --- /dev/null +++ b/tests/predicates-declared/sanity-return-type.js @@ -0,0 +1,3 @@ +// @flow + +declare function f2(x: mixed): string %checks(Array.isArray(x)); diff --git a/tests/predicates-inferred/__snapshots__/jsfmt.spec.js.snap b/tests/predicates-inferred/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..a19780c8be50 --- /dev/null +++ b/tests/predicates-inferred/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,289 @@ +exports[`test sanity.js 1`] = ` +"// @flow + +// Sanity check: shouldn\'t be allowed to declare a predicate AND use \`chekcs\` + +function check(y): %checks(typeof y === \"string\") { + return typeof y === \"number\"; +} + +declare var y: number | boolean; + +if (check(y)) { + (y: number); +} + +// Sanity: disallowed body +function indirect_is_number(y): %checks { + var y = 1; + return typeof y === \"number\"; +} + +function bak(z: string | number): number { + if (indirect_is_number(z)) { + return z; + } else { + return z.length; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (5:19) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$7.flowParsePrimaryType (/node_modules/babylon/lib/index.js:5099:8) + at Parser.pp$7.flowParsePostfixType (/node_modules/babylon/lib/index.js:5105:19) + at Parser.pp$7.flowParsePrefixType (/node_modules/babylon/lib/index.js:5122:17) + at Parser.pp$7.flowParseAnonFunctionWithoutParens (/node_modules/babylon/lib/index.js:5127:20) + at Parser.pp$7.flowParseIntersectionType (/node_modules/babylon/lib/index.js:5141:19) + at Parser.pp$7.flowParseUnionType (/node_modules/babylon/lib/index.js:5151:19) + at Parser.pp$7.flowParseType (/node_modules/babylon/lib/index.js:5162:19) + at Parser.pp$7.flowParseTypeInitialiser (/node_modules/babylon/lib/index.js:4430:19) +" +`; + +exports[`test sanity-multi-params.js 1`] = ` +"// @flow + +// Feature: multi params +function multi_param(w,x,y,z): %checks { + return typeof z === \"string\"; +} + +function foo(x: string | Array): string { + if (multi_param(\"1\", \"2\", x, \"3\")) { + return x; + } else { + return x.join(); + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (4:31) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$7.flowParsePrimaryType (/node_modules/babylon/lib/index.js:5099:8) + at Parser.pp$7.flowParsePostfixType (/node_modules/babylon/lib/index.js:5105:19) + at Parser.pp$7.flowParsePrefixType (/node_modules/babylon/lib/index.js:5122:17) + at Parser.pp$7.flowParseAnonFunctionWithoutParens (/node_modules/babylon/lib/index.js:5127:20) + at Parser.pp$7.flowParseIntersectionType (/node_modules/babylon/lib/index.js:5141:19) + at Parser.pp$7.flowParseUnionType (/node_modules/babylon/lib/index.js:5151:19) + at Parser.pp$7.flowParseType (/node_modules/babylon/lib/index.js:5162:19) + at Parser.pp$7.flowParseTypeInitialiser (/node_modules/babylon/lib/index.js:4430:19) +" +`; + +exports[`test sanity-ordering.js 1`] = ` +"// @flow + +declare var key: string; +declare var obj: { page: ?Object; }; + +if (dotAccess(obj)) { + (obj.page: Object); +} + +function dotAccess(head, create) { + const path = \'path.location\'; + const stack = path.split(\'.\'); + do { + const key = stack.shift(); + head = head[key] || create && (head[key] = {}); + } while (stack.length && head); + return head; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:925 + if (endsWithBrace(doBody)) + ^ + +ReferenceError: endsWithBrace is not defined + at genericPrintNoParens (/src/printer.js:925:11) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:561:16 + at FastPath.call (/src/fast-path.js:113:16) +" +`; + +exports[`test sanity-unbound-var.js 1`] = ` +"// @flow + +declare var y: mixed; + +// Sanity check: this should fail, because the preficate function +// checks \`y\` instead of \`x\`. +function err(x): %checks { + return typeof y === \"string\"; +} + +function foo(x: string | Array): string { + if (err(x)) { + return x; + } else { + return x.join(); + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (7:17) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$7.flowParsePrimaryType (/node_modules/babylon/lib/index.js:5099:8) + at Parser.pp$7.flowParsePostfixType (/node_modules/babylon/lib/index.js:5105:19) + at Parser.pp$7.flowParsePrefixType (/node_modules/babylon/lib/index.js:5122:17) + at Parser.pp$7.flowParseAnonFunctionWithoutParens (/node_modules/babylon/lib/index.js:5127:20) + at Parser.pp$7.flowParseIntersectionType (/node_modules/babylon/lib/index.js:5141:19) + at Parser.pp$7.flowParseUnionType (/node_modules/babylon/lib/index.js:5151:19) + at Parser.pp$7.flowParseType (/node_modules/babylon/lib/index.js:5162:19) + at Parser.pp$7.flowParseTypeInitialiser (/node_modules/babylon/lib/index.js:4430:19) +" +`; + +exports[`test simple-predicate-func.js 1`] = ` +"// @flow + +function is_string(y): %checks { + return typeof y === \"string\"; +} + +function is_bool(y): %checks { + return typeof y === \"boolean\"; +} + +function is_number(y): %checks { + return typeof y === \"number\"; +} + +// Feature check: +function foo(x: string | Array): string { + if (is_string(x)) { + // The use of \`is_string\` as a conditional check + // should guarantee the narrowing of the type of \`x\` + // to string. + return x; + } else { + // Accordingly the negation of the above check + // guarantees that \`x\` here is an Array + return x.join(); + } +} + +// Same as above but refining an offset +function bar(z: { f: string | Array}): string { + if (is_string(z.f)) { + return z.f; + } else { + return z.f.join(); + } +} + +function is_number_or_bool(y): %checks { + return is_number(y) || is_bool(y); +} + +function baz(z: string | number): number { + if (is_number_or_bool(z)) { + return z; + } else { + return z.length; + } +} + +// Feature: multi params +function multi_param(w,x,y,z): %checks { + return typeof z === \"string\"; +} + +function foo(x: string | Array): string { + if (multi_param(\"1\", \"2\", \"3\", x)) { + return x; + } else { + return x.join(); + } +} + +function foo(a, b) { + if (two_strings(a, b)) { + from_two_strings(a, b); + } +} + +function two_strings(x,y): %checks { + return is_string(x) && is_string(y) ; +} + +declare function from_two_strings(x: string, y: string): void; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (3:23) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$7.flowParsePrimaryType (/node_modules/babylon/lib/index.js:5099:8) + at Parser.pp$7.flowParsePostfixType (/node_modules/babylon/lib/index.js:5105:19) + at Parser.pp$7.flowParsePrefixType (/node_modules/babylon/lib/index.js:5122:17) + at Parser.pp$7.flowParseAnonFunctionWithoutParens (/node_modules/babylon/lib/index.js:5127:20) + at Parser.pp$7.flowParseIntersectionType (/node_modules/babylon/lib/index.js:5141:19) + at Parser.pp$7.flowParseUnionType (/node_modules/babylon/lib/index.js:5151:19) + at Parser.pp$7.flowParseType (/node_modules/babylon/lib/index.js:5162:19) + at Parser.pp$7.flowParseTypeInitialiser (/node_modules/babylon/lib/index.js:4430:19) +" +`; + +exports[`test simple-predicate-func-post.js 1`] = ` +"// @flow + +// Feature check: +// The predicate function is defined after the conditional check + +function foo(x: string | Array): string { + if (is_string(x)) { + // The use of \`is_string\` as a conditional check + // should guarantee the narrowing of the type of \`x\` + // to string. + return x; + } else { + // Accordingly the negation of the above check + // guarantees that \`x\` here is an Array + return x.join(); + } +} + +function is_string(x): %checks { + return typeof x === \"string\"; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (19:23) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$7.flowParsePrimaryType (/node_modules/babylon/lib/index.js:5099:8) + at Parser.pp$7.flowParsePostfixType (/node_modules/babylon/lib/index.js:5105:19) + at Parser.pp$7.flowParsePrefixType (/node_modules/babylon/lib/index.js:5122:17) + at Parser.pp$7.flowParseAnonFunctionWithoutParens (/node_modules/babylon/lib/index.js:5127:20) + at Parser.pp$7.flowParseIntersectionType (/node_modules/babylon/lib/index.js:5141:19) + at Parser.pp$7.flowParseUnionType (/node_modules/babylon/lib/index.js:5151:19) + at Parser.pp$7.flowParseType (/node_modules/babylon/lib/index.js:5162:19) + at Parser.pp$7.flowParseTypeInitialiser (/node_modules/babylon/lib/index.js:4430:19) +" +`; diff --git a/tests/predicates-inferred/jsfmt.spec.js b/tests/predicates-inferred/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/predicates-inferred/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/predicates-inferred/sanity-multi-params.js b/tests/predicates-inferred/sanity-multi-params.js new file mode 100644 index 000000000000..e08ae04e2c67 --- /dev/null +++ b/tests/predicates-inferred/sanity-multi-params.js @@ -0,0 +1,14 @@ +// @flow + +// Feature: multi params +function multi_param(w,x,y,z): %checks { + return typeof z === "string"; +} + +function foo(x: string | Array): string { + if (multi_param("1", "2", x, "3")) { + return x; + } else { + return x.join(); + } +} diff --git a/tests/predicates-inferred/sanity-ordering.js b/tests/predicates-inferred/sanity-ordering.js new file mode 100644 index 000000000000..741e65fd305f --- /dev/null +++ b/tests/predicates-inferred/sanity-ordering.js @@ -0,0 +1,18 @@ +// @flow + +declare var key: string; +declare var obj: { page: ?Object; }; + +if (dotAccess(obj)) { + (obj.page: Object); +} + +function dotAccess(head, create) { + const path = 'path.location'; + const stack = path.split('.'); + do { + const key = stack.shift(); + head = head[key] || create && (head[key] = {}); + } while (stack.length && head); + return head; +} diff --git a/tests/predicates-inferred/sanity-unbound-var.js b/tests/predicates-inferred/sanity-unbound-var.js new file mode 100644 index 000000000000..8ff02beb0e9c --- /dev/null +++ b/tests/predicates-inferred/sanity-unbound-var.js @@ -0,0 +1,17 @@ +// @flow + +declare var y: mixed; + +// Sanity check: this should fail, because the preficate function +// checks `y` instead of `x`. +function err(x): %checks { + return typeof y === "string"; +} + +function foo(x: string | Array): string { + if (err(x)) { + return x; + } else { + return x.join(); + } +} diff --git a/tests/predicates-inferred/sanity.js b/tests/predicates-inferred/sanity.js new file mode 100644 index 000000000000..5a2aa6d59ce6 --- /dev/null +++ b/tests/predicates-inferred/sanity.js @@ -0,0 +1,27 @@ +// @flow + +// Sanity check: shouldn't be allowed to declare a predicate AND use `chekcs` + +function check(y): %checks(typeof y === "string") { + return typeof y === "number"; +} + +declare var y: number | boolean; + +if (check(y)) { + (y: number); +} + +// Sanity: disallowed body +function indirect_is_number(y): %checks { + var y = 1; + return typeof y === "number"; +} + +function bak(z: string | number): number { + if (indirect_is_number(z)) { + return z; + } else { + return z.length; + } +} diff --git a/tests/predicates-inferred/simple-predicate-func-post.js b/tests/predicates-inferred/simple-predicate-func-post.js new file mode 100644 index 000000000000..ba5f390920dd --- /dev/null +++ b/tests/predicates-inferred/simple-predicate-func-post.js @@ -0,0 +1,21 @@ +// @flow + +// Feature check: +// The predicate function is defined after the conditional check + +function foo(x: string | Array): string { + if (is_string(x)) { + // The use of `is_string` as a conditional check + // should guarantee the narrowing of the type of `x` + // to string. + return x; + } else { + // Accordingly the negation of the above check + // guarantees that `x` here is an Array + return x.join(); + } +} + +function is_string(x): %checks { + return typeof x === "string"; +} diff --git a/tests/predicates-inferred/simple-predicate-func.js b/tests/predicates-inferred/simple-predicate-func.js new file mode 100644 index 000000000000..bb254dbe5c7c --- /dev/null +++ b/tests/predicates-inferred/simple-predicate-func.js @@ -0,0 +1,73 @@ +// @flow + +function is_string(y): %checks { + return typeof y === "string"; +} + +function is_bool(y): %checks { + return typeof y === "boolean"; +} + +function is_number(y): %checks { + return typeof y === "number"; +} + +// Feature check: +function foo(x: string | Array): string { + if (is_string(x)) { + // The use of `is_string` as a conditional check + // should guarantee the narrowing of the type of `x` + // to string. + return x; + } else { + // Accordingly the negation of the above check + // guarantees that `x` here is an Array + return x.join(); + } +} + +// Same as above but refining an offset +function bar(z: { f: string | Array}): string { + if (is_string(z.f)) { + return z.f; + } else { + return z.f.join(); + } +} + +function is_number_or_bool(y): %checks { + return is_number(y) || is_bool(y); +} + +function baz(z: string | number): number { + if (is_number_or_bool(z)) { + return z; + } else { + return z.length; + } +} + +// Feature: multi params +function multi_param(w,x,y,z): %checks { + return typeof z === "string"; +} + +function foo(x: string | Array): string { + if (multi_param("1", "2", "3", x)) { + return x; + } else { + return x.join(); + } +} + +function foo(a, b) { + if (two_strings(a, b)) { + from_two_strings(a, b); + } +} + +function two_strings(x,y): %checks { + return is_string(x) && is_string(y) ; +} + +declare function from_two_strings(x: string, y: string): void; diff --git a/tests/predicates-parsing/__snapshots__/jsfmt.spec.js.snap b/tests/predicates-parsing/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..f2a3a00e626d --- /dev/null +++ b/tests/predicates-parsing/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,224 @@ +exports[`test fail-0.js 1`] = ` +"// @flow + +// Error: \'declare\', \'checks\' but missing predicate + +declare function f2(x: mixed): boolean %checks; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (5:39) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$7.flowParseDeclareFunction (/node_modules/babylon/lib/index.js:4467:8) + at Parser.pp$7.flowParseDeclare (/node_modules/babylon/lib/index.js:4476:17) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5235:25) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) +" +`; + +exports[`test fail-1.js 1`] = ` +"// @flow + +// Error: no return statement + +function f6(x: mixed): %checks (x !== null) { } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (5:23) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$7.flowParsePrimaryType (/node_modules/babylon/lib/index.js:5099:8) + at Parser.pp$7.flowParsePostfixType (/node_modules/babylon/lib/index.js:5105:19) + at Parser.pp$7.flowParsePrefixType (/node_modules/babylon/lib/index.js:5122:17) + at Parser.pp$7.flowParseAnonFunctionWithoutParens (/node_modules/babylon/lib/index.js:5127:20) + at Parser.pp$7.flowParseIntersectionType (/node_modules/babylon/lib/index.js:5141:19) + at Parser.pp$7.flowParseUnionType (/node_modules/babylon/lib/index.js:5151:19) + at Parser.pp$7.flowParseType (/node_modules/babylon/lib/index.js:5162:19) + at Parser.pp$7.flowParseTypeInitialiser (/node_modules/babylon/lib/index.js:4430:19) +" +`; + +exports[`test fail-2.js 1`] = ` +"// @flow + +var a2 = (x: mixed): %checks (x !== null) => { // Error: body form + var x = 1; return x; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (3:19) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$1.parseVarStatement (/node_modules/babylon/lib/index.js:2043:8) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1735:19) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) + at Parser.parse (/node_modules/babylon/lib/index.js:1543:17) + at Object.parse$1 [as parse] (/node_modules/babylon/lib/index.js:6472:37) +" +`; + +exports[`test fail-3.js 1`] = ` +"// @flow + +// Cannot declare predicate with a function body is present. + +function f5(x: mixed): %checks (x !== null) { return x !== null } + +var a2 = (x: mixed): %checks (x !== null) => x !== null; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (5:23) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$7.flowParsePrimaryType (/node_modules/babylon/lib/index.js:5099:8) + at Parser.pp$7.flowParsePostfixType (/node_modules/babylon/lib/index.js:5105:19) + at Parser.pp$7.flowParsePrefixType (/node_modules/babylon/lib/index.js:5122:17) + at Parser.pp$7.flowParseAnonFunctionWithoutParens (/node_modules/babylon/lib/index.js:5127:20) + at Parser.pp$7.flowParseIntersectionType (/node_modules/babylon/lib/index.js:5141:19) + at Parser.pp$7.flowParseUnionType (/node_modules/babylon/lib/index.js:5151:19) + at Parser.pp$7.flowParseType (/node_modules/babylon/lib/index.js:5162:19) + at Parser.pp$7.flowParseTypeInitialiser (/node_modules/babylon/lib/index.js:4430:19) +" +`; + +exports[`test pass.js 1`] = ` +"// @flow + +declare function f1(x: mixed): boolean; + +declare function f3(x: mixed): boolean %checks (x !== null); + +declare function f4(x: mixed): boolean %checks (x !== null); + +function f7(x: mixed): %checks { return x !== null } + +var a0 = (x: mixed) => x !== null; + +var a1 = (x: mixed): %checks => x !== null; + +(x): %checks => x !== null; + +const insert_a_really_big_predicated_arrow_function_name_here = (x) + : %checks => x !== null; + +declare var x; +(x) +checks => 123; + +type checks = any; + +declare function f(x: mixed): checks +(typeof x === null); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (5:39) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$7.flowParseDeclareFunction (/node_modules/babylon/lib/index.js:4467:8) + at Parser.pp$7.flowParseDeclare (/node_modules/babylon/lib/index.js:4476:17) + at Parser.parseExpressionStatement (/node_modules/babylon/lib/index.js:5235:25) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1785:17) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) +" +`; + +exports[`test unsupported-0.js 1`] = ` +"// @flow + +// variables cannot be annotated with a predicate type + +var a3: (x: mixed) => boolean %checks (x !== null); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (5:30) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$1.parseVarStatement (/node_modules/babylon/lib/index.js:2043:8) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1735:19) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) + at Parser.parse (/node_modules/babylon/lib/index.js:1543:17) + at Object.parse$1 [as parse] (/node_modules/babylon/lib/index.js:6472:37) +" +`; + +exports[`test unsupported-1.js 1`] = ` +"// @flow + +// (inited) variables cannot be annotated with a predicate type + +var a4: (x: mixed) => boolean %checks = (x: mixed) => x !== null; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (5:30) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$1.parseVarStatement (/node_modules/babylon/lib/index.js:2043:8) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1735:19) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) + at Parser.parse (/node_modules/babylon/lib/index.js:1543:17) + at Object.parse$1 [as parse] (/node_modules/babylon/lib/index.js:6472:37) +" +`; + +exports[`test unsupported-2.js 1`] = ` +"// @flow + +// Error: no support for checked preds + +var a5: (x: mixed) => boolean %checks(x !== null) = + (y: mixed) => typeof y !== \"string\"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token, expected ; (5:30) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp.semicolon (/node_modules/babylon/lib/index.js:1614:38) + at Parser.pp$1.parseVarStatement (/node_modules/babylon/lib/index.js:2043:8) + at Parser.pp$1.parseStatement (/node_modules/babylon/lib/index.js:1735:19) + at Parser.parseStatement (/node_modules/babylon/lib/index.js:5224:22) + at Parser.pp$1.parseBlockBody (/node_modules/babylon/lib/index.js:2139:21) + at Parser.pp$1.parseTopLevel (/node_modules/babylon/lib/index.js:1651:8) + at Parser.parse (/node_modules/babylon/lib/index.js:1543:17) + at Object.parse$1 [as parse] (/node_modules/babylon/lib/index.js:6472:37) +" +`; diff --git a/tests/predicates-parsing/fail-0.js b/tests/predicates-parsing/fail-0.js new file mode 100644 index 000000000000..93439645deb4 --- /dev/null +++ b/tests/predicates-parsing/fail-0.js @@ -0,0 +1,5 @@ +// @flow + +// Error: 'declare', 'checks' but missing predicate + +declare function f2(x: mixed): boolean %checks; diff --git a/tests/predicates-parsing/fail-1.js b/tests/predicates-parsing/fail-1.js new file mode 100644 index 000000000000..ceb1e926e073 --- /dev/null +++ b/tests/predicates-parsing/fail-1.js @@ -0,0 +1,5 @@ +// @flow + +// Error: no return statement + +function f6(x: mixed): %checks (x !== null) { } diff --git a/tests/predicates-parsing/fail-2.js b/tests/predicates-parsing/fail-2.js new file mode 100644 index 000000000000..68260c92aecc --- /dev/null +++ b/tests/predicates-parsing/fail-2.js @@ -0,0 +1,5 @@ +// @flow + +var a2 = (x: mixed): %checks (x !== null) => { // Error: body form + var x = 1; return x; +} diff --git a/tests/predicates-parsing/fail-3.js b/tests/predicates-parsing/fail-3.js new file mode 100644 index 000000000000..1cd15ba3f689 --- /dev/null +++ b/tests/predicates-parsing/fail-3.js @@ -0,0 +1,7 @@ +// @flow + +// Cannot declare predicate with a function body is present. + +function f5(x: mixed): %checks (x !== null) { return x !== null } + +var a2 = (x: mixed): %checks (x !== null) => x !== null; diff --git a/tests/predicates-parsing/jsfmt.spec.js b/tests/predicates-parsing/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/predicates-parsing/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/predicates-parsing/pass.js b/tests/predicates-parsing/pass.js new file mode 100644 index 000000000000..05e5c54ec9c6 --- /dev/null +++ b/tests/predicates-parsing/pass.js @@ -0,0 +1,27 @@ +// @flow + +declare function f1(x: mixed): boolean; + +declare function f3(x: mixed): boolean %checks (x !== null); + +declare function f4(x: mixed): boolean %checks (x !== null); + +function f7(x: mixed): %checks { return x !== null } + +var a0 = (x: mixed) => x !== null; + +var a1 = (x: mixed): %checks => x !== null; + +(x): %checks => x !== null; + +const insert_a_really_big_predicated_arrow_function_name_here = (x) + : %checks => x !== null; + +declare var x; +(x) +checks => 123; + +type checks = any; + +declare function f(x: mixed): checks +(typeof x === null); diff --git a/tests/predicates-parsing/unsupported-0.js b/tests/predicates-parsing/unsupported-0.js new file mode 100644 index 000000000000..84ed10e27e21 --- /dev/null +++ b/tests/predicates-parsing/unsupported-0.js @@ -0,0 +1,5 @@ +// @flow + +// variables cannot be annotated with a predicate type + +var a3: (x: mixed) => boolean %checks (x !== null); diff --git a/tests/predicates-parsing/unsupported-1.js b/tests/predicates-parsing/unsupported-1.js new file mode 100644 index 000000000000..c9a7af823fd2 --- /dev/null +++ b/tests/predicates-parsing/unsupported-1.js @@ -0,0 +1,5 @@ +// @flow + +// (inited) variables cannot be annotated with a predicate type + +var a4: (x: mixed) => boolean %checks = (x: mixed) => x !== null; diff --git a/tests/predicates-parsing/unsupported-2.js b/tests/predicates-parsing/unsupported-2.js new file mode 100644 index 000000000000..d4999b9c36b5 --- /dev/null +++ b/tests/predicates-parsing/unsupported-2.js @@ -0,0 +1,6 @@ +// @flow + +// Error: no support for checked preds + +var a5: (x: mixed) => boolean %checks(x !== null) = + (y: mixed) => typeof y !== "string"; diff --git a/tests/private/__snapshots__/jsfmt.spec.js.snap b/tests/private/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..69badd198c90 --- /dev/null +++ b/tests/private/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,36 @@ +exports[`test private.js 1`] = ` +"class A { + x: number; + _x: string; + __x: number; + constructor() { this.x = 0; this._x = ""; this.__x = 0; } +} + +class B extends A { + foo() { + var x: number = this.x; + var _x: string = this._x; + var __x: number = this.__x; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class A { + x: number; + _x: string; + __x: number; + constructor() { + this.x = 0; + this._x = ""; + this.__x = 0; + } +} +class B extends A { + foo() { + var x: number = this.x; + var _x: string = this._x; + var __x: number = this.__x; + } +} + +" +`; diff --git a/tests/private/jsfmt.spec.js b/tests/private/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/private/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/private/private.js b/tests/private/private.js new file mode 100644 index 000000000000..60e209d079fc --- /dev/null +++ b/tests/private/private.js @@ -0,0 +1,14 @@ +class A { + x: number; + _x: string; + __x: number; + constructor() { this.x = 0; this._x = ""; this.__x = 0; } +} + +class B extends A { + foo() { + var x: number = this.x; + var _x: string = this._x; + var __x: number = this.__x; + } +} diff --git a/tests/promises/__snapshots__/jsfmt.spec.js.snap b/tests/promises/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..7e3ce9ba9dbe --- /dev/null +++ b/tests/promises/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,428 @@ +exports[`test all.js 1`] = ` +"// @flow + +declare var pstr: Promise; +declare var pnum: Promise; + +Promise.all([ + pstr, + pnum, + true, // non-Promise values passed through +]).then((xs) => { + // tuple information is preserved + let [a,b,c] = xs; + (a: number); // Error: string ~> number + (b: boolean); // Error: number ~> boolean + (c: string); // Error: boolean ~> string + + // array element type is (string | number | boolean) + xs.forEach(x => { + (x: void); // Errors: string ~> void, number ~> void, boolean ~> void + }); +}); + +// First argument is required +Promise.all(); // Error: expected array instead of undefined (too few arguments) + +// Mis-typed arg +Promise.all(0); // Error: expected array instead of number + +// Promise.all is a function +(Promise.all : Function); + +// Promise.all supports iterables +function test(val: Iterable>) { + const r: Promise> = Promise.all(val); +} + +function tes2(val: Map>) { + const r: Promise> = Promise.all(val.values()); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test covariance.js 1`] = ` +"/* @flow */ + +async function testAll() { + /* This is a test case from https://github.com/facebook/flow/issues/1143 + * which was previously an error due to Array\'s invariance and an improper + * definition of Promise.all */ + const x: Array> = []; + const y: Promise> = Promise.all(x); + const z: Array = await y; +} + +async function testRace() { + const x: Array> = []; + const y: Promise = Promise.race(x); + const z: ?string = await y; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test promise.js 1`] = ` +"/** + * @flow + */ + +////////////////////////////////////////////////// +// == Promise constructor resolve() function == // +////////////////////////////////////////////////// + +// Promise constructor resolve(T) -> then(T) +new Promise(function(resolve, reject) { + resolve(0); +}).then(function(num) { + var a: number = num; + + // TODO: The error message that results from this is almost useless + var b: string = num; // Error: number ~> string +}); + +// Promise constructor with arrow function resolve(T) -> then(T) +new Promise((resolve, reject) => resolve(0)) + .then(function(num) { + var a: number = num; + + // TODO: The error message that results from this is almost useless + var b: string = num; // Error: number ~> string + }); + +// Promise constructor resolve(Promise) -> then(T) +new Promise(function(resolve, reject) { + resolve(new Promise(function(resolve, reject) { + resolve(0); + })); +}).then(function(num) { + var a: number = num; + var b: string = num; // Error: number ~> string +}); + +// Promise constructor resolve(Promise>) -> then(T) +new Promise(function(resolve, reject) { + resolve(new Promise(function(resolve, reject) { + resolve(new Promise(function(resolve, reject) { + resolve(0); + })); + })); +}).then(function(num) { + var a: number = num; + var b: string = num; // Error: number ~> string +}); + +// Promise constructor resolve(T); resolve(U); -> then(T|U) +new Promise(function(resolve, reject) { + if (Math.random()) { + resolve(42); + } else { + resolve(\'str\'); + } +}).then(function(numOrStr) { + if (typeof numOrStr === \'string\') { + var a: string = numOrStr; + } else { + var b: number = numOrStr; + } + var c: string = numOrStr; // Error: number|string -> string +}); + +///////////////////////////////////////////////// +// == Promise constructor reject() function == // +///////////////////////////////////////////////// + +// TODO: Promise constructor reject(T) -> catch(T) +new Promise(function(resolve, reject) { + reject(0); +}).catch(function(num) { + var a: number = num; + + // TODO + var b: string = num; // Error: number ~> string +}); + +// TODO: Promise constructor reject(Promise) ~> catch(Promise) +new Promise(function(resolve, reject) { + reject(new Promise(function(resolve, reject) { + reject(0); + })); +}).catch(function(num) { + var a: Promise = num; + + // TODO + var b: number = num; // Error: Promise ~> number +}); + +// TODO: Promise constructor reject(T); reject(U); -> then(T|U) +new Promise(function(resolve, reject) { + if (Math.random()) { + reject(42); + } else { + reject(\'str\'); + } +}).catch(function(numOrStr) { + if (typeof numOrStr === \'string\') { + var a: string = numOrStr; + } else { + var b: number = numOrStr; + } + + // TODO + var c: string = numOrStr; // Error: number|string -> string +}); + +///////////////////////////// +// == Promise.resolve() == // +///////////////////////////// + +// Promise.resolve(T) -> then(T) +Promise.resolve(0).then(function(num) { + var a: number = num; + var b: string = num; // Error: number ~> string +}); + +// Promise.resolve(Promise) -> then(T) +Promise.resolve(Promise.resolve(0)).then(function(num) { + var a: number = num; + var b: string = num; // Error: number ~> string +}); + +// Promise.resolve(Promise>) -> then(T) +Promise.resolve(Promise.resolve(Promise.resolve(0))).then(function(num) { + var a: number = num; + var b: string = num; // Error: number ~> string +}); + +//////////////////////////// +// == Promise.reject() == // +//////////////////////////// + +// TODO: Promise.reject(T) -> catch(T) +Promise.reject(0).catch(function(num) { + var a: number = num; + + // TODO + var b: string = num; // Error: number ~> string +}); + +// TODO: Promise.reject(Promise) -> catch(Promise) +Promise.reject(Promise.resolve(0)).then(function(num) { + var a: Promise = num; + + // TODO + var b: number = num; // Error: Promise ~> number +}); + +////////////////////////////////// +// == Promise.prototype.then == // +////////////////////////////////// + +// resolvedPromise.then():T -> then(T) +Promise.resolve(0) + .then(function(num) { return \'asdf\'; }) + .then(function(str) { + var a: string = str; + var b: number = str; // Error: string ~> number + }); + +// resolvedPromise.then():Promise -> then(T) +Promise.resolve(0) + .then(function(num) { return Promise.resolve(\'asdf\'); }) + .then(function(str) { + var a: string = str; + var b: number = str; // Error: string ~> number + }); + +// resolvedPromise.then():Promise> -> then(T) +Promise.resolve(0) + .then(function(num) { return Promise.resolve(Promise.resolve(\'asdf\')); }) + .then(function(str) { + var a: string = str; + var b: number = str; // Error: string ~> number + }); + +// TODO: resolvedPromise.then() -> catch(T) +Promise.resolve(0) + .then(function(num) { + throw \'str\'; + }) + .catch(function(str) { + var a: string = str; + + // TODO + var b: number = str; // Error: string ~> number + }); + +/////////////////////////////////// +// == Promise.prototype.catch == // +/////////////////////////////////// + +// rejectedPromise.catch():U -> then(U) +Promise.reject(0) + .catch(function(num) { return \'asdf\'; }) + .then(function(str) { + var a: string = str; + var b: number = str; // Error: string ~> number + }); + +// rejectedPromise.catch():Promise -> then(U) +Promise.reject(0) + .catch(function(num) { return Promise.resolve(\'asdf\'); }) + .then(function(str) { + var a: string = str; + var b: number = str; // Error: string ~> number + }); + +// rejectedPromise.catch():Promise> -> then(U) +Promise.reject(0) + .catch(function(num) { return Promise.resolve(Promise.resolve(\'asdf\')); }) + .then(function(str) { + var a: string = str; + var b: number = str; // Error: string ~> number + }); + +// TODO: resolvedPromise -> catch() -> then():T +Promise.resolve(0) + .catch(function(err) {}) + .then(function(num) { + var a: number = num; + + // TODO + var b: string = num; // Error: string ~> number + }); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test resolve_global.js 1`] = ` +"/** + * test Promise name resolution + * @flow + */ + +/** + * 1. introduce shadowing bindings for important names + */ +class Promise {} + +/** + * 2. implicit refs to Promise during desugaring should be unaffected + */ +async function foo(x: boolean) { + if (x) { + return {bar: \'baz\'}; + } else { + return null; + } +} + +async function run() { + console.log(await foo(true)); + console.log(await foo(false)); +} + +run(); + +/** + * 3. but explicit name refs from code and annos resolve + * using the usual rules + */ +// error: \`Promise\` in return expr is the local binding +async function bar() { + return Promise.resolve(0); +} + +// error: return type anno is a ref to the local binding +async function baz(): Promise { + return 0; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test resolve_void.js 1`] = ` +"// @flow + +(Promise.resolve(): Promise); // error + +(Promise.resolve(undefined): Promise); // error +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/promises/all.js b/tests/promises/all.js new file mode 100644 index 000000000000..74d02a75eae0 --- /dev/null +++ b/tests/promises/all.js @@ -0,0 +1,39 @@ +// @flow + +declare var pstr: Promise; +declare var pnum: Promise; + +Promise.all([ + pstr, + pnum, + true, // non-Promise values passed through +]).then((xs) => { + // tuple information is preserved + let [a,b,c] = xs; + (a: number); // Error: string ~> number + (b: boolean); // Error: number ~> boolean + (c: string); // Error: boolean ~> string + + // array element type is (string | number | boolean) + xs.forEach(x => { + (x: void); // Errors: string ~> void, number ~> void, boolean ~> void + }); +}); + +// First argument is required +Promise.all(); // Error: expected array instead of undefined (too few arguments) + +// Mis-typed arg +Promise.all(0); // Error: expected array instead of number + +// Promise.all is a function +(Promise.all : Function); + +// Promise.all supports iterables +function test(val: Iterable>) { + const r: Promise> = Promise.all(val); +} + +function tes2(val: Map>) { + const r: Promise> = Promise.all(val.values()); +} diff --git a/tests/promises/covariance.js b/tests/promises/covariance.js new file mode 100644 index 000000000000..dbfdd6912d72 --- /dev/null +++ b/tests/promises/covariance.js @@ -0,0 +1,16 @@ +/* @flow */ + +async function testAll() { + /* This is a test case from https://github.com/facebook/flow/issues/1143 + * which was previously an error due to Array's invariance and an improper + * definition of Promise.all */ + const x: Array> = []; + const y: Promise> = Promise.all(x); + const z: Array = await y; +} + +async function testRace() { + const x: Array> = []; + const y: Promise = Promise.race(x); + const z: ?string = await y; +} diff --git a/tests/promises/jsfmt.spec.js b/tests/promises/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/promises/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/promises/promise.js b/tests/promises/promise.js new file mode 100644 index 000000000000..1266b018346d --- /dev/null +++ b/tests/promises/promise.js @@ -0,0 +1,228 @@ +/** + * @flow + */ + +////////////////////////////////////////////////// +// == Promise constructor resolve() function == // +////////////////////////////////////////////////// + +// Promise constructor resolve(T) -> then(T) +new Promise(function(resolve, reject) { + resolve(0); +}).then(function(num) { + var a: number = num; + + // TODO: The error message that results from this is almost useless + var b: string = num; // Error: number ~> string +}); + +// Promise constructor with arrow function resolve(T) -> then(T) +new Promise((resolve, reject) => resolve(0)) + .then(function(num) { + var a: number = num; + + // TODO: The error message that results from this is almost useless + var b: string = num; // Error: number ~> string + }); + +// Promise constructor resolve(Promise) -> then(T) +new Promise(function(resolve, reject) { + resolve(new Promise(function(resolve, reject) { + resolve(0); + })); +}).then(function(num) { + var a: number = num; + var b: string = num; // Error: number ~> string +}); + +// Promise constructor resolve(Promise>) -> then(T) +new Promise(function(resolve, reject) { + resolve(new Promise(function(resolve, reject) { + resolve(new Promise(function(resolve, reject) { + resolve(0); + })); + })); +}).then(function(num) { + var a: number = num; + var b: string = num; // Error: number ~> string +}); + +// Promise constructor resolve(T); resolve(U); -> then(T|U) +new Promise(function(resolve, reject) { + if (Math.random()) { + resolve(42); + } else { + resolve('str'); + } +}).then(function(numOrStr) { + if (typeof numOrStr === 'string') { + var a: string = numOrStr; + } else { + var b: number = numOrStr; + } + var c: string = numOrStr; // Error: number|string -> string +}); + +///////////////////////////////////////////////// +// == Promise constructor reject() function == // +///////////////////////////////////////////////// + +// TODO: Promise constructor reject(T) -> catch(T) +new Promise(function(resolve, reject) { + reject(0); +}).catch(function(num) { + var a: number = num; + + // TODO + var b: string = num; // Error: number ~> string +}); + +// TODO: Promise constructor reject(Promise) ~> catch(Promise) +new Promise(function(resolve, reject) { + reject(new Promise(function(resolve, reject) { + reject(0); + })); +}).catch(function(num) { + var a: Promise = num; + + // TODO + var b: number = num; // Error: Promise ~> number +}); + +// TODO: Promise constructor reject(T); reject(U); -> then(T|U) +new Promise(function(resolve, reject) { + if (Math.random()) { + reject(42); + } else { + reject('str'); + } +}).catch(function(numOrStr) { + if (typeof numOrStr === 'string') { + var a: string = numOrStr; + } else { + var b: number = numOrStr; + } + + // TODO + var c: string = numOrStr; // Error: number|string -> string +}); + +///////////////////////////// +// == Promise.resolve() == // +///////////////////////////// + +// Promise.resolve(T) -> then(T) +Promise.resolve(0).then(function(num) { + var a: number = num; + var b: string = num; // Error: number ~> string +}); + +// Promise.resolve(Promise) -> then(T) +Promise.resolve(Promise.resolve(0)).then(function(num) { + var a: number = num; + var b: string = num; // Error: number ~> string +}); + +// Promise.resolve(Promise>) -> then(T) +Promise.resolve(Promise.resolve(Promise.resolve(0))).then(function(num) { + var a: number = num; + var b: string = num; // Error: number ~> string +}); + +//////////////////////////// +// == Promise.reject() == // +//////////////////////////// + +// TODO: Promise.reject(T) -> catch(T) +Promise.reject(0).catch(function(num) { + var a: number = num; + + // TODO + var b: string = num; // Error: number ~> string +}); + +// TODO: Promise.reject(Promise) -> catch(Promise) +Promise.reject(Promise.resolve(0)).then(function(num) { + var a: Promise = num; + + // TODO + var b: number = num; // Error: Promise ~> number +}); + +////////////////////////////////// +// == Promise.prototype.then == // +////////////////////////////////// + +// resolvedPromise.then():T -> then(T) +Promise.resolve(0) + .then(function(num) { return 'asdf'; }) + .then(function(str) { + var a: string = str; + var b: number = str; // Error: string ~> number + }); + +// resolvedPromise.then():Promise -> then(T) +Promise.resolve(0) + .then(function(num) { return Promise.resolve('asdf'); }) + .then(function(str) { + var a: string = str; + var b: number = str; // Error: string ~> number + }); + +// resolvedPromise.then():Promise> -> then(T) +Promise.resolve(0) + .then(function(num) { return Promise.resolve(Promise.resolve('asdf')); }) + .then(function(str) { + var a: string = str; + var b: number = str; // Error: string ~> number + }); + +// TODO: resolvedPromise.then() -> catch(T) +Promise.resolve(0) + .then(function(num) { + throw 'str'; + }) + .catch(function(str) { + var a: string = str; + + // TODO + var b: number = str; // Error: string ~> number + }); + +/////////////////////////////////// +// == Promise.prototype.catch == // +/////////////////////////////////// + +// rejectedPromise.catch():U -> then(U) +Promise.reject(0) + .catch(function(num) { return 'asdf'; }) + .then(function(str) { + var a: string = str; + var b: number = str; // Error: string ~> number + }); + +// rejectedPromise.catch():Promise -> then(U) +Promise.reject(0) + .catch(function(num) { return Promise.resolve('asdf'); }) + .then(function(str) { + var a: string = str; + var b: number = str; // Error: string ~> number + }); + +// rejectedPromise.catch():Promise> -> then(U) +Promise.reject(0) + .catch(function(num) { return Promise.resolve(Promise.resolve('asdf')); }) + .then(function(str) { + var a: string = str; + var b: number = str; // Error: string ~> number + }); + +// TODO: resolvedPromise -> catch() -> then():T +Promise.resolve(0) + .catch(function(err) {}) + .then(function(num) { + var a: number = num; + + // TODO + var b: string = num; // Error: string ~> number + }); diff --git a/tests/promises/resolve_global.js b/tests/promises/resolve_global.js new file mode 100644 index 000000000000..d53873b74cca --- /dev/null +++ b/tests/promises/resolve_global.js @@ -0,0 +1,41 @@ +/** + * test Promise name resolution + * @flow + */ + +/** + * 1. introduce shadowing bindings for important names + */ +class Promise {} + +/** + * 2. implicit refs to Promise during desugaring should be unaffected + */ +async function foo(x: boolean) { + if (x) { + return {bar: 'baz'}; + } else { + return null; + } +} + +async function run() { + console.log(await foo(true)); + console.log(await foo(false)); +} + +run(); + +/** + * 3. but explicit name refs from code and annos resolve + * using the usual rules + */ +// error: `Promise` in return expr is the local binding +async function bar() { + return Promise.resolve(0); +} + +// error: return type anno is a ref to the local binding +async function baz(): Promise { + return 0; +} diff --git a/tests/promises/resolve_void.js b/tests/promises/resolve_void.js new file mode 100644 index 000000000000..c98ec554aa54 --- /dev/null +++ b/tests/promises/resolve_void.js @@ -0,0 +1,5 @@ +// @flow + +(Promise.resolve(): Promise); // error + +(Promise.resolve(undefined): Promise); // error diff --git a/tests/pure_component/__snapshots__/jsfmt.spec.js.snap b/tests/pure_component/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..80fcdf839ab9 --- /dev/null +++ b/tests/pure_component/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,16 @@ +exports[`test test.js 1`] = ` +"var React = require('react'); + +class C extends React.PureComponent { + props: { x: number }; +} +(); // error (\`x\` is a required prop) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var React = require("react"); +class C extends React.PureComponent { + props: { x: number }; +} +;// error (\`x\` is a required prop) + +" +`; diff --git a/tests/pure_component/jsfmt.spec.js b/tests/pure_component/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/pure_component/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/pure_component/test.js b/tests/pure_component/test.js new file mode 100644 index 000000000000..ec8e3daed8ed --- /dev/null +++ b/tests/pure_component/test.js @@ -0,0 +1,6 @@ +var React = require('react'); + +class C extends React.PureComponent { + props: { x: number }; +} +(); // error (`x` is a required prop) diff --git a/tests/qualified/__snapshots__/jsfmt.spec.js.snap b/tests/qualified/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..345f03a98111 --- /dev/null +++ b/tests/qualified/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,25 @@ +exports[`test qualified.js 1`] = ` +"class C { } +var M = { C: C }; + +var x:M.C = 0; + +type foo = {bar: number}; + +declare var of_type_foo: foo; +type bar = typeof of_type_foo.bar; + +var a: bar = 42; +var b: bar = 'asdf'; // Error: string ~> number +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class C {} +var M = { C: C }; +var x: M.C = 0; +type foo = { bar: number }; +declare var of_type_foo: foo; +type bar = typeof of_type_foo.bar; +var a: bar = 42; +var b: bar = "asdf";// Error: string ~> number + +" +`; diff --git a/tests/qualified/jsfmt.spec.js b/tests/qualified/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/qualified/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/qualified/qualified.js b/tests/qualified/qualified.js new file mode 100644 index 000000000000..f5fc7616799c --- /dev/null +++ b/tests/qualified/qualified.js @@ -0,0 +1,12 @@ +class C { } +var M = { C: C }; + +var x:M.C = 0; + +type foo = {bar: number}; + +declare var of_type_foo: foo; +type bar = typeof of_type_foo.bar; + +var a: bar = 42; +var b: bar = 'asdf'; // Error: string ~> number diff --git a/tests/react/ArityError.react.js b/tests/react/ArityError.react.js new file mode 100644 index 000000000000..531fdc1e980e --- /dev/null +++ b/tests/react/ArityError.react.js @@ -0,0 +1,11 @@ +/** + * @providesModule ArityError.react + */ +var React = require('react'); +var AudienceInsightsContainer = React.createClass({ + renderComponent(AudienceInsights: ReactClass<*>) { + return ; + }, +}); + +module.exports = AudienceInsightsContainer; diff --git a/tests/react/__snapshots__/jsfmt.spec.js.snap b/tests/react/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..4f2dc3ec59f8 --- /dev/null +++ b/tests/react/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,395 @@ +exports[`test ArityError.react.js 1`] = ` +"/** + * @providesModule ArityError.react + */ +var React = require(\'react\'); +var AudienceInsightsContainer = React.createClass({ + renderComponent(AudienceInsights: ReactClass<*>) { + return ; + }, +}); + +module.exports = AudienceInsightsContainer; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test createElement_string.js 1`] = ` +"// @flow +import React from \'react\'; + +class Bar extends React.Component {} + +class Foo extends React.Component { + render() { + const Cmp = Math.random() < 0.5 ? \'div\' : Bar; + return (); + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +import React from \"react\"; +class Bar extends React.Component {} +class Foo extends React.Component { + render() { + const Cmp = (Math.random() < 0.5 ? \"div\" : Bar); + return ; + } +} + +" +`; + +exports[`test createElementRequiredProp_string.js 1`] = ` +"// @flow +import React from \'react\'; + +class Bar extends React.Component { + props: { + test: number, + }; + render() { + return ( +
+ {this.props.test} +
+ ) + } +} + +class Foo extends React.Component { + render() { + const Cmp = Math.random() < 0.5 ? \'div\' : Bar; + return (); + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +import React from \"react\"; +class Bar extends React.Component { + props: { test: number }; + render() { + return ( +
+ {this.props.test} +
+ ); + } +} +class Foo extends React.Component { + render() { + const Cmp = (Math.random() < 0.5 ? \"div\" : Bar); + return ; + } +} + +" +`; + +exports[`test import_react.js 1`] = ` +"/* @flow */ + +import react from \"react\"; +import {Component} from \"react\"; + +var a: Component<*,*,*> = new react.Component(); +var b: number = new react.Component(); // Error: ReactComponent ~> number +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test jsx_spread.js 1`] = ` +"/* @flow */ + +var React = require(\'react\'); +var Foo = React.createClass({ + propTypes: { + bar: React.PropTypes.string.isRequired, + }, +}); + +var props = {bar: 42}; +var blah = ; // error bar, number given string expected +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var React = require(\"react\"); +var Foo = React.createClass({ + propTypes: { bar: React.PropTypes.string.isRequired } +}); +var props = { bar: 42 }; +var blah = ;// error bar, number given string expected + +" +`; + +exports[`test proptype_arrayOf.js 1`] = ` +"/* @flow */ + +var React = require(\'react\'); +var Example = React.createClass({ + propTypes: { + arr: React.PropTypes.arrayOf(React.PropTypes.number).isRequired + }, +}); + +var ok_empty = +var ok_numbers = + +var fail_not_array = +var fail_mistyped_elems = +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var React = require(\"react\"); +var Example = React.createClass({ + propTypes: { arr: React.PropTypes.arrayOf(React.PropTypes.number).isRequired } +}); +var ok_empty = ; +var ok_numbers = ; +var fail_not_array = ; +var fail_mistyped_elems = ; + +" +`; + +exports[`test proptype_func.js 1`] = ` +"/* @flow */ + +var React = require(\'react\'); +var Example = React.createClass({ + propTypes: { + func: React.PropTypes.func.isRequired + }, +}); + +var ok_void = {}} />; +var ok_args = {}} />; +var ok_retval = 1} /> + +var fail_mistyped = +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var React = require(\"react\"); +var Example = React.createClass({ + propTypes: { func: React.PropTypes.func.isRequired } +}); +var ok_void = { + +}}/>; +var ok_args = { + +}}/>; +var ok_retval = 1}/>; +var fail_mistyped = ; + +" +`; + +exports[`test proptype_missing.js 1`] = ` +"/* @flow */ + +/* If you create a react component with createClass() but don\'t specify the + * propTypes, what should the type of props be? + * + * It used to be an empty object, but we didn\'t enforce that correctly, so + * people could do whatever they wanted with this.props. + * + * As of 0.21.0 it started to be an error when people used this.props in a + * strict equality situation. It was weird that this was only sometimes + * enforced, so glevi changed this.props to be Object by default. + * + * We may change this back to the empty object at some point and fix the + * situations where it didn\'t used to error + */ +var React = React.createClass({ + getID(): string { + // So this would have been an error in 0.21.0 if we didn\'t make this.props + // Object + switch (this.props.name) { + case \'a\': return \'Bob\'; + default: return \'Alice\'; + } + }, + + render() { + // But this never errored + return
; + } +}); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1020 + }, \"consequent\").indent(options.tabWidth)); + ^ + +TypeError: path.call(...).indent is not a function + at genericPrintNoParens (/src/printer.js:1020:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1005:36) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test proptype_object.js 1`] = ` +"/* @flow */ + +var React = require(\'react\'); +var Example = React.createClass({ + propTypes: { + object: React.PropTypes.object.isRequired + }, +}); + +var ok_empty = ; +var ok_props = ; + +var fail_mistyped = +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var React = require(\"react\"); +var Example = React.createClass({ + propTypes: { object: React.PropTypes.object.isRequired } +}); +var ok_empty = ; +var ok_props = ; +var fail_mistyped = ; + +" +`; + +exports[`test proptype_objectOf.js 1`] = ` +"/* @flow */ + +var React = require(\'react\'); +var Example = React.createClass({ + propTypes: { + obj: React.PropTypes.objectOf(React.PropTypes.number).isRequired + }, +}); + +var ok_empty = +var ok_numbers = + +var fail_not_object = +var fail_mistyped_props = +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var React = require(\"react\"); +var Example = React.createClass({ + propTypes: { + obj: React.PropTypes.objectOf(React.PropTypes.number).isRequired + } +}); +var ok_empty = ; +var ok_numbers = ; +var fail_not_object = ; +var fail_mistyped_props = ; + +" +`; + +exports[`test proptype_oneOf.js 1`] = ` +"/* @flow */ + +var React = require(\'react\'); +var Example = React.createClass({ + propTypes: { + literal: React.PropTypes.oneOf([\"foo\"]).isRequired + }, +}); + +var ex1 = ; +var ex2 = ; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var React = require(\"react\"); +var Example = React.createClass({ + propTypes: { literal: React.PropTypes.oneOf([ \"foo\" ]).isRequired } +}); +var ex1 = ; +var ex2 = ; + +" +`; + +exports[`test proptype_oneOfType.js 1`] = ` +"/* @flow */ + +var React = require(\'react\'); +var Example = React.createClass({ + propTypes: { + prop: React.PropTypes.oneOfType([ + React.PropTypes.string, + React.PropTypes.number + ]).isRequired + }, + render() { + if (typeof this.props.prop === \"string\") { + return
{this.props.prop}
+ } else { + return
{this.props.prop.toFixed(2)}
+ } + } +}); + +var ok_number = ; +var ok_string = ; + +var fail_bool = +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var React = require(\"react\"); +var Example = React.createClass({ + propTypes: { + prop: React.PropTypes.oneOfType( + [ React.PropTypes.string, React.PropTypes.number ] + ).isRequired + }, + render() { + if (typeof this.props.prop === \"string\") { + return
{this.props.prop}
; + } else { + return
{this.props.prop.toFixed(2)}
; + } + } +}); +var ok_number = ; +var ok_string = ; +var fail_bool = ; + +" +`; diff --git a/tests/react/createElementRequiredProp_string.js b/tests/react/createElementRequiredProp_string.js new file mode 100644 index 000000000000..5793a65c4799 --- /dev/null +++ b/tests/react/createElementRequiredProp_string.js @@ -0,0 +1,22 @@ +// @flow +import React from 'react'; + +class Bar extends React.Component { + props: { + test: number, + }; + render() { + return ( +
+ {this.props.test} +
+ ) + } +} + +class Foo extends React.Component { + render() { + const Cmp = Math.random() < 0.5 ? 'div' : Bar; + return (); + } +} diff --git a/tests/react/createElement_string.js b/tests/react/createElement_string.js new file mode 100644 index 000000000000..b6d548afec6a --- /dev/null +++ b/tests/react/createElement_string.js @@ -0,0 +1,11 @@ +// @flow +import React from 'react'; + +class Bar extends React.Component {} + +class Foo extends React.Component { + render() { + const Cmp = Math.random() < 0.5 ? 'div' : Bar; + return (); + } +} diff --git a/tests/react/import_react.js b/tests/react/import_react.js new file mode 100644 index 000000000000..d608b7c2f16c --- /dev/null +++ b/tests/react/import_react.js @@ -0,0 +1,7 @@ +/* @flow */ + +import react from "react"; +import {Component} from "react"; + +var a: Component<*,*,*> = new react.Component(); +var b: number = new react.Component(); // Error: ReactComponent ~> number diff --git a/tests/react/jsfmt.spec.js b/tests/react/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/react/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/react/jsx_spread.js b/tests/react/jsx_spread.js new file mode 100644 index 000000000000..d5214d7e40e6 --- /dev/null +++ b/tests/react/jsx_spread.js @@ -0,0 +1,11 @@ +/* @flow */ + +var React = require('react'); +var Foo = React.createClass({ + propTypes: { + bar: React.PropTypes.string.isRequired, + }, +}); + +var props = {bar: 42}; +var blah = ; // error bar, number given string expected diff --git a/tests/react/proptype_arrayOf.js b/tests/react/proptype_arrayOf.js new file mode 100644 index 000000000000..f6cd3822821b --- /dev/null +++ b/tests/react/proptype_arrayOf.js @@ -0,0 +1,14 @@ +/* @flow */ + +var React = require('react'); +var Example = React.createClass({ + propTypes: { + arr: React.PropTypes.arrayOf(React.PropTypes.number).isRequired + }, +}); + +var ok_empty = +var ok_numbers = + +var fail_not_array = +var fail_mistyped_elems = diff --git a/tests/react/proptype_func.js b/tests/react/proptype_func.js new file mode 100644 index 000000000000..52f452d63793 --- /dev/null +++ b/tests/react/proptype_func.js @@ -0,0 +1,14 @@ +/* @flow */ + +var React = require('react'); +var Example = React.createClass({ + propTypes: { + func: React.PropTypes.func.isRequired + }, +}); + +var ok_void = {}} />; +var ok_args = {}} />; +var ok_retval = 1} /> + +var fail_mistyped = diff --git a/tests/react/proptype_missing.js b/tests/react/proptype_missing.js new file mode 100644 index 000000000000..29fa980fab45 --- /dev/null +++ b/tests/react/proptype_missing.js @@ -0,0 +1,30 @@ +/* @flow */ + +/* If you create a react component with createClass() but don't specify the + * propTypes, what should the type of props be? + * + * It used to be an empty object, but we didn't enforce that correctly, so + * people could do whatever they wanted with this.props. + * + * As of 0.21.0 it started to be an error when people used this.props in a + * strict equality situation. It was weird that this was only sometimes + * enforced, so glevi changed this.props to be Object by default. + * + * We may change this back to the empty object at some point and fix the + * situations where it didn't used to error + */ +var React = React.createClass({ + getID(): string { + // So this would have been an error in 0.21.0 if we didn't make this.props + // Object + switch (this.props.name) { + case 'a': return 'Bob'; + default: return 'Alice'; + } + }, + + render() { + // But this never errored + return
; + } +}); diff --git a/tests/react/proptype_object.js b/tests/react/proptype_object.js new file mode 100644 index 000000000000..5a4754df6126 --- /dev/null +++ b/tests/react/proptype_object.js @@ -0,0 +1,13 @@ +/* @flow */ + +var React = require('react'); +var Example = React.createClass({ + propTypes: { + object: React.PropTypes.object.isRequired + }, +}); + +var ok_empty = ; +var ok_props = ; + +var fail_mistyped = diff --git a/tests/react/proptype_objectOf.js b/tests/react/proptype_objectOf.js new file mode 100644 index 000000000000..abead5617f30 --- /dev/null +++ b/tests/react/proptype_objectOf.js @@ -0,0 +1,14 @@ +/* @flow */ + +var React = require('react'); +var Example = React.createClass({ + propTypes: { + obj: React.PropTypes.objectOf(React.PropTypes.number).isRequired + }, +}); + +var ok_empty = +var ok_numbers = + +var fail_not_object = +var fail_mistyped_props = diff --git a/tests/react/proptype_oneOf.js b/tests/react/proptype_oneOf.js new file mode 100644 index 000000000000..f7486afb9a16 --- /dev/null +++ b/tests/react/proptype_oneOf.js @@ -0,0 +1,11 @@ +/* @flow */ + +var React = require('react'); +var Example = React.createClass({ + propTypes: { + literal: React.PropTypes.oneOf(["foo"]).isRequired + }, +}); + +var ex1 = ; +var ex2 = ; diff --git a/tests/react/proptype_oneOfType.js b/tests/react/proptype_oneOfType.js new file mode 100644 index 000000000000..3fa66d947cb6 --- /dev/null +++ b/tests/react/proptype_oneOfType.js @@ -0,0 +1,23 @@ +/* @flow */ + +var React = require('react'); +var Example = React.createClass({ + propTypes: { + prop: React.PropTypes.oneOfType([ + React.PropTypes.string, + React.PropTypes.number + ]).isRequired + }, + render() { + if (typeof this.props.prop === "string") { + return
{this.props.prop}
+ } else { + return
{this.props.prop.toFixed(2)}
+ } + } +}); + +var ok_number = ; +var ok_string = ; + +var fail_bool = diff --git a/tests/react_functional/__snapshots__/jsfmt.spec.js.snap b/tests/react_functional/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..ff19495104e0 --- /dev/null +++ b/tests/react_functional/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,32 @@ +exports[`test test.js 1`] = ` +"import React from \"react\"; + +function F(props: { foo: string }) {} +; // error: missing \`foo\` +; // error: number ~> string +; // ok + +// props subtyping is property-wise covariant +function G(props: { foo: string|numner }) {} +; // ok + +var Z = 0; +; // error, expected React component +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/react_functional/jsfmt.spec.js b/tests/react_functional/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/react_functional/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/react_functional/test.js b/tests/react_functional/test.js new file mode 100644 index 000000000000..acdec6f9409e --- /dev/null +++ b/tests/react_functional/test.js @@ -0,0 +1,13 @@ +import React from "react"; + +function F(props: { foo: string }) {} +; // error: missing `foo` +; // error: number ~> string +; // ok + +// props subtyping is property-wise covariant +function G(props: { foo: string|numner }) {} +; // ok + +var Z = 0; +; // error, expected React component diff --git a/tests/react_modules/__snapshots__/jsfmt.spec.js.snap b/tests/react_modules/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..2fedbc3a5ef0 --- /dev/null +++ b/tests/react_modules/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,241 @@ +exports[`test createclass-callsite.js 1`] = ` +"/* @flow */ +var React = require(\'react\'); +var Hello = require(\'./createclass-module\'); + +var HelloLocal = React.createClass({ + propTypes: { + name: React.PropTypes.string.isRequired, + }, + + render: function(): React.Element<*> { + return
{this.props.name}
; + } +}); + +var Callsite = React.createClass({ + render: function(): React.Element<*> { + return ( +
+ + +
+ ); + } +}); + +module.exports = Callsite; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test createclass-module.js 1`] = ` +"/* @flow */ +var React = require(\'react\'); + +var Hello = React.createClass({ + propTypes: { + name: React.PropTypes.string.isRequired, + }, + + render: function(): React.Element<*> { + return
{this.props.name}
; + } +}); + +module.exports = Hello; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test es6class-proptypes-callsite.js 1`] = ` +"/* @flow */ +import React from \'react\'; +import Hello from \'./es6class-proptypes-module\'; + +class HelloLocal extends React.Component { + defaultProps = {}; + propTypes = { + name: React.PropTypes.string.isRequired, + }; + render(): React.Element<*> { + return
{this.props.name}
; + } +} + +class Callsite extends React.Component { + render(): React.Element<*> { + return ( +
+ + +
+ ); + } +} + +module.exports = Callsite; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1180:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test es6class-proptypes-module.js 1`] = ` +"/* @flow */ +import React from \'react\'; + +class Hello extends React.Component { + defaultProps = {}; + propTypes = { + name: React.PropTypes.string.isRequired, + }; + + render(): React.Element<*> { + return
{this.props.name}
; + } +} + +module.exports = Hello; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1180:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test es6class-types-callsite.js 1`] = ` +"/* @flow */ +import React from \'react\'; +import Hello from \'./es6class-types-module\'; + +type Props = {name: string}; + +class HelloLocal extends React.Component { + props: Props; + + render(): React.Element<*> { + return
{this.props.name}
; + } +} + +class Callsite extends React.Component { + render(): React.Element<*> { + return ( +
+ + +
+ ); + } +} + +module.exports = Callsite; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1180:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test es6class-types-module.js 1`] = ` +"/* @flow */ +import React from \'react\'; + +type Props = {name: string}; + +class Hello extends React.Component<{}, Props, void>{ + props: Props; + static defaultProps: {}; + + render(): React.Element<*> { + return
{this.props.name}
; + } +} + +module.exports = Hello; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1180:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/react_modules/createclass-callsite.js b/tests/react_modules/createclass-callsite.js new file mode 100644 index 000000000000..5a5f3b175953 --- /dev/null +++ b/tests/react_modules/createclass-callsite.js @@ -0,0 +1,26 @@ +/* @flow */ +var React = require('react'); +var Hello = require('./createclass-module'); + +var HelloLocal = React.createClass({ + propTypes: { + name: React.PropTypes.string.isRequired, + }, + + render: function(): React.Element<*> { + return
{this.props.name}
; + } +}); + +var Callsite = React.createClass({ + render: function(): React.Element<*> { + return ( +
+ + +
+ ); + } +}); + +module.exports = Callsite; diff --git a/tests/react_modules/createclass-module.js b/tests/react_modules/createclass-module.js new file mode 100644 index 000000000000..3b7e25cdd5dc --- /dev/null +++ b/tests/react_modules/createclass-module.js @@ -0,0 +1,14 @@ +/* @flow */ +var React = require('react'); + +var Hello = React.createClass({ + propTypes: { + name: React.PropTypes.string.isRequired, + }, + + render: function(): React.Element<*> { + return
{this.props.name}
; + } +}); + +module.exports = Hello; diff --git a/tests/react_modules/es6class-proptypes-callsite.js b/tests/react_modules/es6class-proptypes-callsite.js new file mode 100644 index 000000000000..6a50aa90ee7a --- /dev/null +++ b/tests/react_modules/es6class-proptypes-callsite.js @@ -0,0 +1,26 @@ +/* @flow */ +import React from 'react'; +import Hello from './es6class-proptypes-module'; + +class HelloLocal extends React.Component { + defaultProps = {}; + propTypes = { + name: React.PropTypes.string.isRequired, + }; + render(): React.Element<*> { + return
{this.props.name}
; + } +} + +class Callsite extends React.Component { + render(): React.Element<*> { + return ( +
+ + +
+ ); + } +} + +module.exports = Callsite; diff --git a/tests/react_modules/es6class-proptypes-module.js b/tests/react_modules/es6class-proptypes-module.js new file mode 100644 index 000000000000..86e55da1b28f --- /dev/null +++ b/tests/react_modules/es6class-proptypes-module.js @@ -0,0 +1,15 @@ +/* @flow */ +import React from 'react'; + +class Hello extends React.Component { + defaultProps = {}; + propTypes = { + name: React.PropTypes.string.isRequired, + }; + + render(): React.Element<*> { + return
{this.props.name}
; + } +} + +module.exports = Hello; diff --git a/tests/react_modules/es6class-types-callsite.js b/tests/react_modules/es6class-types-callsite.js new file mode 100644 index 000000000000..c5586b682c17 --- /dev/null +++ b/tests/react_modules/es6class-types-callsite.js @@ -0,0 +1,26 @@ +/* @flow */ +import React from 'react'; +import Hello from './es6class-types-module'; + +type Props = {name: string}; + +class HelloLocal extends React.Component { + props: Props; + + render(): React.Element<*> { + return
{this.props.name}
; + } +} + +class Callsite extends React.Component { + render(): React.Element<*> { + return ( +
+ + +
+ ); + } +} + +module.exports = Callsite; diff --git a/tests/react_modules/es6class-types-module.js b/tests/react_modules/es6class-types-module.js new file mode 100644 index 000000000000..d91eeab83f36 --- /dev/null +++ b/tests/react_modules/es6class-types-module.js @@ -0,0 +1,15 @@ +/* @flow */ +import React from 'react'; + +type Props = {name: string}; + +class Hello extends React.Component<{}, Props, void>{ + props: Props; + static defaultProps: {}; + + render(): React.Element<*> { + return
{this.props.name}
; + } +} + +module.exports = Hello; diff --git a/tests/react_modules/jsfmt.spec.js b/tests/react_modules/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/react_modules/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/rec/__snapshots__/jsfmt.spec.js.snap b/tests/rec/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..e319f1c39127 --- /dev/null +++ b/tests/rec/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,226 @@ +exports[`test issue-598.js 1`] = ` +"/* @flow */ + +type F
= { foo(x: A): F } +declare function foo(x: any): F; +({ foo }: F); + +function bar(y: F): F { return y; } +function bar1(y: F): F { return y; } +function bar2(y: F): F { return y; } + +type Functor = { + map(f: (val: A) => B): Functor +} + +function identity(val: A): Functor { + return { + map(f: (_: typeof val) => B): Functor { return identity(f(val)) } + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1479:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test issue-1228.js 1`] = ` +"/* @flow */ + +type Task + = { chain(next:(input:value) => Task): + Task + } + +function id(x: Task): Task { return x; } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1479:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test.js 1`] = ` +"class P { x: X; } // this is like Promise + +type Pstar = X | Pstar>; // this is like Promise* + +var p: P = new P; +(p.x: string); // error + +var pstar: Pstar = 0; // OK +(pstar: number); // error, but limit potentially unbounded number of errors! + // e.g., P ~/~ number, P> ~/~ number, ... + +pstar = p; // OK +(pstar.x: string); // error + +pstar = (new P: P>); // OK +(pstar.x: string); // error +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1172:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test2.js 1`] = ` +"var a = []; // Array ~> a +function bar() { + a = a.concat([]); // terminate despite expanding types: + // a ~> .concat(Array) + // Array ~> .concat(Array) + // Array ~> a + // Array ~> .concat(Array) + // Array ~> a +}; + +class A { + x: A>; +} +var a_ = new A; +function foo0() { + a_ = a_.x; // terminate despite expanding types +} + +type T = { y: S }; +type S = T>; +function foo1(b: S<*>) { + b = b.y; // terminate despite expanding types, OK + // S<*> = { y: S> } + // Both S> and S<*> expand to { y: { y: ... }}. +} + +class D { } +class B extends D { } +class C extends B { } +((new C: C): D) // error: number ~/~ string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1172:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test3.js 1`] = ` +"type I = () => I>; +type J = () => J>; + +function foo(x: I): J { + return x; // terminate despite expanding types, OK + // I and J both expand to () => () => ... +} + +type Q = { x: X; } +type P = () => Q>; + +function bar(x: P): () => P { + return x; // terminate despite expanding types, error + // P = () => { x: P } + // () => P = () => () => { x: P } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1479:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test4.js 1`] = ` +"type T = T // cycle in type alias should not cause non-termination +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +type T = T;// cycle in type alias should not cause non-termination + +" +`; + +exports[`test test5.js 1`] = ` +"/* @flow */ + +type NestedArray = Array>; + +function flatten(arrArg: NestedArray) { + let arr = arrArg; + while (true) { + arr = Array.prototype.concat.apply([], arr); + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1479:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/rec/issue-1228.js b/tests/rec/issue-1228.js new file mode 100644 index 000000000000..6eae30ab211c --- /dev/null +++ b/tests/rec/issue-1228.js @@ -0,0 +1,8 @@ +/* @flow */ + +type Task + = { chain(next:(input:value) => Task): + Task + } + +function id(x: Task): Task { return x; } diff --git a/tests/rec/issue-598.js b/tests/rec/issue-598.js new file mode 100644 index 000000000000..b42d9ec635c8 --- /dev/null +++ b/tests/rec/issue-598.js @@ -0,0 +1,19 @@ +/* @flow */ + +type F = { foo(x: A): F } +declare function foo(x: any): F; +({ foo }: F); + +function bar(y: F): F { return y; } +function bar1(y: F): F { return y; } +function bar2(y: F): F { return y; } + +type Functor = { + map(f: (val: A) => B): Functor +} + +function identity(val: A): Functor { + return { + map(f: (_: typeof val) => B): Functor { return identity(f(val)) } + } +} diff --git a/tests/rec/jsfmt.spec.js b/tests/rec/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/rec/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/rec/test.js b/tests/rec/test.js new file mode 100644 index 000000000000..4b224d0ce984 --- /dev/null +++ b/tests/rec/test.js @@ -0,0 +1,16 @@ +class P { x: X; } // this is like Promise + +type Pstar = X | Pstar>; // this is like Promise* + +var p: P = new P; +(p.x: string); // error + +var pstar: Pstar = 0; // OK +(pstar: number); // error, but limit potentially unbounded number of errors! + // e.g., P ~/~ number, P> ~/~ number, ... + +pstar = p; // OK +(pstar.x: string); // error + +pstar = (new P: P>); // OK +(pstar.x: string); // error diff --git a/tests/rec/test2.js b/tests/rec/test2.js new file mode 100644 index 000000000000..f52df80b5415 --- /dev/null +++ b/tests/rec/test2.js @@ -0,0 +1,30 @@ +var a = []; // Array ~> a +function bar() { + a = a.concat([]); // terminate despite expanding types: + // a ~> .concat(Array) + // Array ~> .concat(Array) + // Array ~> a + // Array ~> .concat(Array) + // Array ~> a +}; + +class A { + x: A>; +} +var a_ = new A; +function foo0() { + a_ = a_.x; // terminate despite expanding types +} + +type T = { y: S }; +type S = T>; +function foo1(b: S<*>) { + b = b.y; // terminate despite expanding types, OK + // S<*> = { y: S> } + // Both S> and S<*> expand to { y: { y: ... }}. +} + +class D { } +class B extends D { } +class C extends B { } +((new C: C): D) // error: number ~/~ string diff --git a/tests/rec/test3.js b/tests/rec/test3.js new file mode 100644 index 000000000000..9c2baa21a3b5 --- /dev/null +++ b/tests/rec/test3.js @@ -0,0 +1,16 @@ +type I = () => I>; +type J = () => J>; + +function foo(x: I): J { + return x; // terminate despite expanding types, OK + // I and J both expand to () => () => ... +} + +type Q = { x: X; } +type P = () => Q>; + +function bar(x: P): () => P { + return x; // terminate despite expanding types, error + // P = () => { x: P } + // () => P = () => () => { x: P } +} diff --git a/tests/rec/test4.js b/tests/rec/test4.js new file mode 100644 index 000000000000..7116cd9dbfbc --- /dev/null +++ b/tests/rec/test4.js @@ -0,0 +1 @@ +type T = T // cycle in type alias should not cause non-termination diff --git a/tests/rec/test5.js b/tests/rec/test5.js new file mode 100644 index 000000000000..3052dcead8c2 --- /dev/null +++ b/tests/rec/test5.js @@ -0,0 +1,10 @@ +/* @flow */ + +type NestedArray = Array>; + +function flatten(arrArg: NestedArray) { + let arr = arrArg; + while (true) { + arr = Array.prototype.concat.apply([], arr); + } +} diff --git a/tests/recheck-haste/A1.js b/tests/recheck-haste/A1.js new file mode 100644 index 000000000000..638d95f80d1d --- /dev/null +++ b/tests/recheck-haste/A1.js @@ -0,0 +1,4 @@ +/** + * @providesModule A + * @flow + */ diff --git a/tests/recheck-haste/A3.js b/tests/recheck-haste/A3.js new file mode 100644 index 000000000000..62bd9a77de77 --- /dev/null +++ b/tests/recheck-haste/A3.js @@ -0,0 +1,3 @@ +// @flow + +require('A'); diff --git a/tests/recheck-haste/__snapshots__/jsfmt.spec.js.snap b/tests/recheck-haste/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..9bc3b995516a --- /dev/null +++ b/tests/recheck-haste/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,21 @@ +exports[`test A1.js 1`] = ` +"/** + * @providesModule A + * @flow + */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; + +exports[`test A3.js 1`] = ` +"// @flow + +require('A'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +require("A"); + +" +`; diff --git a/tests/recheck-haste/jsfmt.spec.js b/tests/recheck-haste/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/recheck-haste/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/recheck-haste/tmp1A/A2.js b/tests/recheck-haste/tmp1A/A2.js new file mode 100644 index 000000000000..cf331fe3deb2 --- /dev/null +++ b/tests/recheck-haste/tmp1A/A2.js @@ -0,0 +1,4 @@ +/** + * @providesModule B + * @flow + */ diff --git a/tests/recheck-haste/tmp1A/__snapshots__/jsfmt.spec.js.snap b/tests/recheck-haste/tmp1A/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..b200fabe0f33 --- /dev/null +++ b/tests/recheck-haste/tmp1A/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,10 @@ +exports[`test A2.js 1`] = ` +"/** + * @providesModule B + * @flow + */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; diff --git a/tests/recheck-haste/tmp1A/jsfmt.spec.js b/tests/recheck-haste/tmp1A/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/recheck-haste/tmp1A/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/recheck-haste/tmp2A/A3.js b/tests/recheck-haste/tmp2A/A3.js new file mode 100644 index 000000000000..c62d55de1472 --- /dev/null +++ b/tests/recheck-haste/tmp2A/A3.js @@ -0,0 +1,3 @@ +// @flow + +require('B'); diff --git a/tests/recheck-haste/tmp2A/__snapshots__/jsfmt.spec.js.snap b/tests/recheck-haste/tmp2A/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..9b95ee207fcb --- /dev/null +++ b/tests/recheck-haste/tmp2A/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,10 @@ +exports[`test A3.js 1`] = ` +"// @flow + +require('B'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +require("B"); + +" +`; diff --git a/tests/recheck-haste/tmp2A/jsfmt.spec.js b/tests/recheck-haste/tmp2A/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/recheck-haste/tmp2A/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/recheck/__snapshots__/jsfmt.spec.js.snap b/tests/recheck/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..ef429d1fc181 --- /dev/null +++ b/tests/recheck/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,352 @@ +exports[`test a1.js 1`] = ` +"// @flow + +function foo(x: number): string { return 5; } + +foo(0); + +module.exports = foo; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +function foo(x: number): string { + return 5; +} +foo(0); +module.exports = foo; + +" +`; + +exports[`test a2.js 1`] = ` +"// @flow + +const foo = require(\'./a1\'); + +module.exports = foo(\"\"); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +const foo = require(\"./a1\"); +module.exports = foo(\"\"); + +" +`; + +exports[`test a3.js 1`] = ` +"// @flow + +const five = require(\'./a2\'); + +(five + five: string); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +const five = require(\"./a2\"); +(five + five: string); + +" +`; + +exports[`test b0.js 1`] = ` +"// @flow + +class C { x: C; } +class E { x: C; } + +module.exports = { C, E }; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +class C { + x: C; +} +class E { + x: C; +} +module.exports = { C, E }; + +" +`; + +exports[`test b1.js 1`] = ` +"// @flow + +import { C, E } from \"./b0\"; +function foo() { return C; } +function bar() { return E; } +let X = foo(); +class F extends X { } +class D extends F { } +module.exports = { C, D }; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +import { C, E } from \"./b0\"; +function foo() { + return C; +} +function bar() { + return E; +} +let X = foo(); +class F extends X {} +class D extends F {} +module.exports = { C, D }; + +" +`; + +exports[`test b2.js 1`] = ` +"// @flow + +module.exports = require(\"./b1\"); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +module.exports = require(\"./b1\"); + +" +`; + +exports[`test b3.js 1`] = ` +"// @flow + +import { C, D } from \"./b2\"; + +(new D: C); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +import { C, D } from \"./b2\"; +(new D(): C); + +" +`; + +exports[`test c1.js 1`] = ` +"// @flow + +export function foo(props: { x: number }) { } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +export function foo(props: { x: number }) { + +} + +" +`; + +exports[`test c2.js 1`] = ` +"// @flow + +import { foo } from \"./c1\"; + +export function bar(props: { x: number }) { + foo({ x: 0 }); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +import { foo } from \"./c1\"; +export function bar(props: { x: number }) { + foo({ x: 0 }); +} + +" +`; + +exports[`test c3.js 1`] = ` +"// @flow + +import { bar } from \"./c2\"; + +bar({ x: 0 }); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +import { bar } from \"./c2\"; +bar({ x: 0 }); + +" +`; + +exports[`test d1.js 1`] = ` +"// @flow + +export class A {} +export class B {} +export var x = new A; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +export class A {} +export class B {} +export var x = new A(); + +" +`; + +exports[`test d2.js 1`] = ` +"// @flow + +import {A, x} from \"./d1\"; +export var y: A = x; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +import { A, x } from \"./d1\"; +export var y: A = x; + +" +`; + +exports[`test e1.js 1`] = ` +"// @flow + +export type Action = + | { type: \'FOO\' } + | { type: \'BAR\' } +; + +export const LIFE = 42; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test e2.js 1`] = ` +"// @flow + +import type { Action } from \'./e1\'; + +const f = (): Action => { + return { type: \'FOO\' }; +} + +import { LIFE } from \'./e1\'; + +(LIFE: 42); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/ast-types/lib/types.js:60 + throw new Error(str + \" does not match type \" + this); + ^ + +Error: {type: NumericLiteralTypeAnnotation, start: 139, end: 141, loc: [object Object], value: 42, extra: [object Object]} does not match type Printable + at Type.Tp.assert (/node_modules/ast-types/lib/types.js:60:19) + at genericPrintNoParens (/src/printer.js:221:24) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) +" +`; + +exports[`test f1.js 1`] = ` +"// @flow + +type T = { x: number }; +type S = { x: string }; + +declare var a: T; +declare var b: S; +declare var c: T; + +module.exports = { a, b, c }; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +type T = { x: number }; +type S = { x: string }; +declare var a: T; +declare var b: S; +declare var c: T; +module.exports = { a, b, c }; + +" +`; + +exports[`test f2.js 1`] = ` +"// @flow + +var { a, b, c } = require(\'./f1\'); +(c: { x: number }); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +var { a, b, c } = require(\"./f1\"); +(c: { x: number }); + +" +`; + +exports[`test g1.js 1`] = ` +"// @flow + +export class C { } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +export class C {} + +" +`; + +exports[`test g2.js 1`] = ` +"// @flow + +import { C } from \'./g1\'; + +class D extends C { } + +module.exports = { D }; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +import { C } from \"./g1\"; +class D extends C {} +module.exports = { D }; + +" +`; + +exports[`test g3.js 1`] = ` +"// @flow + +import { C } from \'./g1\'; +import { D } from \'./g2\'; + +(new D: C) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +import { C } from \"./g1\"; +import { D } from \"./g2\"; +(new D(): C); + +" +`; + +exports[`test h1.js 1`] = ` +"// @flow + +export type Foo = number; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +export type Foo = number; + +" +`; + +exports[`test h2.js 1`] = ` +"// @flow + +import type { Foo } from \'./h1\'; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +import type { Foo } from \"./h1\"; + +" +`; diff --git a/tests/recheck/a1.js b/tests/recheck/a1.js new file mode 100644 index 000000000000..2a9c78c41807 --- /dev/null +++ b/tests/recheck/a1.js @@ -0,0 +1,7 @@ +// @flow + +function foo(x: number): string { return 5; } + +foo(0); + +module.exports = foo; diff --git a/tests/recheck/a2.js b/tests/recheck/a2.js new file mode 100644 index 000000000000..cfb5d1cd21db --- /dev/null +++ b/tests/recheck/a2.js @@ -0,0 +1,5 @@ +// @flow + +const foo = require('./a1'); + +module.exports = foo(""); diff --git a/tests/recheck/a3.js b/tests/recheck/a3.js new file mode 100644 index 000000000000..93135f1dfd8b --- /dev/null +++ b/tests/recheck/a3.js @@ -0,0 +1,5 @@ +// @flow + +const five = require('./a2'); + +(five + five: string); diff --git a/tests/recheck/b0.js b/tests/recheck/b0.js new file mode 100644 index 000000000000..a054df3838fb --- /dev/null +++ b/tests/recheck/b0.js @@ -0,0 +1,6 @@ +// @flow + +class C { x: C; } +class E { x: C; } + +module.exports = { C, E }; diff --git a/tests/recheck/b1.js b/tests/recheck/b1.js new file mode 100644 index 000000000000..02d08fc99ae4 --- /dev/null +++ b/tests/recheck/b1.js @@ -0,0 +1,9 @@ +// @flow + +import { C, E } from "./b0"; +function foo() { return C; } +function bar() { return E; } +let X = foo(); +class F extends X { } +class D extends F { } +module.exports = { C, D }; diff --git a/tests/recheck/b2.js b/tests/recheck/b2.js new file mode 100644 index 000000000000..89602457fce4 --- /dev/null +++ b/tests/recheck/b2.js @@ -0,0 +1,3 @@ +// @flow + +module.exports = require("./b1"); diff --git a/tests/recheck/b3.js b/tests/recheck/b3.js new file mode 100644 index 000000000000..948f528d624e --- /dev/null +++ b/tests/recheck/b3.js @@ -0,0 +1,5 @@ +// @flow + +import { C, D } from "./b2"; + +(new D: C); diff --git a/tests/recheck/c1.js b/tests/recheck/c1.js new file mode 100644 index 000000000000..34760f1969f4 --- /dev/null +++ b/tests/recheck/c1.js @@ -0,0 +1,3 @@ +// @flow + +export function foo(props: { x: number }) { } diff --git a/tests/recheck/c2.js b/tests/recheck/c2.js new file mode 100644 index 000000000000..5a7549516cf5 --- /dev/null +++ b/tests/recheck/c2.js @@ -0,0 +1,7 @@ +// @flow + +import { foo } from "./c1"; + +export function bar(props: { x: number }) { + foo({ x: 0 }); +} diff --git a/tests/recheck/c3.js b/tests/recheck/c3.js new file mode 100644 index 000000000000..cf0e94248644 --- /dev/null +++ b/tests/recheck/c3.js @@ -0,0 +1,5 @@ +// @flow + +import { bar } from "./c2"; + +bar({ x: 0 }); diff --git a/tests/recheck/d1.js b/tests/recheck/d1.js new file mode 100644 index 000000000000..b5dd060f3f0f --- /dev/null +++ b/tests/recheck/d1.js @@ -0,0 +1,5 @@ +// @flow + +export class A {} +export class B {} +export var x = new A; diff --git a/tests/recheck/d2.js b/tests/recheck/d2.js new file mode 100644 index 000000000000..7dd91ff07f40 --- /dev/null +++ b/tests/recheck/d2.js @@ -0,0 +1,4 @@ +// @flow + +import {A, x} from "./d1"; +export var y: A = x; diff --git a/tests/recheck/e1.js b/tests/recheck/e1.js new file mode 100644 index 000000000000..d0de69b553ef --- /dev/null +++ b/tests/recheck/e1.js @@ -0,0 +1,8 @@ +// @flow + +export type Action = + | { type: 'FOO' } + | { type: 'BAR' } +; + +export const LIFE = 42; diff --git a/tests/recheck/e2.js b/tests/recheck/e2.js new file mode 100644 index 000000000000..cb730cc55dc1 --- /dev/null +++ b/tests/recheck/e2.js @@ -0,0 +1,11 @@ +// @flow + +import type { Action } from './e1'; + +const f = (): Action => { + return { type: 'FOO' }; +} + +import { LIFE } from './e1'; + +(LIFE: 42); diff --git a/tests/recheck/f1.js b/tests/recheck/f1.js new file mode 100644 index 000000000000..f367b83232d6 --- /dev/null +++ b/tests/recheck/f1.js @@ -0,0 +1,10 @@ +// @flow + +type T = { x: number }; +type S = { x: string }; + +declare var a: T; +declare var b: S; +declare var c: T; + +module.exports = { a, b, c }; diff --git a/tests/recheck/f2.js b/tests/recheck/f2.js new file mode 100644 index 000000000000..33c925aefac5 --- /dev/null +++ b/tests/recheck/f2.js @@ -0,0 +1,4 @@ +// @flow + +var { a, b, c } = require('./f1'); +(c: { x: number }); diff --git a/tests/recheck/g1.js b/tests/recheck/g1.js new file mode 100644 index 000000000000..2db46640d122 --- /dev/null +++ b/tests/recheck/g1.js @@ -0,0 +1,3 @@ +// @flow + +export class C { } diff --git a/tests/recheck/g2.js b/tests/recheck/g2.js new file mode 100644 index 000000000000..5da8b17aff83 --- /dev/null +++ b/tests/recheck/g2.js @@ -0,0 +1,7 @@ +// @flow + +import { C } from './g1'; + +class D extends C { } + +module.exports = { D }; diff --git a/tests/recheck/g3.js b/tests/recheck/g3.js new file mode 100644 index 000000000000..a922c8b50bb3 --- /dev/null +++ b/tests/recheck/g3.js @@ -0,0 +1,6 @@ +// @flow + +import { C } from './g1'; +import { D } from './g2'; + +(new D: C) diff --git a/tests/recheck/h1.js b/tests/recheck/h1.js new file mode 100644 index 000000000000..658225cb3857 --- /dev/null +++ b/tests/recheck/h1.js @@ -0,0 +1,3 @@ +// @flow + +export type Foo = number; diff --git a/tests/recheck/h2.js b/tests/recheck/h2.js new file mode 100644 index 000000000000..27cc789c77ab --- /dev/null +++ b/tests/recheck/h2.js @@ -0,0 +1,3 @@ +// @flow + +import type { Foo } from './h1'; diff --git a/tests/recheck/jsfmt.spec.js b/tests/recheck/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/recheck/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/recheck/tmp1a/__snapshots__/jsfmt.spec.js.snap b/tests/recheck/tmp1a/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..35a87e8c321c --- /dev/null +++ b/tests/recheck/tmp1a/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,18 @@ +exports[`test a1.js 1`] = ` +"// @flow + +function foo(x: number): number { return 5; } + +foo(0); + +module.exports = foo; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +function foo(x: number): number { + return 5; +} +foo(0); +module.exports = foo; + +" +`; diff --git a/tests/recheck/tmp1a/a1.js b/tests/recheck/tmp1a/a1.js new file mode 100644 index 000000000000..6aea0d8312b9 --- /dev/null +++ b/tests/recheck/tmp1a/a1.js @@ -0,0 +1,7 @@ +// @flow + +function foo(x: number): number { return 5; } + +foo(0); + +module.exports = foo; diff --git a/tests/recheck/tmp1a/jsfmt.spec.js b/tests/recheck/tmp1a/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/recheck/tmp1a/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/recheck/tmp1b/__snapshots__/jsfmt.spec.js.snap b/tests/recheck/tmp1b/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..cd873290ace9 --- /dev/null +++ b/tests/recheck/tmp1b/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,26 @@ +exports[`test b1.js 1`] = ` +"// @flow + +import { C, E } from "./b0"; +function foo() { return C; } +function bar() { return E; } +let X = bar(); +class F extends X { } +class D extends F { } +module.exports = { C, D }; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +import { C, E } from "./b0"; +function foo() { + return C; +} +function bar() { + return E; +} +let X = bar(); +class F extends X {} +class D extends F {} +module.exports = { C, D }; + +" +`; diff --git a/tests/recheck/tmp1b/b1.js b/tests/recheck/tmp1b/b1.js new file mode 100644 index 000000000000..468f26a53950 --- /dev/null +++ b/tests/recheck/tmp1b/b1.js @@ -0,0 +1,9 @@ +// @flow + +import { C, E } from "./b0"; +function foo() { return C; } +function bar() { return E; } +let X = bar(); +class F extends X { } +class D extends F { } +module.exports = { C, D }; diff --git a/tests/recheck/tmp1b/jsfmt.spec.js b/tests/recheck/tmp1b/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/recheck/tmp1b/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/recheck/tmp1c/__snapshots__/jsfmt.spec.js.snap b/tests/recheck/tmp1c/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..812fec93444b --- /dev/null +++ b/tests/recheck/tmp1c/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,17 @@ +exports[`test c2.js 1`] = ` +"// @flow + +import { foo } from "./c1"; + +export function bar(props: { y: number }) { + foo({ y: 0 }); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +import { foo } from "./c1"; +export function bar(props: { y: number }) { + foo({ y: 0 }); +} + +" +`; diff --git a/tests/recheck/tmp1c/c2.js b/tests/recheck/tmp1c/c2.js new file mode 100644 index 000000000000..78f854440217 --- /dev/null +++ b/tests/recheck/tmp1c/c2.js @@ -0,0 +1,7 @@ +// @flow + +import { foo } from "./c1"; + +export function bar(props: { y: number }) { + foo({ y: 0 }); +} diff --git a/tests/recheck/tmp1c/jsfmt.spec.js b/tests/recheck/tmp1c/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/recheck/tmp1c/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/recheck/tmp1d/__snapshots__/jsfmt.spec.js.snap b/tests/recheck/tmp1d/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..0bff460b8b4e --- /dev/null +++ b/tests/recheck/tmp1d/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,14 @@ +exports[`test d1.js 1`] = ` +"// @flow + +export class A {} +export class B {} +export var x = new B; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +export class A {} +export class B {} +export var x = new B(); + +" +`; diff --git a/tests/recheck/tmp1d/d1.js b/tests/recheck/tmp1d/d1.js new file mode 100644 index 000000000000..bfcfcbdf7f2c --- /dev/null +++ b/tests/recheck/tmp1d/d1.js @@ -0,0 +1,5 @@ +// @flow + +export class A {} +export class B {} +export var x = new B; diff --git a/tests/recheck/tmp1d/jsfmt.spec.js b/tests/recheck/tmp1d/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/recheck/tmp1d/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/recheck/tmp1e/__snapshots__/jsfmt.spec.js.snap b/tests/recheck/tmp1e/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..6aea92cc691b --- /dev/null +++ b/tests/recheck/tmp1e/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,30 @@ +exports[`test e2.js 1`] = ` +"// @flow + +import type { Action } from \'./e1\'; + +const f = (): Action => { + return { type: \'QUX\' }; +} + +import { LIFE } from \'./e1\'; + +(LIFE: 42); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/ast-types/lib/types.js:60 + throw new Error(str + \" does not match type \" + this); + ^ + +Error: {type: NumericLiteralTypeAnnotation, start: 139, end: 141, loc: [object Object], value: 42, extra: [object Object]} does not match type Printable + at Type.Tp.assert (/node_modules/ast-types/lib/types.js:60:19) + at genericPrintNoParens (/src/printer.js:221:24) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) +" +`; diff --git a/tests/recheck/tmp1e/e2.js b/tests/recheck/tmp1e/e2.js new file mode 100644 index 000000000000..337afa6cfe53 --- /dev/null +++ b/tests/recheck/tmp1e/e2.js @@ -0,0 +1,11 @@ +// @flow + +import type { Action } from './e1'; + +const f = (): Action => { + return { type: 'QUX' }; +} + +import { LIFE } from './e1'; + +(LIFE: 42); diff --git a/tests/recheck/tmp1e/jsfmt.spec.js b/tests/recheck/tmp1e/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/recheck/tmp1e/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/recheck/tmp1f/__snapshots__/jsfmt.spec.js.snap b/tests/recheck/tmp1f/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..e93239948127 --- /dev/null +++ b/tests/recheck/tmp1f/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,22 @@ +exports[`test f1.js 1`] = ` +"// @flow + +type T = { x: number }; +type S = { x: string }; + +declare var a: T; +declare var b: S; +declare var c: S; + +module.exports = { a, b, c }; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +type T = { x: number }; +type S = { x: string }; +declare var a: T; +declare var b: S; +declare var c: S; +module.exports = { a, b, c }; + +" +`; diff --git a/tests/recheck/tmp1f/f1.js b/tests/recheck/tmp1f/f1.js new file mode 100644 index 000000000000..4fa64d3756fc --- /dev/null +++ b/tests/recheck/tmp1f/f1.js @@ -0,0 +1,10 @@ +// @flow + +type T = { x: number }; +type S = { x: string }; + +declare var a: T; +declare var b: S; +declare var c: S; + +module.exports = { a, b, c }; diff --git a/tests/recheck/tmp1f/jsfmt.spec.js b/tests/recheck/tmp1f/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/recheck/tmp1f/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/recheck/tmp1g/__snapshots__/jsfmt.spec.js.snap b/tests/recheck/tmp1g/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..124e3714c8f1 --- /dev/null +++ b/tests/recheck/tmp1g/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,12 @@ +exports[`test g1.js 1`] = ` +"// @flow + +export class C { } +export var extra = null; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +export class C {} +export var extra = null; + +" +`; diff --git a/tests/recheck/tmp1g/g1.js b/tests/recheck/tmp1g/g1.js new file mode 100644 index 000000000000..bef8fa52cafa --- /dev/null +++ b/tests/recheck/tmp1g/g1.js @@ -0,0 +1,4 @@ +// @flow + +export class C { } +export var extra = null; diff --git a/tests/recheck/tmp1g/jsfmt.spec.js b/tests/recheck/tmp1g/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/recheck/tmp1g/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/recheck/tmp1h/__snapshots__/jsfmt.spec.js.snap b/tests/recheck/tmp1h/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..13d5fbb33b30 --- /dev/null +++ b/tests/recheck/tmp1h/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,10 @@ +exports[`test h1.js 1`] = ` +"// @flow + +export type Bar = number; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +export type Bar = number; + +" +`; diff --git a/tests/recheck/tmp1h/h1.js b/tests/recheck/tmp1h/h1.js new file mode 100644 index 000000000000..ef374d6a5c5c --- /dev/null +++ b/tests/recheck/tmp1h/h1.js @@ -0,0 +1,3 @@ +// @flow + +export type Bar = number; diff --git a/tests/recheck/tmp1h/jsfmt.spec.js b/tests/recheck/tmp1h/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/recheck/tmp1h/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/recheck/tmp2a/__snapshots__/jsfmt.spec.js.snap b/tests/recheck/tmp2a/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..dd2074794bdf --- /dev/null +++ b/tests/recheck/tmp2a/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,18 @@ +exports[`test a1.js 1`] = ` +"// @flow + +function foo(x: number): number { return 5; } + +foo(""); + +module.exports = foo; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +function foo(x: number): number { + return 5; +} +foo(""); +module.exports = foo; + +" +`; diff --git a/tests/recheck/tmp2a/a1.js b/tests/recheck/tmp2a/a1.js new file mode 100644 index 000000000000..16f0c5ce36ca --- /dev/null +++ b/tests/recheck/tmp2a/a1.js @@ -0,0 +1,7 @@ +// @flow + +function foo(x: number): number { return 5; } + +foo(""); + +module.exports = foo; diff --git a/tests/recheck/tmp2a/jsfmt.spec.js b/tests/recheck/tmp2a/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/recheck/tmp2a/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/recheck/tmp2b/__snapshots__/jsfmt.spec.js.snap b/tests/recheck/tmp2b/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..04d276feff6f --- /dev/null +++ b/tests/recheck/tmp2b/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,19 @@ +exports[`test b0.js 1`] = ` +"// @flow + +class C { x: C; } +class E extends C { x: C; } + +module.exports = { C, E }; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +class C { + x: C; +} +class E extends C { + x: C; +} +module.exports = { C, E }; + +" +`; diff --git a/tests/recheck/tmp2b/b0.js b/tests/recheck/tmp2b/b0.js new file mode 100644 index 000000000000..e6b98e33120f --- /dev/null +++ b/tests/recheck/tmp2b/b0.js @@ -0,0 +1,6 @@ +// @flow + +class C { x: C; } +class E extends C { x: C; } + +module.exports = { C, E }; diff --git a/tests/recheck/tmp2b/jsfmt.spec.js b/tests/recheck/tmp2b/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/recheck/tmp2b/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/recheck/tmp2c/__snapshots__/jsfmt.spec.js.snap b/tests/recheck/tmp2c/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..7b20457b536d --- /dev/null +++ b/tests/recheck/tmp2c/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,12 @@ +exports[`test c1.js 1`] = ` +"// @flow + +export function foo(props: { y: number }) { } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +export function foo(props: { y: number }) { + +} + +" +`; diff --git a/tests/recheck/tmp2c/c1.js b/tests/recheck/tmp2c/c1.js new file mode 100644 index 000000000000..a2e88ae8f295 --- /dev/null +++ b/tests/recheck/tmp2c/c1.js @@ -0,0 +1,3 @@ +// @flow + +export function foo(props: { y: number }) { } diff --git a/tests/recheck/tmp2c/jsfmt.spec.js b/tests/recheck/tmp2c/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/recheck/tmp2c/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/recheck/tmp2e/__snapshots__/jsfmt.spec.js.snap b/tests/recheck/tmp2e/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..6a9b521d85b6 --- /dev/null +++ b/tests/recheck/tmp2e/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,27 @@ +exports[`test e1.js 1`] = ` +"// @flow + +export type Action = + | { type: \'QUX\' } + | { type: \'BAR\' } +; + +export const LIFE = 42; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/recheck/tmp2e/e1.js b/tests/recheck/tmp2e/e1.js new file mode 100644 index 000000000000..36a4c56b9b24 --- /dev/null +++ b/tests/recheck/tmp2e/e1.js @@ -0,0 +1,8 @@ +// @flow + +export type Action = + | { type: 'QUX' } + | { type: 'BAR' } +; + +export const LIFE = 42; diff --git a/tests/recheck/tmp2e/jsfmt.spec.js b/tests/recheck/tmp2e/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/recheck/tmp2e/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/recheck/tmp2f/__snapshots__/jsfmt.spec.js.snap b/tests/recheck/tmp2f/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..4ae23f4c5660 --- /dev/null +++ b/tests/recheck/tmp2f/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,22 @@ +exports[`test f1.js 1`] = ` +"// @flow + +type T = { x: number }; +type S = { x: string }; + +declare var a: T; +declare var b: S; +declare var c: T; + +module.exports = { a, b, c }; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +type T = { x: number }; +type S = { x: string }; +declare var a: T; +declare var b: S; +declare var c: T; +module.exports = { a, b, c }; + +" +`; diff --git a/tests/recheck/tmp2f/f1.js b/tests/recheck/tmp2f/f1.js new file mode 100644 index 000000000000..f367b83232d6 --- /dev/null +++ b/tests/recheck/tmp2f/f1.js @@ -0,0 +1,10 @@ +// @flow + +type T = { x: number }; +type S = { x: string }; + +declare var a: T; +declare var b: S; +declare var c: T; + +module.exports = { a, b, c }; diff --git a/tests/recheck/tmp2f/jsfmt.spec.js b/tests/recheck/tmp2f/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/recheck/tmp2f/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/recheck/tmp3e/__snapshots__/jsfmt.spec.js.snap b/tests/recheck/tmp3e/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..2f2da9b320b2 --- /dev/null +++ b/tests/recheck/tmp3e/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,58 @@ +exports[`test e1.js 1`] = ` +"// @flow + +export type Action = + | { type: \'QUX\' } + | { type: \'BAR\' } +; + +export const LIFE = 0; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test e2.js 1`] = ` +"// @flow + +import type { Action } from \'./e1\'; + +const f = (): Action => { + return { type: \'QUX\' }; +} + +import { LIFE } from \'./e1\'; + +(LIFE: 0); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/ast-types/lib/types.js:60 + throw new Error(str + \" does not match type \" + this); + ^ + +Error: {type: NumericLiteralTypeAnnotation, start: 139, end: 140, loc: [object Object], value: 0, extra: [object Object]} does not match type Printable + at Type.Tp.assert (/node_modules/ast-types/lib/types.js:60:19) + at genericPrintNoParens (/src/printer.js:221:24) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) +" +`; diff --git a/tests/recheck/tmp3e/e1.js b/tests/recheck/tmp3e/e1.js new file mode 100644 index 000000000000..244efab277dc --- /dev/null +++ b/tests/recheck/tmp3e/e1.js @@ -0,0 +1,8 @@ +// @flow + +export type Action = + | { type: 'QUX' } + | { type: 'BAR' } +; + +export const LIFE = 0; diff --git a/tests/recheck/tmp3e/e2.js b/tests/recheck/tmp3e/e2.js new file mode 100644 index 000000000000..6a7ca1b4fa5c --- /dev/null +++ b/tests/recheck/tmp3e/e2.js @@ -0,0 +1,11 @@ +// @flow + +import type { Action } from './e1'; + +const f = (): Action => { + return { type: 'QUX' }; +} + +import { LIFE } from './e1'; + +(LIFE: 0); diff --git a/tests/recheck/tmp3e/jsfmt.spec.js b/tests/recheck/tmp3e/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/recheck/tmp3e/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/recheck/tmp3f/__snapshots__/jsfmt.spec.js.snap b/tests/recheck/tmp3f/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..f58b33b71daa --- /dev/null +++ b/tests/recheck/tmp3f/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,22 @@ +exports[`test f1.js 1`] = ` +"// @flow + +type T = { x: number }; +type S = { x: string }; + +declare var a: T; +declare var b: S; +declare var c: T; + +module.exports = { a, b, c: a }; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +type T = { x: number }; +type S = { x: string }; +declare var a: T; +declare var b: S; +declare var c: T; +module.exports = { a, b, c: a }; + +" +`; diff --git a/tests/recheck/tmp3f/f1.js b/tests/recheck/tmp3f/f1.js new file mode 100644 index 000000000000..40a98b9b68b8 --- /dev/null +++ b/tests/recheck/tmp3f/f1.js @@ -0,0 +1,10 @@ +// @flow + +type T = { x: number }; +type S = { x: string }; + +declare var a: T; +declare var b: S; +declare var c: T; + +module.exports = { a, b, c: a }; diff --git a/tests/recheck/tmp3f/jsfmt.spec.js b/tests/recheck/tmp3f/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/recheck/tmp3f/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/recheck/tmp4f/__snapshots__/jsfmt.spec.js.snap b/tests/recheck/tmp4f/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..c9815810a0ee --- /dev/null +++ b/tests/recheck/tmp4f/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,22 @@ +exports[`test f1.js 1`] = ` +"// @flow + +type T = { x: number }; +type S = { x: string }; + +declare var a: T; +declare var b: S; +declare var c: T; + +module.exports = { a, b, c: b }; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +type T = { x: number }; +type S = { x: string }; +declare var a: T; +declare var b: S; +declare var c: T; +module.exports = { a, b, c: b }; + +" +`; diff --git a/tests/recheck/tmp4f/f1.js b/tests/recheck/tmp4f/f1.js new file mode 100644 index 000000000000..77954a93f28a --- /dev/null +++ b/tests/recheck/tmp4f/f1.js @@ -0,0 +1,10 @@ +// @flow + +type T = { x: number }; +type S = { x: string }; + +declare var a: T; +declare var b: S; +declare var c: T; + +module.exports = { a, b, c: b }; diff --git a/tests/recheck/tmp4f/jsfmt.spec.js b/tests/recheck/tmp4f/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/recheck/tmp4f/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/record/__snapshots__/jsfmt.spec.js.snap b/tests/record/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..3e4a86f4d980 --- /dev/null +++ b/tests/record/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,43 @@ +exports[`test test.js 1`] = ` +"type Key1 = \'foo\' | \'bar\'; // make an enum type with known key set +var o1: {[key: Key1]: number} = { + foo: 0, + bar: \"\", // error: string ~/~ number +}; +o1.foo; // OK +o1.qux; // error: qux not found +o1.toString(); // ok + +type R = {foo: any, bar: any}; +type Key2 = $Keys; // another way to make an enum type, with unknown key set +var o2: {[key: Key2]: number} = { foo: 0 }; // OK to leave out bar +o2.bar; // OK to access bar +o2.qux; // error: qux not found + +class C { + x: $Subtype<{[key: $Keys]: any}>; // object with larger key set than X\'s +} +class D extends C<{foo: number, bar: string}> { + x: { foo: number, qux: boolean }; // error: qux not found +} + +type AnyKey = $Keys; +var o3: {[key: AnyKey]: number} = { foo: 0 }; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/record/jsfmt.spec.js b/tests/record/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/record/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/record/test.js b/tests/record/test.js new file mode 100644 index 000000000000..b7b1357f13c4 --- /dev/null +++ b/tests/record/test.js @@ -0,0 +1,24 @@ +type Key1 = 'foo' | 'bar'; // make an enum type with known key set +var o1: {[key: Key1]: number} = { + foo: 0, + bar: "", // error: string ~/~ number +}; +o1.foo; // OK +o1.qux; // error: qux not found +o1.toString(); // ok + +type R = {foo: any, bar: any}; +type Key2 = $Keys; // another way to make an enum type, with unknown key set +var o2: {[key: Key2]: number} = { foo: 0 }; // OK to leave out bar +o2.bar; // OK to access bar +o2.qux; // error: qux not found + +class C { + x: $Subtype<{[key: $Keys]: any}>; // object with larger key set than X's +} +class D extends C<{foo: number, bar: string}> { + x: { foo: number, qux: boolean }; // error: qux not found +} + +type AnyKey = $Keys; +var o3: {[key: AnyKey]: number} = { foo: 0 }; diff --git a/tests/refi/__snapshots__/jsfmt.spec.js.snap b/tests/refi/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..a9fd427a869d --- /dev/null +++ b/tests/refi/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,1196 @@ +exports[`test bound.js 1`] = ` +"// refinements of bound vars (closed-over locals) +// should have the same lifetimes as heap objects. + +var x : ?string = \"xxx\"; + +var tests = +[ + function() { + var y : string = x; // not ok + }, + + function() { + if (x != null) { + var y : string = x; // ok + } + }, + + function() { + if (x == null) {} else { + var y : string = x; // ok + } + }, + + function() { + if (x == null) + return; + var y : string = x; // ok + }, + + function() { + if (!(x != null)) {} else { + var y : string = x; // ok + } + }, + + /* TODO we actually allow this currently; fix + // requires further remedial work in Env + function() { + if (x != null) { + alert(\"\"); + var y : string = x; // not ok + } + }, + */ + function() { + if (x != null) {} + var y : string = x; // not ok + }, + + function() { + if (x != null) { + } else { + var y : string = x; // not ok + } + }, + + function() { + var y : string = x != null ? x : \"\"; // ok + }, + + function() { + var y : string = x || \"\"; // ok + }, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// refinements of bound vars (closed-over locals) +// should have the same lifetimes as heap objects. +var x: ?string = \"xxx\"; +var tests = [ + function() { + var y: string = x;// not ok + }, + function() { + if (x != null) { + var y: string = x;// ok + } + }, + function() { + if (x == null) { + + } else { + var y: string = x;// ok + } + }, + function() { + if (x == null) + return; + var y: string = x;// ok + }, + function() { + if (!(x != null)) { + + } else { + var y: string = x;// ok + } + }, + /* TODO we actually allow this currently; fix + // requires further remedial work in Env + function() { + if (x != null) { + alert(\"\"); + var y : string = x; // not ok + } + }, + */ + function() { + if (x != null) { + + } + var y: string = x;// not ok + }, + function() { + if (x != null) { + + } else { + var y: string = x;// not ok + } + }, + function() { + var y: string = (x != null ? x : \"\");// ok + }, + function() { + var y: string = x || \"\";// ok + } +]; + +" +`; + +exports[`test heap.js 1`] = ` +"var tests = +[ + function() { + var x : {p:?string} = {p:\"xxx\"}; + var y : string = x.p; // not ok + }, + + function() { + var x : {p:?string} = {p:\"xxx\"}; + if (x.p != null) { + var y : string = x.p; // ok + } + }, + + function() { + var x : {p:?string} = {p:\"xxx\"}; + if (x.p == null) {} else { + var y : string = x.p; // ok + } + }, + + function() { + var x : {p:?string} = {p:\"xxx\"}; + if (x.p == null) + return; + var y : string = x.p; // ok + }, + + function() { + var x : {p:?string} = {p:\"xxx\"}; + if (!(x.p != null)) {} else { + var y : string = x.p; // ok + } + }, + + function() { + var x : {p:?string} = {p:\"xxx\"}; + if (x.p != null) { + alert(\"\"); + var y : string = x.p; // not ok + } + }, + + function () { + var x : {p:?string} = {p:\"xxx\"}; + if (x.p != null) { + x.p = null; + var y : string = x.p; // not ok + } + }, + + function() { + var x : {p:?string} = {p:\"xxx\"}; + if (x.p != null) {} + var y : string = x.p; // not ok + }, + + function() { + var x : {p:?string} = {p:\"xxx\"}; + if (x.p != null) { + } else { + var y : string = x.p; // not ok + } + }, + + function() { + var x : {p:?string} = {p:\"xxx\"}; + var y : string = x.p != null ? x.p : \"\"; // ok + }, + + function() { + var x : {p:?string} = {p:\"xxx\"}; + var y : string = x.p || \"\"; // ok + }, + + function() { + var x : {p:string | string[]} = {p:[\"xxx\"]}; + if (Array.isArray(x.p)) { + var y : string[] = x.p; // ok + } else { + var z : string = x.p; // ok + } + }, + + function() { + var x : {y: ?string} = {y: null}; + if (!x.y) { + x.y = \"foo\"; + } + (x.y: string); + }, + + function() { + var x : {y: ?string} = {y: null}; + if (x.y) { + } else { + x.y = \"foo\"; + } + (x.y: string); + }, + + function() { + var x : {y: ?string} = {y: null}; + if (!x.y) { + x.y = 123; // error + } + (x.y: string); // error, this got widened to a number + }, + + function() { + var x : {y: ?string} = {y: null}; + if (x.y) { + x.y = \"foo\"; + } else { + x.y = \"bar\"; + } + (x.y : string); + }, + + function() { + var x : {y: string | number | boolean} = {y: false}; + if (typeof x.y == \"number\") { + x.y = \"foo\"; + } + (x.y : string); // error, could also be boolean + }, + + function() { + var x : {y: string | number | boolean} = {y: false}; + if (typeof x.y == \"number\") { + x.y = \"foo\"; + } else if (typeof x.y == \"boolean\") { + x.y = \"bar\"; + } + (x.y : boolean); // error, string + }, + + function() { + var x : {y: ?string} = {y: null}; + if (!x.y) { + x.y = \"foo\"; + } + if (x.y) { + x.y = null; + } + (x.y : string); // error + }, + + function() { + var x : {y: string | number | boolean} = {y: false}; + if (typeof x.y == \"number\") { + x.y = \"foo\"; + } + // now x.y can is string | boolean + if (typeof x.y == \"string\") { + x.y = false; + } + // now x.y is only boolean + (x.y : string); // error + }, + + function() { + var x : {y: string | number | boolean} = {y: false}; + if (typeof x.y == \"number\") { + x.y = \"foo\"; + } + // now x.y can is string | boolean + if (typeof x.y == \"string\") { + x.y = 123; + } + // now x.y is number | boolean + (x.y : string); // error + }, + + function() { + var x : {y: ?string} = {y: null}; + var z : string = \"foo\"; + if (x.y) { + x.y = z; + } else { + x.y = z; + } + (x.y : string); + }, + + function(x: string) { + if (x === \'a\') {} + (x: \'b\'); // error (but only once, string !~> \'b\'; \'a\' is irrelevant) + }, + + function(x: mixed) { + if (typeof x.bar === \'string\') {} // error, so \`x.bar\` refinement is empty + (x: string & number); + }, + + // --- nested conditionals --- + // after a branch, the current scope may have changed. this causes the + // subsequent assignment to refine the new scope. these tests make sure that + // the scope that gets merged after the if statement is the correct + // post-condition scope, not the one that was saved at the beginning of the + // if statement. + + function() { + let x: { foo: ?string } = { foo: null }; + if (!x.foo) { + if (false) {} + x.foo = \"foo\"; + } + (x.foo: string); + }, + + function() { + let x: { foo: ?string } = { foo: null }; + if (!x.foo) { + while(false) {} + x.foo = \"foo\"; + } + (x.foo: string); + }, + + function() { + let x: { foo: ?string } = { foo: null }; + if (!x.foo) { + for (var i = 0; i < 0; i++) {} + x.foo = \"foo\"; + } + (x.foo: string); + }, + + function() { + var x : {p:?string} = {p:\"xxx\"}; + if (x.p != null) { + var {p} = x; // TODO: annot checked against type of x + (p : string); // ok + } + }, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test lex.js 1`] = ` +"function block_scope(x: string | number) { + { + let x; + x = \"\"; // doesn\'t refine outer x + } + (x : string); // error: number ~> string +} + +function switch_scope(x: string | number) { + switch (x) { + default: + let x; + x = \"\"; // doesn\'t refine outer x + } + (x : string); // error: number ~> string +} + +function try_scope(x: string | number) { + try { + let x; + x = \"\"; // doesn\'t refine outer x + } catch (e) { + x = \"\"; // refinement would only escape if both sides refined + } + (x : string); // error: number ~> string +} + +function try_scope_catch(x: string | number) { + try { + x = \"\"; // refinement would only escape if both sides refined + } catch (e) { + let x; + x = \"\"; // doesn\'t refine outer x + } + (x : string); // error: number ~> string +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test local.js 1`] = ` +"var paths = +[ + function() { + var x : ?string = \"xxx\"; + var y : string = x; // not ok + }, + + function() { + var x : ?string = \"xxx\"; + if (x != null) { + var y : string = x; // ok + } + }, + + function() { + var x : ?string = \"xxx\"; + if (x == null) {} else { + var y : string = x; // ok + } + }, + + function() { + var x : ?string = \"xxx\"; + if (x == null) + return; + var y : string = x; // ok + }, + + function() { + var x : ?string = \"xxx\"; + if (!(x != null)) {} else { + var y : string = x; // ok + } + }, + + function() { + var x : ?string = \"xxx\"; + if (x != null) { + alert(\"\"); + var y : string = x; // ok + } + }, + + function() { + var x : ?string = \"xxx\"; + if (x != null) {} + var y : string = x; // not ok + }, + + function() { + var x : ?string = \"xxx\"; + if (x != null) { + } else { + var y : string = x; // not ok + } + }, + + function() { + var x : ?string = \"xxx\"; + var y : string = x != null ? x : \"\"; // ok + }, + + function() { + var x : ?string = \"xxx\"; + var y : string = x || \"\"; // ok + }, + + function() { + var x : string | string[] = [\"xxx\"]; + if (Array.isArray(x)) { + var y : string[] = x; // ok + } else { + var z : string = x; // ok + } + }, + + function() { + var x : ?string = null; + if (!x) { + x = \"xxx\"; + } + var y : string = x; + }, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test null_tests.js 1`] = ` +"var null_tests = +[ + // expr != null + function() { + var x : ?string = \"xxx\"; + if (x != null) { + var y : string = x; // ok + } + }, + + function() { + var x : ?string = \"xxx\"; + if (null != x) { + var y : string = x; // ok + } + }, + + function() { + var x : {p:?string} = {p:\"xxx\"}; + if (x.p != null) { + var y : string = x.p; // ok + } + }, + + function() { + var x : {p:{q:?string}} = {p:{q:\"xxx\"}}; + if (x.p.q != null) { + var y : string = x.p.q; // ok + } + }, + + // expr == null + function() { + var x : ?string = \"xxx\"; + if (x == null) {} else { + var y : string = x; // ok + } + }, + + function() { + var x : {p:?string} = {p:\"xxx\"}; + if (x.p == null) {} else { + var y : string = x.p; // ok + } + }, + + function() { + var x : {p:{q:?string}} = {p:{q:\"xxx\"}}; + if (x.p.q == null) {} else { + var y : string = x.p.q; // ok + } + }, + + // expr !== null + function() { + var x : ?string = \"xxx\"; + if (x !== null) { + var y : string | void = x; // ok + } + }, + + function() { + var x : {p:?string} = {p:\"xxx\"}; + if (x.p !== null) { + var y : string | void = x.p; // ok + } + }, + + function() { + var x : {p:{q:?string}} = {p:{q:\"xxx\"}}; + if (x.p.q !== null) { + var y : string | void = x.p.q; // ok + } + }, + + // expr === null + function() { + var x : ?string = \"xxx\"; + if (x === null) {} else { + var y : string | void = x; // ok + } + }, + + function() { + var x : {p:?string} = {p:\"xxx\"}; + if (x.p === null) {} else { + var y : string | void = x.p; // ok + } + }, + + function() { + var x : {p:{q:?string}} = {p:{q:\"xxx\"}}; + if (x.p.q === null) {} else { + var y : string | void = x.p.q; // ok + } + }, +]; + +// this.p op null +class C { + p: ?string; + + ensure0(): string { + if (this.p != null) + return this.p; + else + return \"\"; + } + + ensure1(): string { + if (this.p == null) + return \"\"; + return this.p; + } + + ensure2(): string | void { + if (this.p !== null) + return this.p; + else + return \"\"; + } + + ensure3(): string | void { + if (this.p === null) + return \"\"; + return this.p; + } +} + +// super.p op null +class D extends C { + + ensure100(): string { + if (super.p != null) + return super.p; + else + return \"\"; + } + + ensure101(): string { + if (super.p == null) + return \"\"; + else + return super.p; + } + + ensure103(): string { + if (super.p != null) { + alert(\"\"); + return super.p; // not ok + } + return \"\"; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test switch.js 1`] = ` +"// @flow +function foo(a,b,c) { + switch (c) { + case a.x.y: // OK + case b.x.y: // OK + return; + default: + return; + } +} + +// test refis out of switches that are exhaustive without default case + +function exhaustion1(x): number { + var foo; + switch (x) { + case 0: // falls through + case 1: + foo = 3; + break; + default: + throw new Error(\'Invalid state\'); + } + return foo; // no error +} + +function exhaustion2(x, y): number { + var foo; + switch (x) { + case 0: + if (y) { + break; // leaks uninitialized foo out of switch + } + /** + * TODO this shouldn\'t cause an error, because the path that + * runs it will always go on to assign a number to foo. But + * we\'ll need true isolation in env snapshots to model this. + * Currently tvars are always shared between clones - we\'d + * have to rework envs pretty extensively to avoid it. + * + foo = \"\"; + */ + case 1: + foo = 3; + break; + default: + throw new Error(\'Invalid state\'); + } + return foo; // error, possibly uninitialized +} + +function exhaustion3(x): number { + let foo = null; + switch (x) { + case 0: // falls through + case 1: + foo = 3; + break; + default: + throw new Error(\'Invalid state\'); + } + return foo; // no error +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1020 + }, \"consequent\").indent(options.tabWidth)); + ^ + +TypeError: path.call(...).indent is not a function + at genericPrintNoParens (/src/printer.js:1020:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1005:36) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test typeof_tests.js 1`] = ` +"var null_tests = +[ + // typeof expr == typename + function() { + var x : ?string = \"xxx\"; + if (typeof x == \"string\") { + var y : string = x; // ok + } + }, + + function() { + var x : ?string = \"xxx\"; + if (\"string\" == typeof x) { + var y : string = x; // ok + } + }, + + function() { + var x : {p:?string} = {p:\"xxx\"}; + if (typeof x.p == \"string\") { + var y : string = x.p; // ok + } + }, + + function() { + var x : {p:{q:?string}} = {p:{q:\"xxx\"}}; + if (typeof x.p.q == \"string\") { + var y : string = x.p.q; // ok + } + }, + + // typeof expr != typename + function() { + var x : ?string = \"xxx\"; + if (typeof x != \"string\") {} else { + var y : string = x; // ok + } + }, + + function() { + var x : {p:?string} = {p:\"xxx\"}; + if (typeof x.p != \"string\") {} else { + var y : string = x.p; // ok + } + }, + + function() { + var x : {p:{q:?string}} = {p:{q:\"xxx\"}}; + if (typeof x.p.q != \"string\") {} else { + var y : string = x.p.q; // ok + } + }, + + // typeof expr === typename + function() { + var x : ?string = \"xxx\"; + if (typeof x === \"string\") { + var y : string = x; // ok + } + }, + + function() { + var x : {p:?string} = {p:\"xxx\"}; + if (typeof x.p === \"string\") { + var y : string = x.p; // ok + } + }, + + function() { + var x : {p:{q:?string}} = {p:{q:\"xxx\"}}; + if (typeof x.p.q === \"string\") { + var y : string = x.p.q; // ok + } + }, + + // typeof expr !== typename + function() { + var x : ?string = \"xxx\"; + if (typeof x !== \"string\") {} else { + var y : string = x; // ok + } + }, + + function() { + var x : {p:?string} = {p:\"xxx\"}; + if (typeof x.p !== \"string\") {} else { + var y : string = x.p; // ok + } + }, + + function() { + var x : {p:{q:?string}} = {p:{q:\"xxx\"}}; + if (typeof x.p.q !== \"string\") {} else { + var y : string = x.p.q; // ok + } + }, +]; + +// typeof this.p op typename +class A { + p: ?string; + + ensure0(): string { + if (typeof this.p == \"string\") + return this.p; + else + return \"\"; + } + + ensure1(): string { + if (typeof this.p != \"string\") + return \"\"; + else + return this.p; + } + + ensure2(): string | void { + if (typeof this.p === \"string\") + return this.p; + else + return \"\"; + } + + ensure3(): string | void { + if (typeof this.p !== \"string\") + return \"\"; + else + return this.p; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test undef_tests.js 1`] = ` +"var undef_tests = +[ + // NOTE: not (yet?) supporting non-strict eq test for undefined + + // expr !== undefined + function() { + var x : ?string = \"xxx\"; + if (x !== undefined && x !== null) { + var y : string = x; // ok + } + }, + + function() { + var x : ?string = \"xxx\"; + if (undefined !== x && x !== null) { + var y : string = x; // ok + } + }, + + function() { + var x : {p:?string} = {p:\"xxx\"}; + if (x.p !== undefined && x.p !== null) { + var y : string = x.p; // ok + } + }, + + function() { + var x : {p:{q:?string}} = {p:{q:\"xxx\"}}; + if (x.p.q !== undefined && x.p.q !== null) { + var y : string = x.p.q; // ok + } + }, + + // expr === undefined + function() { + var x : ?string = \"xxx\"; + if (x === undefined || x === null) {} else { + var y : string = x; // ok + } + }, + + function() { + var x : {p:?string} = {p:\"xxx\"}; + if (x.p === undefined || x.p === null) {} else { + var y : string = x.p; // ok + } + }, + + function() { + var x : {p:{q:?string}} = {p:{q:\"xxx\"}}; + if (x.p.q === undefined || x.p.q === null) {} else { + var y : string = x.p.q; // ok + } + }, +]; + +// this.p op undefined +class A { + p: ?string; + + ensure0(): string { + if (this.p !== undefined && this.p !== null) + return this.p; + else + return \"\"; + } + + ensure1(): string { + if (this.p === undefined || this.p === null) + return \"\"; + else + return this.p; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var undef_tests = [ + // NOTE: not (yet?) supporting non-strict eq test for undefined + // expr !== undefined + function() { + var x: ?string = \"xxx\"; + if (x !== undefined && x !== null) { + var y: string = x;// ok + } + }, + function() { + var x: ?string = \"xxx\"; + if (undefined !== x && x !== null) { + var y: string = x;// ok + } + }, + function() { + var x: { p: ?string } = { p: \"xxx\" }; + if (x.p !== undefined && x.p !== null) { + var y: string = x.p;// ok + } + }, + function() { + var x: { p: { q: ?string } } = { p: { q: \"xxx\" } }; + if (x.p.q !== undefined && x.p.q !== null) { + var y: string = x.p.q;// ok + } + }, + // expr === undefined + function() { + var x: ?string = \"xxx\"; + if (x === undefined || x === null) { + + } else { + var y: string = x;// ok + } + }, + function() { + var x: { p: ?string } = { p: \"xxx\" }; + if (x.p === undefined || x.p === null) { + + } else { + var y: string = x.p;// ok + } + }, + function() { + var x: { p: { q: ?string } } = { p: { q: \"xxx\" } }; + if (x.p.q === undefined || x.p.q === null) { + + } else { + var y: string = x.p.q;// ok + } + } +]; +// this.p op undefined +class A { + p: ?string; + ensure0(): string { + if (this.p !== undefined && this.p !== null) + return this.p; +else + return \"\"; + } + ensure1(): string { + if (this.p === undefined || this.p === null) + return \"\"; +else + return this.p; + } +} + +" +`; + +exports[`test void_tests.js 1`] = ` +"var void_tests = +[ + // NOTE: not (yet?) supporting non-strict eq test for undefined + + // expr !== void(...) + function() { + var x : ?string = \"xxx\"; + if (x !== void(0) && x !== null) { + var y : string = x; // ok + } + }, + + function() { + var x : ?string = \"xxx\"; + if (void(0) !== x && x !== null) { + var y : string = x; // ok + } + }, + + function() { + var x : {p:?string} = {p:\"xxx\"}; + if (x.p !== void(0) && x.p !== null) { + var y : string | void = x.p; // ok + } + }, + + function() { + var x : {p:{q:?string}} = {p:{q:\"xxx\"}}; + if (x.p.q !== void(0) && x.p.q !== null) { + var y : string = x.p.q; // ok + } + }, + + // expr === void(...) + function() { + var x : ?string = \"xxx\"; + if (x === void(0) || x === null) {} else { + var y : string = x; // ok + } + }, + + function() { + var x : {p:?string} = {p:\"xxx\"}; + if (x.p === void(0) || x.p === null) {} else { + var y : string = x.p; // ok + } + }, + + function() { + var x : {p:{q:?string}} = {p:{q:\"xxx\"}}; + if (x.p.q === void(0) || x.p.q === null) {} else { + var y : string = x.p.q; // ok + } + }, +]; + +// this.p op void(...) +class A { + p: ?string; + + ensure0(): string { + if (this.p !== void(0) && this.p !== null) + return this.p; + else + return \"\"; + } + + ensure1(): string { + if (this.p === void(0) || this.p === null) + return \"\"; + else + return this.p; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/refi/bound.js b/tests/refi/bound.js new file mode 100644 index 000000000000..2c8381126568 --- /dev/null +++ b/tests/refi/bound.js @@ -0,0 +1,64 @@ +// refinements of bound vars (closed-over locals) +// should have the same lifetimes as heap objects. + +var x : ?string = "xxx"; + +var tests = +[ + function() { + var y : string = x; // not ok + }, + + function() { + if (x != null) { + var y : string = x; // ok + } + }, + + function() { + if (x == null) {} else { + var y : string = x; // ok + } + }, + + function() { + if (x == null) + return; + var y : string = x; // ok + }, + + function() { + if (!(x != null)) {} else { + var y : string = x; // ok + } + }, + + /* TODO we actually allow this currently; fix + // requires further remedial work in Env + function() { + if (x != null) { + alert(""); + var y : string = x; // not ok + } + }, + */ + function() { + if (x != null) {} + var y : string = x; // not ok + }, + + function() { + if (x != null) { + } else { + var y : string = x; // not ok + } + }, + + function() { + var y : string = x != null ? x : ""; // ok + }, + + function() { + var y : string = x || ""; // ok + }, +]; diff --git a/tests/refi/heap.js b/tests/refi/heap.js new file mode 100644 index 000000000000..16a4c2baaa5c --- /dev/null +++ b/tests/refi/heap.js @@ -0,0 +1,237 @@ +var tests = +[ + function() { + var x : {p:?string} = {p:"xxx"}; + var y : string = x.p; // not ok + }, + + function() { + var x : {p:?string} = {p:"xxx"}; + if (x.p != null) { + var y : string = x.p; // ok + } + }, + + function() { + var x : {p:?string} = {p:"xxx"}; + if (x.p == null) {} else { + var y : string = x.p; // ok + } + }, + + function() { + var x : {p:?string} = {p:"xxx"}; + if (x.p == null) + return; + var y : string = x.p; // ok + }, + + function() { + var x : {p:?string} = {p:"xxx"}; + if (!(x.p != null)) {} else { + var y : string = x.p; // ok + } + }, + + function() { + var x : {p:?string} = {p:"xxx"}; + if (x.p != null) { + alert(""); + var y : string = x.p; // not ok + } + }, + + function () { + var x : {p:?string} = {p:"xxx"}; + if (x.p != null) { + x.p = null; + var y : string = x.p; // not ok + } + }, + + function() { + var x : {p:?string} = {p:"xxx"}; + if (x.p != null) {} + var y : string = x.p; // not ok + }, + + function() { + var x : {p:?string} = {p:"xxx"}; + if (x.p != null) { + } else { + var y : string = x.p; // not ok + } + }, + + function() { + var x : {p:?string} = {p:"xxx"}; + var y : string = x.p != null ? x.p : ""; // ok + }, + + function() { + var x : {p:?string} = {p:"xxx"}; + var y : string = x.p || ""; // ok + }, + + function() { + var x : {p:string | string[]} = {p:["xxx"]}; + if (Array.isArray(x.p)) { + var y : string[] = x.p; // ok + } else { + var z : string = x.p; // ok + } + }, + + function() { + var x : {y: ?string} = {y: null}; + if (!x.y) { + x.y = "foo"; + } + (x.y: string); + }, + + function() { + var x : {y: ?string} = {y: null}; + if (x.y) { + } else { + x.y = "foo"; + } + (x.y: string); + }, + + function() { + var x : {y: ?string} = {y: null}; + if (!x.y) { + x.y = 123; // error + } + (x.y: string); // error, this got widened to a number + }, + + function() { + var x : {y: ?string} = {y: null}; + if (x.y) { + x.y = "foo"; + } else { + x.y = "bar"; + } + (x.y : string); + }, + + function() { + var x : {y: string | number | boolean} = {y: false}; + if (typeof x.y == "number") { + x.y = "foo"; + } + (x.y : string); // error, could also be boolean + }, + + function() { + var x : {y: string | number | boolean} = {y: false}; + if (typeof x.y == "number") { + x.y = "foo"; + } else if (typeof x.y == "boolean") { + x.y = "bar"; + } + (x.y : boolean); // error, string + }, + + function() { + var x : {y: ?string} = {y: null}; + if (!x.y) { + x.y = "foo"; + } + if (x.y) { + x.y = null; + } + (x.y : string); // error + }, + + function() { + var x : {y: string | number | boolean} = {y: false}; + if (typeof x.y == "number") { + x.y = "foo"; + } + // now x.y can is string | boolean + if (typeof x.y == "string") { + x.y = false; + } + // now x.y is only boolean + (x.y : string); // error + }, + + function() { + var x : {y: string | number | boolean} = {y: false}; + if (typeof x.y == "number") { + x.y = "foo"; + } + // now x.y can is string | boolean + if (typeof x.y == "string") { + x.y = 123; + } + // now x.y is number | boolean + (x.y : string); // error + }, + + function() { + var x : {y: ?string} = {y: null}; + var z : string = "foo"; + if (x.y) { + x.y = z; + } else { + x.y = z; + } + (x.y : string); + }, + + function(x: string) { + if (x === 'a') {} + (x: 'b'); // error (but only once, string !~> 'b'; 'a' is irrelevant) + }, + + function(x: mixed) { + if (typeof x.bar === 'string') {} // error, so `x.bar` refinement is empty + (x: string & number); + }, + + // --- nested conditionals --- + // after a branch, the current scope may have changed. this causes the + // subsequent assignment to refine the new scope. these tests make sure that + // the scope that gets merged after the if statement is the correct + // post-condition scope, not the one that was saved at the beginning of the + // if statement. + + function() { + let x: { foo: ?string } = { foo: null }; + if (!x.foo) { + if (false) {} + x.foo = "foo"; + } + (x.foo: string); + }, + + function() { + let x: { foo: ?string } = { foo: null }; + if (!x.foo) { + while(false) {} + x.foo = "foo"; + } + (x.foo: string); + }, + + function() { + let x: { foo: ?string } = { foo: null }; + if (!x.foo) { + for (var i = 0; i < 0; i++) {} + x.foo = "foo"; + } + (x.foo: string); + }, + + function() { + var x : {p:?string} = {p:"xxx"}; + if (x.p != null) { + var {p} = x; // TODO: annot checked against type of x + (p : string); // ok + } + }, +]; diff --git a/tests/refi/jsfmt.spec.js b/tests/refi/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/refi/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/refi/lex.js b/tests/refi/lex.js new file mode 100644 index 000000000000..891931b5412f --- /dev/null +++ b/tests/refi/lex.js @@ -0,0 +1,36 @@ +function block_scope(x: string | number) { + { + let x; + x = ""; // doesn't refine outer x + } + (x : string); // error: number ~> string +} + +function switch_scope(x: string | number) { + switch (x) { + default: + let x; + x = ""; // doesn't refine outer x + } + (x : string); // error: number ~> string +} + +function try_scope(x: string | number) { + try { + let x; + x = ""; // doesn't refine outer x + } catch (e) { + x = ""; // refinement would only escape if both sides refined + } + (x : string); // error: number ~> string +} + +function try_scope_catch(x: string | number) { + try { + x = ""; // refinement would only escape if both sides refined + } catch (e) { + let x; + x = ""; // doesn't refine outer x + } + (x : string); // error: number ~> string +} diff --git a/tests/refi/local.js b/tests/refi/local.js new file mode 100644 index 000000000000..372cdfa0d657 --- /dev/null +++ b/tests/refi/local.js @@ -0,0 +1,84 @@ +var paths = +[ + function() { + var x : ?string = "xxx"; + var y : string = x; // not ok + }, + + function() { + var x : ?string = "xxx"; + if (x != null) { + var y : string = x; // ok + } + }, + + function() { + var x : ?string = "xxx"; + if (x == null) {} else { + var y : string = x; // ok + } + }, + + function() { + var x : ?string = "xxx"; + if (x == null) + return; + var y : string = x; // ok + }, + + function() { + var x : ?string = "xxx"; + if (!(x != null)) {} else { + var y : string = x; // ok + } + }, + + function() { + var x : ?string = "xxx"; + if (x != null) { + alert(""); + var y : string = x; // ok + } + }, + + function() { + var x : ?string = "xxx"; + if (x != null) {} + var y : string = x; // not ok + }, + + function() { + var x : ?string = "xxx"; + if (x != null) { + } else { + var y : string = x; // not ok + } + }, + + function() { + var x : ?string = "xxx"; + var y : string = x != null ? x : ""; // ok + }, + + function() { + var x : ?string = "xxx"; + var y : string = x || ""; // ok + }, + + function() { + var x : string | string[] = ["xxx"]; + if (Array.isArray(x)) { + var y : string[] = x; // ok + } else { + var z : string = x; // ok + } + }, + + function() { + var x : ?string = null; + if (!x) { + x = "xxx"; + } + var y : string = x; + }, +]; diff --git a/tests/refi/null_tests.js b/tests/refi/null_tests.js new file mode 100644 index 000000000000..f7c6ad7c111f --- /dev/null +++ b/tests/refi/null_tests.js @@ -0,0 +1,154 @@ +var null_tests = +[ + // expr != null + function() { + var x : ?string = "xxx"; + if (x != null) { + var y : string = x; // ok + } + }, + + function() { + var x : ?string = "xxx"; + if (null != x) { + var y : string = x; // ok + } + }, + + function() { + var x : {p:?string} = {p:"xxx"}; + if (x.p != null) { + var y : string = x.p; // ok + } + }, + + function() { + var x : {p:{q:?string}} = {p:{q:"xxx"}}; + if (x.p.q != null) { + var y : string = x.p.q; // ok + } + }, + + // expr == null + function() { + var x : ?string = "xxx"; + if (x == null) {} else { + var y : string = x; // ok + } + }, + + function() { + var x : {p:?string} = {p:"xxx"}; + if (x.p == null) {} else { + var y : string = x.p; // ok + } + }, + + function() { + var x : {p:{q:?string}} = {p:{q:"xxx"}}; + if (x.p.q == null) {} else { + var y : string = x.p.q; // ok + } + }, + + // expr !== null + function() { + var x : ?string = "xxx"; + if (x !== null) { + var y : string | void = x; // ok + } + }, + + function() { + var x : {p:?string} = {p:"xxx"}; + if (x.p !== null) { + var y : string | void = x.p; // ok + } + }, + + function() { + var x : {p:{q:?string}} = {p:{q:"xxx"}}; + if (x.p.q !== null) { + var y : string | void = x.p.q; // ok + } + }, + + // expr === null + function() { + var x : ?string = "xxx"; + if (x === null) {} else { + var y : string | void = x; // ok + } + }, + + function() { + var x : {p:?string} = {p:"xxx"}; + if (x.p === null) {} else { + var y : string | void = x.p; // ok + } + }, + + function() { + var x : {p:{q:?string}} = {p:{q:"xxx"}}; + if (x.p.q === null) {} else { + var y : string | void = x.p.q; // ok + } + }, +]; + +// this.p op null +class C { + p: ?string; + + ensure0(): string { + if (this.p != null) + return this.p; + else + return ""; + } + + ensure1(): string { + if (this.p == null) + return ""; + return this.p; + } + + ensure2(): string | void { + if (this.p !== null) + return this.p; + else + return ""; + } + + ensure3(): string | void { + if (this.p === null) + return ""; + return this.p; + } +} + +// super.p op null +class D extends C { + + ensure100(): string { + if (super.p != null) + return super.p; + else + return ""; + } + + ensure101(): string { + if (super.p == null) + return ""; + else + return super.p; + } + + ensure103(): string { + if (super.p != null) { + alert(""); + return super.p; // not ok + } + return ""; + } +} diff --git a/tests/refi/switch.js b/tests/refi/switch.js new file mode 100644 index 000000000000..f1100fe2cfc8 --- /dev/null +++ b/tests/refi/switch.js @@ -0,0 +1,63 @@ +// @flow +function foo(a,b,c) { + switch (c) { + case a.x.y: // OK + case b.x.y: // OK + return; + default: + return; + } +} + +// test refis out of switches that are exhaustive without default case + +function exhaustion1(x): number { + var foo; + switch (x) { + case 0: // falls through + case 1: + foo = 3; + break; + default: + throw new Error('Invalid state'); + } + return foo; // no error +} + +function exhaustion2(x, y): number { + var foo; + switch (x) { + case 0: + if (y) { + break; // leaks uninitialized foo out of switch + } + /** + * TODO this shouldn't cause an error, because the path that + * runs it will always go on to assign a number to foo. But + * we'll need true isolation in env snapshots to model this. + * Currently tvars are always shared between clones - we'd + * have to rework envs pretty extensively to avoid it. + * + foo = ""; + */ + case 1: + foo = 3; + break; + default: + throw new Error('Invalid state'); + } + return foo; // error, possibly uninitialized +} + +function exhaustion3(x): number { + let foo = null; + switch (x) { + case 0: // falls through + case 1: + foo = 3; + break; + default: + throw new Error('Invalid state'); + } + return foo; // no error +} diff --git a/tests/refi/typeof_tests.js b/tests/refi/typeof_tests.js new file mode 100644 index 000000000000..7eefa8b5c274 --- /dev/null +++ b/tests/refi/typeof_tests.js @@ -0,0 +1,130 @@ +var null_tests = +[ + // typeof expr == typename + function() { + var x : ?string = "xxx"; + if (typeof x == "string") { + var y : string = x; // ok + } + }, + + function() { + var x : ?string = "xxx"; + if ("string" == typeof x) { + var y : string = x; // ok + } + }, + + function() { + var x : {p:?string} = {p:"xxx"}; + if (typeof x.p == "string") { + var y : string = x.p; // ok + } + }, + + function() { + var x : {p:{q:?string}} = {p:{q:"xxx"}}; + if (typeof x.p.q == "string") { + var y : string = x.p.q; // ok + } + }, + + // typeof expr != typename + function() { + var x : ?string = "xxx"; + if (typeof x != "string") {} else { + var y : string = x; // ok + } + }, + + function() { + var x : {p:?string} = {p:"xxx"}; + if (typeof x.p != "string") {} else { + var y : string = x.p; // ok + } + }, + + function() { + var x : {p:{q:?string}} = {p:{q:"xxx"}}; + if (typeof x.p.q != "string") {} else { + var y : string = x.p.q; // ok + } + }, + + // typeof expr === typename + function() { + var x : ?string = "xxx"; + if (typeof x === "string") { + var y : string = x; // ok + } + }, + + function() { + var x : {p:?string} = {p:"xxx"}; + if (typeof x.p === "string") { + var y : string = x.p; // ok + } + }, + + function() { + var x : {p:{q:?string}} = {p:{q:"xxx"}}; + if (typeof x.p.q === "string") { + var y : string = x.p.q; // ok + } + }, + + // typeof expr !== typename + function() { + var x : ?string = "xxx"; + if (typeof x !== "string") {} else { + var y : string = x; // ok + } + }, + + function() { + var x : {p:?string} = {p:"xxx"}; + if (typeof x.p !== "string") {} else { + var y : string = x.p; // ok + } + }, + + function() { + var x : {p:{q:?string}} = {p:{q:"xxx"}}; + if (typeof x.p.q !== "string") {} else { + var y : string = x.p.q; // ok + } + }, +]; + +// typeof this.p op typename +class A { + p: ?string; + + ensure0(): string { + if (typeof this.p == "string") + return this.p; + else + return ""; + } + + ensure1(): string { + if (typeof this.p != "string") + return ""; + else + return this.p; + } + + ensure2(): string | void { + if (typeof this.p === "string") + return this.p; + else + return ""; + } + + ensure3(): string | void { + if (typeof this.p !== "string") + return ""; + else + return this.p; + } +} diff --git a/tests/refi/undef_tests.js b/tests/refi/undef_tests.js new file mode 100644 index 000000000000..c23dfeaa101a --- /dev/null +++ b/tests/refi/undef_tests.js @@ -0,0 +1,74 @@ +var undef_tests = +[ + // NOTE: not (yet?) supporting non-strict eq test for undefined + + // expr !== undefined + function() { + var x : ?string = "xxx"; + if (x !== undefined && x !== null) { + var y : string = x; // ok + } + }, + + function() { + var x : ?string = "xxx"; + if (undefined !== x && x !== null) { + var y : string = x; // ok + } + }, + + function() { + var x : {p:?string} = {p:"xxx"}; + if (x.p !== undefined && x.p !== null) { + var y : string = x.p; // ok + } + }, + + function() { + var x : {p:{q:?string}} = {p:{q:"xxx"}}; + if (x.p.q !== undefined && x.p.q !== null) { + var y : string = x.p.q; // ok + } + }, + + // expr === undefined + function() { + var x : ?string = "xxx"; + if (x === undefined || x === null) {} else { + var y : string = x; // ok + } + }, + + function() { + var x : {p:?string} = {p:"xxx"}; + if (x.p === undefined || x.p === null) {} else { + var y : string = x.p; // ok + } + }, + + function() { + var x : {p:{q:?string}} = {p:{q:"xxx"}}; + if (x.p.q === undefined || x.p.q === null) {} else { + var y : string = x.p.q; // ok + } + }, +]; + +// this.p op undefined +class A { + p: ?string; + + ensure0(): string { + if (this.p !== undefined && this.p !== null) + return this.p; + else + return ""; + } + + ensure1(): string { + if (this.p === undefined || this.p === null) + return ""; + else + return this.p; + } +} diff --git a/tests/refi/void_tests.js b/tests/refi/void_tests.js new file mode 100644 index 000000000000..f74e7bcc8260 --- /dev/null +++ b/tests/refi/void_tests.js @@ -0,0 +1,74 @@ +var void_tests = +[ + // NOTE: not (yet?) supporting non-strict eq test for undefined + + // expr !== void(...) + function() { + var x : ?string = "xxx"; + if (x !== void(0) && x !== null) { + var y : string = x; // ok + } + }, + + function() { + var x : ?string = "xxx"; + if (void(0) !== x && x !== null) { + var y : string = x; // ok + } + }, + + function() { + var x : {p:?string} = {p:"xxx"}; + if (x.p !== void(0) && x.p !== null) { + var y : string | void = x.p; // ok + } + }, + + function() { + var x : {p:{q:?string}} = {p:{q:"xxx"}}; + if (x.p.q !== void(0) && x.p.q !== null) { + var y : string = x.p.q; // ok + } + }, + + // expr === void(...) + function() { + var x : ?string = "xxx"; + if (x === void(0) || x === null) {} else { + var y : string = x; // ok + } + }, + + function() { + var x : {p:?string} = {p:"xxx"}; + if (x.p === void(0) || x.p === null) {} else { + var y : string = x.p; // ok + } + }, + + function() { + var x : {p:{q:?string}} = {p:{q:"xxx"}}; + if (x.p.q === void(0) || x.p.q === null) {} else { + var y : string = x.p.q; // ok + } + }, +]; + +// this.p op void(...) +class A { + p: ?string; + + ensure0(): string { + if (this.p !== void(0) && this.p !== null) + return this.p; + else + return ""; + } + + ensure1(): string { + if (this.p === void(0) || this.p === null) + return ""; + else + return this.p; + } +} diff --git a/tests/refinements/__snapshots__/jsfmt.spec.js.snap b/tests/refinements/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..3cb42ac081d0 --- /dev/null +++ b/tests/refinements/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,2290 @@ +exports[`test assignment.js 1`] = ` +"/* @flow */ + +function foo(x : ?number) { + var y; + if (y = x) { + var z = y * 1000; + } +} + +type Bar = { + parent: ?Bar; + doStuff: () => void +} + +function bar0(x : Bar) { + while (x = x.parent) { // can\'t assign x to ?Bar + x.doStuff(); + } +} + +function bar1(x : ?Bar) { + while (x = x.parent) { // x.parent might be null + x.doStuff(); + } +} + +function bar2(x : Bar) { + var y = x; + while (y = y.parent) { + y.doStuff(); + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test ast_node.js 1`] = ` +"type Node1 = { + kind: \'Node1\', + prop1?: string +}; + +type Node2 = { + kind: \'Node2\', + prop2?: string +} + +export type ASTNode = Node1 | Node2; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test bool.js 1`] = ` +"/* @flow */ + +function foo(x: ?bool) { + if (x === false) { + return; + } + + if (x === true) { + return; + } + + x[0]; // error on null and undefined +} + +function bar(x: ?bool) { + if (x !== true) { + if (x !== false) { + x[0]; // error on null and undefined + } + } +} + +function baz(x: ?bool) { + if (100 * false) { + return; + } + if (false * 100) { + return; + } +} + +let tests = [ + function(x: { done: true, result: string } | { done: false }) { + if (x.done === true) { + return x.result; + } + return x.result; // error + }, + + function(x: { done: true, result: string } | { done: false }) { + if (true === x.done) { + return x.result; + } + return x.result; // error + }, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test computed_string_literal.js 1`] = ` +"// @flow + +type A = { + \'b_c\': ?string +}; + +function stuff(str: string) {} + +function testProperty(a: A) { + if (a.b_c) { + stuff(a.b_c) + } +} + +function testLiteralProperty(a: A) { + if (a[\'b_c\']) { + stuff(a[\'b_c\']) + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +type A = { \"b_c\": ?string }; +function stuff(str: string) { + +} +function testProperty(a: A) { + if (a.b_c) { + stuff(a.b_c); + } +} +function testLiteralProperty(a: A) { + if (a[\"b_c\"]) { + stuff(a[\"b_c\"]); + } +} + +" +`; + +exports[`test cond_prop.js 1`] = ` +"/* @flow */ + +type Type = Name | ListType | NonNullType; +type Name = {kind: \'Name\', value: string, type: void }; +type ListType = {kind: \'ListType\', type: Type}; +type NonNullType = {kind: \'NonNullType\', type: Name | ListType | BadType}; +type BadType = {}; + +function getTypeASTName(typeAST: Type): string { + if (!typeAST.type) throw new Error(\'Must be wrapping type\'); // OK + return getTypeASTName(typeAST.type); // error, BadType not a subtype of Type +} + +let tests = [ + function(x: { done: true, result: string } | { done: false }) { + if (x.done) { + return x.result; + } + return x.result; // error + }, + + function(x: { done: true, result: string } | { foo: string }) { + if (x.done) { + return x.result; // error, consider { foo: \"herp\", done: \"derp\" } + } + return x.result; // error + }, + + function() { + type T + = { foo: Object, bar: string } + | { baz: string, quux: string } + + function testAlwaysTruthyProp(t: T) { + if (t.foo) { + (t.bar: string); // error, consider { baz: \"x\", quux: \"y\", foo: \"boom\" } + } else { + (t.quux: string); // ok. since foo is an object (always truthy), the + // else case completely rules out the first branch of + // the union. + } + } + + function testSometimesTruthyProp(t: T) { + if (t.bar) { + (t.foo: Object); // error, consider { baz: \"x\", quux: \"y\", bar: \"boom\" } + } else { + (t.quux: string); // error, consider { foo: {}, bar: \"\" } + } + } + }, +] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test constants.js 1`] = ` +"/* @flow */ + +export const SUCCESS: \'SUCCESS\' = \'SUCCESS\'; +export const ERROR: \'ERROR\' = \'ERROR\'; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +export const SUCCESS: \"SUCCESS\" = \"SUCCESS\"; +export const ERROR: \"ERROR\" = \"ERROR\"; + +" +`; + +exports[`test eq.js 1`] = ` +"/* @flow */ + +let tests = [ + function(x: string, y: number) { + if (x == y) {} // error, string & number are not comparable (unsafe casting) + if (x === y) {} // no error, to match \`let z = (x === y)\` which is allowed + }, + + function(x: string) { + if (x == undefined) {} // ok + if (x == void 0) {} // ok + }, + + function(x: string) { + if (x == null) {} // ok + }, + + function(x: { y: \'foo\' } | { y: \'bar\' }) { + if (x.y == 123) {} // error + if (x.y === 123) {} // ok + }, +] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test exists.js 1`] = ` +"declare class Foo { + foo: string; +} + +function foo0(x: ?string): string { + return x && x || \"\"; +} + +function foo1(x: ?Foo): string { + return x && x.foo || \"\"; +} + +function foo2(x: ?Class): string { + return x && new x().foo || \"\"; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test func_call.js 1`] = ` +"// @flow + +let tests = [ + function(x: { y?: string }, z: () => string) { + if (x.y) { + // make sure we visit the AST in the correct order. if we visit z() before + // x.y, then the function call will invalidate the refinement of x.y + // incorrectly. + x.y.indexOf(z()); // no error + } + }, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test hasOwnProperty.js 1`] = ` +"/* @flow */ + +function foo(x:{y?:() => void}) { + x.y(); // error: could be undefined + if (x.hasOwnProperty(\'y\')) { + x.y(); // error: still could be undefined + } + if (x.hasOwnProperty(\'z\')) { + x.z(); // error: unreachable, but we don\'t help you here + } +} + +function bar(x:Object) { + x.y(); // treated as \`any\`, so allowed + if (x.hasOwnProperty(\'y\')) { + x.y(); // still treated as \`any\`, so allowed + } + if (x.hasOwnProperty(\'z\')) { + x.z(); // also treated as \`any\`, so allowed + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test heap_defassign.js 1`] = ` +"// @flow + +type Obj = { p: number | string } + +function f () {} + +function def_assign_function_havoc(obj: Obj) { + obj.p = 10; // (obj.p : number) + f(); // clears refi + var x: number = obj.p; // error, obj.p : number | string +} + +function def_assign_setprop_havoc(obj: Obj, obj2: Obj) { + obj.p = 10; // (obj.p : number) + obj2.p = \'hey\'; // clears refi + var x: number = obj.p; // error, obj.p : number | string +} + +function def_assign_index_havoc(obj: Obj, obj2: Obj) { + obj.p = 10; // (obj.p : number) + obj2[\'p\'] = \'hey\'; // clears refi + var x: number = obj.p; // error, obj.p : number | string +} + +function def_assign_within_if(b: boolean, obj: Obj) { + if (b) { + obj.p = 10; // (obj.p : number) + var x: number = obj.p // ok by def assign + } + var y: number = obj.p; // error, obj.p : number | string +} + +function def_assign_within_while(b: boolean, obj: Obj) { + while (b) { + obj.p = 10; // (obj.p : number) + var x: number = obj.p // ok by def assign + } + var y: number = obj.p; // error, obj.p : number | string +} + +function def_assign_within_do(b: boolean, obj: Obj) { + do { + obj.p = 10; // (obj.p : number) + var x: number = obj.p // ok by def assign + } while (b); + var y: number = obj.p; // no error, loop runs at least once +} + +function def_assign_within_try(b: boolean, obj: Obj) { + obj.p = 10; // (obj.p : number) + try { + f(); // clears refi and might throw + obj.p = \'hey\'; + } catch (e) { + f(); // clears refi and might throw + obj.p = \'hey\'; + } finally { + // NOTE: the values understood to flow to obj.p at this point + // include the number 42 written downstream; + // so if we did y:string, we would get at least a spurious error + // (among other reasonable errors caused by values written upstream) + var y: number = obj.p; // error, string ~/~ number + obj.p = 42; + } + var z:string = obj.p; // error, number ~/~ string +} + +function def_assign_within_for(b: boolean, obj: Obj) { + for (; b; ) { + obj.p = 10; // (obj.p : number) + var x: number = obj.p // ok by def assign + } + var z: number = obj.p; // error, (number | string) ~/~ number +} + +// --- name-sensitive havoc --- + +type Obj2 = { q: number | string } + +function def_assign_setprop_nohavoc(obj: Obj, obj2: Obj2) { + obj.p = 10; // (obj.p : number) + obj2.q = \'hey\'; // doesn\'t clear refi of .p + var x: number = obj.p; // still ok +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test lib.js 1`] = ` +"/* @flow */ + +declare var BAZ: {stuff?: (x: number) => void} | void; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test missing-property-cond.js 1`] = ` +"// @flow + +function foo1(o: { x: number }) { + if (o.p1) { // OK, this is an idiomatic way of testing property existence + o.x; + } +} + +function foo2(o: { x: number }) { + if (o.p2) { // OK + o.p2.x; // error, since o.p2\'s type is unknown (e.g., could be \`number\`) + } +} + +function foo3(o: { x: number }) { + o.p3.x; // usual error outside conditional +} + +function foo4(o: $Exact<{ x: number }>) { + if (o.p4) { // OK + o.p4.x; // currently OK, should be unreachable + } else { + o.p4.x; // error + } +} + +function foo5() { + const o = { }; + _foo5(); + if (o.p) { o.p(); } + function _foo5() { + o.p = function() { } + } +} + +function foo6(o: mixed) { + if (o.bar) {} // error, any lookup on mixed is unsafe +} + +function foo7(o: mixed) { + if (typeof o.bar === \'string\') {} // error + if (o && typeof o.bar === \'string\') {} // ok + if (o != null && typeof o.bar === \'string\') {} // ok + if (o !== null && o !== undefined && typeof o.bar === \'string\') {} // ok +} + +function foo8(o: { p: mixed }) { + if (o.p && o.p.q) {} // this is ok because o.p is truthy, so o.p.q is safe + if (o.p && o.p.q && o.p.q.r) {} +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test mixed.js 1`] = ` +"/* @flow */ + +function takesNumber(x: number) {} +function takesString(x: string) {} + +function num(x: mixed) { + if (typeof x === \"number\") { + takesString(x); // error + (!x: false); // error: we don\'t know the truthiness of x + } + if (typeof x === \"number\" && x) { + (!x: false); // ok + } + if (x && typeof x === \"number\") { + (!x: false); // ok + } +} + +function str(x: mixed) { + if (typeof x === \"string\") { + takesNumber(x); // error + (!x: false); // error: we don\'t know the truthiness of x + } + if (typeof x === \"string\" && x) { + (!x: false); // ok + } + if (x && typeof x === \"string\") { + (!x: false); // ok + } +} + +function bool(x: mixed) { + if (typeof x === \"boolean\") { + takesString(x); // error + (x: true); // error: we don\'t know the truthiness of x + } + if (typeof x === \"boolean\" && x) { + (x: true); // ok + } + if (x && typeof x === \"boolean\") { + (x: true); // ok + } +} + +function fun(x: mixed) { + if (typeof x === \"function\") { + takesString(x); // error + } +} + +function obj0(x: mixed) { + if (typeof x === \"object\") { + takesString(x); // error + } +} + +function obj1(x: mixed) { + if (Array.isArray(x)) { + takesString(x); // error + } +} + +function undef(x: mixed) { + if (typeof x === \"undefined\") { + takesString(x); // error + } +} + +function null_(x: mixed) { + if (x === null) { + takesString(x); // error + } +} + +function maybe(x: mixed) { + if (x == null) { + takesString(x); // error + } +} + +function true_(x: mixed) { + if (x === true) { + takesString(x); // error + } +} + +function false_(x: mixed) { + if (x === false) { + takesString(x); // error + } +} + +function obj2(x: mixed) { + if (typeof x === \"object\") { + (x: { [key: string]: mixed } | null); + if (x !== null) { + (x[\'foo\']: string); // error, mixed + } + } +} + +function obj2(x: mixed) { + if (typeof x === \"object\" && x) { + (x: Object); + } + if (x && typeof x === \"object\") { + (x: Object); + } + if (x != null && typeof x === \"object\") { + (x: Object); + } + if (x !== null && typeof x === \"object\") { + (x: Object); + } +} + +function arr0(x: mixed) { + if (Array.isArray(x)) { + takesString(x[0]); // error + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test node1.js 1`] = ` +"module.exports = \'Node1\'; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +module.exports = \"Node1\"; + +" +`; + +exports[`test not.js 1`] = ` +"/* @flow */ + +function foo(x: ?bool) { + if (!x) { + x++; // should error for null, void and bool (false) + } +} + +function bar(x: ?number) { + if (!x) { + x[0]; // should error for null, void and number (0) + } +} + +function baz (x: ?number) { + if (x === null || x === undefined) { + return; + } + + if (!x) { + x[0]; // should error for number (0) + } +} + +class TestClass {} + +let tests = [ + function() { + var y = true; + while (y) { + y = !y; + } + }, + function(x: Function) { + (!x: false); // ok, functions are always truthy + }, + function(x: Object) { + (!x: false); // ok, objects are always truthy + }, + function(x: string) { + (!x: false); // error, strings are not always truthy + }, + function(x: number) { + (!x: false); // error, numbers are not always truthy + }, + function(x: boolean) { + (!x: false); // error, bools are not always truthy + }, + function(x: TestClass) { + (!x: false); // ok, classes are always truthy + }, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +function foo(x: ?boolean) { + if (!x) { + x++;// should error for null, void and bool (false) + } +} +function bar(x: ?number) { + if (!x) { + x[0];// should error for null, void and number (0) + } +} +function baz(x: ?number) { + if (x === null || x === undefined) { + return; + } + if (!x) { + x[0];// should error for number (0) + } +} +class TestClass {} +let tests = [ + function() { + var y = true; + while (y) { + y = !y; + } + }, + function(x: Function) { + (!x: false);// ok, functions are always truthy + }, + function(x: Object) { + (!x: false);// ok, objects are always truthy + }, + function(x: string) { + (!x: false);// error, strings are not always truthy + }, + function(x: number) { + (!x: false);// error, numbers are not always truthy + }, + function(x: boolean) { + (!x: false);// error, bools are not always truthy + }, + function(x: TestClass) { + (!x: false);// ok, classes are always truthy + } +]; + +" +`; + +exports[`test null.js 1`] = ` +"/* @flow */ + +function null_bogus_comparison() { + if (100 * null) { + return; + } + if (null * 100) { + return; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +function null_bogus_comparison() { + if (100 * null) { + return; + } + if (null * 100) { + return; + } +} + +" +`; + +exports[`test number.js 1`] = ` +"// @flow + +type Mode = 0 | 1 | 2; + +let tests = [ + function(x: number) { + if (x === 0) { + (x: void); // error + } + (x: 0); // error + }, + + function(x: number) { + if (x !== 0) { + (x: 0); // error + } + (x: void); // error + }, + + function(x: 1): 0 { + if (x === 0) { + return x; // unreachable, no error + } + return 0; + }, + + function(x: 0): number { + if (x === 1) { + return x; + } + return x; + }, + + function(x: 0) { + if (x !== 1) { + (x: 0); + } + (x: 0); + }, + + function(x: 0): number { + if (x === 0) { + return x; + } + return x; + }, + + function(x: 0 | 1) { + if (x === 0) { + (x: 0); + (x: void); // error + } + if (x === 1) { + (x: 1); + (x: void); // error + } + }, + + function(x: { foo: number }): 0 { + if (x.foo === 0) { + return x.foo; + } + return x.foo; // error + }, + + function( + x: { kind: 0, foo: number } | { kind: 1, bar: number } + ): number { + if (x.kind === 0) { + return x.foo; + } else { + return x.bar; + } + }, + + function(num: number, obj: { foo: number }) { + if (num === obj.bar) { // ok, typos allowed in conditionals + } + }, + + function(num: number, obj: {[key: string]: number}) { + if (num === obj.bar) { // ok + } + }, + + function(n: number): Mode { + if (n !== 0 && n !== 1 && n !== 2) { + throw new Error(\"Wrong number passed\"); + } + return n; + }, + + function(s: number): ?Mode { + if (s === 0) { + return s; + } else if (s === 3) { + return s; // error + } + }, + + function(mode: Mode) { + switch (mode) { + case 0: + (mode: 0); + break; + + case 1: + case 2: + (mode: 1 | 2); + break; + } + }, + + function(x: number): 0 { + if (x) { + return x; // error + } else { + return x; // no error, inferred to be 0 + } + }, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/ast-types/lib/types.js:60 + throw new Error(str + \" does not match type \" + this); + ^ + +Error: {type: NumericLiteralTypeAnnotation, start: 22, end: 23, loc: [object Object], value: 0, extra: [object Object]} does not match type Printable + at Type.Tp.assert (/node_modules/ast-types/lib/types.js:60:19) + at genericPrintNoParens (/src/printer.js:221:24) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1530:42) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) +" +`; + +exports[`test property.js 1`] = ` +"/* @flow */ + +function a(x: {[key: string]: ?string}, y: string): string { + if (x[y]) { + return x[y]; + } + return \"\"; +} + +function b(x: {y: {[key: string]: ?string}}, z: string): string { + if (x.y[z]) { + return x.y[z]; + } + return \"\"; +} + +function c(x: {[key: string]: ?string}, y: {z: string}): string { + if (x[y.z]) { + return x[y.z]; + } + return \"\"; +} + +function d(x: {y: {[key: string]: ?string}}, a: {b: string}): string { + if (x.y[a.b]) { + return x.y[a.b]; + } + return \"\"; +} + +function a_array(x: Array, y: number): string { + if (x[y]) { + return x[y]; + } + return \"\"; +} + +function b_array(x: {y: Array}, z: number): string { + if (x.y[z]) { + return x.y[z]; + } + return \"\"; +} + +function c_array(x: Array, y: {z: number}): string { + if (x[y.z]) { + return x[y.z]; + } + return \"\"; +} + +function d_array(x: {y: Array}, a: {b: number}): string { + if (x.y[a.b]) { + return x.y[a.b]; + } + return \"\"; +} + +function e_array(x: Array): string { + if (x[0]) { + return x[0]; + } + return \"\"; +} + +// --- name-sensitive havoc --- + +function c2(x: {[key: string]: ?string}, y: {z: string}): string { + if (x[y.z]) { + y.z = \"HEY\"; + return x[y.z]; // error + } + return \"\"; +} + +function c3(x: {[key: string]: ?string}, y: {z: string, a: string}): string { + if (x[y.z]) { + y.a = \"HEY\"; + return x[y.z]; // ok + } + return \"\"; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test refinements.js 1`] = ` +"function foo(b) { + var x = b? 0 : null; + while (typeof x == \"string\" || typeof x == \"number\") { + var y:string = x; + x = false; + } + var z:string = x; +} + +function bar(b) { + var x = b? 0 : null; + do { + var y:string = x; + x = false; + } while (x === null); + var z:string = x; +} + +function maybe_throw() { } +function qux() { + var x = 0; + try { + maybe_throw(); + x = \"hello\"; + } catch (e) { + maybe_throw(); + x = \"hello\"; + } finally { + // NOTE: the values understood to flow to x at this point + // include the number 42 written downstream; + // so if we did y:string, we would get at least a spurious error + // (among other reasonable errors caused by values written upstream) + var y:number = x; + x = 42; + } + var z:string = x; +} + +function corge(b) { + for (var x = b? 0 : null; + typeof x == \"string\" || typeof x == \"number\"; + x = false) { + var y:string = x; + } + var z:string = x; +} + +function waldo() { + var o = {}; + var x = false; + for (x in o) { + x = 0; // commenting this out would propagate x:string downstream + } + var z:number = x; +} + +// regression test: bring a global into scope by testing it. +// this has no refinement consequences and is error-free. +// the way we currently cache global lookups causes uneven +// distribution of the global\'s entries at path merge time, +// so we need to recognize that it\'s legit rather than an +// internal error. +// +function global_in_conditional0(x: number) { + // merge_env + if (x != 0) { + if (BAZ) { + } + } +} + +function global_in_conditional2(x: number) { + // copy_env + for (var i = 0; i < 100; i++) { + if (BAZ) { + } + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:925 + if (endsWithBrace(doBody)) + ^ + +ReferenceError: endsWithBrace is not defined + at genericPrintNoParens (/src/printer.js:925:11) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:561:16 + at FastPath.call (/src/fast-path.js:113:16) +" +`; + +exports[`test string.js 1`] = ` +"// @flow + +type Mode = \"a\" | \"b\" | \"c\"; + +let tests = [ + function(x: string) { + if (x === \'foo\') { + (x: void); // error + } + (x: \'foo\'); // error + }, + + function(x: string) { + if (x !== \'foo\') { + (x: \'foo\'); // error + } + (x: void); // error + }, + + function(x: \'bar\'): \'foo\' { + if (x === \'foo\') { + return x; // unreachable, no error + } + return \'foo\'; + }, + + function(x: \'foo\'): string { + if (x === \'bar\') { + return x; + } + return x; + }, + + function(x: \'foo\') { + if (x !== \'bar\') { + (x: \'foo\'); + } + (x: \'foo\'); + }, + + function(x: \'foo\'): string { + if (x === \'foo\') { + return x; + } + return x; + }, + + function(x: \'foo\' | \'bar\') { + if (x === \'foo\') { + (x: \'foo\'); + (x: void); // error + } + if (x === \'bar\') { + (x: \'bar\'); + (x: void); // error + } + }, + + function(x: { foo: string }): \'foo\' { + if (x.foo === \'foo\') { + return x.foo; + } + return x.foo; // error + }, + + function( + x: { kind: \'foo\', foo: string } | { kind: \'bar\', bar: string } + ): string { + if (x.kind === \'foo\') { + return x.foo; + } else { + return x.bar; + } + }, + + function(str: string, obj: { foo: string }) { + if (str === obj.bar) { // ok, typos allowed in conditionals + } + }, + + function(str: string, obj: {[key: string]: string}) { + if (str === obj.bar) { // ok + } + }, + + function(str: string): Mode { + var ch = str[0]; + if (ch !== \"a\" && ch !== \"b\" && ch !== \"c\") { + throw new Error(\"Wrong string passed\"); + } + return ch; + }, + + function(s: string): ?Mode { + if (s === \"a\") { + return s; + } else if (s === \"d\") { + return s; // error + } + }, + + function(mode: Mode) { + switch (mode) { + case \"a\": + (mode: \"a\"); + break; + + case \"b\": + case \"c\": + (mode: \"b\" | \"c\"); + break; + } + }, + + function(x: string): \"\" { + if (x) { + return x; // error + } else { + return x; // no error, inferred to be \"\" + } + }, + + // Simple template literals are ok + function(x: string): \'foo\' { + if (x === \`foo\`) { + return x; + } + if (\`foo\` === x) { + return x; + } + return \'foo\'; + }, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test super_member.js 1`] = ` +"/* @flow */ + +class A { + prop: string; + method(): string { + return \"A\"; + } +} + +class B { + test(): string { + if (super.prop) { // super.prop doesn\'t exist + return super.prop; // error, unknown type passed to string expected + } + return \"B\"; + } +} + +class C extends A { + test(): string { + if (super.prop) { + return super.prop; // OK + } + return \"C\"; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +class A { + prop: string; + method(): string { + return \"A\"; + } +} +class B { + test(): string { + if (super.prop) { + // super.prop doesn\'t exist + return super.prop;// error, unknown type passed to string expected + } + return \"B\"; + } +} +class C extends A { + test(): string { + if (super.prop) { + return super.prop;// OK + } + return \"C\"; + } +} + +" +`; + +exports[`test switch.js 1`] = ` +"/* @flow */ + +function foo(text: string | number): string { + switch (typeof text) { +  case \'string\': +   return text; + case \'number\': + return text; // error, should return string +  default: +   return \'wat\'; + } +} + +function bar(text: string | number): string { + switch (typeof text) { + case \'string\': + return text[0]; +  default: + return (text++) + \'\'; + } +} + +function baz1(text: string | number): string { + switch (typeof text) { + case \'number\': + case \'string\': + return text[0]; // error, [0] on number +  default: + return \'wat\'; + } +} + +function baz2(text: string | number): string { + switch (typeof text) { + case \'string\': + case \'number\': + return text[0]; // error, [0] on number +  default: + return \'wat\'; + } +} + +function corge(text: string | number | Array): string { + switch (typeof text) { + case \'object\': + return text[0]; + case \'string\': + case \'number\': + // using ++ since it isn\'t valid on arrays or strings. + // should only error for string since Array was filtered out. + return (text++) + \'\'; +  default: + return \'wat\'; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test tagged_union.js 1`] = ` +"// example 1 + +type Type = Name | ListType; +type Name = {kind: \'Name\', value: string}; +type ListType = {kind: \'ListType\', name: string}; + +function getTypeASTName(typeAST: Type): string { + if (typeAST.kind === \'Name\') { + return typeAST.value; // OK, since typeAST: Name + } else { + return typeAST.name; // OK, since typeAST: ListType + } +} + +// example 2 +import type {ASTNode} from \'./ast_node\'; +var Node = require(\'./node1\'); // Node = \"Node1\" +function foo(x: ASTNode) { + if (x.kind === Node) { + return x.prop1.charAt(0); // typeAST: Node1, but x.prop1 may be undefined + } + return null; +} + +// example 3 +type Apple = { kind: \'Fruit\', taste: \'Bad\' } +type Orange = { kind: \'Fruit\', taste: \'Good\' } +type Broccoli = { kind: \'Veg\', taste: \'Bad\', raw: \'No\' } +type Carrot = { kind: \'Veg\', taste: \'Good\', raw: \'Maybe\' } + +type Breakfast = Apple | Orange | Broccoli | Carrot + +function bar(x: Breakfast) { + if (x.kind === \'Fruit\') { (x.taste: \'Good\'); } // error, Apple.taste = Bad + else (x.raw: \'No\'); // error, Carrot.raw = Maybe +} + +function qux(x: Breakfast) { + if (x.taste === \'Good\') { + (x.raw: \'Yes\' | \'No\'); // 2 errors: + // Orange.raw doesn\'t exist + // Carrot.raw is neither Yes nor No + } +} + +// example 4 +function list(n) { + if (n > 0) return { kind: \"cons\", next: list(n-1) }; + return { kind: \"nil\" }; +} +function length(l) { + switch (l.kind) { + case \"cons\": return 1 + length(l.next); + default: return 0; + } +} +function check(n) { + if (n >= 0) return (n === (length(list(n)))); + return true; +} + + +// example 5 +var EnumKind = { A: 1, B: 2, C: 3}; +type A = { kind: 1, A: number }; +type B = { kind: 2, B: number }; +type C = { kind: 3, C: number }; +function kind(x: A | B | C): number { + switch (x.kind) { + case EnumKind.A: return x.A; + case EnumKind.B: return x.B; + default: return x.A; // error, x: C and property A not found in type C + } +} +kind({ kind: EnumKind.A, A: 1 }); + +// example 6 +type Citizen = { citizen: true }; +type NonCitizen = { citizen: false, nationality: string } +function nationality(x: Citizen | NonCitizen) { + if (x.citizen) return \"Shire\" + else return x.nationality; +} + +let tests = [ + // non-existent props + function test7(x: A) { + if (x.kindTypo === 1) { // typos are allowed to be tested + (x.kindTypo: string); // typos can\'t be used, though + } + }, + + // nested objects + function test8(x: {foo: {bar: 1}}) { + if (x.foo.bar === 1) {} + if (x.fooTypo.bar === 1) {} // error, fooTypo doesn\'t exist + }, + + // invalid RHS + function(x: A) { + if (x.kind === (null).toString()) {} // error, method on null + if ({kind: 1}.kind === (null).toString()) {} // error, method on null + }, + + // non-objects on LHS + function( + x: Array, y: string, z: number, q: boolean, + r: Object, s: Function, t: () => void + ) { + if (x.length === 0) {} + if (x.legnth === 0) { // typos are allowed to be tested + (x.legnth: number); // inside the block, it\'s a number + (x.legnth: string); // error: number literal 0 !~> string + } + if (y.length === 0) {} + if (y.legnth === 0) { // typos are allowed to be tested + (y.legnth: number); // inside the block, it\'s a number + (y.legnth: string); // error: number literal 0 !~> string + } + if (z.toString === 0) {} + if (z.toStirng === 0) { // typos are allowed to be tested + (z.toStirng: number); // inside the block, it\'s a number + (z.toStirng: string); // error: number literal 0 !~> string + } + if (q.valueOf === 0) {} + if (q.valeuOf === 0) { // typos are allowed to be tested + (q.valeuOf: number); // inside the block, it\'s a number + (q.valeuOf: string); // error: number literal 0 !~> string + } + if (r.toStirng === 0) { // typos are allowed to be tested + (r.toStirng: empty); // props on AnyObjT are \`any\` + } + if (s.call === 0) {} + if (s.calll === 0) { // typos are allowed to be tested + (t.calll: empty); // ok, props on functions are \`any\` :/ + } + if (t.call === 0) {} + if (t.calll === 0) { // typos are allowed to be tested + (t.calll: empty); // ok, props on functions are \`any\` :/ + } + }, + + // sentinel props become the RHS + function(x: { str: string, num: number, bool: boolean }) { + if (x.str === \'str\') { + (x.str: \'not str\'); // error: \'str\' !~> \'not str\' + } + if (x.num === 123) { + (x.num: 456); // error: 123 !~> 456 + } + if (x.bool === true) { + (x.bool: false); // error: true !~> false + } + // even if it doesn\'t exist... + if (x.badStr === \'bad\') { + (x.badStr: empty); // error: \'bad\' !~> empty + } + if (x.badNum === 123) { + (x.badNum: empty); // error: 123 !~> empty + } + if (x.badBool === true) { + (x.badBool: empty); // error: true !~> empty + } + }, + + // type mismatch + function(x: { foo: 123, y: string } | { foo: \'foo\', z: string }) { + if (x.foo === 123) { + (x.y: string); + x.z; // error + } else { + (x.z: string); + x.y; // error + } + if (x.foo === \'foo\') { + (x.z: string); + x.y; // error + } else { + (x.y: string); + x.z; // error + } + }, + + // type mismatch, but one is not a literal + function(x: { foo: number, y: string } | { foo: \'foo\', z: string }) { + if (x.foo === 123) { + (x.y: string); // ok, because 123 !== \'foo\' + x.z; // error + } else { + x.y; // error: x.foo could be a string + x.z; // error: could still be either case (if foo was a different number) + } + + if (x.foo === \'foo\') { + (x.z: string); + x.y; // error + } else { + (x.y: string); + x.z; // error + } + }, + + // type mismatch, neither is a literal + function(x: { foo: number, y: string } | { foo: string, z: string }) { + if (x.foo === 123) { + (x.y: string); // ok, because 123 !== string + x.z; // error + } else { + x.y; // error: x.foo could be a string + x.z; // error: could still be either case (if foo was a different number) + } + + if (x.foo === \'foo\') { + (x.z: string); + x.y; // error + } else { + x.y; // error: x.foo could be a different string + x.z; // error: x.foo could be a number + } + }, + + // type mismatch, neither is a literal, test is not a literal either + function( + x: { foo: number, y: string } | { foo: string, z: string }, + num: number + ) { + if (x.foo === num) { + x.y; // error: flow isn\'t smart enough to figure this out yet + x.z; // error + } + } +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test tagged_union_import.js 1`] = ` +"/* @flow */ + +import { SUCCESS, ERROR } from \'./constants\' + +type Success = { + type: typeof SUCCESS, + message: string +} + +type Error = { + type: typeof ERROR, + error: string +} + +function handleStatus(status: Success | Error) { + switch(status.type) { + case SUCCESS: + console.log(\`Successful: \${status.message}\`); + break; + default: + console.log(\`Errored: \${status.error}\`); + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test typeof.js 1`] = ` +"/* @flow */ + +function foo(x: bool | number) { + if (typeof x === \"boolean\") { + x[0]; // error for boolean, not number + } +} + +function bar(): number { + var x = null; + if (typeof x === \"object\") { + return x; // error, null + } + return 0; +} + +/* refining globals */ +function fn0() { + if (typeof BAZ !== \'undefined\' && + typeof BAZ.stuff === \'function\') { + BAZ.stuff(123); + } + BAZ.stuff(123); // error, refinement is gone +} +function fn1() { + BAZ.stuff; // error, could be undefined + if (typeof BAZ !== \'undefined\' && + typeof BAZ.stuff === \'function\') { + BAZ.stuff(123); // ok + BAZ.stuff(123); // error, refinement is gone + } +} + +function anyfun(x: number | Function): number { + if (typeof x === \"function\") { + return 0; + } + return x; // OK, x refined to \`number\` +} + +function anyobj(x: number | Object): number { + if (typeof x === \"object\") { + return 0; + } + return x; // OK, x refined to \`number\` +} + +function testInvalidValue(x: mixed) { + if (typeof x === \"foo\") { // error + return 0; + } +} + +function testTemplateLiteral(x: string | number) { + if (typeof x === \`string\`) { + return x.length; + } +} + +function testInvalidTemplateLiteral(x: string | number) { + if (typeof x === \`foo\`) { // error + return 0; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test undef.js 1`] = ` +"/* @flow */ + +function undef_var(x: ?number) { + if (x !== null && x !== undefined) { + var y = x * 1000; + } +} + +function undef_var_rev(x: ?number) { + if (x === null || x === undefined) { + } else { + var y = x * 1000; + } +} + +function undef_prop(x: { x: ?number }) { + if (x.x !== null && x.x !== undefined) { + var y = x.x * 1000; + } +} + +function undef_prop_rev(x: { x: ?number }) { + if (x.x === null || x.x === undefined) { + } else { + var y = x.x * 1000; + } +} + +function undef_var_fail(x: ?number) { + if (x !== undefined) { + var y = x * 1000; + } +} + +function undef_var_fail_rev(x: ?number) { + if (x === undefined) { + } else { + var y = x * 1000; + } +} + +function undef_prop_fail(x: { x: ?number }) { + if (x.x !== undefined) { + var y = x.x * 1000; + } +} + +function undef_prop_fail_rev(x: { x: ?number }) { + if (x.x === undefined) { + } else { + var y = x.x * 1000; + } +} + +function undef_unreachable(x: number) { + if (x === undefined) { + var y = x * 1000; // unreachable + } + if (x == undefined) { + var z = x * 1000; // unreachable + } +} + +function undef_var_nonstrict(x: ?number, y: ?number) { + if (x != undefined) { + var a = x * 1000; + } + if (y == undefined){ + var b = y * 1000; // error + } +} + +function undef_bogus_comparison() { + if (100 * undefined) { + return; + } + if (undefined * 100) { + return; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +function undef_var(x: ?number) { + if (x !== null && x !== undefined) { + var y = x * 1000; + } +} +function undef_var_rev(x: ?number) { + if (x === null || x === undefined) { + + } else { + var y = x * 1000; + } +} +function undef_prop(x: { x: ?number }) { + if (x.x !== null && x.x !== undefined) { + var y = x.x * 1000; + } +} +function undef_prop_rev(x: { x: ?number }) { + if (x.x === null || x.x === undefined) { + + } else { + var y = x.x * 1000; + } +} +function undef_var_fail(x: ?number) { + if (x !== undefined) { + var y = x * 1000; + } +} +function undef_var_fail_rev(x: ?number) { + if (x === undefined) { + + } else { + var y = x * 1000; + } +} +function undef_prop_fail(x: { x: ?number }) { + if (x.x !== undefined) { + var y = x.x * 1000; + } +} +function undef_prop_fail_rev(x: { x: ?number }) { + if (x.x === undefined) { + + } else { + var y = x.x * 1000; + } +} +function undef_unreachable(x: number) { + if (x === undefined) { + var y = x * 1000;// unreachable + } + if (x == undefined) { + var z = x * 1000;// unreachable + } +} +function undef_var_nonstrict(x: ?number, y: ?number) { + if (x != undefined) { + var a = x * 1000; + } + if (y == undefined) { + var b = y * 1000;// error + } +} +function undef_bogus_comparison() { + if (100 * undefined) { + return; + } + if (undefined * 100) { + return; + } +} + +" +`; + +exports[`test union.js 1`] = ` +"/* @flow */ + +type thing = number | bool + +function foo(x: thing) { + if (x === true) { + x[0]; // error on boolean + } +} + +function bar(x: thing) { + if (x !== true && x !== false) { + x[0]; // error on number + } +} + +function baz(x: ?thing) { + if (x && x !== true) { + x[0]; // error on number + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test void.js 1`] = ` +"/* @flow */ + +function void_var(x: ?number) { + if (x !== null && x !== void(0)) { + var y = x * 1000; + } +} + +function void_var_rev(x: ?number) { + if (x === null || x === void(0)) { + } else { + var y = x * 1000; + } +} + +function void_pro(x: { x: ?number }) { + if (x.x !== null && x.x !== void(0)) { + var y = x.x * 1000; + } +} + +function void_pro_rev(x: { x: ?number }) { + if (x.x === null || x.x === void(0)) { + } else { + var y = x.x * 1000; + } +} + +function void_var_fail(x: ?number) { + if (x !== void(0)) { + var y = x * 1000; + } +} + +function void_var_fail_rev(x: ?number) { + if (x === void(0)) { + } else { + var y = x * 1000; + } +} + +function void_pro_fail(x: { x: ?number }) { + if (x.x !== void(0)) { + var y = x.x * 1000; + } +} + +function void_pro_fail_rev(x: { x: ?number }) { + if (x.x === void(0)) { + } else { + var y = x.x * 1000; + } +} + +function void_var_side_effect(x: ?number) { + if (x !== null && x !== void(x * 1000)) { + var y = x * 1000; + } +} + +function void_var_side_effect_rev(x: ?number) { + if (x === null || x === void(x * 1000)) { + } else { + var y = x * 1000; + } +} + +function void_prop_side_effect(x: { x: ?number }) { + if (x.x !== null && x.x !== void(x.x * 1000)) { + var y = x.x * 1000; + } +} + +function void_prop_side_effect_rev(x: { x: ?number }) { + if (x.x === null || x.x === void(x.x * 1000)) { + } else { + var y = x.x * 1000; + } +} + +function void_bogus_comparison() { + if (100 * void(0)) { + return; + } + if (void(0) * 100) { + return; + } +} + +function void_redefined_undefined(x: ?number) { + var undefined = \"foo\"; + if (x !== null && x !== void(0)) { + var y = x * 1000; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +function void_var(x: ?number) { + if (x !== null && x !== void 0) { + var y = x * 1000; + } +} +function void_var_rev(x: ?number) { + if (x === null || x === void 0) { + + } else { + var y = x * 1000; + } +} +function void_pro(x: { x: ?number }) { + if (x.x !== null && x.x !== void 0) { + var y = x.x * 1000; + } +} +function void_pro_rev(x: { x: ?number }) { + if (x.x === null || x.x === void 0) { + + } else { + var y = x.x * 1000; + } +} +function void_var_fail(x: ?number) { + if (x !== void 0) { + var y = x * 1000; + } +} +function void_var_fail_rev(x: ?number) { + if (x === void 0) { + + } else { + var y = x * 1000; + } +} +function void_pro_fail(x: { x: ?number }) { + if (x.x !== void 0) { + var y = x.x * 1000; + } +} +function void_pro_fail_rev(x: { x: ?number }) { + if (x.x === void 0) { + + } else { + var y = x.x * 1000; + } +} +function void_var_side_effect(x: ?number) { + if (x !== null && x !== void (x * 1000)) { + var y = x * 1000; + } +} +function void_var_side_effect_rev(x: ?number) { + if (x === null || x === void (x * 1000)) { + + } else { + var y = x * 1000; + } +} +function void_prop_side_effect(x: { x: ?number }) { + if (x.x !== null && x.x !== void (x.x * 1000)) { + var y = x.x * 1000; + } +} +function void_prop_side_effect_rev(x: { x: ?number }) { + if (x.x === null || x.x === void (x.x * 1000)) { + + } else { + var y = x.x * 1000; + } +} +function void_bogus_comparison() { + if (100 * void 0) { + return; + } + if (void 0 * 100) { + return; + } +} +function void_redefined_undefined(x: ?number) { + var undefined = \"foo\"; + if (x !== null && x !== void 0) { + var y = x * 1000; + } +} + +" +`; diff --git a/tests/refinements/assignment.js b/tests/refinements/assignment.js new file mode 100644 index 000000000000..a331d6d3e271 --- /dev/null +++ b/tests/refinements/assignment.js @@ -0,0 +1,32 @@ +/* @flow */ + +function foo(x : ?number) { + var y; + if (y = x) { + var z = y * 1000; + } +} + +type Bar = { + parent: ?Bar; + doStuff: () => void +} + +function bar0(x : Bar) { + while (x = x.parent) { // can't assign x to ?Bar + x.doStuff(); + } +} + +function bar1(x : ?Bar) { + while (x = x.parent) { // x.parent might be null + x.doStuff(); + } +} + +function bar2(x : Bar) { + var y = x; + while (y = y.parent) { + y.doStuff(); + } +} diff --git a/tests/refinements/ast_node.js b/tests/refinements/ast_node.js new file mode 100644 index 000000000000..396d466e1d94 --- /dev/null +++ b/tests/refinements/ast_node.js @@ -0,0 +1,11 @@ +type Node1 = { + kind: 'Node1', + prop1?: string +}; + +type Node2 = { + kind: 'Node2', + prop2?: string +} + +export type ASTNode = Node1 | Node2; diff --git a/tests/refinements/bool.js b/tests/refinements/bool.js new file mode 100644 index 000000000000..5730681c83a7 --- /dev/null +++ b/tests/refinements/bool.js @@ -0,0 +1,46 @@ +/* @flow */ + +function foo(x: ?bool) { + if (x === false) { + return; + } + + if (x === true) { + return; + } + + x[0]; // error on null and undefined +} + +function bar(x: ?bool) { + if (x !== true) { + if (x !== false) { + x[0]; // error on null and undefined + } + } +} + +function baz(x: ?bool) { + if (100 * false) { + return; + } + if (false * 100) { + return; + } +} + +let tests = [ + function(x: { done: true, result: string } | { done: false }) { + if (x.done === true) { + return x.result; + } + return x.result; // error + }, + + function(x: { done: true, result: string } | { done: false }) { + if (true === x.done) { + return x.result; + } + return x.result; // error + }, +]; diff --git a/tests/refinements/computed_string_literal.js b/tests/refinements/computed_string_literal.js new file mode 100644 index 000000000000..93fa0716cec4 --- /dev/null +++ b/tests/refinements/computed_string_literal.js @@ -0,0 +1,19 @@ +// @flow + +type A = { + 'b_c': ?string +}; + +function stuff(str: string) {} + +function testProperty(a: A) { + if (a.b_c) { + stuff(a.b_c) + } +} + +function testLiteralProperty(a: A) { + if (a['b_c']) { + stuff(a['b_c']) + } +} diff --git a/tests/refinements/cond_prop.js b/tests/refinements/cond_prop.js new file mode 100644 index 000000000000..7bab5dfef721 --- /dev/null +++ b/tests/refinements/cond_prop.js @@ -0,0 +1,52 @@ +/* @flow */ + +type Type = Name | ListType | NonNullType; +type Name = {kind: 'Name', value: string, type: void }; +type ListType = {kind: 'ListType', type: Type}; +type NonNullType = {kind: 'NonNullType', type: Name | ListType | BadType}; +type BadType = {}; + +function getTypeASTName(typeAST: Type): string { + if (!typeAST.type) throw new Error('Must be wrapping type'); // OK + return getTypeASTName(typeAST.type); // error, BadType not a subtype of Type +} + +let tests = [ + function(x: { done: true, result: string } | { done: false }) { + if (x.done) { + return x.result; + } + return x.result; // error + }, + + function(x: { done: true, result: string } | { foo: string }) { + if (x.done) { + return x.result; // error, consider { foo: "herp", done: "derp" } + } + return x.result; // error + }, + + function() { + type T + = { foo: Object, bar: string } + | { baz: string, quux: string } + + function testAlwaysTruthyProp(t: T) { + if (t.foo) { + (t.bar: string); // error, consider { baz: "x", quux: "y", foo: "boom" } + } else { + (t.quux: string); // ok. since foo is an object (always truthy), the + // else case completely rules out the first branch of + // the union. + } + } + + function testSometimesTruthyProp(t: T) { + if (t.bar) { + (t.foo: Object); // error, consider { baz: "x", quux: "y", bar: "boom" } + } else { + (t.quux: string); // error, consider { foo: {}, bar: "" } + } + } + }, +] diff --git a/tests/refinements/constants.js b/tests/refinements/constants.js new file mode 100644 index 000000000000..c845d58c24db --- /dev/null +++ b/tests/refinements/constants.js @@ -0,0 +1,4 @@ +/* @flow */ + +export const SUCCESS: 'SUCCESS' = 'SUCCESS'; +export const ERROR: 'ERROR' = 'ERROR'; diff --git a/tests/refinements/eq.js b/tests/refinements/eq.js new file mode 100644 index 000000000000..24fdaab5a448 --- /dev/null +++ b/tests/refinements/eq.js @@ -0,0 +1,22 @@ +/* @flow */ + +let tests = [ + function(x: string, y: number) { + if (x == y) {} // error, string & number are not comparable (unsafe casting) + if (x === y) {} // no error, to match `let z = (x === y)` which is allowed + }, + + function(x: string) { + if (x == undefined) {} // ok + if (x == void 0) {} // ok + }, + + function(x: string) { + if (x == null) {} // ok + }, + + function(x: { y: 'foo' } | { y: 'bar' }) { + if (x.y == 123) {} // error + if (x.y === 123) {} // ok + }, +] diff --git a/tests/refinements/exists.js b/tests/refinements/exists.js new file mode 100644 index 000000000000..b26e29a5b980 --- /dev/null +++ b/tests/refinements/exists.js @@ -0,0 +1,15 @@ +declare class Foo { + foo: string; +} + +function foo0(x: ?string): string { + return x && x || ""; +} + +function foo1(x: ?Foo): string { + return x && x.foo || ""; +} + +function foo2(x: ?Class): string { + return x && new x().foo || ""; +} diff --git a/tests/refinements/func_call.js b/tests/refinements/func_call.js new file mode 100644 index 000000000000..693c3c44e929 --- /dev/null +++ b/tests/refinements/func_call.js @@ -0,0 +1,12 @@ +// @flow + +let tests = [ + function(x: { y?: string }, z: () => string) { + if (x.y) { + // make sure we visit the AST in the correct order. if we visit z() before + // x.y, then the function call will invalidate the refinement of x.y + // incorrectly. + x.y.indexOf(z()); // no error + } + }, +]; diff --git a/tests/refinements/hasOwnProperty.js b/tests/refinements/hasOwnProperty.js new file mode 100644 index 000000000000..1545a554df94 --- /dev/null +++ b/tests/refinements/hasOwnProperty.js @@ -0,0 +1,21 @@ +/* @flow */ + +function foo(x:{y?:() => void}) { + x.y(); // error: could be undefined + if (x.hasOwnProperty('y')) { + x.y(); // error: still could be undefined + } + if (x.hasOwnProperty('z')) { + x.z(); // error: unreachable, but we don't help you here + } +} + +function bar(x:Object) { + x.y(); // treated as `any`, so allowed + if (x.hasOwnProperty('y')) { + x.y(); // still treated as `any`, so allowed + } + if (x.hasOwnProperty('z')) { + x.z(); // also treated as `any`, so allowed + } +} diff --git a/tests/refinements/heap_defassign.js b/tests/refinements/heap_defassign.js new file mode 100644 index 000000000000..09a760588469 --- /dev/null +++ b/tests/refinements/heap_defassign.js @@ -0,0 +1,84 @@ +// @flow + +type Obj = { p: number | string } + +function f () {} + +function def_assign_function_havoc(obj: Obj) { + obj.p = 10; // (obj.p : number) + f(); // clears refi + var x: number = obj.p; // error, obj.p : number | string +} + +function def_assign_setprop_havoc(obj: Obj, obj2: Obj) { + obj.p = 10; // (obj.p : number) + obj2.p = 'hey'; // clears refi + var x: number = obj.p; // error, obj.p : number | string +} + +function def_assign_index_havoc(obj: Obj, obj2: Obj) { + obj.p = 10; // (obj.p : number) + obj2['p'] = 'hey'; // clears refi + var x: number = obj.p; // error, obj.p : number | string +} + +function def_assign_within_if(b: boolean, obj: Obj) { + if (b) { + obj.p = 10; // (obj.p : number) + var x: number = obj.p // ok by def assign + } + var y: number = obj.p; // error, obj.p : number | string +} + +function def_assign_within_while(b: boolean, obj: Obj) { + while (b) { + obj.p = 10; // (obj.p : number) + var x: number = obj.p // ok by def assign + } + var y: number = obj.p; // error, obj.p : number | string +} + +function def_assign_within_do(b: boolean, obj: Obj) { + do { + obj.p = 10; // (obj.p : number) + var x: number = obj.p // ok by def assign + } while (b); + var y: number = obj.p; // no error, loop runs at least once +} + +function def_assign_within_try(b: boolean, obj: Obj) { + obj.p = 10; // (obj.p : number) + try { + f(); // clears refi and might throw + obj.p = 'hey'; + } catch (e) { + f(); // clears refi and might throw + obj.p = 'hey'; + } finally { + // NOTE: the values understood to flow to obj.p at this point + // include the number 42 written downstream; + // so if we did y:string, we would get at least a spurious error + // (among other reasonable errors caused by values written upstream) + var y: number = obj.p; // error, string ~/~ number + obj.p = 42; + } + var z:string = obj.p; // error, number ~/~ string +} + +function def_assign_within_for(b: boolean, obj: Obj) { + for (; b; ) { + obj.p = 10; // (obj.p : number) + var x: number = obj.p // ok by def assign + } + var z: number = obj.p; // error, (number | string) ~/~ number +} + +// --- name-sensitive havoc --- + +type Obj2 = { q: number | string } + +function def_assign_setprop_nohavoc(obj: Obj, obj2: Obj2) { + obj.p = 10; // (obj.p : number) + obj2.q = 'hey'; // doesn't clear refi of .p + var x: number = obj.p; // still ok +} diff --git a/tests/refinements/jsfmt.spec.js b/tests/refinements/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/refinements/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/refinements/lib.js b/tests/refinements/lib.js new file mode 100644 index 000000000000..211648743a38 --- /dev/null +++ b/tests/refinements/lib.js @@ -0,0 +1,3 @@ +/* @flow */ + +declare var BAZ: {stuff?: (x: number) => void} | void; diff --git a/tests/refinements/missing-property-cond.js b/tests/refinements/missing-property-cond.js new file mode 100644 index 000000000000..944bd0f99d7d --- /dev/null +++ b/tests/refinements/missing-property-cond.js @@ -0,0 +1,50 @@ +// @flow + +function foo1(o: { x: number }) { + if (o.p1) { // OK, this is an idiomatic way of testing property existence + o.x; + } +} + +function foo2(o: { x: number }) { + if (o.p2) { // OK + o.p2.x; // error, since o.p2's type is unknown (e.g., could be `number`) + } +} + +function foo3(o: { x: number }) { + o.p3.x; // usual error outside conditional +} + +function foo4(o: $Exact<{ x: number }>) { + if (o.p4) { // OK + o.p4.x; // currently OK, should be unreachable + } else { + o.p4.x; // error + } +} + +function foo5() { + const o = { }; + _foo5(); + if (o.p) { o.p(); } + function _foo5() { + o.p = function() { } + } +} + +function foo6(o: mixed) { + if (o.bar) {} // error, any lookup on mixed is unsafe +} + +function foo7(o: mixed) { + if (typeof o.bar === 'string') {} // error + if (o && typeof o.bar === 'string') {} // ok + if (o != null && typeof o.bar === 'string') {} // ok + if (o !== null && o !== undefined && typeof o.bar === 'string') {} // ok +} + +function foo8(o: { p: mixed }) { + if (o.p && o.p.q) {} // this is ok because o.p is truthy, so o.p.q is safe + if (o.p && o.p.q && o.p.q.r) {} +} diff --git a/tests/refinements/mixed.js b/tests/refinements/mixed.js new file mode 100644 index 000000000000..5c12b32c541e --- /dev/null +++ b/tests/refinements/mixed.js @@ -0,0 +1,121 @@ +/* @flow */ + +function takesNumber(x: number) {} +function takesString(x: string) {} + +function num(x: mixed) { + if (typeof x === "number") { + takesString(x); // error + (!x: false); // error: we don't know the truthiness of x + } + if (typeof x === "number" && x) { + (!x: false); // ok + } + if (x && typeof x === "number") { + (!x: false); // ok + } +} + +function str(x: mixed) { + if (typeof x === "string") { + takesNumber(x); // error + (!x: false); // error: we don't know the truthiness of x + } + if (typeof x === "string" && x) { + (!x: false); // ok + } + if (x && typeof x === "string") { + (!x: false); // ok + } +} + +function bool(x: mixed) { + if (typeof x === "boolean") { + takesString(x); // error + (x: true); // error: we don't know the truthiness of x + } + if (typeof x === "boolean" && x) { + (x: true); // ok + } + if (x && typeof x === "boolean") { + (x: true); // ok + } +} + +function fun(x: mixed) { + if (typeof x === "function") { + takesString(x); // error + } +} + +function obj0(x: mixed) { + if (typeof x === "object") { + takesString(x); // error + } +} + +function obj1(x: mixed) { + if (Array.isArray(x)) { + takesString(x); // error + } +} + +function undef(x: mixed) { + if (typeof x === "undefined") { + takesString(x); // error + } +} + +function null_(x: mixed) { + if (x === null) { + takesString(x); // error + } +} + +function maybe(x: mixed) { + if (x == null) { + takesString(x); // error + } +} + +function true_(x: mixed) { + if (x === true) { + takesString(x); // error + } +} + +function false_(x: mixed) { + if (x === false) { + takesString(x); // error + } +} + +function obj2(x: mixed) { + if (typeof x === "object") { + (x: { [key: string]: mixed } | null); + if (x !== null) { + (x['foo']: string); // error, mixed + } + } +} + +function obj2(x: mixed) { + if (typeof x === "object" && x) { + (x: Object); + } + if (x && typeof x === "object") { + (x: Object); + } + if (x != null && typeof x === "object") { + (x: Object); + } + if (x !== null && typeof x === "object") { + (x: Object); + } +} + +function arr0(x: mixed) { + if (Array.isArray(x)) { + takesString(x[0]); // error + } +} diff --git a/tests/refinements/node1.js b/tests/refinements/node1.js new file mode 100644 index 000000000000..97afd3f36556 --- /dev/null +++ b/tests/refinements/node1.js @@ -0,0 +1 @@ +module.exports = 'Node1'; diff --git a/tests/refinements/not.js b/tests/refinements/not.js new file mode 100644 index 000000000000..dafd50927609 --- /dev/null +++ b/tests/refinements/not.js @@ -0,0 +1,52 @@ +/* @flow */ + +function foo(x: ?bool) { + if (!x) { + x++; // should error for null, void and bool (false) + } +} + +function bar(x: ?number) { + if (!x) { + x[0]; // should error for null, void and number (0) + } +} + +function baz (x: ?number) { + if (x === null || x === undefined) { + return; + } + + if (!x) { + x[0]; // should error for number (0) + } +} + +class TestClass {} + +let tests = [ + function() { + var y = true; + while (y) { + y = !y; + } + }, + function(x: Function) { + (!x: false); // ok, functions are always truthy + }, + function(x: Object) { + (!x: false); // ok, objects are always truthy + }, + function(x: string) { + (!x: false); // error, strings are not always truthy + }, + function(x: number) { + (!x: false); // error, numbers are not always truthy + }, + function(x: boolean) { + (!x: false); // error, bools are not always truthy + }, + function(x: TestClass) { + (!x: false); // ok, classes are always truthy + }, +]; diff --git a/tests/refinements/null.js b/tests/refinements/null.js new file mode 100644 index 000000000000..f21881abda93 --- /dev/null +++ b/tests/refinements/null.js @@ -0,0 +1,10 @@ +/* @flow */ + +function null_bogus_comparison() { + if (100 * null) { + return; + } + if (null * 100) { + return; + } +} diff --git a/tests/refinements/number.js b/tests/refinements/number.js new file mode 100644 index 000000000000..4c192b8e428e --- /dev/null +++ b/tests/refinements/number.js @@ -0,0 +1,121 @@ +// @flow + +type Mode = 0 | 1 | 2; + +let tests = [ + function(x: number) { + if (x === 0) { + (x: void); // error + } + (x: 0); // error + }, + + function(x: number) { + if (x !== 0) { + (x: 0); // error + } + (x: void); // error + }, + + function(x: 1): 0 { + if (x === 0) { + return x; // unreachable, no error + } + return 0; + }, + + function(x: 0): number { + if (x === 1) { + return x; + } + return x; + }, + + function(x: 0) { + if (x !== 1) { + (x: 0); + } + (x: 0); + }, + + function(x: 0): number { + if (x === 0) { + return x; + } + return x; + }, + + function(x: 0 | 1) { + if (x === 0) { + (x: 0); + (x: void); // error + } + if (x === 1) { + (x: 1); + (x: void); // error + } + }, + + function(x: { foo: number }): 0 { + if (x.foo === 0) { + return x.foo; + } + return x.foo; // error + }, + + function( + x: { kind: 0, foo: number } | { kind: 1, bar: number } + ): number { + if (x.kind === 0) { + return x.foo; + } else { + return x.bar; + } + }, + + function(num: number, obj: { foo: number }) { + if (num === obj.bar) { // ok, typos allowed in conditionals + } + }, + + function(num: number, obj: {[key: string]: number}) { + if (num === obj.bar) { // ok + } + }, + + function(n: number): Mode { + if (n !== 0 && n !== 1 && n !== 2) { + throw new Error("Wrong number passed"); + } + return n; + }, + + function(s: number): ?Mode { + if (s === 0) { + return s; + } else if (s === 3) { + return s; // error + } + }, + + function(mode: Mode) { + switch (mode) { + case 0: + (mode: 0); + break; + + case 1: + case 2: + (mode: 1 | 2); + break; + } + }, + + function(x: number): 0 { + if (x) { + return x; // error + } else { + return x; // no error, inferred to be 0 + } + }, +]; diff --git a/tests/refinements/property.js b/tests/refinements/property.js new file mode 100644 index 000000000000..6513e1deb340 --- /dev/null +++ b/tests/refinements/property.js @@ -0,0 +1,82 @@ +/* @flow */ + +function a(x: {[key: string]: ?string}, y: string): string { + if (x[y]) { + return x[y]; + } + return ""; +} + +function b(x: {y: {[key: string]: ?string}}, z: string): string { + if (x.y[z]) { + return x.y[z]; + } + return ""; +} + +function c(x: {[key: string]: ?string}, y: {z: string}): string { + if (x[y.z]) { + return x[y.z]; + } + return ""; +} + +function d(x: {y: {[key: string]: ?string}}, a: {b: string}): string { + if (x.y[a.b]) { + return x.y[a.b]; + } + return ""; +} + +function a_array(x: Array, y: number): string { + if (x[y]) { + return x[y]; + } + return ""; +} + +function b_array(x: {y: Array}, z: number): string { + if (x.y[z]) { + return x.y[z]; + } + return ""; +} + +function c_array(x: Array, y: {z: number}): string { + if (x[y.z]) { + return x[y.z]; + } + return ""; +} + +function d_array(x: {y: Array}, a: {b: number}): string { + if (x.y[a.b]) { + return x.y[a.b]; + } + return ""; +} + +function e_array(x: Array): string { + if (x[0]) { + return x[0]; + } + return ""; +} + +// --- name-sensitive havoc --- + +function c2(x: {[key: string]: ?string}, y: {z: string}): string { + if (x[y.z]) { + y.z = "HEY"; + return x[y.z]; // error + } + return ""; +} + +function c3(x: {[key: string]: ?string}, y: {z: string, a: string}): string { + if (x[y.z]) { + y.a = "HEY"; + return x[y.z]; // ok + } + return ""; +} diff --git a/tests/refinements/refinements.js b/tests/refinements/refinements.js new file mode 100644 index 000000000000..76da9e52058a --- /dev/null +++ b/tests/refinements/refinements.js @@ -0,0 +1,78 @@ +function foo(b) { + var x = b? 0 : null; + while (typeof x == "string" || typeof x == "number") { + var y:string = x; + x = false; + } + var z:string = x; +} + +function bar(b) { + var x = b? 0 : null; + do { + var y:string = x; + x = false; + } while (x === null); + var z:string = x; +} + +function maybe_throw() { } +function qux() { + var x = 0; + try { + maybe_throw(); + x = "hello"; + } catch (e) { + maybe_throw(); + x = "hello"; + } finally { + // NOTE: the values understood to flow to x at this point + // include the number 42 written downstream; + // so if we did y:string, we would get at least a spurious error + // (among other reasonable errors caused by values written upstream) + var y:number = x; + x = 42; + } + var z:string = x; +} + +function corge(b) { + for (var x = b? 0 : null; + typeof x == "string" || typeof x == "number"; + x = false) { + var y:string = x; + } + var z:string = x; +} + +function waldo() { + var o = {}; + var x = false; + for (x in o) { + x = 0; // commenting this out would propagate x:string downstream + } + var z:number = x; +} + +// regression test: bring a global into scope by testing it. +// this has no refinement consequences and is error-free. +// the way we currently cache global lookups causes uneven +// distribution of the global's entries at path merge time, +// so we need to recognize that it's legit rather than an +// internal error. +// +function global_in_conditional0(x: number) { + // merge_env + if (x != 0) { + if (BAZ) { + } + } +} + +function global_in_conditional2(x: number) { + // copy_env + for (var i = 0; i < 100; i++) { + if (BAZ) { + } + } +} diff --git a/tests/refinements/string.js b/tests/refinements/string.js new file mode 100644 index 000000000000..62873deda8e4 --- /dev/null +++ b/tests/refinements/string.js @@ -0,0 +1,133 @@ +// @flow + +type Mode = "a" | "b" | "c"; + +let tests = [ + function(x: string) { + if (x === 'foo') { + (x: void); // error + } + (x: 'foo'); // error + }, + + function(x: string) { + if (x !== 'foo') { + (x: 'foo'); // error + } + (x: void); // error + }, + + function(x: 'bar'): 'foo' { + if (x === 'foo') { + return x; // unreachable, no error + } + return 'foo'; + }, + + function(x: 'foo'): string { + if (x === 'bar') { + return x; + } + return x; + }, + + function(x: 'foo') { + if (x !== 'bar') { + (x: 'foo'); + } + (x: 'foo'); + }, + + function(x: 'foo'): string { + if (x === 'foo') { + return x; + } + return x; + }, + + function(x: 'foo' | 'bar') { + if (x === 'foo') { + (x: 'foo'); + (x: void); // error + } + if (x === 'bar') { + (x: 'bar'); + (x: void); // error + } + }, + + function(x: { foo: string }): 'foo' { + if (x.foo === 'foo') { + return x.foo; + } + return x.foo; // error + }, + + function( + x: { kind: 'foo', foo: string } | { kind: 'bar', bar: string } + ): string { + if (x.kind === 'foo') { + return x.foo; + } else { + return x.bar; + } + }, + + function(str: string, obj: { foo: string }) { + if (str === obj.bar) { // ok, typos allowed in conditionals + } + }, + + function(str: string, obj: {[key: string]: string}) { + if (str === obj.bar) { // ok + } + }, + + function(str: string): Mode { + var ch = str[0]; + if (ch !== "a" && ch !== "b" && ch !== "c") { + throw new Error("Wrong string passed"); + } + return ch; + }, + + function(s: string): ?Mode { + if (s === "a") { + return s; + } else if (s === "d") { + return s; // error + } + }, + + function(mode: Mode) { + switch (mode) { + case "a": + (mode: "a"); + break; + + case "b": + case "c": + (mode: "b" | "c"); + break; + } + }, + + function(x: string): "" { + if (x) { + return x; // error + } else { + return x; // no error, inferred to be "" + } + }, + + // Simple template literals are ok + function(x: string): 'foo' { + if (x === `foo`) { + return x; + } + if (`foo` === x) { + return x; + } + return 'foo'; + }, +]; diff --git a/tests/refinements/super_member.js b/tests/refinements/super_member.js new file mode 100644 index 000000000000..3aa941b12d8f --- /dev/null +++ b/tests/refinements/super_member.js @@ -0,0 +1,26 @@ +/* @flow */ + +class A { + prop: string; + method(): string { + return "A"; + } +} + +class B { + test(): string { + if (super.prop) { // super.prop doesn't exist + return super.prop; // error, unknown type passed to string expected + } + return "B"; + } +} + +class C extends A { + test(): string { + if (super.prop) { + return super.prop; // OK + } + return "C"; + } +} diff --git a/tests/refinements/switch.js b/tests/refinements/switch.js new file mode 100644 index 000000000000..f5e41cd22cac --- /dev/null +++ b/tests/refinements/switch.js @@ -0,0 +1,55 @@ +/* @flow */ + +function foo(text: string | number): string { + switch (typeof text) { +  case 'string': +   return text; + case 'number': + return text; // error, should return string +  default: +   return 'wat'; + } +} + +function bar(text: string | number): string { + switch (typeof text) { + case 'string': + return text[0]; +  default: + return (text++) + ''; + } +} + +function baz1(text: string | number): string { + switch (typeof text) { + case 'number': + case 'string': + return text[0]; // error, [0] on number +  default: + return 'wat'; + } +} + +function baz2(text: string | number): string { + switch (typeof text) { + case 'string': + case 'number': + return text[0]; // error, [0] on number +  default: + return 'wat'; + } +} + +function corge(text: string | number | Array): string { + switch (typeof text) { + case 'object': + return text[0]; + case 'string': + case 'number': + // using ++ since it isn't valid on arrays or strings. + // should only error for string since Array was filtered out. + return (text++) + ''; +  default: + return 'wat'; + } +} diff --git a/tests/refinements/tagged_union.js b/tests/refinements/tagged_union.js new file mode 100644 index 000000000000..19c5c185fb45 --- /dev/null +++ b/tests/refinements/tagged_union.js @@ -0,0 +1,232 @@ +// example 1 + +type Type = Name | ListType; +type Name = {kind: 'Name', value: string}; +type ListType = {kind: 'ListType', name: string}; + +function getTypeASTName(typeAST: Type): string { + if (typeAST.kind === 'Name') { + return typeAST.value; // OK, since typeAST: Name + } else { + return typeAST.name; // OK, since typeAST: ListType + } +} + +// example 2 +import type {ASTNode} from './ast_node'; +var Node = require('./node1'); // Node = "Node1" +function foo(x: ASTNode) { + if (x.kind === Node) { + return x.prop1.charAt(0); // typeAST: Node1, but x.prop1 may be undefined + } + return null; +} + +// example 3 +type Apple = { kind: 'Fruit', taste: 'Bad' } +type Orange = { kind: 'Fruit', taste: 'Good' } +type Broccoli = { kind: 'Veg', taste: 'Bad', raw: 'No' } +type Carrot = { kind: 'Veg', taste: 'Good', raw: 'Maybe' } + +type Breakfast = Apple | Orange | Broccoli | Carrot + +function bar(x: Breakfast) { + if (x.kind === 'Fruit') { (x.taste: 'Good'); } // error, Apple.taste = Bad + else (x.raw: 'No'); // error, Carrot.raw = Maybe +} + +function qux(x: Breakfast) { + if (x.taste === 'Good') { + (x.raw: 'Yes' | 'No'); // 2 errors: + // Orange.raw doesn't exist + // Carrot.raw is neither Yes nor No + } +} + +// example 4 +function list(n) { + if (n > 0) return { kind: "cons", next: list(n-1) }; + return { kind: "nil" }; +} +function length(l) { + switch (l.kind) { + case "cons": return 1 + length(l.next); + default: return 0; + } +} +function check(n) { + if (n >= 0) return (n === (length(list(n)))); + return true; +} + + +// example 5 +var EnumKind = { A: 1, B: 2, C: 3}; +type A = { kind: 1, A: number }; +type B = { kind: 2, B: number }; +type C = { kind: 3, C: number }; +function kind(x: A | B | C): number { + switch (x.kind) { + case EnumKind.A: return x.A; + case EnumKind.B: return x.B; + default: return x.A; // error, x: C and property A not found in type C + } +} +kind({ kind: EnumKind.A, A: 1 }); + +// example 6 +type Citizen = { citizen: true }; +type NonCitizen = { citizen: false, nationality: string } +function nationality(x: Citizen | NonCitizen) { + if (x.citizen) return "Shire" + else return x.nationality; +} + +let tests = [ + // non-existent props + function test7(x: A) { + if (x.kindTypo === 1) { // typos are allowed to be tested + (x.kindTypo: string); // typos can't be used, though + } + }, + + // nested objects + function test8(x: {foo: {bar: 1}}) { + if (x.foo.bar === 1) {} + if (x.fooTypo.bar === 1) {} // error, fooTypo doesn't exist + }, + + // invalid RHS + function(x: A) { + if (x.kind === (null).toString()) {} // error, method on null + if ({kind: 1}.kind === (null).toString()) {} // error, method on null + }, + + // non-objects on LHS + function( + x: Array, y: string, z: number, q: boolean, + r: Object, s: Function, t: () => void + ) { + if (x.length === 0) {} + if (x.legnth === 0) { // typos are allowed to be tested + (x.legnth: number); // inside the block, it's a number + (x.legnth: string); // error: number literal 0 !~> string + } + if (y.length === 0) {} + if (y.legnth === 0) { // typos are allowed to be tested + (y.legnth: number); // inside the block, it's a number + (y.legnth: string); // error: number literal 0 !~> string + } + if (z.toString === 0) {} + if (z.toStirng === 0) { // typos are allowed to be tested + (z.toStirng: number); // inside the block, it's a number + (z.toStirng: string); // error: number literal 0 !~> string + } + if (q.valueOf === 0) {} + if (q.valeuOf === 0) { // typos are allowed to be tested + (q.valeuOf: number); // inside the block, it's a number + (q.valeuOf: string); // error: number literal 0 !~> string + } + if (r.toStirng === 0) { // typos are allowed to be tested + (r.toStirng: empty); // props on AnyObjT are `any` + } + if (s.call === 0) {} + if (s.calll === 0) { // typos are allowed to be tested + (t.calll: empty); // ok, props on functions are `any` :/ + } + if (t.call === 0) {} + if (t.calll === 0) { // typos are allowed to be tested + (t.calll: empty); // ok, props on functions are `any` :/ + } + }, + + // sentinel props become the RHS + function(x: { str: string, num: number, bool: boolean }) { + if (x.str === 'str') { + (x.str: 'not str'); // error: 'str' !~> 'not str' + } + if (x.num === 123) { + (x.num: 456); // error: 123 !~> 456 + } + if (x.bool === true) { + (x.bool: false); // error: true !~> false + } + // even if it doesn't exist... + if (x.badStr === 'bad') { + (x.badStr: empty); // error: 'bad' !~> empty + } + if (x.badNum === 123) { + (x.badNum: empty); // error: 123 !~> empty + } + if (x.badBool === true) { + (x.badBool: empty); // error: true !~> empty + } + }, + + // type mismatch + function(x: { foo: 123, y: string } | { foo: 'foo', z: string }) { + if (x.foo === 123) { + (x.y: string); + x.z; // error + } else { + (x.z: string); + x.y; // error + } + if (x.foo === 'foo') { + (x.z: string); + x.y; // error + } else { + (x.y: string); + x.z; // error + } + }, + + // type mismatch, but one is not a literal + function(x: { foo: number, y: string } | { foo: 'foo', z: string }) { + if (x.foo === 123) { + (x.y: string); // ok, because 123 !== 'foo' + x.z; // error + } else { + x.y; // error: x.foo could be a string + x.z; // error: could still be either case (if foo was a different number) + } + + if (x.foo === 'foo') { + (x.z: string); + x.y; // error + } else { + (x.y: string); + x.z; // error + } + }, + + // type mismatch, neither is a literal + function(x: { foo: number, y: string } | { foo: string, z: string }) { + if (x.foo === 123) { + (x.y: string); // ok, because 123 !== string + x.z; // error + } else { + x.y; // error: x.foo could be a string + x.z; // error: could still be either case (if foo was a different number) + } + + if (x.foo === 'foo') { + (x.z: string); + x.y; // error + } else { + x.y; // error: x.foo could be a different string + x.z; // error: x.foo could be a number + } + }, + + // type mismatch, neither is a literal, test is not a literal either + function( + x: { foo: number, y: string } | { foo: string, z: string }, + num: number + ) { + if (x.foo === num) { + x.y; // error: flow isn't smart enough to figure this out yet + x.z; // error + } + } +]; diff --git a/tests/refinements/tagged_union_import.js b/tests/refinements/tagged_union_import.js new file mode 100644 index 000000000000..e6c39c83ade0 --- /dev/null +++ b/tests/refinements/tagged_union_import.js @@ -0,0 +1,23 @@ +/* @flow */ + +import { SUCCESS, ERROR } from './constants' + +type Success = { + type: typeof SUCCESS, + message: string +} + +type Error = { + type: typeof ERROR, + error: string +} + +function handleStatus(status: Success | Error) { + switch(status.type) { + case SUCCESS: + console.log(`Successful: ${status.message}`); + break; + default: + console.log(`Errored: ${status.error}`); + } +} diff --git a/tests/refinements/typeof.js b/tests/refinements/typeof.js new file mode 100644 index 000000000000..0b1f0da2f72c --- /dev/null +++ b/tests/refinements/typeof.js @@ -0,0 +1,64 @@ +/* @flow */ + +function foo(x: bool | number) { + if (typeof x === "boolean") { + x[0]; // error for boolean, not number + } +} + +function bar(): number { + var x = null; + if (typeof x === "object") { + return x; // error, null + } + return 0; +} + +/* refining globals */ +function fn0() { + if (typeof BAZ !== 'undefined' && + typeof BAZ.stuff === 'function') { + BAZ.stuff(123); + } + BAZ.stuff(123); // error, refinement is gone +} +function fn1() { + BAZ.stuff; // error, could be undefined + if (typeof BAZ !== 'undefined' && + typeof BAZ.stuff === 'function') { + BAZ.stuff(123); // ok + BAZ.stuff(123); // error, refinement is gone + } +} + +function anyfun(x: number | Function): number { + if (typeof x === "function") { + return 0; + } + return x; // OK, x refined to `number` +} + +function anyobj(x: number | Object): number { + if (typeof x === "object") { + return 0; + } + return x; // OK, x refined to `number` +} + +function testInvalidValue(x: mixed) { + if (typeof x === "foo") { // error + return 0; + } +} + +function testTemplateLiteral(x: string | number) { + if (typeof x === `string`) { + return x.length; + } +} + +function testInvalidTemplateLiteral(x: string | number) { + if (typeof x === `foo`) { // error + return 0; + } +} diff --git a/tests/refinements/undef.js b/tests/refinements/undef.js new file mode 100644 index 000000000000..14d1b92309ff --- /dev/null +++ b/tests/refinements/undef.js @@ -0,0 +1,80 @@ +/* @flow */ + +function undef_var(x: ?number) { + if (x !== null && x !== undefined) { + var y = x * 1000; + } +} + +function undef_var_rev(x: ?number) { + if (x === null || x === undefined) { + } else { + var y = x * 1000; + } +} + +function undef_prop(x: { x: ?number }) { + if (x.x !== null && x.x !== undefined) { + var y = x.x * 1000; + } +} + +function undef_prop_rev(x: { x: ?number }) { + if (x.x === null || x.x === undefined) { + } else { + var y = x.x * 1000; + } +} + +function undef_var_fail(x: ?number) { + if (x !== undefined) { + var y = x * 1000; + } +} + +function undef_var_fail_rev(x: ?number) { + if (x === undefined) { + } else { + var y = x * 1000; + } +} + +function undef_prop_fail(x: { x: ?number }) { + if (x.x !== undefined) { + var y = x.x * 1000; + } +} + +function undef_prop_fail_rev(x: { x: ?number }) { + if (x.x === undefined) { + } else { + var y = x.x * 1000; + } +} + +function undef_unreachable(x: number) { + if (x === undefined) { + var y = x * 1000; // unreachable + } + if (x == undefined) { + var z = x * 1000; // unreachable + } +} + +function undef_var_nonstrict(x: ?number, y: ?number) { + if (x != undefined) { + var a = x * 1000; + } + if (y == undefined){ + var b = y * 1000; // error + } +} + +function undef_bogus_comparison() { + if (100 * undefined) { + return; + } + if (undefined * 100) { + return; + } +} diff --git a/tests/refinements/union.js b/tests/refinements/union.js new file mode 100644 index 000000000000..145df02c3bfd --- /dev/null +++ b/tests/refinements/union.js @@ -0,0 +1,21 @@ +/* @flow */ + +type thing = number | bool + +function foo(x: thing) { + if (x === true) { + x[0]; // error on boolean + } +} + +function bar(x: thing) { + if (x !== true && x !== false) { + x[0]; // error on number + } +} + +function baz(x: ?thing) { + if (x && x !== true) { + x[0]; // error on number + } +} diff --git a/tests/refinements/void.js b/tests/refinements/void.js new file mode 100644 index 000000000000..87d3a8af8644 --- /dev/null +++ b/tests/refinements/void.js @@ -0,0 +1,95 @@ +/* @flow */ + +function void_var(x: ?number) { + if (x !== null && x !== void(0)) { + var y = x * 1000; + } +} + +function void_var_rev(x: ?number) { + if (x === null || x === void(0)) { + } else { + var y = x * 1000; + } +} + +function void_pro(x: { x: ?number }) { + if (x.x !== null && x.x !== void(0)) { + var y = x.x * 1000; + } +} + +function void_pro_rev(x: { x: ?number }) { + if (x.x === null || x.x === void(0)) { + } else { + var y = x.x * 1000; + } +} + +function void_var_fail(x: ?number) { + if (x !== void(0)) { + var y = x * 1000; + } +} + +function void_var_fail_rev(x: ?number) { + if (x === void(0)) { + } else { + var y = x * 1000; + } +} + +function void_pro_fail(x: { x: ?number }) { + if (x.x !== void(0)) { + var y = x.x * 1000; + } +} + +function void_pro_fail_rev(x: { x: ?number }) { + if (x.x === void(0)) { + } else { + var y = x.x * 1000; + } +} + +function void_var_side_effect(x: ?number) { + if (x !== null && x !== void(x * 1000)) { + var y = x * 1000; + } +} + +function void_var_side_effect_rev(x: ?number) { + if (x === null || x === void(x * 1000)) { + } else { + var y = x * 1000; + } +} + +function void_prop_side_effect(x: { x: ?number }) { + if (x.x !== null && x.x !== void(x.x * 1000)) { + var y = x.x * 1000; + } +} + +function void_prop_side_effect_rev(x: { x: ?number }) { + if (x.x === null || x.x === void(x.x * 1000)) { + } else { + var y = x.x * 1000; + } +} + +function void_bogus_comparison() { + if (100 * void(0)) { + return; + } + if (void(0) * 100) { + return; + } +} + +function void_redefined_undefined(x: ?number) { + var undefined = "foo"; + if (x !== null && x !== void(0)) { + var y = x * 1000; + } +} diff --git a/tests/reflection/__snapshots__/jsfmt.spec.js.snap b/tests/reflection/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..370bc1f8b9fb --- /dev/null +++ b/tests/reflection/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,18 @@ +exports[`test type.js 1`] = ` +"declare var a: number; +var b: typeof a = "..."; +var c: typeof a = "..."; + +type T = number; +var x:T = "..."; + +// what about recursive unions? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +declare var a: number; +var b: typeof a = "..."; +var c: typeof a = "..."; +type T = number; +var x: T = "...";// what about recursive unions? + +" +`; diff --git a/tests/reflection/jsfmt.spec.js b/tests/reflection/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/reflection/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/reflection/type.js b/tests/reflection/type.js new file mode 100644 index 000000000000..de8cd6ed4804 --- /dev/null +++ b/tests/reflection/type.js @@ -0,0 +1,8 @@ +declare var a: number; +var b: typeof a = "..."; +var c: typeof a = "..."; + +type T = number; +var x:T = "..."; + +// what about recursive unions? diff --git a/tests/regexp/__snapshots__/jsfmt.spec.js.snap b/tests/regexp/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..d61d4407d641 --- /dev/null +++ b/tests/regexp/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,9 @@ +exports[`test regexp.js 1`] = ` +"var patt=/Hello/g +var match:number = patt.test("Hello world!"); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var patt = /Hello/g; +var match: number = patt.test("Hello world!"); + +" +`; diff --git a/tests/regexp/jsfmt.spec.js b/tests/regexp/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/regexp/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/regexp/regexp.js b/tests/regexp/regexp.js new file mode 100644 index 000000000000..c8b2340ac812 --- /dev/null +++ b/tests/regexp/regexp.js @@ -0,0 +1,2 @@ +var patt=/Hello/g +var match:number = patt.test("Hello world!"); diff --git a/tests/replace/__snapshots__/jsfmt.spec.js.snap b/tests/replace/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..62ba462c75d7 --- /dev/null +++ b/tests/replace/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,15 @@ +exports[`test test.js 1`] = ` +"var a = 0; + +function foo(x) { } + +foo(""); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var a = 0; +function foo(x) { + +} +foo(""); + +" +`; diff --git a/tests/replace/jsfmt.spec.js b/tests/replace/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/replace/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/replace/test.js b/tests/replace/test.js new file mode 100644 index 000000000000..8a4904c12fa7 --- /dev/null +++ b/tests/replace/test.js @@ -0,0 +1,5 @@ +var a = 0; + +function foo(x) { } + +foo(""); diff --git a/tests/require/B.js b/tests/require/B.js new file mode 100644 index 000000000000..e4d0f1b41f44 --- /dev/null +++ b/tests/require/B.js @@ -0,0 +1,3 @@ +/* @flow */ + +exports.numberValue = 42; diff --git a/tests/require/C.js b/tests/require/C.js new file mode 100644 index 000000000000..e667c4717b69 --- /dev/null +++ b/tests/require/C.js @@ -0,0 +1 @@ +/* @flow */ diff --git a/tests/require/E.js b/tests/require/E.js new file mode 100644 index 000000000000..7d408be6436e --- /dev/null +++ b/tests/require/E.js @@ -0,0 +1,10 @@ +/* @flow */ + +// Local `exports` var is just a ref to module.exports, so mutating the original +// value will affect the exports object but re-binding it makes it useless and +// does not affect the exports value. +module.exports = { + numberValue: 42 +}; + +exports = {stringValue: ''}; diff --git a/tests/require/ProvidesModuleA.js b/tests/require/ProvidesModuleA.js new file mode 100644 index 000000000000..3c651145d2f5 --- /dev/null +++ b/tests/require/ProvidesModuleA.js @@ -0,0 +1,6 @@ +/** + * @providesModule A + * @flow + */ + +exports.numberValue = 42; diff --git a/tests/require/ProvidesModuleD.js b/tests/require/ProvidesModuleD.js new file mode 100644 index 000000000000..a4d5ee842062 --- /dev/null +++ b/tests/require/ProvidesModuleD.js @@ -0,0 +1,4 @@ +/** + * @providesModule D + * @flow + */ diff --git a/tests/require/__snapshots__/jsfmt.spec.js.snap b/tests/require/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..0ce3d84b8ecb --- /dev/null +++ b/tests/require/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,168 @@ +exports[`test B.js 1`] = ` +"/* @flow */ + +exports.numberValue = 42; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +exports.numberValue = 42; + +" +`; + +exports[`test C.js 1`] = ` +"/* @flow */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; + +exports[`test E.js 1`] = ` +"/* @flow */ + +// Local \`exports\` var is just a ref to module.exports, so mutating the original +// value will affect the exports object but re-binding it makes it useless and +// does not affect the exports value. +module.exports = { + numberValue: 42 +}; + +exports = {stringValue: ''}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +// Local \`exports\` var is just a ref to module.exports, so mutating the original +// value will affect the exports object but re-binding it makes it useless and +// does not affect the exports value. +module.exports = { numberValue: 42 }; +exports = { stringValue: "" }; + +" +`; + +exports[`test ProvidesModuleA.js 1`] = ` +"/** + * @providesModule A + * @flow + */ + +exports.numberValue = 42; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule A + * @flow + */ +exports.numberValue = 42; + +" +`; + +exports[`test ProvidesModuleD.js 1`] = ` +"/** + * @providesModule D + * @flow + */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +" +`; + +exports[`test not_builtin_require.js 1`] = ` +"// @flow + +function require() {} +require("not a module name"); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +function require() { + +} +require("not a module name"); + +" +`; + +exports[`test not_builtin_require2.js 1`] = ` +"// @flow + +type require = number; +var a: require = 42; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +type require = number; +var a: require = 42; + +" +`; + +exports[`test require.js 1`] = ` +"/* @flow */ + +function takesANumber(num: number): void {} +function takesAString(str: string): void {} + +// @providesModule +var A = require("A"); +takesANumber(A.numberValue); +takesAString(A.numberValue); + +// File path +var B = require("./B"); +takesANumber(B.numberValue); +takesAString(B.numberValue); + +// C.js exists, but not as a providesModule +require("C"); + +// @providesModule D exists, but not as a filename +require("./D"); + +// E exports an object with a numVal property +var E = require('./E'); +var e_1: number = E.numberValue; +E.stringValue; // Error: The E exports obj has no 'stringValue' property + +// We require that the param passed to require() be a string literal to support +// guaranteed static extraction +var a = './E'; +require(a); // Error: Param must be string literal +require(\`./E\`); // template literals are ok... +require(\`\${'./E'}\`); // error: but only if they have no expressions + +// require.call is allowed but circumverts Flow's static analysis +require.call(null, "DoesNotExist"); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +function takesANumber(num: number): void { + +} +function takesAString(str: string): void { + +} +// @providesModule +var A = require("A"); +takesANumber(A.numberValue); +takesAString(A.numberValue); +// File path +var B = require("./B"); +takesANumber(B.numberValue); +takesAString(B.numberValue); +// C.js exists, but not as a providesModule +require("C"); +// @providesModule D exists, but not as a filename +require("./D"); +// E exports an object with a numVal property +var E = require("./E"); +var e_1: number = E.numberValue; +E.stringValue;// Error: The E exports obj has no 'stringValue' property +// We require that the param passed to require() be a string literal to support +// guaranteed static extraction +var a = "./E"; +require(a);// Error: Param must be string literal +require(\`./E\`);// template literals are ok... +require(\`\${"./E"}\`);// error: but only if they have no expressions +// require.call is allowed but circumverts Flow's static analysis +require.call(null, "DoesNotExist"); + +" +`; diff --git a/tests/require/jsfmt.spec.js b/tests/require/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/require/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/require/not_builtin_require.js b/tests/require/not_builtin_require.js new file mode 100644 index 000000000000..3671905918bb --- /dev/null +++ b/tests/require/not_builtin_require.js @@ -0,0 +1,4 @@ +// @flow + +function require() {} +require("not a module name"); diff --git a/tests/require/not_builtin_require2.js b/tests/require/not_builtin_require2.js new file mode 100644 index 000000000000..682bd304ecb4 --- /dev/null +++ b/tests/require/not_builtin_require2.js @@ -0,0 +1,4 @@ +// @flow + +type require = number; +var a: require = 42; diff --git a/tests/require/require.js b/tests/require/require.js new file mode 100644 index 000000000000..1628df536d39 --- /dev/null +++ b/tests/require/require.js @@ -0,0 +1,35 @@ +/* @flow */ + +function takesANumber(num: number): void {} +function takesAString(str: string): void {} + +// @providesModule +var A = require("A"); +takesANumber(A.numberValue); +takesAString(A.numberValue); + +// File path +var B = require("./B"); +takesANumber(B.numberValue); +takesAString(B.numberValue); + +// C.js exists, but not as a providesModule +require("C"); + +// @providesModule D exists, but not as a filename +require("./D"); + +// E exports an object with a numVal property +var E = require('./E'); +var e_1: number = E.numberValue; +E.stringValue; // Error: The E exports obj has no 'stringValue' property + +// We require that the param passed to require() be a string literal to support +// guaranteed static extraction +var a = './E'; +require(a); // Error: Param must be string literal +require(`./E`); // template literals are ok... +require(`${'./E'}`); // error: but only if they have no expressions + +// require.call is allowed but circumverts Flow's static analysis +require.call(null, "DoesNotExist"); diff --git a/tests/requireLazy/A.js b/tests/requireLazy/A.js new file mode 100644 index 000000000000..37d71689d456 --- /dev/null +++ b/tests/requireLazy/A.js @@ -0,0 +1,9 @@ +/** + * @providesModule A + * @flow + */ + +module.exports = { + numberValueA: 1, + stringValueA: "someString" +}; diff --git a/tests/requireLazy/B.js b/tests/requireLazy/B.js new file mode 100644 index 000000000000..d125081a8a9b --- /dev/null +++ b/tests/requireLazy/B.js @@ -0,0 +1,9 @@ +/** + * @providesModule B + * @flow + */ + +module.exports = { + numberValueB: 1, + stringValueB: "someString" +}; diff --git a/tests/requireLazy/__snapshots__/jsfmt.spec.js.snap b/tests/requireLazy/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..c3f58bdfdcc6 --- /dev/null +++ b/tests/requireLazy/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,93 @@ +exports[`test A.js 1`] = ` +"/** + * @providesModule A + * @flow + */ + +module.exports = { + numberValueA: 1, + stringValueA: "someString" +}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule A + * @flow + */ +module.exports = { numberValueA: 1, stringValueA: "someString" }; + +" +`; + +exports[`test B.js 1`] = ` +"/** + * @providesModule B + * @flow + */ + +module.exports = { + numberValueB: 1, + stringValueB: "someString" +}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule B + * @flow + */ +module.exports = { numberValueB: 1, stringValueB: "someString" }; + +" +`; + +exports[`test requireLazy.js 1`] = ` +"/** + * @flow + */ + +requireLazy(['A', 'B'], function(A, B) { + var num1: number = A.numberValueA; + var str1: string = A.stringValueA; + var num2: number = A.stringValueA; // Error: string ~> number + var str2: string = A.numberValueA; // Error: number ~> string + + var num3: number = B.numberValueB; + var str3: string = B.stringValueB; + var num4: number = B.stringValueB; // Error: string ~> number + var str4: string = B.numberValueB; // Error: number ~> string +}); + +var notA: Object = A; +var notB: Object = B; + +requireLazy(); // Error: No args +requireLazy([nope], function() {}); // Error: Non-stringliteral args +requireLazy(['A']); // Error: No calback expression +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +requireLazy( + [ "A", "B" ], + function(A, B) { + var num1: number = A.numberValueA; + var str1: string = A.stringValueA; + var num2: number = A.stringValueA;// Error: string ~> number + var str2: string = A.numberValueA;// Error: number ~> string + var num3: number = B.numberValueB; + var str3: string = B.stringValueB; + var num4: number = B.stringValueB;// Error: string ~> number + var str4: string = B.numberValueB;// Error: number ~> string + } +); +var notA: Object = A; +var notB: Object = B; +requireLazy();// Error: No args +requireLazy( + [ nope ], + function() { + + } +);// Error: Non-stringliteral args +requireLazy([ "A" ]);// Error: No calback expression + +" +`; diff --git a/tests/requireLazy/jsfmt.spec.js b/tests/requireLazy/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/requireLazy/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/requireLazy/requireLazy.js b/tests/requireLazy/requireLazy.js new file mode 100644 index 000000000000..4bc44c3a87df --- /dev/null +++ b/tests/requireLazy/requireLazy.js @@ -0,0 +1,22 @@ +/** + * @flow + */ + +requireLazy(['A', 'B'], function(A, B) { + var num1: number = A.numberValueA; + var str1: string = A.stringValueA; + var num2: number = A.stringValueA; // Error: string ~> number + var str2: string = A.numberValueA; // Error: number ~> string + + var num3: number = B.numberValueB; + var str3: string = B.stringValueB; + var num4: number = B.stringValueB; // Error: string ~> number + var str4: string = B.numberValueB; // Error: number ~> string +}); + +var notA: Object = A; +var notB: Object = B; + +requireLazy(); // Error: No args +requireLazy([nope], function() {}); // Error: Non-stringliteral args +requireLazy(['A']); // Error: No calback expression diff --git a/tests/return/__snapshots__/jsfmt.spec.js.snap b/tests/return/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..a71e2ce54cd6 --- /dev/null +++ b/tests/return/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,104 @@ +exports[`test function_return.js 1`] = ` +"class C { + foo() { } + bar() { return; } + fn(x:number) { return x; } +} + +function f(x): number { + if (x > 1) { + return 42; + } +} + +function g(x): ?number { + if (x > 1) { + return 42; + } +} + +function h(x): number { + if (x > 1) { + return 42; + } + return; +} + +function i(x): ?number { + if (x > 1) { + return 42; + } + return; +} + +module.exports = C; + +//function fn(x:number) { return x; } +//module.exports = fn; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class C { + foo() { + + } + bar() { + return; + } + fn(x: number) { + return x; + } +} +function f(x): number { + if (x > 1) { + return 42; + } +} +function g(x): ?number { + if (x > 1) { + return 42; + } +} +function h(x): number { + if (x > 1) { + return 42; + } + return; +} +function i(x): ?number { + if (x > 1) { + return 42; + } + return; +} +module.exports = C;//function fn(x:number) { return x; }//module.exports = fn; + +" +`; + +exports[`test void.js 1`] = ` +"/* This is a regression test. At one point we incorrectly inferred the return + type of functions that have an explicit \`undefined\` to be only \`undefined\` -- + ignoring other possible exits. */ +function f(b) { + if (b) { + return undefined; + } else { + return "nope"; + } +} + +(f(true): void); // error: string ~> void +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* This is a regression test. At one point we incorrectly inferred the return + type of functions that have an explicit \`undefined\` to be only \`undefined\` -- + ignoring other possible exits. */ +function f(b) { + if (b) { + return undefined; + } else { + return "nope"; + } +} +(f(true): void);// error: string ~> void + +" +`; diff --git a/tests/return/function_return.js b/tests/return/function_return.js new file mode 100644 index 000000000000..3667ad5d982e --- /dev/null +++ b/tests/return/function_return.js @@ -0,0 +1,36 @@ +class C { + foo() { } + bar() { return; } + fn(x:number) { return x; } +} + +function f(x): number { + if (x > 1) { + return 42; + } +} + +function g(x): ?number { + if (x > 1) { + return 42; + } +} + +function h(x): number { + if (x > 1) { + return 42; + } + return; +} + +function i(x): ?number { + if (x > 1) { + return 42; + } + return; +} + +module.exports = C; + +//function fn(x:number) { return x; } +//module.exports = fn; diff --git a/tests/return/jsfmt.spec.js b/tests/return/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/return/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/return/void.js b/tests/return/void.js new file mode 100644 index 000000000000..6bfe74a3f6ce --- /dev/null +++ b/tests/return/void.js @@ -0,0 +1,12 @@ +/* This is a regression test. At one point we incorrectly inferred the return + type of functions that have an explicit `undefined` to be only `undefined` -- + ignoring other possible exits. */ +function f(b) { + if (b) { + return undefined; + } else { + return "nope"; + } +} + +(f(true): void); // error: string ~> void diff --git a/tests/return_new/__snapshots__/jsfmt.spec.js.snap b/tests/return_new/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..f4fda93a6822 --- /dev/null +++ b/tests/return_new/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,65 @@ +exports[`test test.js 1`] = ` +"function Foo() { return {}; } +var foo: number = new Foo(); // error (returns object literal above) + +function Bar() { return 0; } +var bar: number = new Bar(); // error (returns new object) + +function Qux() { } +var qux: number = new Qux(); // error (returns new object) + +class A { } +function B() { return new A(); } +var a: A = new B(); // OK (returns new A) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +function Foo() { + return {}; +} +var foo: number = new Foo();// error (returns object literal above) +function Bar() { + return 0; +} +var bar: number = new Bar();// error (returns new object) +function Qux() { + +} +var qux: number = new Qux();// error (returns new object) +class A {} +function B() { + return new A(); +} +var a: A = new B();// OK (returns new A) + +" +`; + +exports[`test test2.js 1`] = ` +"declare class D { + constructor(): { x: number }; // OK + y: any; +} + +var d = new D(); +d.x = \"\"; // error, string ~/~ number (but property x is found) + +(new D: D); // error, new D is an object, D not in proto chain + +module.exports = D; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/return_new/jsfmt.spec.js b/tests/return_new/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/return_new/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/return_new/test.js b/tests/return_new/test.js new file mode 100644 index 000000000000..ba0f36f87353 --- /dev/null +++ b/tests/return_new/test.js @@ -0,0 +1,12 @@ +function Foo() { return {}; } +var foo: number = new Foo(); // error (returns object literal above) + +function Bar() { return 0; } +var bar: number = new Bar(); // error (returns new object) + +function Qux() { } +var qux: number = new Qux(); // error (returns new object) + +class A { } +function B() { return new A(); } +var a: A = new B(); // OK (returns new A) diff --git a/tests/return_new/test2.js b/tests/return_new/test2.js new file mode 100644 index 000000000000..8e961a2addd5 --- /dev/null +++ b/tests/return_new/test2.js @@ -0,0 +1,11 @@ +declare class D { + constructor(): { x: number }; // OK + y: any; +} + +var d = new D(); +d.x = ""; // error, string ~/~ number (but property x is found) + +(new D: D); // error, new D is an object, D not in proto chain + +module.exports = D; diff --git a/tests/seal/__snapshots__/jsfmt.spec.js.snap b/tests/seal/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..8a555f736668 --- /dev/null +++ b/tests/seal/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,33 @@ +exports[`test imp.js 1`] = ` +"/* @flow */ + +var imp = require('./obj_annot'); +imp({ name: "imp" }); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var imp = require("./obj_annot"); +imp({ name: "imp" }); + +" +`; + +exports[`test obj_annot.js 1`] = ` +"/* @flow */ + +function foo(param: { name: string; }): number { + return param.id; +} + +foo({ name: "test" }); + +module.exports = foo; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +function foo(param: { name: string }): number { + return param.id; +} +foo({ name: "test" }); +module.exports = foo; + +" +`; diff --git a/tests/seal/imp.js b/tests/seal/imp.js new file mode 100644 index 000000000000..caae6a1bc613 --- /dev/null +++ b/tests/seal/imp.js @@ -0,0 +1,4 @@ +/* @flow */ + +var imp = require('./obj_annot'); +imp({ name: "imp" }); diff --git a/tests/seal/jsfmt.spec.js b/tests/seal/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/seal/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/seal/obj_annot.js b/tests/seal/obj_annot.js new file mode 100644 index 000000000000..7df3e9ee6615 --- /dev/null +++ b/tests/seal/obj_annot.js @@ -0,0 +1,9 @@ +/* @flow */ + +function foo(param: { name: string; }): number { + return param.id; +} + +foo({ name: "test" }); + +module.exports = foo; diff --git a/tests/sealed/__snapshots__/jsfmt.spec.js.snap b/tests/sealed/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..9583e539e32b --- /dev/null +++ b/tests/sealed/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,88 @@ +exports[`test function.js 1`] = ` +"/* @flow */ + +function Bar(x: number) { + this.x = x; +} +Bar.prototype.getX = function() { return this.x; } +Bar.prototype.getY = function(): string { return this.y; } + +module.exports = Bar; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +function Bar(x: number) { + this.x = x; +} +Bar.prototype.getX = function() { + return this.x; +}; +Bar.prototype.getY = function(): string { + return this.y; +}; +module.exports = Bar; + +" +`; + +exports[`test proto.js 1`] = ` +"function Foo() { } +var o = new Foo(); +var x:number = o.x; + +Foo.prototype.m = function() { return this.x; } + +var y:number = o.m(); +o.x = "..."; + +Foo.prototype = { m: function() { return false; } } + +var export_o: { x:any; } = o; // awkward type cast + +module.exports = export_o; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +function Foo() { + +} +var o = new Foo(); +var x: number = o.x; +Foo.prototype.m = function() { + return this.x; +}; +var y: number = o.m(); +o.x = "..."; +Foo.prototype = { + m: function() { + return false; + } +}; +var export_o: { x: any } = o;// awkward type cast +module.exports = export_o; + +" +`; + +exports[`test sealed.js 1`] = ` +"var o = require('./proto'); + +o.z = 0; +var x:string = o.x; + +var Bar = require('./function'); +var a = new Bar(234); +a.x = 123; +a.y = 'abc'; // error, needs to be declared in Bar's constructor +(a.getX(): number); +(a.getY(): string); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var o = require("./proto"); +o.z = 0; +var x: string = o.x; +var Bar = require("./function"); +var a = new Bar(234); +a.x = 123; +a.y = "abc";// error, needs to be declared in Bar's constructor +(a.getX(): number); +(a.getY(): string); + +" +`; diff --git a/tests/sealed/function.js b/tests/sealed/function.js new file mode 100644 index 000000000000..ebfacc5c5e0c --- /dev/null +++ b/tests/sealed/function.js @@ -0,0 +1,9 @@ +/* @flow */ + +function Bar(x: number) { + this.x = x; +} +Bar.prototype.getX = function() { return this.x; } +Bar.prototype.getY = function(): string { return this.y; } + +module.exports = Bar; diff --git a/tests/sealed/jsfmt.spec.js b/tests/sealed/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/sealed/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/sealed/proto.js b/tests/sealed/proto.js new file mode 100644 index 000000000000..c14fa9e28891 --- /dev/null +++ b/tests/sealed/proto.js @@ -0,0 +1,14 @@ +function Foo() { } +var o = new Foo(); +var x:number = o.x; + +Foo.prototype.m = function() { return this.x; } + +var y:number = o.m(); +o.x = "..."; + +Foo.prototype = { m: function() { return false; } } + +var export_o: { x:any; } = o; // awkward type cast + +module.exports = export_o; diff --git a/tests/sealed/sealed.js b/tests/sealed/sealed.js new file mode 100644 index 000000000000..82c955333c94 --- /dev/null +++ b/tests/sealed/sealed.js @@ -0,0 +1,11 @@ +var o = require('./proto'); + +o.z = 0; +var x:string = o.x; + +var Bar = require('./function'); +var a = new Bar(234); +a.x = 123; +a.y = 'abc'; // error, needs to be declared in Bar's constructor +(a.getX(): number); +(a.getY(): string); diff --git a/tests/sealed_objects/__snapshots__/jsfmt.spec.js.snap b/tests/sealed_objects/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..bba2a19de525 --- /dev/null +++ b/tests/sealed_objects/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,40 @@ +exports[`test test.js 1`] = ` +"var o1 = { x: 0 }; +var s1: string = o1.y; // error + +var o2: { x: number; y?: string; } = { x: 0 }; +var s2: string = o2.y || ""; // ok + +var o3: { x: number; y?: string; } = ({ x: 0, y: 0 }: { x: number; }); +var s3: string = o3.y || ""; // error + +var o4: { x: number; y?: string; } = ({ x: 0 }: { x: number; [_:any]:any}); +var s4: string = o4.y || ""; // ok + +var o5 = { x: 0, ...{} }; +var s5: string = o5.y; // ok (spreads make object types extensible) + +var o6: { x: number; [_:any]:any } = { x: 0 }; +var s6: string = o6.y; // ok (indexers make object types extensible) + +var o7: { x: number; y?: string; } = ({ x: 0, y: 0 }: { x: number; [_:any]:number}); // error +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var o1 = { x: 0 }; +var s1: string = o1.y;// error +var o2: { x: number, y?: string } = { x: 0 }; +var s2: string = o2.y || "";// ok +var o3: { x: number, y?: string } = ({ x: 0, y: 0 }: { x: number }); +var s3: string = o3.y || "";// error +var o4: { x: number, y?: string } = ({ x: 0 }: { [_: any]: any, x: number }); +var s4: string = o4.y || "";// ok +var o5 = { x: 0, ...{} }; +var s5: string = o5.y;// ok (spreads make object types extensible) +var o6: { [_: any]: any, x: number } = { x: 0 }; +var s6: string = o6.y;// ok (indexers make object types extensible) +var o7: { x: number, y?: string } = ({ x: 0, y: 0 }: { + [_: any]: number, + x: number +});// error + +" +`; diff --git a/tests/sealed_objects/jsfmt.spec.js b/tests/sealed_objects/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/sealed_objects/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/sealed_objects/test.js b/tests/sealed_objects/test.js new file mode 100644 index 000000000000..746ef5d81951 --- /dev/null +++ b/tests/sealed_objects/test.js @@ -0,0 +1,19 @@ +var o1 = { x: 0 }; +var s1: string = o1.y; // error + +var o2: { x: number; y?: string; } = { x: 0 }; +var s2: string = o2.y || ""; // ok + +var o3: { x: number; y?: string; } = ({ x: 0, y: 0 }: { x: number; }); +var s3: string = o3.y || ""; // error + +var o4: { x: number; y?: string; } = ({ x: 0 }: { x: number; [_:any]:any}); +var s4: string = o4.y || ""; // ok + +var o5 = { x: 0, ...{} }; +var s5: string = o5.y; // ok (spreads make object types extensible) + +var o6: { x: number; [_:any]:any } = { x: 0 }; +var s6: string = o6.y; // ok (indexers make object types extensible) + +var o7: { x: number; y?: string; } = ({ x: 0, y: 0 }: { x: number; [_:any]:number}); // error diff --git a/tests/shape/__snapshots__/jsfmt.spec.js.snap b/tests/shape/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..6797d7f4a419 --- /dev/null +++ b/tests/shape/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,26 @@ +exports[`test test.js 1`] = ` +"type Foo = { + field: number, +} + +var x: {field?: number} = {}; +var y: $Shape = x; +(y.field: number) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/shape/jsfmt.spec.js b/tests/shape/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/shape/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/shape/test.js b/tests/shape/test.js new file mode 100644 index 000000000000..c71770f50278 --- /dev/null +++ b/tests/shape/test.js @@ -0,0 +1,7 @@ +type Foo = { + field: number, +} + +var x: {field?: number} = {}; +var y: $Shape = x; +(y.field: number) diff --git a/tests/simple_arrays/__snapshots__/jsfmt.spec.js.snap b/tests/simple_arrays/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..53a54ba638f7 --- /dev/null +++ b/tests/simple_arrays/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,77 @@ +exports[`test array.js 1`] = ` +"/* @flow */ +var a = []; +for (var i = 0; i < 10; ++i) { + if (i % 2 == 0) { a[i] = 0; } + else { a[i] = ''; }; +} + +// \`i\` never gets a lower bound, so the array access is stalled until the +// function is called. +function foo(i): string { return a[i]; } + +// here, because we call \`bar\`, we the array access constraint is discharged and +// we realize a type error. +function bar(i): string { return a[i]; } +bar(0); + +// annotations suffice to unblock the access constraint as well, so only +// uncalled internal functions will not find a type error, which is acceptable +// behavior as such functions are dead code. +function baz(i:number): string { return a[i]; } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var a = [ ]; +for (var i = 0; i < 10; ++i) { + if (i % 2 == 0) { + a[i] = 0; + } else { + a[i] = ""; + } +} +// \`i\` never gets a lower bound, so the array access is stalled until the +// function is called. +function foo(i): string { + return a[i]; +} +// here, because we call \`bar\`, we the array access constraint is discharged and +// we realize a type error. +function bar(i): string { + return a[i]; +} +bar(0); +// annotations suffice to unblock the access constraint as well, so only +// uncalled internal functions will not find a type error, which is acceptable +// behavior as such functions are dead code. +function baz(i: number): string { + return a[i]; +} + +" +`; + +exports[`test array2.js 1`] = ` +"/* @flow */ +var a = []; +for (var i = 0; i < 10; ++i) { + if (i % 2 == 0) { a[i] = 0; } + else { a[i] = ''; }; +} + +function foo(i: number): string { return a[i]; } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var a = [ ]; +for (var i = 0; i < 10; ++i) { + if (i % 2 == 0) { + a[i] = 0; + } else { + a[i] = ""; + } +} +function foo(i: number): string { + return a[i]; +} + +" +`; diff --git a/tests/simple_arrays/array.js b/tests/simple_arrays/array.js new file mode 100644 index 000000000000..c44a1442b19c --- /dev/null +++ b/tests/simple_arrays/array.js @@ -0,0 +1,20 @@ +/* @flow */ +var a = []; +for (var i = 0; i < 10; ++i) { + if (i % 2 == 0) { a[i] = 0; } + else { a[i] = ''; }; +} + +// `i` never gets a lower bound, so the array access is stalled until the +// function is called. +function foo(i): string { return a[i]; } + +// here, because we call `bar`, we the array access constraint is discharged and +// we realize a type error. +function bar(i): string { return a[i]; } +bar(0); + +// annotations suffice to unblock the access constraint as well, so only +// uncalled internal functions will not find a type error, which is acceptable +// behavior as such functions are dead code. +function baz(i:number): string { return a[i]; } diff --git a/tests/simple_arrays/array2.js b/tests/simple_arrays/array2.js new file mode 100644 index 000000000000..b42ec500eac3 --- /dev/null +++ b/tests/simple_arrays/array2.js @@ -0,0 +1,8 @@ +/* @flow */ +var a = []; +for (var i = 0; i < 10; ++i) { + if (i % 2 == 0) { a[i] = 0; } + else { a[i] = ''; }; +} + +function foo(i: number): string { return a[i]; } diff --git a/tests/simple_arrays/jsfmt.spec.js b/tests/simple_arrays/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/simple_arrays/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/singleton/__snapshots__/jsfmt.spec.js.snap b/tests/singleton/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..882d3227ca6f --- /dev/null +++ b/tests/singleton/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,117 @@ +exports[`test boolean.js 1`] = ` +"/* @flow */ + +function veryOptimistic(isThisAwesome: true): boolean { + return isThisAwesome; +} + +var x : boolean = veryOptimistic(true); +var y : boolean = veryOptimistic(false); // error + +function veryPessimistic(isThisAwesome: true): boolean { + return !isThisAwesome; // test bool conversion +} + +var x : boolean = veryPessimistic(true); +var y : boolean = veryPessimistic(false); // error + +type MyOwnBooleanLOL = true | false + +function bar(x: MyOwnBooleanLOL): false { + if (x) { + return x; + } else { + return !x; + } +} + +bar(true); +bar(false); +bar(1); // error + +function alwaysFalsy(x: boolean): false { + if (x) { + return !x; + } else { + return x; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test number.js 1`] = ` +"/* @flow */ + +function highlander(howMany: 1): number { + return howMany; // there can be only one! +} + +highlander(1); +highlander(2); // error + + +type Foo = 1 | 2 + +function bar(num: Foo): number { + return num + 1; +} + +bar(1); +bar(2); +bar(3); // error + +type ComparatorResult = -1 | 0 | 1 +function sort(fn: (x: any, y: any) => ComparatorResult) {} +sort((x, y) => -1); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/ast-types/lib/types.js:60 + throw new Error(str + \" does not match type \" + this); + ^ + +Error: {type: NumericLiteralTypeAnnotation, start: 42, end: 43, loc: [object Object], value: 1, extra: [object Object]} does not match type Printable + at Type.Tp.assert (/node_modules/ast-types/lib/types.js:60:19) + at genericPrintNoParens (/src/printer.js:221:24) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) +" +`; + +exports[`test string.js 1`] = ` +"/* @flow */ + +type NoSpaces = \"foobar\" +(\"foobar\": NoSpaces); + +type HasSpaces = \"foo bar\" +(\"foo bar\": HasSpaces); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +type NoSpaces = \"foobar\"; +(\"foobar\": NoSpaces); +type HasSpaces = \"foo bar\"; +(\"foo bar\": HasSpaces); + +" +`; diff --git a/tests/singleton/boolean.js b/tests/singleton/boolean.js new file mode 100644 index 000000000000..dede9679e160 --- /dev/null +++ b/tests/singleton/boolean.js @@ -0,0 +1,37 @@ +/* @flow */ + +function veryOptimistic(isThisAwesome: true): boolean { + return isThisAwesome; +} + +var x : boolean = veryOptimistic(true); +var y : boolean = veryOptimistic(false); // error + +function veryPessimistic(isThisAwesome: true): boolean { + return !isThisAwesome; // test bool conversion +} + +var x : boolean = veryPessimistic(true); +var y : boolean = veryPessimistic(false); // error + +type MyOwnBooleanLOL = true | false + +function bar(x: MyOwnBooleanLOL): false { + if (x) { + return x; + } else { + return !x; + } +} + +bar(true); +bar(false); +bar(1); // error + +function alwaysFalsy(x: boolean): false { + if (x) { + return !x; + } else { + return x; + } +} diff --git a/tests/singleton/jsfmt.spec.js b/tests/singleton/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/singleton/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/singleton/number.js b/tests/singleton/number.js new file mode 100644 index 000000000000..160e9223d340 --- /dev/null +++ b/tests/singleton/number.js @@ -0,0 +1,23 @@ +/* @flow */ + +function highlander(howMany: 1): number { + return howMany; // there can be only one! +} + +highlander(1); +highlander(2); // error + + +type Foo = 1 | 2 + +function bar(num: Foo): number { + return num + 1; +} + +bar(1); +bar(2); +bar(3); // error + +type ComparatorResult = -1 | 0 | 1 +function sort(fn: (x: any, y: any) => ComparatorResult) {} +sort((x, y) => -1); diff --git a/tests/singleton/string.js b/tests/singleton/string.js new file mode 100644 index 000000000000..aac4a5c70372 --- /dev/null +++ b/tests/singleton/string.js @@ -0,0 +1,7 @@ +/* @flow */ + +type NoSpaces = "foobar" +("foobar": NoSpaces); + +type HasSpaces = "foo bar" +("foo bar": HasSpaces); diff --git a/tests/spread/__snapshots__/jsfmt.spec.js.snap b/tests/spread/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..6267c1718017 --- /dev/null +++ b/tests/spread/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,225 @@ +exports[`test test.js 1`] = ` +"function parseTimestamp(timestamp: string): number { + return 0; +} + +function parseCounter(line: string): number { + return 0; +} + +function parseGroup(lines: Array): { + counter: number; + begin: number; + end: number; + text: string; +} { + var counter = parseCounter(lines[0]); + var timeframe = parseTimeframe(lines[1]); + return { + counter, + ...timeframe, + text: lines[2] + }; +} + +function parseTimeframe(line: string): { begin: number; end: number } { + var timestamps = line.split(\'-->\'); + return { + begin: parseTimestamp(timestamps[0].trim()), + end: parseTimestamp(timestamps[1].trim()) + }; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test2.js 1`] = ` +"/** + * @flow + */ + +function foo(o) { + bar({...o}); +} +function bar(_: {foo:number}) { } +foo({foo: 42}); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +function foo(o) { + bar({ ...o }); +} +function bar(_: { foo: number }) { + +} +foo({ foo: 42 }); + +" +`; + +exports[`test test3.js 1`] = ` +"var p = { y: \"\" }; +var q = { z: \"\" }; +var o = { + x: 5, + ...p, + ...q, +}; +var y: number = o.y; +var z: number = o.z; + +// test conflicting keys (they get unioned) +var r = { y: 123 }; +var s = { + ...p, + ...r, +}; +var t: boolean = s.y; // error, string or number +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var p = { y: \"\" }; +var q = { z: \"\" }; +var o = { x: 5, ...p, ...q }; +var y: number = o.y; +var z: number = o.z; +// test conflicting keys (they get unioned) +var r = { y: 123 }; +var s = { ...p, ...r }; +var t: boolean = s.y;// error, string or number + +" +`; + +exports[`test test4.js 1`] = ` +"/* @flow */ +function test(...nums: Array) {} + +test(0, ...[1, 2, 3]); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +function test(...nums) { + +} +test(0, ...[ 1, 2, 3 ]); + +" +`; + +exports[`test test5.js 1`] = ` +"/* @flow */ + +declare function map( + obj: {[key: string]: Tv}, + iterator:((obj: Tv) => TNext), +): Array; + +/** + * Tests overriding a property via a spread, where the value is a tvar. the + * type of the prop from the object that is being overridden (\`x.kind\` in the + * case below) should //not// feed back into the tvar (\`value\`), since the + * result is a new object. + */ +function test( + x: {kind: ?string}, + kinds: {[key: string]: string} +): Array<{kind: ?string}> { + return map(kinds, (value) => { + (value: string); // OK + return { + ...x, + kind: value, + }; + }); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1368:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test6.js 1`] = ` +"var o = { + foo: \'bar\' +}; +o = {...o}; +(o: {foo: string}); + +var p = { + foo: \'bar\' +}; +(p: {foo: string; abc: string}); // error, p doesn\'t have \`abc\` yet +p = {...p, abc: \'def\'}; +(p: {foo: string; abc: string}); + +var q = { + foo: \'bar\' +}; +for (var i = 0; i < 10; i++) { + q = {...q}; +} +(q: {foo: string}); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var o = { foo: \"bar\" }; +o = { ...o }; +(o: { foo: string }); +var p = { foo: \"bar\" }; +(p: { foo: string, abc: string });// error, p doesn\'t have \`abc\` yet +p = { ...p, abc: \"def\" }; +(p: { foo: string, abc: string }); +var q = { foo: \"bar\" }; +for (var i = 0; i < 10; i++) { + q = { ...q }; +} +(q: { foo: string }); + +" +`; + +exports[`test test7.js 1`] = ` +"// @flow + +let tests = [ + function(x: Object) { + ({...x}: Object); + ({...x}: void); // error, Object + }, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +let tests = [ + function(x: Object) { + ({ ...x }: Object); + ({ ...x }: void);// error, Object + } +]; + +" +`; diff --git a/tests/spread/jsfmt.spec.js b/tests/spread/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/spread/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/spread/test.js b/tests/spread/test.js new file mode 100644 index 000000000000..19c223947226 --- /dev/null +++ b/tests/spread/test.js @@ -0,0 +1,30 @@ +function parseTimestamp(timestamp: string): number { + return 0; +} + +function parseCounter(line: string): number { + return 0; +} + +function parseGroup(lines: Array): { + counter: number; + begin: number; + end: number; + text: string; +} { + var counter = parseCounter(lines[0]); + var timeframe = parseTimeframe(lines[1]); + return { + counter, + ...timeframe, + text: lines[2] + }; +} + +function parseTimeframe(line: string): { begin: number; end: number } { + var timestamps = line.split('-->'); + return { + begin: parseTimestamp(timestamps[0].trim()), + end: parseTimestamp(timestamps[1].trim()) + }; +} diff --git a/tests/spread/test2.js b/tests/spread/test2.js new file mode 100644 index 000000000000..d2efac4f66b4 --- /dev/null +++ b/tests/spread/test2.js @@ -0,0 +1,9 @@ +/** + * @flow + */ + +function foo(o) { + bar({...o}); +} +function bar(_: {foo:number}) { } +foo({foo: 42}); diff --git a/tests/spread/test3.js b/tests/spread/test3.js new file mode 100644 index 000000000000..85c1194be5b1 --- /dev/null +++ b/tests/spread/test3.js @@ -0,0 +1,17 @@ +var p = { y: "" }; +var q = { z: "" }; +var o = { + x: 5, + ...p, + ...q, +}; +var y: number = o.y; +var z: number = o.z; + +// test conflicting keys (they get unioned) +var r = { y: 123 }; +var s = { + ...p, + ...r, +}; +var t: boolean = s.y; // error, string or number diff --git a/tests/spread/test4.js b/tests/spread/test4.js new file mode 100644 index 000000000000..6b58cbdc7dac --- /dev/null +++ b/tests/spread/test4.js @@ -0,0 +1,4 @@ +/* @flow */ +function test(...nums: Array) {} + +test(0, ...[1, 2, 3]); diff --git a/tests/spread/test5.js b/tests/spread/test5.js new file mode 100644 index 000000000000..500a78f203c0 --- /dev/null +++ b/tests/spread/test5.js @@ -0,0 +1,25 @@ +/* @flow */ + +declare function map( + obj: {[key: string]: Tv}, + iterator:((obj: Tv) => TNext), +): Array; + +/** + * Tests overriding a property via a spread, where the value is a tvar. the + * type of the prop from the object that is being overridden (`x.kind` in the + * case below) should //not// feed back into the tvar (`value`), since the + * result is a new object. + */ +function test( + x: {kind: ?string}, + kinds: {[key: string]: string} +): Array<{kind: ?string}> { + return map(kinds, (value) => { + (value: string); // OK + return { + ...x, + kind: value, + }; + }); +} diff --git a/tests/spread/test6.js b/tests/spread/test6.js new file mode 100644 index 000000000000..54613a74d78c --- /dev/null +++ b/tests/spread/test6.js @@ -0,0 +1,20 @@ +var o = { + foo: 'bar' +}; +o = {...o}; +(o: {foo: string}); + +var p = { + foo: 'bar' +}; +(p: {foo: string; abc: string}); // error, p doesn't have `abc` yet +p = {...p, abc: 'def'}; +(p: {foo: string; abc: string}); + +var q = { + foo: 'bar' +}; +for (var i = 0; i < 10; i++) { + q = {...q}; +} +(q: {foo: string}); diff --git a/tests/spread/test7.js b/tests/spread/test7.js new file mode 100644 index 000000000000..a0c090fc7874 --- /dev/null +++ b/tests/spread/test7.js @@ -0,0 +1,8 @@ +// @flow + +let tests = [ + function(x: Object) { + ({...x}: Object); + ({...x}: void); // error, Object + }, +]; diff --git a/tests/static_overload/__snapshots__/jsfmt.spec.js.snap b/tests/static_overload/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..5f11981fdd11 --- /dev/null +++ b/tests/static_overload/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,7 @@ +exports[`test test.js 1`] = ` +"var x: number = StaticOverload.foo(0); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var x: number = StaticOverload.foo(0); + +" +`; diff --git a/tests/static_overload/jsfmt.spec.js b/tests/static_overload/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/static_overload/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/static_overload/lib/__snapshots__/jsfmt.spec.js.snap b/tests/static_overload/lib/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..3e5e45e624ec --- /dev/null +++ b/tests/static_overload/lib/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,23 @@ +exports[`test lib.js 1`] = ` +"declare class StaticOverload { + static foo(x: number): number; + static foo(x: string): string; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/static_overload/lib/jsfmt.spec.js b/tests/static_overload/lib/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/static_overload/lib/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/static_overload/lib/lib.js b/tests/static_overload/lib/lib.js new file mode 100644 index 000000000000..61179705b9d2 --- /dev/null +++ b/tests/static_overload/lib/lib.js @@ -0,0 +1,4 @@ +declare class StaticOverload { + static foo(x: number): number; + static foo(x: string): string; +} diff --git a/tests/static_overload/test.js b/tests/static_overload/test.js new file mode 100644 index 000000000000..f72428ff8ccd --- /dev/null +++ b/tests/static_overload/test.js @@ -0,0 +1 @@ +var x: number = StaticOverload.foo(0); diff --git a/tests/statics/__snapshots__/jsfmt.spec.js.snap b/tests/statics/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..342379db62a2 --- /dev/null +++ b/tests/statics/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,47 @@ +exports[`test class_statics.js 1`] = ` +"class C { + static f(x:number) { } + static x:string; +} + +C.g = function(x:string) { C.f(x); }; +C.g(0); + +var x:number = C.x; +C.x = 0;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class C { + f(x: number) { + + } + static x: string; +} +C.g = function(x: string) { + C.f(x); +}; +C.g(0); +var x: number = C.x; +C.x = 0; + +" +`; + +exports[`test funstatics.js 1`] = ` +"function C() { } +C.prototype.f = function() { return C.g(0); } +C.g = function(x) { return x; }; + +var x:string = new C().f(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +function C() { + +} +C.prototype.f = function() { + return C.g(0); +}; +C.g = function(x) { + return x; +}; +var x: string = new C().f(); + +" +`; diff --git a/tests/statics/class_statics.js b/tests/statics/class_statics.js new file mode 100644 index 000000000000..fc9a02a2b7b1 --- /dev/null +++ b/tests/statics/class_statics.js @@ -0,0 +1,10 @@ +class C { + static f(x:number) { } + static x:string; +} + +C.g = function(x:string) { C.f(x); }; +C.g(0); + +var x:number = C.x; +C.x = 0; \ No newline at end of file diff --git a/tests/statics/funstatics.js b/tests/statics/funstatics.js new file mode 100644 index 000000000000..0e50eca1213c --- /dev/null +++ b/tests/statics/funstatics.js @@ -0,0 +1,5 @@ +function C() { } +C.prototype.f = function() { return C.g(0); } +C.g = function(x) { return x; }; + +var x:string = new C().f(); diff --git a/tests/statics/jsfmt.spec.js b/tests/statics/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/statics/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/strict/__snapshots__/jsfmt.spec.js.snap b/tests/strict/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..82c4b9ed76a8 --- /dev/null +++ b/tests/strict/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,83 @@ +exports[`test annot.js 1`] = ` +"var A = require('./unknown_class'); + +class B extends A { + foo(x:A):A { return x; } + bar(x) { } + qux(x:X,y:Y):X { return x;} +} + +module.exports = B; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var A = require("./unknown_class"); +class B extends A { + foo(x: A): A { + return x; + } + bar(x) { + + } + qux(x: X, y: Y): X { + return x; + } +} +module.exports = B; + +" +`; + +exports[`test fun.js 1`] = ` +"/** + * @flow + */ + +// progressively annotate: + +//function f(x) { return x; } +function f(x:number) { return x; } +//function f(x:number):string { return x; } + +var x:string = f(0); + +module.exports = f; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +// progressively annotate: +//function f(x) { return x; } +function f(x: number) { + return x; +} +//function f(x:number):string { return x; } +var x: string = f(0); +module.exports = f; + +" +`; + +exports[`test obj.js 1`] = ` +"/** + * @flow + */ + +// progressively annotate: + +var o = { x: 0 } +//var o: {x: number;} = { x: 0 } + +var x:string = o.x; + +module.exports = o; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +// progressively annotate: +var o = { x: 0 }; +//var o: {x: number;} = { x: 0 } +var x: string = o.x; +module.exports = o; + +" +`; diff --git a/tests/strict/annot.js b/tests/strict/annot.js new file mode 100644 index 000000000000..69e385f2ca2c --- /dev/null +++ b/tests/strict/annot.js @@ -0,0 +1,9 @@ +var A = require('./unknown_class'); + +class B extends A { + foo(x:A):A { return x; } + bar(x) { } + qux(x:X,y:Y):X { return x;} +} + +module.exports = B; diff --git a/tests/strict/fun.js b/tests/strict/fun.js new file mode 100644 index 000000000000..b09992315b1e --- /dev/null +++ b/tests/strict/fun.js @@ -0,0 +1,13 @@ +/** + * @flow + */ + +// progressively annotate: + +//function f(x) { return x; } +function f(x:number) { return x; } +//function f(x:number):string { return x; } + +var x:string = f(0); + +module.exports = f; diff --git a/tests/strict/jsfmt.spec.js b/tests/strict/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/strict/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/strict/obj.js b/tests/strict/obj.js new file mode 100644 index 000000000000..0add7bed5bb1 --- /dev/null +++ b/tests/strict/obj.js @@ -0,0 +1,12 @@ +/** + * @flow + */ + +// progressively annotate: + +var o = { x: 0 } +//var o: {x: number;} = { x: 0 } + +var x:string = o.x; + +module.exports = o; diff --git a/tests/strict_requires/A.js b/tests/strict_requires/A.js new file mode 100644 index 000000000000..0df880dc9718 --- /dev/null +++ b/tests/strict_requires/A.js @@ -0,0 +1,2 @@ +/* @flow */ +module.exports = 0; diff --git a/tests/strict_requires/B.js b/tests/strict_requires/B.js new file mode 100644 index 000000000000..e954bc64a74b --- /dev/null +++ b/tests/strict_requires/B.js @@ -0,0 +1,2 @@ +/* @flow */ +module.exports = { foo: "" } diff --git a/tests/strict_requires/C.js b/tests/strict_requires/C.js new file mode 100644 index 000000000000..991ad5a6ca64 --- /dev/null +++ b/tests/strict_requires/C.js @@ -0,0 +1,6 @@ +/* @flow */ +var o = { + A: require('./A'), + ...require('./B'), +}; +module.exports = o; diff --git a/tests/strict_requires/D.js b/tests/strict_requires/D.js new file mode 100644 index 000000000000..53659ea52004 --- /dev/null +++ b/tests/strict_requires/D.js @@ -0,0 +1,5 @@ +/* @flow */ +var C = require('./C'); +var x: number = C.foo; +var y: string = C.A; +C.A = false; diff --git a/tests/strict_requires/__snapshots__/jsfmt.spec.js.snap b/tests/strict_requires/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..a9e678089e60 --- /dev/null +++ b/tests/strict_requires/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,50 @@ +exports[`test A.js 1`] = ` +"/* @flow */ +module.exports = 0; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +module.exports = 0; + +" +`; + +exports[`test B.js 1`] = ` +"/* @flow */ +module.exports = { foo: "" } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +module.exports = { foo: "" }; + +" +`; + +exports[`test C.js 1`] = ` +"/* @flow */ +var o = { + A: require('./A'), + ...require('./B'), +}; +module.exports = o; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var o = { A: require("./A"), ...require("./B") }; +module.exports = o; + +" +`; + +exports[`test D.js 1`] = ` +"/* @flow */ +var C = require('./C'); +var x: number = C.foo; +var y: string = C.A; +C.A = false; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var C = require("./C"); +var x: number = C.foo; +var y: string = C.A; +C.A = false; + +" +`; diff --git a/tests/strict_requires/jsfmt.spec.js b/tests/strict_requires/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/strict_requires/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/strings/__snapshots__/jsfmt.spec.js.snap b/tests/strings/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..6ec2f5384a0a --- /dev/null +++ b/tests/strings/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,42 @@ +exports[`test strings.js 1`] = ` +"\"abc\" +\'abc\' + +\'\\\'\' + +\'\"\' +\'\\\"\' +\'\\\\\"\' + +\"\'\" +\"\\\'\" +\"\\\\\'\" + +\"\'\\\"\" +\'\\\'\"\' + +\'\\\\\' +\"\\\\\" + +\'\\0\' +\'🐶\' +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\"abc\"; +\"abc\"; +\"\\\\\'\"; +\"\\\"\"; +\"\\\\\\\"\"; +\"\\\\\\\\\\\"\"; +\"\'\"; +\"\\\\\'\"; +\"\\\\\\\\\'\"; +\"\'\\\\\\\"\"; +\"\\\\\'\\\"\"; +\"\\\\\\\\\"; +\"\\\\\\\\\"; +\"\\\\0\"; +\"🐶\"; + + +" +`; diff --git a/tests/strings/jsfmt.spec.js b/tests/strings/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/strings/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/strings/strings.js b/tests/strings/strings.js new file mode 100644 index 000000000000..b296b144d398 --- /dev/null +++ b/tests/strings/strings.js @@ -0,0 +1,21 @@ +"abc" +'abc' + +'\'' + +'"' +'\"' +'\\"' + +"'" +"\'" +"\\'" + +"'\"" +'\'"' + +'\\' +"\\" + +'\0' +'🐶' diff --git a/tests/structural_subtyping/__snapshots__/jsfmt.spec.js.snap b/tests/structural_subtyping/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..e216003a5a18 --- /dev/null +++ b/tests/structural_subtyping/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,146 @@ +exports[`test builtin.js 1`] = ` +"/** + * @flow + */ + +interface IHasLength { + length: number; +} + +var lengthTest1: IHasLength = []; +var lengthTest2: IHasLength = \'hello\'; +var lengthTest3: IHasLength = 123; // number doesn\'t have length +var lengthTest4: IHasLength = true; // bool doesn\'t have length +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1391 + fromString(\", \").join(path.map(print, \"extends\")) + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1391:28) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:238:16 + at FastPath.call (/src/fast-path.js:113:16) +" +`; + +exports[`test class.js 1`] = ` +"/** + * @flow + */ + +class ClassWithXString { + x: string; +} + +interface IHasXString { + x: string; +} + +interface IHasXNumber { + x: number; +} + +interface IHasYString { + y: string; +} + +var testInstance1: IHasXString = new ClassWithXString(); +var testInstance2: IHasXNumber = new ClassWithXString(); // Error wrong type +var testInstance3: IHasYString = new ClassWithXString(); // Error missing prop +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1391 + fromString(\", \").join(path.map(print, \"extends\")) + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1391:28) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:238:16 + at FastPath.call (/src/fast-path.js:113:16) +" +`; + +exports[`test obj.js 1`] = ` +"/** + * @flow + */ + +interface IHasXString { + x: string; +} + +var propTest1: IHasXString = { x: \'hello\' }; +var propTest2: IHasXString = { x: 123 }; // Error string != number +var propTest3: IHasXString = {}; // Property not found +var propTest4: IHasXString = ({}: Object); + +function propTest5(y: {[key: string]: string}) { + (y: IHasXString); // OK +} + +function propTest6(y: {[key: string]: number}) { + (y: IHasXString); // error: string != number +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1391 + fromString(\", \").join(path.map(print, \"extends\")) + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1391:28) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:238:16 + at FastPath.call (/src/fast-path.js:113:16) +" +`; + +exports[`test optional.js 1`] = ` +"/* @flow */ + +interface HasOptional { + a: string, + b?: number, +}; + +var test1: HasOptional = { a: \"hello\" } + +var test2: HasOptional = {}; // Error: missing property a + +var test3: HasOptional = { a: \"hello\", b: true }; // Error: boolean ~> number +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1391 + fromString(\", \").join(path.map(print, \"extends\")) + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1391:28) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:238:16 + at FastPath.call (/src/fast-path.js:113:16) +" +`; diff --git a/tests/structural_subtyping/builtin.js b/tests/structural_subtyping/builtin.js new file mode 100644 index 000000000000..ebc0b2de9c27 --- /dev/null +++ b/tests/structural_subtyping/builtin.js @@ -0,0 +1,12 @@ +/** + * @flow + */ + +interface IHasLength { + length: number; +} + +var lengthTest1: IHasLength = []; +var lengthTest2: IHasLength = 'hello'; +var lengthTest3: IHasLength = 123; // number doesn't have length +var lengthTest4: IHasLength = true; // bool doesn't have length diff --git a/tests/structural_subtyping/class.js b/tests/structural_subtyping/class.js new file mode 100644 index 000000000000..9fa9d3039674 --- /dev/null +++ b/tests/structural_subtyping/class.js @@ -0,0 +1,23 @@ +/** + * @flow + */ + +class ClassWithXString { + x: string; +} + +interface IHasXString { + x: string; +} + +interface IHasXNumber { + x: number; +} + +interface IHasYString { + y: string; +} + +var testInstance1: IHasXString = new ClassWithXString(); +var testInstance2: IHasXNumber = new ClassWithXString(); // Error wrong type +var testInstance3: IHasYString = new ClassWithXString(); // Error missing prop diff --git a/tests/structural_subtyping/jsfmt.spec.js b/tests/structural_subtyping/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/structural_subtyping/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/structural_subtyping/obj.js b/tests/structural_subtyping/obj.js new file mode 100644 index 000000000000..6a9189ac0f62 --- /dev/null +++ b/tests/structural_subtyping/obj.js @@ -0,0 +1,20 @@ +/** + * @flow + */ + +interface IHasXString { + x: string; +} + +var propTest1: IHasXString = { x: 'hello' }; +var propTest2: IHasXString = { x: 123 }; // Error string != number +var propTest3: IHasXString = {}; // Property not found +var propTest4: IHasXString = ({}: Object); + +function propTest5(y: {[key: string]: string}) { + (y: IHasXString); // OK +} + +function propTest6(y: {[key: string]: number}) { + (y: IHasXString); // error: string != number +} diff --git a/tests/structural_subtyping/optional.js b/tests/structural_subtyping/optional.js new file mode 100644 index 000000000000..102a511ef7a2 --- /dev/null +++ b/tests/structural_subtyping/optional.js @@ -0,0 +1,12 @@ +/* @flow */ + +interface HasOptional { + a: string, + b?: number, +}; + +var test1: HasOptional = { a: "hello" } + +var test2: HasOptional = {}; // Error: missing property a + +var test3: HasOptional = { a: "hello", b: true }; // Error: boolean ~> number diff --git a/tests/suggest/__snapshots__/jsfmt.spec.js.snap b/tests/suggest/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..9b7010ce1817 --- /dev/null +++ b/tests/suggest/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,39 @@ +exports[`test lib.js 1`] = ` +"/* @flow */ + +function bar(w: number): number { return w; } + +module.exports = bar; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +function bar(w: number): number { + return w; +} +module.exports = bar; + +" +`; + +exports[`test suggest.js 1`] = ` +"/* @flow */ + +var bar = require('./lib'); + +function foo(z: number) { return bar(z); } + +var array = ["foo", "bar"]; +array; + +module.exports = {foo:foo}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +var bar = require("./lib"); +function foo(z: number) { + return bar(z); +} +var array = [ "foo", "bar" ]; +array; +module.exports = { foo: foo }; + +" +`; diff --git a/tests/suggest/jsfmt.spec.js b/tests/suggest/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/suggest/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/suggest/lib.js b/tests/suggest/lib.js new file mode 100644 index 000000000000..f89c80bf62c5 --- /dev/null +++ b/tests/suggest/lib.js @@ -0,0 +1,5 @@ +/* @flow */ + +function bar(w: number): number { return w; } + +module.exports = bar; diff --git a/tests/suggest/suggest.js b/tests/suggest/suggest.js new file mode 100644 index 000000000000..e2b005194721 --- /dev/null +++ b/tests/suggest/suggest.js @@ -0,0 +1,10 @@ +/* @flow */ + +var bar = require('./lib'); + +function foo(z: number) { return bar(z); } + +var array = ["foo", "bar"]; +array; + +module.exports = {foo:foo}; diff --git a/tests/super/__snapshots__/jsfmt.spec.js.snap b/tests/super/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..3173dd9a56b6 --- /dev/null +++ b/tests/super/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,263 @@ +exports[`test constructor.js 1`] = ` +"class A { x: number; } + +class B { + x: number; + constructor() { + this.x; // OK + } +} + +class C extends A { } + +class D extends A { + y: number; + constructor() { + this.y; // error (no super call) + this.x; // error (no super call) + } +} + +class E extends A { + y: number; + constructor() { + super(); + this.y; // OK + this.x; // OK + } +} + +function leak(f) { + f.y; // error + f.x; // error +} +class F extends A { + y: number; + constructor() { + leak(this); // error (no super call yet) + super(); + this.y; + this.x; + } +} + +class G extends A { + constructor(b) { + super.x; // error (no super call) + } +} + +class H extends A { + y: number; + constructor() { + if (Math.random() < 0.5) + super(); + else + super(); + this.y; // OK + this.x; // OK + } +} + +class I_ { + constructor(leaked_this) { + leaked_this.foo() + } + foo() { } +} +class I extends I_ { + constructor() { + super(this); // error (no super call yet) + } +} + +class J__ { } +class J_ extends J__ { + constructor(closure_leaking_this) { + closure_leaking_this(); + super(); + } + foo() { } +} +class J extends J_ { + constructor() { + super(() => this.foo()); // error (no super call yet) + // The reason for this error is that super constructor could call the + // closure and therefore access this before calling its own super + // constructor (as shown above). The only safe thing to do in the super + // constructor is to save the closure so that it can be called later, after + // initialization is done (as shown below). + } +} + +class K_ { + closure_leaking_this: () => void; + constructor(closure_leaking_this) { + this.closure_leaking_this = closure_leaking_this; + } + foo() { } +} +class K extends K_ { + constructor() { + super(() => { if (_this) _this.foo() }); // OK + var _this = this; + this.closure_leaking_this(); + } +} + +// even though super() calls the parent\'s constructor(), it does not do the same +// conversion on non-objects. so \`new L_()\` returns an instance of \`L_\` because +// the constructor returns false (a non-object), but \`super()\` returns false, +// which then similarly causes \`new L()\` to return an instance of \`L\`. +class L_ { + constructor() { + return false; + } +} +class L extends L_ { + constructor() { + let x: boolean = super(); + return x; + } +} +(new L_(): L_); +(new L(): L); + +// similarly, the converse is true: if the parent\'s constructor returns an +// object, then the child does too. +class M_ { + constructor() { + return {foo: \'foo\'}; + } +} +class M extends M_ { + constructor() { + return super(); + } +} +(new M_(): {foo: string}); +(new M(): {foo: string}); + +// however! super() calls the parent constructor with the subclass as its \`this\` +// value (essentially \`super.constructor.call(this)\`). +class N_ { + constructor(): this { + let x = this; + return x; + } +} +class N extends N_ { + constructor() { + return super(); + } +} +(new N_(): N_); +(new N(): N); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test import.js 1`] = ` +"class D { + foo(): number { return 0; } +} +module.exports = D; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class D { + foo(): number { + return 0; + } +} +module.exports = D; + +" +`; + +exports[`test super.js 1`] = ` +" +class A { + constructor(x:number) { } + static staticMethod(x:string): string { return x; } + f(x:string) { } +} + +class B extends A { + constructor(x:string,y:number) { + super(x); + } + + static anotherStatic() { + (super.staticMethod(\'foo\'): number); // error, string !~> number + (super.doesntExist()); // error, A doesn\'t have a doesntExist method + } + + g() { + super.f(0); + return super.g; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class A { + constructor(x: number) { + + } + staticMethod(x: string): string { + return x; + } + f(x: string) { + + } +} +class B extends A { + constructor(x: string, y: number) { + super(x); + } + anotherStatic() { + (super.staticMethod(\"foo\"): number);// error, string !~> number + super.doesntExist();// error, A doesn\'t have a doesntExist method + } + g() { + super.f(0); + return super.g; + } +} + +" +`; + +exports[`test test.js 1`] = ` +"var D = require(\'./import\'); +class C extends D { + constructor() { return super(); } + foo() { return super.foo(); } +} +module.exports = C; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var D = require(\"./import\"); +class C extends D { + constructor() { + return super(); + } + foo() { + return super.foo(); + } +} +module.exports = C; + +" +`; diff --git a/tests/super/constructor.js b/tests/super/constructor.js new file mode 100644 index 000000000000..3e11a1dfdb03 --- /dev/null +++ b/tests/super/constructor.js @@ -0,0 +1,154 @@ +class A { x: number; } + +class B { + x: number; + constructor() { + this.x; // OK + } +} + +class C extends A { } + +class D extends A { + y: number; + constructor() { + this.y; // error (no super call) + this.x; // error (no super call) + } +} + +class E extends A { + y: number; + constructor() { + super(); + this.y; // OK + this.x; // OK + } +} + +function leak(f) { + f.y; // error + f.x; // error +} +class F extends A { + y: number; + constructor() { + leak(this); // error (no super call yet) + super(); + this.y; + this.x; + } +} + +class G extends A { + constructor(b) { + super.x; // error (no super call) + } +} + +class H extends A { + y: number; + constructor() { + if (Math.random() < 0.5) + super(); + else + super(); + this.y; // OK + this.x; // OK + } +} + +class I_ { + constructor(leaked_this) { + leaked_this.foo() + } + foo() { } +} +class I extends I_ { + constructor() { + super(this); // error (no super call yet) + } +} + +class J__ { } +class J_ extends J__ { + constructor(closure_leaking_this) { + closure_leaking_this(); + super(); + } + foo() { } +} +class J extends J_ { + constructor() { + super(() => this.foo()); // error (no super call yet) + // The reason for this error is that super constructor could call the + // closure and therefore access this before calling its own super + // constructor (as shown above). The only safe thing to do in the super + // constructor is to save the closure so that it can be called later, after + // initialization is done (as shown below). + } +} + +class K_ { + closure_leaking_this: () => void; + constructor(closure_leaking_this) { + this.closure_leaking_this = closure_leaking_this; + } + foo() { } +} +class K extends K_ { + constructor() { + super(() => { if (_this) _this.foo() }); // OK + var _this = this; + this.closure_leaking_this(); + } +} + +// even though super() calls the parent's constructor(), it does not do the same +// conversion on non-objects. so `new L_()` returns an instance of `L_` because +// the constructor returns false (a non-object), but `super()` returns false, +// which then similarly causes `new L()` to return an instance of `L`. +class L_ { + constructor() { + return false; + } +} +class L extends L_ { + constructor() { + let x: boolean = super(); + return x; + } +} +(new L_(): L_); +(new L(): L); + +// similarly, the converse is true: if the parent's constructor returns an +// object, then the child does too. +class M_ { + constructor() { + return {foo: 'foo'}; + } +} +class M extends M_ { + constructor() { + return super(); + } +} +(new M_(): {foo: string}); +(new M(): {foo: string}); + +// however! super() calls the parent constructor with the subclass as its `this` +// value (essentially `super.constructor.call(this)`). +class N_ { + constructor(): this { + let x = this; + return x; + } +} +class N extends N_ { + constructor() { + return super(); + } +} +(new N_(): N_); +(new N(): N); diff --git a/tests/super/import.js b/tests/super/import.js new file mode 100644 index 000000000000..4ca97a19d780 --- /dev/null +++ b/tests/super/import.js @@ -0,0 +1,4 @@ +class D { + foo(): number { return 0; } +} +module.exports = D; diff --git a/tests/super/jsfmt.spec.js b/tests/super/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/super/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/super/super.js b/tests/super/super.js new file mode 100644 index 000000000000..1bdae53e2366 --- /dev/null +++ b/tests/super/super.js @@ -0,0 +1,22 @@ + +class A { + constructor(x:number) { } + static staticMethod(x:string): string { return x; } + f(x:string) { } +} + +class B extends A { + constructor(x:string,y:number) { + super(x); + } + + static anotherStatic() { + (super.staticMethod('foo'): number); // error, string !~> number + (super.doesntExist()); // error, A doesn't have a doesntExist method + } + + g() { + super.f(0); + return super.g; + } +} diff --git a/tests/super/test.js b/tests/super/test.js new file mode 100644 index 000000000000..3a9a3c3654d7 --- /dev/null +++ b/tests/super/test.js @@ -0,0 +1,6 @@ +var D = require('./import'); +class C extends D { + constructor() { return super(); } + foo() { return super.foo(); } +} +module.exports = C; diff --git a/tests/suppress/A.js b/tests/suppress/A.js new file mode 100644 index 000000000000..3411f3e65e0a --- /dev/null +++ b/tests/suppress/A.js @@ -0,0 +1,24 @@ +// $FlowFixMe +var test1: string = 123; // This error should be suppressed + +// $FlowIssue +var test2: string = 123; // This error should be suppressed + +function getNum() { + return 123; +} + +// $FlowFixMe This was the second loc in the error +var test3: string = getNum(); // This error should be suppressed + +// $FlowFixMe Error unused suppression + +var test4: string = 123; // This error is NOT suppressed + + // $FlowFixMe Indentation shouldn't matter +var test5: string = 123; // This error should be suppressed + +/* + * $FlowNewLine + */ +var test6: string = 123; diff --git a/tests/suppress/B.js b/tests/suppress/B.js new file mode 100644 index 000000000000..b4f41e62eb69 --- /dev/null +++ b/tests/suppress/B.js @@ -0,0 +1,2 @@ +// $FlowFixMe +var test1: string = library_num; diff --git a/tests/suppress/C.js b/tests/suppress/C.js new file mode 100644 index 000000000000..80c35d500c6e --- /dev/null +++ b/tests/suppress/C.js @@ -0,0 +1,8 @@ +function takesAString(x: string): void {} + +function runTest(y: number): void { + takesAString( + /* $FlowFixMe - suppressing the error op location should also work */ + y, + ); +} diff --git a/tests/suppress/__snapshots__/jsfmt.spec.js.snap b/tests/suppress/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..4b43391295c6 --- /dev/null +++ b/tests/suppress/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,86 @@ +exports[`test A.js 1`] = ` +"// $FlowFixMe +var test1: string = 123; // This error should be suppressed + +// $FlowIssue +var test2: string = 123; // This error should be suppressed + +function getNum() { + return 123; +} + +// $FlowFixMe This was the second loc in the error +var test3: string = getNum(); // This error should be suppressed + +// $FlowFixMe Error unused suppression + +var test4: string = 123; // This error is NOT suppressed + + // $FlowFixMe Indentation shouldn't matter +var test5: string = 123; // This error should be suppressed + +/* + * $FlowNewLine + */ +var test6: string = 123; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// $FlowFixMe +var test1: string = 123;// This error should be suppressed +// $FlowIssue +var test2: string = 123;// This error should be suppressed +function getNum() { + return 123; +} +// $FlowFixMe This was the second loc in the error +var test3: string = getNum();// This error should be suppressed +// $FlowFixMe Error unused suppression +var test4: string = 123;// This error is NOT suppressed// $FlowFixMe Indentation shouldn't matter +var test5: string = 123;// This error should be suppressed +/* + * $FlowNewLine + */ +var test6: string = 123; + +" +`; + +exports[`test B.js 1`] = ` +"// $FlowFixMe +var test1: string = library_num; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// $FlowFixMe +var test1: string = library_num; + +" +`; + +exports[`test C.js 1`] = ` +"function takesAString(x: string): void {} + +function runTest(y: number): void { + takesAString( + /* $FlowFixMe - suppressing the error op location should also work */ + y, + ); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +function takesAString(x: string): void { + +} +function runTest(y: number): void { + takesAString( + /* $FlowFixMe - suppressing the error op location should also work */ + y + ); +} + +" +`; + +exports[`test lib.js 1`] = ` +"declare var library_num: number; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +declare var library_num: number; + +" +`; diff --git a/tests/suppress/jsfmt.spec.js b/tests/suppress/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/suppress/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/suppress/lib.js b/tests/suppress/lib.js new file mode 100644 index 000000000000..21cfa88e53e5 --- /dev/null +++ b/tests/suppress/lib.js @@ -0,0 +1 @@ +declare var library_num: number; diff --git a/tests/suppress_incremental/__snapshots__/jsfmt.spec.js.snap b/tests/suppress_incremental/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..16a23d3be09a --- /dev/null +++ b/tests/suppress_incremental/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,10 @@ +exports[`test test.js 1`] = ` +"// @flow + +(123: number); // no errors +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +(123: number);// no errors + +" +`; diff --git a/tests/suppress_incremental/jsfmt.spec.js b/tests/suppress_incremental/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/suppress_incremental/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/suppress_incremental/test.js b/tests/suppress_incremental/test.js new file mode 100644 index 000000000000..8f4e80213ed3 --- /dev/null +++ b/tests/suppress_incremental/test.js @@ -0,0 +1,3 @@ +// @flow + +(123: number); // no errors diff --git a/tests/suppress_traces/__snapshots__/jsfmt.spec.js.snap b/tests/suppress_traces/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..32292e7511df --- /dev/null +++ b/tests/suppress_traces/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,34 @@ +exports[`test traces.js 1`] = ` +"/* + * The location marked with the FlowFixMe does not show up in the original + * error but shows up in the flow check --traces 10 result. This test makes + * sure that we don't suppress the error due to a location that only shows up + * when --traces is turned on. + */ + +// $FlowFixMe - Error unused suppression +function bar(): number { return 5; } + +function foo(x: string) { + return bar(); +} + +var a: string = foo('hi'); // error number ~> string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* + * The location marked with the FlowFixMe does not show up in the original + * error but shows up in the flow check --traces 10 result. This test makes + * sure that we don't suppress the error due to a location that only shows up + * when --traces is turned on. + */ +// $FlowFixMe - Error unused suppression +function bar(): number { + return 5; +} +function foo(x: string) { + return bar(); +} +var a: string = foo("hi");// error number ~> string + +" +`; diff --git a/tests/suppress_traces/jsfmt.spec.js b/tests/suppress_traces/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/suppress_traces/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/suppress_traces/traces.js b/tests/suppress_traces/traces.js new file mode 100644 index 000000000000..1a34c3771dc6 --- /dev/null +++ b/tests/suppress_traces/traces.js @@ -0,0 +1,15 @@ +/* + * The location marked with the FlowFixMe does not show up in the original + * error but shows up in the flow check --traces 10 result. This test makes + * sure that we don't suppress the error due to a location that only shows up + * when --traces is turned on. + */ + +// $FlowFixMe - Error unused suppression +function bar(): number { return 5; } + +function foo(x: string) { + return bar(); +} + +var a: string = foo('hi'); // error number ~> string diff --git a/tests/switch/__snapshots__/jsfmt.spec.js.snap b/tests/switch/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..ba9fd1e81846 --- /dev/null +++ b/tests/switch/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,262 @@ +exports[`test more_switch.js 1`] = ` +"/* @flow */ + +function foo(x): number { + switch (x) { + case 0: + case 1: return 1; + default: throw new Error(\'hi\'); + } +} + +function bar(x) { + switch (x) { + case 0: break; + default: return; + } + 1; +} + +function baz(x): number { + switch (x) { + case 0: break; + case 1: return 1; + default: throw new Error(\'hi\'); + } + return 2; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1020 + }, \"consequent\").indent(options.tabWidth)); + ^ + +TypeError: path.call(...).indent is not a function + at genericPrintNoParens (/src/printer.js:1020:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1005:36) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test switch.js 1`] = ` +"/** + * @flow + */ +function foo( +): number { + switch (\'foo\') { + case \'foo\': + return 1; + } + return 2; +} + +function bar() { + switch (\'bar\') { + case \'bar\': + break; + default: + break; + } +} + +function qux(b) { + var x = b? 0: \"\"; + switch(\'qux\') { + case \'\': + x = 0; + case \'qux\': + x = x*x; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1020 + }, \"consequent\").indent(options.tabWidth)); + ^ + +TypeError: path.call(...).indent is not a function + at genericPrintNoParens (/src/printer.js:1020:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1005:36) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test switch_default_fallthrough.js 1`] = ` +"/** + * @flow + */ +function foo(x : mixed): string { + var a = \"\"; + var b = \"\"; + + switch (x) { + case \"foo\": + a = 0; + default: + b = 0; + } + + // a is now string | number + (a : string); // error, string | number ~/> string + (a : number); // error, string | number ~/> number + + // b is now number + (b : number); // ok + return b; // error, number ~/> string +} + +function baz(x: mixed): number { + var a = \"\"; + var b = \"\"; + + switch (x) { + case \"baz\": + a = 0; + break; + case \"bar\": + a = \"\"; + default: + b = 0; + } + + // a is now string | number + (a : string); // error, string | number ~/> string + (a : number); // error, string | number ~/> number + + // b is now string | number + (b : string); // error, string | number ~/> string + (b : number); // error, string | number ~/> number + + return a+b; // error, string ~/> number +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1020 + }, \"consequent\").indent(options.tabWidth)); + ^ + +TypeError: path.call(...).indent is not a function + at genericPrintNoParens (/src/printer.js:1020:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1005:36) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test trailing_cases.js 1`] = ` +"/** + * trailing cases are allowed - spot checks that we handle them as usual + * @flow + */ +function f1(i) { + var x; + + switch (i) { + case 0: + x = 0; + break; + case 1: + x = 1; + break; + default: + x = -1; + break; + case 2: + x = \"2\"; + break; + } + + var y:number = x; // error, number | string ~/> number +} + +function f2(i) { + var x; + + switch (i) { + case 0: + case 1: + default: + x = 1; + break; + case 2: + // does not fall through default + } + + var y:number = x; // error, number | uninitialized ~/> number +} + +function f3(i) { + var x; + + switch (i) { + case 0: + case 1: + default: + // falls through to subsequent cases + case 2: + x = 1; + } + + var y:number = x; // no error +} + +function foo(x): number { + switch (x) { + case 0: + default: throw new Error(\'hi\'); + case 1: return 1; + } +} + +function bar(x) { + switch (x) { + default: return; + case 0: break; + } + 1; +} + +function baz(x): number { + switch (x) { + case 0: break; + default: throw new Error(\'hi\'); + case 1: return 1; + } + return 2; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1020 + }, \"consequent\").indent(options.tabWidth)); + ^ + +TypeError: path.call(...).indent is not a function + at genericPrintNoParens (/src/printer.js:1020:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1005:36) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/switch/jsfmt.spec.js b/tests/switch/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/switch/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/switch/more_switch.js b/tests/switch/more_switch.js new file mode 100644 index 000000000000..1b478e4a708e --- /dev/null +++ b/tests/switch/more_switch.js @@ -0,0 +1,26 @@ +/* @flow */ + +function foo(x): number { + switch (x) { + case 0: + case 1: return 1; + default: throw new Error('hi'); + } +} + +function bar(x) { + switch (x) { + case 0: break; + default: return; + } + 1; +} + +function baz(x): number { + switch (x) { + case 0: break; + case 1: return 1; + default: throw new Error('hi'); + } + return 2; +} diff --git a/tests/switch/switch.js b/tests/switch/switch.js new file mode 100644 index 000000000000..7431b929b4ea --- /dev/null +++ b/tests/switch/switch.js @@ -0,0 +1,30 @@ +/** + * @flow + */ +function foo( +): number { + switch ('foo') { + case 'foo': + return 1; + } + return 2; +} + +function bar() { + switch ('bar') { + case 'bar': + break; + default: + break; + } +} + +function qux(b) { + var x = b? 0: ""; + switch('qux') { + case '': + x = 0; + case 'qux': + x = x*x; + } +} diff --git a/tests/switch/switch_default_fallthrough.js b/tests/switch/switch_default_fallthrough.js new file mode 100644 index 000000000000..2b79bada2503 --- /dev/null +++ b/tests/switch/switch_default_fallthrough.js @@ -0,0 +1,47 @@ +/** + * @flow + */ +function foo(x : mixed): string { + var a = ""; + var b = ""; + + switch (x) { + case "foo": + a = 0; + default: + b = 0; + } + + // a is now string | number + (a : string); // error, string | number ~/> string + (a : number); // error, string | number ~/> number + + // b is now number + (b : number); // ok + return b; // error, number ~/> string +} + +function baz(x: mixed): number { + var a = ""; + var b = ""; + + switch (x) { + case "baz": + a = 0; + break; + case "bar": + a = ""; + default: + b = 0; + } + + // a is now string | number + (a : string); // error, string | number ~/> string + (a : number); // error, string | number ~/> number + + // b is now string | number + (b : string); // error, string | number ~/> string + (b : number); // error, string | number ~/> number + + return a+b; // error, string ~/> number +} diff --git a/tests/switch/trailing_cases.js b/tests/switch/trailing_cases.js new file mode 100644 index 000000000000..72f60aa92c28 --- /dev/null +++ b/tests/switch/trailing_cases.js @@ -0,0 +1,80 @@ +/** + * trailing cases are allowed - spot checks that we handle them as usual + * @flow + */ +function f1(i) { + var x; + + switch (i) { + case 0: + x = 0; + break; + case 1: + x = 1; + break; + default: + x = -1; + break; + case 2: + x = "2"; + break; + } + + var y:number = x; // error, number | string ~/> number +} + +function f2(i) { + var x; + + switch (i) { + case 0: + case 1: + default: + x = 1; + break; + case 2: + // does not fall through default + } + + var y:number = x; // error, number | uninitialized ~/> number +} + +function f3(i) { + var x; + + switch (i) { + case 0: + case 1: + default: + // falls through to subsequent cases + case 2: + x = 1; + } + + var y:number = x; // no error +} + +function foo(x): number { + switch (x) { + case 0: + default: throw new Error('hi'); + case 1: return 1; + } +} + +function bar(x) { + switch (x) { + default: return; + case 0: break; + } + 1; +} + +function baz(x): number { + switch (x) { + case 0: break; + default: throw new Error('hi'); + case 1: return 1; + } + return 2; +} diff --git a/tests/symbol/__snapshots__/jsfmt.spec.js.snap b/tests/symbol/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..6d9d2d58fbdd --- /dev/null +++ b/tests/symbol/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,14 @@ +exports[`test symbol.js 1`] = ` +"var FOO = Symbol(); +var BAR = Symbol('bar'); + +// TODO: Expected error +var WAT = Symbol('foo', 'bar'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var FOO = Symbol(); +var BAR = Symbol("bar"); +// TODO: Expected error +var WAT = Symbol("foo", "bar"); + +" +`; diff --git a/tests/symbol/jsfmt.spec.js b/tests/symbol/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/symbol/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/symbol/symbol.js b/tests/symbol/symbol.js new file mode 100644 index 000000000000..f8c625d14c95 --- /dev/null +++ b/tests/symbol/symbol.js @@ -0,0 +1,5 @@ +var FOO = Symbol(); +var BAR = Symbol('bar'); + +// TODO: Expected error +var WAT = Symbol('foo', 'bar'); diff --git a/tests/symlink/__snapshots__/jsfmt.spec.js.snap b/tests/symlink/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..6a3e902ee7d4 --- /dev/null +++ b/tests/symlink/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,25 @@ +exports[`test bar.js 1`] = ` +"export type Foo = { x: number; } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +export type Foo = { x: number }; + +" +`; + +exports[`test foo.js 1`] = ` +"export type Foo = { x: number; } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +export type Foo = { x: number }; + +" +`; + +exports[`test qux.js 1`] = ` +"import type { Foo } from './bar.js'; +({ x: "" }: Foo); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +import type { Foo } from "./bar.js"; +({ x: "" }: Foo); + +" +`; diff --git a/tests/symlink/bar.js b/tests/symlink/bar.js new file mode 100644 index 000000000000..41044c7c6ca9 --- /dev/null +++ b/tests/symlink/bar.js @@ -0,0 +1 @@ +export type Foo = { x: number; } diff --git a/tests/symlink/foo.js b/tests/symlink/foo.js new file mode 100644 index 000000000000..41044c7c6ca9 --- /dev/null +++ b/tests/symlink/foo.js @@ -0,0 +1 @@ +export type Foo = { x: number; } diff --git a/tests/symlink/jsfmt.spec.js b/tests/symlink/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/symlink/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/symlink/qux.js b/tests/symlink/qux.js new file mode 100644 index 000000000000..1595d2426a9d --- /dev/null +++ b/tests/symlink/qux.js @@ -0,0 +1,2 @@ +import type { Foo } from './bar.js'; +({ x: "" }: Foo); diff --git a/tests/tagged-unions/__snapshots__/jsfmt.spec.js.snap b/tests/tagged-unions/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..f1954cc1a99c --- /dev/null +++ b/tests/tagged-unions/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,242 @@ +exports[`test classes.js 1`] = ` +"// @flow + +class Foo { + type: \'foo\'; + foo: string; +} + +class Bar { + type: \'bar\'; + bar: string; +} + +type Foobar = Foo | Bar; + +function foobar(x: Foobar): string { + if (x.type === \'foo\') { + return foo(x); + } else if (x.type === \'bar\') { + return bar(x); + } else { + return \'unknown\'; + } +} + +function foo(x: Foo): string { + return x.foo; +} + +function bar(x: Bar): string { + return x.bar; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test interfaces-neg.js 1`] = ` +"/* @flow */ + +declare interface IDataBase { + id: string, + name: string, +} + +declare interface IUserData extends IDataBase { + kind: \"user\", +} + +declare interface ISystemData extends IDataBase { + kind: \"system\", +} + +declare type IData = IUserData | ISystemData; + +const data: IData = { + id: \"\", + name: \"\", + kind: \"system\", +} + +if (data.kind === \"user\") { + (data: ISystemData); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1391 + fromString(\", \").join(path.map(print, \"extends\")) + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1391:28) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:238:16 + at FastPath.call (/src/fast-path.js:113:16) +" +`; + +exports[`test interfaces-pos.js 1`] = ` +"/* @flow */ + +declare interface IDataBase { + id: string, + name: string, +} + +declare interface IUserData extends IDataBase { + kind: \"user\", +} + +declare interface ISystemData extends IDataBase { + kind: \"system\", +} + +declare type IData = IUserData | ISystemData; + +const data: IData = { + id: \"\", + name: \"\", + kind: \"system\", +} + +if (data.kind === \"system\") { + (data: ISystemData); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1391 + fromString(\", \").join(path.map(print, \"extends\")) + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1391:28) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:238:16 + at FastPath.call (/src/fast-path.js:113:16) +" +`; + +exports[`test type-decls-neg.js 1`] = ` +"/* @flow */ + +type DataBase = { + id: string, + name: string, +}; + +type UserData = { + id: string, + name: string, + kind: \"user\", +} + +type SystemData = { + id: string, + name: string, + kind: \"system\", +} + +declare type Data = UserData | SystemData; + +const data: Data = { + id: \"\", + name: \"\", + kind: \"system\", +} + +if (data.kind === \"user\") { + (data: SystemData); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test type-decls-pos.js 1`] = ` +"/* @flow */ + +type DataBase = { + id: string, + name: string, +}; + +type UserData = { + id: string, + name: string, + kind: \"user\", +} + +type SystemData = { + id: string, + name: string, + kind: \"system\", +} + +declare type Data = UserData | SystemData; + +const data: Data = { + id: \"\", + name: \"\", + kind: \"system\", +} + +if (data.kind === \"system\") { + (data: SystemData); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/tagged-unions/classes.js b/tests/tagged-unions/classes.js new file mode 100644 index 000000000000..0a1a0853dfbf --- /dev/null +++ b/tests/tagged-unions/classes.js @@ -0,0 +1,31 @@ +// @flow + +class Foo { + type: 'foo'; + foo: string; +} + +class Bar { + type: 'bar'; + bar: string; +} + +type Foobar = Foo | Bar; + +function foobar(x: Foobar): string { + if (x.type === 'foo') { + return foo(x); + } else if (x.type === 'bar') { + return bar(x); + } else { + return 'unknown'; + } +} + +function foo(x: Foo): string { + return x.foo; +} + +function bar(x: Bar): string { + return x.bar; +} diff --git a/tests/tagged-unions/interfaces-neg.js b/tests/tagged-unions/interfaces-neg.js new file mode 100644 index 000000000000..c9909d06ccad --- /dev/null +++ b/tests/tagged-unions/interfaces-neg.js @@ -0,0 +1,26 @@ +/* @flow */ + +declare interface IDataBase { + id: string, + name: string, +} + +declare interface IUserData extends IDataBase { + kind: "user", +} + +declare interface ISystemData extends IDataBase { + kind: "system", +} + +declare type IData = IUserData | ISystemData; + +const data: IData = { + id: "", + name: "", + kind: "system", +} + +if (data.kind === "user") { + (data: ISystemData); +} diff --git a/tests/tagged-unions/interfaces-pos.js b/tests/tagged-unions/interfaces-pos.js new file mode 100644 index 000000000000..533dfe171e7b --- /dev/null +++ b/tests/tagged-unions/interfaces-pos.js @@ -0,0 +1,26 @@ +/* @flow */ + +declare interface IDataBase { + id: string, + name: string, +} + +declare interface IUserData extends IDataBase { + kind: "user", +} + +declare interface ISystemData extends IDataBase { + kind: "system", +} + +declare type IData = IUserData | ISystemData; + +const data: IData = { + id: "", + name: "", + kind: "system", +} + +if (data.kind === "system") { + (data: ISystemData); +} diff --git a/tests/tagged-unions/jsfmt.spec.js b/tests/tagged-unions/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/tagged-unions/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/tagged-unions/type-decls-neg.js b/tests/tagged-unions/type-decls-neg.js new file mode 100644 index 000000000000..c7fbd789d1c5 --- /dev/null +++ b/tests/tagged-unions/type-decls-neg.js @@ -0,0 +1,30 @@ +/* @flow */ + +type DataBase = { + id: string, + name: string, +}; + +type UserData = { + id: string, + name: string, + kind: "user", +} + +type SystemData = { + id: string, + name: string, + kind: "system", +} + +declare type Data = UserData | SystemData; + +const data: Data = { + id: "", + name: "", + kind: "system", +} + +if (data.kind === "user") { + (data: SystemData); +} diff --git a/tests/tagged-unions/type-decls-pos.js b/tests/tagged-unions/type-decls-pos.js new file mode 100644 index 000000000000..dec1ed6a0c0a --- /dev/null +++ b/tests/tagged-unions/type-decls-pos.js @@ -0,0 +1,30 @@ +/* @flow */ + +type DataBase = { + id: string, + name: string, +}; + +type UserData = { + id: string, + name: string, + kind: "user", +} + +type SystemData = { + id: string, + name: string, + kind: "system", +} + +declare type Data = UserData | SystemData; + +const data: Data = { + id: "", + name: "", + kind: "system", +} + +if (data.kind === "system") { + (data: SystemData); +} diff --git a/tests/taint/__snapshots__/jsfmt.spec.js.snap b/tests/taint/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..7acccb220455 --- /dev/null +++ b/tests/taint/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,453 @@ +exports[`test adder.js 1`] = ` +"// @flow + +function f(x : $Tainted, y : $Tainted) { + var z : $Tainted = x + y; +} +function f1(x : $Tainted, y : number) { + var z : $Tainted = x + y; +} +function f2(x : number, y : $Tainted) { + var z : $Tainted = x + y; +} +// This should cause an error. +function f3(x : $Tainted, y : number) { + var z : number = x + y; +} +// This should cause an error. +function f4(x : number, y : $Tainted) { + var z : number = x + y; +} +// This should cause an error. +function f5(x : number, y : $Tainted) { + var z : string = x + y; +} +// This should cause an error. +function f6(x : string, y : $Tainted) { + var z : string = x + y; +} +// This should cause an error. +function f7(x : $Tainted, y : $Tainted) { + var z : string = x + y; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test any_object.js 1`] = ` +"// @flow + +let tests = [ + // setting a property + function(x: $Tainted, y: string) { + let obj: Object = {}; + obj.foo = x; // error, taint ~> any + obj[y] = x; // error, taint ~> any + }, + + // getting a property + function() { + let obj: Object = { foo: \'foo\' }; + (obj.foo: $Tainted); // ok + }, + + // calling a method + function(x: $Tainted) { + let obj: Object = {}; + obj.foo(x); // error, taint ~> any + + let foo = obj.foo; + foo(x); // error, taint ~> any + }, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test call-object-property.js 1`] = ` +"// @flow + +function foo(x : $Tainted, o : Object) { + // Error + o.f(x); +} +function foo1(x : $Tainted, o : {f : (y : $Tainted) => void}) { + o.f(x); +} +function foo2(o1 : Object, o2 : {t : $Tainted}) { + o1.f(o2.t); +} +function foo3(x : $Tainted, o : {f : (y : $Tainted) => void}) { + o.f(x); +} +function f_foo1(x : $Tainted, f : Function) { + // Error + f(x); +} +function f_foo2(f1 : Function, o : {t : $Tainted}) { + f1(o.t); +} +function f_foo3(f1 : Function, o1 : Object, o2 : {t : $Tainted}) { + (f1(o1))(o2.t); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test comparator.js 1`] = ` +"// @flow +// Should cause an error. +function f(x : $Tainted, y : $Tainted) { + var z : $Tainted = x < y; +} +// Should cause an error. +function f1(x : string, y : $Tainted) { + var z : $Tainted = x < y; +} +// Should cause an error. +function f2(x : $Tainted, y : number) { + var z : $Tainted = x < y; +} +// Note: We allow removing Taint when two tainted +// values are compared. +function f3(x : $Tainted, y : string) { + var z : bool = x < y; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test function.js 1`] = ` +"// @flow + +let tests = [ + // flows any to each param + function(x: any, y: $Tainted) { + x(y); // error, taint ~> any + }, + + // calling \`any\` returns \`any\` + function(x: any, y: $Tainted) { + let z = x(); + z(y); + } +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test globals.js 1`] = ` +"// @flow + +class A { + f(x : $Tainted) { + fakeDocument.location = x; // error + doStuff(x); // ok + } + f1(x : $Tainted) { + // TODO(rcastano): should cause an error. + window.fakeLocation = x; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test lib.js 1`] = ` +"declare class FakeLocation { + assign(url: string): void; +} + +declare class FakeDocument { + location: FakeLocation; +} + +declare function doStuff(x: $Tainted): void; + +declare var fakeDocument: FakeDocument; +declare var fakeLocation: FakeLocation; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test taint1.js 1`] = ` +"/* + * + * @flow + */ +class A { + f(tainted : $Tainted) { + // This shouldn\'t give a warning (both are tainted) + var also_tainted : $Tainted = tainted; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test taint2.js 1`] = ` +"/* + * + * @flow + */ +class A { + f(tainted : $Tainted) { + // This *should* give a warning. + fakeDocument.location.assign(tainted); + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test taint3.js 1`] = ` +"/* + * + * @flow + */ +class A { + f(tainted : $Tainted) { + // The Tainted annotation should still flow. + var safe = tainted; + // This should give a warning. + var loc : string = safe; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test taint4.js 1`] = ` +"/* + * + * @flow + */ + +var safe : string = \"safe\"; +// This should be allowed. +var tainted : $Tainted = safe + +function f(x : $Tainted) { + // Should cause error. + var y : any = x; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test use-types.js 1`] = ` +"/* + * @flow + */ + +// Should cause an error. +function foo (x : $Tainted) { + var should_fail : number = x * 42; +} +// Should cause an error. +function foo1 (x : $Tainted<{f: number}>) { + var ok : number = x.f; +} +// Should cause an error. +function foo2 (o : {f (y:number):number}, t: $Tainted) { + return o.f(t); +} + +function foo3 (x : $Tainted<{f: number}>) { + var also_tainted : $Tainted = x.f; +} +// Should cause an error. +function foo4 (a : $Tainted>) { + var trusted : string = a[0]; +} +// Type error. +function foo5 (a : $Tainted>) { + var trusted_number : number = a[0]; +} + +function foo6 (a : $Tainted>) { + var trusted : $Tainted = a[0]; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/taint/adder.js b/tests/taint/adder.js new file mode 100644 index 000000000000..50dbaf2edae3 --- /dev/null +++ b/tests/taint/adder.js @@ -0,0 +1,31 @@ +// @flow + +function f(x : $Tainted, y : $Tainted) { + var z : $Tainted = x + y; +} +function f1(x : $Tainted, y : number) { + var z : $Tainted = x + y; +} +function f2(x : number, y : $Tainted) { + var z : $Tainted = x + y; +} +// This should cause an error. +function f3(x : $Tainted, y : number) { + var z : number = x + y; +} +// This should cause an error. +function f4(x : number, y : $Tainted) { + var z : number = x + y; +} +// This should cause an error. +function f5(x : number, y : $Tainted) { + var z : string = x + y; +} +// This should cause an error. +function f6(x : string, y : $Tainted) { + var z : string = x + y; +} +// This should cause an error. +function f7(x : $Tainted, y : $Tainted) { + var z : string = x + y; +} diff --git a/tests/taint/any_object.js b/tests/taint/any_object.js new file mode 100644 index 000000000000..6569b7b61069 --- /dev/null +++ b/tests/taint/any_object.js @@ -0,0 +1,25 @@ +// @flow + +let tests = [ + // setting a property + function(x: $Tainted, y: string) { + let obj: Object = {}; + obj.foo = x; // error, taint ~> any + obj[y] = x; // error, taint ~> any + }, + + // getting a property + function() { + let obj: Object = { foo: 'foo' }; + (obj.foo: $Tainted); // ok + }, + + // calling a method + function(x: $Tainted) { + let obj: Object = {}; + obj.foo(x); // error, taint ~> any + + let foo = obj.foo; + foo(x); // error, taint ~> any + }, +]; diff --git a/tests/taint/call-object-property.js b/tests/taint/call-object-property.js new file mode 100644 index 000000000000..7cece3014cf2 --- /dev/null +++ b/tests/taint/call-object-property.js @@ -0,0 +1,25 @@ +// @flow + +function foo(x : $Tainted, o : Object) { + // Error + o.f(x); +} +function foo1(x : $Tainted, o : {f : (y : $Tainted) => void}) { + o.f(x); +} +function foo2(o1 : Object, o2 : {t : $Tainted}) { + o1.f(o2.t); +} +function foo3(x : $Tainted, o : {f : (y : $Tainted) => void}) { + o.f(x); +} +function f_foo1(x : $Tainted, f : Function) { + // Error + f(x); +} +function f_foo2(f1 : Function, o : {t : $Tainted}) { + f1(o.t); +} +function f_foo3(f1 : Function, o1 : Object, o2 : {t : $Tainted}) { + (f1(o1))(o2.t); +} diff --git a/tests/taint/comparator.js b/tests/taint/comparator.js new file mode 100644 index 000000000000..defe902f0c6f --- /dev/null +++ b/tests/taint/comparator.js @@ -0,0 +1,18 @@ +// @flow +// Should cause an error. +function f(x : $Tainted, y : $Tainted) { + var z : $Tainted = x < y; +} +// Should cause an error. +function f1(x : string, y : $Tainted) { + var z : $Tainted = x < y; +} +// Should cause an error. +function f2(x : $Tainted, y : number) { + var z : $Tainted = x < y; +} +// Note: We allow removing Taint when two tainted +// values are compared. +function f3(x : $Tainted, y : string) { + var z : bool = x < y; +} diff --git a/tests/taint/function.js b/tests/taint/function.js new file mode 100644 index 000000000000..f007468ba5f8 --- /dev/null +++ b/tests/taint/function.js @@ -0,0 +1,14 @@ +// @flow + +let tests = [ + // flows any to each param + function(x: any, y: $Tainted) { + x(y); // error, taint ~> any + }, + + // calling `any` returns `any` + function(x: any, y: $Tainted) { + let z = x(); + z(y); + } +]; diff --git a/tests/taint/globals.js b/tests/taint/globals.js new file mode 100644 index 000000000000..18efa6515d2e --- /dev/null +++ b/tests/taint/globals.js @@ -0,0 +1,12 @@ +// @flow + +class A { + f(x : $Tainted) { + fakeDocument.location = x; // error + doStuff(x); // ok + } + f1(x : $Tainted) { + // TODO(rcastano): should cause an error. + window.fakeLocation = x; + } +} diff --git a/tests/taint/jsfmt.spec.js b/tests/taint/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/taint/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/taint/lib.js b/tests/taint/lib.js new file mode 100644 index 000000000000..3ca07bf6f6a4 --- /dev/null +++ b/tests/taint/lib.js @@ -0,0 +1,12 @@ +declare class FakeLocation { + assign(url: string): void; +} + +declare class FakeDocument { + location: FakeLocation; +} + +declare function doStuff(x: $Tainted): void; + +declare var fakeDocument: FakeDocument; +declare var fakeLocation: FakeLocation; diff --git a/tests/taint/taint1.js b/tests/taint/taint1.js new file mode 100644 index 000000000000..29b1260e0865 --- /dev/null +++ b/tests/taint/taint1.js @@ -0,0 +1,10 @@ +/* + * + * @flow + */ +class A { + f(tainted : $Tainted) { + // This shouldn't give a warning (both are tainted) + var also_tainted : $Tainted = tainted; + } +} diff --git a/tests/taint/taint2.js b/tests/taint/taint2.js new file mode 100644 index 000000000000..7322d8fe84c6 --- /dev/null +++ b/tests/taint/taint2.js @@ -0,0 +1,10 @@ +/* + * + * @flow + */ +class A { + f(tainted : $Tainted) { + // This *should* give a warning. + fakeDocument.location.assign(tainted); + } +} diff --git a/tests/taint/taint3.js b/tests/taint/taint3.js new file mode 100644 index 000000000000..1e3cb105a7dd --- /dev/null +++ b/tests/taint/taint3.js @@ -0,0 +1,12 @@ +/* + * + * @flow + */ +class A { + f(tainted : $Tainted) { + // The Tainted annotation should still flow. + var safe = tainted; + // This should give a warning. + var loc : string = safe; + } +} diff --git a/tests/taint/taint4.js b/tests/taint/taint4.js new file mode 100644 index 000000000000..61f2ee45b90f --- /dev/null +++ b/tests/taint/taint4.js @@ -0,0 +1,13 @@ +/* + * + * @flow + */ + +var safe : string = "safe"; +// This should be allowed. +var tainted : $Tainted = safe + +function f(x : $Tainted) { + // Should cause error. + var y : any = x; +} diff --git a/tests/taint/use-types.js b/tests/taint/use-types.js new file mode 100644 index 000000000000..07f081fec69e --- /dev/null +++ b/tests/taint/use-types.js @@ -0,0 +1,32 @@ +/* + * @flow + */ + +// Should cause an error. +function foo (x : $Tainted) { + var should_fail : number = x * 42; +} +// Should cause an error. +function foo1 (x : $Tainted<{f: number}>) { + var ok : number = x.f; +} +// Should cause an error. +function foo2 (o : {f (y:number):number}, t: $Tainted) { + return o.f(t); +} + +function foo3 (x : $Tainted<{f: number}>) { + var also_tainted : $Tainted = x.f; +} +// Should cause an error. +function foo4 (a : $Tainted>) { + var trusted : string = a[0]; +} +// Type error. +function foo5 (a : $Tainted>) { + var trusted_number : number = a[0]; +} + +function foo6 (a : $Tainted>) { + var trusted : $Tainted = a[0]; +} diff --git a/tests/template/__snapshots__/jsfmt.spec.js.snap b/tests/template/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..ad3180202c4f --- /dev/null +++ b/tests/template/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,64 @@ +exports[`test template.js 1`] = ` +"/* @flow */ + +(\`foo\`: string); // ok +(\`bar\`: 'bar'); // ok +(\`baz\`: number); // error + +\`foo \${123} bar\`; // ok, number can be appended to string +\`foo \${{bar: 123}} baz\`; // error, object can't be appended + +let tests = [ + function(x: string) { + \`foo \${x}\`; // ok + \`\${x} bar\`; // ok + \`foo \${'bar'} \${x}\`; // ok + }, + function(x: number) { + \`foo \${x}\`; // ok + \`\${x} bar\`; // ok + \`foo \${'bar'} \${x}\`; // ok + }, + function(x: boolean) { + \`foo \${x}\`; // error + \`\${x} bar\`; // error + \`foo \${'bar'} \${x}\`; // error + }, + function(x: mixed) { + \`foo \${x}\`; // error + \`\${x} bar\`; // error + \`foo \${'bar'} \${x}\`; // error + }, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +(\`foo\`: string);// ok +(\`bar\`: "bar");// ok +(\`baz\`: number);// error +\`foo \${123} bar\`;// ok, number can be appended to string +\`foo \${{ bar: 123 }} baz\`;// error, object can't be appended +let tests = [ + function(x: string) { + \`foo \${x}\`;// ok + \`\${x} bar\`;// ok + \`foo \${"bar"} \${x}\`;// ok + }, + function(x: number) { + \`foo \${x}\`;// ok + \`\${x} bar\`;// ok + \`foo \${"bar"} \${x}\`;// ok + }, + function(x: boolean) { + \`foo \${x}\`;// error + \`\${x} bar\`;// error + \`foo \${"bar"} \${x}\`;// error + }, + function(x: mixed) { + \`foo \${x}\`;// error + \`\${x} bar\`;// error + \`foo \${"bar"} \${x}\`;// error + } +]; + +" +`; diff --git a/tests/template/jsfmt.spec.js b/tests/template/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/template/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/template/template.js b/tests/template/template.js new file mode 100644 index 000000000000..5db2b3976cd3 --- /dev/null +++ b/tests/template/template.js @@ -0,0 +1,31 @@ +/* @flow */ + +(`foo`: string); // ok +(`bar`: 'bar'); // ok +(`baz`: number); // error + +`foo ${123} bar`; // ok, number can be appended to string +`foo ${{bar: 123}} baz`; // error, object can't be appended + +let tests = [ + function(x: string) { + `foo ${x}`; // ok + `${x} bar`; // ok + `foo ${'bar'} ${x}`; // ok + }, + function(x: number) { + `foo ${x}`; // ok + `${x} bar`; // ok + `foo ${'bar'} ${x}`; // ok + }, + function(x: boolean) { + `foo ${x}`; // error + `${x} bar`; // error + `foo ${'bar'} ${x}`; // error + }, + function(x: mixed) { + `foo ${x}`; // error + `${x} bar`; // error + `foo ${'bar'} ${x}`; // error + }, +]; diff --git a/tests/this/This.js b/tests/this/This.js new file mode 100644 index 000000000000..6656c4ae0463 --- /dev/null +++ b/tests/this/This.js @@ -0,0 +1,60 @@ +/* @providesModule This */ + +function F() { this.x = 0; } +F.prototype.m = function() { this.y = 0; } + +function foo(p:string) { } + +var o1 = new F(); // sets o1.x to 0 +o1.x = ""; +foo(o1.x); // ok by definite assignment + +var o2 = new F(); +o1.y = 0; +o2.y = ""; +foo(o2.y); // setting o1.y to 0 has no effect on o2.y + +var o3 = new F(); +o3.m(); // sets o3.y to 0 +o3.y = ""; +foo(o3.y); // ok by definite assignment +foo(o2.y); // setting o3.y to 0 has no effect on o2.y + +/* + * this bindings: + */ + +/* standard functions may rebind this */ +function f1() : number { + return this.x +} + +var f1_1 = f1.bind({x: 0})(); // ok +var f1_2 : string = f1.bind({x: 0})(); // error, number -> string +var f1_3 = f1.bind({x: ""})(); // error, string -> number +// TODO make this error blame the call site, rather than the function body +var f1_4 = f1(); // error, (global object).x + +/* arrow functions bind this at point of definition */ +/* top level arrow functions bind this to global object */ +var a1 = () => { + return this.x +} + +var ax = a1(); // error, (this:mixed).x + +/* nested arrows bind enclosing this (which may itself rebind) */ +function f2() : number { + var a2 = () => { return this.x }; + return a2() +} + +var f2_1 = f2.bind({x: 0})(); // ok +var f2_2 : string = f2.bind({x: 0})(); // error, number -> string +var f2_3 = f2.bind({x: ""})(); // error, string -> number +// TODO make this error blame the call site, rather than the function body +var f2_4 = f2(); // error, (global object).x + +(this: void); + +module.exports = true; diff --git a/tests/this/__snapshots__/jsfmt.spec.js.snap b/tests/this/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..9541761fed9f --- /dev/null +++ b/tests/this/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,184 @@ +exports[`test This.js 1`] = ` +"/* @providesModule This */ + +function F() { this.x = 0; } +F.prototype.m = function() { this.y = 0; } + +function foo(p:string) { } + +var o1 = new F(); // sets o1.x to 0 +o1.x = ""; +foo(o1.x); // ok by definite assignment + +var o2 = new F(); +o1.y = 0; +o2.y = ""; +foo(o2.y); // setting o1.y to 0 has no effect on o2.y + +var o3 = new F(); +o3.m(); // sets o3.y to 0 +o3.y = ""; +foo(o3.y); // ok by definite assignment +foo(o2.y); // setting o3.y to 0 has no effect on o2.y + +/* + * this bindings: + */ + +/* standard functions may rebind this */ +function f1() : number { + return this.x +} + +var f1_1 = f1.bind({x: 0})(); // ok +var f1_2 : string = f1.bind({x: 0})(); // error, number -> string +var f1_3 = f1.bind({x: ""})(); // error, string -> number +// TODO make this error blame the call site, rather than the function body +var f1_4 = f1(); // error, (global object).x + +/* arrow functions bind this at point of definition */ +/* top level arrow functions bind this to global object */ +var a1 = () => { + return this.x +} + +var ax = a1(); // error, (this:mixed).x + +/* nested arrows bind enclosing this (which may itself rebind) */ +function f2() : number { + var a2 = () => { return this.x }; + return a2() +} + +var f2_1 = f2.bind({x: 0})(); // ok +var f2_2 : string = f2.bind({x: 0})(); // error, number -> string +var f2_3 = f2.bind({x: ""})(); // error, string -> number +// TODO make this error blame the call site, rather than the function body +var f2_4 = f2(); // error, (global object).x + +(this: void); + +module.exports = true; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @providesModule This */ +function F() { + this.x = 0; +} +F.prototype.m = function() { + this.y = 0; +}; +function foo(p: string) { + +} +var o1 = new F();// sets o1.x to 0 +o1.x = ""; +foo(o1.x);// ok by definite assignment +var o2 = new F(); +o1.y = 0; +o2.y = ""; +foo(o2.y);// setting o1.y to 0 has no effect on o2.y +var o3 = new F(); +o3.m();// sets o3.y to 0 +o3.y = ""; +foo(o3.y);// ok by definite assignment +foo(o2.y);// setting o3.y to 0 has no effect on o2.y +/* + * this bindings: + */ +/* standard functions may rebind this */ +function f1(): number { + return this.x; +} +var f1_1 = f1.bind({ x: 0 })();// ok +var f1_2: string = f1.bind({ x: 0 })();// error, number -> string +var f1_3 = f1.bind({ x: "" })();// error, string -> number +// TODO make this error blame the call site, rather than the function body +var f1_4 = f1();// error, (global object).x +/* arrow functions bind this at point of definition */ +/* top level arrow functions bind this to global object */ +var a1 = () => { + return this.x; +}; +var ax = a1();// error, (this:mixed).x +/* nested arrows bind enclosing this (which may itself rebind) */ +function f2(): number { + var a2 = () => { + return this.x; + }; + return a2(); +} +var f2_1 = f2.bind({ x: 0 })();// ok +var f2_2: string = f2.bind({ x: 0 })();// error, number -> string +var f2_3 = f2.bind({ x: "" })();// error, string -> number +// TODO make this error blame the call site, rather than the function body +var f2_4 = f2();// error, (global object).x +(this: void); +module.exports = true; + +" +`; + +exports[`test arrows.js 1`] = ` +"class C { + foo() { + return () => { return this.bar(); }; // OK, since this: C + } + bar() { return this; } // return type is C +} +var c = new C; +var f = c.foo(); +var i = f(); // OK +(i: C); // OK + +class D extends C { } +var d = new D; +var g = d.foo(); +var j = g(); // OK +(j: D); // error, since return type of bar is C, not the type of \`this\` + +class E { + foo(x: number) { } +} +class F extends E { + foo() { // OK to override with generalization + (() => { + super.foo(""); // find super method, error due to incorrect arg + })(); + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class C { + foo() { + return () => { + return this.bar(); + };// OK, since this: C + } + bar() { + return this; + }// return type is C +} +var c = new C(); +var f = c.foo(); +var i = f();// OK +(i: C);// OK +class D extends C {} +var d = new D(); +var g = d.foo(); +var j = g();// OK +(j: D);// error, since return type of bar is C, not the type of \`this\` +class E { + foo(x: number) { + + } +} +class F extends E { + foo() { + // OK to override with generalization + (() => { + super.foo("");// find super method, error due to incorrect arg + })(); + } +} + +" +`; diff --git a/tests/this/arrows.js b/tests/this/arrows.js new file mode 100644 index 000000000000..2217bf0b7d56 --- /dev/null +++ b/tests/this/arrows.js @@ -0,0 +1,27 @@ +class C { + foo() { + return () => { return this.bar(); }; // OK, since this: C + } + bar() { return this; } // return type is C +} +var c = new C; +var f = c.foo(); +var i = f(); // OK +(i: C); // OK + +class D extends C { } +var d = new D; +var g = d.foo(); +var j = g(); // OK +(j: D); // error, since return type of bar is C, not the type of `this` + +class E { + foo(x: number) { } +} +class F extends E { + foo() { // OK to override with generalization + (() => { + super.foo(""); // find super method, error due to incorrect arg + })(); + } +} diff --git a/tests/this/jsfmt.spec.js b/tests/this/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/this/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/this_ctor/__snapshots__/jsfmt.spec.js.snap b/tests/this_ctor/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..2c702518c36a --- /dev/null +++ b/tests/this_ctor/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,36 @@ +exports[`test test.js 1`] = ` +"class A { + n: number; + constructor(n: number) { + this.n = n; + } + clone(): A { + return new this.constructor(this.n); + } + badClone(): number { + return new this.constructor(this.n); // Error A ~> number + } +} + +var a1 = new A(1); +var a2: A = new a1.constructor(2); +var a3: A = a2.clone(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class A { + n: number; + constructor(n: number) { + this.n = n; + } + clone(): A { + return new this.constructor(this.n); + } + badClone(): number { + return new this.constructor(this.n);// Error A ~> number + } +} +var a1 = new A(1); +var a2: A = new a1.constructor(2); +var a3: A = a2.clone(); + +" +`; diff --git a/tests/this_ctor/jsfmt.spec.js b/tests/this_ctor/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/this_ctor/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/this_ctor/test.js b/tests/this_ctor/test.js new file mode 100644 index 000000000000..cbcc6e218d1f --- /dev/null +++ b/tests/this_ctor/test.js @@ -0,0 +1,16 @@ +class A { + n: number; + constructor(n: number) { + this.n = n; + } + clone(): A { + return new this.constructor(this.n); + } + badClone(): number { + return new this.constructor(this.n); // Error A ~> number + } +} + +var a1 = new A(1); +var a2: A = new a1.constructor(2); +var a3: A = a2.clone(); diff --git a/tests/this_type/__snapshots__/jsfmt.spec.js.snap b/tests/this_type/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..887bf7880742 --- /dev/null +++ b/tests/this_type/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,474 @@ +exports[`test class_expr.js 1`] = ` +"/* @flow */ +// issue #1191 + +const Thing = class Thing { + zark() { + this.x = 123; // error: property not found (must be declared) + } +}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +// issue #1191 +const Thing = class Thing { + zark() { + this.x = 123;// error: property not found (must be declared) + } +}; + +" +`; + +exports[`test contra.js 1`] = ` +"// Counterexample with contravariant this type + +class C { + next: this; // error (see below for exploit): \`this\` should only appear in + // covariant positions +} + +class D extends C { } + +var d = new D(); +(d: C).next = new C; +(d.next: D); // sneaky + +class A { + foo(that: X) { } // error: can\'t hide contravariance using a bound +} + +class B extends A { + foo(that: Y) { } // error (see above, catches hidden override) +} + +// covariance checks on this type in invariant positions + +class Invariant { + out_object(): { _: this } { return { _: this }; } + in_object(_: { _: this }) { } + inout_object: { _: this }; + + out_array(): Array { return [this]; } + in_array(_: Array) { } + inout_array: Array; +} + +// covariance checks on this type as type args + +class Misc { + // Set has invariant X + out_set(): Set { return new Set().add(this); } + in_set(_: Set) { } + inout_set: Set; + + // Promise has covariant X + async out_promise(): Promise { return this; } + in_promise(_: Promise) { } + inout_promise: Promise; + + // Generator has covariant X, covariant Y, contravariant Z + *out_generator(): Generator { + yield this; + return this; + } + in_generator(_: Generator) { } + inout_generator: Generator; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test export.js 1`] = ` +"export class A1 { + foo(): this { return this; } + bar(): this { return this; } +} + +export class A2 { + foo(): this { return this; } + bar(): this { return this; } + qux(x: X): X { return x; } +} + +export class A3 extends A2 {} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1172:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test generics.js 1`] = ` +"class Generic { + clone(): Generic { return this; } +} + +class Implicit { arg: X; val: X; } +class ImplicitNumber extends Implicit { arg: number; } + +(new ImplicitNumber().val: string) // error: number ~> string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1172:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test import.js 1`] = ` +"// Check that imports are handled properly with this types + +import { A1 } from \'./export\'; +import type { A2 } from \'./export\'; +import { A3 } from \'./export\'; + +class B1 extends A1 { + foo(): B1 { return new B1(); } // error +} + +(new B1().bar(): B1); // OK + +class B3 extends A3 { + foo(): B3 { return new B3(); } // error +} + +(new B3().bar(): B3<*>); // OK +(new B3().qux(0): string); // error + +(new B3().bar(): A2<*>); // OK +((new B3().bar(): B3): A2); // error +((new B3(): A2).qux(0): string); // error +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1172:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test interface.js 1`] = ` +"interface I { xs: Array; } +interface J { f(): J; } +class C { + xs: Array; + f(): C { return this; } +} +function foo(c: C): I { return c; } +function bar(c: C): J { return c; } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1391 + fromString(\", \").join(path.map(print, \"extends\")) + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1391:28) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:238:16 + at FastPath.call (/src/fast-path.js:113:16) +" +`; + +exports[`test lib_client.js 1`] = ` +"(new DoublyLinkedList().prev(): DoublyLinkedList); +(new DoublyLinkedList().next(): DoublyLinkedList) + +var MiniImmutable = require(\"mini-immutable\"); +class C { + map: MiniImmutable.OrderedMap; + update() { + this.map = this.map.set(0,\"\"); + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test self.js 1`] = ` +"class A { + foo() { return this; } // return of foo is not annotated to get around + // substituting this below + bar(): this { return new A().foo(); } // same as returning : A, so error + qux(): this { return this.bar(); } // OK (don\'t cascade errors) +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class A { + foo() { + return this; + }// return of foo is not annotated to get around// substituting this below + bar(): this { + return new A().foo(); + }// same as returning : A, so error + qux(): this { + return this.bar(); + }// OK (don\'t cascade errors) +} + +" +`; + +exports[`test statics.js 1`] = ` +"// supporting \`this\` type in statics + +class A { + static make(): this { // factory method, whose return type \`this\` (still) + // describes instances of A or subclasses of A: the + // meaning of the \`this\` type is not changed simply by + // switching into a static context + return new this; // but in a static context, the value \`this\` is bound to + // the class, instead of instances of the class + } +} +class B extends A { } // inherits statics method too, with \`this\` bound to the class + +(A.make(): A); // OK +(B.make(): B); // OK +(B.make(): A); // OK +(A.make(): B); // error +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// supporting \`this\` type in statics +class A { + make(): this { + // factory method, whose return type \`this\` (still) + // describes instances of A or subclasses of A: the + // meaning of the \`this\` type is not changed simply by + // switching into a static context + return new this();// but in a static context, the value \`this\` is bound to// the class, instead of instances of the class + } +} +class B extends A {}// inherits statics method too, with \`this\` bound to the class +(A.make(): A);// OK +(B.make(): B);// OK +(B.make(): A);// OK +(A.make(): B);// error + +" +`; + +exports[`test test.js 1`] = ` +"// Examples without \`this\` types (compare with examples below) + +class Base { + foo() { return this; } + qux() { return new Base; } + + bar() { return this; } + bar_caller() { return this.bar(); } +} + +class Inherit extends Base { } + +class Override extends Base { + foo() { return this; } // OK + qux() { return this; } // OK, too + + bar() { return new Override; } // OK (cf. error below) +} + +class InheritOverride extends Override { } + +(new Inherit().foo(): Base); +(new Inherit().foo(): Inherit); // error (cf. OK below) +((new Inherit(): Base).foo(): Base); +(new Override().foo(): Base); +(new Override().foo(): Override); // OK +((new Override(): Base).foo(): Base); + +(new InheritOverride().bar_caller(): InheritOverride); // error + // blame flips below + +// Examples with \`this\` types (compare with examples above) + +class Base2 { + foo(): this { return this; } + qux(): Base2 { return new Base2; } + + bar(): this { return this; } + bar_caller(): this { return this.bar(); } + + corge(that: this) { } + grault(that: Base2) { } +} + +class Inherit2 extends Base2 { } + +class Override2 extends Base2 { + foo(): this { return this; } // OK + qux(): this { return this; } // OK, too + + bar(): Override2 { return new Override2; } // error (cf. OK above) + // see exploit below + + corge(that: this) { } // error + // see exploit below + grault(that: this) { } // error, too +} + +class InheritOverride2 extends Override2 { } + +(new Inherit2().foo(): Base2); +(new Inherit2().foo(): Inherit2); // OK (cf. error above) +((new Inherit2(): Base2).foo(): Base2); +(new Override2().foo(): Base2); +(new Override2().foo(): Override2); // OK +((new Override2(): Base2).foo(): Base2); + +(new InheritOverride2().bar_caller(): InheritOverride2); // exploits error above + +(new Override2(): Base2).corge(new Base2()); // exploits error above +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Examples without \`this\` types (compare with examples below) +class Base { + foo() { + return this; + } + qux() { + return new Base(); + } + bar() { + return this; + } + bar_caller() { + return this.bar(); + } +} +class Inherit extends Base {} +class Override extends Base { + foo() { + return this; + }// OK + qux() { + return this; + }// OK, too + bar() { + return new Override(); + }// OK (cf. error below) +} +class InheritOverride extends Override {} +(new Inherit().foo(): Base); +(new Inherit().foo(): Inherit);// error (cf. OK below) +((new Inherit(): Base).foo(): Base); +(new Override().foo(): Base); +(new Override().foo(): Override);// OK +((new Override(): Base).foo(): Base); +(new InheritOverride().bar_caller(): InheritOverride);// error// blame flips below +// Examples with \`this\` types (compare with examples above) +class Base2 { + foo(): this { + return this; + } + qux(): Base2 { + return new Base2(); + } + bar(): this { + return this; + } + bar_caller(): this { + return this.bar(); + } + corge(that: this) { + + } + grault(that: Base2) { + + } +} +class Inherit2 extends Base2 {} +class Override2 extends Base2 { + foo(): this { + return this; + }// OK + qux(): this { + return this; + }// OK, too + bar(): Override2 { + return new Override2(); + }// error (cf. OK above)// see exploit below + corge(that: this) { + + }// error// see exploit below + grault(that: this) { + + }// error, too +} +class InheritOverride2 extends Override2 {} +(new Inherit2().foo(): Base2); +(new Inherit2().foo(): Inherit2);// OK (cf. error above) +((new Inherit2(): Base2).foo(): Base2); +(new Override2().foo(): Base2); +(new Override2().foo(): Override2);// OK +((new Override2(): Base2).foo(): Base2); +(new InheritOverride2().bar_caller(): InheritOverride2);// exploits error above +(new Override2(): Base2).corge(new Base2());// exploits error above + +" +`; diff --git a/tests/this_type/class_expr.js b/tests/this_type/class_expr.js new file mode 100644 index 000000000000..9001dfc7b639 --- /dev/null +++ b/tests/this_type/class_expr.js @@ -0,0 +1,8 @@ +/* @flow */ +// issue #1191 + +const Thing = class Thing { + zark() { + this.x = 123; // error: property not found (must be declared) + } +}; diff --git a/tests/this_type/contra.js b/tests/this_type/contra.js new file mode 100644 index 000000000000..1bb3c3397614 --- /dev/null +++ b/tests/this_type/contra.js @@ -0,0 +1,54 @@ +// Counterexample with contravariant this type + +class C { + next: this; // error (see below for exploit): `this` should only appear in + // covariant positions +} + +class D extends C { } + +var d = new D(); +(d: C).next = new C; +(d.next: D); // sneaky + +class A { + foo(that: X) { } // error: can't hide contravariance using a bound +} + +class B extends A { + foo(that: Y) { } // error (see above, catches hidden override) +} + +// covariance checks on this type in invariant positions + +class Invariant { + out_object(): { _: this } { return { _: this }; } + in_object(_: { _: this }) { } + inout_object: { _: this }; + + out_array(): Array { return [this]; } + in_array(_: Array) { } + inout_array: Array; +} + +// covariance checks on this type as type args + +class Misc { + // Set has invariant X + out_set(): Set { return new Set().add(this); } + in_set(_: Set) { } + inout_set: Set; + + // Promise has covariant X + async out_promise(): Promise { return this; } + in_promise(_: Promise) { } + inout_promise: Promise; + + // Generator has covariant X, covariant Y, contravariant Z + *out_generator(): Generator { + yield this; + return this; + } + in_generator(_: Generator) { } + inout_generator: Generator; +} diff --git a/tests/this_type/export.js b/tests/this_type/export.js new file mode 100644 index 000000000000..57303de679e7 --- /dev/null +++ b/tests/this_type/export.js @@ -0,0 +1,12 @@ +export class A1 { + foo(): this { return this; } + bar(): this { return this; } +} + +export class A2 { + foo(): this { return this; } + bar(): this { return this; } + qux(x: X): X { return x; } +} + +export class A3 extends A2 {} diff --git a/tests/this_type/generics.js b/tests/this_type/generics.js new file mode 100644 index 000000000000..0514ed85ae1f --- /dev/null +++ b/tests/this_type/generics.js @@ -0,0 +1,8 @@ +class Generic { + clone(): Generic { return this; } +} + +class Implicit { arg: X; val: X; } +class ImplicitNumber extends Implicit { arg: number; } + +(new ImplicitNumber().val: string) // error: number ~> string diff --git a/tests/this_type/import.js b/tests/this_type/import.js new file mode 100644 index 000000000000..b742e07d7a68 --- /dev/null +++ b/tests/this_type/import.js @@ -0,0 +1,22 @@ +// Check that imports are handled properly with this types + +import { A1 } from './export'; +import type { A2 } from './export'; +import { A3 } from './export'; + +class B1 extends A1 { + foo(): B1 { return new B1(); } // error +} + +(new B1().bar(): B1); // OK + +class B3 extends A3 { + foo(): B3 { return new B3(); } // error +} + +(new B3().bar(): B3<*>); // OK +(new B3().qux(0): string); // error + +(new B3().bar(): A2<*>); // OK +((new B3().bar(): B3): A2); // error +((new B3(): A2).qux(0): string); // error diff --git a/tests/this_type/interface.js b/tests/this_type/interface.js new file mode 100644 index 000000000000..0c1de6a4c9a2 --- /dev/null +++ b/tests/this_type/interface.js @@ -0,0 +1,8 @@ +interface I { xs: Array; } +interface J { f(): J; } +class C { + xs: Array; + f(): C { return this; } +} +function foo(c: C): I { return c; } +function bar(c: C): J { return c; } diff --git a/tests/this_type/jsfmt.spec.js b/tests/this_type/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/this_type/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/this_type/lib/__snapshots__/jsfmt.spec.js.snap b/tests/this_type/lib/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..04cd629de70f --- /dev/null +++ b/tests/this_type/lib/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,38 @@ +exports[`test decl.js 1`] = ` +"// The following declare classes use \`this\` types effectively to avoid type +// errors in ../lib_client.js. If support for \`this\` types in declare classes +// is disabled for perf reasons, these will produce warnings. + +declare class LinkedList { + next(): this; +} +declare class DoublyLinkedList extends LinkedList { + prev(): this; +} + +declare module \"mini-immutable\" { + declare class Map { + set(key: K, value: V): this; // more precise than Map (see below) + } + declare class OrderedMap extends Map { + // inherits set method returning OrderedMap instead of Map + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/this_type/lib/decl.js b/tests/this_type/lib/decl.js new file mode 100644 index 000000000000..f3f3e398a68e --- /dev/null +++ b/tests/this_type/lib/decl.js @@ -0,0 +1,19 @@ +// The following declare classes use `this` types effectively to avoid type +// errors in ../lib_client.js. If support for `this` types in declare classes +// is disabled for perf reasons, these will produce warnings. + +declare class LinkedList { + next(): this; +} +declare class DoublyLinkedList extends LinkedList { + prev(): this; +} + +declare module "mini-immutable" { + declare class Map { + set(key: K, value: V): this; // more precise than Map (see below) + } + declare class OrderedMap extends Map { + // inherits set method returning OrderedMap instead of Map + } +} diff --git a/tests/this_type/lib/jsfmt.spec.js b/tests/this_type/lib/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/this_type/lib/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/this_type/lib_client.js b/tests/this_type/lib_client.js new file mode 100644 index 000000000000..e646c7d721ab --- /dev/null +++ b/tests/this_type/lib_client.js @@ -0,0 +1,10 @@ +(new DoublyLinkedList().prev(): DoublyLinkedList); +(new DoublyLinkedList().next(): DoublyLinkedList) + +var MiniImmutable = require("mini-immutable"); +class C { + map: MiniImmutable.OrderedMap; + update() { + this.map = this.map.set(0,""); + } +} diff --git a/tests/this_type/self.js b/tests/this_type/self.js new file mode 100644 index 000000000000..68a8db1e0f2d --- /dev/null +++ b/tests/this_type/self.js @@ -0,0 +1,6 @@ +class A { + foo() { return this; } // return of foo is not annotated to get around + // substituting this below + bar(): this { return new A().foo(); } // same as returning : A, so error + qux(): this { return this.bar(); } // OK (don't cascade errors) +} diff --git a/tests/this_type/statics.js b/tests/this_type/statics.js new file mode 100644 index 000000000000..c2ed665599c6 --- /dev/null +++ b/tests/this_type/statics.js @@ -0,0 +1,17 @@ +// supporting `this` type in statics + +class A { + static make(): this { // factory method, whose return type `this` (still) + // describes instances of A or subclasses of A: the + // meaning of the `this` type is not changed simply by + // switching into a static context + return new this; // but in a static context, the value `this` is bound to + // the class, instead of instances of the class + } +} +class B extends A { } // inherits statics method too, with `this` bound to the class + +(A.make(): A); // OK +(B.make(): B); // OK +(B.make(): A); // OK +(A.make(): B); // error diff --git a/tests/this_type/test.js b/tests/this_type/test.js new file mode 100644 index 000000000000..a47b3ca57ca8 --- /dev/null +++ b/tests/this_type/test.js @@ -0,0 +1,70 @@ +// Examples without `this` types (compare with examples below) + +class Base { + foo() { return this; } + qux() { return new Base; } + + bar() { return this; } + bar_caller() { return this.bar(); } +} + +class Inherit extends Base { } + +class Override extends Base { + foo() { return this; } // OK + qux() { return this; } // OK, too + + bar() { return new Override; } // OK (cf. error below) +} + +class InheritOverride extends Override { } + +(new Inherit().foo(): Base); +(new Inherit().foo(): Inherit); // error (cf. OK below) +((new Inherit(): Base).foo(): Base); +(new Override().foo(): Base); +(new Override().foo(): Override); // OK +((new Override(): Base).foo(): Base); + +(new InheritOverride().bar_caller(): InheritOverride); // error + // blame flips below + +// Examples with `this` types (compare with examples above) + +class Base2 { + foo(): this { return this; } + qux(): Base2 { return new Base2; } + + bar(): this { return this; } + bar_caller(): this { return this.bar(); } + + corge(that: this) { } + grault(that: Base2) { } +} + +class Inherit2 extends Base2 { } + +class Override2 extends Base2 { + foo(): this { return this; } // OK + qux(): this { return this; } // OK, too + + bar(): Override2 { return new Override2; } // error (cf. OK above) + // see exploit below + + corge(that: this) { } // error + // see exploit below + grault(that: this) { } // error, too +} + +class InheritOverride2 extends Override2 { } + +(new Inherit2().foo(): Base2); +(new Inherit2().foo(): Inherit2); // OK (cf. error above) +((new Inherit2(): Base2).foo(): Base2); +(new Override2().foo(): Base2); +(new Override2().foo(): Override2); // OK +((new Override2(): Base2).foo(): Base2); + +(new InheritOverride2().bar_caller(): InheritOverride2); // exploits error above + +(new Override2(): Base2).corge(new Base2()); // exploits error above diff --git a/tests/throw/__snapshots__/jsfmt.spec.js.snap b/tests/throw/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..14240dc16502 --- /dev/null +++ b/tests/throw/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,46 @@ +exports[`test test.js 1`] = ` +"/** + * @flow + */ + +function f(): number { + throw new Error(); // OK to not return +} + +function g(a: ?string) { + if (a == null) { + throw new Error(); + } + return a*1; // a is not null +} + +function h(x: number): string { + if (x) { + return 'foo'; + } else { + throw new Error(); + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +function f(): number { + throw new Error();// OK to not return +} +function g(a: ?string) { + if (a == null) { + throw new Error(); + } + return a * 1;// a is not null +} +function h(x: number): string { + if (x) { + return "foo"; + } else { + throw new Error(); + } +} + +" +`; diff --git a/tests/throw/jsfmt.spec.js b/tests/throw/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/throw/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/throw/test.js b/tests/throw/test.js new file mode 100644 index 000000000000..a77bd4752c38 --- /dev/null +++ b/tests/throw/test.js @@ -0,0 +1,22 @@ +/** + * @flow + */ + +function f(): number { + throw new Error(); // OK to not return +} + +function g(a: ?string) { + if (a == null) { + throw new Error(); + } + return a*1; // a is not null +} + +function h(x: number): string { + if (x) { + return 'foo'; + } else { + throw new Error(); + } +} diff --git a/tests/traces/Traces.js b/tests/traces/Traces.js new file mode 100644 index 000000000000..50dc2194f4c5 --- /dev/null +++ b/tests/traces/Traces.js @@ -0,0 +1,20 @@ +// arg/param type mismatch on arg 0 +function g0(y:string) { } +function f0(x) { g0(x) } +f0(0); + +// ...on arg n +function g1(a:string, b:string) { } +function f1(x, y) { g1(x, y) } +f1("hey", 0); + +// h/o call with function expr +function g2(ylam: (s:string) => number) { } +function f2(xlam) { g2(xlam) } +f2(function(x) { return x * x }); + +// h/o call with function def +function g3(ylam: (s:string) => number) { } +function f3(xlam) { g3(xlam) } +function double(n) { return n * 2 } +f3(double); diff --git a/tests/traces/Traces2.js b/tests/traces/Traces2.js new file mode 100644 index 000000000000..89089d9a4f2f --- /dev/null +++ b/tests/traces/Traces2.js @@ -0,0 +1,19 @@ +// @flow + +var React = require('react'); + +var A = React.createClass({ + propTypes: { foo: React.PropTypes.string.isRequired } +}); + +var B = React.createClass({ + propTypes: { bar: React.PropTypes.string.isRequired } +}); + +function f(b): React.Element<*> { + if (b) { + return ; + } else { + return ; + } +} diff --git a/tests/traces/__snapshots__/jsfmt.spec.js.snap b/tests/traces/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..18c2d489deab --- /dev/null +++ b/tests/traces/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,78 @@ +exports[`test Traces.js 1`] = ` +"// arg/param type mismatch on arg 0 +function g0(y:string) { } +function f0(x) { g0(x) } +f0(0); + +// ...on arg n +function g1(a:string, b:string) { } +function f1(x, y) { g1(x, y) } +f1(\"hey\", 0); + +// h/o call with function expr +function g2(ylam: (s:string) => number) { } +function f2(xlam) { g2(xlam) } +f2(function(x) { return x * x }); + +// h/o call with function def +function g3(ylam: (s:string) => number) { } +function f3(xlam) { g3(xlam) } +function double(n) { return n * 2 } +f3(double); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test Traces2.js 1`] = ` +"// @flow + +var React = require(\'react\'); + +var A = React.createClass({ + propTypes: { foo: React.PropTypes.string.isRequired } +}); + +var B = React.createClass({ + propTypes: { bar: React.PropTypes.string.isRequired } +}); + +function f(b): React.Element<*> { + if (b) { + return ; + } else { + return ; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/traces/jsfmt.spec.js b/tests/traces/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/traces/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/traits/__snapshots__/jsfmt.spec.js.snap b/tests/traits/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..65e29e8fc244 --- /dev/null +++ b/tests/traits/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,54 @@ +exports[`test test.js 1`] = ` +"declare class Foo extends Qux mixins Bar { + // KeyedCollection <: Collection + // ...KeyedIterable +} +declare class Bar extends Baz { + // KeyedIterable <: Iterable + y: T +} +declare class Qux extends Baz { + // Collection <: Iterable + y: T, z: T +} +declare class Baz { + // Iterable + x: T +} + +((new Foo).x: number); // error: Qux wins +((new Foo).y: string); // error: Bar wins +((new Foo).z: number); // error: Qux wins +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +declare class Foo {} +declare class Bar { // KeyedIterable <: Iterable y: T } +declare class Qux { // Collection <: Iterable y: T, z: T } +declare class Baz { // Iterable x: T } +(new Foo().x: number);// error: Qux wins +(new Foo().y: string);// error: Bar wins +(new Foo().z: number);// error: Qux wins + +" +`; + +exports[`test test2.js 1`] = ` +"declare interface I { } +declare class C mixins I { } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1391 + fromString(\", \").join(path.map(print, \"extends\")) + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1391:28) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at /src/printer.js:1601:18 + at FastPath.map (/src/fast-path.js:167:19) + at printStatementSequence (/src/printer.js:1586:8) + at /src/printer.js:238:16 + at FastPath.call (/src/fast-path.js:113:16) +" +`; diff --git a/tests/traits/jsfmt.spec.js b/tests/traits/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/traits/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/traits/test.js b/tests/traits/test.js new file mode 100644 index 000000000000..569907ac1a9c --- /dev/null +++ b/tests/traits/test.js @@ -0,0 +1,20 @@ +declare class Foo extends Qux mixins Bar { + // KeyedCollection <: Collection + // ...KeyedIterable +} +declare class Bar extends Baz { + // KeyedIterable <: Iterable + y: T +} +declare class Qux extends Baz { + // Collection <: Iterable + y: T, z: T +} +declare class Baz { + // Iterable + x: T +} + +((new Foo).x: number); // error: Qux wins +((new Foo).y: string); // error: Bar wins +((new Foo).z: number); // error: Qux wins diff --git a/tests/traits/test2.js b/tests/traits/test2.js new file mode 100644 index 000000000000..244d25a93ed5 --- /dev/null +++ b/tests/traits/test2.js @@ -0,0 +1,2 @@ +declare interface I { } +declare class C mixins I { } diff --git a/tests/try/__snapshots__/jsfmt.spec.js.snap b/tests/try/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..1174df0a3b97 --- /dev/null +++ b/tests/try/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,567 @@ +exports[`test abnormals.js 1`] = ` +"/* @flow */ + +/* This test documents an issue we used to have with merging the environment of + * the try block and the catch block. The error variable, when inspected and in + * the presence of an abnormal, would sometimes kind of leak. It would hit an + * abnormal. It was weird. + */ +function foo() { + try { + } catch(error) { + if (error.foo === 4) { + throw error; + } + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +/* This test documents an issue we used to have with merging the environment of + * the try block and the catch block. The error variable, when inspected and in + * the presence of an abnormal, would sometimes kind of leak. It would hit an + * abnormal. It was weird. + */ +function foo() { + try { + + } catch (error) { + if (error.foo === 4) { + throw error; + } + } +} + +" +`; + +exports[`test init.js 1`] = ` +"/*** + * test initialization tracking of hoisted stuff + * @flow + */ + +// for illustrative purposes only - Flow considers a throw possible +// anywhere within a block +function might_throw() {} + +// local use of annotated var within try is ok +function f() { + try { + var x:number = 0; + var y:number = x; + } catch (e) { + } +} + +// and within catch +function f() { + try { + } catch (e) { + var x:number = 0; + var y:number = x; + } +} + +// but not across try/catch +function f() { + try { + might_throw(); + var x:number = 0; + } catch (e) { + var y:number = x; // error + } +} + +// or try/finally +function f() { + try { + might_throw(); + var x:number = 0; + } finally { + var y:number = x; // error + } +} + +// or catch/finally +function f() { + try { + } catch (e) { + var x:number = 0; + } finally { + var y:number = x; // error + } +} + +// or try/catch/finally if init doesn't dominate +function f() { + try { + var x:number = 0; + } catch (e) { + might_throw(); + var x:number = 0; + } finally { + var y:number = x; // error + } +} + +// post-use ok because init dominates here +function f() { + try { + var x:number = 0; + } catch (e) { + might_throw(); // ...but if so, suffix is not reached + var x:number = 0; + } + var y:number = x; +} + +// and here +function f() { + try { + } catch (e) { + } finally { + might_throw(); // ...but if so, suffix is not reached + var x:number = 0; + } + var y:number = x; +} + +// and here +function f() { + try { + var x:number; + } catch (e) { + } finally { + might_throw(); // ...but if so, suffix is not reached + x = 0; + } + var y:number = x; +} + +// and here, thank you JS for the wonder that is hoisting +function f() { + try { + } catch (e) { + var x:number; + } finally { + might_throw(); // ...but if so, suffix is not reached + x = 0; + } + var y:number = x; +} + +// error if used prior to init +function f() { + var y:number = x; // error + try { + var x:number = 0; + } catch (e) { + } +} + +// another non-dominated post +function f() { + try { + var x:number = 0; + } catch (e) { + } + var y:number = x; // error +} + +// ditto +function f() { + try { + } catch (e) { + var x:number = 0; + } + var y:number = x; // error +} + +// ditto +function f(b) { + try { + var x:number; + if (b) { + throw new Error(); + } + x = 0; + } catch (e) { + } + var y:number = x; // error +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/*** + * test initialization tracking of hoisted stuff + * @flow + */ +// for illustrative purposes only - Flow considers a throw possible +// anywhere within a block +function might_throw() { + +} +// local use of annotated var within try is ok +function f() { + try { + var x: number = 0; + var y: number = x; + } catch (e) { + + } +} +// and within catch +function f() { + try { + + } catch (e) { + var x: number = 0; + var y: number = x; + } +} +// but not across try/catch +function f() { + try { + might_throw(); + var x: number = 0; + } catch (e) { + var y: number = x;// error + } +} +// or try/finally +function f() { + try { + might_throw(); + var x: number = 0; + } finally { + var y: number = x;// error + } +} +// or catch/finally +function f() { + try { + + } catch (e) { + var x: number = 0; + } finally { + var y: number = x;// error + } +} +// or try/catch/finally if init doesn't dominate +function f() { + try { + var x: number = 0; + } catch (e) { + might_throw(); + var x: number = 0; + } finally { + var y: number = x;// error + } +} +// post-use ok because init dominates here +function f() { + try { + var x: number = 0; + } catch (e) { + might_throw();// ...but if so, suffix is not reached + var x: number = 0; + } + var y: number = x; +} +// and here +function f() { + try { + + } catch (e) { + + } finally { + might_throw();// ...but if so, suffix is not reached + var x: number = 0; + } + var y: number = x; +} +// and here +function f() { + try { + var x: number; + } catch (e) { + + } finally { + might_throw();// ...but if so, suffix is not reached + x = 0; + } + var y: number = x; +} +// and here, thank you JS for the wonder that is hoisting +function f() { + try { + + } catch (e) { + var x: number; + } finally { + might_throw();// ...but if so, suffix is not reached + x = 0; + } + var y: number = x; +} +// error if used prior to init +function f() { + var y: number = x;// error + try { + var x: number = 0; + } catch (e) { + + } +} +// another non-dominated post +function f() { + try { + var x: number = 0; + } catch (e) { + + } + var y: number = x;// error +} +// ditto +function f() { + try { + + } catch (e) { + var x: number = 0; + } + var y: number = x;// error +} +// ditto +function f(b) { + try { + var x: number; + if (b) { + throw new Error(); + } + x = 0; + } catch (e) { + + } + var y: number = x;// error +} + +" +`; + +exports[`test return.js 1`] = ` +"/** + * @flow + */ + +function foo(x: ?number): string { + try { + } catch (e) { + return 'bar'; + } + console.log(); + return 'foo'; +} + +function bar(): string { + try { + return 'foo'; + } catch (e) { + return 'bar'; + } +} + +function baz(): string { + try { + throw new Error("foo"); + } catch (e) { + return "foo"; + } + return "bar"; // unreachable +} + +function qux(): string { + try { + throw new Error("foo"); + } catch (e) { + } + console.log(); + return 'bar'; +} + +function quux(): string { + try { + return qux(); + } catch (e) { + } + return 'bar'; +} + +function bliffl(): string { + try { + throw new Error("foo"); + } catch (e) { + return "foo"; + } finally { + return "bar"; + } +} + +function corge(): string { + try { + return 'foo'; + } catch (e) { + throw new Error('bar'); + } + bar(); // unreachable +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +function foo(x: ?number): string { + try { + + } catch (e) { + return "bar"; + } + console.log(); + return "foo"; +} +function bar(): string { + try { + return "foo"; + } catch (e) { + return "bar"; + } +} +function baz(): string { + try { + throw new Error("foo"); + } catch (e) { + return "foo"; + } + return "bar";// unreachable +} +function qux(): string { + try { + throw new Error("foo"); + } catch (e) { + + } + console.log(); + return "bar"; +} +function quux(): string { + try { + return qux(); + } catch (e) { + + } + return "bar"; +} +function bliffl(): string { + try { + throw new Error("foo"); + } catch (e) { + return "foo"; + } finally { + return "bar"; + } +} +function corge(): string { + try { + return "foo"; + } catch (e) { + throw new Error("bar"); + } + bar();// unreachable +} + +" +`; + +exports[`test test.js 1`] = ` +"/*** + * test env state tracking thru try/catch/finally + * @flow + */ + +function foo() { + var x = 0; + var y; + try { + x = ""; + } catch(e) { + x = false; + throw -1; + } finally { + y = {}; + } + // here via [try; finally] only. + x(); // string ~/> function call (no num or bool error) + y(); // object ~/> function call (no uninitialized error) +} + +function bar(response) { + var payload; + try { + payload = JSON.parse(response); + } catch (e) { + throw new Error('...'); + } + // here via [try] only. + if (payload.error) { // ok + // ... + } +} + +function qux() { + var x = 5; + try { + throw -1; + } finally { + } + x(); // unreachable +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/*** + * test env state tracking thru try/catch/finally + * @flow + */ +function foo() { + var x = 0; + var y; + try { + x = ""; + } catch (e) { + x = false; + throw -1; + } finally { + y = {}; + } + // here via [try; finally] only. + x();// string ~/> function call (no num or bool error) + y();// object ~/> function call (no uninitialized error) +} +function bar(response) { + var payload; + try { + payload = JSON.parse(response); + } catch (e) { + throw new Error("..."); + } + // here via [try] only. + if (payload.error) { + + } +} +function qux() { + var x = 5; + try { + throw -1; + } finally { + + } + x();// unreachable +} + +" +`; diff --git a/tests/try/abnormals.js b/tests/try/abnormals.js new file mode 100644 index 000000000000..93f26579bfb6 --- /dev/null +++ b/tests/try/abnormals.js @@ -0,0 +1,15 @@ +/* @flow */ + +/* This test documents an issue we used to have with merging the environment of + * the try block and the catch block. The error variable, when inspected and in + * the presence of an abnormal, would sometimes kind of leak. It would hit an + * abnormal. It was weird. + */ +function foo() { + try { + } catch(error) { + if (error.foo === 4) { + throw error; + } + } +} diff --git a/tests/try/init.js b/tests/try/init.js new file mode 100644 index 000000000000..be19b23e1b52 --- /dev/null +++ b/tests/try/init.js @@ -0,0 +1,154 @@ +/*** + * test initialization tracking of hoisted stuff + * @flow + */ + +// for illustrative purposes only - Flow considers a throw possible +// anywhere within a block +function might_throw() {} + +// local use of annotated var within try is ok +function f() { + try { + var x:number = 0; + var y:number = x; + } catch (e) { + } +} + +// and within catch +function f() { + try { + } catch (e) { + var x:number = 0; + var y:number = x; + } +} + +// but not across try/catch +function f() { + try { + might_throw(); + var x:number = 0; + } catch (e) { + var y:number = x; // error + } +} + +// or try/finally +function f() { + try { + might_throw(); + var x:number = 0; + } finally { + var y:number = x; // error + } +} + +// or catch/finally +function f() { + try { + } catch (e) { + var x:number = 0; + } finally { + var y:number = x; // error + } +} + +// or try/catch/finally if init doesn't dominate +function f() { + try { + var x:number = 0; + } catch (e) { + might_throw(); + var x:number = 0; + } finally { + var y:number = x; // error + } +} + +// post-use ok because init dominates here +function f() { + try { + var x:number = 0; + } catch (e) { + might_throw(); // ...but if so, suffix is not reached + var x:number = 0; + } + var y:number = x; +} + +// and here +function f() { + try { + } catch (e) { + } finally { + might_throw(); // ...but if so, suffix is not reached + var x:number = 0; + } + var y:number = x; +} + +// and here +function f() { + try { + var x:number; + } catch (e) { + } finally { + might_throw(); // ...but if so, suffix is not reached + x = 0; + } + var y:number = x; +} + +// and here, thank you JS for the wonder that is hoisting +function f() { + try { + } catch (e) { + var x:number; + } finally { + might_throw(); // ...but if so, suffix is not reached + x = 0; + } + var y:number = x; +} + +// error if used prior to init +function f() { + var y:number = x; // error + try { + var x:number = 0; + } catch (e) { + } +} + +// another non-dominated post +function f() { + try { + var x:number = 0; + } catch (e) { + } + var y:number = x; // error +} + +// ditto +function f() { + try { + } catch (e) { + var x:number = 0; + } + var y:number = x; // error +} + +// ditto +function f(b) { + try { + var x:number; + if (b) { + throw new Error(); + } + x = 0; + } catch (e) { + } + var y:number = x; // error +} diff --git a/tests/try/jsfmt.spec.js b/tests/try/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/try/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/try/return.js b/tests/try/return.js new file mode 100644 index 000000000000..eafe017bb3e7 --- /dev/null +++ b/tests/try/return.js @@ -0,0 +1,65 @@ +/** + * @flow + */ + +function foo(x: ?number): string { + try { + } catch (e) { + return 'bar'; + } + console.log(); + return 'foo'; +} + +function bar(): string { + try { + return 'foo'; + } catch (e) { + return 'bar'; + } +} + +function baz(): string { + try { + throw new Error("foo"); + } catch (e) { + return "foo"; + } + return "bar"; // unreachable +} + +function qux(): string { + try { + throw new Error("foo"); + } catch (e) { + } + console.log(); + return 'bar'; +} + +function quux(): string { + try { + return qux(); + } catch (e) { + } + return 'bar'; +} + +function bliffl(): string { + try { + throw new Error("foo"); + } catch (e) { + return "foo"; + } finally { + return "bar"; + } +} + +function corge(): string { + try { + return 'foo'; + } catch (e) { + throw new Error('bar'); + } + bar(); // unreachable +} diff --git a/tests/try/test.js b/tests/try/test.js new file mode 100644 index 000000000000..8ed40eec4766 --- /dev/null +++ b/tests/try/test.js @@ -0,0 +1,42 @@ +/*** + * test env state tracking thru try/catch/finally + * @flow + */ + +function foo() { + var x = 0; + var y; + try { + x = ""; + } catch(e) { + x = false; + throw -1; + } finally { + y = {}; + } + // here via [try; finally] only. + x(); // string ~/> function call (no num or bool error) + y(); // object ~/> function call (no uninitialized error) +} + +function bar(response) { + var payload; + try { + payload = JSON.parse(response); + } catch (e) { + throw new Error('...'); + } + // here via [try] only. + if (payload.error) { // ok + // ... + } +} + +function qux() { + var x = 5; + try { + throw -1; + } finally { + } + x(); // unreachable +} diff --git a/tests/tuples/__snapshots__/jsfmt.spec.js.snap b/tests/tuples/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..1e62f945a534 --- /dev/null +++ b/tests/tuples/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,105 @@ +exports[`test array.js 1`] = ` +"// @flow + +function foo(x: Array): [number, ?number] { + return x; // Error, can\'t enforce arity when flowing array to tuple +} + +function foo(x: Array): [number, ?number] { + return [x[0], x[1]]; // OK. This is unsound, but at least arity is enforced +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test optional.js 1`] = ` +"// @flow + +([0, undefined]: [number, ?string]); // Ok, correct arity +([0]: [number, ?string]); // Error, arity is enforced + +([]: [?number, string]); // error, since second element is not marked optional +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1237 + throw new Error(\"unprintable type: \" + JSON.stringify(n.type)); + ^ + +Error: unprintable type: \"TupleTypeAnnotation\" + at genericPrintNoParens (/src/printer.js:1237:13) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test too-few.js 1`] = ` +"/* @flow */ + +function foo(a: [Object, Object]) {} + +foo([ {} ]); // error, too few elements in array passed to a tuple +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1237 + throw new Error(\"unprintable type: \" + JSON.stringify(n.type)); + ^ + +Error: unprintable type: \"TupleTypeAnnotation\" + at genericPrintNoParens (/src/printer.js:1237:13) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test tuples.js 1`] = ` +"var a: [] = []; +var b: [] = [123]; // Error - arity mismatch +var c: [number] = []; // nope +var d: [number, string] = [123,\'duck\']; +var e: [number, string,] = [123,\'duck\']; +var f: [number, string] = [123, 456]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1237 + throw new Error(\"unprintable type: \" + JSON.stringify(n.type)); + ^ + +Error: unprintable type: \"TupleTypeAnnotation\" + at genericPrintNoParens (/src/printer.js:1237:13) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/tuples/array.js b/tests/tuples/array.js new file mode 100644 index 000000000000..08f7a4c0a972 --- /dev/null +++ b/tests/tuples/array.js @@ -0,0 +1,9 @@ +// @flow + +function foo(x: Array): [number, ?number] { + return x; // Error, can't enforce arity when flowing array to tuple +} + +function foo(x: Array): [number, ?number] { + return [x[0], x[1]]; // OK. This is unsound, but at least arity is enforced +} diff --git a/tests/tuples/jsfmt.spec.js b/tests/tuples/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/tuples/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/tuples/optional.js b/tests/tuples/optional.js new file mode 100644 index 000000000000..46764c0fff06 --- /dev/null +++ b/tests/tuples/optional.js @@ -0,0 +1,6 @@ +// @flow + +([0, undefined]: [number, ?string]); // Ok, correct arity +([0]: [number, ?string]); // Error, arity is enforced + +([]: [?number, string]); // error, since second element is not marked optional diff --git a/tests/tuples/too-few.js b/tests/tuples/too-few.js new file mode 100644 index 000000000000..cb8b2d62a094 --- /dev/null +++ b/tests/tuples/too-few.js @@ -0,0 +1,5 @@ +/* @flow */ + +function foo(a: [Object, Object]) {} + +foo([ {} ]); // error, too few elements in array passed to a tuple diff --git a/tests/tuples/tuples.js b/tests/tuples/tuples.js new file mode 100644 index 000000000000..f44f9c75f7fb --- /dev/null +++ b/tests/tuples/tuples.js @@ -0,0 +1,6 @@ +var a: [] = []; +var b: [] = [123]; // Error - arity mismatch +var c: [number] = []; // nope +var d: [number, string] = [123,'duck']; +var e: [number, string,] = [123,'duck']; +var f: [number, string] = [123, 456]; diff --git a/tests/type-at-pos/__snapshots__/jsfmt.spec.js.snap b/tests/type-at-pos/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..08d61143e1bd --- /dev/null +++ b/tests/type-at-pos/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,275 @@ +exports[`test destructuring.js 1`] = ` +"// @flow + +let [x, y] = [1, 2]; + +/** + * Test what happens when the destructuring is unevaluated. In this case, + * \`this\` in a function is unbound, so we never actually find out the type of + * \`this.returnsATuple()\` is; thus, we never evaluate \`b\` and so type-at-pos + * returns EmptyT. + */ +export const X = { + returnsATuple: function(): [number, number] { + return [1, 2]; + }, + + test: function() { + let [a, b] = this.returnsATuple(); + } +}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1237 + throw new Error(\"unprintable type: \" + JSON.stringify(n.type)); + ^ + +Error: unprintable type: \"TupleTypeAnnotation\" + at genericPrintNoParens (/src/printer.js:1237:13) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test function_expressions.js 1`] = ` +"// @flow + +// TODO: type-at-pos between the ()\'s should be () => void +// class X { +// foo(): void {} +// } + +const y = { + bar(): void {} +}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +// TODO: type-at-pos between the ()\'s should be () => void +// class X { +// foo(): void {} +// } +const y = { + bar(): void { + + } +}; + +" +`; + +exports[`test generics.js 1`] = ` +"// @flow + +class C { } +var cn: C = new C; +cn; + +function foo() { return C; } +var D = foo(); +var dn: D = new C; +dn; + +type E = C; +var en: E = new C; +en; + +type F = C; +var fn: F = new C; +fn; + +type O = { x: X }; +var on: O = { x: 0 }; +on; + +type Mono = C; +var mn: Mono = new C; // error: application of non-poly type +mn; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1172:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test import.js 1`] = ` +"// @flow +var num = 42; +function bar() { } +bar(); +module.exports = num; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +var num = 42; +function bar() { + +} +bar(); +module.exports = num; + +" +`; + +exports[`test object_special_cases.js 1`] = ` +"/* @flow */ + +let tests = [ + function() { + let x = {}; + Object.defineProperty(x, \'foo\', { value: \'\' }); + }, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +let tests = [ + function() { + let x = {}; + Object.defineProperty(x, \"foo\", { value: \"\" }); + } +]; + +" +`; + +exports[`test optional.js 1`] = ` +"// @flow + +function foo(x?: string) { + return x; +} + +foo(); + +function bar(obj: { x?: string }) { + return obj.x; +} + +function qux(x?) { + return x; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +function foo(x: string) { + return x; +} +foo(); +function bar(obj: { x?: string }) { + return obj.x; +} +function qux(x) { + return x; +} + +" +`; + +exports[`test predicates.js 1`] = ` +"/* @flow */ + +let x = 0; +if (x == null) {} +if (x == undefined) {} +if (Array.isArray(x)) {} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +let x = 0; +if (x == null) { + +} +if (x == undefined) { + +} +if (Array.isArray(x)) { + +} + +" +`; + +exports[`test react.js 1`] = ` +"import React from \"react\"; +React.createElement; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +import React from \"react\"; +React.createElement; + +" +`; + +exports[`test templates.js 1`] = ` +"/* @flow */ +\`foo bar\`; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +\`foo bar\`; + +" +`; + +exports[`test test.js 1`] = ` +"// @flow +var str = require(\'./import\'); +function foo() { } +foo(); +str + +type Point = [number, string]; +const x:Point = [1, \"foo\"]; +type MyStr = \"cool\"; +const y:MyStr = \"cool\"; +type MyBool = true; +const z:MyBool = true; +type MyNum = 42; +const w:MyNum = 42; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1237 + throw new Error(\"unprintable type: \" + JSON.stringify(n.type)); + ^ + +Error: unprintable type: \"TupleTypeAnnotation\" + at genericPrintNoParens (/src/printer.js:1237:13) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test trycatch.js 1`] = ` +"// @flow + +try { + throw \"foo\"; +} catch (e) {} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +try { + throw \"foo\"; +} catch (e) { + +} + +" +`; diff --git a/tests/type-at-pos/destructuring.js b/tests/type-at-pos/destructuring.js new file mode 100644 index 000000000000..103545e77005 --- /dev/null +++ b/tests/type-at-pos/destructuring.js @@ -0,0 +1,19 @@ +// @flow + +let [x, y] = [1, 2]; + +/** + * Test what happens when the destructuring is unevaluated. In this case, + * `this` in a function is unbound, so we never actually find out the type of + * `this.returnsATuple()` is; thus, we never evaluate `b` and so type-at-pos + * returns EmptyT. + */ +export const X = { + returnsATuple: function(): [number, number] { + return [1, 2]; + }, + + test: function() { + let [a, b] = this.returnsATuple(); + } +}; diff --git a/tests/type-at-pos/function_expressions.js b/tests/type-at-pos/function_expressions.js new file mode 100644 index 000000000000..81df45793802 --- /dev/null +++ b/tests/type-at-pos/function_expressions.js @@ -0,0 +1,10 @@ +// @flow + +// TODO: type-at-pos between the ()'s should be () => void +// class X { +// foo(): void {} +// } + +const y = { + bar(): void {} +}; diff --git a/tests/type-at-pos/generics.js b/tests/type-at-pos/generics.js new file mode 100644 index 000000000000..a08506b22101 --- /dev/null +++ b/tests/type-at-pos/generics.js @@ -0,0 +1,26 @@ +// @flow + +class C { } +var cn: C = new C; +cn; + +function foo() { return C; } +var D = foo(); +var dn: D = new C; +dn; + +type E = C; +var en: E = new C; +en; + +type F = C; +var fn: F = new C; +fn; + +type O = { x: X }; +var on: O = { x: 0 }; +on; + +type Mono = C; +var mn: Mono = new C; // error: application of non-poly type +mn; diff --git a/tests/type-at-pos/import.js b/tests/type-at-pos/import.js new file mode 100644 index 000000000000..9a49778fd59d --- /dev/null +++ b/tests/type-at-pos/import.js @@ -0,0 +1,5 @@ +// @flow +var num = 42; +function bar() { } +bar(); +module.exports = num; diff --git a/tests/type-at-pos/jsfmt.spec.js b/tests/type-at-pos/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/type-at-pos/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/type-at-pos/object_special_cases.js b/tests/type-at-pos/object_special_cases.js new file mode 100644 index 000000000000..d34326b96596 --- /dev/null +++ b/tests/type-at-pos/object_special_cases.js @@ -0,0 +1,8 @@ +/* @flow */ + +let tests = [ + function() { + let x = {}; + Object.defineProperty(x, 'foo', { value: '' }); + }, +]; diff --git a/tests/type-at-pos/optional.js b/tests/type-at-pos/optional.js new file mode 100644 index 000000000000..1ed4c4b71c02 --- /dev/null +++ b/tests/type-at-pos/optional.js @@ -0,0 +1,15 @@ +// @flow + +function foo(x?: string) { + return x; +} + +foo(); + +function bar(obj: { x?: string }) { + return obj.x; +} + +function qux(x?) { + return x; +} diff --git a/tests/type-at-pos/predicates.js b/tests/type-at-pos/predicates.js new file mode 100644 index 000000000000..0c68d6da5a4e --- /dev/null +++ b/tests/type-at-pos/predicates.js @@ -0,0 +1,6 @@ +/* @flow */ + +let x = 0; +if (x == null) {} +if (x == undefined) {} +if (Array.isArray(x)) {} diff --git a/tests/type-at-pos/react.js b/tests/type-at-pos/react.js new file mode 100644 index 000000000000..0921cb496171 --- /dev/null +++ b/tests/type-at-pos/react.js @@ -0,0 +1,2 @@ +import React from "react"; +React.createElement; diff --git a/tests/type-at-pos/templates.js b/tests/type-at-pos/templates.js new file mode 100644 index 000000000000..2ce2ae0fba5f --- /dev/null +++ b/tests/type-at-pos/templates.js @@ -0,0 +1,2 @@ +/* @flow */ +`foo bar`; diff --git a/tests/type-at-pos/test.js b/tests/type-at-pos/test.js new file mode 100644 index 000000000000..66d5c396be70 --- /dev/null +++ b/tests/type-at-pos/test.js @@ -0,0 +1,14 @@ +// @flow +var str = require('./import'); +function foo() { } +foo(); +str + +type Point = [number, string]; +const x:Point = [1, "foo"]; +type MyStr = "cool"; +const y:MyStr = "cool"; +type MyBool = true; +const z:MyBool = true; +type MyNum = 42; +const w:MyNum = 42; diff --git a/tests/type-at-pos/trycatch.js b/tests/type-at-pos/trycatch.js new file mode 100644 index 000000000000..35196da89c67 --- /dev/null +++ b/tests/type-at-pos/trycatch.js @@ -0,0 +1,5 @@ +// @flow + +try { + throw "foo"; +} catch (e) {} diff --git a/tests/type-destructors/__snapshots__/jsfmt.spec.js.snap b/tests/type-destructors/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..98f9765766b6 --- /dev/null +++ b/tests/type-destructors/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,86 @@ +exports[`test non_maybe_type.js 1`] = ` +"// @flow + +function foo(x: ?string): $NonMaybeType { + if (x != null) { return x; } + else return 0; // this should be an error +} + +//(foo(): string); // should not be necessary to expose the error above + +(0: $NonMaybeType); // error +(0: $NonMaybeType); // ok +(0: $NonMaybeType); // ok +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test property_type.js 1`] = ` +"type Malformed = $PropertyType; + +type Obj = { x: string }; +type Obj_Prop_x = $PropertyType; + +(42: Obj_Prop_x); + +function foo(o: Obj): $PropertyType { + if (false) return o.x; + else return 0; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test union.js 1`] = ` +"var x0: $NonMaybeType = 0; // ok, number ~> number|string +var x1: $NonMaybeType = true; // err, boolean ~> number|string +var x2: $PropertyType<{p:number}|{p:string},\'p\'> = 0; // ok, number ~> number|string +var x3: $PropertyType<{p:number}|{p:string},\'p\'> = true; // err, boolean ~> number|string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1497:36) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/type-destructors/jsfmt.spec.js b/tests/type-destructors/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/type-destructors/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/type-destructors/non_maybe_type.js b/tests/type-destructors/non_maybe_type.js new file mode 100644 index 000000000000..f3240e822337 --- /dev/null +++ b/tests/type-destructors/non_maybe_type.js @@ -0,0 +1,12 @@ +// @flow + +function foo(x: ?string): $NonMaybeType { + if (x != null) { return x; } + else return 0; // this should be an error +} + +//(foo(): string); // should not be necessary to expose the error above + +(0: $NonMaybeType); // error +(0: $NonMaybeType); // ok +(0: $NonMaybeType); // ok diff --git a/tests/type-destructors/property_type.js b/tests/type-destructors/property_type.js new file mode 100644 index 000000000000..c7bb9752eac2 --- /dev/null +++ b/tests/type-destructors/property_type.js @@ -0,0 +1,11 @@ +type Malformed = $PropertyType; + +type Obj = { x: string }; +type Obj_Prop_x = $PropertyType; + +(42: Obj_Prop_x); + +function foo(o: Obj): $PropertyType { + if (false) return o.x; + else return 0; +} diff --git a/tests/type-destructors/union.js b/tests/type-destructors/union.js new file mode 100644 index 000000000000..bc8b0abc9f40 --- /dev/null +++ b/tests/type-destructors/union.js @@ -0,0 +1,4 @@ +var x0: $NonMaybeType = 0; // ok, number ~> number|string +var x1: $NonMaybeType = true; // err, boolean ~> number|string +var x2: $PropertyType<{p:number}|{p:string},'p'> = 0; // ok, number ~> number|string +var x3: $PropertyType<{p:number}|{p:string},'p'> = true; // err, boolean ~> number|string diff --git a/tests/type-printer/__snapshots__/jsfmt.spec.js.snap b/tests/type-printer/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..001e98bea762 --- /dev/null +++ b/tests/type-printer/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,5104 @@ +exports[`test printBinaryExpression.js 1`] = ` +"\'use babel\'; +/* @flow */ + +/* + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the license found in the LICENSE file in + * the root directory of this source tree. + */ + +import type {BinaryExpression} from \'./types\'; + +function printBinaryExpression( + node: BinaryExpression, +) { + console.log(node); +} + +module.exports = printBinaryExpression; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\"use babel\"; +/* @flow */ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the license found in the LICENSE file in + * the root directory of this source tree. + */ +import type { BinaryExpression } from \"./types\"; +function printBinaryExpression(node: BinaryExpression) { + console.log(node); +} +module.exports = printBinaryExpression; + +" +`; + +exports[`test types.js 1`] = ` +"/** + * @flow + */ + +\'use strict\'; + +/* + * Flow types for the Babylon AST. + */ + +// Abstract types. Something must extend these. + +export type Comment = { + type: \'CommentLine\'; + _CommentLine: void; + value: string; + end: number; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; +} | { + type: \'CommentBlock\'; + _CommentBlock: void; + value: string; + end: number; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; +}; + +export type Declaration = { + type: \'ClassBody\'; + _ClassBody: void; + body: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ClassDeclaration\'; + _ClassDeclaration: void; + body: ClassBody; + id: ?Identifier; + superClass: ?Expression; + decorators: any; + superTypeParameters: any; + typeParameters: any; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'FunctionDeclaration\'; + _FunctionDeclaration: void; + body: BlockStatement; + id: Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; + async: boolean; + defaults: Array; + expression: boolean; + generator: boolean; + params: Array; + rest: ?Identifier; + returnType: ?TypeAnnotation; + typeParameters: ?TypeParameterDeclaration; +} | { + type: \'MethodDefinition\'; + _MethodDefinition: void; + computed: boolean; + key: Node; + kind: \'constructor\' | \'method\' | \'get\' | \'set\'; + static: boolean; + value: FunctionExpression; + decorators: ?Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'VariableDeclaration\'; + _VariableDeclaration: void; + declarations: Array; + kind: \'var\' | \'let\' | \'const\'; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ClassProperty\'; + _ClassProperty: void; + computed: boolean; + key: Node; + static: boolean; + typeAnnotation: ?TypeAnnotation; + value: ?Expression; + decorators: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type Expression = { + type: \'ArrayExpression\'; + _ArrayExpression: void; + elements: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'AssignmentExpression\'; + _AssignmentExpression: void; + left: Pattern; + operator: AssignmentOperator; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'AwaitExpression\'; + _AwaitExpression: void; + all: boolean; + argument: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'BinaryExpression\'; + _BinaryExpression: void; + left: Expression; + operator: BinaryOperator; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'BindExpression\'; + _BindExpression: void; + callee: Node; + object: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'CallExpression\'; + _CallExpression: void; + arguments: Array; + callee: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ClassExpression\'; + _ClassExpression: void; + body: ClassBody; + id: ?Identifier; + superClass: ?Expression; + decorators: any; + superTypeParameters: any; + typeParameters: any; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ComprehensionExpression\'; + _ComprehensionExpression: void; + body: Expression; + blocks: Array; + filter: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ConditionalExpression\'; + _ConditionalExpression: void; + alternate: Expression; + consequent: Expression; + test: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'DoExpression\'; + _DoExpression: void; + body: Statement; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'FunctionExpression\'; + _FunctionExpression: void; + body: BlockStatement; + id: ?Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; + async: boolean; + defaults: Array; + expression: boolean; + generator: boolean; + params: Array; + rest: ?Identifier; + returnType: ?TypeAnnotation; + typeParameters: ?TypeParameterDeclaration; +} | { + type: \'Identifier\'; + _Identifier: void; + name: string; + typeAnnotation: ?TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'Literal\'; + _Literal: void; + raw: string; + regex: ?{pattern: string, flags: string}; + value: ?(string | boolean | number | RegExp); + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'LogicalExpression\'; + _LogicalExpression: void; + left: Expression; + operator: LogicalOperator; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'MemberExpression\'; + _MemberExpression: void; + computed: boolean; + object: Expression; + property: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'NewExpression\'; + _NewExpression: void; + arguments: Array; + callee: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ObjectExpression\'; + _ObjectExpression: void; + properties: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'SequenceExpression\'; + _SequenceExpression: void; + expression: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'TaggedTemplateExpression\'; + _TaggedTemplateExpression: void; + quasi: TemplateLiteral; + tag: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'TemplateLiteral\'; + _TemplateLiteral: void; + expressions: Array; + quasis: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ThisExpression\'; + _ThisExpression: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'UnaryExpression\'; + _UnaryExpression: void; + argument: Expression; + operator: UnaryOperator; + prefix: true; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'UpdateExpression\'; + _UpdateExpression: void; + argument: Expression; + operator: UpdateOperator; + prefix: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'YieldExpression\'; + _YieldExpression: void; + argument: ?Expression; + delegate: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'TypeCastExpression\'; + _TypeCastExpression: void; + expression: Expression; + typeAnnotation: TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'JSXElement\'; + _JSXElement: void; + children: Array; + closingElement: ?JSXClosingElement; + openingElement: JSXOpeningElement; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'JSXEmptyExpression\'; + _JSXEmptyExpression: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'JSXExpressionContainer\'; + _JSXExpressionContainer: void; + expression: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'JSXMemberExpression\'; + _JSXMemberExpression: void; + computed: boolean; + object: Node; + property: JSXIdentifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type Function = { + type: \'ArrowFunctionExpression\'; + _ArrowFunctionExpression: void; + body: Node; + id: ?Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; + async: boolean; + defaults: Array; + expression: boolean; + generator: boolean; + params: Array; + rest: ?Identifier; + returnType: ?TypeAnnotation; + typeParameters: ?TypeParameterDeclaration; +} | { + type: \'FunctionDeclaration\'; + _FunctionDeclaration: void; + body: BlockStatement; + id: Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; + async: boolean; + defaults: Array; + expression: boolean; + generator: boolean; + params: Array; + rest: ?Identifier; + returnType: ?TypeAnnotation; + typeParameters: ?TypeParameterDeclaration; +} | { + type: \'FunctionExpression\'; + _FunctionExpression: void; + body: BlockStatement; + id: ?Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; + async: boolean; + defaults: Array; + expression: boolean; + generator: boolean; + params: Array; + rest: ?Identifier; + returnType: ?TypeAnnotation; + typeParameters: ?TypeParameterDeclaration; +}; + +export type Node = { + type: \'ArrayExpression\'; + _ArrayExpression: void; + elements: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ArrayPattern\'; + _ArrayPattern: void; + elements: Array; + typeAnnotation: ?TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ArrowFunctionExpression\'; + _ArrowFunctionExpression: void; + body: Node; + id: ?Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; + async: boolean; + defaults: Array; + expression: boolean; + generator: boolean; + params: Array; + rest: ?Identifier; + returnType: ?TypeAnnotation; + typeParameters: ?TypeParameterDeclaration; +} | { + type: \'AssignmentExpression\'; + _AssignmentExpression: void; + left: Pattern; + operator: AssignmentOperator; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'AssignmentPattern\'; + _AssignmentPattern: void; + left: Pattern; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'AwaitExpression\'; + _AwaitExpression: void; + all: boolean; + argument: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'BinaryExpression\'; + _BinaryExpression: void; + left: Expression; + operator: BinaryOperator; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'BindExpression\'; + _BindExpression: void; + callee: Node; + object: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'BlockStatement\'; + _BlockStatement: void; + body: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'BreakStatement\'; + _BreakStatement: void; + label: ?Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'CallExpression\'; + _CallExpression: void; + arguments: Array; + callee: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'CatchClause\'; + _CatchClause: void; + body: BlockStatement; + param: Pattern; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ClassBody\'; + _ClassBody: void; + body: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ClassDeclaration\'; + _ClassDeclaration: void; + body: ClassBody; + id: ?Identifier; + superClass: ?Expression; + decorators: any; + superTypeParameters: any; + typeParameters: any; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ClassExpression\'; + _ClassExpression: void; + body: ClassBody; + id: ?Identifier; + superClass: ?Expression; + decorators: any; + superTypeParameters: any; + typeParameters: any; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ComprehensionBlock\'; + _ComprehensionBlock: void; + each: boolean; + left: Pattern; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ComprehensionExpression\'; + _ComprehensionExpression: void; + body: Expression; + blocks: Array; + filter: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ConditionalExpression\'; + _ConditionalExpression: void; + alternate: Expression; + consequent: Expression; + test: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ContinueStatement\'; + _ContinueStatement: void; + label: ?Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'Decorator\'; + _Decorator: void; + expression: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'DebuggerStatement\'; + _DebuggerStatement: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'DoWhileStatement\'; + _DoWhileStatement: void; + body: Statement; + test: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'DoExpression\'; + _DoExpression: void; + body: Statement; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'EmptyStatement\'; + _EmptyStatement: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ExpressionStatement\'; + _ExpressionStatement: void; + expression: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'File\'; + _File: void; + program: Program; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ForInStatement\'; + _ForInStatement: void; + body: Statement; + left: Node; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ForOfStatement\'; + _ForOfStatement: void; + body: Statement; + left: Node; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ForStatement\'; + _ForStatement: void; + init: ?Node; + test: ?Expression; + update: ?Expression; + body: Statement; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'FunctionDeclaration\'; + _FunctionDeclaration: void; + body: BlockStatement; + id: Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; + async: boolean; + defaults: Array; + expression: boolean; + generator: boolean; + params: Array; + rest: ?Identifier; + returnType: ?TypeAnnotation; + typeParameters: ?TypeParameterDeclaration; +} | { + type: \'FunctionExpression\'; + _FunctionExpression: void; + body: BlockStatement; + id: ?Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; + async: boolean; + defaults: Array; + expression: boolean; + generator: boolean; + params: Array; + rest: ?Identifier; + returnType: ?TypeAnnotation; + typeParameters: ?TypeParameterDeclaration; +} | { + type: \'Identifier\'; + _Identifier: void; + name: string; + typeAnnotation: ?TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'IfStatement\'; + _IfStatement: void; + alternate: ?Statement; + consequent: Statement; + test: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ImportDefaultSpecifier\'; + _ImportDefaultSpecifier: void; + local: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ImportNamespaceSpecifier\'; + _ImportNamespaceSpecifier: void; + local: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ImportDeclaration\'; + _ImportDeclaration: void; + specifiers: Array; + source: Literal; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ImportSpecifier\'; + _ImportSpecifier: void; + imported: Node; + local: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'LabeledStatement\'; + _LabeledStatement: void; + body: Statement; + label: Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'Literal\'; + _Literal: void; + raw: string; + regex: ?{pattern: string, flags: string}; + value: ?(string | boolean | number | RegExp); + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'LogicalExpression\'; + _LogicalExpression: void; + left: Expression; + operator: LogicalOperator; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'MemberExpression\'; + _MemberExpression: void; + computed: boolean; + object: Expression; + property: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'MetaProperty\'; + _MetaProperty: void; + meta: Node; + property: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'MethodDefinition\'; + _MethodDefinition: void; + computed: boolean; + key: Node; + kind: \'constructor\' | \'method\' | \'get\' | \'set\'; + static: boolean; + value: FunctionExpression; + decorators: ?Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'NewExpression\'; + _NewExpression: void; + arguments: Array; + callee: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'Noop\'; + _Noop: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ObjectExpression\'; + _ObjectExpression: void; + properties: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ObjectPattern\'; + _ObjectPattern: void; + properties: Array; + typeAnnotation: ?TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'Program\'; + _Program: void; + body: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'Property\'; + _Property: void; + computed: boolean; + key: Node; + kind: \'init\' | \'get\' | \'set\'; + method: boolean; + shorthand: boolean; + value: Node; + decorators: ?Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'RestElement\'; + _RestElement: void; + argument: Pattern; + typeAnnotation: ?TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ReturnStatement\'; + _ReturnStatement: void; + argument: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'SequenceExpression\'; + _SequenceExpression: void; + expression: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'SpreadElement\'; + _SpreadElement: void; + argument: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'SpreadProperty\'; + _SpreadProperty: void; + argument: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'Super\'; + _Super: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'SwitchCase\'; + _SwitchCase: void; + consequent: Array; + test: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'SwitchStatement\'; + _SwitchStatement: void; + cases: Array; + discriminant: Expression; + lexical: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'TaggedTemplateExpression\'; + _TaggedTemplateExpression: void; + quasi: TemplateLiteral; + tag: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'TemplateElement\'; + _TemplateElement: void; + tail: boolean; + value: {cooked: string, raw: string}; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'TemplateLiteral\'; + _TemplateLiteral: void; + expressions: Array; + quasis: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ThisExpression\'; + _ThisExpression: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ThrowStatement\'; + _ThrowStatement: void; + argument: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'TryStatement\'; + _TryStatement: void; + block: BlockStatement; + finalizer: ?BlockStatement; + guardedHandlers: Array; + handler: ?CatchClause; + handlers: ?Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'UnaryExpression\'; + _UnaryExpression: void; + argument: Expression; + operator: UnaryOperator; + prefix: true; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'UpdateExpression\'; + _UpdateExpression: void; + argument: Expression; + operator: UpdateOperator; + prefix: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'VariableDeclaration\'; + _VariableDeclaration: void; + declarations: Array; + kind: \'var\' | \'let\' | \'const\'; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'VariableDeclarator\'; + _VariableDeclarator: void; + id: Pattern; + init: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'WhileStatement\'; + _WhileStatement: void; + body: Statement; + test: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'WithStatement\'; + _WithStatement: void; + body: Statement; + object: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'YieldExpression\'; + _YieldExpression: void; + argument: ?Expression; + delegate: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ExportAllDeclaration\'; + _ExportAllDeclaration: void; + exported: Node; + source: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ExportDefaultDeclaration\'; + _ExportDefaultDeclaration: void; + declaration: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ExportNamedDeclaration\'; + _ExportNamedDeclaration: void; + declaration: Node; + source: Literal; + specifiers: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ExportDefaultSpecifier\'; + _ExportDefaultSpecifier: void; + exported: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ExportNamespaceSpecifier\'; + _ExportNamespaceSpecifier: void; + exported: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ExportSpecifier\'; + _ExportSpecifier: void; + local: Node; + exported: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'AnyTypeAnnotation\'; + _AnyTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ArrayTypeAnnotation\'; + _ArrayTypeAnnotation: void; + elementType: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'BooleanLiteralTypeAnnotation\'; + _BooleanLiteralTypeAnnotation: void; + raw: string; + value: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'BooleanTypeAnnotation\'; + _BooleanTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ClassImplements\'; + _ClassImplements: void; + id: Identifier; + typeParameters: ?TypeParameterInstantiation; + superClass: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ClassProperty\'; + _ClassProperty: void; + computed: boolean; + key: Node; + static: boolean; + typeAnnotation: ?TypeAnnotation; + value: ?Expression; + decorators: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'DeclareClass\'; + _DeclareClass: void; + body: ObjectTypeAnnotation; + extends: Array; + id: Identifier; + typeParameters: ?TypeParameterDeclaration; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'DeclareFunction\'; + _DeclareFunction: void; + id: Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'DeclareModule\'; + _DeclareModule: void; + body: BlockStatement; + id: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'DeclareVariable\'; + _DeclareVariable: void; + id: Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'FunctionTypeAnnotation\'; + _FunctionTypeAnnotation: void; + params: Array; + rest: ?FunctionTypeParam; + returnType: Type; + typeParameters: ?TypeParameterDeclaration; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'FunctionTypeParam\'; + _FunctionTypeParam: void; + name: Identifier; + optional: boolean; + typeAnnotation: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'GenericTypeAnnotation\'; + _GenericTypeAnnotation: void; + id: Node; + typeParameters: ?TypeParameterInstantiation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'InterfaceExtends\'; + _InterfaceExtends: void; + id: Identifier; + typeParameters: ?TypeParameterInstantiation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'InterfaceDeclaration\'; + _InterfaceDeclaration: void; + body: ObjectTypeAnnotation; + extends: Array; + id: Identifier; + typeParameters: ?TypeParameterDeclaration; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'IntersectionTypeAnnotation\'; + _IntersectionTypeAnnotation: void; + types: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'MixedTypeAnnotation\'; + _MixedTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'NullableTypeAnnotation\'; + _NullableTypeAnnotation: void; + typeAnnotation: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'NumberLiteralTypeAnnotation\'; + _NumberLiteralTypeAnnotation: void; + raw: string; + value: number; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'NumberTypeAnnotation\'; + _NumberTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'StringLiteralTypeAnnotation\'; + _StringLiteralTypeAnnotation: void; + raw: string; + value: string; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'StringTypeAnnotation\'; + _StringTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'TupleTypeAnnotation\'; + _TupleTypeAnnotation: void; + types: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'TypeofTypeAnnotation\'; + _TypeofTypeAnnotation: void; + argument: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'TypeAlias\'; + _TypeAlias: void; + id: Identifier; + right: Type; + typeParameters: ?TypeParameterDeclaration; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'TypeAnnotation\'; + _TypeAnnotation: void; + typeAnnotation: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'TypeCastExpression\'; + _TypeCastExpression: void; + expression: Expression; + typeAnnotation: TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'TypeParameterDeclaration\'; + _TypeParameterDeclaration: void; + params: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'TypeParameterInstantiation\'; + _TypeParameterInstantiation: void; + params: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ObjectTypeAnnotation\'; + _ObjectTypeAnnotation: void; + callProperties: Array; + indexers: Array; + properties: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ObjectTypeCallProperty\'; + _ObjectTypeCallProperty: void; + static: boolean; + value: FunctionTypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ObjectTypeIndexer\'; + _ObjectTypeIndexer: void; + id: Identifier; + key: Type; + value: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ObjectTypeProperty\'; + _ObjectTypeProperty: void; + key: Node; + optional: boolean; + value: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'QualifiedTypeIdentifier\'; + _QualifiedTypeIdentifier: void; + id: Identifier; + qualification: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'UnionTypeAnnotation\'; + _UnionTypeAnnotation: void; + types: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'VoidTypeAnnotation\'; + _VoidTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'JSXAttribute\'; + _JSXAttribute: void; + name: Node; + value: ?Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'JSXClosingElement\'; + _JSXClosingElement: void; + name: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'JSXElement\'; + _JSXElement: void; + children: Array; + closingElement: ?JSXClosingElement; + openingElement: JSXOpeningElement; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'JSXEmptyExpression\'; + _JSXEmptyExpression: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'JSXExpressionContainer\'; + _JSXExpressionContainer: void; + expression: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'JSXIdentifier\'; + _JSXIdentifier: void; + name: string; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'JSXMemberExpression\'; + _JSXMemberExpression: void; + computed: boolean; + object: Node; + property: JSXIdentifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'JSXNamespacedName\'; + _JSXNamespacedName: void; + name: JSXIdentifier; + namespace: JSXIdentifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'JSXOpeningElement\'; + _JSXOpeningElement: void; + attributes: Array; + name: Array; + selfClosing: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'JSXSpreadAttribute\'; + _JSXSpreadAttribute: void; + argument: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type Pattern = { + type: \'ArrayPattern\'; + _ArrayPattern: void; + elements: Array; + typeAnnotation: ?TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'AssignmentPattern\'; + _AssignmentPattern: void; + left: Pattern; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'Identifier\'; + _Identifier: void; + name: string; + typeAnnotation: ?TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ObjectPattern\'; + _ObjectPattern: void; + properties: Array; + typeAnnotation: ?TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'RestElement\'; + _RestElement: void; + argument: Pattern; + typeAnnotation: ?TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type Statement = { + type: \'BlockStatement\'; + _BlockStatement: void; + body: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'BreakStatement\'; + _BreakStatement: void; + label: ?Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ContinueStatement\'; + _ContinueStatement: void; + label: ?Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'DoWhileStatement\'; + _DoWhileStatement: void; + body: Statement; + test: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'EmptyStatement\'; + _EmptyStatement: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ExpressionStatement\'; + _ExpressionStatement: void; + expression: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ForInStatement\'; + _ForInStatement: void; + body: Statement; + left: Node; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ForOfStatement\'; + _ForOfStatement: void; + body: Statement; + left: Node; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ForStatement\'; + _ForStatement: void; + init: ?Node; + test: ?Expression; + update: ?Expression; + body: Statement; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'IfStatement\'; + _IfStatement: void; + alternate: ?Statement; + consequent: Statement; + test: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'LabeledStatement\'; + _LabeledStatement: void; + body: Statement; + label: Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ReturnStatement\'; + _ReturnStatement: void; + argument: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'SwitchStatement\'; + _SwitchStatement: void; + cases: Array; + discriminant: Expression; + lexical: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ThrowStatement\'; + _ThrowStatement: void; + argument: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'TryStatement\'; + _TryStatement: void; + block: BlockStatement; + finalizer: ?BlockStatement; + guardedHandlers: Array; + handler: ?CatchClause; + handlers: ?Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'WhileStatement\'; + _WhileStatement: void; + body: Statement; + test: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'WithStatement\'; + _WithStatement: void; + body: Statement; + object: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'DeclareClass\'; + _DeclareClass: void; + body: ObjectTypeAnnotation; + extends: Array; + id: Identifier; + typeParameters: ?TypeParameterDeclaration; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'DeclareFunction\'; + _DeclareFunction: void; + id: Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'DeclareModule\'; + _DeclareModule: void; + body: BlockStatement; + id: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'DeclareVariable\'; + _DeclareVariable: void; + id: Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'InterfaceDeclaration\'; + _InterfaceDeclaration: void; + body: ObjectTypeAnnotation; + extends: Array; + id: Identifier; + typeParameters: ?TypeParameterDeclaration; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'TypeAlias\'; + _TypeAlias: void; + id: Identifier; + right: Type; + typeParameters: ?TypeParameterDeclaration; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type Type = { + type: \'AnyTypeAnnotation\'; + _AnyTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ArrayTypeAnnotation\'; + _ArrayTypeAnnotation: void; + elementType: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'BooleanLiteralTypeAnnotation\'; + _BooleanLiteralTypeAnnotation: void; + raw: string; + value: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'BooleanTypeAnnotation\'; + _BooleanTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'FunctionTypeAnnotation\'; + _FunctionTypeAnnotation: void; + params: Array; + rest: ?FunctionTypeParam; + returnType: Type; + typeParameters: ?TypeParameterDeclaration; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'GenericTypeAnnotation\'; + _GenericTypeAnnotation: void; + id: Node; + typeParameters: ?TypeParameterInstantiation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'IntersectionTypeAnnotation\'; + _IntersectionTypeAnnotation: void; + types: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'MixedTypeAnnotation\'; + _MixedTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'NullableTypeAnnotation\'; + _NullableTypeAnnotation: void; + typeAnnotation: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'NumberLiteralTypeAnnotation\'; + _NumberLiteralTypeAnnotation: void; + raw: string; + value: number; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'NumberTypeAnnotation\'; + _NumberTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'StringLiteralTypeAnnotation\'; + _StringLiteralTypeAnnotation: void; + raw: string; + value: string; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'StringTypeAnnotation\'; + _StringTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'TupleTypeAnnotation\'; + _TupleTypeAnnotation: void; + types: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'ObjectTypeAnnotation\'; + _ObjectTypeAnnotation: void; + callProperties: Array; + indexers: Array; + properties: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'UnionTypeAnnotation\'; + _UnionTypeAnnotation: void; + types: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: \'VoidTypeAnnotation\'; + _VoidTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// Concrete Types. Nothing can extend these. + +export type CommentLine = { + type: \'CommentLine\'; + _CommentLine: void; + value: string; + end: number; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; +}; + +export type CommentBlock = { + type: \'CommentBlock\'; + _CommentBlock: void; + value: string; + end: number; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; +}; + +// Babel concrete types. + +export type ArrayExpression = { + type: \'ArrayExpression\'; + _ArrayExpression: void; + elements: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ArrayPattern = { + type: \'ArrayPattern\'; + _ArrayPattern: void; + elements: Array; + typeAnnotation: ?TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ArrowFunctionExpression = { + type: \'ArrowFunctionExpression\'; + _ArrowFunctionExpression: void; + body: Node; + id: ?Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; + async: boolean; + defaults: Array; + expression: boolean; + generator: boolean; + params: Array; + rest: ?Identifier; + returnType: ?TypeAnnotation; + typeParameters: ?TypeParameterDeclaration; +}; + +type AssignmentOperator = + \'=\' | + \'+=\' | + \'-=\' | + \'*=\' | + \'/=\' | + \'%=\' | + \'<<=\' | + \'>>=\' | + \'>>>=\' | + \'|=\' | + \'^=\' | + \'&=\'; + +export type AssignmentExpression = { + type: \'AssignmentExpression\'; + _AssignmentExpression: void; + left: Pattern; + operator: AssignmentOperator; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type AssignmentPattern = { + type: \'AssignmentPattern\'; + _AssignmentPattern: void; + left: Pattern; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type AwaitExpression = { + type: \'AwaitExpression\'; + _AwaitExpression: void; + all: boolean; + argument: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +type BinaryOperator = + \'==\' | + \'!=\' | + \'===\' | + \'!==\' | + \'<\' | + \'<=\' | + \'>\' | + \'>=\' | + \'<<\' | + \'>>\' | + \'>>>\' | + \'+\' | + \'-\' | + \'*\' | + \'/\' | + \'%\' | + \'&\' | + \'|\' | + \'^\' | + \'in\' | + \'instanceof\' | + \'..\'; + +export type BinaryExpression = { + type: \'BinaryExpression\'; + _BinaryExpression: void; + left: Expression; + operator: BinaryOperator; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: What is this? +export type BindExpression = { + type: \'BindExpression\'; + _BindExpression: void; + callee: Node; + object: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type BlockStatement = { + type: \'BlockStatement\'; + _BlockStatement: void; + body: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type BreakStatement = { + type: \'BreakStatement\'; + _BreakStatement: void; + label: ?Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type CallExpression = { + type: \'CallExpression\'; + _CallExpression: void; + arguments: Array; + callee: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type CatchClause = { + type: \'CatchClause\'; + _CatchClause: void; + body: BlockStatement; + param: Pattern; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ClassBody = { + type: \'ClassBody\'; + _ClassBody: void; + body: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ClassDeclaration = { + type: \'ClassDeclaration\'; + _ClassDeclaration: void; + body: ClassBody; + id: ?Identifier; + superClass: ?Expression; + decorators: any; + superTypeParameters: any; + typeParameters: any; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ClassExpression = { + type: \'ClassExpression\'; + _ClassExpression: void; + body: ClassBody; + id: ?Identifier; + superClass: ?Expression; + decorators: any; + superTypeParameters: any; + typeParameters: any; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ComprehensionBlock = { + type: \'ComprehensionBlock\'; + _ComprehensionBlock: void; + each: boolean; + left: Pattern; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ComprehensionExpression = { + type: \'ComprehensionExpression\'; + _ComprehensionExpression: void; + body: Expression; + blocks: Array; + filter: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ConditionalExpression = { + type: \'ConditionalExpression\'; + _ConditionalExpression: void; + alternate: Expression; + consequent: Expression; + test: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ContinueStatement = { + type: \'ContinueStatement\'; + _ContinueStatement: void; + label: ?Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type Decorator = { + type: \'Decorator\'; + _Decorator: void; + expression: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type DebuggerStatement = { + type: \'DebuggerStatement\'; + _DebuggerStatement: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type DoWhileStatement = { + type: \'DoWhileStatement\'; + _DoWhileStatement: void; + body: Statement; + test: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type DoExpression = { + type: \'DoExpression\'; + _DoExpression: void; + body: Statement; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type EmptyStatement = { + type: \'EmptyStatement\'; + _EmptyStatement: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ExpressionStatement = { + type: \'ExpressionStatement\'; + _ExpressionStatement: void; + expression: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type File = { + type: \'File\'; + _File: void; + program: Program; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ForInStatement = { + type: \'ForInStatement\'; + _ForInStatement: void; + body: Statement; + left: Node; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type ForOfStatement = { + type: \'ForOfStatement\'; + _ForOfStatement: void; + body: Statement; + left: Node; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ForStatement = { + type: \'ForStatement\'; + _ForStatement: void; + init: ?Node; + test: ?Expression; + update: ?Expression; + body: Statement; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type FunctionDeclaration = { + type: \'FunctionDeclaration\'; + _FunctionDeclaration: void; + body: BlockStatement; + id: Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; + async: boolean; + defaults: Array; + expression: boolean; + generator: boolean; + params: Array; + rest: ?Identifier; + returnType: ?TypeAnnotation; + typeParameters: ?TypeParameterDeclaration; +}; + +export type FunctionExpression = { + type: \'FunctionExpression\'; + _FunctionExpression: void; + body: BlockStatement; + id: ?Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; + async: boolean; + defaults: Array; + expression: boolean; + generator: boolean; + params: Array; + rest: ?Identifier; + returnType: ?TypeAnnotation; + typeParameters: ?TypeParameterDeclaration; +}; + +export type Identifier = { + type: \'Identifier\'; + _Identifier: void; + name: string; + typeAnnotation: ?TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type IfStatement = { + type: \'IfStatement\'; + _IfStatement: void; + alternate: ?Statement; + consequent: Statement; + test: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type ImportDefaultSpecifier = { + type: \'ImportDefaultSpecifier\'; + _ImportDefaultSpecifier: void; + local: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type ImportNamespaceSpecifier = { + type: \'ImportNamespaceSpecifier\'; + _ImportNamespaceSpecifier: void; + local: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type ImportDeclaration = { + type: \'ImportDeclaration\'; + _ImportDeclaration: void; + specifiers: Array; + source: Literal; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type ImportSpecifier = { + type: \'ImportSpecifier\'; + _ImportSpecifier: void; + imported: Node; + local: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type LabeledStatement = { + type: \'LabeledStatement\'; + _LabeledStatement: void; + body: Statement; + label: Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type Literal = { + type: \'Literal\'; + _Literal: void; + raw: string; + regex: ?{pattern: string, flags: string}; + value: ?(string | boolean | number | RegExp); + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +type LogicalOperator = \'||\' | \'&&\'; + +export type LogicalExpression = { + type: \'LogicalExpression\'; + _LogicalExpression: void; + left: Expression; + operator: LogicalOperator; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type MemberExpression = { + type: \'MemberExpression\'; + _MemberExpression: void; + computed: boolean; + object: Expression; + property: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type MetaProperty = { + type: \'MetaProperty\'; + _MetaProperty: void; + meta: Node; + property: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type MethodDefinition = { + type: \'MethodDefinition\'; + _MethodDefinition: void; + computed: boolean; + key: Node; + kind: \'constructor\' | \'method\' | \'get\' | \'set\'; + static: boolean; + value: FunctionExpression; + decorators: ?Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type NewExpression = { + type: \'NewExpression\'; + _NewExpression: void; + arguments: Array; + callee: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type Noop = { + type: \'Noop\'; + _Noop: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ObjectExpression = { + type: \'ObjectExpression\'; + _ObjectExpression: void; + properties: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ObjectPattern = { + type: \'ObjectPattern\'; + _ObjectPattern: void; + properties: Array; + typeAnnotation: ?TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type Program = { + type: \'Program\'; + _Program: void; + body: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type Property = { + type: \'Property\'; + _Property: void; + computed: boolean; + key: Node; + kind: \'init\' | \'get\' | \'set\'; + method: boolean; + shorthand: boolean; + value: Node; + decorators: ?Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type RestElement = { + type: \'RestElement\'; + _RestElement: void; + argument: Pattern; + typeAnnotation: ?TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ReturnStatement = { + type: \'ReturnStatement\'; + _ReturnStatement: void; + argument: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type SequenceExpression = { + type: \'SequenceExpression\'; + _SequenceExpression: void; + expression: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type SpreadElement = { + type: \'SpreadElement\'; + _SpreadElement: void; + argument: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type SpreadProperty = { + type: \'SpreadProperty\'; + _SpreadProperty: void; + argument: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type Super = { + type: \'Super\'; + _Super: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type SwitchCase = { + type: \'SwitchCase\'; + _SwitchCase: void; + consequent: Array; + test: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type SwitchStatement = { + type: \'SwitchStatement\'; + _SwitchStatement: void; + cases: Array; + discriminant: Expression; + lexical: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type TaggedTemplateExpression = { + type: \'TaggedTemplateExpression\'; + _TaggedTemplateExpression: void; + quasi: TemplateLiteral; + tag: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type TemplateElement = { + type: \'TemplateElement\'; + _TemplateElement: void; + tail: boolean; + value: {cooked: string, raw: string}; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type TemplateLiteral = { + type: \'TemplateLiteral\'; + _TemplateLiteral: void; + expressions: Array; + quasis: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ThisExpression = { + type: \'ThisExpression\'; + _ThisExpression: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ThrowStatement = { + type: \'ThrowStatement\'; + _ThrowStatement: void; + argument: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type TryStatement = { + type: \'TryStatement\'; + _TryStatement: void; + block: BlockStatement; + finalizer: ?BlockStatement; + guardedHandlers: Array; + handler: ?CatchClause; + handlers: ?Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +type UnaryOperator = \'-\' | \'+\' | \'!\' | \'~\' | \'typeof\' | \'void\' | \'delete\'; + +export type UnaryExpression = { + type: \'UnaryExpression\'; + _UnaryExpression: void; + argument: Expression; + operator: UnaryOperator; + prefix: true; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +type UpdateOperator = \'++\' | \'--\'; + +export type UpdateExpression = { + type: \'UpdateExpression\'; + _UpdateExpression: void; + argument: Expression; + operator: UpdateOperator; + prefix: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type VariableDeclaration = { + type: \'VariableDeclaration\'; + _VariableDeclaration: void; + declarations: Array; + kind: \'var\' | \'let\' | \'const\'; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type VariableDeclarator = { + type: \'VariableDeclarator\'; + _VariableDeclarator: void; + id: Pattern; + init: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type WhileStatement = { + type: \'WhileStatement\'; + _WhileStatement: void; + body: Statement; + test: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type WithStatement = { + type: \'WithStatement\'; + _WithStatement: void; + body: Statement; + object: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type YieldExpression = { + type: \'YieldExpression\'; + _YieldExpression: void; + argument: ?Expression; + delegate: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type ExportAllDeclaration = { + type: \'ExportAllDeclaration\'; + _ExportAllDeclaration: void; + exported: Node; + source: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type ExportDefaultDeclaration = { + type: \'ExportDefaultDeclaration\'; + _ExportDefaultDeclaration: void; + declaration: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type ExportNamedDeclaration = { + type: \'ExportNamedDeclaration\'; + _ExportNamedDeclaration: void; + declaration: Node; + source: Literal; + specifiers: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type ExportDefaultSpecifier = { + type: \'ExportDefaultSpecifier\'; + _ExportDefaultSpecifier: void; + exported: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type ExportNamespaceSpecifier = { + type: \'ExportNamespaceSpecifier\'; + _ExportNamespaceSpecifier: void; + exported: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type ExportSpecifier = { + type: \'ExportSpecifier\'; + _ExportSpecifier: void; + local: Node; + exported: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type AnyTypeAnnotation = { + type: \'AnyTypeAnnotation\'; + _AnyTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ArrayTypeAnnotation = { + type: \'ArrayTypeAnnotation\'; + _ArrayTypeAnnotation: void; + elementType: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type BooleanLiteralTypeAnnotation = { + type: \'BooleanLiteralTypeAnnotation\'; + _BooleanLiteralTypeAnnotation: void; + raw: string; + value: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type BooleanTypeAnnotation = { + type: \'BooleanTypeAnnotation\'; + _BooleanTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ClassImplements = { + type: \'ClassImplements\'; + _ClassImplements: void; + id: Identifier; + typeParameters: ?TypeParameterInstantiation; + superClass: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ClassProperty = { + type: \'ClassProperty\'; + _ClassProperty: void; + computed: boolean; + key: Node; + static: boolean; + typeAnnotation: ?TypeAnnotation; + value: ?Expression; + decorators: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type DeclareClass = { + type: \'DeclareClass\'; + _DeclareClass: void; + body: ObjectTypeAnnotation; + extends: Array; + id: Identifier; + typeParameters: ?TypeParameterDeclaration; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type DeclareFunction = { + type: \'DeclareFunction\'; + _DeclareFunction: void; + id: Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type DeclareModule = { + type: \'DeclareModule\'; + _DeclareModule: void; + body: BlockStatement; + id: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type DeclareVariable = { + type: \'DeclareVariable\'; + _DeclareVariable: void; + id: Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type FunctionTypeAnnotation = { + type: \'FunctionTypeAnnotation\'; + _FunctionTypeAnnotation: void; + params: Array; + rest: ?FunctionTypeParam; + returnType: Type; + typeParameters: ?TypeParameterDeclaration; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type FunctionTypeParam = { + type: \'FunctionTypeParam\'; + _FunctionTypeParam: void; + name: Identifier; + optional: boolean; + typeAnnotation: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type GenericTypeAnnotation = { + type: \'GenericTypeAnnotation\'; + _GenericTypeAnnotation: void; + id: Node; + typeParameters: ?TypeParameterInstantiation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type InterfaceExtends = { + type: \'InterfaceExtends\'; + _InterfaceExtends: void; + id: Identifier; + typeParameters: ?TypeParameterInstantiation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type InterfaceDeclaration = { + type: \'InterfaceDeclaration\'; + _InterfaceDeclaration: void; + body: ObjectTypeAnnotation; + extends: Array; + id: Identifier; + typeParameters: ?TypeParameterDeclaration; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type IntersectionTypeAnnotation = { + type: \'IntersectionTypeAnnotation\'; + _IntersectionTypeAnnotation: void; + types: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type MixedTypeAnnotation = { + type: \'MixedTypeAnnotation\'; + _MixedTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type NullableTypeAnnotation = { + type: \'NullableTypeAnnotation\'; + _NullableTypeAnnotation: void; + typeAnnotation: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type NumberLiteralTypeAnnotation = { + type: \'NumberLiteralTypeAnnotation\'; + _NumberLiteralTypeAnnotation: void; + raw: string; + value: number; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type NumberTypeAnnotation = { + type: \'NumberTypeAnnotation\'; + _NumberTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type StringLiteralTypeAnnotation = { + type: \'StringLiteralTypeAnnotation\'; + _StringLiteralTypeAnnotation: void; + raw: string; + value: string; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type StringTypeAnnotation = { + type: \'StringTypeAnnotation\'; + _StringTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type TupleTypeAnnotation = { + type: \'TupleTypeAnnotation\'; + _TupleTypeAnnotation: void; + types: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type TypeofTypeAnnotation = { + type: \'TypeofTypeAnnotation\'; + _TypeofTypeAnnotation: void; + argument: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type TypeAlias = { + type: \'TypeAlias\'; + _TypeAlias: void; + id: Identifier; + right: Type; + typeParameters: ?TypeParameterDeclaration; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type TypeAnnotation = { + type: \'TypeAnnotation\'; + _TypeAnnotation: void; + typeAnnotation: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type TypeCastExpression = { + type: \'TypeCastExpression\'; + _TypeCastExpression: void; + expression: Expression; + typeAnnotation: TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type TypeParameterDeclaration = { + type: \'TypeParameterDeclaration\'; + _TypeParameterDeclaration: void; + params: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type TypeParameterInstantiation = { + type: \'TypeParameterInstantiation\'; + _TypeParameterInstantiation: void; + params: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ObjectTypeAnnotation = { + type: \'ObjectTypeAnnotation\'; + _ObjectTypeAnnotation: void; + callProperties: Array; + indexers: Array; + properties: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ObjectTypeCallProperty = { + type: \'ObjectTypeCallProperty\'; + _ObjectTypeCallProperty: void; + static: boolean; + value: FunctionTypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ObjectTypeIndexer = { + type: \'ObjectTypeIndexer\'; + _ObjectTypeIndexer: void; + id: Identifier; + key: Type; + value: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ObjectTypeProperty = { + type: \'ObjectTypeProperty\'; + _ObjectTypeProperty: void; + key: Node; + optional: boolean; + value: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type QualifiedTypeIdentifier = { + type: \'QualifiedTypeIdentifier\'; + _QualifiedTypeIdentifier: void; + id: Identifier; + qualification: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type UnionTypeAnnotation = { + type: \'UnionTypeAnnotation\'; + _UnionTypeAnnotation: void; + types: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type VoidTypeAnnotation = { + type: \'VoidTypeAnnotation\'; + _VoidTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type JSXAttribute = { + type: \'JSXAttribute\'; + _JSXAttribute: void; + name: Node; + value: ?Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type JSXClosingElement = { + type: \'JSXClosingElement\'; + _JSXClosingElement: void; + name: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type JSXElement = { + type: \'JSXElement\'; + _JSXElement: void; + children: Array; + closingElement: ?JSXClosingElement; + openingElement: JSXOpeningElement; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type JSXEmptyExpression = { + type: \'JSXEmptyExpression\'; + _JSXEmptyExpression: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type JSXExpressionContainer = { + type: \'JSXExpressionContainer\'; + _JSXExpressionContainer: void; + expression: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type JSXIdentifier = { + type: \'JSXIdentifier\'; + _JSXIdentifier: void; + name: string; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type JSXMemberExpression = { + type: \'JSXMemberExpression\'; + _JSXMemberExpression: void; + computed: boolean; + object: Node; + property: JSXIdentifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type JSXNamespacedName = { + type: \'JSXNamespacedName\'; + _JSXNamespacedName: void; + name: JSXIdentifier; + namespace: JSXIdentifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type JSXOpeningElement = { + type: \'JSXOpeningElement\'; + _JSXOpeningElement: void; + attributes: Array; + name: Array; + selfClosing: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type JSXSpreadAttribute = { + type: \'JSXSpreadAttribute\'; + _JSXSpreadAttribute: void; + argument: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/type-printer/jsfmt.spec.js b/tests/type-printer/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/type-printer/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/type-printer/printBinaryExpression.js b/tests/type-printer/printBinaryExpression.js new file mode 100644 index 000000000000..4b3241fd1bd2 --- /dev/null +++ b/tests/type-printer/printBinaryExpression.js @@ -0,0 +1,20 @@ +'use babel'; +/* @flow */ + +/* + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the license found in the LICENSE file in + * the root directory of this source tree. + */ + +import type {BinaryExpression} from './types'; + +function printBinaryExpression( + node: BinaryExpression, +) { + console.log(node); +} + +module.exports = printBinaryExpression; diff --git a/tests/type-printer/types.js b/tests/type-printer/types.js new file mode 100644 index 000000000000..4f7e499e4def --- /dev/null +++ b/tests/type-printer/types.js @@ -0,0 +1,5045 @@ +/** + * @flow + */ + +'use strict'; + +/* + * Flow types for the Babylon AST. + */ + +// Abstract types. Something must extend these. + +export type Comment = { + type: 'CommentLine'; + _CommentLine: void; + value: string; + end: number; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; +} | { + type: 'CommentBlock'; + _CommentBlock: void; + value: string; + end: number; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; +}; + +export type Declaration = { + type: 'ClassBody'; + _ClassBody: void; + body: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ClassDeclaration'; + _ClassDeclaration: void; + body: ClassBody; + id: ?Identifier; + superClass: ?Expression; + decorators: any; + superTypeParameters: any; + typeParameters: any; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'FunctionDeclaration'; + _FunctionDeclaration: void; + body: BlockStatement; + id: Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; + async: boolean; + defaults: Array; + expression: boolean; + generator: boolean; + params: Array; + rest: ?Identifier; + returnType: ?TypeAnnotation; + typeParameters: ?TypeParameterDeclaration; +} | { + type: 'MethodDefinition'; + _MethodDefinition: void; + computed: boolean; + key: Node; + kind: 'constructor' | 'method' | 'get' | 'set'; + static: boolean; + value: FunctionExpression; + decorators: ?Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'VariableDeclaration'; + _VariableDeclaration: void; + declarations: Array; + kind: 'var' | 'let' | 'const'; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ClassProperty'; + _ClassProperty: void; + computed: boolean; + key: Node; + static: boolean; + typeAnnotation: ?TypeAnnotation; + value: ?Expression; + decorators: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type Expression = { + type: 'ArrayExpression'; + _ArrayExpression: void; + elements: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'AssignmentExpression'; + _AssignmentExpression: void; + left: Pattern; + operator: AssignmentOperator; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'AwaitExpression'; + _AwaitExpression: void; + all: boolean; + argument: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'BinaryExpression'; + _BinaryExpression: void; + left: Expression; + operator: BinaryOperator; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'BindExpression'; + _BindExpression: void; + callee: Node; + object: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'CallExpression'; + _CallExpression: void; + arguments: Array; + callee: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ClassExpression'; + _ClassExpression: void; + body: ClassBody; + id: ?Identifier; + superClass: ?Expression; + decorators: any; + superTypeParameters: any; + typeParameters: any; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ComprehensionExpression'; + _ComprehensionExpression: void; + body: Expression; + blocks: Array; + filter: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ConditionalExpression'; + _ConditionalExpression: void; + alternate: Expression; + consequent: Expression; + test: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'DoExpression'; + _DoExpression: void; + body: Statement; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'FunctionExpression'; + _FunctionExpression: void; + body: BlockStatement; + id: ?Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; + async: boolean; + defaults: Array; + expression: boolean; + generator: boolean; + params: Array; + rest: ?Identifier; + returnType: ?TypeAnnotation; + typeParameters: ?TypeParameterDeclaration; +} | { + type: 'Identifier'; + _Identifier: void; + name: string; + typeAnnotation: ?TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'Literal'; + _Literal: void; + raw: string; + regex: ?{pattern: string, flags: string}; + value: ?(string | boolean | number | RegExp); + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'LogicalExpression'; + _LogicalExpression: void; + left: Expression; + operator: LogicalOperator; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'MemberExpression'; + _MemberExpression: void; + computed: boolean; + object: Expression; + property: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'NewExpression'; + _NewExpression: void; + arguments: Array; + callee: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ObjectExpression'; + _ObjectExpression: void; + properties: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'SequenceExpression'; + _SequenceExpression: void; + expression: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'TaggedTemplateExpression'; + _TaggedTemplateExpression: void; + quasi: TemplateLiteral; + tag: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'TemplateLiteral'; + _TemplateLiteral: void; + expressions: Array; + quasis: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ThisExpression'; + _ThisExpression: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'UnaryExpression'; + _UnaryExpression: void; + argument: Expression; + operator: UnaryOperator; + prefix: true; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'UpdateExpression'; + _UpdateExpression: void; + argument: Expression; + operator: UpdateOperator; + prefix: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'YieldExpression'; + _YieldExpression: void; + argument: ?Expression; + delegate: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'TypeCastExpression'; + _TypeCastExpression: void; + expression: Expression; + typeAnnotation: TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'JSXElement'; + _JSXElement: void; + children: Array; + closingElement: ?JSXClosingElement; + openingElement: JSXOpeningElement; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'JSXEmptyExpression'; + _JSXEmptyExpression: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'JSXExpressionContainer'; + _JSXExpressionContainer: void; + expression: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'JSXMemberExpression'; + _JSXMemberExpression: void; + computed: boolean; + object: Node; + property: JSXIdentifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type Function = { + type: 'ArrowFunctionExpression'; + _ArrowFunctionExpression: void; + body: Node; + id: ?Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; + async: boolean; + defaults: Array; + expression: boolean; + generator: boolean; + params: Array; + rest: ?Identifier; + returnType: ?TypeAnnotation; + typeParameters: ?TypeParameterDeclaration; +} | { + type: 'FunctionDeclaration'; + _FunctionDeclaration: void; + body: BlockStatement; + id: Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; + async: boolean; + defaults: Array; + expression: boolean; + generator: boolean; + params: Array; + rest: ?Identifier; + returnType: ?TypeAnnotation; + typeParameters: ?TypeParameterDeclaration; +} | { + type: 'FunctionExpression'; + _FunctionExpression: void; + body: BlockStatement; + id: ?Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; + async: boolean; + defaults: Array; + expression: boolean; + generator: boolean; + params: Array; + rest: ?Identifier; + returnType: ?TypeAnnotation; + typeParameters: ?TypeParameterDeclaration; +}; + +export type Node = { + type: 'ArrayExpression'; + _ArrayExpression: void; + elements: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ArrayPattern'; + _ArrayPattern: void; + elements: Array; + typeAnnotation: ?TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ArrowFunctionExpression'; + _ArrowFunctionExpression: void; + body: Node; + id: ?Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; + async: boolean; + defaults: Array; + expression: boolean; + generator: boolean; + params: Array; + rest: ?Identifier; + returnType: ?TypeAnnotation; + typeParameters: ?TypeParameterDeclaration; +} | { + type: 'AssignmentExpression'; + _AssignmentExpression: void; + left: Pattern; + operator: AssignmentOperator; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'AssignmentPattern'; + _AssignmentPattern: void; + left: Pattern; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'AwaitExpression'; + _AwaitExpression: void; + all: boolean; + argument: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'BinaryExpression'; + _BinaryExpression: void; + left: Expression; + operator: BinaryOperator; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'BindExpression'; + _BindExpression: void; + callee: Node; + object: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'BlockStatement'; + _BlockStatement: void; + body: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'BreakStatement'; + _BreakStatement: void; + label: ?Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'CallExpression'; + _CallExpression: void; + arguments: Array; + callee: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'CatchClause'; + _CatchClause: void; + body: BlockStatement; + param: Pattern; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ClassBody'; + _ClassBody: void; + body: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ClassDeclaration'; + _ClassDeclaration: void; + body: ClassBody; + id: ?Identifier; + superClass: ?Expression; + decorators: any; + superTypeParameters: any; + typeParameters: any; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ClassExpression'; + _ClassExpression: void; + body: ClassBody; + id: ?Identifier; + superClass: ?Expression; + decorators: any; + superTypeParameters: any; + typeParameters: any; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ComprehensionBlock'; + _ComprehensionBlock: void; + each: boolean; + left: Pattern; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ComprehensionExpression'; + _ComprehensionExpression: void; + body: Expression; + blocks: Array; + filter: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ConditionalExpression'; + _ConditionalExpression: void; + alternate: Expression; + consequent: Expression; + test: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ContinueStatement'; + _ContinueStatement: void; + label: ?Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'Decorator'; + _Decorator: void; + expression: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'DebuggerStatement'; + _DebuggerStatement: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'DoWhileStatement'; + _DoWhileStatement: void; + body: Statement; + test: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'DoExpression'; + _DoExpression: void; + body: Statement; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'EmptyStatement'; + _EmptyStatement: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ExpressionStatement'; + _ExpressionStatement: void; + expression: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'File'; + _File: void; + program: Program; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ForInStatement'; + _ForInStatement: void; + body: Statement; + left: Node; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ForOfStatement'; + _ForOfStatement: void; + body: Statement; + left: Node; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ForStatement'; + _ForStatement: void; + init: ?Node; + test: ?Expression; + update: ?Expression; + body: Statement; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'FunctionDeclaration'; + _FunctionDeclaration: void; + body: BlockStatement; + id: Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; + async: boolean; + defaults: Array; + expression: boolean; + generator: boolean; + params: Array; + rest: ?Identifier; + returnType: ?TypeAnnotation; + typeParameters: ?TypeParameterDeclaration; +} | { + type: 'FunctionExpression'; + _FunctionExpression: void; + body: BlockStatement; + id: ?Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; + async: boolean; + defaults: Array; + expression: boolean; + generator: boolean; + params: Array; + rest: ?Identifier; + returnType: ?TypeAnnotation; + typeParameters: ?TypeParameterDeclaration; +} | { + type: 'Identifier'; + _Identifier: void; + name: string; + typeAnnotation: ?TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'IfStatement'; + _IfStatement: void; + alternate: ?Statement; + consequent: Statement; + test: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ImportDefaultSpecifier'; + _ImportDefaultSpecifier: void; + local: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ImportNamespaceSpecifier'; + _ImportNamespaceSpecifier: void; + local: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ImportDeclaration'; + _ImportDeclaration: void; + specifiers: Array; + source: Literal; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ImportSpecifier'; + _ImportSpecifier: void; + imported: Node; + local: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'LabeledStatement'; + _LabeledStatement: void; + body: Statement; + label: Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'Literal'; + _Literal: void; + raw: string; + regex: ?{pattern: string, flags: string}; + value: ?(string | boolean | number | RegExp); + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'LogicalExpression'; + _LogicalExpression: void; + left: Expression; + operator: LogicalOperator; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'MemberExpression'; + _MemberExpression: void; + computed: boolean; + object: Expression; + property: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'MetaProperty'; + _MetaProperty: void; + meta: Node; + property: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'MethodDefinition'; + _MethodDefinition: void; + computed: boolean; + key: Node; + kind: 'constructor' | 'method' | 'get' | 'set'; + static: boolean; + value: FunctionExpression; + decorators: ?Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'NewExpression'; + _NewExpression: void; + arguments: Array; + callee: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'Noop'; + _Noop: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ObjectExpression'; + _ObjectExpression: void; + properties: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ObjectPattern'; + _ObjectPattern: void; + properties: Array; + typeAnnotation: ?TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'Program'; + _Program: void; + body: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'Property'; + _Property: void; + computed: boolean; + key: Node; + kind: 'init' | 'get' | 'set'; + method: boolean; + shorthand: boolean; + value: Node; + decorators: ?Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'RestElement'; + _RestElement: void; + argument: Pattern; + typeAnnotation: ?TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ReturnStatement'; + _ReturnStatement: void; + argument: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'SequenceExpression'; + _SequenceExpression: void; + expression: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'SpreadElement'; + _SpreadElement: void; + argument: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'SpreadProperty'; + _SpreadProperty: void; + argument: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'Super'; + _Super: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'SwitchCase'; + _SwitchCase: void; + consequent: Array; + test: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'SwitchStatement'; + _SwitchStatement: void; + cases: Array; + discriminant: Expression; + lexical: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'TaggedTemplateExpression'; + _TaggedTemplateExpression: void; + quasi: TemplateLiteral; + tag: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'TemplateElement'; + _TemplateElement: void; + tail: boolean; + value: {cooked: string, raw: string}; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'TemplateLiteral'; + _TemplateLiteral: void; + expressions: Array; + quasis: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ThisExpression'; + _ThisExpression: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ThrowStatement'; + _ThrowStatement: void; + argument: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'TryStatement'; + _TryStatement: void; + block: BlockStatement; + finalizer: ?BlockStatement; + guardedHandlers: Array; + handler: ?CatchClause; + handlers: ?Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'UnaryExpression'; + _UnaryExpression: void; + argument: Expression; + operator: UnaryOperator; + prefix: true; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'UpdateExpression'; + _UpdateExpression: void; + argument: Expression; + operator: UpdateOperator; + prefix: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'VariableDeclaration'; + _VariableDeclaration: void; + declarations: Array; + kind: 'var' | 'let' | 'const'; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'VariableDeclarator'; + _VariableDeclarator: void; + id: Pattern; + init: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'WhileStatement'; + _WhileStatement: void; + body: Statement; + test: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'WithStatement'; + _WithStatement: void; + body: Statement; + object: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'YieldExpression'; + _YieldExpression: void; + argument: ?Expression; + delegate: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ExportAllDeclaration'; + _ExportAllDeclaration: void; + exported: Node; + source: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ExportDefaultDeclaration'; + _ExportDefaultDeclaration: void; + declaration: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ExportNamedDeclaration'; + _ExportNamedDeclaration: void; + declaration: Node; + source: Literal; + specifiers: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ExportDefaultSpecifier'; + _ExportDefaultSpecifier: void; + exported: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ExportNamespaceSpecifier'; + _ExportNamespaceSpecifier: void; + exported: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ExportSpecifier'; + _ExportSpecifier: void; + local: Node; + exported: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'AnyTypeAnnotation'; + _AnyTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ArrayTypeAnnotation'; + _ArrayTypeAnnotation: void; + elementType: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'BooleanLiteralTypeAnnotation'; + _BooleanLiteralTypeAnnotation: void; + raw: string; + value: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'BooleanTypeAnnotation'; + _BooleanTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ClassImplements'; + _ClassImplements: void; + id: Identifier; + typeParameters: ?TypeParameterInstantiation; + superClass: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ClassProperty'; + _ClassProperty: void; + computed: boolean; + key: Node; + static: boolean; + typeAnnotation: ?TypeAnnotation; + value: ?Expression; + decorators: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'DeclareClass'; + _DeclareClass: void; + body: ObjectTypeAnnotation; + extends: Array; + id: Identifier; + typeParameters: ?TypeParameterDeclaration; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'DeclareFunction'; + _DeclareFunction: void; + id: Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'DeclareModule'; + _DeclareModule: void; + body: BlockStatement; + id: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'DeclareVariable'; + _DeclareVariable: void; + id: Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'FunctionTypeAnnotation'; + _FunctionTypeAnnotation: void; + params: Array; + rest: ?FunctionTypeParam; + returnType: Type; + typeParameters: ?TypeParameterDeclaration; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'FunctionTypeParam'; + _FunctionTypeParam: void; + name: Identifier; + optional: boolean; + typeAnnotation: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'GenericTypeAnnotation'; + _GenericTypeAnnotation: void; + id: Node; + typeParameters: ?TypeParameterInstantiation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'InterfaceExtends'; + _InterfaceExtends: void; + id: Identifier; + typeParameters: ?TypeParameterInstantiation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'InterfaceDeclaration'; + _InterfaceDeclaration: void; + body: ObjectTypeAnnotation; + extends: Array; + id: Identifier; + typeParameters: ?TypeParameterDeclaration; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'IntersectionTypeAnnotation'; + _IntersectionTypeAnnotation: void; + types: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'MixedTypeAnnotation'; + _MixedTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'NullableTypeAnnotation'; + _NullableTypeAnnotation: void; + typeAnnotation: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'NumberLiteralTypeAnnotation'; + _NumberLiteralTypeAnnotation: void; + raw: string; + value: number; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'NumberTypeAnnotation'; + _NumberTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'StringLiteralTypeAnnotation'; + _StringLiteralTypeAnnotation: void; + raw: string; + value: string; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'StringTypeAnnotation'; + _StringTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'TupleTypeAnnotation'; + _TupleTypeAnnotation: void; + types: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'TypeofTypeAnnotation'; + _TypeofTypeAnnotation: void; + argument: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'TypeAlias'; + _TypeAlias: void; + id: Identifier; + right: Type; + typeParameters: ?TypeParameterDeclaration; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'TypeAnnotation'; + _TypeAnnotation: void; + typeAnnotation: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'TypeCastExpression'; + _TypeCastExpression: void; + expression: Expression; + typeAnnotation: TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'TypeParameterDeclaration'; + _TypeParameterDeclaration: void; + params: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'TypeParameterInstantiation'; + _TypeParameterInstantiation: void; + params: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ObjectTypeAnnotation'; + _ObjectTypeAnnotation: void; + callProperties: Array; + indexers: Array; + properties: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ObjectTypeCallProperty'; + _ObjectTypeCallProperty: void; + static: boolean; + value: FunctionTypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ObjectTypeIndexer'; + _ObjectTypeIndexer: void; + id: Identifier; + key: Type; + value: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ObjectTypeProperty'; + _ObjectTypeProperty: void; + key: Node; + optional: boolean; + value: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'QualifiedTypeIdentifier'; + _QualifiedTypeIdentifier: void; + id: Identifier; + qualification: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'UnionTypeAnnotation'; + _UnionTypeAnnotation: void; + types: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'VoidTypeAnnotation'; + _VoidTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'JSXAttribute'; + _JSXAttribute: void; + name: Node; + value: ?Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'JSXClosingElement'; + _JSXClosingElement: void; + name: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'JSXElement'; + _JSXElement: void; + children: Array; + closingElement: ?JSXClosingElement; + openingElement: JSXOpeningElement; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'JSXEmptyExpression'; + _JSXEmptyExpression: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'JSXExpressionContainer'; + _JSXExpressionContainer: void; + expression: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'JSXIdentifier'; + _JSXIdentifier: void; + name: string; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'JSXMemberExpression'; + _JSXMemberExpression: void; + computed: boolean; + object: Node; + property: JSXIdentifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'JSXNamespacedName'; + _JSXNamespacedName: void; + name: JSXIdentifier; + namespace: JSXIdentifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'JSXOpeningElement'; + _JSXOpeningElement: void; + attributes: Array; + name: Array; + selfClosing: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'JSXSpreadAttribute'; + _JSXSpreadAttribute: void; + argument: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type Pattern = { + type: 'ArrayPattern'; + _ArrayPattern: void; + elements: Array; + typeAnnotation: ?TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'AssignmentPattern'; + _AssignmentPattern: void; + left: Pattern; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'Identifier'; + _Identifier: void; + name: string; + typeAnnotation: ?TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ObjectPattern'; + _ObjectPattern: void; + properties: Array; + typeAnnotation: ?TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'RestElement'; + _RestElement: void; + argument: Pattern; + typeAnnotation: ?TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type Statement = { + type: 'BlockStatement'; + _BlockStatement: void; + body: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'BreakStatement'; + _BreakStatement: void; + label: ?Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ContinueStatement'; + _ContinueStatement: void; + label: ?Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'DoWhileStatement'; + _DoWhileStatement: void; + body: Statement; + test: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'EmptyStatement'; + _EmptyStatement: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ExpressionStatement'; + _ExpressionStatement: void; + expression: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ForInStatement'; + _ForInStatement: void; + body: Statement; + left: Node; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ForOfStatement'; + _ForOfStatement: void; + body: Statement; + left: Node; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ForStatement'; + _ForStatement: void; + init: ?Node; + test: ?Expression; + update: ?Expression; + body: Statement; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'IfStatement'; + _IfStatement: void; + alternate: ?Statement; + consequent: Statement; + test: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'LabeledStatement'; + _LabeledStatement: void; + body: Statement; + label: Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ReturnStatement'; + _ReturnStatement: void; + argument: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'SwitchStatement'; + _SwitchStatement: void; + cases: Array; + discriminant: Expression; + lexical: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ThrowStatement'; + _ThrowStatement: void; + argument: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'TryStatement'; + _TryStatement: void; + block: BlockStatement; + finalizer: ?BlockStatement; + guardedHandlers: Array; + handler: ?CatchClause; + handlers: ?Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'WhileStatement'; + _WhileStatement: void; + body: Statement; + test: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'WithStatement'; + _WithStatement: void; + body: Statement; + object: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'DeclareClass'; + _DeclareClass: void; + body: ObjectTypeAnnotation; + extends: Array; + id: Identifier; + typeParameters: ?TypeParameterDeclaration; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'DeclareFunction'; + _DeclareFunction: void; + id: Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'DeclareModule'; + _DeclareModule: void; + body: BlockStatement; + id: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'DeclareVariable'; + _DeclareVariable: void; + id: Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'InterfaceDeclaration'; + _InterfaceDeclaration: void; + body: ObjectTypeAnnotation; + extends: Array; + id: Identifier; + typeParameters: ?TypeParameterDeclaration; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'TypeAlias'; + _TypeAlias: void; + id: Identifier; + right: Type; + typeParameters: ?TypeParameterDeclaration; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type Type = { + type: 'AnyTypeAnnotation'; + _AnyTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ArrayTypeAnnotation'; + _ArrayTypeAnnotation: void; + elementType: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'BooleanLiteralTypeAnnotation'; + _BooleanLiteralTypeAnnotation: void; + raw: string; + value: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'BooleanTypeAnnotation'; + _BooleanTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'FunctionTypeAnnotation'; + _FunctionTypeAnnotation: void; + params: Array; + rest: ?FunctionTypeParam; + returnType: Type; + typeParameters: ?TypeParameterDeclaration; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'GenericTypeAnnotation'; + _GenericTypeAnnotation: void; + id: Node; + typeParameters: ?TypeParameterInstantiation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'IntersectionTypeAnnotation'; + _IntersectionTypeAnnotation: void; + types: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'MixedTypeAnnotation'; + _MixedTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'NullableTypeAnnotation'; + _NullableTypeAnnotation: void; + typeAnnotation: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'NumberLiteralTypeAnnotation'; + _NumberLiteralTypeAnnotation: void; + raw: string; + value: number; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'NumberTypeAnnotation'; + _NumberTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'StringLiteralTypeAnnotation'; + _StringLiteralTypeAnnotation: void; + raw: string; + value: string; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'StringTypeAnnotation'; + _StringTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'TupleTypeAnnotation'; + _TupleTypeAnnotation: void; + types: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'ObjectTypeAnnotation'; + _ObjectTypeAnnotation: void; + callProperties: Array; + indexers: Array; + properties: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'UnionTypeAnnotation'; + _UnionTypeAnnotation: void; + types: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +} | { + type: 'VoidTypeAnnotation'; + _VoidTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// Concrete Types. Nothing can extend these. + +export type CommentLine = { + type: 'CommentLine'; + _CommentLine: void; + value: string; + end: number; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; +}; + +export type CommentBlock = { + type: 'CommentBlock'; + _CommentBlock: void; + value: string; + end: number; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; +}; + +// Babel concrete types. + +export type ArrayExpression = { + type: 'ArrayExpression'; + _ArrayExpression: void; + elements: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ArrayPattern = { + type: 'ArrayPattern'; + _ArrayPattern: void; + elements: Array; + typeAnnotation: ?TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ArrowFunctionExpression = { + type: 'ArrowFunctionExpression'; + _ArrowFunctionExpression: void; + body: Node; + id: ?Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; + async: boolean; + defaults: Array; + expression: boolean; + generator: boolean; + params: Array; + rest: ?Identifier; + returnType: ?TypeAnnotation; + typeParameters: ?TypeParameterDeclaration; +}; + +type AssignmentOperator = + '=' | + '+=' | + '-=' | + '*=' | + '/=' | + '%=' | + '<<=' | + '>>=' | + '>>>=' | + '|=' | + '^=' | + '&='; + +export type AssignmentExpression = { + type: 'AssignmentExpression'; + _AssignmentExpression: void; + left: Pattern; + operator: AssignmentOperator; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type AssignmentPattern = { + type: 'AssignmentPattern'; + _AssignmentPattern: void; + left: Pattern; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type AwaitExpression = { + type: 'AwaitExpression'; + _AwaitExpression: void; + all: boolean; + argument: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +type BinaryOperator = + '==' | + '!=' | + '===' | + '!==' | + '<' | + '<=' | + '>' | + '>=' | + '<<' | + '>>' | + '>>>' | + '+' | + '-' | + '*' | + '/' | + '%' | + '&' | + '|' | + '^' | + 'in' | + 'instanceof' | + '..'; + +export type BinaryExpression = { + type: 'BinaryExpression'; + _BinaryExpression: void; + left: Expression; + operator: BinaryOperator; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: What is this? +export type BindExpression = { + type: 'BindExpression'; + _BindExpression: void; + callee: Node; + object: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type BlockStatement = { + type: 'BlockStatement'; + _BlockStatement: void; + body: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type BreakStatement = { + type: 'BreakStatement'; + _BreakStatement: void; + label: ?Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type CallExpression = { + type: 'CallExpression'; + _CallExpression: void; + arguments: Array; + callee: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type CatchClause = { + type: 'CatchClause'; + _CatchClause: void; + body: BlockStatement; + param: Pattern; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ClassBody = { + type: 'ClassBody'; + _ClassBody: void; + body: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ClassDeclaration = { + type: 'ClassDeclaration'; + _ClassDeclaration: void; + body: ClassBody; + id: ?Identifier; + superClass: ?Expression; + decorators: any; + superTypeParameters: any; + typeParameters: any; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ClassExpression = { + type: 'ClassExpression'; + _ClassExpression: void; + body: ClassBody; + id: ?Identifier; + superClass: ?Expression; + decorators: any; + superTypeParameters: any; + typeParameters: any; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ComprehensionBlock = { + type: 'ComprehensionBlock'; + _ComprehensionBlock: void; + each: boolean; + left: Pattern; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ComprehensionExpression = { + type: 'ComprehensionExpression'; + _ComprehensionExpression: void; + body: Expression; + blocks: Array; + filter: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ConditionalExpression = { + type: 'ConditionalExpression'; + _ConditionalExpression: void; + alternate: Expression; + consequent: Expression; + test: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ContinueStatement = { + type: 'ContinueStatement'; + _ContinueStatement: void; + label: ?Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type Decorator = { + type: 'Decorator'; + _Decorator: void; + expression: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type DebuggerStatement = { + type: 'DebuggerStatement'; + _DebuggerStatement: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type DoWhileStatement = { + type: 'DoWhileStatement'; + _DoWhileStatement: void; + body: Statement; + test: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type DoExpression = { + type: 'DoExpression'; + _DoExpression: void; + body: Statement; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type EmptyStatement = { + type: 'EmptyStatement'; + _EmptyStatement: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ExpressionStatement = { + type: 'ExpressionStatement'; + _ExpressionStatement: void; + expression: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type File = { + type: 'File'; + _File: void; + program: Program; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ForInStatement = { + type: 'ForInStatement'; + _ForInStatement: void; + body: Statement; + left: Node; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type ForOfStatement = { + type: 'ForOfStatement'; + _ForOfStatement: void; + body: Statement; + left: Node; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ForStatement = { + type: 'ForStatement'; + _ForStatement: void; + init: ?Node; + test: ?Expression; + update: ?Expression; + body: Statement; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type FunctionDeclaration = { + type: 'FunctionDeclaration'; + _FunctionDeclaration: void; + body: BlockStatement; + id: Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; + async: boolean; + defaults: Array; + expression: boolean; + generator: boolean; + params: Array; + rest: ?Identifier; + returnType: ?TypeAnnotation; + typeParameters: ?TypeParameterDeclaration; +}; + +export type FunctionExpression = { + type: 'FunctionExpression'; + _FunctionExpression: void; + body: BlockStatement; + id: ?Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; + async: boolean; + defaults: Array; + expression: boolean; + generator: boolean; + params: Array; + rest: ?Identifier; + returnType: ?TypeAnnotation; + typeParameters: ?TypeParameterDeclaration; +}; + +export type Identifier = { + type: 'Identifier'; + _Identifier: void; + name: string; + typeAnnotation: ?TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type IfStatement = { + type: 'IfStatement'; + _IfStatement: void; + alternate: ?Statement; + consequent: Statement; + test: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type ImportDefaultSpecifier = { + type: 'ImportDefaultSpecifier'; + _ImportDefaultSpecifier: void; + local: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type ImportNamespaceSpecifier = { + type: 'ImportNamespaceSpecifier'; + _ImportNamespaceSpecifier: void; + local: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type ImportDeclaration = { + type: 'ImportDeclaration'; + _ImportDeclaration: void; + specifiers: Array; + source: Literal; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type ImportSpecifier = { + type: 'ImportSpecifier'; + _ImportSpecifier: void; + imported: Node; + local: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type LabeledStatement = { + type: 'LabeledStatement'; + _LabeledStatement: void; + body: Statement; + label: Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type Literal = { + type: 'Literal'; + _Literal: void; + raw: string; + regex: ?{pattern: string, flags: string}; + value: ?(string | boolean | number | RegExp); + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +type LogicalOperator = '||' | '&&'; + +export type LogicalExpression = { + type: 'LogicalExpression'; + _LogicalExpression: void; + left: Expression; + operator: LogicalOperator; + right: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type MemberExpression = { + type: 'MemberExpression'; + _MemberExpression: void; + computed: boolean; + object: Expression; + property: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type MetaProperty = { + type: 'MetaProperty'; + _MetaProperty: void; + meta: Node; + property: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type MethodDefinition = { + type: 'MethodDefinition'; + _MethodDefinition: void; + computed: boolean; + key: Node; + kind: 'constructor' | 'method' | 'get' | 'set'; + static: boolean; + value: FunctionExpression; + decorators: ?Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type NewExpression = { + type: 'NewExpression'; + _NewExpression: void; + arguments: Array; + callee: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type Noop = { + type: 'Noop'; + _Noop: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ObjectExpression = { + type: 'ObjectExpression'; + _ObjectExpression: void; + properties: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ObjectPattern = { + type: 'ObjectPattern'; + _ObjectPattern: void; + properties: Array; + typeAnnotation: ?TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type Program = { + type: 'Program'; + _Program: void; + body: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type Property = { + type: 'Property'; + _Property: void; + computed: boolean; + key: Node; + kind: 'init' | 'get' | 'set'; + method: boolean; + shorthand: boolean; + value: Node; + decorators: ?Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type RestElement = { + type: 'RestElement'; + _RestElement: void; + argument: Pattern; + typeAnnotation: ?TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ReturnStatement = { + type: 'ReturnStatement'; + _ReturnStatement: void; + argument: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type SequenceExpression = { + type: 'SequenceExpression'; + _SequenceExpression: void; + expression: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type SpreadElement = { + type: 'SpreadElement'; + _SpreadElement: void; + argument: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type SpreadProperty = { + type: 'SpreadProperty'; + _SpreadProperty: void; + argument: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type Super = { + type: 'Super'; + _Super: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type SwitchCase = { + type: 'SwitchCase'; + _SwitchCase: void; + consequent: Array; + test: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type SwitchStatement = { + type: 'SwitchStatement'; + _SwitchStatement: void; + cases: Array; + discriminant: Expression; + lexical: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type TaggedTemplateExpression = { + type: 'TaggedTemplateExpression'; + _TaggedTemplateExpression: void; + quasi: TemplateLiteral; + tag: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type TemplateElement = { + type: 'TemplateElement'; + _TemplateElement: void; + tail: boolean; + value: {cooked: string, raw: string}; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type TemplateLiteral = { + type: 'TemplateLiteral'; + _TemplateLiteral: void; + expressions: Array; + quasis: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ThisExpression = { + type: 'ThisExpression'; + _ThisExpression: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ThrowStatement = { + type: 'ThrowStatement'; + _ThrowStatement: void; + argument: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type TryStatement = { + type: 'TryStatement'; + _TryStatement: void; + block: BlockStatement; + finalizer: ?BlockStatement; + guardedHandlers: Array; + handler: ?CatchClause; + handlers: ?Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +type UnaryOperator = '-' | '+' | '!' | '~' | 'typeof' | 'void' | 'delete'; + +export type UnaryExpression = { + type: 'UnaryExpression'; + _UnaryExpression: void; + argument: Expression; + operator: UnaryOperator; + prefix: true; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +type UpdateOperator = '++' | '--'; + +export type UpdateExpression = { + type: 'UpdateExpression'; + _UpdateExpression: void; + argument: Expression; + operator: UpdateOperator; + prefix: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type VariableDeclaration = { + type: 'VariableDeclaration'; + _VariableDeclaration: void; + declarations: Array; + kind: 'var' | 'let' | 'const'; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type VariableDeclarator = { + type: 'VariableDeclarator'; + _VariableDeclarator: void; + id: Pattern; + init: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type WhileStatement = { + type: 'WhileStatement'; + _WhileStatement: void; + body: Statement; + test: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type WithStatement = { + type: 'WithStatement'; + _WithStatement: void; + body: Statement; + object: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type YieldExpression = { + type: 'YieldExpression'; + _YieldExpression: void; + argument: ?Expression; + delegate: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type ExportAllDeclaration = { + type: 'ExportAllDeclaration'; + _ExportAllDeclaration: void; + exported: Node; + source: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type ExportDefaultDeclaration = { + type: 'ExportDefaultDeclaration'; + _ExportDefaultDeclaration: void; + declaration: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type ExportNamedDeclaration = { + type: 'ExportNamedDeclaration'; + _ExportNamedDeclaration: void; + declaration: Node; + source: Literal; + specifiers: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type ExportDefaultSpecifier = { + type: 'ExportDefaultSpecifier'; + _ExportDefaultSpecifier: void; + exported: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type ExportNamespaceSpecifier = { + type: 'ExportNamespaceSpecifier'; + _ExportNamespaceSpecifier: void; + exported: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type ExportSpecifier = { + type: 'ExportSpecifier'; + _ExportSpecifier: void; + local: Node; + exported: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type AnyTypeAnnotation = { + type: 'AnyTypeAnnotation'; + _AnyTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ArrayTypeAnnotation = { + type: 'ArrayTypeAnnotation'; + _ArrayTypeAnnotation: void; + elementType: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type BooleanLiteralTypeAnnotation = { + type: 'BooleanLiteralTypeAnnotation'; + _BooleanLiteralTypeAnnotation: void; + raw: string; + value: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type BooleanTypeAnnotation = { + type: 'BooleanTypeAnnotation'; + _BooleanTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ClassImplements = { + type: 'ClassImplements'; + _ClassImplements: void; + id: Identifier; + typeParameters: ?TypeParameterInstantiation; + superClass: ?Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ClassProperty = { + type: 'ClassProperty'; + _ClassProperty: void; + computed: boolean; + key: Node; + static: boolean; + typeAnnotation: ?TypeAnnotation; + value: ?Expression; + decorators: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type DeclareClass = { + type: 'DeclareClass'; + _DeclareClass: void; + body: ObjectTypeAnnotation; + extends: Array; + id: Identifier; + typeParameters: ?TypeParameterDeclaration; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type DeclareFunction = { + type: 'DeclareFunction'; + _DeclareFunction: void; + id: Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type DeclareModule = { + type: 'DeclareModule'; + _DeclareModule: void; + body: BlockStatement; + id: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +// TODO: Make this correct. +export type DeclareVariable = { + type: 'DeclareVariable'; + _DeclareVariable: void; + id: Identifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type FunctionTypeAnnotation = { + type: 'FunctionTypeAnnotation'; + _FunctionTypeAnnotation: void; + params: Array; + rest: ?FunctionTypeParam; + returnType: Type; + typeParameters: ?TypeParameterDeclaration; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type FunctionTypeParam = { + type: 'FunctionTypeParam'; + _FunctionTypeParam: void; + name: Identifier; + optional: boolean; + typeAnnotation: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type GenericTypeAnnotation = { + type: 'GenericTypeAnnotation'; + _GenericTypeAnnotation: void; + id: Node; + typeParameters: ?TypeParameterInstantiation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type InterfaceExtends = { + type: 'InterfaceExtends'; + _InterfaceExtends: void; + id: Identifier; + typeParameters: ?TypeParameterInstantiation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type InterfaceDeclaration = { + type: 'InterfaceDeclaration'; + _InterfaceDeclaration: void; + body: ObjectTypeAnnotation; + extends: Array; + id: Identifier; + typeParameters: ?TypeParameterDeclaration; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type IntersectionTypeAnnotation = { + type: 'IntersectionTypeAnnotation'; + _IntersectionTypeAnnotation: void; + types: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type MixedTypeAnnotation = { + type: 'MixedTypeAnnotation'; + _MixedTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type NullableTypeAnnotation = { + type: 'NullableTypeAnnotation'; + _NullableTypeAnnotation: void; + typeAnnotation: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type NumberLiteralTypeAnnotation = { + type: 'NumberLiteralTypeAnnotation'; + _NumberLiteralTypeAnnotation: void; + raw: string; + value: number; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type NumberTypeAnnotation = { + type: 'NumberTypeAnnotation'; + _NumberTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type StringLiteralTypeAnnotation = { + type: 'StringLiteralTypeAnnotation'; + _StringLiteralTypeAnnotation: void; + raw: string; + value: string; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type StringTypeAnnotation = { + type: 'StringTypeAnnotation'; + _StringTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type TupleTypeAnnotation = { + type: 'TupleTypeAnnotation'; + _TupleTypeAnnotation: void; + types: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type TypeofTypeAnnotation = { + type: 'TypeofTypeAnnotation'; + _TypeofTypeAnnotation: void; + argument: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type TypeAlias = { + type: 'TypeAlias'; + _TypeAlias: void; + id: Identifier; + right: Type; + typeParameters: ?TypeParameterDeclaration; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type TypeAnnotation = { + type: 'TypeAnnotation'; + _TypeAnnotation: void; + typeAnnotation: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type TypeCastExpression = { + type: 'TypeCastExpression'; + _TypeCastExpression: void; + expression: Expression; + typeAnnotation: TypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type TypeParameterDeclaration = { + type: 'TypeParameterDeclaration'; + _TypeParameterDeclaration: void; + params: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type TypeParameterInstantiation = { + type: 'TypeParameterInstantiation'; + _TypeParameterInstantiation: void; + params: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ObjectTypeAnnotation = { + type: 'ObjectTypeAnnotation'; + _ObjectTypeAnnotation: void; + callProperties: Array; + indexers: Array; + properties: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ObjectTypeCallProperty = { + type: 'ObjectTypeCallProperty'; + _ObjectTypeCallProperty: void; + static: boolean; + value: FunctionTypeAnnotation; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ObjectTypeIndexer = { + type: 'ObjectTypeIndexer'; + _ObjectTypeIndexer: void; + id: Identifier; + key: Type; + value: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type ObjectTypeProperty = { + type: 'ObjectTypeProperty'; + _ObjectTypeProperty: void; + key: Node; + optional: boolean; + value: Type; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type QualifiedTypeIdentifier = { + type: 'QualifiedTypeIdentifier'; + _QualifiedTypeIdentifier: void; + id: Identifier; + qualification: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type UnionTypeAnnotation = { + type: 'UnionTypeAnnotation'; + _UnionTypeAnnotation: void; + types: Array; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type VoidTypeAnnotation = { + type: 'VoidTypeAnnotation'; + _VoidTypeAnnotation: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type JSXAttribute = { + type: 'JSXAttribute'; + _JSXAttribute: void; + name: Node; + value: ?Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type JSXClosingElement = { + type: 'JSXClosingElement'; + _JSXClosingElement: void; + name: Node; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type JSXElement = { + type: 'JSXElement'; + _JSXElement: void; + children: Array; + closingElement: ?JSXClosingElement; + openingElement: JSXOpeningElement; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type JSXEmptyExpression = { + type: 'JSXEmptyExpression'; + _JSXEmptyExpression: void; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type JSXExpressionContainer = { + type: 'JSXExpressionContainer'; + _JSXExpressionContainer: void; + expression: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type JSXIdentifier = { + type: 'JSXIdentifier'; + _JSXIdentifier: void; + name: string; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type JSXMemberExpression = { + type: 'JSXMemberExpression'; + _JSXMemberExpression: void; + computed: boolean; + object: Node; + property: JSXIdentifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type JSXNamespacedName = { + type: 'JSXNamespacedName'; + _JSXNamespacedName: void; + name: JSXIdentifier; + namespace: JSXIdentifier; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type JSXOpeningElement = { + type: 'JSXOpeningElement'; + _JSXOpeningElement: void; + attributes: Array; + name: Array; + selfClosing: boolean; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; + +export type JSXSpreadAttribute = { + type: 'JSXSpreadAttribute'; + _JSXSpreadAttribute: void; + argument: Expression; + end: number; + innerComments: ?Array; + leadingComments: ?Array; + loc: { + end: {column: number, line: number}, + start: {column: number, line: number}, + }; + start: number; + trailingComments: ?Array; +}; diff --git a/tests/type_args_nonstrict/__snapshots__/jsfmt.spec.js.snap b/tests/type_args_nonstrict/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..a7603c13b402 --- /dev/null +++ b/tests/type_args_nonstrict/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,91 @@ +exports[`test test.js 1`] = ` +"/** + * Test nonstrict type param arity checking, + * as enabled by + * + * [options] + * experimental.strict_type_args=false + * + * in .flowconfig. + * + * @flow + */ + +// no arity error in type annotation using polymorphic class + +class MyClass { + x: T; + constructor(x: T) { + this.x = x; + } +} + +var c: MyClass = new MyClass(0); // no error + +// no arity error in type annotation using polymorphic class with defaulting + +class MyClass2 { + x: T; + y: U; + constructor(x: T, y: U) { + this.x = x; + this.y = y; + } +} + +var c2: MyClass2 = new MyClass2(0, \"\"); // no error + +// no arity error in type annotation using polymorphic type alias + +type MyObject = { + x: T; +} + +var o: MyObject = { x: 0 }; // no error + +// arity error in type alias rhs + +type MySubobject = { y: number } & MyObject; // no error + +// arity error in interface extends + +interface MyInterface { + x: T; +} + +interface MySubinterface extends MyInterface { // no error + y: number; +} + +// no arity error in extends of polymorphic class + +class MySubclass extends MyClass { // ok, type arg inferred + y: number; + constructor(y: number) { + super(y); + } +} + +// no arity error in call of polymorphic function + +function singleton(x: T):Array { return [x]; } + +var num_array:Array = singleton(0); // ok, type arg inferred +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1172:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/type_args_nonstrict/jsfmt.spec.js b/tests/type_args_nonstrict/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/type_args_nonstrict/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/type_args_nonstrict/test.js b/tests/type_args_nonstrict/test.js new file mode 100644 index 000000000000..79ad1329f72b --- /dev/null +++ b/tests/type_args_nonstrict/test.js @@ -0,0 +1,72 @@ +/** + * Test nonstrict type param arity checking, + * as enabled by + * + * [options] + * experimental.strict_type_args=false + * + * in .flowconfig. + * + * @flow + */ + +// no arity error in type annotation using polymorphic class + +class MyClass { + x: T; + constructor(x: T) { + this.x = x; + } +} + +var c: MyClass = new MyClass(0); // no error + +// no arity error in type annotation using polymorphic class with defaulting + +class MyClass2 { + x: T; + y: U; + constructor(x: T, y: U) { + this.x = x; + this.y = y; + } +} + +var c2: MyClass2 = new MyClass2(0, ""); // no error + +// no arity error in type annotation using polymorphic type alias + +type MyObject = { + x: T; +} + +var o: MyObject = { x: 0 }; // no error + +// arity error in type alias rhs + +type MySubobject = { y: number } & MyObject; // no error + +// arity error in interface extends + +interface MyInterface { + x: T; +} + +interface MySubinterface extends MyInterface { // no error + y: number; +} + +// no arity error in extends of polymorphic class + +class MySubclass extends MyClass { // ok, type arg inferred + y: number; + constructor(y: number) { + super(y); + } +} + +// no arity error in call of polymorphic function + +function singleton(x: T):Array { return [x]; } + +var num_array:Array = singleton(0); // ok, type arg inferred diff --git a/tests/type_args_strict/__snapshots__/jsfmt.spec.js.snap b/tests/type_args_strict/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..07374c543490 --- /dev/null +++ b/tests/type_args_strict/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,90 @@ +exports[`test test.js 1`] = ` +"/** + * Test strict type param arity checking. + * + * Full type argument lists are required in type expressions, + * such as type annotations and interface extends clauses. + * Type arguments are optional in value expressions, such as + * class extends clauses and calls of polymorphic functions. + * + * @flow + */ + +// arity error in type annotation using polymorphic class + +class MyClass { + x: T; + constructor(x: T) { + this.x = x; + } +} + +var c: MyClass = new MyClass(0); // error, missing argument list (1) + +// arity error in type annotation using polymorphic class with defaulting + +class MyClass2 { + x: T; + y: U; + constructor(x: T, y: U) { + this.x = x; + this.y = y; + } +} + +var c2: MyClass2 = new MyClass2(0, \"\"); // error, missing argument list (1-2) + +// arity error in type annotation using polymorphic type alias + +type MyObject = { + x: T; +} + +var o: MyObject = { x: 0 }; // error, missing argument list + +// arity error in type alias rhs + +type MySubobject = { y: number } & MyObject; // error, missing argument list + +// arity error in interface extends + +interface MyInterface { + x: T; +} + +interface MySubinterface extends MyInterface { // error, missing argument list + y: number; +} + +// *no* arity error in extends of polymorphic class + +class MySubclass extends MyClass { // ok, type arg inferred + y: number; + constructor(y: number) { + super(y); + } +} + +// *no* arity error in call of polymorphic function + +function singleton(x: T):Array { return [x]; } + +var num_array:Array = singleton(0); // ok, type arg inferred +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1172:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/type_args_strict/jsfmt.spec.js b/tests/type_args_strict/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/type_args_strict/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/type_args_strict/test.js b/tests/type_args_strict/test.js new file mode 100644 index 000000000000..785285f5e63c --- /dev/null +++ b/tests/type_args_strict/test.js @@ -0,0 +1,71 @@ +/** + * Test strict type param arity checking. + * + * Full type argument lists are required in type expressions, + * such as type annotations and interface extends clauses. + * Type arguments are optional in value expressions, such as + * class extends clauses and calls of polymorphic functions. + * + * @flow + */ + +// arity error in type annotation using polymorphic class + +class MyClass { + x: T; + constructor(x: T) { + this.x = x; + } +} + +var c: MyClass = new MyClass(0); // error, missing argument list (1) + +// arity error in type annotation using polymorphic class with defaulting + +class MyClass2 { + x: T; + y: U; + constructor(x: T, y: U) { + this.x = x; + this.y = y; + } +} + +var c2: MyClass2 = new MyClass2(0, ""); // error, missing argument list (1-2) + +// arity error in type annotation using polymorphic type alias + +type MyObject = { + x: T; +} + +var o: MyObject = { x: 0 }; // error, missing argument list + +// arity error in type alias rhs + +type MySubobject = { y: number } & MyObject; // error, missing argument list + +// arity error in interface extends + +interface MyInterface { + x: T; +} + +interface MySubinterface extends MyInterface { // error, missing argument list + y: number; +} + +// *no* arity error in extends of polymorphic class + +class MySubclass extends MyClass { // ok, type arg inferred + y: number; + constructor(y: number) { + super(y); + } +} + +// *no* arity error in call of polymorphic function + +function singleton(x: T):Array { return [x]; } + +var num_array:Array = singleton(0); // ok, type arg inferred diff --git a/tests/type_only_vars/A.js b/tests/type_only_vars/A.js new file mode 100644 index 000000000000..9473797985ff --- /dev/null +++ b/tests/type_only_vars/A.js @@ -0,0 +1,14 @@ +/** + * @flow + */ + +class Foo { +} + +class Bar { +} + +module.exports = { + Foo: Foo, + Bar: Bar +}; diff --git a/tests/type_only_vars/__snapshots__/jsfmt.spec.js.snap b/tests/type_only_vars/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..7ddfb0e9f771 --- /dev/null +++ b/tests/type_only_vars/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,174 @@ +exports[`test A.js 1`] = ` +"/** + * @flow + */ + +class Foo { +} + +class Bar { +} + +module.exports = { + Foo: Foo, + Bar: Bar +}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +class Foo {} +class Bar {} +module.exports = { Foo: Foo, Bar: Bar }; + +" +`; + +exports[`test bad_shadowing.js 1`] = ` +"/** + * @flow + */ + +import typeof A from \"./A.js\"; +import type {Foo, Bar as Baz} from \"./A.js\"; + +type duck = { + quack: () => string; +} + +// These string types should confict with the imported types +var A: string = \"Hello\"; +var Foo: string = \"Goodbye\"; +var Baz: string = \"Go away please\"; + +// This string type should conflict with the typedef +var duck: string = \"quack\"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test good_shadowing.js 1`] = ` +"/** + * @flow + */ + +import typeof A from \"./A.js\"; +import type {Foo, Bar as Baz} from \"./A.js\"; + +var A = require(\'./A.js\'); +var Foo = A.Foo; +var Baz = A.Bar; + +// errors in prev block leave type bindings in place, so these are errors +var m = A; +var n = Foo; +var o = Baz; + +// errors from value positions only +var a: Foo = new Foo(); +var b: Foo = new A.Foo(); +(new A.Bar(): Baz); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +import typeof A from \"./A.js\"; +import type { Foo, Bar as Baz } from \"./A.js\"; +var A = require(\"./A.js\"); +var Foo = A.Foo; +var Baz = A.Bar; +// errors in prev block leave type bindings in place, so these are errors +var m = A; +var n = Foo; +var o = Baz; +// errors from value positions only +var a: Foo = new Foo(); +var b: Foo = new A.Foo(); +(new A.Bar(): Baz); + +" +`; + +exports[`test import_type.js 1`] = ` +"/** + * @flow + */ + +import typeof A from \"./A.js\"; +import type {Foo, Bar as Baz} from \"./A.js\"; + +var actualA = require(\'./A.js\'); + +// You can\'t use it as an identifier +var m = A; +var n = Foo; +var o = Baz; + +// But using it in a type should still work +var a: Foo = new actualA.Foo(); +(new actualA.Bar(): Baz); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +import typeof A from \"./A.js\"; +import type { Foo, Bar as Baz } from \"./A.js\"; +var actualA = require(\"./A.js\"); +// You can\'t use it as an identifier +var m = A; +var n = Foo; +var o = Baz; +// But using it in a type should still work +var a: Foo = new actualA.Foo(); +(new actualA.Bar(): Baz); + +" +`; + +exports[`test type_alias.js 1`] = ` +"/** + * @flow + */ + +type Foo = number; + +// You can\'t use it as an identifier +var x = Foo; + +// But using it in a type should still work +var a: Foo = 123; +var b: Array = [123]; +type c = Foo; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/type_only_vars/bad_shadowing.js b/tests/type_only_vars/bad_shadowing.js new file mode 100644 index 000000000000..44fda51444e7 --- /dev/null +++ b/tests/type_only_vars/bad_shadowing.js @@ -0,0 +1,18 @@ +/** + * @flow + */ + +import typeof A from "./A.js"; +import type {Foo, Bar as Baz} from "./A.js"; + +type duck = { + quack: () => string; +} + +// These string types should confict with the imported types +var A: string = "Hello"; +var Foo: string = "Goodbye"; +var Baz: string = "Go away please"; + +// This string type should conflict with the typedef +var duck: string = "quack"; diff --git a/tests/type_only_vars/good_shadowing.js b/tests/type_only_vars/good_shadowing.js new file mode 100644 index 000000000000..010dbabfebbb --- /dev/null +++ b/tests/type_only_vars/good_shadowing.js @@ -0,0 +1,20 @@ +/** + * @flow + */ + +import typeof A from "./A.js"; +import type {Foo, Bar as Baz} from "./A.js"; + +var A = require('./A.js'); +var Foo = A.Foo; +var Baz = A.Bar; + +// errors in prev block leave type bindings in place, so these are errors +var m = A; +var n = Foo; +var o = Baz; + +// errors from value positions only +var a: Foo = new Foo(); +var b: Foo = new A.Foo(); +(new A.Bar(): Baz); diff --git a/tests/type_only_vars/import_type.js b/tests/type_only_vars/import_type.js new file mode 100644 index 000000000000..b8250c2eff86 --- /dev/null +++ b/tests/type_only_vars/import_type.js @@ -0,0 +1,17 @@ +/** + * @flow + */ + +import typeof A from "./A.js"; +import type {Foo, Bar as Baz} from "./A.js"; + +var actualA = require('./A.js'); + +// You can't use it as an identifier +var m = A; +var n = Foo; +var o = Baz; + +// But using it in a type should still work +var a: Foo = new actualA.Foo(); +(new actualA.Bar(): Baz); diff --git a/tests/type_only_vars/jsfmt.spec.js b/tests/type_only_vars/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/type_only_vars/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/type_only_vars/type_alias.js b/tests/type_only_vars/type_alias.js new file mode 100644 index 000000000000..1633b664d2a7 --- /dev/null +++ b/tests/type_only_vars/type_alias.js @@ -0,0 +1,13 @@ +/** + * @flow + */ + +type Foo = number; + +// You can't use it as an identifier +var x = Foo; + +// But using it in a type should still work +var a: Foo = 123; +var b: Array = [123]; +type c = Foo; diff --git a/tests/type_param_defaults/__snapshots__/jsfmt.spec.js.snap b/tests/type_param_defaults/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..3e30daf61552 --- /dev/null +++ b/tests/type_param_defaults/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,84 @@ +exports[`test classes.js 1`] = ` +"/* @flow */ + +class A { + p: T; + constructor(p: T) { + this.p = p; + } +} + +// Test out simple defaults +class B extends A {} + +var b_number: B = new B(123); +var b_void: B = new B(); +var b_default: B<> = new B(\'hello\'); + +var b_star: B<*> = new B(123); + +(b_number.p: boolean); // Error number ~> boolean +(b_void.p: boolean); // Error void ~> boolean +(b_default.p: boolean); // Error string ~> boolean + +(b_star.p: boolean); // Error number ~> boolean + +class C extends A {} + +var c_number: C = new C(123); // Error number ~> ?string +var c_void: C = new C(); +var c_default: C<> = new C(\'hello\'); +var c_star: C<*> = new C(\'hello\'); + +(c_void.p: boolean); // Error void ~> boolean +(c_default.p: boolean); // Error string ~> boolean +(c_star.p: boolean); // Error string ~> boolean + +class D extends A {} +var d_number: D = new D(123); +var d_void: D = new D(); +var d_default: D = new D(\'hello\'); +var d_too_few_args: D<> = new D(\'hello\'); // Error too few tparams +var d_too_many: D = new D(\'hello\'); // Error too many tparams +var d_star: D<*> = new D(10); // error, number ~> string + +(d_number.p: boolean); // Error number ~> boolean +(d_void.p: boolean); // Error void ~> boolean +(d_default.p: boolean); // Error string ~> boolean +(d_star.p: boolean); // Error number ~> boolean + +class E {} // Error: string ~> number +class F {} // Error: number ~> string + +class G extends A {} + +var g_default: G = new G(\'hello\'); + +(g_default.p: boolean); // Error string ~> boolean + +class H {} // Error - can\'t refer to T before it\'s defined + +class I extends A {} + +var i_number: I = new I(123); // Error number ~> ?string +var i_void: I = new I(); +var i_default: I<> = new I(\'hello\'); +var i_star: I<*> = new I(\'hello\'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1172:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/type_param_defaults/classes.js b/tests/type_param_defaults/classes.js new file mode 100644 index 000000000000..3c6797bc00c9 --- /dev/null +++ b/tests/type_param_defaults/classes.js @@ -0,0 +1,65 @@ +/* @flow */ + +class A { + p: T; + constructor(p: T) { + this.p = p; + } +} + +// Test out simple defaults +class B extends A {} + +var b_number: B = new B(123); +var b_void: B = new B(); +var b_default: B<> = new B('hello'); + +var b_star: B<*> = new B(123); + +(b_number.p: boolean); // Error number ~> boolean +(b_void.p: boolean); // Error void ~> boolean +(b_default.p: boolean); // Error string ~> boolean + +(b_star.p: boolean); // Error number ~> boolean + +class C extends A {} + +var c_number: C = new C(123); // Error number ~> ?string +var c_void: C = new C(); +var c_default: C<> = new C('hello'); +var c_star: C<*> = new C('hello'); + +(c_void.p: boolean); // Error void ~> boolean +(c_default.p: boolean); // Error string ~> boolean +(c_star.p: boolean); // Error string ~> boolean + +class D extends A {} +var d_number: D = new D(123); +var d_void: D = new D(); +var d_default: D = new D('hello'); +var d_too_few_args: D<> = new D('hello'); // Error too few tparams +var d_too_many: D = new D('hello'); // Error too many tparams +var d_star: D<*> = new D(10); // error, number ~> string + +(d_number.p: boolean); // Error number ~> boolean +(d_void.p: boolean); // Error void ~> boolean +(d_default.p: boolean); // Error string ~> boolean +(d_star.p: boolean); // Error number ~> boolean + +class E {} // Error: string ~> number +class F {} // Error: number ~> string + +class G extends A {} + +var g_default: G = new G('hello'); + +(g_default.p: boolean); // Error string ~> boolean + +class H {} // Error - can't refer to T before it's defined + +class I extends A {} + +var i_number: I = new I(123); // Error number ~> ?string +var i_void: I = new I(); +var i_default: I<> = new I('hello'); +var i_star: I<*> = new I('hello'); diff --git a/tests/type_param_defaults/jsfmt.spec.js b/tests/type_param_defaults/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/type_param_defaults/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/type_param_scope/__snapshots__/jsfmt.spec.js.snap b/tests/type_param_scope/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..355cffb598e1 --- /dev/null +++ b/tests/type_param_scope/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,132 @@ +exports[`test class.js 1`] = ` +"class C { + a(x:T, a:A) { + this.b(x); // ok + this.b(a); // error: A ~> incompatible instance of T + } + + b(x:T) {} +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1172:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test default_params.js 1`] = ` +"function f(a:T) { + function g(b:U, c:T = a) { + function h(d:U = b) {} + h(); // ok + h(b); // ok + h(c); // err, T ~> U + } + g(0); // ok + g(0,a); // ok + g(0,0); // error: number ~> T +} +f(0); // ok +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:340:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test method_shadow.js 1`] = ` +"// Ensure method type params properly shadow outer type params. Subclass ensures +// the generated insttype has the correct tvars. Should behave the same for +// classes, interfaces, and declared classes. + +class A { + x:T; + constructor(x:T) { this.x = x } + m(x:T):A { return new A(x) } +} + +class B extends A { + m(x:T):B { return new B(x) } +} + +interface C { + m(x:T):C; +} + +interface D extends C { + m(x:T):D; +} + +declare class E { + m(x:T):E; +} + +declare class F extends E { + m(x:T):F; +} + + +// Bounds can refer to parent type params (until they are shadowed). + +class G { + x:T; + constructor(x:T) { this.x = x } + m(x:T):G { return new G(x) } // T-as-bound is G\'s T +} + +declare var g: G; +g.m(0); // ok +g.m(true); // err, bool ~> number|string +(g.m(\"\"): G); // err, string ~> number + + +// Shadow bounds incompatible with parent + +class H { + x:T; + m(x:T) { + this.x = x; // err, m\'s T != H\'s T + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1172:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/type_param_scope/class.js b/tests/type_param_scope/class.js new file mode 100644 index 000000000000..ef25adc20642 --- /dev/null +++ b/tests/type_param_scope/class.js @@ -0,0 +1,8 @@ +class C { + a(x:T, a:A) { + this.b(x); // ok + this.b(a); // error: A ~> incompatible instance of T + } + + b(x:T) {} +} diff --git a/tests/type_param_scope/default_params.js b/tests/type_param_scope/default_params.js new file mode 100644 index 000000000000..1c48b67b206c --- /dev/null +++ b/tests/type_param_scope/default_params.js @@ -0,0 +1,12 @@ +function f(a:T) { + function g(b:U, c:T = a) { + function h(d:U = b) {} + h(); // ok + h(b); // ok + h(c); // err, T ~> U + } + g(0); // ok + g(0,a); // ok + g(0,0); // error: number ~> T +} +f(0); // ok diff --git a/tests/type_param_scope/jsfmt.spec.js b/tests/type_param_scope/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/type_param_scope/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/type_param_scope/method_shadow.js b/tests/type_param_scope/method_shadow.js new file mode 100644 index 000000000000..93b1223c6f37 --- /dev/null +++ b/tests/type_param_scope/method_shadow.js @@ -0,0 +1,53 @@ +// Ensure method type params properly shadow outer type params. Subclass ensures +// the generated insttype has the correct tvars. Should behave the same for +// classes, interfaces, and declared classes. + +class A { + x:T; + constructor(x:T) { this.x = x } + m(x:T):A { return new A(x) } +} + +class B extends A { + m(x:T):B { return new B(x) } +} + +interface C { + m(x:T):C; +} + +interface D extends C { + m(x:T):D; +} + +declare class E { + m(x:T):E; +} + +declare class F extends E { + m(x:T):F; +} + + +// Bounds can refer to parent type params (until they are shadowed). + +class G { + x:T; + constructor(x:T) { this.x = x } + m(x:T):G { return new G(x) } // T-as-bound is G's T +} + +declare var g: G; +g.m(0); // ok +g.m(true); // err, bool ~> number|string +(g.m(""): G); // err, string ~> number + + +// Shadow bounds incompatible with parent + +class H { + x:T; + m(x:T) { + this.x = x; // err, m's T != H's T + } +} diff --git a/tests/type_param_variance/__snapshots__/jsfmt.spec.js.snap b/tests/type_param_variance/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..a5ec84a76359 --- /dev/null +++ b/tests/type_param_variance/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,46 @@ +exports[`test promise.js 1`] = ` +"/** + * At the moment, all type params are invariant with + * the exception of the single param to the Promise class, + * which is covariant. + * + * Explicit variance control via annotation is coming, + * but not immediately. In the meantime, Promise\'s + * participation in async/await makes certain kinds of + * errors onerous (and nonobvious) without covariance. + * + * @flow + */ + +async function foo(x: boolean): Promise { + if (x) { + return {bar: \'baz\'}; // OK, because of covariant type param + } else { + return null; + } +} + +async function run() { + console.log(await foo(true)); + console.log(await foo(false)); +} + +run() +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/type_param_variance/jsfmt.spec.js b/tests/type_param_variance/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/type_param_variance/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/type_param_variance/promise.js b/tests/type_param_variance/promise.js new file mode 100644 index 000000000000..221bc1e52dbc --- /dev/null +++ b/tests/type_param_variance/promise.js @@ -0,0 +1,27 @@ +/** + * At the moment, all type params are invariant with + * the exception of the single param to the Promise class, + * which is covariant. + * + * Explicit variance control via annotation is coming, + * but not immediately. In the meantime, Promise's + * participation in async/await makes certain kinds of + * errors onerous (and nonobvious) without covariance. + * + * @flow + */ + +async function foo(x: boolean): Promise { + if (x) { + return {bar: 'baz'}; // OK, because of covariant type param + } else { + return null; + } +} + +async function run() { + console.log(await foo(true)); + console.log(await foo(false)); +} + +run() diff --git a/tests/type_param_variance2/__snapshots__/jsfmt.spec.js.snap b/tests/type_param_variance2/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..3d2b133ccb94 --- /dev/null +++ b/tests/type_param_variance2/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,43 @@ +exports[`test promise.js 1`] = ` +"/** + * Here the definition of Promise is routed to the class Promise + * in the user-specified library libs/Promise.js + * + * In such situations we must desugar async/await primitives + * to the shadowed library definition. + * + * @flow + */ + +async function foo(x: boolean): Promise { + if (x) { + return {bar: \'baz\'}; // OK, because of covariant type param + } else { + return null; + } +} + +async function run() { + console.log(await foo(true)); + console.log(await foo(false)); +} + +run() +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/type_param_variance2/jsfmt.spec.js b/tests/type_param_variance2/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/type_param_variance2/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/type_param_variance2/libs/Promise.js b/tests/type_param_variance2/libs/Promise.js new file mode 100644 index 000000000000..a0b2a5ce810e --- /dev/null +++ b/tests/type_param_variance2/libs/Promise.js @@ -0,0 +1,47 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +// These annotations are copy/pasted from the built-in Flow definitions for +// Native Promises (https://phabricator.fb.com/P19792689), +// with www-specific additions added in. + +// Any definitions here will override similarly-named ones in +// library files declared earlier, including default flow libs. + +declare class Promise { + constructor(callback: ( + resolve: (result?: Promise | R) => void, + reject: (error?: any) => void + ) => void): void; + + then( + onFulfill?: ?(value: R) => Promise | ?U, + onReject?: ?(error: any) => Promise | ?U + ): Promise; + + done( + onFulfill?: ?(value: R) => void, + onReject?: ?(error: any) => void + ): void; + + catch( + onReject?: (error: any) => ?Promise | U + ): Promise; + + static resolve(object?: Promise | T): Promise; + static reject(error?: any): Promise; + + // Non-standard APIs + finally( + onSettled?: ?(value: any) => Promise | U + ): Promise; + + static cast(object?: T): Promise; + static all( + promises: Array | T>, + ): Promise>; + static race(promises: Array>): Promise; + + static allObject( + promisesByKey: T + ): Promise<{[key: $Keys]: any}>; +} diff --git a/tests/type_param_variance2/libs/__snapshots__/jsfmt.spec.js.snap b/tests/type_param_variance2/libs/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..acce7f354305 --- /dev/null +++ b/tests/type_param_variance2/libs/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,66 @@ +exports[`test Promise.js 1`] = ` +"// Copyright 2004-present Facebook. All Rights Reserved. + +// These annotations are copy/pasted from the built-in Flow definitions for +// Native Promises (https://phabricator.fb.com/P19792689), +// with www-specific additions added in. + +// Any definitions here will override similarly-named ones in +// library files declared earlier, including default flow libs. + +declare class Promise { + constructor(callback: ( + resolve: (result?: Promise | R) => void, + reject: (error?: any) => void + ) => void): void; + + then( + onFulfill?: ?(value: R) => Promise | ?U, + onReject?: ?(error: any) => Promise | ?U + ): Promise; + + done( + onFulfill?: ?(value: R) => void, + onReject?: ?(error: any) => void + ): void; + + catch( + onReject?: (error: any) => ?Promise | U + ): Promise; + + static resolve(object?: Promise | T): Promise; + static reject(error?: any): Promise; + + // Non-standard APIs + finally( + onSettled?: ?(value: any) => Promise | U + ): Promise; + + static cast(object?: T): Promise; + static all( + promises: Array | T>, + ): Promise>; + static race(promises: Array>): Promise; + + static allObject( + promisesByKey: T + ): Promise<{[key: $Keys]: any}>; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/type_param_variance2/libs/jsfmt.spec.js b/tests/type_param_variance2/libs/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/type_param_variance2/libs/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/type_param_variance2/promise.js b/tests/type_param_variance2/promise.js new file mode 100644 index 000000000000..1d09fca1ebe4 --- /dev/null +++ b/tests/type_param_variance2/promise.js @@ -0,0 +1,24 @@ +/** + * Here the definition of Promise is routed to the class Promise + * in the user-specified library libs/Promise.js + * + * In such situations we must desugar async/await primitives + * to the shadowed library definition. + * + * @flow + */ + +async function foo(x: boolean): Promise { + if (x) { + return {bar: 'baz'}; // OK, because of covariant type param + } else { + return null; + } +} + +async function run() { + console.log(await foo(true)); + console.log(await foo(false)); +} + +run() diff --git a/tests/typeapp_perf/__snapshots__/jsfmt.spec.js.snap b/tests/typeapp_perf/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..11abc849d200 --- /dev/null +++ b/tests/typeapp_perf/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,79 @@ +exports[`test test1.js 1`] = ` +"/* This test ensures that the code below does not take a long time to check. If + * this test is taking a very long time to complete, there is a bug. */ + +class A {} + +type B = A & { + +a: () => B; + +b: () => B; + +c: () => B; + +d: () => B; + +e: () => B; + +f: () => B; + +g: () => B; + +h: () => B; + +i: () => B; +}; + +declare var b: B; + +(b: B); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1479:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test2.js 1`] = ` +"/* This test ensures that the code below does not take a long time to check. If + * this test is taking a very long time to complete, there is a bug. */ + +class A {} + +type B = A & { + +a: (x: B) => void; + +b: (x: B) => void; + +c: (x: B) => void; + +d: (x: B) => void; + +e: (x: B) => void; + +f: (x: B) => void; + +g: (x: B) => void; + +h: (x: B) => void; + +i: (x: B) => void; +}; + +declare var b: B; + +(b: B); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1479:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/typeapp_perf/jsfmt.spec.js b/tests/typeapp_perf/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/typeapp_perf/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/typeapp_perf/test1.js b/tests/typeapp_perf/test1.js new file mode 100644 index 000000000000..485e397ce548 --- /dev/null +++ b/tests/typeapp_perf/test1.js @@ -0,0 +1,20 @@ +/* This test ensures that the code below does not take a long time to check. If + * this test is taking a very long time to complete, there is a bug. */ + +class A {} + +type B = A & { + +a: () => B; + +b: () => B; + +c: () => B; + +d: () => B; + +e: () => B; + +f: () => B; + +g: () => B; + +h: () => B; + +i: () => B; +}; + +declare var b: B; + +(b: B); diff --git a/tests/typeapp_perf/test2.js b/tests/typeapp_perf/test2.js new file mode 100644 index 000000000000..ee8c145c9a7f --- /dev/null +++ b/tests/typeapp_perf/test2.js @@ -0,0 +1,20 @@ +/* This test ensures that the code below does not take a long time to check. If + * this test is taking a very long time to complete, there is a bug. */ + +class A {} + +type B = A & { + +a: (x: B) => void; + +b: (x: B) => void; + +c: (x: B) => void; + +d: (x: B) => void; + +e: (x: B) => void; + +f: (x: B) => void; + +g: (x: B) => void; + +h: (x: B) => void; + +i: (x: B) => void; +}; + +declare var b: B; + +(b: B); diff --git a/tests/typecast/__snapshots__/jsfmt.spec.js.snap b/tests/typecast/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..98b7ec6dc2c4 --- /dev/null +++ b/tests/typecast/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,45 @@ +exports[`test typecast.js 1`] = ` +"/** + * Copyright 2004-present Facebook. All Rights Reserved. + */ +var tests = [ + () => { + // erroneous typcasts raise errors... + var n = (\"hey\" : number); + // ...but \'any\' does dynamic downcasts, if you must + var x: number = (\"hey\": any); + var y = ((\"hey\": any): number); + }, + + () => { + // typecasts in sequences + // (parens always required around typecasts) + var s: string = ((0: number), (\"hey\": string)); + }, + + () => { + // TODO pending array element inference issues + // control case: + // var a : Array = [0, 1, 2, 3, 4, 5, 6, 7, null]; + // typecast case: + // var b = [(0 : ?number), 1, 2, 3, 4, 5, 6, 7, null]; + } +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:706 + return fromString(\", \").join(path.map(print, \"expressions\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:706:31) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:812:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/typecast/jsfmt.spec.js b/tests/typecast/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/typecast/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/typecast/typecast.js b/tests/typecast/typecast.js new file mode 100644 index 000000000000..c3d4d9990534 --- /dev/null +++ b/tests/typecast/typecast.js @@ -0,0 +1,26 @@ +/** + * Copyright 2004-present Facebook. All Rights Reserved. + */ +var tests = [ + () => { + // erroneous typcasts raise errors... + var n = ("hey" : number); + // ...but 'any' does dynamic downcasts, if you must + var x: number = ("hey": any); + var y = (("hey": any): number); + }, + + () => { + // typecasts in sequences + // (parens always required around typecasts) + var s: string = ((0: number), ("hey": string)); + }, + + () => { + // TODO pending array element inference issues + // control case: + // var a : Array = [0, 1, 2, 3, 4, 5, 6, 7, null]; + // typecast case: + // var b = [(0 : ?number), 1, 2, 3, 4, 5, 6, 7, null]; + } +]; diff --git a/tests/typeof/__snapshots__/jsfmt.spec.js.snap b/tests/typeof/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..935fbee15539 --- /dev/null +++ b/tests/typeof/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,101 @@ +exports[`test typeof.js 1`] = ` +"/** + * @flow + */ + +////////////////////////////////// +// == typeof <> == // +////////////////////////////////// + +// MyClass1 is a runtime value, a constructor function +// +class MyClass1 { + getNumber(): number { return 42; } +} + +// a is an instance of MyClass1 - in runtime terms, +// an object produced by the MyClass1 constructor +// function. +// +var a: MyClass1 = new MyClass1(); + +// Following tests are errors which conflate the type +// of the class value itself with the type of its +// instances. + +// Aside: it\'s worth staring at the following (correct) +// type annotations until they make sense: +// +// MyClass1 : Class +// (new MyClass1()) : MyClass1 +// +// The first says that the MyClass1 value (constructor +// function) has type Class - the type of +// functions which produce instances of MyClass1 when +// called as a constructor. +// +// The second says that objects produced by the MyClass1 +// constructor function have type MyClass1 - the type of +// instances of MyClass1. + +// Error: assign the actual MyClass1 value to a variable +// whose annotated type is of instances of MyClass1. +// +var b: MyClass1 = MyClass1; + +class MyClass2 { + getNumber1(): number { return 42; } +} + +// The opposite error: assign an *instance* of MyClass2 +// to a variable whose annotated type is the type of +// the class value (constructor function) MyClass2 itself. +// +var c: typeof MyClass2 = new MyClass2(); + +////////////////////////////////////// +// == typeof <> == // +////////////////////////////////////// + +var numValue:number = 42; +var d: typeof numValue = 100; +var e: typeof numValue = \'asdf\'; // Error: string ~> number + +///////////////////////////////// +// == typeof <> == // +///////////////////////////////// + +type numberAlias = number; + +// This is an error because typeof takes a value, not +// a type, as an argument. However, the current error +// is suboptimal - just \'cannot resolve name\'. TODO. +// +var f: typeof numberAlias = 42; // Error: \'typeof <>\' makes no sense... + +/** + * Use of a non-class/non-function value in type annotation. + * These provoke a specific error, not just the generic + * \"type is incompatible\" + */ + + var Map = { \"A\": \"this is A\", \"B\": \"this is B\", \"C\": \"this is C\" }; + var keys: $Keys = \"A\"; // Error: ineligible value used in type anno +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/typeof/jsfmt.spec.js b/tests/typeof/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/typeof/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/typeof/typeof.js b/tests/typeof/typeof.js new file mode 100644 index 000000000000..f974bb4d58f5 --- /dev/null +++ b/tests/typeof/typeof.js @@ -0,0 +1,82 @@ +/** + * @flow + */ + +////////////////////////////////// +// == typeof <> == // +////////////////////////////////// + +// MyClass1 is a runtime value, a constructor function +// +class MyClass1 { + getNumber(): number { return 42; } +} + +// a is an instance of MyClass1 - in runtime terms, +// an object produced by the MyClass1 constructor +// function. +// +var a: MyClass1 = new MyClass1(); + +// Following tests are errors which conflate the type +// of the class value itself with the type of its +// instances. + +// Aside: it's worth staring at the following (correct) +// type annotations until they make sense: +// +// MyClass1 : Class +// (new MyClass1()) : MyClass1 +// +// The first says that the MyClass1 value (constructor +// function) has type Class - the type of +// functions which produce instances of MyClass1 when +// called as a constructor. +// +// The second says that objects produced by the MyClass1 +// constructor function have type MyClass1 - the type of +// instances of MyClass1. + +// Error: assign the actual MyClass1 value to a variable +// whose annotated type is of instances of MyClass1. +// +var b: MyClass1 = MyClass1; + +class MyClass2 { + getNumber1(): number { return 42; } +} + +// The opposite error: assign an *instance* of MyClass2 +// to a variable whose annotated type is the type of +// the class value (constructor function) MyClass2 itself. +// +var c: typeof MyClass2 = new MyClass2(); + +////////////////////////////////////// +// == typeof <> == // +////////////////////////////////////// + +var numValue:number = 42; +var d: typeof numValue = 100; +var e: typeof numValue = 'asdf'; // Error: string ~> number + +///////////////////////////////// +// == typeof <> == // +///////////////////////////////// + +type numberAlias = number; + +// This is an error because typeof takes a value, not +// a type, as an argument. However, the current error +// is suboptimal - just 'cannot resolve name'. TODO. +// +var f: typeof numberAlias = 42; // Error: 'typeof <>' makes no sense... + +/** + * Use of a non-class/non-function value in type annotation. + * These provoke a specific error, not just the generic + * "type is incompatible" + */ + + var Map = { "A": "this is A", "B": "this is B", "C": "this is C" }; + var keys: $Keys = "A"; // Error: ineligible value used in type anno diff --git a/tests/unary/__snapshots__/jsfmt.spec.js.snap b/tests/unary/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..b8235d8dedbe --- /dev/null +++ b/tests/unary/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,113 @@ +exports[`test unary.js 1`] = ` +"/* @flow */ + +function x0(y: string): number { + return +y; // ok, + exists solely for coercion +} + +function x1(y: string): number { + return -y; // error, we don't allow coercion here +} + +function x3(y: string) { + return ~y; // error, we don't allow coercion here +} + +function x4(y: string): boolean { + return !y; // ok, coercion is allowed +} + +(-1: void); // error, number ~> void +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +function x0(y: string): number { + return +y;// ok, + exists solely for coercion +} +function x1(y: string): number { + return -y;// error, we don't allow coercion here +} +function x3(y: string) { + return ~y;// error, we don't allow coercion here +} +function x4(y: string): boolean { + return !y;// ok, coercion is allowed +} +(-1: void);// error, number ~> void + +" +`; + +exports[`test update.js 1`] = ` +"// @flow + +let tests = [ + function(y: number) { + y++; + y--; + ++y; + --y; + }, + + function(y: string) { + y++; // error, we don't allow coercion here + (y: number); // ok, y is a number now + y++; // error, but you still can't write a number to a string + }, + + function(y: string) { + y--; // error, we don't allow coercion here + }, + + function(y: string) { + ++y; // error, we don't allow coercion here + }, + + function(y: string) { + --y; // error, we don't allow coercion here + }, + + function() { + const y = 123; + y++; // error, can't update const + y--; // error, can't update const + }, + + function(y: any) { + y++; // ok + }, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +let tests = [ + function(y: number) { + y++; + y--; + ++y; + --y; + }, + function(y: string) { + y++;// error, we don't allow coercion here + (y: number);// ok, y is a number now + y++;// error, but you still can't write a number to a string + }, + function(y: string) { + y--;// error, we don't allow coercion here + }, + function(y: string) { + ++y;// error, we don't allow coercion here + }, + function(y: string) { + --y;// error, we don't allow coercion here + }, + function() { + const y = 123; + y++;// error, can't update const + y--;// error, can't update const + }, + function(y: any) { + y++;// ok + } +]; + +" +`; diff --git a/tests/unary/jsfmt.spec.js b/tests/unary/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/unary/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/unary/unary.js b/tests/unary/unary.js new file mode 100644 index 000000000000..6588336aaa87 --- /dev/null +++ b/tests/unary/unary.js @@ -0,0 +1,19 @@ +/* @flow */ + +function x0(y: string): number { + return +y; // ok, + exists solely for coercion +} + +function x1(y: string): number { + return -y; // error, we don't allow coercion here +} + +function x3(y: string) { + return ~y; // error, we don't allow coercion here +} + +function x4(y: string): boolean { + return !y; // ok, coercion is allowed +} + +(-1: void); // error, number ~> void diff --git a/tests/unary/update.js b/tests/unary/update.js new file mode 100644 index 000000000000..690a62243627 --- /dev/null +++ b/tests/unary/update.js @@ -0,0 +1,38 @@ +// @flow + +let tests = [ + function(y: number) { + y++; + y--; + ++y; + --y; + }, + + function(y: string) { + y++; // error, we don't allow coercion here + (y: number); // ok, y is a number now + y++; // error, but you still can't write a number to a string + }, + + function(y: string) { + y--; // error, we don't allow coercion here + }, + + function(y: string) { + ++y; // error, we don't allow coercion here + }, + + function(y: string) { + --y; // error, we don't allow coercion here + }, + + function() { + const y = 123; + y++; // error, can't update const + y--; // error, can't update const + }, + + function(y: any) { + y++; // ok + }, +]; diff --git a/tests/unchecked_haste_module_vs_lib/__snapshots__/jsfmt.spec.js.snap b/tests/unchecked_haste_module_vs_lib/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..fd33665c37b8 --- /dev/null +++ b/tests/unchecked_haste_module_vs_lib/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,59 @@ +exports[`test buffer.js 1`] = ` +"/** + * @providesModule buffer + * + * Not in flow. + * If this module is successfully imported/required, its + * type will be Any, so arbitraty uses won't cause errors. + * However, if a library module declaration is bound to + * the same name as an unchecked module, it will be used + * to satisfy imports/requires instead. + */ + +export var INSPECT_MAX_BYTES = 0; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule buffer + * + * Not in flow. + * If this module is successfully imported/required, its + * type will be Any, so arbitraty uses won't cause errors. + * However, if a library module declaration is bound to + * the same name as an unchecked module, it will be used + * to satisfy imports/requires instead. + */ +export var INSPECT_MAX_BYTES = 0; + +" +`; + +exports[`test test.js 1`] = ` +"/** + * Copyright 2004-present Facebook. All Rights Reserved. + * @flow + */ + +/* 'buffer' is the name of both an unchecked module in this directory, + * and a module declared in library file node.js. + * If the require below resolves to the unchecked module, the mistyping + * that follows will cause no errors, but if we resolve to the library + * instead, we'll get the desired error. + */ +var buffer = require("buffer"); +var x: string = buffer.INSPECT_MAX_BYTES; // error, number ~/> string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * Copyright 2004-present Facebook. All Rights Reserved. + * @flow + */ +/* 'buffer' is the name of both an unchecked module in this directory, + * and a module declared in library file node.js. + * If the require below resolves to the unchecked module, the mistyping + * that follows will cause no errors, but if we resolve to the library + * instead, we'll get the desired error. + */ +var buffer = require("buffer"); +var x: string = buffer.INSPECT_MAX_BYTES;// error, number ~/> string + +" +`; diff --git a/tests/unchecked_haste_module_vs_lib/buffer.js b/tests/unchecked_haste_module_vs_lib/buffer.js new file mode 100644 index 000000000000..223b08155060 --- /dev/null +++ b/tests/unchecked_haste_module_vs_lib/buffer.js @@ -0,0 +1,12 @@ +/** + * @providesModule buffer + * + * Not in flow. + * If this module is successfully imported/required, its + * type will be Any, so arbitraty uses won't cause errors. + * However, if a library module declaration is bound to + * the same name as an unchecked module, it will be used + * to satisfy imports/requires instead. + */ + +export var INSPECT_MAX_BYTES = 0; diff --git a/tests/unchecked_haste_module_vs_lib/jsfmt.spec.js b/tests/unchecked_haste_module_vs_lib/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/unchecked_haste_module_vs_lib/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/unchecked_haste_module_vs_lib/test.js b/tests/unchecked_haste_module_vs_lib/test.js new file mode 100644 index 000000000000..9cb508a5e998 --- /dev/null +++ b/tests/unchecked_haste_module_vs_lib/test.js @@ -0,0 +1,13 @@ +/** + * Copyright 2004-present Facebook. All Rights Reserved. + * @flow + */ + +/* 'buffer' is the name of both an unchecked module in this directory, + * and a module declared in library file node.js. + * If the require below resolves to the unchecked module, the mistyping + * that follows will cause no errors, but if we resolve to the library + * instead, we'll get the desired error. + */ +var buffer = require("buffer"); +var x: string = buffer.INSPECT_MAX_BYTES; // error, number ~/> string diff --git a/tests/unchecked_node_module_vs_lib/__snapshots__/jsfmt.spec.js.snap b/tests/unchecked_node_module_vs_lib/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..3831c78c27d0 --- /dev/null +++ b/tests/unchecked_node_module_vs_lib/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,53 @@ +exports[`test test.js 1`] = ` +"/** + * Test resolution precedence in node: + * checked module > lib def > unchecked module + * + * @flow + */ + +// node_modules/buffer/index.js is unchecked, +// so we shouldn't pick up its boolean redefinition of INSPECT_MAX_BYTES +// +var buffer = require("buffer"); +var b: boolean = buffer.INSPECT_MAX_BYTES; // error, number ~/> boolean + +// node_modules/crypto/index.js is checked, +// so we should pick up its boolean redefinition of DEFAULT_ENCODING +// +var crypto = require("crypto"); +var b: boolean = crypto.DEFAULT_ENCODING; // no error, we've overridden + +// names that are explicit paths shouldn't fall back to lib defs +// +var buffer2 = require("./buffer"); +var x2: string = buffer2.INSPECT_MAX_BYTES; // error, module not found + +var buffer3 = require("./buffer.js"); +var x3: string = buffer3.INSPECT_MAX_BYTES; // error, module not found +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * Test resolution precedence in node: + * checked module > lib def > unchecked module + * + * @flow + */ +// node_modules/buffer/index.js is unchecked, +// so we shouldn't pick up its boolean redefinition of INSPECT_MAX_BYTES +// +var buffer = require("buffer"); +var b: boolean = buffer.INSPECT_MAX_BYTES;// error, number ~/> boolean +// node_modules/crypto/index.js is checked, +// so we should pick up its boolean redefinition of DEFAULT_ENCODING +// +var crypto = require("crypto"); +var b: boolean = crypto.DEFAULT_ENCODING;// no error, we've overridden +// names that are explicit paths shouldn't fall back to lib defs +// +var buffer2 = require("./buffer"); +var x2: string = buffer2.INSPECT_MAX_BYTES;// error, module not found +var buffer3 = require("./buffer.js"); +var x3: string = buffer3.INSPECT_MAX_BYTES;// error, module not found + +" +`; diff --git a/tests/unchecked_node_module_vs_lib/jsfmt.spec.js b/tests/unchecked_node_module_vs_lib/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/unchecked_node_module_vs_lib/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/unchecked_node_module_vs_lib/test.js b/tests/unchecked_node_module_vs_lib/test.js new file mode 100644 index 000000000000..06b24ab57875 --- /dev/null +++ b/tests/unchecked_node_module_vs_lib/test.js @@ -0,0 +1,26 @@ +/** + * Test resolution precedence in node: + * checked module > lib def > unchecked module + * + * @flow + */ + +// node_modules/buffer/index.js is unchecked, +// so we shouldn't pick up its boolean redefinition of INSPECT_MAX_BYTES +// +var buffer = require("buffer"); +var b: boolean = buffer.INSPECT_MAX_BYTES; // error, number ~/> boolean + +// node_modules/crypto/index.js is checked, +// so we should pick up its boolean redefinition of DEFAULT_ENCODING +// +var crypto = require("crypto"); +var b: boolean = crypto.DEFAULT_ENCODING; // no error, we've overridden + +// names that are explicit paths shouldn't fall back to lib defs +// +var buffer2 = require("./buffer"); +var x2: string = buffer2.INSPECT_MAX_BYTES; // error, module not found + +var buffer3 = require("./buffer.js"); +var x3: string = buffer3.INSPECT_MAX_BYTES; // error, module not found diff --git a/tests/undefined/__snapshots__/jsfmt.spec.js.snap b/tests/undefined/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..543dc8f9448e --- /dev/null +++ b/tests/undefined/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,119 @@ +exports[`test issue-518.js 1`] = ` +"function doSomethingAsync(): Promise { + return new Promise((resolve, reject) => { + resolve(); // OK to leave out arg, same as resolve(undefined) + + var anotherVoidPromise: Promise = Promise.resolve(); + resolve(anotherVoidPromise); + }); +} + +// simpler repro to show that too few args are fine when expecting void +function foo(x: void) { } +foo(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test undefined.js 1`] = ` +"function foo() { + var x; + x.foo(); +} + +function bar() { + var x:?{ bar():void; }; + if (x) x.bar(); +} + +function qux(x?: number, y:string = \"\", z) { } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test undefined2.js 1`] = ` +"// @flow + +let tests = [ + function(x: number) { + var id; + var name = id ? \'John\' : undefined; + (name: boolean); // error, string or void + + const bar = [ + undefined, + \'bar\', + ]; + (bar[x]: boolean); // error, string or void + }, + + function(x: number) { + var undefined = \'foo\'; + (undefined: string); // ok + + var x; + if (x !== undefined) { + x[0]; // should error, could be void + } + + const bar = [ + undefined, + \'bar\', + ]; + (bar[x]: boolean); // error, string only + }, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +let tests = [ + function(x: number) { + var id; + var name = (id ? \"John\" : undefined); + (name: boolean);// error, string or void + const bar = [ undefined, \"bar\" ]; + (bar[x]: boolean);// error, string or void + }, + function(x: number) { + var undefined = \"foo\"; + (undefined: string);// ok + var x; + if (x !== undefined) { + x[0];// should error, could be void + } + const bar = [ undefined, \"bar\" ]; + (bar[x]: boolean);// error, string only + } +]; + +" +`; diff --git a/tests/undefined/issue-518.js b/tests/undefined/issue-518.js new file mode 100644 index 000000000000..e73c51091cb1 --- /dev/null +++ b/tests/undefined/issue-518.js @@ -0,0 +1,12 @@ +function doSomethingAsync(): Promise { + return new Promise((resolve, reject) => { + resolve(); // OK to leave out arg, same as resolve(undefined) + + var anotherVoidPromise: Promise = Promise.resolve(); + resolve(anotherVoidPromise); + }); +} + +// simpler repro to show that too few args are fine when expecting void +function foo(x: void) { } +foo(); diff --git a/tests/undefined/jsfmt.spec.js b/tests/undefined/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/undefined/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/undefined/undefined.js b/tests/undefined/undefined.js new file mode 100644 index 000000000000..347d6206cfc3 --- /dev/null +++ b/tests/undefined/undefined.js @@ -0,0 +1,11 @@ +function foo() { + var x; + x.foo(); +} + +function bar() { + var x:?{ bar():void; }; + if (x) x.bar(); +} + +function qux(x?: number, y:string = "", z) { } diff --git a/tests/undefined/undefined2.js b/tests/undefined/undefined2.js new file mode 100644 index 000000000000..bf7f503f38b5 --- /dev/null +++ b/tests/undefined/undefined2.js @@ -0,0 +1,31 @@ +// @flow + +let tests = [ + function(x: number) { + var id; + var name = id ? 'John' : undefined; + (name: boolean); // error, string or void + + const bar = [ + undefined, + 'bar', + ]; + (bar[x]: boolean); // error, string or void + }, + + function(x: number) { + var undefined = 'foo'; + (undefined: string); // ok + + var x; + if (x !== undefined) { + x[0]; // should error, could be void + } + + const bar = [ + undefined, + 'bar', + ]; + (bar[x]: boolean); // error, string only + }, +]; diff --git a/tests/unicode/UnicodeUtils.js b/tests/unicode/UnicodeUtils.js new file mode 100644 index 000000000000..3a06fc53b987 --- /dev/null +++ b/tests/unicode/UnicodeUtils.js @@ -0,0 +1,34 @@ +/** + * @flow + */ + +/** + * @param {number} codeUnit A Unicode code-unit, in range [0, 0x10FFFF] + * @return {boolean} Whether code-unit is in a surrogate (hi/low) range + */ +function inSurrogateRange(codeUnit) { + return 0xD800 <= codeUnit && codeUnit <= 0xDFFF; +} + + +/** + * Return the length of the original Unicode character at given position in the + * String by looking into the UTF-16 code unit; that is equal to 1 for any + * non-surrogate characters in BMP ([U+0000..U+D7FF] and [U+E000, U+FFFF]); and + * returns 2 for the hi/low surrogates ([U+D800..U+DFFF]), which are in fact + * representing non-BMP characters ([U+10000..U+10FFFF]). + * + * Examples: + * - '\u0020' => 1 + * - '\u3020' => 1 + * - '\uD835' => 2 + * - '\uD835\uDDEF' => 2 + * - '\uDDEF' => 2 + * + * @param {string} str Non-empty string + * @param {number} pos Position in the string to look for one code unit + * @return {number} Number 1 or 2 + */ +function utf16Length(str, pos) { + return 1 + inSurrogateRange(str.charCodeAt(pos)); +} diff --git a/tests/unicode/__snapshots__/jsfmt.spec.js.snap b/tests/unicode/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..2ede222aa532 --- /dev/null +++ b/tests/unicode/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,70 @@ +exports[`test UnicodeUtils.js 1`] = ` +"/** + * @flow + */ + +/** + * @param {number} codeUnit A Unicode code-unit, in range [0, 0x10FFFF] + * @return {boolean} Whether code-unit is in a surrogate (hi/low) range + */ +function inSurrogateRange(codeUnit) { + return 0xD800 <= codeUnit && codeUnit <= 0xDFFF; +} + + +/** + * Return the length of the original Unicode character at given position in the + * String by looking into the UTF-16 code unit; that is equal to 1 for any + * non-surrogate characters in BMP ([U+0000..U+D7FF] and [U+E000, U+FFFF]); and + * returns 2 for the hi/low surrogates ([U+D800..U+DFFF]), which are in fact + * representing non-BMP characters ([U+10000..U+10FFFF]). + * + * Examples: + * - \'\\u0020\' => 1 + * - \'\\u3020\' => 1 + * - \'\\uD835\' => 2 + * - \'\\uD835\\uDDEF\' => 2 + * - \'\\uDDEF\' => 2 + * + * @param {string} str Non-empty string + * @param {number} pos Position in the string to look for one code unit + * @return {number} Number 1 or 2 + */ +function utf16Length(str, pos) { + return 1 + inSurrogateRange(str.charCodeAt(pos)); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ +/** + * @param {number} codeUnit A Unicode code-unit, in range [0, 0x10FFFF] + * @return {boolean} Whether code-unit is in a surrogate (hi/low) range + */ +function inSurrogateRange(codeUnit) { + return 55296 <= codeUnit && codeUnit <= 57343; +} +/** + * Return the length of the original Unicode character at given position in the + * String by looking into the UTF-16 code unit; that is equal to 1 for any + * non-surrogate characters in BMP ([U+0000..U+D7FF] and [U+E000, U+FFFF]); and + * returns 2 for the hi/low surrogates ([U+D800..U+DFFF]), which are in fact + * representing non-BMP characters ([U+10000..U+10FFFF]). + * + * Examples: + * - \'\\u0020\' => 1 + * - \'\\u3020\' => 1 + * - \'\\uD835\' => 2 + * - \'\\uD835\\uDDEF\' => 2 + * - \'\\uDDEF\' => 2 + * + * @param {string} str Non-empty string + * @param {number} pos Position in the string to look for one code unit + * @return {number} Number 1 or 2 + */ +function utf16Length(str, pos) { + return 1 + inSurrogateRange(str.charCodeAt(pos)); +} + +" +`; diff --git a/tests/unicode/jsfmt.spec.js b/tests/unicode/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/unicode/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/union-intersection/__snapshots__/jsfmt.spec.js.snap b/tests/union-intersection/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..9ae9ba5a1427 --- /dev/null +++ b/tests/union-intersection/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,4057 @@ +exports[`test gen_big_disjoint_union.js 1`] = ` +"// perf test for big disjoint union with 1000 cases +type TAction = +{ + type: \'a1\', + a1: number, +} | +{ + type: \'a2\', + a1: number, +} | +{ + type: \'a3\', + a1: number, +} | +{ + type: \'a4\', + a1: number, +} | +{ + type: \'a5\', + a1: number, +} | +{ + type: \'a6\', + a1: number, +} | +{ + type: \'a7\', + a1: number, +} | +{ + type: \'a8\', + a1: number, +} | +{ + type: \'a9\', + a1: number, +} | +{ + type: \'a10\', + a1: number, +} | +{ + type: \'a11\', + a2: number, +} | +{ + type: \'a12\', + a2: number, +} | +{ + type: \'a13\', + a2: number, +} | +{ + type: \'a14\', + a2: number, +} | +{ + type: \'a15\', + a2: number, +} | +{ + type: \'a16\', + a2: number, +} | +{ + type: \'a17\', + a2: number, +} | +{ + type: \'a18\', + a2: number, +} | +{ + type: \'a19\', + a2: number, +} | +{ + type: \'a20\', + a2: number, +} | +{ + type: \'a21\', + a3: number, +} | +{ + type: \'a22\', + a3: number, +} | +{ + type: \'a23\', + a3: number, +} | +{ + type: \'a24\', + a3: number, +} | +{ + type: \'a25\', + a3: number, +} | +{ + type: \'a26\', + a3: number, +} | +{ + type: \'a27\', + a3: number, +} | +{ + type: \'a28\', + a3: number, +} | +{ + type: \'a29\', + a3: number, +} | +{ + type: \'a30\', + a3: number, +} | +{ + type: \'a31\', + a4: number, +} | +{ + type: \'a32\', + a4: number, +} | +{ + type: \'a33\', + a4: number, +} | +{ + type: \'a34\', + a4: number, +} | +{ + type: \'a35\', + a4: number, +} | +{ + type: \'a36\', + a4: number, +} | +{ + type: \'a37\', + a4: number, +} | +{ + type: \'a38\', + a4: number, +} | +{ + type: \'a39\', + a4: number, +} | +{ + type: \'a40\', + a4: number, +} | +{ + type: \'a41\', + a5: number, +} | +{ + type: \'a42\', + a5: number, +} | +{ + type: \'a43\', + a5: number, +} | +{ + type: \'a44\', + a5: number, +} | +{ + type: \'a45\', + a5: number, +} | +{ + type: \'a46\', + a5: number, +} | +{ + type: \'a47\', + a5: number, +} | +{ + type: \'a48\', + a5: number, +} | +{ + type: \'a49\', + a5: number, +} | +{ + type: \'a50\', + a5: number, +} | +{ + type: \'a51\', + a6: number, +} | +{ + type: \'a52\', + a6: number, +} | +{ + type: \'a53\', + a6: number, +} | +{ + type: \'a54\', + a6: number, +} | +{ + type: \'a55\', + a6: number, +} | +{ + type: \'a56\', + a6: number, +} | +{ + type: \'a57\', + a6: number, +} | +{ + type: \'a58\', + a6: number, +} | +{ + type: \'a59\', + a6: number, +} | +{ + type: \'a60\', + a6: number, +} | +{ + type: \'a61\', + a7: number, +} | +{ + type: \'a62\', + a7: number, +} | +{ + type: \'a63\', + a7: number, +} | +{ + type: \'a64\', + a7: number, +} | +{ + type: \'a65\', + a7: number, +} | +{ + type: \'a66\', + a7: number, +} | +{ + type: \'a67\', + a7: number, +} | +{ + type: \'a68\', + a7: number, +} | +{ + type: \'a69\', + a7: number, +} | +{ + type: \'a70\', + a7: number, +} | +{ + type: \'a71\', + a8: number, +} | +{ + type: \'a72\', + a8: number, +} | +{ + type: \'a73\', + a8: number, +} | +{ + type: \'a74\', + a8: number, +} | +{ + type: \'a75\', + a8: number, +} | +{ + type: \'a76\', + a8: number, +} | +{ + type: \'a77\', + a8: number, +} | +{ + type: \'a78\', + a8: number, +} | +{ + type: \'a79\', + a8: number, +} | +{ + type: \'a80\', + a8: number, +} | +{ + type: \'a81\', + a9: number, +} | +{ + type: \'a82\', + a9: number, +} | +{ + type: \'a83\', + a9: number, +} | +{ + type: \'a84\', + a9: number, +} | +{ + type: \'a85\', + a9: number, +} | +{ + type: \'a86\', + a9: number, +} | +{ + type: \'a87\', + a9: number, +} | +{ + type: \'a88\', + a9: number, +} | +{ + type: \'a89\', + a9: number, +} | +{ + type: \'a90\', + a9: number, +} | +{ + type: \'a91\', + a10: number, +} | +{ + type: \'a92\', + a10: number, +} | +{ + type: \'a93\', + a10: number, +} | +{ + type: \'a94\', + a10: number, +} | +{ + type: \'a95\', + a10: number, +} | +{ + type: \'a96\', + a10: number, +} | +{ + type: \'a97\', + a10: number, +} | +{ + type: \'a98\', + a10: number, +} | +{ + type: \'a99\', + a10: number, +} | +{ + type: \'a100\', + a10: number, +} | +{ + type: \'a101\', + a11: number, +} | +{ + type: \'a102\', + a11: number, +} | +{ + type: \'a103\', + a11: number, +} | +{ + type: \'a104\', + a11: number, +} | +{ + type: \'a105\', + a11: number, +} | +{ + type: \'a106\', + a11: number, +} | +{ + type: \'a107\', + a11: number, +} | +{ + type: \'a108\', + a11: number, +} | +{ + type: \'a109\', + a11: number, +} | +{ + type: \'a110\', + a11: number, +} | +{ + type: \'a111\', + a12: number, +} | +{ + type: \'a112\', + a12: number, +} | +{ + type: \'a113\', + a12: number, +} | +{ + type: \'a114\', + a12: number, +} | +{ + type: \'a115\', + a12: number, +} | +{ + type: \'a116\', + a12: number, +} | +{ + type: \'a117\', + a12: number, +} | +{ + type: \'a118\', + a12: number, +} | +{ + type: \'a119\', + a12: number, +} | +{ + type: \'a120\', + a12: number, +} | +{ + type: \'a121\', + a13: number, +} | +{ + type: \'a122\', + a13: number, +} | +{ + type: \'a123\', + a13: number, +} | +{ + type: \'a124\', + a13: number, +} | +{ + type: \'a125\', + a13: number, +} | +{ + type: \'a126\', + a13: number, +} | +{ + type: \'a127\', + a13: number, +} | +{ + type: \'a128\', + a13: number, +} | +{ + type: \'a129\', + a13: number, +} | +{ + type: \'a130\', + a13: number, +} | +{ + type: \'a131\', + a14: number, +} | +{ + type: \'a132\', + a14: number, +} | +{ + type: \'a133\', + a14: number, +} | +{ + type: \'a134\', + a14: number, +} | +{ + type: \'a135\', + a14: number, +} | +{ + type: \'a136\', + a14: number, +} | +{ + type: \'a137\', + a14: number, +} | +{ + type: \'a138\', + a14: number, +} | +{ + type: \'a139\', + a14: number, +} | +{ + type: \'a140\', + a14: number, +} | +{ + type: \'a141\', + a15: number, +} | +{ + type: \'a142\', + a15: number, +} | +{ + type: \'a143\', + a15: number, +} | +{ + type: \'a144\', + a15: number, +} | +{ + type: \'a145\', + a15: number, +} | +{ + type: \'a146\', + a15: number, +} | +{ + type: \'a147\', + a15: number, +} | +{ + type: \'a148\', + a15: number, +} | +{ + type: \'a149\', + a15: number, +} | +{ + type: \'a150\', + a15: number, +} | +{ + type: \'a151\', + a16: number, +} | +{ + type: \'a152\', + a16: number, +} | +{ + type: \'a153\', + a16: number, +} | +{ + type: \'a154\', + a16: number, +} | +{ + type: \'a155\', + a16: number, +} | +{ + type: \'a156\', + a16: number, +} | +{ + type: \'a157\', + a16: number, +} | +{ + type: \'a158\', + a16: number, +} | +{ + type: \'a159\', + a16: number, +} | +{ + type: \'a160\', + a16: number, +} | +{ + type: \'a161\', + a17: number, +} | +{ + type: \'a162\', + a17: number, +} | +{ + type: \'a163\', + a17: number, +} | +{ + type: \'a164\', + a17: number, +} | +{ + type: \'a165\', + a17: number, +} | +{ + type: \'a166\', + a17: number, +} | +{ + type: \'a167\', + a17: number, +} | +{ + type: \'a168\', + a17: number, +} | +{ + type: \'a169\', + a17: number, +} | +{ + type: \'a170\', + a17: number, +} | +{ + type: \'a171\', + a18: number, +} | +{ + type: \'a172\', + a18: number, +} | +{ + type: \'a173\', + a18: number, +} | +{ + type: \'a174\', + a18: number, +} | +{ + type: \'a175\', + a18: number, +} | +{ + type: \'a176\', + a18: number, +} | +{ + type: \'a177\', + a18: number, +} | +{ + type: \'a178\', + a18: number, +} | +{ + type: \'a179\', + a18: number, +} | +{ + type: \'a180\', + a18: number, +} | +{ + type: \'a181\', + a19: number, +} | +{ + type: \'a182\', + a19: number, +} | +{ + type: \'a183\', + a19: number, +} | +{ + type: \'a184\', + a19: number, +} | +{ + type: \'a185\', + a19: number, +} | +{ + type: \'a186\', + a19: number, +} | +{ + type: \'a187\', + a19: number, +} | +{ + type: \'a188\', + a19: number, +} | +{ + type: \'a189\', + a19: number, +} | +{ + type: \'a190\', + a19: number, +} | +{ + type: \'a191\', + a20: number, +} | +{ + type: \'a192\', + a20: number, +} | +{ + type: \'a193\', + a20: number, +} | +{ + type: \'a194\', + a20: number, +} | +{ + type: \'a195\', + a20: number, +} | +{ + type: \'a196\', + a20: number, +} | +{ + type: \'a197\', + a20: number, +} | +{ + type: \'a198\', + a20: number, +} | +{ + type: \'a199\', + a20: number, +} | +{ + type: \'a200\', + a20: number, +} | +{ + type: \'a201\', + a21: number, +} | +{ + type: \'a202\', + a21: number, +} | +{ + type: \'a203\', + a21: number, +} | +{ + type: \'a204\', + a21: number, +} | +{ + type: \'a205\', + a21: number, +} | +{ + type: \'a206\', + a21: number, +} | +{ + type: \'a207\', + a21: number, +} | +{ + type: \'a208\', + a21: number, +} | +{ + type: \'a209\', + a21: number, +} | +{ + type: \'a210\', + a21: number, +} | +{ + type: \'a211\', + a22: number, +} | +{ + type: \'a212\', + a22: number, +} | +{ + type: \'a213\', + a22: number, +} | +{ + type: \'a214\', + a22: number, +} | +{ + type: \'a215\', + a22: number, +} | +{ + type: \'a216\', + a22: number, +} | +{ + type: \'a217\', + a22: number, +} | +{ + type: \'a218\', + a22: number, +} | +{ + type: \'a219\', + a22: number, +} | +{ + type: \'a220\', + a22: number, +} | +{ + type: \'a221\', + a23: number, +} | +{ + type: \'a222\', + a23: number, +} | +{ + type: \'a223\', + a23: number, +} | +{ + type: \'a224\', + a23: number, +} | +{ + type: \'a225\', + a23: number, +} | +{ + type: \'a226\', + a23: number, +} | +{ + type: \'a227\', + a23: number, +} | +{ + type: \'a228\', + a23: number, +} | +{ + type: \'a229\', + a23: number, +} | +{ + type: \'a230\', + a23: number, +} | +{ + type: \'a231\', + a24: number, +} | +{ + type: \'a232\', + a24: number, +} | +{ + type: \'a233\', + a24: number, +} | +{ + type: \'a234\', + a24: number, +} | +{ + type: \'a235\', + a24: number, +} | +{ + type: \'a236\', + a24: number, +} | +{ + type: \'a237\', + a24: number, +} | +{ + type: \'a238\', + a24: number, +} | +{ + type: \'a239\', + a24: number, +} | +{ + type: \'a240\', + a24: number, +} | +{ + type: \'a241\', + a25: number, +} | +{ + type: \'a242\', + a25: number, +} | +{ + type: \'a243\', + a25: number, +} | +{ + type: \'a244\', + a25: number, +} | +{ + type: \'a245\', + a25: number, +} | +{ + type: \'a246\', + a25: number, +} | +{ + type: \'a247\', + a25: number, +} | +{ + type: \'a248\', + a25: number, +} | +{ + type: \'a249\', + a25: number, +} | +{ + type: \'a250\', + a25: number, +} | +{ + type: \'a251\', + a26: number, +} | +{ + type: \'a252\', + a26: number, +} | +{ + type: \'a253\', + a26: number, +} | +{ + type: \'a254\', + a26: number, +} | +{ + type: \'a255\', + a26: number, +} | +{ + type: \'a256\', + a26: number, +} | +{ + type: \'a257\', + a26: number, +} | +{ + type: \'a258\', + a26: number, +} | +{ + type: \'a259\', + a26: number, +} | +{ + type: \'a260\', + a26: number, +} | +{ + type: \'a261\', + a27: number, +} | +{ + type: \'a262\', + a27: number, +} | +{ + type: \'a263\', + a27: number, +} | +{ + type: \'a264\', + a27: number, +} | +{ + type: \'a265\', + a27: number, +} | +{ + type: \'a266\', + a27: number, +} | +{ + type: \'a267\', + a27: number, +} | +{ + type: \'a268\', + a27: number, +} | +{ + type: \'a269\', + a27: number, +} | +{ + type: \'a270\', + a27: number, +} | +{ + type: \'a271\', + a28: number, +} | +{ + type: \'a272\', + a28: number, +} | +{ + type: \'a273\', + a28: number, +} | +{ + type: \'a274\', + a28: number, +} | +{ + type: \'a275\', + a28: number, +} | +{ + type: \'a276\', + a28: number, +} | +{ + type: \'a277\', + a28: number, +} | +{ + type: \'a278\', + a28: number, +} | +{ + type: \'a279\', + a28: number, +} | +{ + type: \'a280\', + a28: number, +} | +{ + type: \'a281\', + a29: number, +} | +{ + type: \'a282\', + a29: number, +} | +{ + type: \'a283\', + a29: number, +} | +{ + type: \'a284\', + a29: number, +} | +{ + type: \'a285\', + a29: number, +} | +{ + type: \'a286\', + a29: number, +} | +{ + type: \'a287\', + a29: number, +} | +{ + type: \'a288\', + a29: number, +} | +{ + type: \'a289\', + a29: number, +} | +{ + type: \'a290\', + a29: number, +} | +{ + type: \'a291\', + a30: number, +} | +{ + type: \'a292\', + a30: number, +} | +{ + type: \'a293\', + a30: number, +} | +{ + type: \'a294\', + a30: number, +} | +{ + type: \'a295\', + a30: number, +} | +{ + type: \'a296\', + a30: number, +} | +{ + type: \'a297\', + a30: number, +} | +{ + type: \'a298\', + a30: number, +} | +{ + type: \'a299\', + a30: number, +} | +{ + type: \'a300\', + a30: number, +} | +{ + type: \'a301\', + a31: number, +} | +{ + type: \'a302\', + a31: number, +} | +{ + type: \'a303\', + a31: number, +} | +{ + type: \'a304\', + a31: number, +} | +{ + type: \'a305\', + a31: number, +} | +{ + type: \'a306\', + a31: number, +} | +{ + type: \'a307\', + a31: number, +} | +{ + type: \'a308\', + a31: number, +} | +{ + type: \'a309\', + a31: number, +} | +{ + type: \'a310\', + a31: number, +} | +{ + type: \'a311\', + a32: number, +} | +{ + type: \'a312\', + a32: number, +} | +{ + type: \'a313\', + a32: number, +} | +{ + type: \'a314\', + a32: number, +} | +{ + type: \'a315\', + a32: number, +} | +{ + type: \'a316\', + a32: number, +} | +{ + type: \'a317\', + a32: number, +} | +{ + type: \'a318\', + a32: number, +} | +{ + type: \'a319\', + a32: number, +} | +{ + type: \'a320\', + a32: number, +} | +{ + type: \'a321\', + a33: number, +} | +{ + type: \'a322\', + a33: number, +} | +{ + type: \'a323\', + a33: number, +} | +{ + type: \'a324\', + a33: number, +} | +{ + type: \'a325\', + a33: number, +} | +{ + type: \'a326\', + a33: number, +} | +{ + type: \'a327\', + a33: number, +} | +{ + type: \'a328\', + a33: number, +} | +{ + type: \'a329\', + a33: number, +} | +{ + type: \'a330\', + a33: number, +} | +{ + type: \'a331\', + a34: number, +} | +{ + type: \'a332\', + a34: number, +} | +{ + type: \'a333\', + a34: number, +} | +{ + type: \'a334\', + a34: number, +} | +{ + type: \'a335\', + a34: number, +} | +{ + type: \'a336\', + a34: number, +} | +{ + type: \'a337\', + a34: number, +} | +{ + type: \'a338\', + a34: number, +} | +{ + type: \'a339\', + a34: number, +} | +{ + type: \'a340\', + a34: number, +} | +{ + type: \'a341\', + a35: number, +} | +{ + type: \'a342\', + a35: number, +} | +{ + type: \'a343\', + a35: number, +} | +{ + type: \'a344\', + a35: number, +} | +{ + type: \'a345\', + a35: number, +} | +{ + type: \'a346\', + a35: number, +} | +{ + type: \'a347\', + a35: number, +} | +{ + type: \'a348\', + a35: number, +} | +{ + type: \'a349\', + a35: number, +} | +{ + type: \'a350\', + a35: number, +} | +{ + type: \'a351\', + a36: number, +} | +{ + type: \'a352\', + a36: number, +} | +{ + type: \'a353\', + a36: number, +} | +{ + type: \'a354\', + a36: number, +} | +{ + type: \'a355\', + a36: number, +} | +{ + type: \'a356\', + a36: number, +} | +{ + type: \'a357\', + a36: number, +} | +{ + type: \'a358\', + a36: number, +} | +{ + type: \'a359\', + a36: number, +} | +{ + type: \'a360\', + a36: number, +} | +{ + type: \'a361\', + a37: number, +} | +{ + type: \'a362\', + a37: number, +} | +{ + type: \'a363\', + a37: number, +} | +{ + type: \'a364\', + a37: number, +} | +{ + type: \'a365\', + a37: number, +} | +{ + type: \'a366\', + a37: number, +} | +{ + type: \'a367\', + a37: number, +} | +{ + type: \'a368\', + a37: number, +} | +{ + type: \'a369\', + a37: number, +} | +{ + type: \'a370\', + a37: number, +} | +{ + type: \'a371\', + a38: number, +} | +{ + type: \'a372\', + a38: number, +} | +{ + type: \'a373\', + a38: number, +} | +{ + type: \'a374\', + a38: number, +} | +{ + type: \'a375\', + a38: number, +} | +{ + type: \'a376\', + a38: number, +} | +{ + type: \'a377\', + a38: number, +} | +{ + type: \'a378\', + a38: number, +} | +{ + type: \'a379\', + a38: number, +} | +{ + type: \'a380\', + a38: number, +} | +{ + type: \'a381\', + a39: number, +} | +{ + type: \'a382\', + a39: number, +} | +{ + type: \'a383\', + a39: number, +} | +{ + type: \'a384\', + a39: number, +} | +{ + type: \'a385\', + a39: number, +} | +{ + type: \'a386\', + a39: number, +} | +{ + type: \'a387\', + a39: number, +} | +{ + type: \'a388\', + a39: number, +} | +{ + type: \'a389\', + a39: number, +} | +{ + type: \'a390\', + a39: number, +} | +{ + type: \'a391\', + a40: number, +} | +{ + type: \'a392\', + a40: number, +} | +{ + type: \'a393\', + a40: number, +} | +{ + type: \'a394\', + a40: number, +} | +{ + type: \'a395\', + a40: number, +} | +{ + type: \'a396\', + a40: number, +} | +{ + type: \'a397\', + a40: number, +} | +{ + type: \'a398\', + a40: number, +} | +{ + type: \'a399\', + a40: number, +} | +{ + type: \'a400\', + a40: number, +} | +{ + type: \'a401\', + a41: number, +} | +{ + type: \'a402\', + a41: number, +} | +{ + type: \'a403\', + a41: number, +} | +{ + type: \'a404\', + a41: number, +} | +{ + type: \'a405\', + a41: number, +} | +{ + type: \'a406\', + a41: number, +} | +{ + type: \'a407\', + a41: number, +} | +{ + type: \'a408\', + a41: number, +} | +{ + type: \'a409\', + a41: number, +} | +{ + type: \'a410\', + a41: number, +} | +{ + type: \'a411\', + a42: number, +} | +{ + type: \'a412\', + a42: number, +} | +{ + type: \'a413\', + a42: number, +} | +{ + type: \'a414\', + a42: number, +} | +{ + type: \'a415\', + a42: number, +} | +{ + type: \'a416\', + a42: number, +} | +{ + type: \'a417\', + a42: number, +} | +{ + type: \'a418\', + a42: number, +} | +{ + type: \'a419\', + a42: number, +} | +{ + type: \'a420\', + a42: number, +} | +{ + type: \'a421\', + a43: number, +} | +{ + type: \'a422\', + a43: number, +} | +{ + type: \'a423\', + a43: number, +} | +{ + type: \'a424\', + a43: number, +} | +{ + type: \'a425\', + a43: number, +} | +{ + type: \'a426\', + a43: number, +} | +{ + type: \'a427\', + a43: number, +} | +{ + type: \'a428\', + a43: number, +} | +{ + type: \'a429\', + a43: number, +} | +{ + type: \'a430\', + a43: number, +} | +{ + type: \'a431\', + a44: number, +} | +{ + type: \'a432\', + a44: number, +} | +{ + type: \'a433\', + a44: number, +} | +{ + type: \'a434\', + a44: number, +} | +{ + type: \'a435\', + a44: number, +} | +{ + type: \'a436\', + a44: number, +} | +{ + type: \'a437\', + a44: number, +} | +{ + type: \'a438\', + a44: number, +} | +{ + type: \'a439\', + a44: number, +} | +{ + type: \'a440\', + a44: number, +} | +{ + type: \'a441\', + a45: number, +} | +{ + type: \'a442\', + a45: number, +} | +{ + type: \'a443\', + a45: number, +} | +{ + type: \'a444\', + a45: number, +} | +{ + type: \'a445\', + a45: number, +} | +{ + type: \'a446\', + a45: number, +} | +{ + type: \'a447\', + a45: number, +} | +{ + type: \'a448\', + a45: number, +} | +{ + type: \'a449\', + a45: number, +} | +{ + type: \'a450\', + a45: number, +} | +{ + type: \'a451\', + a46: number, +} | +{ + type: \'a452\', + a46: number, +} | +{ + type: \'a453\', + a46: number, +} | +{ + type: \'a454\', + a46: number, +} | +{ + type: \'a455\', + a46: number, +} | +{ + type: \'a456\', + a46: number, +} | +{ + type: \'a457\', + a46: number, +} | +{ + type: \'a458\', + a46: number, +} | +{ + type: \'a459\', + a46: number, +} | +{ + type: \'a460\', + a46: number, +} | +{ + type: \'a461\', + a47: number, +} | +{ + type: \'a462\', + a47: number, +} | +{ + type: \'a463\', + a47: number, +} | +{ + type: \'a464\', + a47: number, +} | +{ + type: \'a465\', + a47: number, +} | +{ + type: \'a466\', + a47: number, +} | +{ + type: \'a467\', + a47: number, +} | +{ + type: \'a468\', + a47: number, +} | +{ + type: \'a469\', + a47: number, +} | +{ + type: \'a470\', + a47: number, +} | +{ + type: \'a471\', + a48: number, +} | +{ + type: \'a472\', + a48: number, +} | +{ + type: \'a473\', + a48: number, +} | +{ + type: \'a474\', + a48: number, +} | +{ + type: \'a475\', + a48: number, +} | +{ + type: \'a476\', + a48: number, +} | +{ + type: \'a477\', + a48: number, +} | +{ + type: \'a478\', + a48: number, +} | +{ + type: \'a479\', + a48: number, +} | +{ + type: \'a480\', + a48: number, +} | +{ + type: \'a481\', + a49: number, +} | +{ + type: \'a482\', + a49: number, +} | +{ + type: \'a483\', + a49: number, +} | +{ + type: \'a484\', + a49: number, +} | +{ + type: \'a485\', + a49: number, +} | +{ + type: \'a486\', + a49: number, +} | +{ + type: \'a487\', + a49: number, +} | +{ + type: \'a488\', + a49: number, +} | +{ + type: \'a489\', + a49: number, +} | +{ + type: \'a490\', + a49: number, +} | +{ + type: \'a491\', + a50: number, +} | +{ + type: \'a492\', + a50: number, +} | +{ + type: \'a493\', + a50: number, +} | +{ + type: \'a494\', + a50: number, +} | +{ + type: \'a495\', + a50: number, +} | +{ + type: \'a496\', + a50: number, +} | +{ + type: \'a497\', + a50: number, +} | +{ + type: \'a498\', + a50: number, +} | +{ + type: \'a499\', + a50: number, +} | +{ + type: \'a500\', + a50: number, +} | +{ + type: \'a501\', + a51: number, +} | +{ + type: \'a502\', + a51: number, +} | +{ + type: \'a503\', + a51: number, +} | +{ + type: \'a504\', + a51: number, +} | +{ + type: \'a505\', + a51: number, +} | +{ + type: \'a506\', + a51: number, +} | +{ + type: \'a507\', + a51: number, +} | +{ + type: \'a508\', + a51: number, +} | +{ + type: \'a509\', + a51: number, +} | +{ + type: \'a510\', + a51: number, +} | +{ + type: \'a511\', + a52: number, +} | +{ + type: \'a512\', + a52: number, +} | +{ + type: \'a513\', + a52: number, +} | +{ + type: \'a514\', + a52: number, +} | +{ + type: \'a515\', + a52: number, +} | +{ + type: \'a516\', + a52: number, +} | +{ + type: \'a517\', + a52: number, +} | +{ + type: \'a518\', + a52: number, +} | +{ + type: \'a519\', + a52: number, +} | +{ + type: \'a520\', + a52: number, +} | +{ + type: \'a521\', + a53: number, +} | +{ + type: \'a522\', + a53: number, +} | +{ + type: \'a523\', + a53: number, +} | +{ + type: \'a524\', + a53: number, +} | +{ + type: \'a525\', + a53: number, +} | +{ + type: \'a526\', + a53: number, +} | +{ + type: \'a527\', + a53: number, +} | +{ + type: \'a528\', + a53: number, +} | +{ + type: \'a529\', + a53: number, +} | +{ + type: \'a530\', + a53: number, +} | +{ + type: \'a531\', + a54: number, +} | +{ + type: \'a532\', + a54: number, +} | +{ + type: \'a533\', + a54: number, +} | +{ + type: \'a534\', + a54: number, +} | +{ + type: \'a535\', + a54: number, +} | +{ + type: \'a536\', + a54: number, +} | +{ + type: \'a537\', + a54: number, +} | +{ + type: \'a538\', + a54: number, +} | +{ + type: \'a539\', + a54: number, +} | +{ + type: \'a540\', + a54: number, +} | +{ + type: \'a541\', + a55: number, +} | +{ + type: \'a542\', + a55: number, +} | +{ + type: \'a543\', + a55: number, +} | +{ + type: \'a544\', + a55: number, +} | +{ + type: \'a545\', + a55: number, +} | +{ + type: \'a546\', + a55: number, +} | +{ + type: \'a547\', + a55: number, +} | +{ + type: \'a548\', + a55: number, +} | +{ + type: \'a549\', + a55: number, +} | +{ + type: \'a550\', + a55: number, +} | +{ + type: \'a551\', + a56: number, +} | +{ + type: \'a552\', + a56: number, +} | +{ + type: \'a553\', + a56: number, +} | +{ + type: \'a554\', + a56: number, +} | +{ + type: \'a555\', + a56: number, +} | +{ + type: \'a556\', + a56: number, +} | +{ + type: \'a557\', + a56: number, +} | +{ + type: \'a558\', + a56: number, +} | +{ + type: \'a559\', + a56: number, +} | +{ + type: \'a560\', + a56: number, +} | +{ + type: \'a561\', + a57: number, +} | +{ + type: \'a562\', + a57: number, +} | +{ + type: \'a563\', + a57: number, +} | +{ + type: \'a564\', + a57: number, +} | +{ + type: \'a565\', + a57: number, +} | +{ + type: \'a566\', + a57: number, +} | +{ + type: \'a567\', + a57: number, +} | +{ + type: \'a568\', + a57: number, +} | +{ + type: \'a569\', + a57: number, +} | +{ + type: \'a570\', + a57: number, +} | +{ + type: \'a571\', + a58: number, +} | +{ + type: \'a572\', + a58: number, +} | +{ + type: \'a573\', + a58: number, +} | +{ + type: \'a574\', + a58: number, +} | +{ + type: \'a575\', + a58: number, +} | +{ + type: \'a576\', + a58: number, +} | +{ + type: \'a577\', + a58: number, +} | +{ + type: \'a578\', + a58: number, +} | +{ + type: \'a579\', + a58: number, +} | +{ + type: \'a580\', + a58: number, +} | +{ + type: \'a581\', + a59: number, +} | +{ + type: \'a582\', + a59: number, +} | +{ + type: \'a583\', + a59: number, +} | +{ + type: \'a584\', + a59: number, +} | +{ + type: \'a585\', + a59: number, +} | +{ + type: \'a586\', + a59: number, +} | +{ + type: \'a587\', + a59: number, +} | +{ + type: \'a588\', + a59: number, +} | +{ + type: \'a589\', + a59: number, +} | +{ + type: \'a590\', + a59: number, +} | +{ + type: \'a591\', + a60: number, +} | +{ + type: \'a592\', + a60: number, +} | +{ + type: \'a593\', + a60: number, +} | +{ + type: \'a594\', + a60: number, +} | +{ + type: \'a595\', + a60: number, +} | +{ + type: \'a596\', + a60: number, +} | +{ + type: \'a597\', + a60: number, +} | +{ + type: \'a598\', + a60: number, +} | +{ + type: \'a599\', + a60: number, +} | +{ + type: \'a600\', + a60: number, +} | +{ + type: \'a601\', + a61: number, +} | +{ + type: \'a602\', + a61: number, +} | +{ + type: \'a603\', + a61: number, +} | +{ + type: \'a604\', + a61: number, +} | +{ + type: \'a605\', + a61: number, +} | +{ + type: \'a606\', + a61: number, +} | +{ + type: \'a607\', + a61: number, +} | +{ + type: \'a608\', + a61: number, +} | +{ + type: \'a609\', + a61: number, +} | +{ + type: \'a610\', + a61: number, +} | +{ + type: \'a611\', + a62: number, +} | +{ + type: \'a612\', + a62: number, +} | +{ + type: \'a613\', + a62: number, +} | +{ + type: \'a614\', + a62: number, +} | +{ + type: \'a615\', + a62: number, +} | +{ + type: \'a616\', + a62: number, +} | +{ + type: \'a617\', + a62: number, +} | +{ + type: \'a618\', + a62: number, +} | +{ + type: \'a619\', + a62: number, +} | +{ + type: \'a620\', + a62: number, +} | +{ + type: \'a621\', + a63: number, +} | +{ + type: \'a622\', + a63: number, +} | +{ + type: \'a623\', + a63: number, +} | +{ + type: \'a624\', + a63: number, +} | +{ + type: \'a625\', + a63: number, +} | +{ + type: \'a626\', + a63: number, +} | +{ + type: \'a627\', + a63: number, +} | +{ + type: \'a628\', + a63: number, +} | +{ + type: \'a629\', + a63: number, +} | +{ + type: \'a630\', + a63: number, +} | +{ + type: \'a631\', + a64: number, +} | +{ + type: \'a632\', + a64: number, +} | +{ + type: \'a633\', + a64: number, +} | +{ + type: \'a634\', + a64: number, +} | +{ + type: \'a635\', + a64: number, +} | +{ + type: \'a636\', + a64: number, +} | +{ + type: \'a637\', + a64: number, +} | +{ + type: \'a638\', + a64: number, +} | +{ + type: \'a639\', + a64: number, +} | +{ + type: \'a640\', + a64: number, +} | +{ + type: \'a641\', + a65: number, +} | +{ + type: \'a642\', + a65: number, +} | +{ + type: \'a643\', + a65: number, +} | +{ + type: \'a644\', + a65: number, +} | +{ + type: \'a645\', + a65: number, +} | +{ + type: \'a646\', + a65: number, +} | +{ + type: \'a647\', + a65: number, +} | +{ + type: \'a648\', + a65: number, +} | +{ + type: \'a649\', + a65: number, +} | +{ + type: \'a650\', + a65: number, +} | +{ + type: \'a651\', + a66: number, +} | +{ + type: \'a652\', + a66: number, +} | +{ + type: \'a653\', + a66: number, +} | +{ + type: \'a654\', + a66: number, +} | +{ + type: \'a655\', + a66: number, +} | +{ + type: \'a656\', + a66: number, +} | +{ + type: \'a657\', + a66: number, +} | +{ + type: \'a658\', + a66: number, +} | +{ + type: \'a659\', + a66: number, +} | +{ + type: \'a660\', + a66: number, +} | +{ + type: \'a661\', + a67: number, +} | +{ + type: \'a662\', + a67: number, +} | +{ + type: \'a663\', + a67: number, +} | +{ + type: \'a664\', + a67: number, +} | +{ + type: \'a665\', + a67: number, +} | +{ + type: \'a666\', + a67: number, +} | +{ + type: \'a667\', + a67: number, +} | +{ + type: \'a668\', + a67: number, +} | +{ + type: \'a669\', + a67: number, +} | +{ + type: \'a670\', + a67: number, +} | +{ + type: \'a671\', + a68: number, +} | +{ + type: \'a672\', + a68: number, +} | +{ + type: \'a673\', + a68: number, +} | +{ + type: \'a674\', + a68: number, +} | +{ + type: \'a675\', + a68: number, +} | +{ + type: \'a676\', + a68: number, +} | +{ + type: \'a677\', + a68: number, +} | +{ + type: \'a678\', + a68: number, +} | +{ + type: \'a679\', + a68: number, +} | +{ + type: \'a680\', + a68: number, +} | +{ + type: \'a681\', + a69: number, +} | +{ + type: \'a682\', + a69: number, +} | +{ + type: \'a683\', + a69: number, +} | +{ + type: \'a684\', + a69: number, +} | +{ + type: \'a685\', + a69: number, +} | +{ + type: \'a686\', + a69: number, +} | +{ + type: \'a687\', + a69: number, +} | +{ + type: \'a688\', + a69: number, +} | +{ + type: \'a689\', + a69: number, +} | +{ + type: \'a690\', + a69: number, +} | +{ + type: \'a691\', + a70: number, +} | +{ + type: \'a692\', + a70: number, +} | +{ + type: \'a693\', + a70: number, +} | +{ + type: \'a694\', + a70: number, +} | +{ + type: \'a695\', + a70: number, +} | +{ + type: \'a696\', + a70: number, +} | +{ + type: \'a697\', + a70: number, +} | +{ + type: \'a698\', + a70: number, +} | +{ + type: \'a699\', + a70: number, +} | +{ + type: \'a700\', + a70: number, +} | +{ + type: \'a701\', + a71: number, +} | +{ + type: \'a702\', + a71: number, +} | +{ + type: \'a703\', + a71: number, +} | +{ + type: \'a704\', + a71: number, +} | +{ + type: \'a705\', + a71: number, +} | +{ + type: \'a706\', + a71: number, +} | +{ + type: \'a707\', + a71: number, +} | +{ + type: \'a708\', + a71: number, +} | +{ + type: \'a709\', + a71: number, +} | +{ + type: \'a710\', + a71: number, +} | +{ + type: \'a711\', + a72: number, +} | +{ + type: \'a712\', + a72: number, +} | +{ + type: \'a713\', + a72: number, +} | +{ + type: \'a714\', + a72: number, +} | +{ + type: \'a715\', + a72: number, +} | +{ + type: \'a716\', + a72: number, +} | +{ + type: \'a717\', + a72: number, +} | +{ + type: \'a718\', + a72: number, +} | +{ + type: \'a719\', + a72: number, +} | +{ + type: \'a720\', + a72: number, +} | +{ + type: \'a721\', + a73: number, +} | +{ + type: \'a722\', + a73: number, +} | +{ + type: \'a723\', + a73: number, +} | +{ + type: \'a724\', + a73: number, +} | +{ + type: \'a725\', + a73: number, +} | +{ + type: \'a726\', + a73: number, +} | +{ + type: \'a727\', + a73: number, +} | +{ + type: \'a728\', + a73: number, +} | +{ + type: \'a729\', + a73: number, +} | +{ + type: \'a730\', + a73: number, +} | +{ + type: \'a731\', + a74: number, +} | +{ + type: \'a732\', + a74: number, +} | +{ + type: \'a733\', + a74: number, +} | +{ + type: \'a734\', + a74: number, +} | +{ + type: \'a735\', + a74: number, +} | +{ + type: \'a736\', + a74: number, +} | +{ + type: \'a737\', + a74: number, +} | +{ + type: \'a738\', + a74: number, +} | +{ + type: \'a739\', + a74: number, +} | +{ + type: \'a740\', + a74: number, +} | +{ + type: \'a741\', + a75: number, +} | +{ + type: \'a742\', + a75: number, +} | +{ + type: \'a743\', + a75: number, +} | +{ + type: \'a744\', + a75: number, +} | +{ + type: \'a745\', + a75: number, +} | +{ + type: \'a746\', + a75: number, +} | +{ + type: \'a747\', + a75: number, +} | +{ + type: \'a748\', + a75: number, +} | +{ + type: \'a749\', + a75: number, +} | +{ + type: \'a750\', + a75: number, +} | +{ + type: \'a751\', + a76: number, +} | +{ + type: \'a752\', + a76: number, +} | +{ + type: \'a753\', + a76: number, +} | +{ + type: \'a754\', + a76: number, +} | +{ + type: \'a755\', + a76: number, +} | +{ + type: \'a756\', + a76: number, +} | +{ + type: \'a757\', + a76: number, +} | +{ + type: \'a758\', + a76: number, +} | +{ + type: \'a759\', + a76: number, +} | +{ + type: \'a760\', + a76: number, +} | +{ + type: \'a761\', + a77: number, +} | +{ + type: \'a762\', + a77: number, +} | +{ + type: \'a763\', + a77: number, +} | +{ + type: \'a764\', + a77: number, +} | +{ + type: \'a765\', + a77: number, +} | +{ + type: \'a766\', + a77: number, +} | +{ + type: \'a767\', + a77: number, +} | +{ + type: \'a768\', + a77: number, +} | +{ + type: \'a769\', + a77: number, +} | +{ + type: \'a770\', + a77: number, +} | +{ + type: \'a771\', + a78: number, +} | +{ + type: \'a772\', + a78: number, +} | +{ + type: \'a773\', + a78: number, +} | +{ + type: \'a774\', + a78: number, +} | +{ + type: \'a775\', + a78: number, +} | +{ + type: \'a776\', + a78: number, +} | +{ + type: \'a777\', + a78: number, +} | +{ + type: \'a778\', + a78: number, +} | +{ + type: \'a779\', + a78: number, +} | +{ + type: \'a780\', + a78: number, +} | +{ + type: \'a781\', + a79: number, +} | +{ + type: \'a782\', + a79: number, +} | +{ + type: \'a783\', + a79: number, +} | +{ + type: \'a784\', + a79: number, +} | +{ + type: \'a785\', + a79: number, +} | +{ + type: \'a786\', + a79: number, +} | +{ + type: \'a787\', + a79: number, +} | +{ + type: \'a788\', + a79: number, +} | +{ + type: \'a789\', + a79: number, +} | +{ + type: \'a790\', + a79: number, +} | +{ + type: \'a791\', + a80: number, +} | +{ + type: \'a792\', + a80: number, +} | +{ + type: \'a793\', + a80: number, +} | +{ + type: \'a794\', + a80: number, +} | +{ + type: \'a795\', + a80: number, +} | +{ + type: \'a796\', + a80: number, +} | +{ + type: \'a797\', + a80: number, +} | +{ + type: \'a798\', + a80: number, +} | +{ + type: \'a799\', + a80: number, +} | +{ + type: \'a800\', + a80: number, +} | +{ + type: \'a801\', + a81: number, +} | +{ + type: \'a802\', + a81: number, +} | +{ + type: \'a803\', + a81: number, +} | +{ + type: \'a804\', + a81: number, +} | +{ + type: \'a805\', + a81: number, +} | +{ + type: \'a806\', + a81: number, +} | +{ + type: \'a807\', + a81: number, +} | +{ + type: \'a808\', + a81: number, +} | +{ + type: \'a809\', + a81: number, +} | +{ + type: \'a810\', + a81: number, +} | +{ + type: \'a811\', + a82: number, +} | +{ + type: \'a812\', + a82: number, +} | +{ + type: \'a813\', + a82: number, +} | +{ + type: \'a814\', + a82: number, +} | +{ + type: \'a815\', + a82: number, +} | +{ + type: \'a816\', + a82: number, +} | +{ + type: \'a817\', + a82: number, +} | +{ + type: \'a818\', + a82: number, +} | +{ + type: \'a819\', + a82: number, +} | +{ + type: \'a820\', + a82: number, +} | +{ + type: \'a821\', + a83: number, +} | +{ + type: \'a822\', + a83: number, +} | +{ + type: \'a823\', + a83: number, +} | +{ + type: \'a824\', + a83: number, +} | +{ + type: \'a825\', + a83: number, +} | +{ + type: \'a826\', + a83: number, +} | +{ + type: \'a827\', + a83: number, +} | +{ + type: \'a828\', + a83: number, +} | +{ + type: \'a829\', + a83: number, +} | +{ + type: \'a830\', + a83: number, +} | +{ + type: \'a831\', + a84: number, +} | +{ + type: \'a832\', + a84: number, +} | +{ + type: \'a833\', + a84: number, +} | +{ + type: \'a834\', + a84: number, +} | +{ + type: \'a835\', + a84: number, +} | +{ + type: \'a836\', + a84: number, +} | +{ + type: \'a837\', + a84: number, +} | +{ + type: \'a838\', + a84: number, +} | +{ + type: \'a839\', + a84: number, +} | +{ + type: \'a840\', + a84: number, +} | +{ + type: \'a841\', + a85: number, +} | +{ + type: \'a842\', + a85: number, +} | +{ + type: \'a843\', + a85: number, +} | +{ + type: \'a844\', + a85: number, +} | +{ + type: \'a845\', + a85: number, +} | +{ + type: \'a846\', + a85: number, +} | +{ + type: \'a847\', + a85: number, +} | +{ + type: \'a848\', + a85: number, +} | +{ + type: \'a849\', + a85: number, +} | +{ + type: \'a850\', + a85: number, +} | +{ + type: \'a851\', + a86: number, +} | +{ + type: \'a852\', + a86: number, +} | +{ + type: \'a853\', + a86: number, +} | +{ + type: \'a854\', + a86: number, +} | +{ + type: \'a855\', + a86: number, +} | +{ + type: \'a856\', + a86: number, +} | +{ + type: \'a857\', + a86: number, +} | +{ + type: \'a858\', + a86: number, +} | +{ + type: \'a859\', + a86: number, +} | +{ + type: \'a860\', + a86: number, +} | +{ + type: \'a861\', + a87: number, +} | +{ + type: \'a862\', + a87: number, +} | +{ + type: \'a863\', + a87: number, +} | +{ + type: \'a864\', + a87: number, +} | +{ + type: \'a865\', + a87: number, +} | +{ + type: \'a866\', + a87: number, +} | +{ + type: \'a867\', + a87: number, +} | +{ + type: \'a868\', + a87: number, +} | +{ + type: \'a869\', + a87: number, +} | +{ + type: \'a870\', + a87: number, +} | +{ + type: \'a871\', + a88: number, +} | +{ + type: \'a872\', + a88: number, +} | +{ + type: \'a873\', + a88: number, +} | +{ + type: \'a874\', + a88: number, +} | +{ + type: \'a875\', + a88: number, +} | +{ + type: \'a876\', + a88: number, +} | +{ + type: \'a877\', + a88: number, +} | +{ + type: \'a878\', + a88: number, +} | +{ + type: \'a879\', + a88: number, +} | +{ + type: \'a880\', + a88: number, +} | +{ + type: \'a881\', + a89: number, +} | +{ + type: \'a882\', + a89: number, +} | +{ + type: \'a883\', + a89: number, +} | +{ + type: \'a884\', + a89: number, +} | +{ + type: \'a885\', + a89: number, +} | +{ + type: \'a886\', + a89: number, +} | +{ + type: \'a887\', + a89: number, +} | +{ + type: \'a888\', + a89: number, +} | +{ + type: \'a889\', + a89: number, +} | +{ + type: \'a890\', + a89: number, +} | +{ + type: \'a891\', + a90: number, +} | +{ + type: \'a892\', + a90: number, +} | +{ + type: \'a893\', + a90: number, +} | +{ + type: \'a894\', + a90: number, +} | +{ + type: \'a895\', + a90: number, +} | +{ + type: \'a896\', + a90: number, +} | +{ + type: \'a897\', + a90: number, +} | +{ + type: \'a898\', + a90: number, +} | +{ + type: \'a899\', + a90: number, +} | +{ + type: \'a900\', + a90: number, +} | +{ + type: \'a901\', + a91: number, +} | +{ + type: \'a902\', + a91: number, +} | +{ + type: \'a903\', + a91: number, +} | +{ + type: \'a904\', + a91: number, +} | +{ + type: \'a905\', + a91: number, +} | +{ + type: \'a906\', + a91: number, +} | +{ + type: \'a907\', + a91: number, +} | +{ + type: \'a908\', + a91: number, +} | +{ + type: \'a909\', + a91: number, +} | +{ + type: \'a910\', + a91: number, +} | +{ + type: \'a911\', + a92: number, +} | +{ + type: \'a912\', + a92: number, +} | +{ + type: \'a913\', + a92: number, +} | +{ + type: \'a914\', + a92: number, +} | +{ + type: \'a915\', + a92: number, +} | +{ + type: \'a916\', + a92: number, +} | +{ + type: \'a917\', + a92: number, +} | +{ + type: \'a918\', + a92: number, +} | +{ + type: \'a919\', + a92: number, +} | +{ + type: \'a920\', + a92: number, +} | +{ + type: \'a921\', + a93: number, +} | +{ + type: \'a922\', + a93: number, +} | +{ + type: \'a923\', + a93: number, +} | +{ + type: \'a924\', + a93: number, +} | +{ + type: \'a925\', + a93: number, +} | +{ + type: \'a926\', + a93: number, +} | +{ + type: \'a927\', + a93: number, +} | +{ + type: \'a928\', + a93: number, +} | +{ + type: \'a929\', + a93: number, +} | +{ + type: \'a930\', + a93: number, +} | +{ + type: \'a931\', + a94: number, +} | +{ + type: \'a932\', + a94: number, +} | +{ + type: \'a933\', + a94: number, +} | +{ + type: \'a934\', + a94: number, +} | +{ + type: \'a935\', + a94: number, +} | +{ + type: \'a936\', + a94: number, +} | +{ + type: \'a937\', + a94: number, +} | +{ + type: \'a938\', + a94: number, +} | +{ + type: \'a939\', + a94: number, +} | +{ + type: \'a940\', + a94: number, +} | +{ + type: \'a941\', + a95: number, +} | +{ + type: \'a942\', + a95: number, +} | +{ + type: \'a943\', + a95: number, +} | +{ + type: \'a944\', + a95: number, +} | +{ + type: \'a945\', + a95: number, +} | +{ + type: \'a946\', + a95: number, +} | +{ + type: \'a947\', + a95: number, +} | +{ + type: \'a948\', + a95: number, +} | +{ + type: \'a949\', + a95: number, +} | +{ + type: \'a950\', + a95: number, +} | +{ + type: \'a951\', + a96: number, +} | +{ + type: \'a952\', + a96: number, +} | +{ + type: \'a953\', + a96: number, +} | +{ + type: \'a954\', + a96: number, +} | +{ + type: \'a955\', + a96: number, +} | +{ + type: \'a956\', + a96: number, +} | +{ + type: \'a957\', + a96: number, +} | +{ + type: \'a958\', + a96: number, +} | +{ + type: \'a959\', + a96: number, +} | +{ + type: \'a960\', + a96: number, +} | +{ + type: \'a961\', + a97: number, +} | +{ + type: \'a962\', + a97: number, +} | +{ + type: \'a963\', + a97: number, +} | +{ + type: \'a964\', + a97: number, +} | +{ + type: \'a965\', + a97: number, +} | +{ + type: \'a966\', + a97: number, +} | +{ + type: \'a967\', + a97: number, +} | +{ + type: \'a968\', + a97: number, +} | +{ + type: \'a969\', + a97: number, +} | +{ + type: \'a970\', + a97: number, +} | +{ + type: \'a971\', + a98: number, +} | +{ + type: \'a972\', + a98: number, +} | +{ + type: \'a973\', + a98: number, +} | +{ + type: \'a974\', + a98: number, +} | +{ + type: \'a975\', + a98: number, +} | +{ + type: \'a976\', + a98: number, +} | +{ + type: \'a977\', + a98: number, +} | +{ + type: \'a978\', + a98: number, +} | +{ + type: \'a979\', + a98: number, +} | +{ + type: \'a980\', + a98: number, +} | +{ + type: \'a981\', + a99: number, +} | +{ + type: \'a982\', + a99: number, +} | +{ + type: \'a983\', + a99: number, +} | +{ + type: \'a984\', + a99: number, +} | +{ + type: \'a985\', + a99: number, +} | +{ + type: \'a986\', + a99: number, +} | +{ + type: \'a987\', + a99: number, +} | +{ + type: \'a988\', + a99: number, +} | +{ + type: \'a989\', + a99: number, +} | +{ + type: \'a990\', + a99: number, +} | +{ + type: \'a991\', + a100: number, +} | +{ + type: \'a992\', + a100: number, +} | +{ + type: \'a993\', + a100: number, +} | +{ + type: \'a994\', + a100: number, +} | +{ + type: \'a995\', + a100: number, +} | +{ + type: \'a996\', + a100: number, +} | +{ + type: \'a997\', + a100: number, +} | +{ + type: \'a998\', + a100: number, +} | +{ + type: \'a999\', + a100: number, +} | +{ + type: \'a1000\', + a100: number, +}; +function foo(x: TAction): TAction { return x; } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test.js 1`] = ` +"type A = {a: number}; +type B = {b: number}; +type C = {c: number}; + +type T1 = (A | B) & C; +function f1(x: T1): T1 { return x; } + +type T2 = (A & B) | C; +function f2(x: T2): T2 { return x; } + +type T3 = (A & C) | (B & C); +function f3(x: T3): T3 { return x; } + +type T4 = (A | C) & (B | C); +function f4(x: T4): T4 { return x; } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1407:42) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/union-intersection/gen_big_disjoint_union.js b/tests/union-intersection/gen_big_disjoint_union.js new file mode 100644 index 000000000000..7dcc0b8126ad --- /dev/null +++ b/tests/union-intersection/gen_big_disjoint_union.js @@ -0,0 +1,4003 @@ +// perf test for big disjoint union with 1000 cases +type TAction = +{ + type: 'a1', + a1: number, +} | +{ + type: 'a2', + a1: number, +} | +{ + type: 'a3', + a1: number, +} | +{ + type: 'a4', + a1: number, +} | +{ + type: 'a5', + a1: number, +} | +{ + type: 'a6', + a1: number, +} | +{ + type: 'a7', + a1: number, +} | +{ + type: 'a8', + a1: number, +} | +{ + type: 'a9', + a1: number, +} | +{ + type: 'a10', + a1: number, +} | +{ + type: 'a11', + a2: number, +} | +{ + type: 'a12', + a2: number, +} | +{ + type: 'a13', + a2: number, +} | +{ + type: 'a14', + a2: number, +} | +{ + type: 'a15', + a2: number, +} | +{ + type: 'a16', + a2: number, +} | +{ + type: 'a17', + a2: number, +} | +{ + type: 'a18', + a2: number, +} | +{ + type: 'a19', + a2: number, +} | +{ + type: 'a20', + a2: number, +} | +{ + type: 'a21', + a3: number, +} | +{ + type: 'a22', + a3: number, +} | +{ + type: 'a23', + a3: number, +} | +{ + type: 'a24', + a3: number, +} | +{ + type: 'a25', + a3: number, +} | +{ + type: 'a26', + a3: number, +} | +{ + type: 'a27', + a3: number, +} | +{ + type: 'a28', + a3: number, +} | +{ + type: 'a29', + a3: number, +} | +{ + type: 'a30', + a3: number, +} | +{ + type: 'a31', + a4: number, +} | +{ + type: 'a32', + a4: number, +} | +{ + type: 'a33', + a4: number, +} | +{ + type: 'a34', + a4: number, +} | +{ + type: 'a35', + a4: number, +} | +{ + type: 'a36', + a4: number, +} | +{ + type: 'a37', + a4: number, +} | +{ + type: 'a38', + a4: number, +} | +{ + type: 'a39', + a4: number, +} | +{ + type: 'a40', + a4: number, +} | +{ + type: 'a41', + a5: number, +} | +{ + type: 'a42', + a5: number, +} | +{ + type: 'a43', + a5: number, +} | +{ + type: 'a44', + a5: number, +} | +{ + type: 'a45', + a5: number, +} | +{ + type: 'a46', + a5: number, +} | +{ + type: 'a47', + a5: number, +} | +{ + type: 'a48', + a5: number, +} | +{ + type: 'a49', + a5: number, +} | +{ + type: 'a50', + a5: number, +} | +{ + type: 'a51', + a6: number, +} | +{ + type: 'a52', + a6: number, +} | +{ + type: 'a53', + a6: number, +} | +{ + type: 'a54', + a6: number, +} | +{ + type: 'a55', + a6: number, +} | +{ + type: 'a56', + a6: number, +} | +{ + type: 'a57', + a6: number, +} | +{ + type: 'a58', + a6: number, +} | +{ + type: 'a59', + a6: number, +} | +{ + type: 'a60', + a6: number, +} | +{ + type: 'a61', + a7: number, +} | +{ + type: 'a62', + a7: number, +} | +{ + type: 'a63', + a7: number, +} | +{ + type: 'a64', + a7: number, +} | +{ + type: 'a65', + a7: number, +} | +{ + type: 'a66', + a7: number, +} | +{ + type: 'a67', + a7: number, +} | +{ + type: 'a68', + a7: number, +} | +{ + type: 'a69', + a7: number, +} | +{ + type: 'a70', + a7: number, +} | +{ + type: 'a71', + a8: number, +} | +{ + type: 'a72', + a8: number, +} | +{ + type: 'a73', + a8: number, +} | +{ + type: 'a74', + a8: number, +} | +{ + type: 'a75', + a8: number, +} | +{ + type: 'a76', + a8: number, +} | +{ + type: 'a77', + a8: number, +} | +{ + type: 'a78', + a8: number, +} | +{ + type: 'a79', + a8: number, +} | +{ + type: 'a80', + a8: number, +} | +{ + type: 'a81', + a9: number, +} | +{ + type: 'a82', + a9: number, +} | +{ + type: 'a83', + a9: number, +} | +{ + type: 'a84', + a9: number, +} | +{ + type: 'a85', + a9: number, +} | +{ + type: 'a86', + a9: number, +} | +{ + type: 'a87', + a9: number, +} | +{ + type: 'a88', + a9: number, +} | +{ + type: 'a89', + a9: number, +} | +{ + type: 'a90', + a9: number, +} | +{ + type: 'a91', + a10: number, +} | +{ + type: 'a92', + a10: number, +} | +{ + type: 'a93', + a10: number, +} | +{ + type: 'a94', + a10: number, +} | +{ + type: 'a95', + a10: number, +} | +{ + type: 'a96', + a10: number, +} | +{ + type: 'a97', + a10: number, +} | +{ + type: 'a98', + a10: number, +} | +{ + type: 'a99', + a10: number, +} | +{ + type: 'a100', + a10: number, +} | +{ + type: 'a101', + a11: number, +} | +{ + type: 'a102', + a11: number, +} | +{ + type: 'a103', + a11: number, +} | +{ + type: 'a104', + a11: number, +} | +{ + type: 'a105', + a11: number, +} | +{ + type: 'a106', + a11: number, +} | +{ + type: 'a107', + a11: number, +} | +{ + type: 'a108', + a11: number, +} | +{ + type: 'a109', + a11: number, +} | +{ + type: 'a110', + a11: number, +} | +{ + type: 'a111', + a12: number, +} | +{ + type: 'a112', + a12: number, +} | +{ + type: 'a113', + a12: number, +} | +{ + type: 'a114', + a12: number, +} | +{ + type: 'a115', + a12: number, +} | +{ + type: 'a116', + a12: number, +} | +{ + type: 'a117', + a12: number, +} | +{ + type: 'a118', + a12: number, +} | +{ + type: 'a119', + a12: number, +} | +{ + type: 'a120', + a12: number, +} | +{ + type: 'a121', + a13: number, +} | +{ + type: 'a122', + a13: number, +} | +{ + type: 'a123', + a13: number, +} | +{ + type: 'a124', + a13: number, +} | +{ + type: 'a125', + a13: number, +} | +{ + type: 'a126', + a13: number, +} | +{ + type: 'a127', + a13: number, +} | +{ + type: 'a128', + a13: number, +} | +{ + type: 'a129', + a13: number, +} | +{ + type: 'a130', + a13: number, +} | +{ + type: 'a131', + a14: number, +} | +{ + type: 'a132', + a14: number, +} | +{ + type: 'a133', + a14: number, +} | +{ + type: 'a134', + a14: number, +} | +{ + type: 'a135', + a14: number, +} | +{ + type: 'a136', + a14: number, +} | +{ + type: 'a137', + a14: number, +} | +{ + type: 'a138', + a14: number, +} | +{ + type: 'a139', + a14: number, +} | +{ + type: 'a140', + a14: number, +} | +{ + type: 'a141', + a15: number, +} | +{ + type: 'a142', + a15: number, +} | +{ + type: 'a143', + a15: number, +} | +{ + type: 'a144', + a15: number, +} | +{ + type: 'a145', + a15: number, +} | +{ + type: 'a146', + a15: number, +} | +{ + type: 'a147', + a15: number, +} | +{ + type: 'a148', + a15: number, +} | +{ + type: 'a149', + a15: number, +} | +{ + type: 'a150', + a15: number, +} | +{ + type: 'a151', + a16: number, +} | +{ + type: 'a152', + a16: number, +} | +{ + type: 'a153', + a16: number, +} | +{ + type: 'a154', + a16: number, +} | +{ + type: 'a155', + a16: number, +} | +{ + type: 'a156', + a16: number, +} | +{ + type: 'a157', + a16: number, +} | +{ + type: 'a158', + a16: number, +} | +{ + type: 'a159', + a16: number, +} | +{ + type: 'a160', + a16: number, +} | +{ + type: 'a161', + a17: number, +} | +{ + type: 'a162', + a17: number, +} | +{ + type: 'a163', + a17: number, +} | +{ + type: 'a164', + a17: number, +} | +{ + type: 'a165', + a17: number, +} | +{ + type: 'a166', + a17: number, +} | +{ + type: 'a167', + a17: number, +} | +{ + type: 'a168', + a17: number, +} | +{ + type: 'a169', + a17: number, +} | +{ + type: 'a170', + a17: number, +} | +{ + type: 'a171', + a18: number, +} | +{ + type: 'a172', + a18: number, +} | +{ + type: 'a173', + a18: number, +} | +{ + type: 'a174', + a18: number, +} | +{ + type: 'a175', + a18: number, +} | +{ + type: 'a176', + a18: number, +} | +{ + type: 'a177', + a18: number, +} | +{ + type: 'a178', + a18: number, +} | +{ + type: 'a179', + a18: number, +} | +{ + type: 'a180', + a18: number, +} | +{ + type: 'a181', + a19: number, +} | +{ + type: 'a182', + a19: number, +} | +{ + type: 'a183', + a19: number, +} | +{ + type: 'a184', + a19: number, +} | +{ + type: 'a185', + a19: number, +} | +{ + type: 'a186', + a19: number, +} | +{ + type: 'a187', + a19: number, +} | +{ + type: 'a188', + a19: number, +} | +{ + type: 'a189', + a19: number, +} | +{ + type: 'a190', + a19: number, +} | +{ + type: 'a191', + a20: number, +} | +{ + type: 'a192', + a20: number, +} | +{ + type: 'a193', + a20: number, +} | +{ + type: 'a194', + a20: number, +} | +{ + type: 'a195', + a20: number, +} | +{ + type: 'a196', + a20: number, +} | +{ + type: 'a197', + a20: number, +} | +{ + type: 'a198', + a20: number, +} | +{ + type: 'a199', + a20: number, +} | +{ + type: 'a200', + a20: number, +} | +{ + type: 'a201', + a21: number, +} | +{ + type: 'a202', + a21: number, +} | +{ + type: 'a203', + a21: number, +} | +{ + type: 'a204', + a21: number, +} | +{ + type: 'a205', + a21: number, +} | +{ + type: 'a206', + a21: number, +} | +{ + type: 'a207', + a21: number, +} | +{ + type: 'a208', + a21: number, +} | +{ + type: 'a209', + a21: number, +} | +{ + type: 'a210', + a21: number, +} | +{ + type: 'a211', + a22: number, +} | +{ + type: 'a212', + a22: number, +} | +{ + type: 'a213', + a22: number, +} | +{ + type: 'a214', + a22: number, +} | +{ + type: 'a215', + a22: number, +} | +{ + type: 'a216', + a22: number, +} | +{ + type: 'a217', + a22: number, +} | +{ + type: 'a218', + a22: number, +} | +{ + type: 'a219', + a22: number, +} | +{ + type: 'a220', + a22: number, +} | +{ + type: 'a221', + a23: number, +} | +{ + type: 'a222', + a23: number, +} | +{ + type: 'a223', + a23: number, +} | +{ + type: 'a224', + a23: number, +} | +{ + type: 'a225', + a23: number, +} | +{ + type: 'a226', + a23: number, +} | +{ + type: 'a227', + a23: number, +} | +{ + type: 'a228', + a23: number, +} | +{ + type: 'a229', + a23: number, +} | +{ + type: 'a230', + a23: number, +} | +{ + type: 'a231', + a24: number, +} | +{ + type: 'a232', + a24: number, +} | +{ + type: 'a233', + a24: number, +} | +{ + type: 'a234', + a24: number, +} | +{ + type: 'a235', + a24: number, +} | +{ + type: 'a236', + a24: number, +} | +{ + type: 'a237', + a24: number, +} | +{ + type: 'a238', + a24: number, +} | +{ + type: 'a239', + a24: number, +} | +{ + type: 'a240', + a24: number, +} | +{ + type: 'a241', + a25: number, +} | +{ + type: 'a242', + a25: number, +} | +{ + type: 'a243', + a25: number, +} | +{ + type: 'a244', + a25: number, +} | +{ + type: 'a245', + a25: number, +} | +{ + type: 'a246', + a25: number, +} | +{ + type: 'a247', + a25: number, +} | +{ + type: 'a248', + a25: number, +} | +{ + type: 'a249', + a25: number, +} | +{ + type: 'a250', + a25: number, +} | +{ + type: 'a251', + a26: number, +} | +{ + type: 'a252', + a26: number, +} | +{ + type: 'a253', + a26: number, +} | +{ + type: 'a254', + a26: number, +} | +{ + type: 'a255', + a26: number, +} | +{ + type: 'a256', + a26: number, +} | +{ + type: 'a257', + a26: number, +} | +{ + type: 'a258', + a26: number, +} | +{ + type: 'a259', + a26: number, +} | +{ + type: 'a260', + a26: number, +} | +{ + type: 'a261', + a27: number, +} | +{ + type: 'a262', + a27: number, +} | +{ + type: 'a263', + a27: number, +} | +{ + type: 'a264', + a27: number, +} | +{ + type: 'a265', + a27: number, +} | +{ + type: 'a266', + a27: number, +} | +{ + type: 'a267', + a27: number, +} | +{ + type: 'a268', + a27: number, +} | +{ + type: 'a269', + a27: number, +} | +{ + type: 'a270', + a27: number, +} | +{ + type: 'a271', + a28: number, +} | +{ + type: 'a272', + a28: number, +} | +{ + type: 'a273', + a28: number, +} | +{ + type: 'a274', + a28: number, +} | +{ + type: 'a275', + a28: number, +} | +{ + type: 'a276', + a28: number, +} | +{ + type: 'a277', + a28: number, +} | +{ + type: 'a278', + a28: number, +} | +{ + type: 'a279', + a28: number, +} | +{ + type: 'a280', + a28: number, +} | +{ + type: 'a281', + a29: number, +} | +{ + type: 'a282', + a29: number, +} | +{ + type: 'a283', + a29: number, +} | +{ + type: 'a284', + a29: number, +} | +{ + type: 'a285', + a29: number, +} | +{ + type: 'a286', + a29: number, +} | +{ + type: 'a287', + a29: number, +} | +{ + type: 'a288', + a29: number, +} | +{ + type: 'a289', + a29: number, +} | +{ + type: 'a290', + a29: number, +} | +{ + type: 'a291', + a30: number, +} | +{ + type: 'a292', + a30: number, +} | +{ + type: 'a293', + a30: number, +} | +{ + type: 'a294', + a30: number, +} | +{ + type: 'a295', + a30: number, +} | +{ + type: 'a296', + a30: number, +} | +{ + type: 'a297', + a30: number, +} | +{ + type: 'a298', + a30: number, +} | +{ + type: 'a299', + a30: number, +} | +{ + type: 'a300', + a30: number, +} | +{ + type: 'a301', + a31: number, +} | +{ + type: 'a302', + a31: number, +} | +{ + type: 'a303', + a31: number, +} | +{ + type: 'a304', + a31: number, +} | +{ + type: 'a305', + a31: number, +} | +{ + type: 'a306', + a31: number, +} | +{ + type: 'a307', + a31: number, +} | +{ + type: 'a308', + a31: number, +} | +{ + type: 'a309', + a31: number, +} | +{ + type: 'a310', + a31: number, +} | +{ + type: 'a311', + a32: number, +} | +{ + type: 'a312', + a32: number, +} | +{ + type: 'a313', + a32: number, +} | +{ + type: 'a314', + a32: number, +} | +{ + type: 'a315', + a32: number, +} | +{ + type: 'a316', + a32: number, +} | +{ + type: 'a317', + a32: number, +} | +{ + type: 'a318', + a32: number, +} | +{ + type: 'a319', + a32: number, +} | +{ + type: 'a320', + a32: number, +} | +{ + type: 'a321', + a33: number, +} | +{ + type: 'a322', + a33: number, +} | +{ + type: 'a323', + a33: number, +} | +{ + type: 'a324', + a33: number, +} | +{ + type: 'a325', + a33: number, +} | +{ + type: 'a326', + a33: number, +} | +{ + type: 'a327', + a33: number, +} | +{ + type: 'a328', + a33: number, +} | +{ + type: 'a329', + a33: number, +} | +{ + type: 'a330', + a33: number, +} | +{ + type: 'a331', + a34: number, +} | +{ + type: 'a332', + a34: number, +} | +{ + type: 'a333', + a34: number, +} | +{ + type: 'a334', + a34: number, +} | +{ + type: 'a335', + a34: number, +} | +{ + type: 'a336', + a34: number, +} | +{ + type: 'a337', + a34: number, +} | +{ + type: 'a338', + a34: number, +} | +{ + type: 'a339', + a34: number, +} | +{ + type: 'a340', + a34: number, +} | +{ + type: 'a341', + a35: number, +} | +{ + type: 'a342', + a35: number, +} | +{ + type: 'a343', + a35: number, +} | +{ + type: 'a344', + a35: number, +} | +{ + type: 'a345', + a35: number, +} | +{ + type: 'a346', + a35: number, +} | +{ + type: 'a347', + a35: number, +} | +{ + type: 'a348', + a35: number, +} | +{ + type: 'a349', + a35: number, +} | +{ + type: 'a350', + a35: number, +} | +{ + type: 'a351', + a36: number, +} | +{ + type: 'a352', + a36: number, +} | +{ + type: 'a353', + a36: number, +} | +{ + type: 'a354', + a36: number, +} | +{ + type: 'a355', + a36: number, +} | +{ + type: 'a356', + a36: number, +} | +{ + type: 'a357', + a36: number, +} | +{ + type: 'a358', + a36: number, +} | +{ + type: 'a359', + a36: number, +} | +{ + type: 'a360', + a36: number, +} | +{ + type: 'a361', + a37: number, +} | +{ + type: 'a362', + a37: number, +} | +{ + type: 'a363', + a37: number, +} | +{ + type: 'a364', + a37: number, +} | +{ + type: 'a365', + a37: number, +} | +{ + type: 'a366', + a37: number, +} | +{ + type: 'a367', + a37: number, +} | +{ + type: 'a368', + a37: number, +} | +{ + type: 'a369', + a37: number, +} | +{ + type: 'a370', + a37: number, +} | +{ + type: 'a371', + a38: number, +} | +{ + type: 'a372', + a38: number, +} | +{ + type: 'a373', + a38: number, +} | +{ + type: 'a374', + a38: number, +} | +{ + type: 'a375', + a38: number, +} | +{ + type: 'a376', + a38: number, +} | +{ + type: 'a377', + a38: number, +} | +{ + type: 'a378', + a38: number, +} | +{ + type: 'a379', + a38: number, +} | +{ + type: 'a380', + a38: number, +} | +{ + type: 'a381', + a39: number, +} | +{ + type: 'a382', + a39: number, +} | +{ + type: 'a383', + a39: number, +} | +{ + type: 'a384', + a39: number, +} | +{ + type: 'a385', + a39: number, +} | +{ + type: 'a386', + a39: number, +} | +{ + type: 'a387', + a39: number, +} | +{ + type: 'a388', + a39: number, +} | +{ + type: 'a389', + a39: number, +} | +{ + type: 'a390', + a39: number, +} | +{ + type: 'a391', + a40: number, +} | +{ + type: 'a392', + a40: number, +} | +{ + type: 'a393', + a40: number, +} | +{ + type: 'a394', + a40: number, +} | +{ + type: 'a395', + a40: number, +} | +{ + type: 'a396', + a40: number, +} | +{ + type: 'a397', + a40: number, +} | +{ + type: 'a398', + a40: number, +} | +{ + type: 'a399', + a40: number, +} | +{ + type: 'a400', + a40: number, +} | +{ + type: 'a401', + a41: number, +} | +{ + type: 'a402', + a41: number, +} | +{ + type: 'a403', + a41: number, +} | +{ + type: 'a404', + a41: number, +} | +{ + type: 'a405', + a41: number, +} | +{ + type: 'a406', + a41: number, +} | +{ + type: 'a407', + a41: number, +} | +{ + type: 'a408', + a41: number, +} | +{ + type: 'a409', + a41: number, +} | +{ + type: 'a410', + a41: number, +} | +{ + type: 'a411', + a42: number, +} | +{ + type: 'a412', + a42: number, +} | +{ + type: 'a413', + a42: number, +} | +{ + type: 'a414', + a42: number, +} | +{ + type: 'a415', + a42: number, +} | +{ + type: 'a416', + a42: number, +} | +{ + type: 'a417', + a42: number, +} | +{ + type: 'a418', + a42: number, +} | +{ + type: 'a419', + a42: number, +} | +{ + type: 'a420', + a42: number, +} | +{ + type: 'a421', + a43: number, +} | +{ + type: 'a422', + a43: number, +} | +{ + type: 'a423', + a43: number, +} | +{ + type: 'a424', + a43: number, +} | +{ + type: 'a425', + a43: number, +} | +{ + type: 'a426', + a43: number, +} | +{ + type: 'a427', + a43: number, +} | +{ + type: 'a428', + a43: number, +} | +{ + type: 'a429', + a43: number, +} | +{ + type: 'a430', + a43: number, +} | +{ + type: 'a431', + a44: number, +} | +{ + type: 'a432', + a44: number, +} | +{ + type: 'a433', + a44: number, +} | +{ + type: 'a434', + a44: number, +} | +{ + type: 'a435', + a44: number, +} | +{ + type: 'a436', + a44: number, +} | +{ + type: 'a437', + a44: number, +} | +{ + type: 'a438', + a44: number, +} | +{ + type: 'a439', + a44: number, +} | +{ + type: 'a440', + a44: number, +} | +{ + type: 'a441', + a45: number, +} | +{ + type: 'a442', + a45: number, +} | +{ + type: 'a443', + a45: number, +} | +{ + type: 'a444', + a45: number, +} | +{ + type: 'a445', + a45: number, +} | +{ + type: 'a446', + a45: number, +} | +{ + type: 'a447', + a45: number, +} | +{ + type: 'a448', + a45: number, +} | +{ + type: 'a449', + a45: number, +} | +{ + type: 'a450', + a45: number, +} | +{ + type: 'a451', + a46: number, +} | +{ + type: 'a452', + a46: number, +} | +{ + type: 'a453', + a46: number, +} | +{ + type: 'a454', + a46: number, +} | +{ + type: 'a455', + a46: number, +} | +{ + type: 'a456', + a46: number, +} | +{ + type: 'a457', + a46: number, +} | +{ + type: 'a458', + a46: number, +} | +{ + type: 'a459', + a46: number, +} | +{ + type: 'a460', + a46: number, +} | +{ + type: 'a461', + a47: number, +} | +{ + type: 'a462', + a47: number, +} | +{ + type: 'a463', + a47: number, +} | +{ + type: 'a464', + a47: number, +} | +{ + type: 'a465', + a47: number, +} | +{ + type: 'a466', + a47: number, +} | +{ + type: 'a467', + a47: number, +} | +{ + type: 'a468', + a47: number, +} | +{ + type: 'a469', + a47: number, +} | +{ + type: 'a470', + a47: number, +} | +{ + type: 'a471', + a48: number, +} | +{ + type: 'a472', + a48: number, +} | +{ + type: 'a473', + a48: number, +} | +{ + type: 'a474', + a48: number, +} | +{ + type: 'a475', + a48: number, +} | +{ + type: 'a476', + a48: number, +} | +{ + type: 'a477', + a48: number, +} | +{ + type: 'a478', + a48: number, +} | +{ + type: 'a479', + a48: number, +} | +{ + type: 'a480', + a48: number, +} | +{ + type: 'a481', + a49: number, +} | +{ + type: 'a482', + a49: number, +} | +{ + type: 'a483', + a49: number, +} | +{ + type: 'a484', + a49: number, +} | +{ + type: 'a485', + a49: number, +} | +{ + type: 'a486', + a49: number, +} | +{ + type: 'a487', + a49: number, +} | +{ + type: 'a488', + a49: number, +} | +{ + type: 'a489', + a49: number, +} | +{ + type: 'a490', + a49: number, +} | +{ + type: 'a491', + a50: number, +} | +{ + type: 'a492', + a50: number, +} | +{ + type: 'a493', + a50: number, +} | +{ + type: 'a494', + a50: number, +} | +{ + type: 'a495', + a50: number, +} | +{ + type: 'a496', + a50: number, +} | +{ + type: 'a497', + a50: number, +} | +{ + type: 'a498', + a50: number, +} | +{ + type: 'a499', + a50: number, +} | +{ + type: 'a500', + a50: number, +} | +{ + type: 'a501', + a51: number, +} | +{ + type: 'a502', + a51: number, +} | +{ + type: 'a503', + a51: number, +} | +{ + type: 'a504', + a51: number, +} | +{ + type: 'a505', + a51: number, +} | +{ + type: 'a506', + a51: number, +} | +{ + type: 'a507', + a51: number, +} | +{ + type: 'a508', + a51: number, +} | +{ + type: 'a509', + a51: number, +} | +{ + type: 'a510', + a51: number, +} | +{ + type: 'a511', + a52: number, +} | +{ + type: 'a512', + a52: number, +} | +{ + type: 'a513', + a52: number, +} | +{ + type: 'a514', + a52: number, +} | +{ + type: 'a515', + a52: number, +} | +{ + type: 'a516', + a52: number, +} | +{ + type: 'a517', + a52: number, +} | +{ + type: 'a518', + a52: number, +} | +{ + type: 'a519', + a52: number, +} | +{ + type: 'a520', + a52: number, +} | +{ + type: 'a521', + a53: number, +} | +{ + type: 'a522', + a53: number, +} | +{ + type: 'a523', + a53: number, +} | +{ + type: 'a524', + a53: number, +} | +{ + type: 'a525', + a53: number, +} | +{ + type: 'a526', + a53: number, +} | +{ + type: 'a527', + a53: number, +} | +{ + type: 'a528', + a53: number, +} | +{ + type: 'a529', + a53: number, +} | +{ + type: 'a530', + a53: number, +} | +{ + type: 'a531', + a54: number, +} | +{ + type: 'a532', + a54: number, +} | +{ + type: 'a533', + a54: number, +} | +{ + type: 'a534', + a54: number, +} | +{ + type: 'a535', + a54: number, +} | +{ + type: 'a536', + a54: number, +} | +{ + type: 'a537', + a54: number, +} | +{ + type: 'a538', + a54: number, +} | +{ + type: 'a539', + a54: number, +} | +{ + type: 'a540', + a54: number, +} | +{ + type: 'a541', + a55: number, +} | +{ + type: 'a542', + a55: number, +} | +{ + type: 'a543', + a55: number, +} | +{ + type: 'a544', + a55: number, +} | +{ + type: 'a545', + a55: number, +} | +{ + type: 'a546', + a55: number, +} | +{ + type: 'a547', + a55: number, +} | +{ + type: 'a548', + a55: number, +} | +{ + type: 'a549', + a55: number, +} | +{ + type: 'a550', + a55: number, +} | +{ + type: 'a551', + a56: number, +} | +{ + type: 'a552', + a56: number, +} | +{ + type: 'a553', + a56: number, +} | +{ + type: 'a554', + a56: number, +} | +{ + type: 'a555', + a56: number, +} | +{ + type: 'a556', + a56: number, +} | +{ + type: 'a557', + a56: number, +} | +{ + type: 'a558', + a56: number, +} | +{ + type: 'a559', + a56: number, +} | +{ + type: 'a560', + a56: number, +} | +{ + type: 'a561', + a57: number, +} | +{ + type: 'a562', + a57: number, +} | +{ + type: 'a563', + a57: number, +} | +{ + type: 'a564', + a57: number, +} | +{ + type: 'a565', + a57: number, +} | +{ + type: 'a566', + a57: number, +} | +{ + type: 'a567', + a57: number, +} | +{ + type: 'a568', + a57: number, +} | +{ + type: 'a569', + a57: number, +} | +{ + type: 'a570', + a57: number, +} | +{ + type: 'a571', + a58: number, +} | +{ + type: 'a572', + a58: number, +} | +{ + type: 'a573', + a58: number, +} | +{ + type: 'a574', + a58: number, +} | +{ + type: 'a575', + a58: number, +} | +{ + type: 'a576', + a58: number, +} | +{ + type: 'a577', + a58: number, +} | +{ + type: 'a578', + a58: number, +} | +{ + type: 'a579', + a58: number, +} | +{ + type: 'a580', + a58: number, +} | +{ + type: 'a581', + a59: number, +} | +{ + type: 'a582', + a59: number, +} | +{ + type: 'a583', + a59: number, +} | +{ + type: 'a584', + a59: number, +} | +{ + type: 'a585', + a59: number, +} | +{ + type: 'a586', + a59: number, +} | +{ + type: 'a587', + a59: number, +} | +{ + type: 'a588', + a59: number, +} | +{ + type: 'a589', + a59: number, +} | +{ + type: 'a590', + a59: number, +} | +{ + type: 'a591', + a60: number, +} | +{ + type: 'a592', + a60: number, +} | +{ + type: 'a593', + a60: number, +} | +{ + type: 'a594', + a60: number, +} | +{ + type: 'a595', + a60: number, +} | +{ + type: 'a596', + a60: number, +} | +{ + type: 'a597', + a60: number, +} | +{ + type: 'a598', + a60: number, +} | +{ + type: 'a599', + a60: number, +} | +{ + type: 'a600', + a60: number, +} | +{ + type: 'a601', + a61: number, +} | +{ + type: 'a602', + a61: number, +} | +{ + type: 'a603', + a61: number, +} | +{ + type: 'a604', + a61: number, +} | +{ + type: 'a605', + a61: number, +} | +{ + type: 'a606', + a61: number, +} | +{ + type: 'a607', + a61: number, +} | +{ + type: 'a608', + a61: number, +} | +{ + type: 'a609', + a61: number, +} | +{ + type: 'a610', + a61: number, +} | +{ + type: 'a611', + a62: number, +} | +{ + type: 'a612', + a62: number, +} | +{ + type: 'a613', + a62: number, +} | +{ + type: 'a614', + a62: number, +} | +{ + type: 'a615', + a62: number, +} | +{ + type: 'a616', + a62: number, +} | +{ + type: 'a617', + a62: number, +} | +{ + type: 'a618', + a62: number, +} | +{ + type: 'a619', + a62: number, +} | +{ + type: 'a620', + a62: number, +} | +{ + type: 'a621', + a63: number, +} | +{ + type: 'a622', + a63: number, +} | +{ + type: 'a623', + a63: number, +} | +{ + type: 'a624', + a63: number, +} | +{ + type: 'a625', + a63: number, +} | +{ + type: 'a626', + a63: number, +} | +{ + type: 'a627', + a63: number, +} | +{ + type: 'a628', + a63: number, +} | +{ + type: 'a629', + a63: number, +} | +{ + type: 'a630', + a63: number, +} | +{ + type: 'a631', + a64: number, +} | +{ + type: 'a632', + a64: number, +} | +{ + type: 'a633', + a64: number, +} | +{ + type: 'a634', + a64: number, +} | +{ + type: 'a635', + a64: number, +} | +{ + type: 'a636', + a64: number, +} | +{ + type: 'a637', + a64: number, +} | +{ + type: 'a638', + a64: number, +} | +{ + type: 'a639', + a64: number, +} | +{ + type: 'a640', + a64: number, +} | +{ + type: 'a641', + a65: number, +} | +{ + type: 'a642', + a65: number, +} | +{ + type: 'a643', + a65: number, +} | +{ + type: 'a644', + a65: number, +} | +{ + type: 'a645', + a65: number, +} | +{ + type: 'a646', + a65: number, +} | +{ + type: 'a647', + a65: number, +} | +{ + type: 'a648', + a65: number, +} | +{ + type: 'a649', + a65: number, +} | +{ + type: 'a650', + a65: number, +} | +{ + type: 'a651', + a66: number, +} | +{ + type: 'a652', + a66: number, +} | +{ + type: 'a653', + a66: number, +} | +{ + type: 'a654', + a66: number, +} | +{ + type: 'a655', + a66: number, +} | +{ + type: 'a656', + a66: number, +} | +{ + type: 'a657', + a66: number, +} | +{ + type: 'a658', + a66: number, +} | +{ + type: 'a659', + a66: number, +} | +{ + type: 'a660', + a66: number, +} | +{ + type: 'a661', + a67: number, +} | +{ + type: 'a662', + a67: number, +} | +{ + type: 'a663', + a67: number, +} | +{ + type: 'a664', + a67: number, +} | +{ + type: 'a665', + a67: number, +} | +{ + type: 'a666', + a67: number, +} | +{ + type: 'a667', + a67: number, +} | +{ + type: 'a668', + a67: number, +} | +{ + type: 'a669', + a67: number, +} | +{ + type: 'a670', + a67: number, +} | +{ + type: 'a671', + a68: number, +} | +{ + type: 'a672', + a68: number, +} | +{ + type: 'a673', + a68: number, +} | +{ + type: 'a674', + a68: number, +} | +{ + type: 'a675', + a68: number, +} | +{ + type: 'a676', + a68: number, +} | +{ + type: 'a677', + a68: number, +} | +{ + type: 'a678', + a68: number, +} | +{ + type: 'a679', + a68: number, +} | +{ + type: 'a680', + a68: number, +} | +{ + type: 'a681', + a69: number, +} | +{ + type: 'a682', + a69: number, +} | +{ + type: 'a683', + a69: number, +} | +{ + type: 'a684', + a69: number, +} | +{ + type: 'a685', + a69: number, +} | +{ + type: 'a686', + a69: number, +} | +{ + type: 'a687', + a69: number, +} | +{ + type: 'a688', + a69: number, +} | +{ + type: 'a689', + a69: number, +} | +{ + type: 'a690', + a69: number, +} | +{ + type: 'a691', + a70: number, +} | +{ + type: 'a692', + a70: number, +} | +{ + type: 'a693', + a70: number, +} | +{ + type: 'a694', + a70: number, +} | +{ + type: 'a695', + a70: number, +} | +{ + type: 'a696', + a70: number, +} | +{ + type: 'a697', + a70: number, +} | +{ + type: 'a698', + a70: number, +} | +{ + type: 'a699', + a70: number, +} | +{ + type: 'a700', + a70: number, +} | +{ + type: 'a701', + a71: number, +} | +{ + type: 'a702', + a71: number, +} | +{ + type: 'a703', + a71: number, +} | +{ + type: 'a704', + a71: number, +} | +{ + type: 'a705', + a71: number, +} | +{ + type: 'a706', + a71: number, +} | +{ + type: 'a707', + a71: number, +} | +{ + type: 'a708', + a71: number, +} | +{ + type: 'a709', + a71: number, +} | +{ + type: 'a710', + a71: number, +} | +{ + type: 'a711', + a72: number, +} | +{ + type: 'a712', + a72: number, +} | +{ + type: 'a713', + a72: number, +} | +{ + type: 'a714', + a72: number, +} | +{ + type: 'a715', + a72: number, +} | +{ + type: 'a716', + a72: number, +} | +{ + type: 'a717', + a72: number, +} | +{ + type: 'a718', + a72: number, +} | +{ + type: 'a719', + a72: number, +} | +{ + type: 'a720', + a72: number, +} | +{ + type: 'a721', + a73: number, +} | +{ + type: 'a722', + a73: number, +} | +{ + type: 'a723', + a73: number, +} | +{ + type: 'a724', + a73: number, +} | +{ + type: 'a725', + a73: number, +} | +{ + type: 'a726', + a73: number, +} | +{ + type: 'a727', + a73: number, +} | +{ + type: 'a728', + a73: number, +} | +{ + type: 'a729', + a73: number, +} | +{ + type: 'a730', + a73: number, +} | +{ + type: 'a731', + a74: number, +} | +{ + type: 'a732', + a74: number, +} | +{ + type: 'a733', + a74: number, +} | +{ + type: 'a734', + a74: number, +} | +{ + type: 'a735', + a74: number, +} | +{ + type: 'a736', + a74: number, +} | +{ + type: 'a737', + a74: number, +} | +{ + type: 'a738', + a74: number, +} | +{ + type: 'a739', + a74: number, +} | +{ + type: 'a740', + a74: number, +} | +{ + type: 'a741', + a75: number, +} | +{ + type: 'a742', + a75: number, +} | +{ + type: 'a743', + a75: number, +} | +{ + type: 'a744', + a75: number, +} | +{ + type: 'a745', + a75: number, +} | +{ + type: 'a746', + a75: number, +} | +{ + type: 'a747', + a75: number, +} | +{ + type: 'a748', + a75: number, +} | +{ + type: 'a749', + a75: number, +} | +{ + type: 'a750', + a75: number, +} | +{ + type: 'a751', + a76: number, +} | +{ + type: 'a752', + a76: number, +} | +{ + type: 'a753', + a76: number, +} | +{ + type: 'a754', + a76: number, +} | +{ + type: 'a755', + a76: number, +} | +{ + type: 'a756', + a76: number, +} | +{ + type: 'a757', + a76: number, +} | +{ + type: 'a758', + a76: number, +} | +{ + type: 'a759', + a76: number, +} | +{ + type: 'a760', + a76: number, +} | +{ + type: 'a761', + a77: number, +} | +{ + type: 'a762', + a77: number, +} | +{ + type: 'a763', + a77: number, +} | +{ + type: 'a764', + a77: number, +} | +{ + type: 'a765', + a77: number, +} | +{ + type: 'a766', + a77: number, +} | +{ + type: 'a767', + a77: number, +} | +{ + type: 'a768', + a77: number, +} | +{ + type: 'a769', + a77: number, +} | +{ + type: 'a770', + a77: number, +} | +{ + type: 'a771', + a78: number, +} | +{ + type: 'a772', + a78: number, +} | +{ + type: 'a773', + a78: number, +} | +{ + type: 'a774', + a78: number, +} | +{ + type: 'a775', + a78: number, +} | +{ + type: 'a776', + a78: number, +} | +{ + type: 'a777', + a78: number, +} | +{ + type: 'a778', + a78: number, +} | +{ + type: 'a779', + a78: number, +} | +{ + type: 'a780', + a78: number, +} | +{ + type: 'a781', + a79: number, +} | +{ + type: 'a782', + a79: number, +} | +{ + type: 'a783', + a79: number, +} | +{ + type: 'a784', + a79: number, +} | +{ + type: 'a785', + a79: number, +} | +{ + type: 'a786', + a79: number, +} | +{ + type: 'a787', + a79: number, +} | +{ + type: 'a788', + a79: number, +} | +{ + type: 'a789', + a79: number, +} | +{ + type: 'a790', + a79: number, +} | +{ + type: 'a791', + a80: number, +} | +{ + type: 'a792', + a80: number, +} | +{ + type: 'a793', + a80: number, +} | +{ + type: 'a794', + a80: number, +} | +{ + type: 'a795', + a80: number, +} | +{ + type: 'a796', + a80: number, +} | +{ + type: 'a797', + a80: number, +} | +{ + type: 'a798', + a80: number, +} | +{ + type: 'a799', + a80: number, +} | +{ + type: 'a800', + a80: number, +} | +{ + type: 'a801', + a81: number, +} | +{ + type: 'a802', + a81: number, +} | +{ + type: 'a803', + a81: number, +} | +{ + type: 'a804', + a81: number, +} | +{ + type: 'a805', + a81: number, +} | +{ + type: 'a806', + a81: number, +} | +{ + type: 'a807', + a81: number, +} | +{ + type: 'a808', + a81: number, +} | +{ + type: 'a809', + a81: number, +} | +{ + type: 'a810', + a81: number, +} | +{ + type: 'a811', + a82: number, +} | +{ + type: 'a812', + a82: number, +} | +{ + type: 'a813', + a82: number, +} | +{ + type: 'a814', + a82: number, +} | +{ + type: 'a815', + a82: number, +} | +{ + type: 'a816', + a82: number, +} | +{ + type: 'a817', + a82: number, +} | +{ + type: 'a818', + a82: number, +} | +{ + type: 'a819', + a82: number, +} | +{ + type: 'a820', + a82: number, +} | +{ + type: 'a821', + a83: number, +} | +{ + type: 'a822', + a83: number, +} | +{ + type: 'a823', + a83: number, +} | +{ + type: 'a824', + a83: number, +} | +{ + type: 'a825', + a83: number, +} | +{ + type: 'a826', + a83: number, +} | +{ + type: 'a827', + a83: number, +} | +{ + type: 'a828', + a83: number, +} | +{ + type: 'a829', + a83: number, +} | +{ + type: 'a830', + a83: number, +} | +{ + type: 'a831', + a84: number, +} | +{ + type: 'a832', + a84: number, +} | +{ + type: 'a833', + a84: number, +} | +{ + type: 'a834', + a84: number, +} | +{ + type: 'a835', + a84: number, +} | +{ + type: 'a836', + a84: number, +} | +{ + type: 'a837', + a84: number, +} | +{ + type: 'a838', + a84: number, +} | +{ + type: 'a839', + a84: number, +} | +{ + type: 'a840', + a84: number, +} | +{ + type: 'a841', + a85: number, +} | +{ + type: 'a842', + a85: number, +} | +{ + type: 'a843', + a85: number, +} | +{ + type: 'a844', + a85: number, +} | +{ + type: 'a845', + a85: number, +} | +{ + type: 'a846', + a85: number, +} | +{ + type: 'a847', + a85: number, +} | +{ + type: 'a848', + a85: number, +} | +{ + type: 'a849', + a85: number, +} | +{ + type: 'a850', + a85: number, +} | +{ + type: 'a851', + a86: number, +} | +{ + type: 'a852', + a86: number, +} | +{ + type: 'a853', + a86: number, +} | +{ + type: 'a854', + a86: number, +} | +{ + type: 'a855', + a86: number, +} | +{ + type: 'a856', + a86: number, +} | +{ + type: 'a857', + a86: number, +} | +{ + type: 'a858', + a86: number, +} | +{ + type: 'a859', + a86: number, +} | +{ + type: 'a860', + a86: number, +} | +{ + type: 'a861', + a87: number, +} | +{ + type: 'a862', + a87: number, +} | +{ + type: 'a863', + a87: number, +} | +{ + type: 'a864', + a87: number, +} | +{ + type: 'a865', + a87: number, +} | +{ + type: 'a866', + a87: number, +} | +{ + type: 'a867', + a87: number, +} | +{ + type: 'a868', + a87: number, +} | +{ + type: 'a869', + a87: number, +} | +{ + type: 'a870', + a87: number, +} | +{ + type: 'a871', + a88: number, +} | +{ + type: 'a872', + a88: number, +} | +{ + type: 'a873', + a88: number, +} | +{ + type: 'a874', + a88: number, +} | +{ + type: 'a875', + a88: number, +} | +{ + type: 'a876', + a88: number, +} | +{ + type: 'a877', + a88: number, +} | +{ + type: 'a878', + a88: number, +} | +{ + type: 'a879', + a88: number, +} | +{ + type: 'a880', + a88: number, +} | +{ + type: 'a881', + a89: number, +} | +{ + type: 'a882', + a89: number, +} | +{ + type: 'a883', + a89: number, +} | +{ + type: 'a884', + a89: number, +} | +{ + type: 'a885', + a89: number, +} | +{ + type: 'a886', + a89: number, +} | +{ + type: 'a887', + a89: number, +} | +{ + type: 'a888', + a89: number, +} | +{ + type: 'a889', + a89: number, +} | +{ + type: 'a890', + a89: number, +} | +{ + type: 'a891', + a90: number, +} | +{ + type: 'a892', + a90: number, +} | +{ + type: 'a893', + a90: number, +} | +{ + type: 'a894', + a90: number, +} | +{ + type: 'a895', + a90: number, +} | +{ + type: 'a896', + a90: number, +} | +{ + type: 'a897', + a90: number, +} | +{ + type: 'a898', + a90: number, +} | +{ + type: 'a899', + a90: number, +} | +{ + type: 'a900', + a90: number, +} | +{ + type: 'a901', + a91: number, +} | +{ + type: 'a902', + a91: number, +} | +{ + type: 'a903', + a91: number, +} | +{ + type: 'a904', + a91: number, +} | +{ + type: 'a905', + a91: number, +} | +{ + type: 'a906', + a91: number, +} | +{ + type: 'a907', + a91: number, +} | +{ + type: 'a908', + a91: number, +} | +{ + type: 'a909', + a91: number, +} | +{ + type: 'a910', + a91: number, +} | +{ + type: 'a911', + a92: number, +} | +{ + type: 'a912', + a92: number, +} | +{ + type: 'a913', + a92: number, +} | +{ + type: 'a914', + a92: number, +} | +{ + type: 'a915', + a92: number, +} | +{ + type: 'a916', + a92: number, +} | +{ + type: 'a917', + a92: number, +} | +{ + type: 'a918', + a92: number, +} | +{ + type: 'a919', + a92: number, +} | +{ + type: 'a920', + a92: number, +} | +{ + type: 'a921', + a93: number, +} | +{ + type: 'a922', + a93: number, +} | +{ + type: 'a923', + a93: number, +} | +{ + type: 'a924', + a93: number, +} | +{ + type: 'a925', + a93: number, +} | +{ + type: 'a926', + a93: number, +} | +{ + type: 'a927', + a93: number, +} | +{ + type: 'a928', + a93: number, +} | +{ + type: 'a929', + a93: number, +} | +{ + type: 'a930', + a93: number, +} | +{ + type: 'a931', + a94: number, +} | +{ + type: 'a932', + a94: number, +} | +{ + type: 'a933', + a94: number, +} | +{ + type: 'a934', + a94: number, +} | +{ + type: 'a935', + a94: number, +} | +{ + type: 'a936', + a94: number, +} | +{ + type: 'a937', + a94: number, +} | +{ + type: 'a938', + a94: number, +} | +{ + type: 'a939', + a94: number, +} | +{ + type: 'a940', + a94: number, +} | +{ + type: 'a941', + a95: number, +} | +{ + type: 'a942', + a95: number, +} | +{ + type: 'a943', + a95: number, +} | +{ + type: 'a944', + a95: number, +} | +{ + type: 'a945', + a95: number, +} | +{ + type: 'a946', + a95: number, +} | +{ + type: 'a947', + a95: number, +} | +{ + type: 'a948', + a95: number, +} | +{ + type: 'a949', + a95: number, +} | +{ + type: 'a950', + a95: number, +} | +{ + type: 'a951', + a96: number, +} | +{ + type: 'a952', + a96: number, +} | +{ + type: 'a953', + a96: number, +} | +{ + type: 'a954', + a96: number, +} | +{ + type: 'a955', + a96: number, +} | +{ + type: 'a956', + a96: number, +} | +{ + type: 'a957', + a96: number, +} | +{ + type: 'a958', + a96: number, +} | +{ + type: 'a959', + a96: number, +} | +{ + type: 'a960', + a96: number, +} | +{ + type: 'a961', + a97: number, +} | +{ + type: 'a962', + a97: number, +} | +{ + type: 'a963', + a97: number, +} | +{ + type: 'a964', + a97: number, +} | +{ + type: 'a965', + a97: number, +} | +{ + type: 'a966', + a97: number, +} | +{ + type: 'a967', + a97: number, +} | +{ + type: 'a968', + a97: number, +} | +{ + type: 'a969', + a97: number, +} | +{ + type: 'a970', + a97: number, +} | +{ + type: 'a971', + a98: number, +} | +{ + type: 'a972', + a98: number, +} | +{ + type: 'a973', + a98: number, +} | +{ + type: 'a974', + a98: number, +} | +{ + type: 'a975', + a98: number, +} | +{ + type: 'a976', + a98: number, +} | +{ + type: 'a977', + a98: number, +} | +{ + type: 'a978', + a98: number, +} | +{ + type: 'a979', + a98: number, +} | +{ + type: 'a980', + a98: number, +} | +{ + type: 'a981', + a99: number, +} | +{ + type: 'a982', + a99: number, +} | +{ + type: 'a983', + a99: number, +} | +{ + type: 'a984', + a99: number, +} | +{ + type: 'a985', + a99: number, +} | +{ + type: 'a986', + a99: number, +} | +{ + type: 'a987', + a99: number, +} | +{ + type: 'a988', + a99: number, +} | +{ + type: 'a989', + a99: number, +} | +{ + type: 'a990', + a99: number, +} | +{ + type: 'a991', + a100: number, +} | +{ + type: 'a992', + a100: number, +} | +{ + type: 'a993', + a100: number, +} | +{ + type: 'a994', + a100: number, +} | +{ + type: 'a995', + a100: number, +} | +{ + type: 'a996', + a100: number, +} | +{ + type: 'a997', + a100: number, +} | +{ + type: 'a998', + a100: number, +} | +{ + type: 'a999', + a100: number, +} | +{ + type: 'a1000', + a100: number, +}; +function foo(x: TAction): TAction { return x; } diff --git a/tests/union-intersection/jsfmt.spec.js b/tests/union-intersection/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/union-intersection/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/union-intersection/test.js b/tests/union-intersection/test.js new file mode 100644 index 000000000000..83655a3be56d --- /dev/null +++ b/tests/union-intersection/test.js @@ -0,0 +1,15 @@ +type A = {a: number}; +type B = {b: number}; +type C = {c: number}; + +type T1 = (A | B) & C; +function f1(x: T1): T1 { return x; } + +type T2 = (A & B) | C; +function f2(x: T2): T2 { return x; } + +type T3 = (A & C) | (B & C); +function f3(x: T3): T3 { return x; } + +type T4 = (A | C) & (B | C); +function f4(x: T4): T4 { return x; } diff --git a/tests/union/__snapshots__/jsfmt.spec.js.snap b/tests/union/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..11f75a60dcfe --- /dev/null +++ b/tests/union/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,5533 @@ +exports[`test fields.js 1`] = ` +"class C { + x: ?number|string; + constructor() { + this.x = null; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test fields2.js 1`] = ` +"class C { } + +class D { + content: string|C; + copyContent(content: C): string|C { + this.content = content; + return this.content; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test issue-17.js 1`] = ` +"/* @flow */ + +type T = + {type: \"a\"; a: number} | + {type: \"b\"; b: string}; + +var l: Array = [ + {type: \"a\", a: 1}, + {type: \"a\", a: 2}, + {type: \"a\", a: 3}, + {type: \"a\", a: 4}, + {type: \"b\", b: \"monkey\"}, + {type: \"b\", b: \"gorilla\"}, + {type: \"b\", b: \"giraffe\"}, + {type: \"b\", b: \"penguin\"}, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test issue-198.js 1`] = ` +"var p = new Promise(function(resolve, reject) { + resolve(5); +}) + .then(function(num) { + return num.toFixed(); + }) + .then(function(str) { + // This should fail because str is string, not number + return str.toFixed(); + }); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var p = new Promise( + function(resolve, reject) { + resolve(5); + } +).then( + function(num) { + return num.toFixed(); + } +).then( + function(str) { + // This should fail because str is string, not number + return str.toFixed(); + } +); + +" +`; + +exports[`test issue-256.js 1`] = ` +"declare class Myclass { + myfun(myarray: Array): any; +} +declare var myclass: Myclass; + +myclass.myfun([\"1\", \"2\", \"3\", \"4\", \"5\", \"6\", function (ar) {}]) +myclass.myfun([\"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", function (ar) {}]) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1497:36) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test issue-323.js 1`] = ` +"var Foo = require(\"./issue-323-lib\"); +var foo = new Foo(); +var foostr: Foo | string = foo; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test issue-323-lib.js 1`] = ` +"/* @flow */ +class Foo {} +module.exports = Foo; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +class Foo {} +module.exports = Foo; + +" +`; + +exports[`test issue-324.js 1`] = ` +"/* @flow */ +class Foo{}; +class Bar{}; + +var foostr: Foo | string = new Foo(); +var barstr: Bar | string = new Bar(); + +foostr = barstr; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test issue-325.js 1`] = ` +"class Tag { + constructor() { + var a1: Array = []; + var a2: Array = a1; + } +} + +type Node = Tag_ | string; +class Tag_ { + constructor() { + var a1: Array = [new Tag_]; + var a2: Array = a1; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1497:36) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test issue-326.js 1`] = ` +"var numberAndStringArr:Array = [1,2]; +var stringArr:Array = [\'a\',\'b\']; + +var result = numberAndStringArr.concat(stringArr); // no error +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1497:36) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test issue-582.js 1`] = ` +"/*** + * nested unions + * @flow + */ + +// inline +var nested1: (\'foo\' | \'bar\') | \'baz\' = \'baz\'; + +// through tvars +type FooBar = \'foo\' | \'bar\'; +type Baz = \'baz\'; +type FooBarBaz = FooBar | Baz; + +var nested2: FooBarBaz = \'baz\'; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1530:42) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test issue-963.js 1`] = ` +"/*** + * unions with embedded intersections + * @flow + */ + +type t1 = { + p1 : number +}; + +type t2 = { + p2: number +} + +type t3 = { + p3 : number +} + +type intersected = t1 & t2; +type union = intersected | t3; +type union2 = t3 | intersected; + +const u1 : union = { + p3 : 3 +}; + +const u2 : union = { + p1 : 1, + p2 : 2 +}; + +const u3 : union2 = { + p1 : 1, + p2 : 2 +}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1407 + return fromString(\" & \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1407:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test.js 1`] = ` +"var C = require(\'test-lib\'); + +// TODO: spurious error! (replacing C with number makes the error go away) +// type Foo = Array | Array; +type Foo = Array; // workaround +var x:Array = []; +var y:Array = []; +function foo(x:Foo) {} +foo(x); +foo(y); + +// TODO: spurious error! (replacing C with number makes the error go away) +// type Bar = (() => C) | (() => string); +type Bar = () => (C | string); // workaround + +function f() { return \"\"; } +function bar(x:Bar) { } +bar(f); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1479:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test-lib.js 1`] = ` +"/* @providesModule test-lib */ + +class C { } +module.exports = C; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @providesModule test-lib */ +class C {} +module.exports = C; + +" +`; + +exports[`test type-app.js 1`] = ` +"/** + * @flow + */ + +class LocalClass {} + +var a: LocalClass | number = 123; + +// Iterator is defined in a lib file, so the speculative algorithm for the +// union type would incorrectly succeed for Iterator. Only later during +// the merge would we fine the error, but it would be too late. The diff that +// introduces this test fixes this such that the speculative algorithm is +// correctly delayed upon encountering a non-concrete TypeAppT +var b: Iterator | number = 123; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1172:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test union.js 1`] = ` +"function bar(x: Document | string): void { } +bar(0); + +class C { } +class D { } +function CD(b) { + var E = b? C: D; + var c:C = new E(); // error, since E could be D, and D is not a subtype of C + function qux(e: E) { } // this annotation is an error: is it C, or is it D? + function qux2(e: C | D) { } // OK + qux2(new C); +} + +declare class F { + foo(x: number):void; + foo(x: string):void; +} +function corge(b) { + var x = b ? \"\" : 0; + new F().foo(x); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test yuge.js 1`] = ` +"/** + * great big union used as type annotation will be flowed into itself. + * List.mem check avoids pathology, as long as singletons aren\'t converted. + * This is a band-aid, though: see second case. + * @flow + */ +\'use strict\'; + +class SecurityCheckupTypedLogger { + _data: Data; + + setError(value: ErrorCode) { + this._data[\'error\'] = value; + } + + // Bug: right now, any incompatible type coming into a huge + // union might blow the recursion limit. + // TODO real solution is to specialize union reps for obvious cases, + // e.g. (base type, list in decl order, set). Of course we could do + // something quick to get the union size off the Ocaml call stack, + // but not sure it\'s worth doing that before the real solution. + ohThatsNotSoGood() { + (\"\": ErrorCode); // also, error pos omits this line completely + } +} + +type ErrorCode = +0 | +1 | +2 | +3 | +4 | +5 | +6 | +7 | +8 | +9 | +10 | +11 | +12 | +13 | +14 | +15 | +16 | +17 | +18 | +19 | +20 | +21 | +22 | +23 | +24 | +25 | +26 | +27 | +28 | +29 | +30 | +31 | +32 | +33 | +34 | +35 | +36 | +37 | +38 | +39 | +40 | +41 | +42 | +43 | +44 | +45 | +46 | +47 | +48 | +49 | +50 | +51 | +52 | +53 | +54 | +55 | +56 | +57 | +58 | +59 | +60 | +61 | +62 | +63 | +64 | +65 | +66 | +67 | +68 | +69 | +70 | +71 | +72 | +73 | +74 | +75 | +76 | +77 | +78 | +79 | +80 | +81 | +82 | +83 | +84 | +85 | +86 | +87 | +88 | +89 | +90 | +91 | +92 | +93 | +94 | +95 | +96 | +97 | +98 | +99 | +100 | +101 | +102 | +103 | +104 | +105 | +106 | +107 | +108 | +109 | +110 | +111 | +112 | +113 | +114 | +115 | +116 | +117 | +118 | +119 | +120 | +121 | +122 | +123 | +124 | +125 | +126 | +127 | +128 | +129 | +130 | +131 | +132 | +133 | +134 | +135 | +136 | +137 | +138 | +139 | +140 | +141 | +142 | +143 | +144 | +145 | +146 | +147 | +148 | +149 | +150 | +151 | +152 | +153 | +154 | +155 | +156 | +157 | +158 | +159 | +160 | +161 | +162 | +163 | +164 | +165 | +166 | +167 | +168 | +169 | +170 | +171 | +172 | +173 | +174 | +175 | +176 | +177 | +178 | +179 | +180 | +181 | +182 | +183 | +184 | +185 | +186 | +187 | +188 | +189 | +190 | +191 | +192 | +193 | +194 | +195 | +196 | +197 | +198 | +199 | +200 | +201 | +202 | +203 | +204 | +205 | +206 | +207 | +208 | +209 | +210 | +211 | +212 | +213 | +214 | +215 | +216 | +217 | +218 | +219 | +220 | +221 | +222 | +223 | +224 | +225 | +226 | +227 | +228 | +229 | +230 | +231 | +232 | +233 | +234 | +235 | +236 | +237 | +238 | +239 | +240 | +241 | +242 | +243 | +244 | +245 | +246 | +247 | +248 | +249 | +250 | +251 | +252 | +253 | +254 | +255 | +256 | +257 | +258 | +259 | +260 | +261 | +262 | +263 | +264 | +265 | +266 | +267 | +268 | +269 | +270 | +271 | +272 | +273 | +274 | +275 | +276 | +277 | +278 | +279 | +280 | +281 | +282 | +283 | +284 | +285 | +286 | +287 | +288 | +289 | +290 | +291 | +292 | +293 | +294 | +295 | +296 | +297 | +298 | +299 | +300 | +301 | +302 | +303 | +304 | +305 | +306 | +307 | +308 | +309 | +310 | +311 | +312 | +313 | +314 | +315 | +316 | +317 | +318 | +319 | +320 | +321 | +322 | +323 | +324 | +325 | +326 | +327 | +328 | +329 | +330 | +331 | +332 | +333 | +334 | +335 | +336 | +337 | +338 | +339 | +340 | +341 | +342 | +343 | +344 | +345 | +346 | +347 | +348 | +349 | +350 | +351 | +352 | +353 | +354 | +355 | +356 | +357 | +358 | +359 | +360 | +361 | +362 | +363 | +364 | +365 | +366 | +367 | +368 | +369 | +370 | +371 | +372 | +373 | +374 | +375 | +376 | +377 | +378 | +379 | +380 | +381 | +382 | +383 | +384 | +385 | +386 | +387 | +388 | +389 | +390 | +391 | +392 | +393 | +394 | +395 | +396 | +397 | +398 | +399 | +400 | +401 | +402 | +403 | +404 | +405 | +406 | +407 | +408 | +409 | +410 | +411 | +412 | +413 | +414 | +415 | +416 | +417 | +418 | +419 | +420 | +421 | +422 | +423 | +424 | +425 | +426 | +427 | +428 | +429 | +430 | +431 | +432 | +433 | +434 | +435 | +436 | +437 | +438 | +439 | +440 | +441 | +442 | +443 | +444 | +445 | +446 | +447 | +448 | +449 | +450 | +451 | +452 | +453 | +454 | +455 | +456 | +457 | +458 | +459 | +460 | +461 | +462 | +463 | +464 | +465 | +466 | +467 | +468 | +469 | +470 | +471 | +472 | +473 | +474 | +475 | +476 | +477 | +478 | +479 | +480 | +481 | +482 | +483 | +484 | +485 | +486 | +487 | +488 | +489 | +490 | +491 | +492 | +493 | +494 | +495 | +496 | +497 | +498 | +499 | +500 | +501 | +502 | +503 | +504 | +505 | +506 | +507 | +508 | +509 | +510 | +511 | +512 | +513 | +514 | +515 | +516 | +517 | +518 | +519 | +520 | +521 | +522 | +523 | +524 | +525 | +526 | +527 | +528 | +529 | +530 | +531 | +532 | +533 | +534 | +535 | +536 | +537 | +538 | +539 | +540 | +541 | +542 | +543 | +544 | +545 | +546 | +547 | +548 | +549 | +550 | +551 | +552 | +553 | +554 | +555 | +556 | +557 | +558 | +559 | +560 | +561 | +562 | +563 | +564 | +565 | +566 | +567 | +568 | +569 | +570 | +571 | +572 | +573 | +574 | +575 | +576 | +577 | +578 | +579 | +580 | +581 | +582 | +583 | +584 | +585 | +586 | +587 | +588 | +589 | +590 | +591 | +592 | +593 | +594 | +595 | +596 | +597 | +598 | +599 | +600 | +601 | +602 | +603 | +604 | +605 | +606 | +607 | +608 | +609 | +610 | +611 | +612 | +613 | +614 | +615 | +616 | +617 | +618 | +619 | +620 | +621 | +622 | +623 | +624 | +625 | +626 | +627 | +628 | +629 | +630 | +631 | +632 | +633 | +634 | +635 | +636 | +637 | +638 | +639 | +640 | +641 | +642 | +643 | +644 | +645 | +646 | +647 | +648 | +649 | +650 | +651 | +652 | +653 | +654 | +655 | +656 | +657 | +658 | +659 | +660 | +661 | +662 | +663 | +664 | +665 | +666 | +667 | +668 | +669 | +670 | +671 | +672 | +673 | +674 | +675 | +676 | +677 | +678 | +679 | +680 | +681 | +682 | +683 | +684 | +685 | +686 | +687 | +688 | +689 | +690 | +691 | +692 | +693 | +694 | +695 | +696 | +697 | +698 | +699 | +700 | +701 | +702 | +703 | +704 | +705 | +706 | +707 | +708 | +709 | +710 | +711 | +712 | +713 | +714 | +715 | +716 | +717 | +718 | +719 | +720 | +721 | +722 | +723 | +724 | +725 | +726 | +727 | +728 | +729 | +730 | +731 | +732 | +733 | +734 | +735 | +736 | +737 | +738 | +739 | +740 | +741 | +742 | +743 | +744 | +745 | +746 | +747 | +748 | +749 | +750 | +751 | +752 | +753 | +754 | +755 | +756 | +757 | +758 | +759 | +760 | +761 | +762 | +763 | +764 | +765 | +766 | +767 | +768 | +769 | +770 | +771 | +772 | +773 | +774 | +775 | +776 | +777 | +778 | +779 | +780 | +781 | +782 | +783 | +784 | +785 | +786 | +787 | +788 | +789 | +790 | +791 | +792 | +793 | +794 | +795 | +796 | +797 | +798 | +799 | +800 | +801 | +802 | +803 | +804 | +805 | +806 | +807 | +808 | +809 | +810 | +811 | +812 | +813 | +814 | +815 | +816 | +817 | +818 | +819 | +820 | +821 | +822 | +823 | +824 | +825 | +826 | +827 | +828 | +829 | +830 | +831 | +832 | +833 | +834 | +835 | +836 | +837 | +838 | +839 | +840 | +841 | +842 | +843 | +844 | +845 | +846 | +847 | +848 | +849 | +850 | +851 | +852 | +853 | +854 | +855 | +856 | +857 | +858 | +859 | +860 | +861 | +862 | +863 | +864 | +865 | +866 | +867 | +868 | +869 | +870 | +871 | +872 | +873 | +874 | +875 | +876 | +877 | +878 | +879 | +880 | +881 | +882 | +883 | +884 | +885 | +886 | +887 | +888 | +889 | +890 | +891 | +892 | +893 | +894 | +895 | +896 | +897 | +898 | +899 | +900 | +901 | +902 | +903 | +904 | +905 | +906 | +907 | +908 | +909 | +910 | +911 | +912 | +913 | +914 | +915 | +916 | +917 | +918 | +919 | +920 | +921 | +922 | +923 | +924 | +925 | +926 | +927 | +928 | +929 | +930 | +931 | +932 | +933 | +934 | +935 | +936 | +937 | +938 | +939 | +940 | +941 | +942 | +943 | +944 | +945 | +946 | +947 | +948 | +949 | +950 | +951 | +952 | +953 | +954 | +955 | +956 | +957 | +958 | +959 | +960 | +961 | +962 | +963 | +964 | +965 | +966 | +967 | +968 | +969 | +970 | +971 | +972 | +973 | +974 | +975 | +976 | +977 | +978 | +979 | +980 | +981 | +982 | +983 | +984 | +985 | +986 | +987 | +988 | +989 | +990 | +991 | +992 | +993 | +994 | +995 | +996 | +997 | +998 | +999 | +1000 | +1001 | +1002 | +1003 | +1004 | +1005 | +1006 | +1007 | +1008 | +1009 | +1010 | +1011 | +1012 | +1013 | +1014 | +1015 | +1016 | +1017 | +1018 | +1019 | +1020 | +1021 | +1022 | +1023 | +1024 | +1025 | +1026 | +1027 | +1028 | +1029 | +1030 | +1031 | +1032 | +1033 | +1034 | +1035 | +1036 | +1037 | +1038 | +1039 | +1040 | +1041 | +1042 | +1043 | +1044 | +1045 | +1046 | +1047 | +1048 | +1049 | +1050 | +1051 | +1052 | +1053 | +1054 | +1055 | +1056 | +1057 | +1058 | +1059 | +1060 | +1061 | +1062 | +1063 | +1064 | +1065 | +1066 | +1067 | +1068 | +1069 | +1070 | +1071 | +1072 | +1073 | +1074 | +1075 | +1076 | +1077 | +1078 | +1079 | +1080 | +1081 | +1082 | +1083 | +1084 | +1085 | +1086 | +1087 | +1088 | +1089 | +1090 | +1091 | +1092 | +1093 | +1094 | +1095 | +1096 | +1097 | +1098 | +1099 | +1100 | +1101 | +1102 | +1103 | +1104 | +1105 | +1106 | +1107 | +1108 | +1109 | +1110 | +1111 | +1112 | +1113 | +1114 | +1115 | +1116 | +1117 | +1118 | +1119 | +1120 | +1121 | +1122 | +1123 | +1124 | +1125 | +1126 | +1127 | +1128 | +1129 | +1130 | +1131 | +1132 | +1133 | +1134 | +1135 | +1136 | +1137 | +1138 | +1139 | +1140 | +1141 | +1142 | +1143 | +1144 | +1145 | +1146 | +1147 | +1148 | +1149 | +1150 | +1151 | +1152 | +1153 | +1154 | +1155 | +1156 | +1157 | +1158 | +1159 | +1160 | +1161 | +1162 | +1163 | +1164 | +1165 | +1166 | +1167 | +1168 | +1169 | +1170 | +1171 | +1172 | +1173 | +1174 | +1175 | +1176 | +1177 | +1178 | +1179 | +1180 | +1181 | +1182 | +1183 | +1184 | +1185 | +1186 | +1187 | +1188 | +1189 | +1190 | +1191 | +1192 | +1193 | +1194 | +1195 | +1196 | +1197 | +1198 | +1199 | +1200 | +1201 | +1202 | +1203 | +1204 | +1205 | +1206 | +1207 | +1208 | +1209 | +1210 | +1211 | +1212 | +1213 | +1214 | +1215 | +1216 | +1217 | +1218 | +1219 | +1220 | +1221 | +1222 | +1223 | +1224 | +1225 | +1226 | +1227 | +1228 | +1229 | +1230 | +1231 | +1232 | +1233 | +1234 | +1235 | +1236 | +1237 | +1238 | +1239 | +1240 | +1241 | +1242 | +1243 | +1244 | +1245 | +1246 | +1247 | +1248 | +1249 | +1250 | +1251 | +1252 | +1253 | +1254 | +1255 | +1256 | +1257 | +1258 | +1259 | +1260 | +1261 | +1262 | +1263 | +1264 | +1265 | +1266 | +1267 | +1268 | +1269 | +1270 | +1271 | +1272 | +1273 | +1274 | +1275 | +1276 | +1277 | +1278 | +1279 | +1280 | +1281 | +1282 | +1283 | +1284 | +1285 | +1286 | +1287 | +1288 | +1289 | +1290 | +1291 | +1292 | +1293 | +1294 | +1295 | +1296 | +1297 | +1298 | +1299 | +1300 | +1301 | +1302 | +1303 | +1304 | +1305 | +1306 | +1307 | +1308 | +1309 | +1310 | +1311 | +1312 | +1313 | +1314 | +1315 | +1316 | +1317 | +1318 | +1319 | +1320 | +1321 | +1322 | +1323 | +1324 | +1325 | +1326 | +1327 | +1328 | +1329 | +1330 | +1331 | +1332 | +1333 | +1334 | +1335 | +1336 | +1337 | +1338 | +1339 | +1340 | +1341 | +1342 | +1343 | +1344 | +1345 | +1346 | +1347 | +1348 | +1349 | +1350 | +1351 | +1352 | +1353 | +1354 | +1355 | +1356 | +1357 | +1358 | +1359 | +1360 | +1361 | +1362 | +1363 | +1364 | +1365 | +1366 | +1367 | +1368 | +1369 | +1370 | +1371 | +1372 | +1373 | +1374 | +1375 | +1376 | +1377 | +1378 | +1379 | +1380 | +1381 | +1382 | +1383 | +1384 | +1385 | +1386 | +1387 | +1388 | +1389 | +1390 | +1391 | +1392 | +1393 | +1394 | +1395 | +1396 | +1397 | +1398 | +1399 | +1400 | +1401 | +1402 | +1403 | +1404 | +1405 | +1406 | +1407 | +1408 | +1409 | +1410 | +1411 | +1412 | +1413 | +1414 | +1415 | +1416 | +1417 | +1418 | +1419 | +1420 | +1421 | +1422 | +1423 | +1424 | +1425 | +1426 | +1427 | +1428 | +1429 | +1430 | +1431 | +1432 | +1433 | +1434 | +1435 | +1436 | +1437 | +1438 | +1439 | +1440 | +1441 | +1442 | +1443 | +1444 | +1445 | +1446 | +1447 | +1448 | +1449 | +1450 | +1451 | +1452 | +1453 | +1454 | +1455 | +1456 | +1457 | +1458 | +1459 | +1460 | +1461 | +1462 | +1463 | +1464 | +1465 | +1466 | +1467 | +1468 | +1469 | +1470 | +1471 | +1472 | +1473 | +1474 | +1475 | +1476 | +1477 | +1478 | +1479 | +1480 | +1481 | +1482 | +1483 | +1484 | +1485 | +1486 | +1487 | +1488 | +1489 | +1490 | +1491 | +1492 | +1493 | +1494 | +1495 | +1496 | +1497 | +1498 | +1499 | +1500 | +1501 | +1502 | +1503 | +1504 | +1505 | +1506 | +1507 | +1508 | +1509 | +1510 | +1511 | +1512 | +1513 | +1514 | +1515 | +1516 | +1517 | +1518 | +1519 | +1520 | +1521 | +1522 | +1523 | +1524 | +1525 | +1526 | +1527 | +1528 | +1529 | +1530 | +1531 | +1532 | +1533 | +1534 | +1535 | +1536 | +1537 | +1538 | +1539 | +1540 | +1541 | +1542 | +1543 | +1544 | +1545 | +1546 | +1547 | +1548 | +1549 | +1550 | +1551 | +1552 | +1553 | +1554 | +1555 | +1556 | +1557 | +1558 | +1559 | +1560 | +1561 | +1562 | +1563 | +1564 | +1565 | +1566 | +1567 | +1568 | +1569 | +1570 | +1571 | +1572 | +1573 | +1574 | +1575 | +1576 | +1577 | +1578 | +1579 | +1580 | +1581 | +1582 | +1583 | +1584 | +1585 | +1586 | +1587 | +1588 | +1589 | +1590 | +1591 | +1592 | +1593 | +1594 | +1595 | +1596 | +1597 | +1598 | +1599 | +1600 | +1601 | +1602 | +1603 | +1604 | +1605 | +1606 | +1607 | +1608 | +1609 | +1610 | +1611 | +1612 | +1613 | +1614 | +1615 | +1616 | +1617 | +1618 | +1619 | +1620 | +1621 | +1622 | +1623 | +1624 | +1625 | +1626 | +1627 | +1628 | +1629 | +1630 | +1631 | +1632 | +1633 | +1634 | +1635 | +1636 | +1637 | +1638 | +1639 | +1640 | +1641 | +1642 | +1643 | +1644 | +1645 | +1646 | +1647 | +1648 | +1649 | +1650 | +1651 | +1652 | +1653 | +1654 | +1655 | +1656 | +1657 | +1658 | +1659 | +1660 | +1661 | +1662 | +1663 | +1664 | +1665 | +1666 | +1667 | +1668 | +1669 | +1670 | +1671 | +1672 | +1673 | +1674 | +1675 | +1676 | +1677 | +1678 | +1679 | +1680 | +1681 | +1682 | +1683 | +1684 | +1685 | +1686 | +1687 | +1688 | +1689 | +1690 | +1691 | +1692 | +1693 | +1694 | +1695 | +1696 | +1697 | +1698 | +1699 | +1700 | +1701 | +1702 | +1703 | +1704 | +1705 | +1706 | +1707 | +1708 | +1709 | +1710 | +1711 | +1712 | +1713 | +1714 | +1715 | +1716 | +1717 | +1718 | +1719 | +1720 | +1721 | +1722 | +1723 | +1724 | +1725 | +1726 | +1727 | +1728 | +1729 | +1730 | +1731 | +1732 | +1733 | +1734 | +1735 | +1736 | +1737 | +1738 | +1739 | +1740 | +1741 | +1742 | +1743 | +1744 | +1745 | +1746 | +1747 | +1748 | +1749 | +1750 | +1751 | +1752 | +1753 | +1754 | +1755 | +1756 | +1757 | +1758 | +1759 | +1760 | +1761 | +1762 | +1763 | +1764 | +1765 | +1766 | +1767 | +1768 | +1769 | +1770 | +1771 | +1772 | +1773 | +1774 | +1775 | +1776 | +1777 | +1778 | +1779 | +1780 | +1781 | +1782 | +1783 | +1784 | +1785 | +1786 | +1787 | +1788 | +1789 | +1790 | +1791 | +1792 | +1793 | +1794 | +1795 | +1796 | +1797 | +1798 | +1799 | +1800 | +1801 | +1802 | +1803 | +1804 | +1805 | +1806 | +1807 | +1808 | +1809 | +1810 | +1811 | +1812 | +1813 | +1814 | +1815 | +1816 | +1817 | +1818 | +1819 | +1820 | +1821 | +1822 | +1823 | +1824 | +1825 | +1826 | +1827 | +1828 | +1829 | +1830 | +1831 | +1832 | +1833 | +1834 | +1835 | +1836 | +1837 | +1838 | +1839 | +1840 | +1841 | +1842 | +1843 | +1844 | +1845 | +1846 | +1847 | +1848 | +1849 | +1850 | +1851 | +1852 | +1853 | +1854 | +1855 | +1856 | +1857 | +1858 | +1859 | +1860 | +1861 | +1862 | +1863 | +1864 | +1865 | +1866 | +1867 | +1868 | +1869 | +1870 | +1871 | +1872 | +1873 | +1874 | +1875 | +1876 | +1877 | +1878 | +1879 | +1880 | +1881 | +1882 | +1883 | +1884 | +1885 | +1886 | +1887 | +1888 | +1889 | +1890 | +1891 | +1892 | +1893 | +1894 | +1895 | +1896 | +1897 | +1898 | +1899 | +1900 | +1901 | +1902 | +1903 | +1904 | +1905 | +1906 | +1907 | +1908 | +1909 | +1910 | +1911 | +1912 | +1913 | +1914 | +1915 | +1916 | +1917 | +1918 | +1919 | +1920 | +1921 | +1922 | +1923 | +1924 | +1925 | +1926 | +1927 | +1928 | +1929 | +1930 | +1931 | +1932 | +1933 | +1934 | +1935 | +1936 | +1937 | +1938 | +1939 | +1940 | +1941 | +1942 | +1943 | +1944 | +1945 | +1946 | +1947 | +1948 | +1949 | +1950 | +1951 | +1952 | +1953 | +1954 | +1955 | +1956 | +1957 | +1958 | +1959 | +1960 | +1961 | +1962 | +1963 | +1964 | +1965 | +1966 | +1967 | +1968 | +1969 | +1970 | +1971 | +1972 | +1973 | +1974 | +1975 | +1976 | +1977 | +1978 | +1979 | +1980 | +1981 | +1982 | +1983 | +1984 | +1985 | +1986 | +1987 | +1988 | +1989 | +1990 | +1991 | +1992 | +1993 | +1994 | +1995 | +1996 | +1997 | +1998 | +1999 | +2000 | +2001 | +2002 | +2003 | +2004 | +2005 | +2006 | +2007 | +2008 | +2009 | +2010 | +2011 | +2012 | +2013 | +2014 | +2015 | +2016 | +2017 | +2018 | +2019 | +2020 | +2021 | +2022 | +2023 | +2024 | +2025 | +2026 | +2027 | +2028 | +2029 | +2030 | +2031 | +2032 | +2033 | +2034 | +2035 | +2036 | +2037 | +2038 | +2039 | +2040 | +2041 | +2042 | +2043 | +2044 | +2045 | +2046 | +2047 | +2048 | +2049 | +2050 | +2051 | +2052 | +2053 | +2054 | +2055 | +2056 | +2057 | +2058 | +2059 | +2060 | +2061 | +2062 | +2063 | +2064 | +2065 | +2066 | +2067 | +2068 | +2069 | +2070 | +2071 | +2072 | +2073 | +2074 | +2075 | +2076 | +2077 | +2078 | +2079 | +2080 | +2081 | +2082 | +2083 | +2084 | +2085 | +2086 | +2087 | +2088 | +2089 | +2090 | +2091 | +2092 | +2093 | +2094 | +2095 | +2096 | +2097 | +2098 | +2099 | +2100 | +2101 | +2102 | +2103 | +2104 | +2105 | +2106 | +2107 | +2108 | +2109 | +2110 | +2111 | +2112 | +2113 | +2114 | +2115 | +2116 | +2117 | +2118 | +2119 | +2120 | +2121 | +2122 | +2123 | +2124 | +2125 | +2126 | +2127 | +2128 | +2129 | +2130 | +2131 | +2132 | +2133 | +2134 | +2135 | +2136 | +2137 | +2138 | +2139 | +2140 | +2141 | +2142 | +2143 | +2144 | +2145 | +2146 | +2147 | +2148 | +2149 | +2150 | +2151 | +2152 | +2153 | +2154 | +2155 | +2156 | +2157 | +2158 | +2159 | +2160 | +2161 | +2162 | +2163 | +2164 | +2165 | +2166 | +2167 | +2168 | +2169 | +2170 | +2171 | +2172 | +2173 | +2174 | +2175 | +2176 | +2177 | +2178 | +2179 | +2180 | +2181 | +2182 | +2183 | +2184 | +2185 | +2186 | +2187 | +2188 | +2189 | +2190 | +2191 | +2192 | +2193 | +2194 | +2195 | +2196 | +2197 | +2198 | +2199 | +2200 | +2201 | +2202 | +2203 | +2204 | +2205 | +2206 | +2207 | +2208 | +2209 | +2210 | +2211 | +2212 | +2213 | +2214 | +2215 | +2216 | +2217 | +2218 | +2219 | +2220 | +2221 | +2222 | +2223 | +2224 | +2225 | +2226 | +2227 | +2228 | +2229 | +2230 | +2231 | +2232 | +2233 | +2234 | +2235 | +2236 | +2237 | +2238 | +2239 | +2240 | +2241 | +2242 | +2243 | +2244 | +2245 | +2246 | +2247 | +2248 | +2249 | +2250 | +2251 | +2252 | +2253 | +2254 | +2255 | +2256 | +2257 | +2258 | +2259 | +2260 | +2261 | +2262 | +2263 | +2264 | +2265 | +2266 | +2267 | +2268 | +2269 | +2270 | +2271 | +2272 | +2273 | +2274 | +2275 | +2276 | +2277 | +2278 | +2279 | +2280 | +2281 | +2282 | +2283 | +2284 | +2285 | +2286 | +2287 | +2288 | +2289 | +2290 | +2291 | +2292 | +2293 | +2294 | +2295 | +2296 | +2297 | +2298 | +2299 | +2300 | +2301 | +2302 | +2303 | +2304 | +2305 | +2306 | +2307 | +2308 | +2309 | +2310 | +2311 | +2312 | +2313 | +2314 | +2315 | +2316 | +2317 | +2318 | +2319 | +2320 | +2321 | +2322 | +2323 | +2324 | +2325 | +2326 | +2327 | +2328 | +2329 | +2330 | +2331 | +2332 | +2333 | +2334 | +2335 | +2336 | +2337 | +2338 | +2339 | +2340 | +2341 | +2342 | +2343 | +2344 | +2345 | +2346 | +2347 | +2348 | +2349 | +2350 | +2351 | +2352 | +2353 | +2354 | +2355 | +2356 | +2357 | +2358 | +2359 | +2360 | +2361 | +2362 | +2363 | +2364 | +2365 | +2366 | +2367 | +2368 | +2369 | +2370 | +2371 | +2372 | +2373 | +2374 | +2375 | +2376 | +2377 | +2378 | +2379 | +2380 | +2381 | +2382 | +2383 | +2384 | +2385 | +2386 | +2387 | +2388 | +2389 | +2390 | +2391 | +2392 | +2393 | +2394 | +2395 | +2396 | +2397 | +2398 | +2399 | +2400 | +2401 | +2402 | +2403 | +2404 | +2405 | +2406 | +2407 | +2408 | +2409 | +2410 | +2411 | +2412 | +2413 | +2414 | +2415 | +2416 | +2417 | +2418 | +2419 | +2420 | +2421 | +2422 | +2423 | +2424 | +2425 | +2426 | +2427 | +2428 | +2429 | +2430 | +2431 | +2432 | +2433 | +2434 | +2435 | +2436 | +2437 | +2438 | +2439 | +2440 | +2441 | +2442 | +2443 | +2444 | +2445 | +2446 | +2447 | +2448 | +2449 | +2450 | +2451 | +2452 | +2453 | +2454 | +2455 | +2456 | +2457 | +2458 | +2459 | +2460 | +2461 | +2462 | +2463 | +2464 | +2465 | +2466 | +2467 | +2468 | +2469 | +2470 | +2471 | +2472 | +2473 | +2474 | +2475 | +2476 | +2477 | +2478 | +2479 | +2480 | +2481 | +2482 | +2483 | +2484 | +2485 | +2486 | +2487 | +2488 | +2489 | +2490 | +2491 | +2492 | +2493 | +2494 | +2495 | +2496 | +2497 | +2498 | +2499 | +2500 | +2501 | +2502 | +2503 | +2504 | +2505 | +2506 | +2507 | +2508 | +2509 | +2510 | +2511 | +2512 | +2513 | +2514 | +2515 | +2516 | +2517 | +2518 | +2519 | +2520 | +2521 | +2522 | +2523 | +2524 | +2525 | +2526 | +2527 | +2528 | +2529 | +2530 | +2531 | +2532 | +2533 | +2534 | +2535 | +2536 | +2537 | +2538 | +2539 | +2540 | +2541 | +2542 | +2543 | +2544 | +2545 | +2546 | +2547 | +2548 | +2549 | +2550 | +2551 | +2552 | +2553 | +2554 | +2555 | +2556 | +2557 | +2558 | +2559 | +2560 | +2561 | +2562 | +2563 | +2564 | +2565 | +2566 | +2567 | +2568 | +2569 | +2570 | +2571 | +2572 | +2573 | +2574 | +2575 | +2576 | +2577 | +2578 | +2579 | +2580 | +2581 | +2582 | +2583 | +2584 | +2585 | +2586 | +2587 | +2588 | +2589 | +2590 | +2591 | +2592 | +2593 | +2594 | +2595 | +2596 | +2597 | +2598 | +2599 | +2600 | +2601 | +2602 | +2603 | +2604 | +2605 | +2606 | +2607 | +2608 | +2609 | +2610 | +2611 | +2612 | +2613 | +2614 | +2615 | +2616 | +2617 | +2618 | +2619 | +2620 | +2621 | +2622 | +2623 | +2624 | +2625 | +2626 | +2627 | +2628 | +2629 | +2630 | +2631 | +2632 | +2633 | +2634 | +2635 | +2636 | +2637 | +2638 | +2639 | +2640 | +2641 | +2642 | +2643 | +2644 | +2645 | +2646 | +2647 | +2648 | +2649 | +2650 | +2651 | +2652 | +2653 | +2654 | +2655 | +2656 | +2657 | +2658 | +2659 | +2660 | +2661 | +2662 | +2663 | +2664 | +2665 | +2666 | +2667 | +2668 | +2669 | +2670 | +2671 | +2672 | +2673 | +2674 | +2675 | +2676 | +2677 | +2678 | +2679 | +2680 | +2681 | +2682 | +2683 | +2684 | +2685 | +2686 | +2687 | +2688 | +2689 | +2690 | +2691 | +2692 | +2693 | +2694 | +2695 | +2696 | +2697 | +2698 | +2699 | +2700 | +2701 | +2702 | +2703 | +2704 | +2705 | +2706 | +2707 | +2708 | +2709 | +2710 | +2711 | +2712 | +2713 | +2714 | +2715 | +2716 | +2717 | +2718 | +2719 | +2720 | +2721 | +2722 | +2723 | +2724 | +2725 | +2726 | +2727 | +2728 | +2729 | +2730 | +2731 | +2732 | +2733 | +2734 | +2735 | +2736 | +2737 | +2738 | +2739 | +2740 | +2741 | +2742 | +2743 | +2744 | +2745 | +2746 | +2747 | +2748 | +2749 | +2750 | +2751 | +2752 | +2753 | +2754 | +2755 | +2756 | +2757 | +2758 | +2759 | +2760 | +2761 | +2762 | +2763 | +2764 | +2765 | +2766 | +2767 | +2768 | +2769 | +2770 | +2771 | +2772 | +2773 | +2774 | +2775 | +2776 | +2777 | +2778 | +2779 | +2780 | +2781 | +2782 | +2783 | +2784 | +2785 | +2786 | +2787 | +2788 | +2789 | +2790 | +2791 | +2792 | +2793 | +2794 | +2795 | +2796 | +2797 | +2798 | +2799 | +2800 | +2801 | +2802 | +2803 | +2804 | +2805 | +2806 | +2807 | +2808 | +2809 | +2810 | +2811 | +2812 | +2813 | +2814 | +2815 | +2816 | +2817 | +2818 | +2819 | +2820 | +2821 | +2822 | +2823 | +2824 | +2825 | +2826 | +2827 | +2828 | +2829 | +2830 | +2831 | +2832 | +2833 | +2834 | +2835 | +2836 | +2837 | +2838 | +2839 | +2840 | +2841 | +2842 | +2843 | +2844 | +2845 | +2846 | +2847 | +2848 | +2849 | +2850 | +2851 | +2852 | +2853 | +2854 | +2855 | +2856 | +2857 | +2858 | +2859 | +2860 | +2861 | +2862 | +2863 | +2864 | +2865 | +2866 | +2867 | +2868 | +2869 | +2870 | +2871 | +2872 | +2873 | +2874 | +2875 | +2876 | +2877 | +2878 | +2879 | +2880 | +2881 | +2882 | +2883 | +2884 | +2885 | +2886 | +2887 | +2888 | +2889 | +2890 | +2891 | +2892 | +2893 | +2894 | +2895 | +2896 | +2897 | +2898 | +2899 | +2900 | +2901 | +2902 | +2903 | +2904 | +2905 | +2906 | +2907 | +2908 | +2909 | +2910 | +2911 | +2912 | +2913 | +2914 | +2915 | +2916 | +2917 | +2918 | +2919 | +2920 | +2921 | +2922 | +2923 | +2924 | +2925 | +2926 | +2927 | +2928 | +2929 | +2930 | +2931 | +2932 | +2933 | +2934 | +2935 | +2936 | +2937 | +2938 | +2939 | +2940 | +2941 | +2942 | +2943 | +2944 | +2945 | +2946 | +2947 | +2948 | +2949 | +2950 | +2951 | +2952 | +2953 | +2954 | +2955 | +2956 | +2957 | +2958 | +2959 | +2960 | +2961 | +2962 | +2963 | +2964 | +2965 | +2966 | +2967 | +2968 | +2969 | +2970 | +2971 | +2972 | +2973 | +2974 | +2975 | +2976 | +2977 | +2978 | +2979 | +2980 | +2981 | +2982 | +2983 | +2984 | +2985 | +2986 | +2987 | +2988 | +2989 | +2990 | +2991 | +2992 | +2993 | +2994 | +2995 | +2996 | +2997 | +2998 | +2999 | +3000 | +3001 | +3002 | +3003 | +3004 | +3005 | +3006 | +3007 | +3008 | +3009 | +3010 | +3011 | +3012 | +3013 | +3014 | +3015 | +3016 | +3017 | +3018 | +3019 | +3020 | +3021 | +3022 | +3023 | +3024 | +3025 | +3026 | +3027 | +3028 | +3029 | +3030 | +3031 | +3032 | +3033 | +3034 | +3035 | +3036 | +3037 | +3038 | +3039 | +3040 | +3041 | +3042 | +3043 | +3044 | +3045 | +3046 | +3047 | +3048 | +3049 | +3050 | +3051 | +3052 | +3053 | +3054 | +3055 | +3056 | +3057 | +3058 | +3059 | +3060 | +3061 | +3062 | +3063 | +3064 | +3065 | +3066 | +3067 | +3068 | +3069 | +3070 | +3071 | +3072 | +3073 | +3074 | +3075 | +3076 | +3077 | +3078 | +3079 | +3080 | +3081 | +3082 | +3083 | +3084 | +3085 | +3086 | +3087 | +3088 | +3089 | +3090 | +3091 | +3092 | +3093 | +3094 | +3095 | +3096 | +3097 | +3098 | +3099 | +3100 | +3101 | +3102 | +3103 | +3104 | +3105 | +3106 | +3107 | +3108 | +3109 | +3110 | +3111 | +3112 | +3113 | +3114 | +3115 | +3116 | +3117 | +3118 | +3119 | +3120 | +3121 | +3122 | +3123 | +3124 | +3125 | +3126 | +3127 | +3128 | +3129 | +3130 | +3131 | +3132 | +3133 | +3134 | +3135 | +3136 | +3137 | +3138 | +3139 | +3140 | +3141 | +3142 | +3143 | +3144 | +3145 | +3146 | +3147 | +3148 | +3149 | +3150 | +3151 | +3152 | +3153 | +3154 | +3155 | +3156 | +3157 | +3158 | +3159 | +3160 | +3161 | +3162 | +3163 | +3164 | +3165 | +3166 | +3167 | +3168 | +3169 | +3170 | +3171 | +3172 | +3173 | +3174 | +3175 | +3176 | +3177 | +3178 | +3179 | +3180 | +3181 | +3182 | +3183 | +3184 | +3185 | +3186 | +3187 | +3188 | +3189 | +3190 | +3191 | +3192 | +3193 | +3194 | +3195 | +3196 | +3197 | +3198 | +3199 | +3200 | +3201 | +3202 | +3203 | +3204 | +3205 | +3206 | +3207 | +3208 | +3209 | +3210 | +3211 | +3212 | +3213 | +3214 | +3215 | +3216 | +3217 | +3218 | +3219 | +3220 | +3221 | +3222 | +3223 | +3224 | +3225 | +3226 | +3227 | +3228 | +3229 | +3230 | +3231 | +3232 | +3233 | +3234 | +3235 | +3236 | +3237 | +3238 | +3239 | +3240 | +3241 | +3242 | +3243 | +3244 | +3245 | +3246 | +3247 | +3248 | +3249 | +3250 | +3251 | +3252 | +3253 | +3254 | +3255 | +3256 | +3257 | +3258 | +3259 | +3260 | +3261 | +3262 | +3263 | +3264 | +3265 | +3266 | +3267 | +3268 | +3269 | +3270 | +3271 | +3272 | +3273 | +3274 | +3275 | +3276 | +3277 | +3278 | +3279 | +3280 | +3281 | +3282 | +3283 | +3284 | +3285 | +3286 | +3287 | +3288 | +3289 | +3290 | +3291 | +3292 | +3293 | +3294 | +3295 | +3296 | +3297 | +3298 | +3299 | +3300 | +3301 | +3302 | +3303 | +3304 | +3305 | +3306 | +3307 | +3308 | +3309 | +3310 | +3311 | +3312 | +3313 | +3314 | +3315 | +3316 | +3317 | +3318 | +3319 | +3320 | +3321 | +3322 | +3323 | +3324 | +3325 | +3326 | +3327 | +3328 | +3329 | +3330 | +3331 | +3332 | +3333 | +3334 | +3335 | +3336 | +3337 | +3338 | +3339 | +3340 | +3341 | +3342 | +3343 | +3344 | +3345 | +3346 | +3347 | +3348 | +3349 | +3350 | +3351 | +3352 | +3353 | +3354 | +3355 | +3356 | +3357 | +3358 | +3359 | +3360 | +3361 | +3362 | +3363 | +3364 | +3365 | +3366 | +3367 | +3368 | +3369 | +3370 | +3371 | +3372 | +3373 | +3374 | +3375 | +3376 | +3377 | +3378 | +3379 | +3380 | +3381 | +3382 | +3383 | +3384 | +3385 | +3386 | +3387 | +3388 | +3389 | +3390 | +3391 | +3392 | +3393 | +3394 | +3395 | +3396 | +3397 | +3398 | +3399 | +3400 | +3401 | +3402 | +3403 | +3404 | +3405 | +3406 | +3407 | +3408 | +3409 | +3410 | +3411 | +3412 | +3413 | +3414 | +3415 | +3416 | +3417 | +3418 | +3419 | +3420 | +3421 | +3422 | +3423 | +3424 | +3425 | +3426 | +3427 | +3428 | +3429 | +3430 | +3431 | +3432 | +3433 | +3434 | +3435 | +3436 | +3437 | +3438 | +3439 | +3440 | +3441 | +3442 | +3443 | +3444 | +3445 | +3446 | +3447 | +3448 | +3449 | +3450 | +3451 | +3452 | +3453 | +3454 | +3455 | +3456 | +3457 | +3458 | +3459 | +3460 | +3461 | +3462 | +3463 | +3464 | +3465 | +3466 | +3467 | +3468 | +3469 | +3470 | +3471 | +3472 | +3473 | +3474 | +3475 | +3476 | +3477 | +3478 | +3479 | +3480 | +3481 | +3482 | +3483 | +3484 | +3485 | +3486 | +3487 | +3488 | +3489 | +3490 | +3491 | +3492 | +3493 | +3494 | +3495 | +3496 | +3497 | +3498 | +3499 | +3500 | +3501 | +3502 | +3503 | +3504 | +3505 | +3506 | +3507 | +3508 | +3509 | +3510 | +3511 | +3512 | +3513 | +3514 | +3515 | +3516 | +3517 | +3518 | +3519 | +3520 | +3521 | +3522 | +3523 | +3524 | +3525 | +3526 | +3527 | +3528 | +3529 | +3530 | +3531 | +3532 | +3533 | +3534 | +3535 | +3536 | +3537 | +3538 | +3539 | +3540 | +3541 | +3542 | +3543 | +3544 | +3545 | +3546 | +3547 | +3548 | +3549 | +3550 | +3551 | +3552 | +3553 | +3554 | +3555 | +3556 | +3557 | +3558 | +3559 | +3560 | +3561 | +3562 | +3563 | +3564 | +3565 | +3566 | +3567 | +3568 | +3569 | +3570 | +3571 | +3572 | +3573 | +3574 | +3575 | +3576 | +3577 | +3578 | +3579 | +3580 | +3581 | +3582 | +3583 | +3584 | +3585 | +3586 | +3587 | +3588 | +3589 | +3590 | +3591 | +3592 | +3593 | +3594 | +3595 | +3596 | +3597 | +3598 | +3599 | +3600 | +3601 | +3602 | +3603 | +3604 | +3605 | +3606 | +3607 | +3608 | +3609 | +3610 | +3611 | +3612 | +3613 | +3614 | +3615 | +3616 | +3617 | +3618 | +3619 | +3620 | +3621 | +3622 | +3623 | +3624 | +3625 | +3626 | +3627 | +3628 | +3629 | +3630 | +3631 | +3632 | +3633 | +3634 | +3635 | +3636 | +3637 | +3638 | +3639 | +3640 | +3641 | +3642 | +3643 | +3644 | +3645 | +3646 | +3647 | +3648 | +3649 | +3650 | +3651 | +3652 | +3653 | +3654 | +3655 | +3656 | +3657 | +3658 | +3659 | +3660 | +3661 | +3662 | +3663 | +3664 | +3665 | +3666 | +3667 | +3668 | +3669 | +3670 | +3671 | +3672 | +3673 | +3674 | +3675 | +3676 | +3677 | +3678 | +3679 | +3680 | +3681 | +3682 | +3683 | +3684 | +3685 | +3686 | +3687 | +3688 | +3689 | +3690 | +3691 | +3692 | +3693 | +3694 | +3695 | +3696 | +3697 | +3698 | +3699 | +3700 | +3701 | +3702 | +3703 | +3704 | +3705 | +3706 | +3707 | +3708 | +3709 | +3710 | +3711 | +3712 | +3713 | +3714 | +3715 | +3716 | +3717 | +3718 | +3719 | +3720 | +3721 | +3722 | +3723 | +3724 | +3725 | +3726 | +3727 | +3728 | +3729 | +3730 | +3731 | +3732 | +3733 | +3734 | +3735 | +3736 | +3737 | +3738 | +3739 | +3740 | +3741 | +3742 | +3743 | +3744 | +3745 | +3746 | +3747 | +3748 | +3749 | +3750 | +3751 | +3752 | +3753 | +3754 | +3755 | +3756 | +3757 | +3758 | +3759 | +3760 | +3761 | +3762 | +3763 | +3764 | +3765 | +3766 | +3767 | +3768 | +3769 | +3770 | +3771 | +3772 | +3773 | +3774 | +3775 | +3776 | +3777 | +3778 | +3779 | +3780 | +3781 | +3782 | +3783 | +3784 | +3785 | +3786 | +3787 | +3788 | +3789 | +3790 | +3791 | +3792 | +3793 | +3794 | +3795 | +3796 | +3797 | +3798 | +3799 | +3800 | +3801 | +3802 | +3803 | +3804 | +3805 | +3806 | +3807 | +3808 | +3809 | +3810 | +3811 | +3812 | +3813 | +3814 | +3815 | +3816 | +3817 | +3818 | +3819 | +3820 | +3821 | +3822 | +3823 | +3824 | +3825 | +3826 | +3827 | +3828 | +3829 | +3830 | +3831 | +3832 | +3833 | +3834 | +3835 | +3836 | +3837 | +3838 | +3839 | +3840 | +3841 | +3842 | +3843 | +3844 | +3845 | +3846 | +3847 | +3848 | +3849 | +3850 | +3851 | +3852 | +3853 | +3854 | +3855 | +3856 | +3857 | +3858 | +3859 | +3860 | +3861 | +3862 | +3863 | +3864 | +3865 | +3866 | +3867 | +3868 | +3869 | +3870 | +3871 | +3872 | +3873 | +3874 | +3875 | +3876 | +3877 | +3878 | +3879 | +3880 | +3881 | +3882 | +3883 | +3884 | +3885 | +3886 | +3887 | +3888 | +3889 | +3890 | +3891 | +3892 | +3893 | +3894 | +3895 | +3896 | +3897 | +3898 | +3899 | +3900 | +3901 | +3902 | +3903 | +3904 | +3905 | +3906 | +3907 | +3908 | +3909 | +3910 | +3911 | +3912 | +3913 | +3914 | +3915 | +3916 | +3917 | +3918 | +3919 | +3920 | +3921 | +3922 | +3923 | +3924 | +3925 | +3926 | +3927 | +3928 | +3929 | +3930 | +3931 | +3932 | +3933 | +3934 | +3935 | +3936 | +3937 | +3938 | +3939 | +3940 | +3941 | +3942 | +3943 | +3944 | +3945 | +3946 | +3947 | +3948 | +3949 | +3950 | +3951 | +3952 | +3953 | +3954 | +3955 | +3956 | +3957 | +3958 | +3959 | +3960 | +3961 | +3962 | +3963 | +3964 | +3965 | +3966 | +3967 | +3968 | +3969 | +3970 | +3971 | +3972 | +3973 | +3974 | +3975 | +3976 | +3977 | +3978 | +3979 | +3980 | +3981 | +3982 | +3983 | +3984 | +3985 | +3986 | +3987 | +3988 | +3989 | +3990 | +3991 | +3992 | +3993 | +3994 | +3995 | +3996 | +3997 | +3998 | +3999 | +4000 | +4001 | +4002 | +4003 | +4004 | +4005 | +4006 | +4007 | +4008 | +4009 | +4010 | +4011 | +4012 | +4013 | +4014 | +4015 | +4016 | +4017 | +4018 | +4019 | +4020 | +4021 | +4022 | +4023 | +4024 | +4025 | +4026 | +4027 | +4028 | +4029 | +4030 | +4031 | +4032 | +4033 | +4034 | +4035 | +4036 | +4037 | +4038 | +4039 | +4040 | +4041 | +4042 | +4043 | +4044 | +4045 | +4046 | +4047 | +4048 | +4049 | +4050 | +4051 | +4052 | +4053 | +4054 | +4055 | +4056 | +4057 | +4058 | +4059 | +4060 | +4061 | +4062 | +4063 | +4064 | +4065 | +4066 | +4067 | +4068 | +4069 | +4070 | +4071 | +4072 | +4073 | +4074 | +4075 | +4076 | +4077 | +4078 | +4079 | +4080 | +4081 | +4082 | +4083 | +4084 | +4085 | +4086 | +4087 | +4088 | +4089 | +4090 | +4091 | +4092 | +4093 | +4094 | +4095 | +4096 | +4097 | +4098 | +4099 | +4100 | +4101 | +4102 | +4103 | +4104 | +4105 | +4106 | +4107 | +4108 | +4109 | +4110 | +4111 | +4112 | +4113 | +4114 | +4115 | +4116 | +4117 | +4118 | +4119 | +4120 | +4121 | +4122 | +4123 | +4124 | +4125 | +4126 | +4127 | +4128 | +4129 | +4130 | +4131 | +4132 | +4133 | +4134 | +4135 | +4136 | +4137 | +4138 | +4139 | +4140 | +4141 | +4142 | +4143 | +4144 | +4145 | +4146 | +4147 | +4148 | +4149 | +4150 | +4151 | +4152 | +4153 | +4154 | +4155 | +4156 | +4157 | +4158 | +4159 | +4160 | +4161 | +4162 | +4163 | +4164 | +4165 | +4166 | +4167 | +4168 | +4169 | +4170 | +4171 | +4172 | +4173 | +4174 | +4175 | +4176 | +4177 | +4178 | +4179 | +4180 | +4181 | +4182 | +4183 | +4184 | +4185 | +4186 | +4187 | +4188 | +4189 | +4190 | +4191 | +4192 | +4193 | +4194 | +4195 | +4196 | +4197 | +4198 | +4199 | +4200 | +4201 | +4202 | +4203 | +4204 | +4205 | +4206 | +4207 | +4208 | +4209 | +4210 | +4211 | +4212 | +4213 | +4214 | +4215 | +4216 | +4217 | +4218 | +4219 | +4220 | +4221 | +4222 | +4223 | +4224 | +4225 | +4226 | +4227 | +4228 | +4229 | +4230 | +4231 | +4232 | +4233 | +4234 | +4235 | +4236 | +4237 | +4238 | +4239 | +4240 | +4241 | +4242 | +4243 | +4244 | +4245 | +4246 | +4247 | +4248 | +4249 | +4250 | +4251 | +4252 | +4253 | +4254 | +4255 | +4256 | +4257 | +4258 | +4259 | +4260 | +4261 | +4262 | +4263 | +4264 | +4265 | +4266 | +4267 | +4268 | +4269 | +4270 | +4271 | +4272 | +4273 | +4274 | +4275 | +4276 | +4277 | +4278 | +4279 | +4280 | +4281 | +4282 | +4283 | +4284 | +4285 | +4286 | +4287 | +4288 | +4289 | +4290 | +4291 | +4292 | +4293 | +4294 | +4295 | +4296 | +4297 | +4298 | +4299 | +4300 | +4301 | +4302 | +4303 | +4304 | +4305 | +4306 | +4307 | +4308 | +4309 | +4310 | +4311 | +4312 | +4313 | +4314 | +4315 | +4316 | +4317 | +4318 | +4319 | +4320 | +4321 | +4322 | +4323 | +4324 | +4325 | +4326 | +4327 | +4328 | +4329 | +4330 | +4331 | +4332 | +4333 | +4334 | +4335 | +4336 | +4337 | +4338 | +4339 | +4340 | +4341 | +4342 | +4343 | +4344 | +4345 | +4346 | +4347 | +4348 | +4349 | +4350 | +4351 | +4352 | +4353 | +4354 | +4355 | +4356 | +4357 | +4358 | +4359 | +4360 | +4361 | +4362 | +4363 | +4364 | +4365 | +4366 | +4367 | +4368 | +4369 | +4370 | +4371 | +4372 | +4373 | +4374 | +4375 | +4376 | +4377 | +4378 | +4379 | +4380 | +4381 | +4382 | +4383 | +4384 | +4385 | +4386 | +4387 | +4388 | +4389 | +4390 | +4391 | +4392 | +4393 | +4394 | +4395 | +4396 | +4397 | +4398 | +4399 | +4400 | +4401 | +4402 | +4403 | +4404 | +4405 | +4406 | +4407 | +4408 | +4409 | +4410 | +4411 | +4412 | +4413 | +4414 | +4415 | +4416 | +4417 | +4418 | +4419 | +4420 | +4421 | +4422 | +4423 | +4424 | +4425 | +4426 | +4427 | +4428 | +4429 | +4430 | +4431 | +4432 | +4433 | +4434 | +4435 | +4436 | +4437 | +4438 | +4439 | +4440 | +4441 | +4442 | +4443 | +4444 | +4445 | +4446 | +4447 | +4448 | +4449 | +4450 | +4451 | +4452 | +4453 | +4454 | +4455 | +4456 | +4457 | +4458 | +4459 | +4460 | +4461 | +4462 | +4463 | +4464 | +4465 | +4466 | +4467 | +4468 | +4469 | +4470 | +4471 | +4472 | +4473 | +4474 | +4475 | +4476 | +4477 | +4478 | +4479 | +4480 | +4481 | +4482 | +4483 | +4484 | +4485 | +4486 | +4487 | +4488 | +4489 | +4490 | +4491 | +4492 | +4493 | +4494 | +4495 | +4496 | +4497 | +4498 | +4499 | +4500 | +4501 | +4502 | +4503 | +4504 | +4505 | +4506 | +4507 | +4508 | +4509 | +4510 | +4511 | +4512 | +4513 | +4514 | +4515 | +4516 | +4517 | +4518 | +4519 | +4520 | +4521 | +4522 | +4523 | +4524 | +4525 | +4526 | +4527 | +4528 | +4529 | +4530 | +4531 | +4532 | +4533 | +4534 | +4535 | +4536 | +4537 | +4538 | +4539 | +4540 | +4541 | +4542 | +4543 | +4544 | +4545 | +4546 | +4547 | +4548 | +4549 | +4550 | +4551 | +4552 | +4553 | +4554 | +4555 | +4556 | +4557 | +4558 | +4559 | +4560 | +4561 | +4562 | +4563 | +4564 | +4565 | +4566 | +4567 | +4568 | +4569 | +4570 | +4571 | +4572 | +4573 | +4574 | +4575 | +4576 | +4577 | +4578 | +4579 | +4580 | +4581 | +4582 | +4583 | +4584 | +4585 | +4586 | +4587 | +4588 | +4589 | +4590 | +4591 | +4592 | +4593 | +4594 | +4595 | +4596 | +4597 | +4598 | +4599 | +4600 | +4601 | +4602 | +4603 | +4604 | +4605 | +4606 | +4607 | +4608 | +4609 | +4610 | +4611 | +4612 | +4613 | +4614 | +4615 | +4616 | +4617 | +4618 | +4619 | +4620 | +4621 | +4622 | +4623 | +4624 | +4625 | +4626 | +4627 | +4628 | +4629 | +4630 | +4631 | +4632 | +4633 | +4634 | +4635 | +4636 | +4637 | +4638 | +4639 | +4640 | +4641 | +4642 | +4643 | +4644 | +4645 | +4646 | +4647 | +4648 | +4649 | +4650 | +4651 | +4652 | +4653 | +4654 | +4655 | +4656 | +4657 | +4658 | +4659 | +4660 | +4661 | +4662 | +4663 | +4664 | +4665 | +4666 | +4667 | +4668 | +4669 | +4670 | +4671 | +4672 | +4673 | +4674 | +4675 | +4676 | +4677 | +4678 | +4679 | +4680 | +4681 | +4682 | +4683 | +4684 | +4685 | +4686 | +4687 | +4688 | +4689 | +4690 | +4691 | +4692 | +4693 | +4694 | +4695 | +4696 | +4697 | +4698 | +4699 | +4700 | +4701 | +4702 | +4703 | +4704 | +4705 | +4706 | +4707 | +4708 | +4709 | +4710 | +4711 | +4712 | +4713 | +4714 | +4715 | +4716 | +4717 | +4718 | +4719 | +4720 | +4721 | +4722 | +4723 | +4724 | +4725 | +4726 | +4727 | +4728 | +4729 | +4730 | +4731 | +4732 | +4733 | +4734 | +4735 | +4736 | +4737 | +4738 | +4739 | +4740 | +4741 | +4742 | +4743 | +4744 | +4745 | +4746 | +4747 | +4748 | +4749 | +4750 | +4751 | +4752 | +4753 | +4754 | +4755 | +4756 | +4757 | +4758 | +4759 | +4760 | +4761 | +4762 | +4763 | +4764 | +4765 | +4766 | +4767 | +4768 | +4769 | +4770 | +4771 | +4772 | +4773 | +4774 | +4775 | +4776 | +4777 | +4778 | +4779 | +4780 | +4781 | +4782 | +4783 | +4784 | +4785 | +4786 | +4787 | +4788 | +4789 | +4790 | +4791 | +4792 | +4793 | +4794 | +4795 | +4796 | +4797 | +4798 | +4799 | +4800 | +4801 | +4802 | +4803 | +4804 | +4805 | +4806 | +4807 | +4808 | +4809 | +4810 | +4811 | +4812 | +4813 | +4814 | +4815 | +4816 | +4817 | +4818 | +4819 | +4820 | +4821 | +4822 | +4823 | +4824 | +4825 | +4826 | +4827 | +4828 | +4829 | +4830 | +4831 | +4832 | +4833 | +4834 | +4835 | +4836 | +4837 | +4838 | +4839 | +4840 | +4841 | +4842 | +4843 | +4844 | +4845 | +4846 | +4847 | +4848 | +4849 | +4850 | +4851 | +4852 | +4853 | +4854 | +4855 | +4856 | +4857 | +4858 | +4859 | +4860 | +4861 | +4862 | +4863 | +4864 | +4865 | +4866 | +4867 | +4868 | +4869 | +4870 | +4871 | +4872 | +4873 | +4874 | +4875 | +4876 | +4877 | +4878 | +4879 | +4880 | +4881 | +4882 | +4883 | +4884 | +4885 | +4886 | +4887 | +4888 | +4889 | +4890 | +4891 | +4892 | +4893 | +4894 | +4895 | +4896 | +4897 | +4898 | +4899 | +4900 | +4901 | +4902 | +4903 | +4904 | +4905 | +4906 | +4907 | +4908 | +4909 | +4910 | +4911 | +4912 | +4913 | +4914 | +4915 | +4916 | +4917 | +4918 | +4919 | +4920 | +4921 | +4922 | +4923 | +4924 | +4925 | +4926 | +4927 | +4928 | +4929 | +4930 | +4931 | +4932 | +4933 | +4934 | +4935 | +4936 | +4937 | +4938 | +4939 | +4940 | +4941 | +4942 | +4943 | +4944 | +4945 | +4946 | +4947 | +4948 | +4949 | +4950 | +4951 | +4952 | +4953 | +4954 | +4955 | +4956 | +4957 | +4958 | +4959 | +4960 | +4961 | +4962 | +4963 | +4964 | +4965 | +4966 | +4967 | +4968 | +4969 | +4970 | +4971 | +4972 | +4973 | +4974 | +4975 | +4976 | +4977 | +4978 | +4979 | +4980 | +4981 | +4982 | +4983 | +4984 | +4985 | +4986 | +4987 | +4988 | +4989 | +4990 | +4991 | +4992 | +4993 | +4994 | +4995 | +4996 | +4997 | +4998 | +4999; + +type Data = { + error?: ErrorCode, +}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/ast-types/lib/types.js:60 + throw new Error(str + \" does not match type \" + this); + ^ + +Error: {type: NumericLiteralTypeAnnotation, start: 843, end: 844, loc: [object Object], value: 0, extra: [object Object]} does not match type Printable + at Type.Tp.assert (/node_modules/ast-types/lib/types.js:60:19) + at genericPrintNoParens (/src/printer.js:221:24) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1530:42) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) +" +`; diff --git a/tests/union/fields.js b/tests/union/fields.js new file mode 100644 index 000000000000..f3bb77b06f98 --- /dev/null +++ b/tests/union/fields.js @@ -0,0 +1,6 @@ +class C { + x: ?number|string; + constructor() { + this.x = null; + } +} diff --git a/tests/union/fields2.js b/tests/union/fields2.js new file mode 100644 index 000000000000..b065e17fddb2 --- /dev/null +++ b/tests/union/fields2.js @@ -0,0 +1,9 @@ +class C { } + +class D { + content: string|C; + copyContent(content: C): string|C { + this.content = content; + return this.content; + } +} diff --git a/tests/union/issue-17.js b/tests/union/issue-17.js new file mode 100644 index 000000000000..18c6dc49571c --- /dev/null +++ b/tests/union/issue-17.js @@ -0,0 +1,16 @@ +/* @flow */ + +type T = + {type: "a"; a: number} | + {type: "b"; b: string}; + +var l: Array = [ + {type: "a", a: 1}, + {type: "a", a: 2}, + {type: "a", a: 3}, + {type: "a", a: 4}, + {type: "b", b: "monkey"}, + {type: "b", b: "gorilla"}, + {type: "b", b: "giraffe"}, + {type: "b", b: "penguin"}, +]; diff --git a/tests/union/issue-198.js b/tests/union/issue-198.js new file mode 100644 index 000000000000..72072f498088 --- /dev/null +++ b/tests/union/issue-198.js @@ -0,0 +1,10 @@ +var p = new Promise(function(resolve, reject) { + resolve(5); +}) + .then(function(num) { + return num.toFixed(); + }) + .then(function(str) { + // This should fail because str is string, not number + return str.toFixed(); + }); diff --git a/tests/union/issue-256.js b/tests/union/issue-256.js new file mode 100644 index 000000000000..5fc84c9519be --- /dev/null +++ b/tests/union/issue-256.js @@ -0,0 +1,7 @@ +declare class Myclass { + myfun(myarray: Array): any; +} +declare var myclass: Myclass; + +myclass.myfun(["1", "2", "3", "4", "5", "6", function (ar) {}]) +myclass.myfun(["1", "2", "3", "4", "5", "6", "7", function (ar) {}]) diff --git a/tests/union/issue-323-lib.js b/tests/union/issue-323-lib.js new file mode 100644 index 000000000000..8d0e73801568 --- /dev/null +++ b/tests/union/issue-323-lib.js @@ -0,0 +1,3 @@ +/* @flow */ +class Foo {} +module.exports = Foo; diff --git a/tests/union/issue-323.js b/tests/union/issue-323.js new file mode 100644 index 000000000000..f5216b84e06c --- /dev/null +++ b/tests/union/issue-323.js @@ -0,0 +1,3 @@ +var Foo = require("./issue-323-lib"); +var foo = new Foo(); +var foostr: Foo | string = foo; diff --git a/tests/union/issue-324.js b/tests/union/issue-324.js new file mode 100644 index 000000000000..a18cf7a8482f --- /dev/null +++ b/tests/union/issue-324.js @@ -0,0 +1,8 @@ +/* @flow */ +class Foo{}; +class Bar{}; + +var foostr: Foo | string = new Foo(); +var barstr: Bar | string = new Bar(); + +foostr = barstr; diff --git a/tests/union/issue-325.js b/tests/union/issue-325.js new file mode 100644 index 000000000000..7fefbc251caa --- /dev/null +++ b/tests/union/issue-325.js @@ -0,0 +1,14 @@ +class Tag { + constructor() { + var a1: Array = []; + var a2: Array = a1; + } +} + +type Node = Tag_ | string; +class Tag_ { + constructor() { + var a1: Array = [new Tag_]; + var a2: Array = a1; + } +} diff --git a/tests/union/issue-326.js b/tests/union/issue-326.js new file mode 100644 index 000000000000..d3de6e5f9616 --- /dev/null +++ b/tests/union/issue-326.js @@ -0,0 +1,4 @@ +var numberAndStringArr:Array = [1,2]; +var stringArr:Array = ['a','b']; + +var result = numberAndStringArr.concat(stringArr); // no error diff --git a/tests/union/issue-582.js b/tests/union/issue-582.js new file mode 100644 index 000000000000..1793a84226dd --- /dev/null +++ b/tests/union/issue-582.js @@ -0,0 +1,14 @@ +/*** + * nested unions + * @flow + */ + +// inline +var nested1: ('foo' | 'bar') | 'baz' = 'baz'; + +// through tvars +type FooBar = 'foo' | 'bar'; +type Baz = 'baz'; +type FooBarBaz = FooBar | Baz; + +var nested2: FooBarBaz = 'baz'; diff --git a/tests/union/issue-963.js b/tests/union/issue-963.js new file mode 100644 index 000000000000..b4803c6da641 --- /dev/null +++ b/tests/union/issue-963.js @@ -0,0 +1,34 @@ +/*** + * unions with embedded intersections + * @flow + */ + +type t1 = { + p1 : number +}; + +type t2 = { + p2: number +} + +type t3 = { + p3 : number +} + +type intersected = t1 & t2; +type union = intersected | t3; +type union2 = t3 | intersected; + +const u1 : union = { + p3 : 3 +}; + +const u2 : union = { + p1 : 1, + p2 : 2 +}; + +const u3 : union2 = { + p1 : 1, + p2 : 2 +}; diff --git a/tests/union/jsfmt.spec.js b/tests/union/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/union/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/union/test-lib.js b/tests/union/test-lib.js new file mode 100644 index 000000000000..e994d2afd4dd --- /dev/null +++ b/tests/union/test-lib.js @@ -0,0 +1,4 @@ +/* @providesModule test-lib */ + +class C { } +module.exports = C; diff --git a/tests/union/test.js b/tests/union/test.js new file mode 100644 index 000000000000..5acbee01e223 --- /dev/null +++ b/tests/union/test.js @@ -0,0 +1,18 @@ +var C = require('test-lib'); + +// TODO: spurious error! (replacing C with number makes the error go away) +// type Foo = Array | Array; +type Foo = Array; // workaround +var x:Array = []; +var y:Array = []; +function foo(x:Foo) {} +foo(x); +foo(y); + +// TODO: spurious error! (replacing C with number makes the error go away) +// type Bar = (() => C) | (() => string); +type Bar = () => (C | string); // workaround + +function f() { return ""; } +function bar(x:Bar) { } +bar(f); diff --git a/tests/union/type-app.js b/tests/union/type-app.js new file mode 100644 index 000000000000..21b4feffd7e7 --- /dev/null +++ b/tests/union/type-app.js @@ -0,0 +1,14 @@ +/** + * @flow + */ + +class LocalClass {} + +var a: LocalClass | number = 123; + +// Iterator is defined in a lib file, so the speculative algorithm for the +// union type would incorrectly succeed for Iterator. Only later during +// the merge would we fine the error, but it would be too late. The diff that +// introduces this test fixes this such that the speculative algorithm is +// correctly delayed upon encountering a non-concrete TypeAppT +var b: Iterator | number = 123; diff --git a/tests/union/union.js b/tests/union/union.js new file mode 100644 index 000000000000..1aa068b024de --- /dev/null +++ b/tests/union/union.js @@ -0,0 +1,21 @@ +function bar(x: Document | string): void { } +bar(0); + +class C { } +class D { } +function CD(b) { + var E = b? C: D; + var c:C = new E(); // error, since E could be D, and D is not a subtype of C + function qux(e: E) { } // this annotation is an error: is it C, or is it D? + function qux2(e: C | D) { } // OK + qux2(new C); +} + +declare class F { + foo(x: number):void; + foo(x: string):void; +} +function corge(b) { + var x = b ? "" : 0; + new F().foo(x); +} diff --git a/tests/union/yuge.js b/tests/union/yuge.js new file mode 100644 index 000000000000..ad2028252900 --- /dev/null +++ b/tests/union/yuge.js @@ -0,0 +1,5031 @@ +/** + * great big union used as type annotation will be flowed into itself. + * List.mem check avoids pathology, as long as singletons aren't converted. + * This is a band-aid, though: see second case. + * @flow + */ +'use strict'; + +class SecurityCheckupTypedLogger { + _data: Data; + + setError(value: ErrorCode) { + this._data['error'] = value; + } + + // Bug: right now, any incompatible type coming into a huge + // union might blow the recursion limit. + // TODO real solution is to specialize union reps for obvious cases, + // e.g. (base type, list in decl order, set). Of course we could do + // something quick to get the union size off the Ocaml call stack, + // but not sure it's worth doing that before the real solution. + ohThatsNotSoGood() { + ("": ErrorCode); // also, error pos omits this line completely + } +} + +type ErrorCode = +0 | +1 | +2 | +3 | +4 | +5 | +6 | +7 | +8 | +9 | +10 | +11 | +12 | +13 | +14 | +15 | +16 | +17 | +18 | +19 | +20 | +21 | +22 | +23 | +24 | +25 | +26 | +27 | +28 | +29 | +30 | +31 | +32 | +33 | +34 | +35 | +36 | +37 | +38 | +39 | +40 | +41 | +42 | +43 | +44 | +45 | +46 | +47 | +48 | +49 | +50 | +51 | +52 | +53 | +54 | +55 | +56 | +57 | +58 | +59 | +60 | +61 | +62 | +63 | +64 | +65 | +66 | +67 | +68 | +69 | +70 | +71 | +72 | +73 | +74 | +75 | +76 | +77 | +78 | +79 | +80 | +81 | +82 | +83 | +84 | +85 | +86 | +87 | +88 | +89 | +90 | +91 | +92 | +93 | +94 | +95 | +96 | +97 | +98 | +99 | +100 | +101 | +102 | +103 | +104 | +105 | +106 | +107 | +108 | +109 | +110 | +111 | +112 | +113 | +114 | +115 | +116 | +117 | +118 | +119 | +120 | +121 | +122 | +123 | +124 | +125 | +126 | +127 | +128 | +129 | +130 | +131 | +132 | +133 | +134 | +135 | +136 | +137 | +138 | +139 | +140 | +141 | +142 | +143 | +144 | +145 | +146 | +147 | +148 | +149 | +150 | +151 | +152 | +153 | +154 | +155 | +156 | +157 | +158 | +159 | +160 | +161 | +162 | +163 | +164 | +165 | +166 | +167 | +168 | +169 | +170 | +171 | +172 | +173 | +174 | +175 | +176 | +177 | +178 | +179 | +180 | +181 | +182 | +183 | +184 | +185 | +186 | +187 | +188 | +189 | +190 | +191 | +192 | +193 | +194 | +195 | +196 | +197 | +198 | +199 | +200 | +201 | +202 | +203 | +204 | +205 | +206 | +207 | +208 | +209 | +210 | +211 | +212 | +213 | +214 | +215 | +216 | +217 | +218 | +219 | +220 | +221 | +222 | +223 | +224 | +225 | +226 | +227 | +228 | +229 | +230 | +231 | +232 | +233 | +234 | +235 | +236 | +237 | +238 | +239 | +240 | +241 | +242 | +243 | +244 | +245 | +246 | +247 | +248 | +249 | +250 | +251 | +252 | +253 | +254 | +255 | +256 | +257 | +258 | +259 | +260 | +261 | +262 | +263 | +264 | +265 | +266 | +267 | +268 | +269 | +270 | +271 | +272 | +273 | +274 | +275 | +276 | +277 | +278 | +279 | +280 | +281 | +282 | +283 | +284 | +285 | +286 | +287 | +288 | +289 | +290 | +291 | +292 | +293 | +294 | +295 | +296 | +297 | +298 | +299 | +300 | +301 | +302 | +303 | +304 | +305 | +306 | +307 | +308 | +309 | +310 | +311 | +312 | +313 | +314 | +315 | +316 | +317 | +318 | +319 | +320 | +321 | +322 | +323 | +324 | +325 | +326 | +327 | +328 | +329 | +330 | +331 | +332 | +333 | +334 | +335 | +336 | +337 | +338 | +339 | +340 | +341 | +342 | +343 | +344 | +345 | +346 | +347 | +348 | +349 | +350 | +351 | +352 | +353 | +354 | +355 | +356 | +357 | +358 | +359 | +360 | +361 | +362 | +363 | +364 | +365 | +366 | +367 | +368 | +369 | +370 | +371 | +372 | +373 | +374 | +375 | +376 | +377 | +378 | +379 | +380 | +381 | +382 | +383 | +384 | +385 | +386 | +387 | +388 | +389 | +390 | +391 | +392 | +393 | +394 | +395 | +396 | +397 | +398 | +399 | +400 | +401 | +402 | +403 | +404 | +405 | +406 | +407 | +408 | +409 | +410 | +411 | +412 | +413 | +414 | +415 | +416 | +417 | +418 | +419 | +420 | +421 | +422 | +423 | +424 | +425 | +426 | +427 | +428 | +429 | +430 | +431 | +432 | +433 | +434 | +435 | +436 | +437 | +438 | +439 | +440 | +441 | +442 | +443 | +444 | +445 | +446 | +447 | +448 | +449 | +450 | +451 | +452 | +453 | +454 | +455 | +456 | +457 | +458 | +459 | +460 | +461 | +462 | +463 | +464 | +465 | +466 | +467 | +468 | +469 | +470 | +471 | +472 | +473 | +474 | +475 | +476 | +477 | +478 | +479 | +480 | +481 | +482 | +483 | +484 | +485 | +486 | +487 | +488 | +489 | +490 | +491 | +492 | +493 | +494 | +495 | +496 | +497 | +498 | +499 | +500 | +501 | +502 | +503 | +504 | +505 | +506 | +507 | +508 | +509 | +510 | +511 | +512 | +513 | +514 | +515 | +516 | +517 | +518 | +519 | +520 | +521 | +522 | +523 | +524 | +525 | +526 | +527 | +528 | +529 | +530 | +531 | +532 | +533 | +534 | +535 | +536 | +537 | +538 | +539 | +540 | +541 | +542 | +543 | +544 | +545 | +546 | +547 | +548 | +549 | +550 | +551 | +552 | +553 | +554 | +555 | +556 | +557 | +558 | +559 | +560 | +561 | +562 | +563 | +564 | +565 | +566 | +567 | +568 | +569 | +570 | +571 | +572 | +573 | +574 | +575 | +576 | +577 | +578 | +579 | +580 | +581 | +582 | +583 | +584 | +585 | +586 | +587 | +588 | +589 | +590 | +591 | +592 | +593 | +594 | +595 | +596 | +597 | +598 | +599 | +600 | +601 | +602 | +603 | +604 | +605 | +606 | +607 | +608 | +609 | +610 | +611 | +612 | +613 | +614 | +615 | +616 | +617 | +618 | +619 | +620 | +621 | +622 | +623 | +624 | +625 | +626 | +627 | +628 | +629 | +630 | +631 | +632 | +633 | +634 | +635 | +636 | +637 | +638 | +639 | +640 | +641 | +642 | +643 | +644 | +645 | +646 | +647 | +648 | +649 | +650 | +651 | +652 | +653 | +654 | +655 | +656 | +657 | +658 | +659 | +660 | +661 | +662 | +663 | +664 | +665 | +666 | +667 | +668 | +669 | +670 | +671 | +672 | +673 | +674 | +675 | +676 | +677 | +678 | +679 | +680 | +681 | +682 | +683 | +684 | +685 | +686 | +687 | +688 | +689 | +690 | +691 | +692 | +693 | +694 | +695 | +696 | +697 | +698 | +699 | +700 | +701 | +702 | +703 | +704 | +705 | +706 | +707 | +708 | +709 | +710 | +711 | +712 | +713 | +714 | +715 | +716 | +717 | +718 | +719 | +720 | +721 | +722 | +723 | +724 | +725 | +726 | +727 | +728 | +729 | +730 | +731 | +732 | +733 | +734 | +735 | +736 | +737 | +738 | +739 | +740 | +741 | +742 | +743 | +744 | +745 | +746 | +747 | +748 | +749 | +750 | +751 | +752 | +753 | +754 | +755 | +756 | +757 | +758 | +759 | +760 | +761 | +762 | +763 | +764 | +765 | +766 | +767 | +768 | +769 | +770 | +771 | +772 | +773 | +774 | +775 | +776 | +777 | +778 | +779 | +780 | +781 | +782 | +783 | +784 | +785 | +786 | +787 | +788 | +789 | +790 | +791 | +792 | +793 | +794 | +795 | +796 | +797 | +798 | +799 | +800 | +801 | +802 | +803 | +804 | +805 | +806 | +807 | +808 | +809 | +810 | +811 | +812 | +813 | +814 | +815 | +816 | +817 | +818 | +819 | +820 | +821 | +822 | +823 | +824 | +825 | +826 | +827 | +828 | +829 | +830 | +831 | +832 | +833 | +834 | +835 | +836 | +837 | +838 | +839 | +840 | +841 | +842 | +843 | +844 | +845 | +846 | +847 | +848 | +849 | +850 | +851 | +852 | +853 | +854 | +855 | +856 | +857 | +858 | +859 | +860 | +861 | +862 | +863 | +864 | +865 | +866 | +867 | +868 | +869 | +870 | +871 | +872 | +873 | +874 | +875 | +876 | +877 | +878 | +879 | +880 | +881 | +882 | +883 | +884 | +885 | +886 | +887 | +888 | +889 | +890 | +891 | +892 | +893 | +894 | +895 | +896 | +897 | +898 | +899 | +900 | +901 | +902 | +903 | +904 | +905 | +906 | +907 | +908 | +909 | +910 | +911 | +912 | +913 | +914 | +915 | +916 | +917 | +918 | +919 | +920 | +921 | +922 | +923 | +924 | +925 | +926 | +927 | +928 | +929 | +930 | +931 | +932 | +933 | +934 | +935 | +936 | +937 | +938 | +939 | +940 | +941 | +942 | +943 | +944 | +945 | +946 | +947 | +948 | +949 | +950 | +951 | +952 | +953 | +954 | +955 | +956 | +957 | +958 | +959 | +960 | +961 | +962 | +963 | +964 | +965 | +966 | +967 | +968 | +969 | +970 | +971 | +972 | +973 | +974 | +975 | +976 | +977 | +978 | +979 | +980 | +981 | +982 | +983 | +984 | +985 | +986 | +987 | +988 | +989 | +990 | +991 | +992 | +993 | +994 | +995 | +996 | +997 | +998 | +999 | +1000 | +1001 | +1002 | +1003 | +1004 | +1005 | +1006 | +1007 | +1008 | +1009 | +1010 | +1011 | +1012 | +1013 | +1014 | +1015 | +1016 | +1017 | +1018 | +1019 | +1020 | +1021 | +1022 | +1023 | +1024 | +1025 | +1026 | +1027 | +1028 | +1029 | +1030 | +1031 | +1032 | +1033 | +1034 | +1035 | +1036 | +1037 | +1038 | +1039 | +1040 | +1041 | +1042 | +1043 | +1044 | +1045 | +1046 | +1047 | +1048 | +1049 | +1050 | +1051 | +1052 | +1053 | +1054 | +1055 | +1056 | +1057 | +1058 | +1059 | +1060 | +1061 | +1062 | +1063 | +1064 | +1065 | +1066 | +1067 | +1068 | +1069 | +1070 | +1071 | +1072 | +1073 | +1074 | +1075 | +1076 | +1077 | +1078 | +1079 | +1080 | +1081 | +1082 | +1083 | +1084 | +1085 | +1086 | +1087 | +1088 | +1089 | +1090 | +1091 | +1092 | +1093 | +1094 | +1095 | +1096 | +1097 | +1098 | +1099 | +1100 | +1101 | +1102 | +1103 | +1104 | +1105 | +1106 | +1107 | +1108 | +1109 | +1110 | +1111 | +1112 | +1113 | +1114 | +1115 | +1116 | +1117 | +1118 | +1119 | +1120 | +1121 | +1122 | +1123 | +1124 | +1125 | +1126 | +1127 | +1128 | +1129 | +1130 | +1131 | +1132 | +1133 | +1134 | +1135 | +1136 | +1137 | +1138 | +1139 | +1140 | +1141 | +1142 | +1143 | +1144 | +1145 | +1146 | +1147 | +1148 | +1149 | +1150 | +1151 | +1152 | +1153 | +1154 | +1155 | +1156 | +1157 | +1158 | +1159 | +1160 | +1161 | +1162 | +1163 | +1164 | +1165 | +1166 | +1167 | +1168 | +1169 | +1170 | +1171 | +1172 | +1173 | +1174 | +1175 | +1176 | +1177 | +1178 | +1179 | +1180 | +1181 | +1182 | +1183 | +1184 | +1185 | +1186 | +1187 | +1188 | +1189 | +1190 | +1191 | +1192 | +1193 | +1194 | +1195 | +1196 | +1197 | +1198 | +1199 | +1200 | +1201 | +1202 | +1203 | +1204 | +1205 | +1206 | +1207 | +1208 | +1209 | +1210 | +1211 | +1212 | +1213 | +1214 | +1215 | +1216 | +1217 | +1218 | +1219 | +1220 | +1221 | +1222 | +1223 | +1224 | +1225 | +1226 | +1227 | +1228 | +1229 | +1230 | +1231 | +1232 | +1233 | +1234 | +1235 | +1236 | +1237 | +1238 | +1239 | +1240 | +1241 | +1242 | +1243 | +1244 | +1245 | +1246 | +1247 | +1248 | +1249 | +1250 | +1251 | +1252 | +1253 | +1254 | +1255 | +1256 | +1257 | +1258 | +1259 | +1260 | +1261 | +1262 | +1263 | +1264 | +1265 | +1266 | +1267 | +1268 | +1269 | +1270 | +1271 | +1272 | +1273 | +1274 | +1275 | +1276 | +1277 | +1278 | +1279 | +1280 | +1281 | +1282 | +1283 | +1284 | +1285 | +1286 | +1287 | +1288 | +1289 | +1290 | +1291 | +1292 | +1293 | +1294 | +1295 | +1296 | +1297 | +1298 | +1299 | +1300 | +1301 | +1302 | +1303 | +1304 | +1305 | +1306 | +1307 | +1308 | +1309 | +1310 | +1311 | +1312 | +1313 | +1314 | +1315 | +1316 | +1317 | +1318 | +1319 | +1320 | +1321 | +1322 | +1323 | +1324 | +1325 | +1326 | +1327 | +1328 | +1329 | +1330 | +1331 | +1332 | +1333 | +1334 | +1335 | +1336 | +1337 | +1338 | +1339 | +1340 | +1341 | +1342 | +1343 | +1344 | +1345 | +1346 | +1347 | +1348 | +1349 | +1350 | +1351 | +1352 | +1353 | +1354 | +1355 | +1356 | +1357 | +1358 | +1359 | +1360 | +1361 | +1362 | +1363 | +1364 | +1365 | +1366 | +1367 | +1368 | +1369 | +1370 | +1371 | +1372 | +1373 | +1374 | +1375 | +1376 | +1377 | +1378 | +1379 | +1380 | +1381 | +1382 | +1383 | +1384 | +1385 | +1386 | +1387 | +1388 | +1389 | +1390 | +1391 | +1392 | +1393 | +1394 | +1395 | +1396 | +1397 | +1398 | +1399 | +1400 | +1401 | +1402 | +1403 | +1404 | +1405 | +1406 | +1407 | +1408 | +1409 | +1410 | +1411 | +1412 | +1413 | +1414 | +1415 | +1416 | +1417 | +1418 | +1419 | +1420 | +1421 | +1422 | +1423 | +1424 | +1425 | +1426 | +1427 | +1428 | +1429 | +1430 | +1431 | +1432 | +1433 | +1434 | +1435 | +1436 | +1437 | +1438 | +1439 | +1440 | +1441 | +1442 | +1443 | +1444 | +1445 | +1446 | +1447 | +1448 | +1449 | +1450 | +1451 | +1452 | +1453 | +1454 | +1455 | +1456 | +1457 | +1458 | +1459 | +1460 | +1461 | +1462 | +1463 | +1464 | +1465 | +1466 | +1467 | +1468 | +1469 | +1470 | +1471 | +1472 | +1473 | +1474 | +1475 | +1476 | +1477 | +1478 | +1479 | +1480 | +1481 | +1482 | +1483 | +1484 | +1485 | +1486 | +1487 | +1488 | +1489 | +1490 | +1491 | +1492 | +1493 | +1494 | +1495 | +1496 | +1497 | +1498 | +1499 | +1500 | +1501 | +1502 | +1503 | +1504 | +1505 | +1506 | +1507 | +1508 | +1509 | +1510 | +1511 | +1512 | +1513 | +1514 | +1515 | +1516 | +1517 | +1518 | +1519 | +1520 | +1521 | +1522 | +1523 | +1524 | +1525 | +1526 | +1527 | +1528 | +1529 | +1530 | +1531 | +1532 | +1533 | +1534 | +1535 | +1536 | +1537 | +1538 | +1539 | +1540 | +1541 | +1542 | +1543 | +1544 | +1545 | +1546 | +1547 | +1548 | +1549 | +1550 | +1551 | +1552 | +1553 | +1554 | +1555 | +1556 | +1557 | +1558 | +1559 | +1560 | +1561 | +1562 | +1563 | +1564 | +1565 | +1566 | +1567 | +1568 | +1569 | +1570 | +1571 | +1572 | +1573 | +1574 | +1575 | +1576 | +1577 | +1578 | +1579 | +1580 | +1581 | +1582 | +1583 | +1584 | +1585 | +1586 | +1587 | +1588 | +1589 | +1590 | +1591 | +1592 | +1593 | +1594 | +1595 | +1596 | +1597 | +1598 | +1599 | +1600 | +1601 | +1602 | +1603 | +1604 | +1605 | +1606 | +1607 | +1608 | +1609 | +1610 | +1611 | +1612 | +1613 | +1614 | +1615 | +1616 | +1617 | +1618 | +1619 | +1620 | +1621 | +1622 | +1623 | +1624 | +1625 | +1626 | +1627 | +1628 | +1629 | +1630 | +1631 | +1632 | +1633 | +1634 | +1635 | +1636 | +1637 | +1638 | +1639 | +1640 | +1641 | +1642 | +1643 | +1644 | +1645 | +1646 | +1647 | +1648 | +1649 | +1650 | +1651 | +1652 | +1653 | +1654 | +1655 | +1656 | +1657 | +1658 | +1659 | +1660 | +1661 | +1662 | +1663 | +1664 | +1665 | +1666 | +1667 | +1668 | +1669 | +1670 | +1671 | +1672 | +1673 | +1674 | +1675 | +1676 | +1677 | +1678 | +1679 | +1680 | +1681 | +1682 | +1683 | +1684 | +1685 | +1686 | +1687 | +1688 | +1689 | +1690 | +1691 | +1692 | +1693 | +1694 | +1695 | +1696 | +1697 | +1698 | +1699 | +1700 | +1701 | +1702 | +1703 | +1704 | +1705 | +1706 | +1707 | +1708 | +1709 | +1710 | +1711 | +1712 | +1713 | +1714 | +1715 | +1716 | +1717 | +1718 | +1719 | +1720 | +1721 | +1722 | +1723 | +1724 | +1725 | +1726 | +1727 | +1728 | +1729 | +1730 | +1731 | +1732 | +1733 | +1734 | +1735 | +1736 | +1737 | +1738 | +1739 | +1740 | +1741 | +1742 | +1743 | +1744 | +1745 | +1746 | +1747 | +1748 | +1749 | +1750 | +1751 | +1752 | +1753 | +1754 | +1755 | +1756 | +1757 | +1758 | +1759 | +1760 | +1761 | +1762 | +1763 | +1764 | +1765 | +1766 | +1767 | +1768 | +1769 | +1770 | +1771 | +1772 | +1773 | +1774 | +1775 | +1776 | +1777 | +1778 | +1779 | +1780 | +1781 | +1782 | +1783 | +1784 | +1785 | +1786 | +1787 | +1788 | +1789 | +1790 | +1791 | +1792 | +1793 | +1794 | +1795 | +1796 | +1797 | +1798 | +1799 | +1800 | +1801 | +1802 | +1803 | +1804 | +1805 | +1806 | +1807 | +1808 | +1809 | +1810 | +1811 | +1812 | +1813 | +1814 | +1815 | +1816 | +1817 | +1818 | +1819 | +1820 | +1821 | +1822 | +1823 | +1824 | +1825 | +1826 | +1827 | +1828 | +1829 | +1830 | +1831 | +1832 | +1833 | +1834 | +1835 | +1836 | +1837 | +1838 | +1839 | +1840 | +1841 | +1842 | +1843 | +1844 | +1845 | +1846 | +1847 | +1848 | +1849 | +1850 | +1851 | +1852 | +1853 | +1854 | +1855 | +1856 | +1857 | +1858 | +1859 | +1860 | +1861 | +1862 | +1863 | +1864 | +1865 | +1866 | +1867 | +1868 | +1869 | +1870 | +1871 | +1872 | +1873 | +1874 | +1875 | +1876 | +1877 | +1878 | +1879 | +1880 | +1881 | +1882 | +1883 | +1884 | +1885 | +1886 | +1887 | +1888 | +1889 | +1890 | +1891 | +1892 | +1893 | +1894 | +1895 | +1896 | +1897 | +1898 | +1899 | +1900 | +1901 | +1902 | +1903 | +1904 | +1905 | +1906 | +1907 | +1908 | +1909 | +1910 | +1911 | +1912 | +1913 | +1914 | +1915 | +1916 | +1917 | +1918 | +1919 | +1920 | +1921 | +1922 | +1923 | +1924 | +1925 | +1926 | +1927 | +1928 | +1929 | +1930 | +1931 | +1932 | +1933 | +1934 | +1935 | +1936 | +1937 | +1938 | +1939 | +1940 | +1941 | +1942 | +1943 | +1944 | +1945 | +1946 | +1947 | +1948 | +1949 | +1950 | +1951 | +1952 | +1953 | +1954 | +1955 | +1956 | +1957 | +1958 | +1959 | +1960 | +1961 | +1962 | +1963 | +1964 | +1965 | +1966 | +1967 | +1968 | +1969 | +1970 | +1971 | +1972 | +1973 | +1974 | +1975 | +1976 | +1977 | +1978 | +1979 | +1980 | +1981 | +1982 | +1983 | +1984 | +1985 | +1986 | +1987 | +1988 | +1989 | +1990 | +1991 | +1992 | +1993 | +1994 | +1995 | +1996 | +1997 | +1998 | +1999 | +2000 | +2001 | +2002 | +2003 | +2004 | +2005 | +2006 | +2007 | +2008 | +2009 | +2010 | +2011 | +2012 | +2013 | +2014 | +2015 | +2016 | +2017 | +2018 | +2019 | +2020 | +2021 | +2022 | +2023 | +2024 | +2025 | +2026 | +2027 | +2028 | +2029 | +2030 | +2031 | +2032 | +2033 | +2034 | +2035 | +2036 | +2037 | +2038 | +2039 | +2040 | +2041 | +2042 | +2043 | +2044 | +2045 | +2046 | +2047 | +2048 | +2049 | +2050 | +2051 | +2052 | +2053 | +2054 | +2055 | +2056 | +2057 | +2058 | +2059 | +2060 | +2061 | +2062 | +2063 | +2064 | +2065 | +2066 | +2067 | +2068 | +2069 | +2070 | +2071 | +2072 | +2073 | +2074 | +2075 | +2076 | +2077 | +2078 | +2079 | +2080 | +2081 | +2082 | +2083 | +2084 | +2085 | +2086 | +2087 | +2088 | +2089 | +2090 | +2091 | +2092 | +2093 | +2094 | +2095 | +2096 | +2097 | +2098 | +2099 | +2100 | +2101 | +2102 | +2103 | +2104 | +2105 | +2106 | +2107 | +2108 | +2109 | +2110 | +2111 | +2112 | +2113 | +2114 | +2115 | +2116 | +2117 | +2118 | +2119 | +2120 | +2121 | +2122 | +2123 | +2124 | +2125 | +2126 | +2127 | +2128 | +2129 | +2130 | +2131 | +2132 | +2133 | +2134 | +2135 | +2136 | +2137 | +2138 | +2139 | +2140 | +2141 | +2142 | +2143 | +2144 | +2145 | +2146 | +2147 | +2148 | +2149 | +2150 | +2151 | +2152 | +2153 | +2154 | +2155 | +2156 | +2157 | +2158 | +2159 | +2160 | +2161 | +2162 | +2163 | +2164 | +2165 | +2166 | +2167 | +2168 | +2169 | +2170 | +2171 | +2172 | +2173 | +2174 | +2175 | +2176 | +2177 | +2178 | +2179 | +2180 | +2181 | +2182 | +2183 | +2184 | +2185 | +2186 | +2187 | +2188 | +2189 | +2190 | +2191 | +2192 | +2193 | +2194 | +2195 | +2196 | +2197 | +2198 | +2199 | +2200 | +2201 | +2202 | +2203 | +2204 | +2205 | +2206 | +2207 | +2208 | +2209 | +2210 | +2211 | +2212 | +2213 | +2214 | +2215 | +2216 | +2217 | +2218 | +2219 | +2220 | +2221 | +2222 | +2223 | +2224 | +2225 | +2226 | +2227 | +2228 | +2229 | +2230 | +2231 | +2232 | +2233 | +2234 | +2235 | +2236 | +2237 | +2238 | +2239 | +2240 | +2241 | +2242 | +2243 | +2244 | +2245 | +2246 | +2247 | +2248 | +2249 | +2250 | +2251 | +2252 | +2253 | +2254 | +2255 | +2256 | +2257 | +2258 | +2259 | +2260 | +2261 | +2262 | +2263 | +2264 | +2265 | +2266 | +2267 | +2268 | +2269 | +2270 | +2271 | +2272 | +2273 | +2274 | +2275 | +2276 | +2277 | +2278 | +2279 | +2280 | +2281 | +2282 | +2283 | +2284 | +2285 | +2286 | +2287 | +2288 | +2289 | +2290 | +2291 | +2292 | +2293 | +2294 | +2295 | +2296 | +2297 | +2298 | +2299 | +2300 | +2301 | +2302 | +2303 | +2304 | +2305 | +2306 | +2307 | +2308 | +2309 | +2310 | +2311 | +2312 | +2313 | +2314 | +2315 | +2316 | +2317 | +2318 | +2319 | +2320 | +2321 | +2322 | +2323 | +2324 | +2325 | +2326 | +2327 | +2328 | +2329 | +2330 | +2331 | +2332 | +2333 | +2334 | +2335 | +2336 | +2337 | +2338 | +2339 | +2340 | +2341 | +2342 | +2343 | +2344 | +2345 | +2346 | +2347 | +2348 | +2349 | +2350 | +2351 | +2352 | +2353 | +2354 | +2355 | +2356 | +2357 | +2358 | +2359 | +2360 | +2361 | +2362 | +2363 | +2364 | +2365 | +2366 | +2367 | +2368 | +2369 | +2370 | +2371 | +2372 | +2373 | +2374 | +2375 | +2376 | +2377 | +2378 | +2379 | +2380 | +2381 | +2382 | +2383 | +2384 | +2385 | +2386 | +2387 | +2388 | +2389 | +2390 | +2391 | +2392 | +2393 | +2394 | +2395 | +2396 | +2397 | +2398 | +2399 | +2400 | +2401 | +2402 | +2403 | +2404 | +2405 | +2406 | +2407 | +2408 | +2409 | +2410 | +2411 | +2412 | +2413 | +2414 | +2415 | +2416 | +2417 | +2418 | +2419 | +2420 | +2421 | +2422 | +2423 | +2424 | +2425 | +2426 | +2427 | +2428 | +2429 | +2430 | +2431 | +2432 | +2433 | +2434 | +2435 | +2436 | +2437 | +2438 | +2439 | +2440 | +2441 | +2442 | +2443 | +2444 | +2445 | +2446 | +2447 | +2448 | +2449 | +2450 | +2451 | +2452 | +2453 | +2454 | +2455 | +2456 | +2457 | +2458 | +2459 | +2460 | +2461 | +2462 | +2463 | +2464 | +2465 | +2466 | +2467 | +2468 | +2469 | +2470 | +2471 | +2472 | +2473 | +2474 | +2475 | +2476 | +2477 | +2478 | +2479 | +2480 | +2481 | +2482 | +2483 | +2484 | +2485 | +2486 | +2487 | +2488 | +2489 | +2490 | +2491 | +2492 | +2493 | +2494 | +2495 | +2496 | +2497 | +2498 | +2499 | +2500 | +2501 | +2502 | +2503 | +2504 | +2505 | +2506 | +2507 | +2508 | +2509 | +2510 | +2511 | +2512 | +2513 | +2514 | +2515 | +2516 | +2517 | +2518 | +2519 | +2520 | +2521 | +2522 | +2523 | +2524 | +2525 | +2526 | +2527 | +2528 | +2529 | +2530 | +2531 | +2532 | +2533 | +2534 | +2535 | +2536 | +2537 | +2538 | +2539 | +2540 | +2541 | +2542 | +2543 | +2544 | +2545 | +2546 | +2547 | +2548 | +2549 | +2550 | +2551 | +2552 | +2553 | +2554 | +2555 | +2556 | +2557 | +2558 | +2559 | +2560 | +2561 | +2562 | +2563 | +2564 | +2565 | +2566 | +2567 | +2568 | +2569 | +2570 | +2571 | +2572 | +2573 | +2574 | +2575 | +2576 | +2577 | +2578 | +2579 | +2580 | +2581 | +2582 | +2583 | +2584 | +2585 | +2586 | +2587 | +2588 | +2589 | +2590 | +2591 | +2592 | +2593 | +2594 | +2595 | +2596 | +2597 | +2598 | +2599 | +2600 | +2601 | +2602 | +2603 | +2604 | +2605 | +2606 | +2607 | +2608 | +2609 | +2610 | +2611 | +2612 | +2613 | +2614 | +2615 | +2616 | +2617 | +2618 | +2619 | +2620 | +2621 | +2622 | +2623 | +2624 | +2625 | +2626 | +2627 | +2628 | +2629 | +2630 | +2631 | +2632 | +2633 | +2634 | +2635 | +2636 | +2637 | +2638 | +2639 | +2640 | +2641 | +2642 | +2643 | +2644 | +2645 | +2646 | +2647 | +2648 | +2649 | +2650 | +2651 | +2652 | +2653 | +2654 | +2655 | +2656 | +2657 | +2658 | +2659 | +2660 | +2661 | +2662 | +2663 | +2664 | +2665 | +2666 | +2667 | +2668 | +2669 | +2670 | +2671 | +2672 | +2673 | +2674 | +2675 | +2676 | +2677 | +2678 | +2679 | +2680 | +2681 | +2682 | +2683 | +2684 | +2685 | +2686 | +2687 | +2688 | +2689 | +2690 | +2691 | +2692 | +2693 | +2694 | +2695 | +2696 | +2697 | +2698 | +2699 | +2700 | +2701 | +2702 | +2703 | +2704 | +2705 | +2706 | +2707 | +2708 | +2709 | +2710 | +2711 | +2712 | +2713 | +2714 | +2715 | +2716 | +2717 | +2718 | +2719 | +2720 | +2721 | +2722 | +2723 | +2724 | +2725 | +2726 | +2727 | +2728 | +2729 | +2730 | +2731 | +2732 | +2733 | +2734 | +2735 | +2736 | +2737 | +2738 | +2739 | +2740 | +2741 | +2742 | +2743 | +2744 | +2745 | +2746 | +2747 | +2748 | +2749 | +2750 | +2751 | +2752 | +2753 | +2754 | +2755 | +2756 | +2757 | +2758 | +2759 | +2760 | +2761 | +2762 | +2763 | +2764 | +2765 | +2766 | +2767 | +2768 | +2769 | +2770 | +2771 | +2772 | +2773 | +2774 | +2775 | +2776 | +2777 | +2778 | +2779 | +2780 | +2781 | +2782 | +2783 | +2784 | +2785 | +2786 | +2787 | +2788 | +2789 | +2790 | +2791 | +2792 | +2793 | +2794 | +2795 | +2796 | +2797 | +2798 | +2799 | +2800 | +2801 | +2802 | +2803 | +2804 | +2805 | +2806 | +2807 | +2808 | +2809 | +2810 | +2811 | +2812 | +2813 | +2814 | +2815 | +2816 | +2817 | +2818 | +2819 | +2820 | +2821 | +2822 | +2823 | +2824 | +2825 | +2826 | +2827 | +2828 | +2829 | +2830 | +2831 | +2832 | +2833 | +2834 | +2835 | +2836 | +2837 | +2838 | +2839 | +2840 | +2841 | +2842 | +2843 | +2844 | +2845 | +2846 | +2847 | +2848 | +2849 | +2850 | +2851 | +2852 | +2853 | +2854 | +2855 | +2856 | +2857 | +2858 | +2859 | +2860 | +2861 | +2862 | +2863 | +2864 | +2865 | +2866 | +2867 | +2868 | +2869 | +2870 | +2871 | +2872 | +2873 | +2874 | +2875 | +2876 | +2877 | +2878 | +2879 | +2880 | +2881 | +2882 | +2883 | +2884 | +2885 | +2886 | +2887 | +2888 | +2889 | +2890 | +2891 | +2892 | +2893 | +2894 | +2895 | +2896 | +2897 | +2898 | +2899 | +2900 | +2901 | +2902 | +2903 | +2904 | +2905 | +2906 | +2907 | +2908 | +2909 | +2910 | +2911 | +2912 | +2913 | +2914 | +2915 | +2916 | +2917 | +2918 | +2919 | +2920 | +2921 | +2922 | +2923 | +2924 | +2925 | +2926 | +2927 | +2928 | +2929 | +2930 | +2931 | +2932 | +2933 | +2934 | +2935 | +2936 | +2937 | +2938 | +2939 | +2940 | +2941 | +2942 | +2943 | +2944 | +2945 | +2946 | +2947 | +2948 | +2949 | +2950 | +2951 | +2952 | +2953 | +2954 | +2955 | +2956 | +2957 | +2958 | +2959 | +2960 | +2961 | +2962 | +2963 | +2964 | +2965 | +2966 | +2967 | +2968 | +2969 | +2970 | +2971 | +2972 | +2973 | +2974 | +2975 | +2976 | +2977 | +2978 | +2979 | +2980 | +2981 | +2982 | +2983 | +2984 | +2985 | +2986 | +2987 | +2988 | +2989 | +2990 | +2991 | +2992 | +2993 | +2994 | +2995 | +2996 | +2997 | +2998 | +2999 | +3000 | +3001 | +3002 | +3003 | +3004 | +3005 | +3006 | +3007 | +3008 | +3009 | +3010 | +3011 | +3012 | +3013 | +3014 | +3015 | +3016 | +3017 | +3018 | +3019 | +3020 | +3021 | +3022 | +3023 | +3024 | +3025 | +3026 | +3027 | +3028 | +3029 | +3030 | +3031 | +3032 | +3033 | +3034 | +3035 | +3036 | +3037 | +3038 | +3039 | +3040 | +3041 | +3042 | +3043 | +3044 | +3045 | +3046 | +3047 | +3048 | +3049 | +3050 | +3051 | +3052 | +3053 | +3054 | +3055 | +3056 | +3057 | +3058 | +3059 | +3060 | +3061 | +3062 | +3063 | +3064 | +3065 | +3066 | +3067 | +3068 | +3069 | +3070 | +3071 | +3072 | +3073 | +3074 | +3075 | +3076 | +3077 | +3078 | +3079 | +3080 | +3081 | +3082 | +3083 | +3084 | +3085 | +3086 | +3087 | +3088 | +3089 | +3090 | +3091 | +3092 | +3093 | +3094 | +3095 | +3096 | +3097 | +3098 | +3099 | +3100 | +3101 | +3102 | +3103 | +3104 | +3105 | +3106 | +3107 | +3108 | +3109 | +3110 | +3111 | +3112 | +3113 | +3114 | +3115 | +3116 | +3117 | +3118 | +3119 | +3120 | +3121 | +3122 | +3123 | +3124 | +3125 | +3126 | +3127 | +3128 | +3129 | +3130 | +3131 | +3132 | +3133 | +3134 | +3135 | +3136 | +3137 | +3138 | +3139 | +3140 | +3141 | +3142 | +3143 | +3144 | +3145 | +3146 | +3147 | +3148 | +3149 | +3150 | +3151 | +3152 | +3153 | +3154 | +3155 | +3156 | +3157 | +3158 | +3159 | +3160 | +3161 | +3162 | +3163 | +3164 | +3165 | +3166 | +3167 | +3168 | +3169 | +3170 | +3171 | +3172 | +3173 | +3174 | +3175 | +3176 | +3177 | +3178 | +3179 | +3180 | +3181 | +3182 | +3183 | +3184 | +3185 | +3186 | +3187 | +3188 | +3189 | +3190 | +3191 | +3192 | +3193 | +3194 | +3195 | +3196 | +3197 | +3198 | +3199 | +3200 | +3201 | +3202 | +3203 | +3204 | +3205 | +3206 | +3207 | +3208 | +3209 | +3210 | +3211 | +3212 | +3213 | +3214 | +3215 | +3216 | +3217 | +3218 | +3219 | +3220 | +3221 | +3222 | +3223 | +3224 | +3225 | +3226 | +3227 | +3228 | +3229 | +3230 | +3231 | +3232 | +3233 | +3234 | +3235 | +3236 | +3237 | +3238 | +3239 | +3240 | +3241 | +3242 | +3243 | +3244 | +3245 | +3246 | +3247 | +3248 | +3249 | +3250 | +3251 | +3252 | +3253 | +3254 | +3255 | +3256 | +3257 | +3258 | +3259 | +3260 | +3261 | +3262 | +3263 | +3264 | +3265 | +3266 | +3267 | +3268 | +3269 | +3270 | +3271 | +3272 | +3273 | +3274 | +3275 | +3276 | +3277 | +3278 | +3279 | +3280 | +3281 | +3282 | +3283 | +3284 | +3285 | +3286 | +3287 | +3288 | +3289 | +3290 | +3291 | +3292 | +3293 | +3294 | +3295 | +3296 | +3297 | +3298 | +3299 | +3300 | +3301 | +3302 | +3303 | +3304 | +3305 | +3306 | +3307 | +3308 | +3309 | +3310 | +3311 | +3312 | +3313 | +3314 | +3315 | +3316 | +3317 | +3318 | +3319 | +3320 | +3321 | +3322 | +3323 | +3324 | +3325 | +3326 | +3327 | +3328 | +3329 | +3330 | +3331 | +3332 | +3333 | +3334 | +3335 | +3336 | +3337 | +3338 | +3339 | +3340 | +3341 | +3342 | +3343 | +3344 | +3345 | +3346 | +3347 | +3348 | +3349 | +3350 | +3351 | +3352 | +3353 | +3354 | +3355 | +3356 | +3357 | +3358 | +3359 | +3360 | +3361 | +3362 | +3363 | +3364 | +3365 | +3366 | +3367 | +3368 | +3369 | +3370 | +3371 | +3372 | +3373 | +3374 | +3375 | +3376 | +3377 | +3378 | +3379 | +3380 | +3381 | +3382 | +3383 | +3384 | +3385 | +3386 | +3387 | +3388 | +3389 | +3390 | +3391 | +3392 | +3393 | +3394 | +3395 | +3396 | +3397 | +3398 | +3399 | +3400 | +3401 | +3402 | +3403 | +3404 | +3405 | +3406 | +3407 | +3408 | +3409 | +3410 | +3411 | +3412 | +3413 | +3414 | +3415 | +3416 | +3417 | +3418 | +3419 | +3420 | +3421 | +3422 | +3423 | +3424 | +3425 | +3426 | +3427 | +3428 | +3429 | +3430 | +3431 | +3432 | +3433 | +3434 | +3435 | +3436 | +3437 | +3438 | +3439 | +3440 | +3441 | +3442 | +3443 | +3444 | +3445 | +3446 | +3447 | +3448 | +3449 | +3450 | +3451 | +3452 | +3453 | +3454 | +3455 | +3456 | +3457 | +3458 | +3459 | +3460 | +3461 | +3462 | +3463 | +3464 | +3465 | +3466 | +3467 | +3468 | +3469 | +3470 | +3471 | +3472 | +3473 | +3474 | +3475 | +3476 | +3477 | +3478 | +3479 | +3480 | +3481 | +3482 | +3483 | +3484 | +3485 | +3486 | +3487 | +3488 | +3489 | +3490 | +3491 | +3492 | +3493 | +3494 | +3495 | +3496 | +3497 | +3498 | +3499 | +3500 | +3501 | +3502 | +3503 | +3504 | +3505 | +3506 | +3507 | +3508 | +3509 | +3510 | +3511 | +3512 | +3513 | +3514 | +3515 | +3516 | +3517 | +3518 | +3519 | +3520 | +3521 | +3522 | +3523 | +3524 | +3525 | +3526 | +3527 | +3528 | +3529 | +3530 | +3531 | +3532 | +3533 | +3534 | +3535 | +3536 | +3537 | +3538 | +3539 | +3540 | +3541 | +3542 | +3543 | +3544 | +3545 | +3546 | +3547 | +3548 | +3549 | +3550 | +3551 | +3552 | +3553 | +3554 | +3555 | +3556 | +3557 | +3558 | +3559 | +3560 | +3561 | +3562 | +3563 | +3564 | +3565 | +3566 | +3567 | +3568 | +3569 | +3570 | +3571 | +3572 | +3573 | +3574 | +3575 | +3576 | +3577 | +3578 | +3579 | +3580 | +3581 | +3582 | +3583 | +3584 | +3585 | +3586 | +3587 | +3588 | +3589 | +3590 | +3591 | +3592 | +3593 | +3594 | +3595 | +3596 | +3597 | +3598 | +3599 | +3600 | +3601 | +3602 | +3603 | +3604 | +3605 | +3606 | +3607 | +3608 | +3609 | +3610 | +3611 | +3612 | +3613 | +3614 | +3615 | +3616 | +3617 | +3618 | +3619 | +3620 | +3621 | +3622 | +3623 | +3624 | +3625 | +3626 | +3627 | +3628 | +3629 | +3630 | +3631 | +3632 | +3633 | +3634 | +3635 | +3636 | +3637 | +3638 | +3639 | +3640 | +3641 | +3642 | +3643 | +3644 | +3645 | +3646 | +3647 | +3648 | +3649 | +3650 | +3651 | +3652 | +3653 | +3654 | +3655 | +3656 | +3657 | +3658 | +3659 | +3660 | +3661 | +3662 | +3663 | +3664 | +3665 | +3666 | +3667 | +3668 | +3669 | +3670 | +3671 | +3672 | +3673 | +3674 | +3675 | +3676 | +3677 | +3678 | +3679 | +3680 | +3681 | +3682 | +3683 | +3684 | +3685 | +3686 | +3687 | +3688 | +3689 | +3690 | +3691 | +3692 | +3693 | +3694 | +3695 | +3696 | +3697 | +3698 | +3699 | +3700 | +3701 | +3702 | +3703 | +3704 | +3705 | +3706 | +3707 | +3708 | +3709 | +3710 | +3711 | +3712 | +3713 | +3714 | +3715 | +3716 | +3717 | +3718 | +3719 | +3720 | +3721 | +3722 | +3723 | +3724 | +3725 | +3726 | +3727 | +3728 | +3729 | +3730 | +3731 | +3732 | +3733 | +3734 | +3735 | +3736 | +3737 | +3738 | +3739 | +3740 | +3741 | +3742 | +3743 | +3744 | +3745 | +3746 | +3747 | +3748 | +3749 | +3750 | +3751 | +3752 | +3753 | +3754 | +3755 | +3756 | +3757 | +3758 | +3759 | +3760 | +3761 | +3762 | +3763 | +3764 | +3765 | +3766 | +3767 | +3768 | +3769 | +3770 | +3771 | +3772 | +3773 | +3774 | +3775 | +3776 | +3777 | +3778 | +3779 | +3780 | +3781 | +3782 | +3783 | +3784 | +3785 | +3786 | +3787 | +3788 | +3789 | +3790 | +3791 | +3792 | +3793 | +3794 | +3795 | +3796 | +3797 | +3798 | +3799 | +3800 | +3801 | +3802 | +3803 | +3804 | +3805 | +3806 | +3807 | +3808 | +3809 | +3810 | +3811 | +3812 | +3813 | +3814 | +3815 | +3816 | +3817 | +3818 | +3819 | +3820 | +3821 | +3822 | +3823 | +3824 | +3825 | +3826 | +3827 | +3828 | +3829 | +3830 | +3831 | +3832 | +3833 | +3834 | +3835 | +3836 | +3837 | +3838 | +3839 | +3840 | +3841 | +3842 | +3843 | +3844 | +3845 | +3846 | +3847 | +3848 | +3849 | +3850 | +3851 | +3852 | +3853 | +3854 | +3855 | +3856 | +3857 | +3858 | +3859 | +3860 | +3861 | +3862 | +3863 | +3864 | +3865 | +3866 | +3867 | +3868 | +3869 | +3870 | +3871 | +3872 | +3873 | +3874 | +3875 | +3876 | +3877 | +3878 | +3879 | +3880 | +3881 | +3882 | +3883 | +3884 | +3885 | +3886 | +3887 | +3888 | +3889 | +3890 | +3891 | +3892 | +3893 | +3894 | +3895 | +3896 | +3897 | +3898 | +3899 | +3900 | +3901 | +3902 | +3903 | +3904 | +3905 | +3906 | +3907 | +3908 | +3909 | +3910 | +3911 | +3912 | +3913 | +3914 | +3915 | +3916 | +3917 | +3918 | +3919 | +3920 | +3921 | +3922 | +3923 | +3924 | +3925 | +3926 | +3927 | +3928 | +3929 | +3930 | +3931 | +3932 | +3933 | +3934 | +3935 | +3936 | +3937 | +3938 | +3939 | +3940 | +3941 | +3942 | +3943 | +3944 | +3945 | +3946 | +3947 | +3948 | +3949 | +3950 | +3951 | +3952 | +3953 | +3954 | +3955 | +3956 | +3957 | +3958 | +3959 | +3960 | +3961 | +3962 | +3963 | +3964 | +3965 | +3966 | +3967 | +3968 | +3969 | +3970 | +3971 | +3972 | +3973 | +3974 | +3975 | +3976 | +3977 | +3978 | +3979 | +3980 | +3981 | +3982 | +3983 | +3984 | +3985 | +3986 | +3987 | +3988 | +3989 | +3990 | +3991 | +3992 | +3993 | +3994 | +3995 | +3996 | +3997 | +3998 | +3999 | +4000 | +4001 | +4002 | +4003 | +4004 | +4005 | +4006 | +4007 | +4008 | +4009 | +4010 | +4011 | +4012 | +4013 | +4014 | +4015 | +4016 | +4017 | +4018 | +4019 | +4020 | +4021 | +4022 | +4023 | +4024 | +4025 | +4026 | +4027 | +4028 | +4029 | +4030 | +4031 | +4032 | +4033 | +4034 | +4035 | +4036 | +4037 | +4038 | +4039 | +4040 | +4041 | +4042 | +4043 | +4044 | +4045 | +4046 | +4047 | +4048 | +4049 | +4050 | +4051 | +4052 | +4053 | +4054 | +4055 | +4056 | +4057 | +4058 | +4059 | +4060 | +4061 | +4062 | +4063 | +4064 | +4065 | +4066 | +4067 | +4068 | +4069 | +4070 | +4071 | +4072 | +4073 | +4074 | +4075 | +4076 | +4077 | +4078 | +4079 | +4080 | +4081 | +4082 | +4083 | +4084 | +4085 | +4086 | +4087 | +4088 | +4089 | +4090 | +4091 | +4092 | +4093 | +4094 | +4095 | +4096 | +4097 | +4098 | +4099 | +4100 | +4101 | +4102 | +4103 | +4104 | +4105 | +4106 | +4107 | +4108 | +4109 | +4110 | +4111 | +4112 | +4113 | +4114 | +4115 | +4116 | +4117 | +4118 | +4119 | +4120 | +4121 | +4122 | +4123 | +4124 | +4125 | +4126 | +4127 | +4128 | +4129 | +4130 | +4131 | +4132 | +4133 | +4134 | +4135 | +4136 | +4137 | +4138 | +4139 | +4140 | +4141 | +4142 | +4143 | +4144 | +4145 | +4146 | +4147 | +4148 | +4149 | +4150 | +4151 | +4152 | +4153 | +4154 | +4155 | +4156 | +4157 | +4158 | +4159 | +4160 | +4161 | +4162 | +4163 | +4164 | +4165 | +4166 | +4167 | +4168 | +4169 | +4170 | +4171 | +4172 | +4173 | +4174 | +4175 | +4176 | +4177 | +4178 | +4179 | +4180 | +4181 | +4182 | +4183 | +4184 | +4185 | +4186 | +4187 | +4188 | +4189 | +4190 | +4191 | +4192 | +4193 | +4194 | +4195 | +4196 | +4197 | +4198 | +4199 | +4200 | +4201 | +4202 | +4203 | +4204 | +4205 | +4206 | +4207 | +4208 | +4209 | +4210 | +4211 | +4212 | +4213 | +4214 | +4215 | +4216 | +4217 | +4218 | +4219 | +4220 | +4221 | +4222 | +4223 | +4224 | +4225 | +4226 | +4227 | +4228 | +4229 | +4230 | +4231 | +4232 | +4233 | +4234 | +4235 | +4236 | +4237 | +4238 | +4239 | +4240 | +4241 | +4242 | +4243 | +4244 | +4245 | +4246 | +4247 | +4248 | +4249 | +4250 | +4251 | +4252 | +4253 | +4254 | +4255 | +4256 | +4257 | +4258 | +4259 | +4260 | +4261 | +4262 | +4263 | +4264 | +4265 | +4266 | +4267 | +4268 | +4269 | +4270 | +4271 | +4272 | +4273 | +4274 | +4275 | +4276 | +4277 | +4278 | +4279 | +4280 | +4281 | +4282 | +4283 | +4284 | +4285 | +4286 | +4287 | +4288 | +4289 | +4290 | +4291 | +4292 | +4293 | +4294 | +4295 | +4296 | +4297 | +4298 | +4299 | +4300 | +4301 | +4302 | +4303 | +4304 | +4305 | +4306 | +4307 | +4308 | +4309 | +4310 | +4311 | +4312 | +4313 | +4314 | +4315 | +4316 | +4317 | +4318 | +4319 | +4320 | +4321 | +4322 | +4323 | +4324 | +4325 | +4326 | +4327 | +4328 | +4329 | +4330 | +4331 | +4332 | +4333 | +4334 | +4335 | +4336 | +4337 | +4338 | +4339 | +4340 | +4341 | +4342 | +4343 | +4344 | +4345 | +4346 | +4347 | +4348 | +4349 | +4350 | +4351 | +4352 | +4353 | +4354 | +4355 | +4356 | +4357 | +4358 | +4359 | +4360 | +4361 | +4362 | +4363 | +4364 | +4365 | +4366 | +4367 | +4368 | +4369 | +4370 | +4371 | +4372 | +4373 | +4374 | +4375 | +4376 | +4377 | +4378 | +4379 | +4380 | +4381 | +4382 | +4383 | +4384 | +4385 | +4386 | +4387 | +4388 | +4389 | +4390 | +4391 | +4392 | +4393 | +4394 | +4395 | +4396 | +4397 | +4398 | +4399 | +4400 | +4401 | +4402 | +4403 | +4404 | +4405 | +4406 | +4407 | +4408 | +4409 | +4410 | +4411 | +4412 | +4413 | +4414 | +4415 | +4416 | +4417 | +4418 | +4419 | +4420 | +4421 | +4422 | +4423 | +4424 | +4425 | +4426 | +4427 | +4428 | +4429 | +4430 | +4431 | +4432 | +4433 | +4434 | +4435 | +4436 | +4437 | +4438 | +4439 | +4440 | +4441 | +4442 | +4443 | +4444 | +4445 | +4446 | +4447 | +4448 | +4449 | +4450 | +4451 | +4452 | +4453 | +4454 | +4455 | +4456 | +4457 | +4458 | +4459 | +4460 | +4461 | +4462 | +4463 | +4464 | +4465 | +4466 | +4467 | +4468 | +4469 | +4470 | +4471 | +4472 | +4473 | +4474 | +4475 | +4476 | +4477 | +4478 | +4479 | +4480 | +4481 | +4482 | +4483 | +4484 | +4485 | +4486 | +4487 | +4488 | +4489 | +4490 | +4491 | +4492 | +4493 | +4494 | +4495 | +4496 | +4497 | +4498 | +4499 | +4500 | +4501 | +4502 | +4503 | +4504 | +4505 | +4506 | +4507 | +4508 | +4509 | +4510 | +4511 | +4512 | +4513 | +4514 | +4515 | +4516 | +4517 | +4518 | +4519 | +4520 | +4521 | +4522 | +4523 | +4524 | +4525 | +4526 | +4527 | +4528 | +4529 | +4530 | +4531 | +4532 | +4533 | +4534 | +4535 | +4536 | +4537 | +4538 | +4539 | +4540 | +4541 | +4542 | +4543 | +4544 | +4545 | +4546 | +4547 | +4548 | +4549 | +4550 | +4551 | +4552 | +4553 | +4554 | +4555 | +4556 | +4557 | +4558 | +4559 | +4560 | +4561 | +4562 | +4563 | +4564 | +4565 | +4566 | +4567 | +4568 | +4569 | +4570 | +4571 | +4572 | +4573 | +4574 | +4575 | +4576 | +4577 | +4578 | +4579 | +4580 | +4581 | +4582 | +4583 | +4584 | +4585 | +4586 | +4587 | +4588 | +4589 | +4590 | +4591 | +4592 | +4593 | +4594 | +4595 | +4596 | +4597 | +4598 | +4599 | +4600 | +4601 | +4602 | +4603 | +4604 | +4605 | +4606 | +4607 | +4608 | +4609 | +4610 | +4611 | +4612 | +4613 | +4614 | +4615 | +4616 | +4617 | +4618 | +4619 | +4620 | +4621 | +4622 | +4623 | +4624 | +4625 | +4626 | +4627 | +4628 | +4629 | +4630 | +4631 | +4632 | +4633 | +4634 | +4635 | +4636 | +4637 | +4638 | +4639 | +4640 | +4641 | +4642 | +4643 | +4644 | +4645 | +4646 | +4647 | +4648 | +4649 | +4650 | +4651 | +4652 | +4653 | +4654 | +4655 | +4656 | +4657 | +4658 | +4659 | +4660 | +4661 | +4662 | +4663 | +4664 | +4665 | +4666 | +4667 | +4668 | +4669 | +4670 | +4671 | +4672 | +4673 | +4674 | +4675 | +4676 | +4677 | +4678 | +4679 | +4680 | +4681 | +4682 | +4683 | +4684 | +4685 | +4686 | +4687 | +4688 | +4689 | +4690 | +4691 | +4692 | +4693 | +4694 | +4695 | +4696 | +4697 | +4698 | +4699 | +4700 | +4701 | +4702 | +4703 | +4704 | +4705 | +4706 | +4707 | +4708 | +4709 | +4710 | +4711 | +4712 | +4713 | +4714 | +4715 | +4716 | +4717 | +4718 | +4719 | +4720 | +4721 | +4722 | +4723 | +4724 | +4725 | +4726 | +4727 | +4728 | +4729 | +4730 | +4731 | +4732 | +4733 | +4734 | +4735 | +4736 | +4737 | +4738 | +4739 | +4740 | +4741 | +4742 | +4743 | +4744 | +4745 | +4746 | +4747 | +4748 | +4749 | +4750 | +4751 | +4752 | +4753 | +4754 | +4755 | +4756 | +4757 | +4758 | +4759 | +4760 | +4761 | +4762 | +4763 | +4764 | +4765 | +4766 | +4767 | +4768 | +4769 | +4770 | +4771 | +4772 | +4773 | +4774 | +4775 | +4776 | +4777 | +4778 | +4779 | +4780 | +4781 | +4782 | +4783 | +4784 | +4785 | +4786 | +4787 | +4788 | +4789 | +4790 | +4791 | +4792 | +4793 | +4794 | +4795 | +4796 | +4797 | +4798 | +4799 | +4800 | +4801 | +4802 | +4803 | +4804 | +4805 | +4806 | +4807 | +4808 | +4809 | +4810 | +4811 | +4812 | +4813 | +4814 | +4815 | +4816 | +4817 | +4818 | +4819 | +4820 | +4821 | +4822 | +4823 | +4824 | +4825 | +4826 | +4827 | +4828 | +4829 | +4830 | +4831 | +4832 | +4833 | +4834 | +4835 | +4836 | +4837 | +4838 | +4839 | +4840 | +4841 | +4842 | +4843 | +4844 | +4845 | +4846 | +4847 | +4848 | +4849 | +4850 | +4851 | +4852 | +4853 | +4854 | +4855 | +4856 | +4857 | +4858 | +4859 | +4860 | +4861 | +4862 | +4863 | +4864 | +4865 | +4866 | +4867 | +4868 | +4869 | +4870 | +4871 | +4872 | +4873 | +4874 | +4875 | +4876 | +4877 | +4878 | +4879 | +4880 | +4881 | +4882 | +4883 | +4884 | +4885 | +4886 | +4887 | +4888 | +4889 | +4890 | +4891 | +4892 | +4893 | +4894 | +4895 | +4896 | +4897 | +4898 | +4899 | +4900 | +4901 | +4902 | +4903 | +4904 | +4905 | +4906 | +4907 | +4908 | +4909 | +4910 | +4911 | +4912 | +4913 | +4914 | +4915 | +4916 | +4917 | +4918 | +4919 | +4920 | +4921 | +4922 | +4923 | +4924 | +4925 | +4926 | +4927 | +4928 | +4929 | +4930 | +4931 | +4932 | +4933 | +4934 | +4935 | +4936 | +4937 | +4938 | +4939 | +4940 | +4941 | +4942 | +4943 | +4944 | +4945 | +4946 | +4947 | +4948 | +4949 | +4950 | +4951 | +4952 | +4953 | +4954 | +4955 | +4956 | +4957 | +4958 | +4959 | +4960 | +4961 | +4962 | +4963 | +4964 | +4965 | +4966 | +4967 | +4968 | +4969 | +4970 | +4971 | +4972 | +4973 | +4974 | +4975 | +4976 | +4977 | +4978 | +4979 | +4980 | +4981 | +4982 | +4983 | +4984 | +4985 | +4986 | +4987 | +4988 | +4989 | +4990 | +4991 | +4992 | +4993 | +4994 | +4995 | +4996 | +4997 | +4998 | +4999; + +type Data = { + error?: ErrorCode, +}; diff --git a/tests/union_new/__snapshots__/jsfmt.spec.js.snap b/tests/union_new/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..8c6fb30c46f7 --- /dev/null +++ b/tests/union_new/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,1768 @@ +exports[`test issue-815.js 1`] = ` +"/* @flow */ +type T = A|B; +class U {}; +declare var children: U; +(children: T|U); +class A {}; +class B {}; + +type VirtualElement = Thunk|VirtualNode; +type Child = VirtualElement; +type Children = Array; + + +class Thunk {} +class VirtualNode { + children: Child|Children; + constructor(type, children/*:Children*/) { + this.children = children.length === 1 ? children[0] : + children; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test issue-824.js 1`] = ` +"import { B, C } from \"./issue-824-helper\"; + +type K = B | C; + +type I = { + which(): number; +}; + +export default class A { + static foo(p: K): bool { + return false; + } + static bar(p: I & K): bool { + return this.foo(p); + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test issue-824-helper.js 1`] = ` +"import A from \"./issue-824\"; + +export class B extends A { + which(): number { + return 1; + } +} +export class C extends A { + which(): number { + return 2; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +import A from \"./issue-824\"; +export class B extends A { + which(): number { + return 1; + } +} +export class C extends A { + which(): number { + return 2; + } +} + +" +`; + +exports[`test issue-1349.js 1`] = ` +"/* @flow */ + +var bar: Array<{b: ?boolean, c: number} | {b: boolean}> = [ + {b: true, c: 123}, + {b: true} +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1497:36) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test issue-1371.js 1`] = ` +"function create(a: any): { type: \'B\', data: number } | { type: \'A\', data: string } +{ + return { + type: \'A\', + data: a + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test issue-1455.js 1`] = ` +"/* @flow */ +import type {Foobar} from \"./issue-1455-helper\" + +function create(content: ?Foobar | String | Array) { +} + +function node(content: ?Foobar | String | Array) { + create(content) +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test issue-1455-helper.js 1`] = ` +"/* @flow */ + +export class Foobar { } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +export class Foobar {} + +" +`; + +exports[`test issue-1462-i.js 1`] = ` +"type Common = { +}; + +type A = Common & { + type: \'A\', + foo: number +}; + +type B = Common & { + type: \'B\', + foo: Array +} + +type MyType = A | B; + +function print(x: number) { + console.log(x); +} + +function printAll(val: MyType) { + print(val.foo); // <--- foo could be an array +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1407 + return fromString(\" & \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1407:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test issue-1462-ii.js 1`] = ` +"type Common = { +}; + +type A = { + type: \'A\', + foo: number +} & Common; + +type B = { + type: \'B\', + foo: Array +} & Common; + +type MyType = A | B; + + +function print(x: number) { + console.log(x); +} + +function printAll(val: MyType) { + if (val.type === \'A\') { + print(val.foo); + } else { + val.foo.forEach(print); + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1407 + return fromString(\" & \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1407:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test issue-1664.js 1`] = ` +"/* @flow */ + +type DataBase = { + id: string, + name: string, +}; + +type UserData = DataBase & { + kind: \"user\", +}; + +type SystemData = DataBase & { + kind: \"system\", +} + +type Data = UserData | SystemData; + +const data: Data = { + id: \"\", + name: \"\", + kind: \"system\", +} + +if (data.kind === \"system\") { + (data: SystemData); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1407 + return fromString(\" & \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1407:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test issue-1759.js 1`] = ` +"// @flow + +type X = ({a:true} & {b:string}) | ({a:false} & {c:string}); +//type X = {a:true, b:string} | {a:false, c:string}; // this works. + +function hello(x:X): string { + if (x.a === true) return x.b; else return x.c; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1407 + return fromString(\" & \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1407:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1530:42) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test issue-2232.js 1`] = ` +"/* @flow */ + +declare type Entity = { + id: T, + name: string +} + +declare type StringEntity = Entity + + +declare type Foo = StringEntity & { + bars: Object, + kind: 1 +} +declare type EmptyFoo = StringEntity & { + bars: null, + kind: 2 +} + +function test(f: Foo| EmptyFoo) { + if (f.kind === 1) { + (f: Foo) + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1479:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test1.js 1`] = ` +"// @noflow + +/** + * Test that shows how the implementation of union types is broken + */ + +////////////////////////////// +// example with object types +////////////////////////////// + +function obj(a: A1 | A2) { + return a.x; +} + +const obj_result = obj({ x: \"\" }); // currently an error! (expect it to be OK) + +// Type definitions used above are defined below, but in an order that +// deliberately makes their full resolution as lazy as possible. The call above +// blocks until A1 is partially resolved. Since the argument partially matches +// A1, that branch is selected. Later, that branch errors, but other branches +// have been lost by then. + +type A1 = { x: B1 }; +type A2 = { x: B2 }; + +type B1 = number; +type B2 = string; + +(obj_result: B1 | B2); + +/////////////////////////////////////// +// similar example with function types +/////////////////////////////////////// + +function fun(a: A3 | A4) { + return a(); +} + +const fun_result = fun(() => \"\"); + +type A3 = () => B3; +type A4 = () => B4; + +type B3 = number; +type B4 = string; + +(fun_result: B3 | B4); + +///////////////////////////////////////////// +// similar example with class instance types +///////////////////////////////////////////// + +function inst(a: A5 | A6) { } + +class B5 { } +class B6 { } +inst([new B6]); + +type A5 = B5[]; +type A6 = B6[]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test2.js 1`] = ` +"// @noflow + +/** + * Test that shows how the implementation of union types is broken + */ + +////////////////////////////// +// example with object types +////////////////////////////// + +function obj(a: { x: number } | { x: string }) { } + +obj(({ x: \"\" }: A1)); + +type A1 = { x: B1 }; + +type B1 = string; + +/////////////////////////////////////// +// similar example with function types +/////////////////////////////////////// + +function fun(a: (() => number) | (() => string)) { } + +fun(((() => \"\"): A2)); + +type A2 = () => B2; + +type B2 = string; + +///////////////////////////////////////////////////// +// similar example with generic class instance types +///////////////////////////////////////////////////// + +class C { } + +function inst(a: C | C) { } + +inst((new C: A3)); + +type A3 = C; + +type B3 = string; + +///////////////////////////////////////////// +// similar example with generic type aliases +///////////////////////////////////////////// + +function alias(a: T | T) { } +alias({ x: (x: V) => { } }); + +type T = { x: U } +type U = (x: V) => void; +type V = X; + +type B4 = string; + +// class statics + +function stat(a: { x: number } | { x: string }) { } + +class D { + static x: B5; +} + +stat(D); + +type B5 = string; + +// tuples + +function tup(a: [number,boolean] | [string,boolean]) { } + +tup(([\"\",false]: A6)); + +type A6 = [B6,boolean]; +type B6 = string; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test3.js 1`] = ` +"// @noflow + +/** + * Test that shows how the implementation of union types is broken + */ + +/////////////////////////////// +// example with function types +/////////////////////////////// + +function fun(a: ((x: number) => void) | ((x: string) => void)) { } + +fun((((x) => {}): A1)); + +type A1 = (x: B1) => void; + +type B1 = string; + +//////////////////////////// +// example with array types +//////////////////////////// + +function arr(a: number[] | string[]) { } + +arr(([]: A2)); + +type A2 = B2[]; + +type B2 = string; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1530:42) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test4.js 1`] = ` +"// @noflow + +/** + * Test that shows how the implementation of union types is broken + */ + +/////////////////////////////// +// example with function types +/////////////////////////////// + +function fun(a: ((x: number) => void) | ((x: string) => void)) { } + +const a1 = ((x) => {}: A1); +fun(a1); + +function fun_call(x: string) { a1(x); } + +type A1 = (x: B1) => void; + +type B1 = string; + +//////////////////////////// +// example with array types +//////////////////////////// + +function arr(a: number[] | string[]) { } + +const a2 = ([]: A2); +arr(a2); + +function arr_set(x: string, i: number) { a2[i] = x; } +function arr_get(i: number): string { return a2[i]; } + +type A2 = B2[]; + +type B2 = string; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1530:42) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test5.js 1`] = ` +"// @noflow + +/** + * Test that shows how the implementation of union types is broken + */ + +/////////////////////////////// +// example with function types +/////////////////////////////// + +function fun(a: ((x: number) => number) | ((x: string) => string)) { } + +function a1(x) { return x; } +fun(a1); + +function fun_call(x: string): string { return a1(x); } + +///////////////////////////// +// example with array types +///////////////////////////// + +function arr(a: number[] | string[]) { } + +var a2 = []; +arr(a2); + +function arr_set(x: string, i: number) { a2[i] = x; } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1530:42) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test6.js 1`] = ` +"// @noflow + +/** + * Test that shows how the implementation of union types is broken + */ + +////////////////////////////////////////// +// example with generic class inheritance +////////////////////////////////////////// + +function inst(a: E): C | C { return a; } + +const mk_C = () => C; +const mk_D = () => D; +const mk_E = () => E; + +type B4 = string; + +const _D = mk_D(); +class E extends _D { } + +const _C = mk_C(); +class D extends _C { } + +class C { } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test7.js 1`] = ` +"// @noflow + +/** + * Test that shows how the implementation of union types is broken + */ + +//////////////////// +// recursive types +//////////////////// + +function rec(x: F1 | F2) { } +rec({ x: 0 }); + +type F1 = G1; +type F2 = G2; +type G1 = { x: H1, y?: G1 }; +type G2 = { x: H2, y?: G2 }; +type H1 = string; +type H2 = number; + +/////////////////////////////// +// polymorphic recursive types +/////////////////////////////// + +function polyrec(x: PF | PF) { } +rec({ x: 0 }); + +type PF = PG; +type PG = { x: X, y?: PG }; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test8.js 1`] = ` +"// @noflow + +/** + * Test that shows how the implementation of union types is broken + */ + +////////////////////// +// nested union types +////////////////////// + +function rec(x: F1 | F2) { } +rec({ x: 0 }); + +type F1 = G1 | G1_; +type F2 = G2 | G2_; +type G1 = { x: H1 }; +type G1_ = { x: H1_ }; +type G2 = { x: H2 }; +type G2_ = { x: H2_ }; +type H1 = boolean; +type H1_ = string; +type H2 = boolean; +type H2_ = number; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test9.js 1`] = ` +"// @noflow + +/** + * Test that shows how the implementation of union types is broken + */ + +//////////////// +// interference +//////////////// + +function square(x? = 0) { + return x * x; +} + +function foo(f: ((_: ?number) => ?number) | (() => void)) { } +foo((x): number => square(x)) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1530:42) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test10.js 1`] = ` +"// @noflow + +function id(x: X): X { return x; } + +///////////////////////// +// primitive annotations +///////////////////////// + +function check_prim(_: number | string) { } + +// ok +check_prim(\"\"); + +// ...even when they \"flow\" in +check_prim(id(\"\")); + +////////////////////////////// +// class instance annotations +////////////////////////////// + +class C { } +class D { } +function check_inst(_: C | D) { } + +// ok +check_inst(new D); + +// ...even when they \"flow\" in +check_inst(id(new C)); + +//////////////////////// +// function annotations +//////////////////////// + +function check_fun(_: ((_: number) => number) | ((_: string) => string)) { } + +// help! +check_fun((x) => x); + +////////////////////// +// object annotations +////////////////////// + +function check_obj(_: { x: number } | { x: string }) { } + +// ok +check_obj({ x: \"\" }); + +// help! +check_obj({ x: id(\"\") }); + +///////////////////// +// array annotations +///////////////////// + +function check_arr(_: number[] | string[]) { } + +// ok +check_arr([\"\"]); + +// help! +check_arr([id(\"\")]); + +////////////////////////////////////// +// generic class instance annotations +////////////////////////////////////// + +class P { } +function check_poly_inst(_: P | P) { } + +// help! +check_poly_inst(new P); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:340:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test11.js 1`] = ` +"// @noflow + +// disjoint unions + +function length(list: List) { + if (list.kind === \"cons\") return length(list.next) + 1; + else return 0; +} + + +length({ kind: \"nil\" }); +length({ kind: \"cons\" }); // missing \`next\` +length({ kind: \"cons\", next: { kind: \"nil\" } }); +length({ kind: \"empty\" }); // \`kind\` not found + +type List = Nil | Cons; +type Nil = { kind: \"nil\" }; +type Cons = { kind: \"cons\", next: List }; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test12.js 1`] = ` +"// @noflow + +// polymorphic recursive types + +type F = { f: F, x: X } +type G = { x: number } +type H = { x: string } + +function rec(x: F): G | H { return x; } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1479:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test13.js 1`] = ` +"// @noflow + +/* ensure there are no unintended side effects when trying branches */ + +({type: \'B\', id: \'hi\'}: { + type: \'A\'; + id: ?string; +} | { + type: \'B\'; + id: string; +}); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test14.js 1`] = ` +"// @noflow + +// annotations + +declare class C { + get(): X; +} + +function union(o: { x: string } | { x: number }) { } + +function foo(c: C) { + union({ x: c.get() }); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test15.js 1`] = ` +"// @noflow + +// functions as objects + +function foo(target: EventTarget) { + target.addEventListener(\'click\', (e) => {}); +} + +declare class EventTarget { + addEventListener(type: \'foo\', listener: KeyboardEventHandler): void; + addEventListener(type: string, listener: EventHandler): void; +} + +declare class Event { } +declare class KeyboardEvent { } + +type EventHandler = (event: Event) => mixed +type KeyboardEventHandler = (event: KeyboardEvent) => mixed + +// example where globals are not yet resolved + +function bar(x: (() => void) | { x: number }) { } + +bar(() => { }); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:340:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test16.js 1`] = ` +"// @noflow + +// annotations + +type T = number | (() => string); +type Foo = T | (() => bool); + +type Bar = number | (() => string) | (() => bool); + +function foo(x: Foo) { } +foo(() => qux()); + +function bar(x: Bar) { } +bar(() => qux()); + +var x = false; +function qux() { return x; } +x = \"\"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1530:42) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test17.js 1`] = ` +"// @noflow + +// Array#concat + +[].concat([]); + +([].concat([0,1])[1]: string) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @noflow +// Array#concat +[ ].concat([ ]); +([ ].concat([ 0, 1 ])[1]: string); + +" +`; + +exports[`test test18.js 1`] = ` +"// @noflow + +// method overloads + +declare class C { + m(x: number): void; + m(x: string): void; +} + +function f() { return 0; } + +new C().m(f()); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test19.js 1`] = ` +"// @noflow + +// constructor overloads + +function m() { + return new D(); +} + +declare class D { + constructor(_: void): void; + constructor(_: null): void; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:340:16) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test20.js 1`] = ` +"// @noflow + +// Array#reduce + +[0,1].reduce((x,y,i) => y); + +[\"a\", \"b\"].reduce( + (regex, representation, index) => { + return regex + (index ? \'|\' : \'\') + \'(\' + representation + \')\'; + }, + \'\', +); + +[\"\"].reduce((acc,str) => acc * str.length); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @noflow +// Array#reduce +[ 0, 1 ].reduce((x, y, i) => y); +[ \"a\", \"b\" ].reduce( + (regex, representation, index) => { + return regex + ((index ? \"|\" : \"\")) + \"(\" + representation + \")\"; + }, + \"\" +); +[ \"\" ].reduce((acc, str) => acc * str.length); + +" +`; + +exports[`test test21.js 1`] = ` +"// @noflow + +// annotations for disjoint unions + +type T = + | { type: \"FOO\", x: number } + | { type: \"BAR\", x: string } + +({ type: (bar(): \"BAR\"), x: str() }: T); + +({ type: bar(), x: str() }: T); + +({ type: bar(), x: (str(): string) }: T); + +function bar() { + return \"BAR\"; +} + +function str() { + return \"hello\"; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test22.js 1`] = ` +"// @noflow + +// refinement of disjoint unions + +type Empty = { } + +type Success = { + type: \'SUCCESS\'; + result: string; +}; + +type Error = { + type: \'ERROR\'; +} & Empty; + +export type T = Success | Error; + +function foo(x: T) { + if (x.type === \'SUCCESS\') return x.result; + else return x.result; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1407 + return fromString(\" & \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1407:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test23.js 1`] = ` +"// @noflow + +// nested intersections (see also lib/test23_lib.js) + +type NestedObj = { } & { dummy: SomeLibClass }; + +type Obj = NestedObj & { x: string }; + +function foo(obj: Obj) { + obj.x; // should be OK + obj.x; // should also be OK (the check above shouldn\'t affect anything) +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1407 + return fromString(\" & \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1407:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test24.js 1`] = ` +"// @noflow + +// scaling test for full type resolution + +declare class C { + addListener(event: string, listener: Function): C; + emit(event: string, ...args:Array): boolean; + listeners(event: string): Array; + listenerCount(event: string): number; + on(event: string, listener: Function): C; + once(event: string, listener: Function): C; + removeAllListeners(event?: string): C; + removeListener(event: string, listener: Function): C; + setMaxListeners(n: number): void; +} + +declare class D extends C { + listen(port: number, hostname?: string, backlog?: number, callback?: Function): D; + listen(path: string, callback?: Function): D; + listen(handle: Object, callback?: Function): D; + close(callback?: Function): D; + address(): number; + connections: number; + maxConnections: number; + getConnections(callback: Function): void; + ref(): D; + unref(): D; +} + +(0: D | number); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test25.js 1`] = ` +"// @noflow + +// termination test (see also lib/test25_lib.js) + +function foo(rows: Rows, set: Set) { + return rows.reduce_rows( + (set, row) => row.reduce_row( + (set, i) => set.add(i), + set, + ), + set, + ); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test26.js 1`] = ` +"// @noflow + +declare function foo(x: number): number; +declare function foo(x: string): string; + +declare var x: number | string; + +(foo(x): number | string); + +type T = number | string; +declare var y: T; + +(foo(y): T); + +declare class Record { + set(x: \'foo\', y: number): void; + set(x: \'bar\', y: string): void; +} + +new Record().set(\'foo\', \"42\"); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1254:25) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test27.js 1`] = ` +"// @noflow + +type X = ({a:true} & {b:string}) | ({a:false} & {c:string}); +//type X = {a:true, b:string} | {a:false, c:string}; // this works. + +function hello1(x:X): string { + if (x.a === true) return x.b; else return x.c; +} + +function hello2(x:X): string { + if (x.a === false) return x.c; else return x.b; +} + +function hello3(x:X): string { + if (x.a) { return x.b; } else { return x.c; } +} + +function hello4(x:X): string { + if (!x.a) { return x.c; } else { return x.b; } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1407 + return fromString(\" & \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1407:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.map (/src/fast-path.js:167:19) + at genericPrintNoParens (/src/printer.js:1530:42) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test29.js 1`] = ` +"// @noflow + +// Make sure caching doesn\'t cause a spurious successful match (e.g., when a +// failed match is tried again). This may happen, e.g., when checking +// polymorphic definitions, where the same code may be checked multiple times +// with different instantiations. + +type Row = { x: string }; + +declare class D { + reduce( + callbackfn: (previousValue: T, currentValue: T) => T, + initialValue: void + ): T; + reduce( + callbackfn: (previousValue: U, currentValue: T) => U, + initialValue: U + ): U; +} + +class C { + foo( + rows: D, + minWidth: number, + ): number { + return rows.reduce( + (length, row) => 0, + minWidth, + ); + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1368:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test30.js 1`] = ` +"// @noflow + +const Constants = require(\'./test30-helper\'); + +type ActionType = + | { type: \'foo\', x: number } + | { type: \'bar\', x: number } + +({ type: Constants.BAR, x: 0 }: ActionType); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1530 + return fromString(\" | \").join(path.map(print, \"types\")); + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1530:32) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1481:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test30-helper.js 1`] = ` +"// @noflow + +module.exports = { + FOO: \'foo\', + BAR: \'bar\', +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @noflow +module.exports = { FOO: \"foo\", BAR: \"bar\" }; + +" +`; + +exports[`test test31.js 1`] = ` +"// @noflow + +// make sure tuples are type arguments (as used e.g. when viewing maps as +// key/value iterables) work + +interface SomeIterator { } + +interface SomeIterable { + it(): SomeIterator; +} + +declare class SomeMap { + it(): SomeIterator<[K,V]>; + set(k: K, v: V): void; +} + +declare class ImmutableMap { } + +declare function convert(iter: SomeIterable<[K,V]>): ImmutableMap; + +function foo(): ImmutableMap { + const countersGlobalMap = new SomeMap(); + countersGlobalMap.set(\"\", false); + return convert(countersGlobalMap); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1384:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test32.js 1`] = ` +"// @flow + +// make sure that full resolution jobs don\'t cache improperly to signal success +// when they have failed earlier + +function foo(value: Indirect | number): Indirect | number { + const castedValue: number = typeof value === \'number\' ? value : 0; + return castedValue; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1374:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/union_new/issue-1349.js b/tests/union_new/issue-1349.js new file mode 100644 index 000000000000..3bb5c9f3ba3c --- /dev/null +++ b/tests/union_new/issue-1349.js @@ -0,0 +1,6 @@ +/* @flow */ + +var bar: Array<{b: ?boolean, c: number} | {b: boolean}> = [ + {b: true, c: 123}, + {b: true} +]; diff --git a/tests/union_new/issue-1371.js b/tests/union_new/issue-1371.js new file mode 100644 index 000000000000..e46b4059ea6b --- /dev/null +++ b/tests/union_new/issue-1371.js @@ -0,0 +1,7 @@ +function create(a: any): { type: 'B', data: number } | { type: 'A', data: string } +{ + return { + type: 'A', + data: a + } +} diff --git a/tests/union_new/issue-1455-helper.js b/tests/union_new/issue-1455-helper.js new file mode 100644 index 000000000000..a483642f8423 --- /dev/null +++ b/tests/union_new/issue-1455-helper.js @@ -0,0 +1,3 @@ +/* @flow */ + +export class Foobar { } diff --git a/tests/union_new/issue-1455.js b/tests/union_new/issue-1455.js new file mode 100644 index 000000000000..1163aaf93311 --- /dev/null +++ b/tests/union_new/issue-1455.js @@ -0,0 +1,9 @@ +/* @flow */ +import type {Foobar} from "./issue-1455-helper" + +function create(content: ?Foobar | String | Array) { +} + +function node(content: ?Foobar | String | Array) { + create(content) +} diff --git a/tests/union_new/issue-1462-i.js b/tests/union_new/issue-1462-i.js new file mode 100644 index 000000000000..e844d3b78221 --- /dev/null +++ b/tests/union_new/issue-1462-i.js @@ -0,0 +1,22 @@ +type Common = { +}; + +type A = Common & { + type: 'A', + foo: number +}; + +type B = Common & { + type: 'B', + foo: Array +} + +type MyType = A | B; + +function print(x: number) { + console.log(x); +} + +function printAll(val: MyType) { + print(val.foo); // <--- foo could be an array +} diff --git a/tests/union_new/issue-1462-ii.js b/tests/union_new/issue-1462-ii.js new file mode 100644 index 000000000000..260eb7c19017 --- /dev/null +++ b/tests/union_new/issue-1462-ii.js @@ -0,0 +1,27 @@ +type Common = { +}; + +type A = { + type: 'A', + foo: number +} & Common; + +type B = { + type: 'B', + foo: Array +} & Common; + +type MyType = A | B; + + +function print(x: number) { + console.log(x); +} + +function printAll(val: MyType) { + if (val.type === 'A') { + print(val.foo); + } else { + val.foo.forEach(print); + } +} diff --git a/tests/union_new/issue-1664.js b/tests/union_new/issue-1664.js new file mode 100644 index 000000000000..e2d1e2d50425 --- /dev/null +++ b/tests/union_new/issue-1664.js @@ -0,0 +1,26 @@ +/* @flow */ + +type DataBase = { + id: string, + name: string, +}; + +type UserData = DataBase & { + kind: "user", +}; + +type SystemData = DataBase & { + kind: "system", +} + +type Data = UserData | SystemData; + +const data: Data = { + id: "", + name: "", + kind: "system", +} + +if (data.kind === "system") { + (data: SystemData); +} diff --git a/tests/union_new/issue-1759.js b/tests/union_new/issue-1759.js new file mode 100644 index 000000000000..af3a1a3fe51f --- /dev/null +++ b/tests/union_new/issue-1759.js @@ -0,0 +1,8 @@ +// @flow + +type X = ({a:true} & {b:string}) | ({a:false} & {c:string}); +//type X = {a:true, b:string} | {a:false, c:string}; // this works. + +function hello(x:X): string { + if (x.a === true) return x.b; else return x.c; +} diff --git a/tests/union_new/issue-2232.js b/tests/union_new/issue-2232.js new file mode 100644 index 000000000000..2401f36058f5 --- /dev/null +++ b/tests/union_new/issue-2232.js @@ -0,0 +1,24 @@ +/* @flow */ + +declare type Entity = { + id: T, + name: string +} + +declare type StringEntity = Entity + + +declare type Foo = StringEntity & { + bars: Object, + kind: 1 +} +declare type EmptyFoo = StringEntity & { + bars: null, + kind: 2 +} + +function test(f: Foo| EmptyFoo) { + if (f.kind === 1) { + (f: Foo) + } +} diff --git a/tests/union_new/issue-815.js b/tests/union_new/issue-815.js new file mode 100644 index 000000000000..44f2bed4b095 --- /dev/null +++ b/tests/union_new/issue-815.js @@ -0,0 +1,21 @@ +/* @flow */ +type T = A|B; +class U {}; +declare var children: U; +(children: T|U); +class A {}; +class B {}; + +type VirtualElement = Thunk|VirtualNode; +type Child = VirtualElement; +type Children = Array; + + +class Thunk {} +class VirtualNode { + children: Child|Children; + constructor(type, children/*:Children*/) { + this.children = children.length === 1 ? children[0] : + children; + } +} diff --git a/tests/union_new/issue-824-helper.js b/tests/union_new/issue-824-helper.js new file mode 100644 index 000000000000..289eb998e500 --- /dev/null +++ b/tests/union_new/issue-824-helper.js @@ -0,0 +1,12 @@ +import A from "./issue-824"; + +export class B extends A { + which(): number { + return 1; + } +} +export class C extends A { + which(): number { + return 2; + } +} diff --git a/tests/union_new/issue-824.js b/tests/union_new/issue-824.js new file mode 100644 index 000000000000..655e03d1391b --- /dev/null +++ b/tests/union_new/issue-824.js @@ -0,0 +1,16 @@ +import { B, C } from "./issue-824-helper"; + +type K = B | C; + +type I = { + which(): number; +}; + +export default class A { + static foo(p: K): bool { + return false; + } + static bar(p: I & K): bool { + return this.foo(p); + } +} diff --git a/tests/union_new/jsfmt.spec.js b/tests/union_new/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/union_new/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/union_new/lib/__snapshots__/jsfmt.spec.js.snap b/tests/union_new/lib/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..fe17b3fb98ef --- /dev/null +++ b/tests/union_new/lib/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,69 @@ +exports[`test test23_lib.js 1`] = ` +"declare class SomeLibClass { } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +declare class SomeLibClass {} + +" +`; + +exports[`test test25_lib.js 1`] = ` +"declare class Set { + add(): Set; +} + +declare class Row { + reduce_row( + callbackfn: (previousValue: number, currentValue: number) => number, + initialValue: void + ): number; + reduce_row( + callbackfn: (previousValue: U, currentValue: number) => U, + initialValue: U + ): U; +} + +declare class Rows { + reduce_rows( + callbackfn: (previousValue: X, currentValue: Row) => X, + initialValue: X + ): X; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1348 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1348:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1452:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; + +exports[`test test32_lib.js 1`] = ` +"type Indirect = Array; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/src/printer.js:1497 + fromString(\", \").join(path.map(print, \"params\")), + ^ + +TypeError: fromString(...).join is not a function + at genericPrintNoParens (/src/printer.js:1497:26) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) + at printGenerically (/src/printer.js:111:12) + at FastPath.call (/src/fast-path.js:113:16) + at genericPrintNoParens (/src/printer.js:1479:14) + at genericPrint (/src/printer.js:166:7) + at p (/src/printer.js:111:37) + at exports.printComments (/src/comments.js:327:20) +" +`; diff --git a/tests/union_new/lib/jsfmt.spec.js b/tests/union_new/lib/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/union_new/lib/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/union_new/lib/test23_lib.js b/tests/union_new/lib/test23_lib.js new file mode 100644 index 000000000000..a0b41766ae6e --- /dev/null +++ b/tests/union_new/lib/test23_lib.js @@ -0,0 +1 @@ +declare class SomeLibClass { } diff --git a/tests/union_new/lib/test25_lib.js b/tests/union_new/lib/test25_lib.js new file mode 100644 index 000000000000..d79c71a20c19 --- /dev/null +++ b/tests/union_new/lib/test25_lib.js @@ -0,0 +1,21 @@ +declare class Set { + add(): Set; +} + +declare class Row { + reduce_row( + callbackfn: (previousValue: number, currentValue: number) => number, + initialValue: void + ): number; + reduce_row( + callbackfn: (previousValue: U, currentValue: number) => U, + initialValue: U + ): U; +} + +declare class Rows { + reduce_rows( + callbackfn: (previousValue: X, currentValue: Row) => X, + initialValue: X + ): X; +} diff --git a/tests/union_new/lib/test32_lib.js b/tests/union_new/lib/test32_lib.js new file mode 100644 index 000000000000..e0daf1518b2f --- /dev/null +++ b/tests/union_new/lib/test32_lib.js @@ -0,0 +1 @@ +type Indirect = Array; diff --git a/tests/union_new/test1.js b/tests/union_new/test1.js new file mode 100644 index 000000000000..10758e4134b2 --- /dev/null +++ b/tests/union_new/test1.js @@ -0,0 +1,60 @@ +// @noflow + +/** + * Test that shows how the implementation of union types is broken + */ + +////////////////////////////// +// example with object types +////////////////////////////// + +function obj(a: A1 | A2) { + return a.x; +} + +const obj_result = obj({ x: "" }); // currently an error! (expect it to be OK) + +// Type definitions used above are defined below, but in an order that +// deliberately makes their full resolution as lazy as possible. The call above +// blocks until A1 is partially resolved. Since the argument partially matches +// A1, that branch is selected. Later, that branch errors, but other branches +// have been lost by then. + +type A1 = { x: B1 }; +type A2 = { x: B2 }; + +type B1 = number; +type B2 = string; + +(obj_result: B1 | B2); + +/////////////////////////////////////// +// similar example with function types +/////////////////////////////////////// + +function fun(a: A3 | A4) { + return a(); +} + +const fun_result = fun(() => ""); + +type A3 = () => B3; +type A4 = () => B4; + +type B3 = number; +type B4 = string; + +(fun_result: B3 | B4); + +///////////////////////////////////////////// +// similar example with class instance types +///////////////////////////////////////////// + +function inst(a: A5 | A6) { } + +class B5 { } +class B6 { } +inst([new B6]); + +type A5 = B5[]; +type A6 = B6[]; diff --git a/tests/union_new/test10.js b/tests/union_new/test10.js new file mode 100644 index 000000000000..deaa835fe346 --- /dev/null +++ b/tests/union_new/test10.js @@ -0,0 +1,72 @@ +// @noflow + +function id(x: X): X { return x; } + +///////////////////////// +// primitive annotations +///////////////////////// + +function check_prim(_: number | string) { } + +// ok +check_prim(""); + +// ...even when they "flow" in +check_prim(id("")); + +////////////////////////////// +// class instance annotations +////////////////////////////// + +class C { } +class D { } +function check_inst(_: C | D) { } + +// ok +check_inst(new D); + +// ...even when they "flow" in +check_inst(id(new C)); + +//////////////////////// +// function annotations +//////////////////////// + +function check_fun(_: ((_: number) => number) | ((_: string) => string)) { } + +// help! +check_fun((x) => x); + +////////////////////// +// object annotations +////////////////////// + +function check_obj(_: { x: number } | { x: string }) { } + +// ok +check_obj({ x: "" }); + +// help! +check_obj({ x: id("") }); + +///////////////////// +// array annotations +///////////////////// + +function check_arr(_: number[] | string[]) { } + +// ok +check_arr([""]); + +// help! +check_arr([id("")]); + +////////////////////////////////////// +// generic class instance annotations +////////////////////////////////////// + +class P { } +function check_poly_inst(_: P | P) { } + +// help! +check_poly_inst(new P); diff --git a/tests/union_new/test11.js b/tests/union_new/test11.js new file mode 100644 index 000000000000..c5a02b7fbf81 --- /dev/null +++ b/tests/union_new/test11.js @@ -0,0 +1,18 @@ +// @noflow + +// disjoint unions + +function length(list: List) { + if (list.kind === "cons") return length(list.next) + 1; + else return 0; +} + + +length({ kind: "nil" }); +length({ kind: "cons" }); // missing `next` +length({ kind: "cons", next: { kind: "nil" } }); +length({ kind: "empty" }); // `kind` not found + +type List = Nil | Cons; +type Nil = { kind: "nil" }; +type Cons = { kind: "cons", next: List }; diff --git a/tests/union_new/test12.js b/tests/union_new/test12.js new file mode 100644 index 000000000000..45aff3456d35 --- /dev/null +++ b/tests/union_new/test12.js @@ -0,0 +1,9 @@ +// @noflow + +// polymorphic recursive types + +type F = { f: F, x: X } +type G = { x: number } +type H = { x: string } + +function rec(x: F): G | H { return x; } diff --git a/tests/union_new/test13.js b/tests/union_new/test13.js new file mode 100644 index 000000000000..89333fc536a4 --- /dev/null +++ b/tests/union_new/test13.js @@ -0,0 +1,11 @@ +// @noflow + +/* ensure there are no unintended side effects when trying branches */ + +({type: 'B', id: 'hi'}: { + type: 'A'; + id: ?string; +} | { + type: 'B'; + id: string; +}); diff --git a/tests/union_new/test14.js b/tests/union_new/test14.js new file mode 100644 index 000000000000..8b69352cd591 --- /dev/null +++ b/tests/union_new/test14.js @@ -0,0 +1,13 @@ +// @noflow + +// annotations + +declare class C { + get(): X; +} + +function union(o: { x: string } | { x: number }) { } + +function foo(c: C) { + union({ x: c.get() }); +} diff --git a/tests/union_new/test15.js b/tests/union_new/test15.js new file mode 100644 index 000000000000..c5660df4e330 --- /dev/null +++ b/tests/union_new/test15.js @@ -0,0 +1,24 @@ +// @noflow + +// functions as objects + +function foo(target: EventTarget) { + target.addEventListener('click', (e) => {}); +} + +declare class EventTarget { + addEventListener(type: 'foo', listener: KeyboardEventHandler): void; + addEventListener(type: string, listener: EventHandler): void; +} + +declare class Event { } +declare class KeyboardEvent { } + +type EventHandler = (event: Event) => mixed +type KeyboardEventHandler = (event: KeyboardEvent) => mixed + +// example where globals are not yet resolved + +function bar(x: (() => void) | { x: number }) { } + +bar(() => { }); diff --git a/tests/union_new/test16.js b/tests/union_new/test16.js new file mode 100644 index 000000000000..0debe2bd0a86 --- /dev/null +++ b/tests/union_new/test16.js @@ -0,0 +1,18 @@ +// @noflow + +// annotations + +type T = number | (() => string); +type Foo = T | (() => bool); + +type Bar = number | (() => string) | (() => bool); + +function foo(x: Foo) { } +foo(() => qux()); + +function bar(x: Bar) { } +bar(() => qux()); + +var x = false; +function qux() { return x; } +x = ""; diff --git a/tests/union_new/test17.js b/tests/union_new/test17.js new file mode 100644 index 000000000000..919fa91246c5 --- /dev/null +++ b/tests/union_new/test17.js @@ -0,0 +1,7 @@ +// @noflow + +// Array#concat + +[].concat([]); + +([].concat([0,1])[1]: string) diff --git a/tests/union_new/test18.js b/tests/union_new/test18.js new file mode 100644 index 000000000000..6ae2bbd21c8c --- /dev/null +++ b/tests/union_new/test18.js @@ -0,0 +1,12 @@ +// @noflow + +// method overloads + +declare class C { + m(x: number): void; + m(x: string): void; +} + +function f() { return 0; } + +new C().m(f()); diff --git a/tests/union_new/test19.js b/tests/union_new/test19.js new file mode 100644 index 000000000000..2178cdf8a7d0 --- /dev/null +++ b/tests/union_new/test19.js @@ -0,0 +1,12 @@ +// @noflow + +// constructor overloads + +function m() { + return new D(); +} + +declare class D { + constructor(_: void): void; + constructor(_: null): void; +} diff --git a/tests/union_new/test2.js b/tests/union_new/test2.js new file mode 100644 index 000000000000..4647de3ccdfb --- /dev/null +++ b/tests/union_new/test2.js @@ -0,0 +1,77 @@ +// @noflow + +/** + * Test that shows how the implementation of union types is broken + */ + +////////////////////////////// +// example with object types +////////////////////////////// + +function obj(a: { x: number } | { x: string }) { } + +obj(({ x: "" }: A1)); + +type A1 = { x: B1 }; + +type B1 = string; + +/////////////////////////////////////// +// similar example with function types +/////////////////////////////////////// + +function fun(a: (() => number) | (() => string)) { } + +fun(((() => ""): A2)); + +type A2 = () => B2; + +type B2 = string; + +///////////////////////////////////////////////////// +// similar example with generic class instance types +///////////////////////////////////////////////////// + +class C { } + +function inst(a: C | C) { } + +inst((new C: A3)); + +type A3 = C; + +type B3 = string; + +///////////////////////////////////////////// +// similar example with generic type aliases +///////////////////////////////////////////// + +function alias(a: T | T) { } +alias({ x: (x: V) => { } }); + +type T = { x: U } +type U = (x: V) => void; +type V = X; + +type B4 = string; + +// class statics + +function stat(a: { x: number } | { x: string }) { } + +class D { + static x: B5; +} + +stat(D); + +type B5 = string; + +// tuples + +function tup(a: [number,boolean] | [string,boolean]) { } + +tup((["",false]: A6)); + +type A6 = [B6,boolean]; +type B6 = string; diff --git a/tests/union_new/test20.js b/tests/union_new/test20.js new file mode 100644 index 000000000000..1aaab84f7941 --- /dev/null +++ b/tests/union_new/test20.js @@ -0,0 +1,14 @@ +// @noflow + +// Array#reduce + +[0,1].reduce((x,y,i) => y); + +["a", "b"].reduce( + (regex, representation, index) => { + return regex + (index ? '|' : '') + '(' + representation + ')'; + }, + '', +); + +[""].reduce((acc,str) => acc * str.length); diff --git a/tests/union_new/test21.js b/tests/union_new/test21.js new file mode 100644 index 000000000000..9f68c9485371 --- /dev/null +++ b/tests/union_new/test21.js @@ -0,0 +1,21 @@ +// @noflow + +// annotations for disjoint unions + +type T = + | { type: "FOO", x: number } + | { type: "BAR", x: string } + +({ type: (bar(): "BAR"), x: str() }: T); + +({ type: bar(), x: str() }: T); + +({ type: bar(), x: (str(): string) }: T); + +function bar() { + return "BAR"; +} + +function str() { + return "hello"; +} diff --git a/tests/union_new/test22.js b/tests/union_new/test22.js new file mode 100644 index 000000000000..2b7a263f859b --- /dev/null +++ b/tests/union_new/test22.js @@ -0,0 +1,21 @@ +// @noflow + +// refinement of disjoint unions + +type Empty = { } + +type Success = { + type: 'SUCCESS'; + result: string; +}; + +type Error = { + type: 'ERROR'; +} & Empty; + +export type T = Success | Error; + +function foo(x: T) { + if (x.type === 'SUCCESS') return x.result; + else return x.result; +} diff --git a/tests/union_new/test23.js b/tests/union_new/test23.js new file mode 100644 index 000000000000..ad65416fcffa --- /dev/null +++ b/tests/union_new/test23.js @@ -0,0 +1,12 @@ +// @noflow + +// nested intersections (see also lib/test23_lib.js) + +type NestedObj = { } & { dummy: SomeLibClass }; + +type Obj = NestedObj & { x: string }; + +function foo(obj: Obj) { + obj.x; // should be OK + obj.x; // should also be OK (the check above shouldn't affect anything) +} diff --git a/tests/union_new/test24.js b/tests/union_new/test24.js new file mode 100644 index 000000000000..98d2d704242d --- /dev/null +++ b/tests/union_new/test24.js @@ -0,0 +1,30 @@ +// @noflow + +// scaling test for full type resolution + +declare class C { + addListener(event: string, listener: Function): C; + emit(event: string, ...args:Array): boolean; + listeners(event: string): Array; + listenerCount(event: string): number; + on(event: string, listener: Function): C; + once(event: string, listener: Function): C; + removeAllListeners(event?: string): C; + removeListener(event: string, listener: Function): C; + setMaxListeners(n: number): void; +} + +declare class D extends C { + listen(port: number, hostname?: string, backlog?: number, callback?: Function): D; + listen(path: string, callback?: Function): D; + listen(handle: Object, callback?: Function): D; + close(callback?: Function): D; + address(): number; + connections: number; + maxConnections: number; + getConnections(callback: Function): void; + ref(): D; + unref(): D; +} + +(0: D | number); diff --git a/tests/union_new/test25.js b/tests/union_new/test25.js new file mode 100644 index 000000000000..581bed11d031 --- /dev/null +++ b/tests/union_new/test25.js @@ -0,0 +1,13 @@ +// @noflow + +// termination test (see also lib/test25_lib.js) + +function foo(rows: Rows, set: Set) { + return rows.reduce_rows( + (set, row) => row.reduce_row( + (set, i) => set.add(i), + set, + ), + set, + ); +} diff --git a/tests/union_new/test26.js b/tests/union_new/test26.js new file mode 100644 index 000000000000..501c49c1eaa2 --- /dev/null +++ b/tests/union_new/test26.js @@ -0,0 +1,20 @@ +// @noflow + +declare function foo(x: number): number; +declare function foo(x: string): string; + +declare var x: number | string; + +(foo(x): number | string); + +type T = number | string; +declare var y: T; + +(foo(y): T); + +declare class Record { + set(x: 'foo', y: number): void; + set(x: 'bar', y: string): void; +} + +new Record().set('foo', "42"); diff --git a/tests/union_new/test27.js b/tests/union_new/test27.js new file mode 100644 index 000000000000..823609224da8 --- /dev/null +++ b/tests/union_new/test27.js @@ -0,0 +1,20 @@ +// @noflow + +type X = ({a:true} & {b:string}) | ({a:false} & {c:string}); +//type X = {a:true, b:string} | {a:false, c:string}; // this works. + +function hello1(x:X): string { + if (x.a === true) return x.b; else return x.c; +} + +function hello2(x:X): string { + if (x.a === false) return x.c; else return x.b; +} + +function hello3(x:X): string { + if (x.a) { return x.b; } else { return x.c; } +} + +function hello4(x:X): string { + if (!x.a) { return x.c; } else { return x.b; } +} diff --git a/tests/union_new/test29.js b/tests/union_new/test29.js new file mode 100644 index 000000000000..5db89360ada5 --- /dev/null +++ b/tests/union_new/test29.js @@ -0,0 +1,31 @@ +// @noflow + +// Make sure caching doesn't cause a spurious successful match (e.g., when a +// failed match is tried again). This may happen, e.g., when checking +// polymorphic definitions, where the same code may be checked multiple times +// with different instantiations. + +type Row = { x: string }; + +declare class D { + reduce( + callbackfn: (previousValue: T, currentValue: T) => T, + initialValue: void + ): T; + reduce( + callbackfn: (previousValue: U, currentValue: T) => U, + initialValue: U + ): U; +} + +class C { + foo( + rows: D, + minWidth: number, + ): number { + return rows.reduce( + (length, row) => 0, + minWidth, + ); + } +} diff --git a/tests/union_new/test3.js b/tests/union_new/test3.js new file mode 100644 index 000000000000..fd12e5fd150f --- /dev/null +++ b/tests/union_new/test3.js @@ -0,0 +1,29 @@ +// @noflow + +/** + * Test that shows how the implementation of union types is broken + */ + +/////////////////////////////// +// example with function types +/////////////////////////////// + +function fun(a: ((x: number) => void) | ((x: string) => void)) { } + +fun((((x) => {}): A1)); + +type A1 = (x: B1) => void; + +type B1 = string; + +//////////////////////////// +// example with array types +//////////////////////////// + +function arr(a: number[] | string[]) { } + +arr(([]: A2)); + +type A2 = B2[]; + +type B2 = string; diff --git a/tests/union_new/test30-helper.js b/tests/union_new/test30-helper.js new file mode 100644 index 000000000000..b4eccdb14226 --- /dev/null +++ b/tests/union_new/test30-helper.js @@ -0,0 +1,6 @@ +// @noflow + +module.exports = { + FOO: 'foo', + BAR: 'bar', +} diff --git a/tests/union_new/test30.js b/tests/union_new/test30.js new file mode 100644 index 000000000000..96ec6ec5618c --- /dev/null +++ b/tests/union_new/test30.js @@ -0,0 +1,9 @@ +// @noflow + +const Constants = require('./test30-helper'); + +type ActionType = + | { type: 'foo', x: number } + | { type: 'bar', x: number } + +({ type: Constants.BAR, x: 0 }: ActionType); diff --git a/tests/union_new/test31.js b/tests/union_new/test31.js new file mode 100644 index 000000000000..23c2642bb22f --- /dev/null +++ b/tests/union_new/test31.js @@ -0,0 +1,25 @@ +// @noflow + +// make sure tuples are type arguments (as used e.g. when viewing maps as +// key/value iterables) work + +interface SomeIterator { } + +interface SomeIterable { + it(): SomeIterator; +} + +declare class SomeMap { + it(): SomeIterator<[K,V]>; + set(k: K, v: V): void; +} + +declare class ImmutableMap { } + +declare function convert(iter: SomeIterable<[K,V]>): ImmutableMap; + +function foo(): ImmutableMap { + const countersGlobalMap = new SomeMap(); + countersGlobalMap.set("", false); + return convert(countersGlobalMap); +} diff --git a/tests/union_new/test32.js b/tests/union_new/test32.js new file mode 100644 index 000000000000..b6f730ec9a37 --- /dev/null +++ b/tests/union_new/test32.js @@ -0,0 +1,9 @@ +// @flow + +// make sure that full resolution jobs don't cache improperly to signal success +// when they have failed earlier + +function foo(value: Indirect | number): Indirect | number { + const castedValue: number = typeof value === 'number' ? value : 0; + return castedValue; +} diff --git a/tests/union_new/test4.js b/tests/union_new/test4.js new file mode 100644 index 000000000000..93dd1dccaac4 --- /dev/null +++ b/tests/union_new/test4.js @@ -0,0 +1,36 @@ +// @noflow + +/** + * Test that shows how the implementation of union types is broken + */ + +/////////////////////////////// +// example with function types +/////////////////////////////// + +function fun(a: ((x: number) => void) | ((x: string) => void)) { } + +const a1 = ((x) => {}: A1); +fun(a1); + +function fun_call(x: string) { a1(x); } + +type A1 = (x: B1) => void; + +type B1 = string; + +//////////////////////////// +// example with array types +//////////////////////////// + +function arr(a: number[] | string[]) { } + +const a2 = ([]: A2); +arr(a2); + +function arr_set(x: string, i: number) { a2[i] = x; } +function arr_get(i: number): string { return a2[i]; } + +type A2 = B2[]; + +type B2 = string; diff --git a/tests/union_new/test5.js b/tests/union_new/test5.js new file mode 100644 index 000000000000..0222e8f934fc --- /dev/null +++ b/tests/union_new/test5.js @@ -0,0 +1,27 @@ +// @noflow + +/** + * Test that shows how the implementation of union types is broken + */ + +/////////////////////////////// +// example with function types +/////////////////////////////// + +function fun(a: ((x: number) => number) | ((x: string) => string)) { } + +function a1(x) { return x; } +fun(a1); + +function fun_call(x: string): string { return a1(x); } + +///////////////////////////// +// example with array types +///////////////////////////// + +function arr(a: number[] | string[]) { } + +var a2 = []; +arr(a2); + +function arr_set(x: string, i: number) { a2[i] = x; } diff --git a/tests/union_new/test6.js b/tests/union_new/test6.js new file mode 100644 index 000000000000..c780bb848351 --- /dev/null +++ b/tests/union_new/test6.js @@ -0,0 +1,25 @@ +// @noflow + +/** + * Test that shows how the implementation of union types is broken + */ + +////////////////////////////////////////// +// example with generic class inheritance +////////////////////////////////////////// + +function inst(a: E): C | C { return a; } + +const mk_C = () => C; +const mk_D = () => D; +const mk_E = () => E; + +type B4 = string; + +const _D = mk_D(); +class E extends _D { } + +const _C = mk_C(); +class D extends _C { } + +class C { } diff --git a/tests/union_new/test7.js b/tests/union_new/test7.js new file mode 100644 index 000000000000..1f9f5713b938 --- /dev/null +++ b/tests/union_new/test7.js @@ -0,0 +1,29 @@ +// @noflow + +/** + * Test that shows how the implementation of union types is broken + */ + +//////////////////// +// recursive types +//////////////////// + +function rec(x: F1 | F2) { } +rec({ x: 0 }); + +type F1 = G1; +type F2 = G2; +type G1 = { x: H1, y?: G1 }; +type G2 = { x: H2, y?: G2 }; +type H1 = string; +type H2 = number; + +/////////////////////////////// +// polymorphic recursive types +/////////////////////////////// + +function polyrec(x: PF | PF) { } +rec({ x: 0 }); + +type PF = PG; +type PG = { x: X, y?: PG }; diff --git a/tests/union_new/test8.js b/tests/union_new/test8.js new file mode 100644 index 000000000000..4afbc4c62b5f --- /dev/null +++ b/tests/union_new/test8.js @@ -0,0 +1,23 @@ +// @noflow + +/** + * Test that shows how the implementation of union types is broken + */ + +////////////////////// +// nested union types +////////////////////// + +function rec(x: F1 | F2) { } +rec({ x: 0 }); + +type F1 = G1 | G1_; +type F2 = G2 | G2_; +type G1 = { x: H1 }; +type G1_ = { x: H1_ }; +type G2 = { x: H2 }; +type G2_ = { x: H2_ }; +type H1 = boolean; +type H1_ = string; +type H2 = boolean; +type H2_ = number; diff --git a/tests/union_new/test9.js b/tests/union_new/test9.js new file mode 100644 index 000000000000..18221e5d82cd --- /dev/null +++ b/tests/union_new/test9.js @@ -0,0 +1,16 @@ +// @noflow + +/** + * Test that shows how the implementation of union types is broken + */ + +//////////////// +// interference +//////////////// + +function square(x? = 0) { + return x * x; +} + +function foo(f: ((_: ?number) => ?number) | (() => void)) { } +foo((x): number => square(x)) diff --git a/tests/unreachable/__snapshots__/jsfmt.spec.js.snap b/tests/unreachable/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..dc6dbc6e22a9 --- /dev/null +++ b/tests/unreachable/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,95 @@ +exports[`test typecheck.js 1`] = ` +"/* @flow */ + +function test1(): string { + return bar(); + + function bar() { + return 0; + } +} + +// regression test for analysis after abnormal control flow: +// consts must not become bot (EmptyT). + +function test2() { + const n = 0; + + return; + + function f() { + var x: typeof n = 0; // no error, n is still number + var y: string = n; // error, n is number (EmptyT would work) + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +function test1(): string { + return bar(); + function bar() { + return 0; + } +} +// regression test for analysis after abnormal control flow: +// consts must not become bot (EmptyT). +function test2() { + const n = 0; + return; + function f() { + var x: typeof n = 0;// no error, n is still number + var y: string = n;// error, n is number (EmptyT would work) + } +} + +" +`; + +exports[`test unreachable.js 1`] = ` +"/* @flow */ + +function foo(x, y) { + "use strict"; + return bar(x) + baz(y); + + // function declaration is hoisted, should not generate warning + function bar (ecks) { + return x + ecks; + } + + // assignment is not hoisted, should generate warning + var baz = function (why) { + return y + why; + }; + + // variable declaration is hoisted, should not generate warning + var x, y, z; + + // assignments are not hoisted, should generate 2 warnings + var t, + u = 5, + v, + w = 7; +} + +foo(1, 2); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +function foo(x, y) { "use strict"; + return bar(x) + baz(y); + // function declaration is hoisted, should not generate warning + function bar(ecks) { + return x + ecks; + } + // assignment is not hoisted, should generate warning + var baz = function(why) { + return y + why; + }; + // variable declaration is hoisted, should not generate warning + var xy,z; + // assignments are not hoisted, should generate 2 warnings + var tu = 5,v,w = 7; +} +foo(1, 2); + +" +`; diff --git a/tests/unreachable/jsfmt.spec.js b/tests/unreachable/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/unreachable/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/unreachable/typecheck.js b/tests/unreachable/typecheck.js new file mode 100644 index 000000000000..78c517f82e75 --- /dev/null +++ b/tests/unreachable/typecheck.js @@ -0,0 +1,23 @@ +/* @flow */ + +function test1(): string { + return bar(); + + function bar() { + return 0; + } +} + +// regression test for analysis after abnormal control flow: +// consts must not become bot (EmptyT). + +function test2() { + const n = 0; + + return; + + function f() { + var x: typeof n = 0; // no error, n is still number + var y: string = n; // error, n is number (EmptyT would work) + } +} diff --git a/tests/unreachable/unreachable.js b/tests/unreachable/unreachable.js new file mode 100644 index 000000000000..84cee2a9e380 --- /dev/null +++ b/tests/unreachable/unreachable.js @@ -0,0 +1,27 @@ +/* @flow */ + +function foo(x, y) { + "use strict"; + return bar(x) + baz(y); + + // function declaration is hoisted, should not generate warning + function bar (ecks) { + return x + ecks; + } + + // assignment is not hoisted, should generate warning + var baz = function (why) { + return y + why; + }; + + // variable declaration is hoisted, should not generate warning + var x, y, z; + + // assignments are not hoisted, should generate 2 warnings + var t, + u = 5, + v, + w = 7; +} + +foo(1, 2); diff --git a/tests/value/__snapshots__/jsfmt.spec.js.snap b/tests/value/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..778376ff6508 --- /dev/null +++ b/tests/value/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,16 @@ +exports[`test value.js 1`] = ` +"var o = {}; +o["x"] = 4; +var y:string = o["x"]; + +var table: { [_: string]: number } = {}; +table["x"] = "hello"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var o = {}; +o["x"] = 4; +var y: string = o["x"]; +var table: { [_: string]: number } = {}; +table["x"] = "hello"; + +" +`; diff --git a/tests/value/jsfmt.spec.js b/tests/value/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/value/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/value/value.js b/tests/value/value.js new file mode 100644 index 000000000000..ef7f957c6fe0 --- /dev/null +++ b/tests/value/value.js @@ -0,0 +1,6 @@ +var o = {}; +o["x"] = 4; +var y:string = o["x"]; + +var table: { [_: string]: number } = {}; +table["x"] = "hello"; diff --git a/tests/vim_emacs_errors/__snapshots__/jsfmt.spec.js.snap b/tests/vim_emacs_errors/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..fc4cf2d5fc7f --- /dev/null +++ b/tests/vim_emacs_errors/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,10 @@ +exports[`test test.js 1`] = ` +"// @flow + +(123: string); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow +(123: string); + +" +`; diff --git a/tests/vim_emacs_errors/jsfmt.spec.js b/tests/vim_emacs_errors/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/vim_emacs_errors/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/vim_emacs_errors/test.js b/tests/vim_emacs_errors/test.js new file mode 100644 index 000000000000..aa418762398d --- /dev/null +++ b/tests/vim_emacs_errors/test.js @@ -0,0 +1,3 @@ +// @flow + +(123: string); diff --git a/tests/weakmode/__snapshots__/jsfmt.spec.js.snap b/tests/weakmode/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..82293afbb43f --- /dev/null +++ b/tests/weakmode/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,47 @@ +exports[`test should_fail_without_weak.js 1`] = ` +"/* @flow */ +// This should fail without weak mode, because of the glaring type error. + +function returns_a_string() { + return "Hello"; +} + +function expects_an_int() { + return returns_a_string() * 10; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +// This should fail without weak mode, because of the glaring type error. +function returns_a_string() { + return "Hello"; +} +function expects_an_int() { + return returns_a_string() * 10; +} + +" +`; + +exports[`test should_pass_with_weak.js 1`] = ` +"/* @flow weak */ +// This should fail without weak mode, because of the glaring type error. + +function returns_a_string() { + return "Hello"; +} + +function expects_an_int() { + return returns_a_string() * 10; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow weak */ +// This should fail without weak mode, because of the glaring type error. +function returns_a_string() { + return "Hello"; +} +function expects_an_int() { + return returns_a_string() * 10; +} + +" +`; diff --git a/tests/weakmode/jsfmt.spec.js b/tests/weakmode/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/weakmode/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/weakmode/should_fail_without_weak.js b/tests/weakmode/should_fail_without_weak.js new file mode 100644 index 000000000000..9dcc0c64d334 --- /dev/null +++ b/tests/weakmode/should_fail_without_weak.js @@ -0,0 +1,10 @@ +/* @flow */ +// This should fail without weak mode, because of the glaring type error. + +function returns_a_string() { + return "Hello"; +} + +function expects_an_int() { + return returns_a_string() * 10; +} diff --git a/tests/weakmode/should_pass_with_weak.js b/tests/weakmode/should_pass_with_weak.js new file mode 100644 index 000000000000..14fce153d528 --- /dev/null +++ b/tests/weakmode/should_pass_with_weak.js @@ -0,0 +1,10 @@ +/* @flow weak */ +// This should fail without weak mode, because of the glaring type error. + +function returns_a_string() { + return "Hello"; +} + +function expects_an_int() { + return returns_a_string() * 10; +} diff --git a/tests/while/__snapshots__/jsfmt.spec.js.snap b/tests/while/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..5b99d486fc53 --- /dev/null +++ b/tests/while/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,73 @@ +exports[`test abnormal.js 1`] = ` +"/* @flow */ + +function foo(x: boolean) { + var ii = 10; + while (ii-- >= 0) { + if (x) { + continue; + } + return; + } + //console.log('this is still reachable'); +} + +function bar(x: boolean) { + var ii = 0; + while (ii > 0) { + return; + } + //console.log('this is still reachable'); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +function foo(x: boolean) { + var ii = 10; + while (ii-- >= 0) { + if (x) { + continue; + } + return; + }//console.log('this is still reachable'); +} +function bar(x: boolean) { + var ii = 0; + while (ii > 0) { + return; + }//console.log('this is still reachable'); +} + +" +`; + +exports[`test test.js 1`] = ` +"class C { + m() { return new C; } +} +function blah() {} +var node: ?C = new C; +while (node) { + var parent = node.m(); + var cloneable: C = node; + blah(); + node = parent.m(); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class C { + m() { + return new C(); + } +} +function blah() { + +} +var node: ?C = new C(); +while (node) { + var parent = node.m(); + var cloneable: C = node; + blah(); + node = parent.m(); +} + +" +`; diff --git a/tests/while/abnormal.js b/tests/while/abnormal.js new file mode 100644 index 000000000000..35431642a482 --- /dev/null +++ b/tests/while/abnormal.js @@ -0,0 +1,20 @@ +/* @flow */ + +function foo(x: boolean) { + var ii = 10; + while (ii-- >= 0) { + if (x) { + continue; + } + return; + } + //console.log('this is still reachable'); +} + +function bar(x: boolean) { + var ii = 0; + while (ii > 0) { + return; + } + //console.log('this is still reachable'); +} diff --git a/tests/while/jsfmt.spec.js b/tests/while/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/while/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/while/test.js b/tests/while/test.js new file mode 100644 index 000000000000..16f55038cdb7 --- /dev/null +++ b/tests/while/test.js @@ -0,0 +1,11 @@ +class C { + m() { return new C; } +} +function blah() {} +var node: ?C = new C; +while (node) { + var parent = node.m(); + var cloneable: C = node; + blah(); + node = parent.m(); +} diff --git a/tests/window/__snapshots__/jsfmt.spec.js.snap b/tests/window/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..fd3a6302ffdf --- /dev/null +++ b/tests/window/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,24 @@ +exports[`test window1.js 1`] = ` +"/* +@providesModule Window1 +*/ +module.exports = window.history; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* +@providesModule Window1 +*/ +module.exports = window.history; + +" +`; + +exports[`test window2.js 1`] = ` +"/* @providesModule Window2 */ + +module.exports = window.parent; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @providesModule Window2 */ +module.exports = window.parent; + +" +`; diff --git a/tests/window/jsfmt.spec.js b/tests/window/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/window/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/window/window1.js b/tests/window/window1.js new file mode 100644 index 000000000000..c5e6ac93949d --- /dev/null +++ b/tests/window/window1.js @@ -0,0 +1,4 @@ +/* +@providesModule Window1 +*/ +module.exports = window.history; diff --git a/tests/window/window2.js b/tests/window/window2.js new file mode 100644 index 000000000000..b16e6aa9eb88 --- /dev/null +++ b/tests/window/window2.js @@ -0,0 +1,3 @@ +/* @providesModule Window2 */ + +module.exports = window.parent; diff --git a/tests/x/XControllerURIBuilder.js b/tests/x/XControllerURIBuilder.js new file mode 100644 index 000000000000..78429106b236 --- /dev/null +++ b/tests/x/XControllerURIBuilder.js @@ -0,0 +1,30 @@ +/** + * @generated SignedSource<<13ca42bbc7fe91f15057861e18a77d88>> + * + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * !! This file is a check-in of a static_upstream project! !! + * !! !! + * !! You should not modify this file directly. Instead: !! + * !! 1) Use `fjs use-upstream` to temporarily replace this with !! + * !! the latest version from upstream. !! + * !! 2) Make your changes, test them, etc. !! + * !! 3) Use `fjs push-upstream` to copy your changes back to !! + * !! static_upstream. !! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * + * @providesModule XControllerURIBuilder + * @typechecks + */ + +// ... +class XControllerURIBuilder { + + // ... + getURI() { + // ... + var tokenRegex = new RegExp(/^\{(\?)?(.+?)\}$/); + // ... + } +} + +module.exports = XControllerURIBuilder; diff --git a/tests/x/__snapshots__/jsfmt.spec.js.snap b/tests/x/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..2dc029c12a2e --- /dev/null +++ b/tests/x/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,61 @@ +exports[`test XControllerURIBuilder.js 1`] = ` +"/** + * @generated SignedSource<<13ca42bbc7fe91f15057861e18a77d88>> + * + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * !! This file is a check-in of a static_upstream project! !! + * !! !! + * !! You should not modify this file directly. Instead: !! + * !! 1) Use \`fjs use-upstream\` to temporarily replace this with !! + * !! the latest version from upstream. !! + * !! 2) Make your changes, test them, etc. !! + * !! 3) Use \`fjs push-upstream\` to copy your changes back to !! + * !! static_upstream. !! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * + * @providesModule XControllerURIBuilder + * @typechecks + */ + +// ... +class XControllerURIBuilder { + + // ... + getURI() { + // ... + var tokenRegex = new RegExp(/^\\{(\\?)?(.+?)\\}$/); + // ... + } +} + +module.exports = XControllerURIBuilder; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @generated SignedSource<<13ca42bbc7fe91f15057861e18a77d88>> + * + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * !! This file is a check-in of a static_upstream project! !! + * !! !! + * !! You should not modify this file directly. Instead: !! + * !! 1) Use \`fjs use-upstream\` to temporarily replace this with !! + * !! the latest version from upstream. !! + * !! 2) Make your changes, test them, etc. !! + * !! 3) Use \`fjs push-upstream\` to copy your changes back to !! + * !! static_upstream. !! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * + * @providesModule XControllerURIBuilder + * @typechecks + */ +// ... +class XControllerURIBuilder { + // ... + getURI() { + // ... + var tokenRegex = new RegExp(/^\\{(\\?)?(.+?)\\}$/);// ... + } +} +module.exports = XControllerURIBuilder; + +" +`; diff --git a/tests/x/jsfmt.spec.js b/tests/x/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/x/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/yield/__snapshots__/jsfmt.spec.js.snap b/tests/yield/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000000..18bae5f5cf57 --- /dev/null +++ b/tests/yield/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,34 @@ +exports[`test yield_arrow_error.js 1`] = ` +"function * f () { + var e = () => { yield 1; }; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/node_modules/babylon/lib/index.js:4255 + throw err; + ^ + +SyntaxError: Unexpected token (2:18) + at Parser.pp$5.raise (/node_modules/babylon/lib/index.js:4252:13) + at Parser.pp.unexpected (/node_modules/babylon/lib/index.js:1633:8) + at Parser.pp$3.parseIdentifier (/node_modules/babylon/lib/index.js:4128:10) + at Parser.pp$3.parseExprAtom (/node_modules/babylon/lib/index.js:3490:21) + at Parser.parseExprAtom (/node_modules/babylon/lib/index.js:6408:22) + at Parser.pp$3.parseExprSubscripts (/node_modules/babylon/lib/index.js:3337:19) + at Parser.pp$3.parseMaybeUnary (/node_modules/babylon/lib/index.js:3317:19) + at Parser.pp$3.parseExprOps (/node_modules/babylon/lib/index.js:3247:19) + at Parser.pp$3.parseMaybeConditional (/node_modules/babylon/lib/index.js:3224:19) + at Parser.pp$3.parseMaybeAssign (/node_modules/babylon/lib/index.js:3187:19) +" +`; + +exports[`test yield_arrow_error2.js 1`] = ` +"function * f () { + var e = () => yield 1; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +function* f() { + var e = () => yield 1; +} + +" +`; diff --git a/tests/yield/jsfmt.spec.js b/tests/yield/jsfmt.spec.js new file mode 100644 index 000000000000..989047bccc52 --- /dev/null +++ b/tests/yield/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/yield/yield_arrow_error.js b/tests/yield/yield_arrow_error.js new file mode 100644 index 000000000000..330a07e8ad02 --- /dev/null +++ b/tests/yield/yield_arrow_error.js @@ -0,0 +1,3 @@ +function * f () { + var e = () => { yield 1; }; +} diff --git a/tests/yield/yield_arrow_error2.js b/tests/yield/yield_arrow_error2.js new file mode 100644 index 000000000000..243ac23ee67c --- /dev/null +++ b/tests/yield/yield_arrow_error2.js @@ -0,0 +1,3 @@ +function * f () { + var e = () => yield 1; +} diff --git a/tests_config/run_spec.js b/tests_config/run_spec.js new file mode 100644 index 000000000000..a7345bb42fb0 --- /dev/null +++ b/tests_config/run_spec.js @@ -0,0 +1,80 @@ +const fs = require('fs'); +const child_process = require('child_process'); + +function run_spec(dirname) { + fs.readdirSync(dirname).forEach(filename => { + if (filename.endsWith('.js') && filename !== 'jsfmt.spec.js') { + const path = dirname + '/' + filename; + + const RUN_AST_TESTS = + // true || // UNCOMMENT! + false; + + if (!RUN_AST_TESTS) { + const source = read(path); + const output = prettyprint(path); + test(filename, () => { + expect(source + '~'.repeat(80) + '\n' + output).toMatchSnapshot(); + }); + } + + if (RUN_AST_TESTS) { + test(path + ' parse', () => { + const file = read(dirname + '/' + filename); + const ast = parse(file); + const ppfile = pp(file); + const ppast = parse(ppfile); + expect(ast).toEqual(ppast); + }); + } + + } + }); +} +global.run_spec = run_spec; + +function stripLocation(ast) { + if (Array.isArray(ast)) { + return ast.map(e => stripLocation(e)); + } + if (typeof ast === 'object') { + const newObj = {}; + for (var key in ast) { + if (key === 'loc' || key === 'range' || key === 'raw' || key === 'comments') { + continue; + } + newObj[key] = stripLocation(ast[key]); + } + return newObj; + } + return ast; +} + +function parse(string) { + const flowParser = require('flow-parser'); + return stripLocation(flowParser.parse(string)); +} + +function prettyprint(path) { + const result = child_process.spawnSync( + './bin/jscodefmt', + [path], + ); + return ( + result.stdout.toString() || + result.stderr.toString() + .replace(new RegExp(process.cwd(), 'g'), '') + ); +} + +function pp(string) { + const tmp = 'tmp' + Math.random() + '.js'; + fs.writeFileSync(tmp, string); + const result = prettyprint(tmp); + fs.unlinkSync(tmp); + return result; +} + +function read(filename) { + return fs.readFileSync(filename, 'utf8'); +}